unix 2.8.2.1 → 2.8.3.0
raw patch · 15 files changed
+1441/−296 lines, 15 filesdep ~basedep ~timePVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: base, time
API changes (from Hackage documentation)
+ System.Posix.Files: CAttributes :: Word64 -> CAttributes
+ System.Posix.Files: ExtendedFileStatus :: ForeignPtr CStatx -> ExtendedFileStatus
+ System.Posix.Files: StatxFlags :: CInt -> StatxFlags
+ System.Posix.Files: StatxMask :: CInt -> StatxMask
+ System.Posix.Files: accessTimeHiResX :: ExtendedFileStatus -> POSIXTime
+ System.Posix.Files: creationTimeHiResX :: ExtendedFileStatus -> POSIXTime
+ System.Posix.Files: defaultStatxFlags :: StatxFlags
+ System.Posix.Files: defaultStatxMask :: StatxMask
+ System.Posix.Files: deviceIDX :: ExtendedFileStatus -> DeviceID
+ System.Posix.Files: fileAppendX :: ExtendedFileStatus -> Bool
+ System.Posix.Files: fileBlockSizeX :: ExtendedFileStatus -> CBlkSize
+ System.Posix.Files: fileBlocksX :: ExtendedFileStatus -> Word64
+ System.Posix.Files: fileCompressedX :: ExtendedFileStatus -> Bool
+ System.Posix.Files: fileDaxX :: ExtendedFileStatus -> Bool
+ System.Posix.Files: fileEncryptedX :: ExtendedFileStatus -> Bool
+ System.Posix.Files: fileGroupX :: ExtendedFileStatus -> GroupID
+ System.Posix.Files: fileIDX :: ExtendedFileStatus -> FileID
+ System.Posix.Files: fileImmutableX :: ExtendedFileStatus -> Bool
+ System.Posix.Files: fileModeX :: ExtendedFileStatus -> FileMode
+ System.Posix.Files: fileNoDumpX :: ExtendedFileStatus -> Bool
+ System.Posix.Files: fileOwnerX :: ExtendedFileStatus -> UserID
+ System.Posix.Files: fileSizeX :: ExtendedFileStatus -> Word64
+ System.Posix.Files: fileVerityX :: ExtendedFileStatus -> Bool
+ System.Posix.Files: getExtendedFileStatus :: Maybe Fd -> FilePath -> StatxFlags -> StatxMask -> IO ExtendedFileStatus
+ System.Posix.Files: isBlockDeviceX :: ExtendedFileStatus -> Bool
+ System.Posix.Files: isCharacterDeviceX :: ExtendedFileStatus -> Bool
+ System.Posix.Files: isDirectoryX :: ExtendedFileStatus -> Bool
+ System.Posix.Files: isNamedPipeX :: ExtendedFileStatus -> Bool
+ System.Posix.Files: isRegularFileX :: ExtendedFileStatus -> Bool
+ System.Posix.Files: isSocketX :: ExtendedFileStatus -> Bool
+ System.Posix.Files: isSymbolicLinkX :: ExtendedFileStatus -> Bool
+ System.Posix.Files: linkCountX :: ExtendedFileStatus -> CNlink
+ System.Posix.Files: modificationTimeHiResX :: ExtendedFileStatus -> POSIXTime
+ System.Posix.Files: mountIDX :: ExtendedFileStatus -> Word64
+ System.Posix.Files: newtype ExtendedFileStatus
+ System.Posix.Files: newtype StatxFlags
+ System.Posix.Files: newtype StatxMask
+ System.Posix.Files: newtype {-# CTYPE "__u64" #-} CAttributes
+ System.Posix.Files: pattern DontSync :: StatxFlags
+ System.Posix.Files: pattern EmptyPath :: StatxFlags
+ System.Posix.Files: pattern ForceSync :: StatxFlags
+ System.Posix.Files: pattern NoAutoMount :: StatxFlags
+ System.Posix.Files: pattern StatxAll :: StatxMask
+ System.Posix.Files: pattern StatxAtime :: StatxMask
+ System.Posix.Files: pattern StatxBasicStats :: StatxMask
+ System.Posix.Files: pattern StatxBlocks :: StatxMask
+ System.Posix.Files: pattern StatxBtime :: StatxMask
+ System.Posix.Files: pattern StatxCtime :: StatxMask
+ System.Posix.Files: pattern StatxGid :: StatxMask
+ System.Posix.Files: pattern StatxIno :: StatxMask
+ System.Posix.Files: pattern StatxMntId :: StatxMask
+ System.Posix.Files: pattern StatxMode :: StatxMask
+ System.Posix.Files: pattern StatxMtime :: StatxMask
+ System.Posix.Files: pattern StatxNlink :: StatxMask
+ System.Posix.Files: pattern StatxSize :: StatxMask
+ System.Posix.Files: pattern StatxType :: StatxMask
+ System.Posix.Files: pattern StatxUid :: StatxMask
+ System.Posix.Files: pattern SymlinkNoFollow :: StatxFlags
+ System.Posix.Files: pattern SyncAsStat :: StatxFlags
+ System.Posix.Files: specialDeviceIDX :: ExtendedFileStatus -> DeviceID
+ System.Posix.Files: statusChangeTimeHiResX :: ExtendedFileStatus -> POSIXTime
+ System.Posix.Files.ByteString: CAttributes :: Word64 -> CAttributes
+ System.Posix.Files.ByteString: ExtendedFileStatus :: ForeignPtr CStatx -> ExtendedFileStatus
+ System.Posix.Files.ByteString: StatxFlags :: CInt -> StatxFlags
+ System.Posix.Files.ByteString: StatxMask :: CInt -> StatxMask
+ System.Posix.Files.ByteString: accessTimeHiResX :: ExtendedFileStatus -> POSIXTime
+ System.Posix.Files.ByteString: creationTimeHiResX :: ExtendedFileStatus -> POSIXTime
+ System.Posix.Files.ByteString: defaultStatxFlags :: StatxFlags
+ System.Posix.Files.ByteString: defaultStatxMask :: StatxMask
+ System.Posix.Files.ByteString: deviceIDX :: ExtendedFileStatus -> DeviceID
+ System.Posix.Files.ByteString: fileAppendX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.ByteString: fileBlockSizeX :: ExtendedFileStatus -> CBlkSize
+ System.Posix.Files.ByteString: fileBlocksX :: ExtendedFileStatus -> Word64
+ System.Posix.Files.ByteString: fileCompressedX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.ByteString: fileDaxX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.ByteString: fileEncryptedX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.ByteString: fileGroupX :: ExtendedFileStatus -> GroupID
+ System.Posix.Files.ByteString: fileIDX :: ExtendedFileStatus -> FileID
+ System.Posix.Files.ByteString: fileImmutableX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.ByteString: fileModeX :: ExtendedFileStatus -> FileMode
+ System.Posix.Files.ByteString: fileNoDumpX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.ByteString: fileOwnerX :: ExtendedFileStatus -> UserID
+ System.Posix.Files.ByteString: fileSizeX :: ExtendedFileStatus -> Word64
+ System.Posix.Files.ByteString: fileVerityX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.ByteString: getExtendedFileStatus :: Maybe Fd -> RawFilePath -> StatxFlags -> StatxMask -> IO ExtendedFileStatus
+ System.Posix.Files.ByteString: isBlockDeviceX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.ByteString: isCharacterDeviceX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.ByteString: isDirectoryX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.ByteString: isNamedPipeX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.ByteString: isRegularFileX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.ByteString: isSocketX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.ByteString: isSymbolicLinkX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.ByteString: linkCountX :: ExtendedFileStatus -> CNlink
+ System.Posix.Files.ByteString: modificationTimeHiResX :: ExtendedFileStatus -> POSIXTime
+ System.Posix.Files.ByteString: mountIDX :: ExtendedFileStatus -> Word64
+ System.Posix.Files.ByteString: newtype ExtendedFileStatus
+ System.Posix.Files.ByteString: newtype StatxFlags
+ System.Posix.Files.ByteString: newtype StatxMask
+ System.Posix.Files.ByteString: newtype {-# CTYPE "__u64" #-} CAttributes
+ System.Posix.Files.ByteString: pattern DontSync :: StatxFlags
+ System.Posix.Files.ByteString: pattern EmptyPath :: StatxFlags
+ System.Posix.Files.ByteString: pattern ForceSync :: StatxFlags
+ System.Posix.Files.ByteString: pattern NoAutoMount :: StatxFlags
+ System.Posix.Files.ByteString: pattern StatxAll :: StatxMask
+ System.Posix.Files.ByteString: pattern StatxAtime :: StatxMask
+ System.Posix.Files.ByteString: pattern StatxBasicStats :: StatxMask
+ System.Posix.Files.ByteString: pattern StatxBlocks :: StatxMask
+ System.Posix.Files.ByteString: pattern StatxBtime :: StatxMask
+ System.Posix.Files.ByteString: pattern StatxCtime :: StatxMask
+ System.Posix.Files.ByteString: pattern StatxGid :: StatxMask
+ System.Posix.Files.ByteString: pattern StatxIno :: StatxMask
+ System.Posix.Files.ByteString: pattern StatxMntId :: StatxMask
+ System.Posix.Files.ByteString: pattern StatxMode :: StatxMask
+ System.Posix.Files.ByteString: pattern StatxMtime :: StatxMask
+ System.Posix.Files.ByteString: pattern StatxNlink :: StatxMask
+ System.Posix.Files.ByteString: pattern StatxSize :: StatxMask
+ System.Posix.Files.ByteString: pattern StatxType :: StatxMask
+ System.Posix.Files.ByteString: pattern StatxUid :: StatxMask
+ System.Posix.Files.ByteString: pattern SymlinkNoFollow :: StatxFlags
+ System.Posix.Files.ByteString: pattern SyncAsStat :: StatxFlags
+ System.Posix.Files.ByteString: specialDeviceIDX :: ExtendedFileStatus -> DeviceID
+ System.Posix.Files.ByteString: statusChangeTimeHiResX :: ExtendedFileStatus -> POSIXTime
+ System.Posix.Files.PosixString: CAttributes :: Word64 -> CAttributes
+ System.Posix.Files.PosixString: ExtendedFileStatus :: ForeignPtr CStatx -> ExtendedFileStatus
+ System.Posix.Files.PosixString: StatxFlags :: CInt -> StatxFlags
+ System.Posix.Files.PosixString: StatxMask :: CInt -> StatxMask
+ System.Posix.Files.PosixString: accessTimeHiResX :: ExtendedFileStatus -> POSIXTime
+ System.Posix.Files.PosixString: creationTimeHiResX :: ExtendedFileStatus -> POSIXTime
+ System.Posix.Files.PosixString: defaultStatxFlags :: StatxFlags
+ System.Posix.Files.PosixString: defaultStatxMask :: StatxMask
+ System.Posix.Files.PosixString: deviceIDX :: ExtendedFileStatus -> DeviceID
+ System.Posix.Files.PosixString: fileAppendX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.PosixString: fileBlockSizeX :: ExtendedFileStatus -> CBlkSize
+ System.Posix.Files.PosixString: fileBlocksX :: ExtendedFileStatus -> Word64
+ System.Posix.Files.PosixString: fileCompressedX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.PosixString: fileDaxX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.PosixString: fileEncryptedX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.PosixString: fileGroupX :: ExtendedFileStatus -> GroupID
+ System.Posix.Files.PosixString: fileIDX :: ExtendedFileStatus -> FileID
+ System.Posix.Files.PosixString: fileImmutableX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.PosixString: fileModeX :: ExtendedFileStatus -> FileMode
+ System.Posix.Files.PosixString: fileNoDumpX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.PosixString: fileOwnerX :: ExtendedFileStatus -> UserID
+ System.Posix.Files.PosixString: fileSizeX :: ExtendedFileStatus -> Word64
+ System.Posix.Files.PosixString: fileVerityX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.PosixString: getExtendedFileStatus :: Maybe Fd -> PosixPath -> StatxFlags -> StatxMask -> IO ExtendedFileStatus
+ System.Posix.Files.PosixString: isBlockDeviceX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.PosixString: isCharacterDeviceX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.PosixString: isDirectoryX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.PosixString: isNamedPipeX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.PosixString: isRegularFileX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.PosixString: isSocketX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.PosixString: isSymbolicLinkX :: ExtendedFileStatus -> Bool
+ System.Posix.Files.PosixString: linkCountX :: ExtendedFileStatus -> CNlink
+ System.Posix.Files.PosixString: modificationTimeHiResX :: ExtendedFileStatus -> POSIXTime
+ System.Posix.Files.PosixString: mountIDX :: ExtendedFileStatus -> Word64
+ System.Posix.Files.PosixString: newtype ExtendedFileStatus
+ System.Posix.Files.PosixString: newtype StatxFlags
+ System.Posix.Files.PosixString: newtype StatxMask
+ System.Posix.Files.PosixString: newtype {-# CTYPE "__u64" #-} CAttributes
+ System.Posix.Files.PosixString: pattern DontSync :: StatxFlags
+ System.Posix.Files.PosixString: pattern EmptyPath :: StatxFlags
+ System.Posix.Files.PosixString: pattern ForceSync :: StatxFlags
+ System.Posix.Files.PosixString: pattern NoAutoMount :: StatxFlags
+ System.Posix.Files.PosixString: pattern StatxAll :: StatxMask
+ System.Posix.Files.PosixString: pattern StatxAtime :: StatxMask
+ System.Posix.Files.PosixString: pattern StatxBasicStats :: StatxMask
+ System.Posix.Files.PosixString: pattern StatxBlocks :: StatxMask
+ System.Posix.Files.PosixString: pattern StatxBtime :: StatxMask
+ System.Posix.Files.PosixString: pattern StatxCtime :: StatxMask
+ System.Posix.Files.PosixString: pattern StatxGid :: StatxMask
+ System.Posix.Files.PosixString: pattern StatxIno :: StatxMask
+ System.Posix.Files.PosixString: pattern StatxMntId :: StatxMask
+ System.Posix.Files.PosixString: pattern StatxMode :: StatxMask
+ System.Posix.Files.PosixString: pattern StatxMtime :: StatxMask
+ System.Posix.Files.PosixString: pattern StatxNlink :: StatxMask
+ System.Posix.Files.PosixString: pattern StatxSize :: StatxMask
+ System.Posix.Files.PosixString: pattern StatxType :: StatxMask
+ System.Posix.Files.PosixString: pattern StatxUid :: StatxMask
+ System.Posix.Files.PosixString: pattern SymlinkNoFollow :: StatxFlags
+ System.Posix.Files.PosixString: pattern SyncAsStat :: StatxFlags
+ System.Posix.Files.PosixString: specialDeviceIDX :: ExtendedFileStatus -> DeviceID
+ System.Posix.Files.PosixString: statusChangeTimeHiResX :: ExtendedFileStatus -> POSIXTime
- System.Posix.PosixString: data PosixString
+ System.Posix.PosixString: data () => PosixString
Files
- System/Posix/Env/Internal.hsc +6/−3
- System/Posix/Files.hsc +83/−0
- System/Posix/Files/ByteString.hsc +83/−0
- System/Posix/Files/Common.hsc +659/−1
- System/Posix/Files/PosixString.hsc +85/−1
- cbits/HsUnix.c +2/−0
- changelog.md +6/−0
- configure +29/−0
- configure.ac +5/−0
- include/HsUnix.h +7/−0
- include/HsUnixConfig.h.in +9/−0
- tests/FileExtendedStatus.hs +166/−0
- tests/Test.hs +0/−283
- tests/Test.hsc +292/−0
- unix.cabal +9/−8
System/Posix/Env/Internal.hsc view
@@ -22,8 +22,11 @@ foreign import ccall unsafe "_NSGetEnviron" nsGetEnviron :: IO (Ptr (Ptr CString)) #else-getCEnviron = peek c_environ_p+getCEnviron = _getCEnviron -foreign import ccall unsafe "&environ"- c_environ_p :: Ptr (Ptr CString)+-- N.B. we cannot import `environ` directly in Haskell as it may be a weak symbol+-- which requires special treatment by the compiler, which GHC is not equipped to+-- provide. See GHC #24011.+foreign import ccall unsafe "__hsunix_get_environ"+ _getCEnviron :: IO (Ptr CString) #endif
System/Posix/Files.hsc view
@@ -1,5 +1,6 @@ {-# LANGUAGE Safe #-} {-# LANGUAGE CApiFFI #-}+{-# LANGUAGE PatternSynonyms #-} ----------------------------------------------------------------------------- -- |@@ -62,6 +63,69 @@ fileBlockSize, fileBlocks, + -- * Extended file status+ ExtendedFileStatus(..),+ CAttributes(..),+ -- ** Obtaining extended file status+ getExtendedFileStatus,+ -- ** Flags+ StatxFlags(..),+ defaultStatxFlags,+ pattern EmptyPath,+ pattern NoAutoMount,+ pattern SymlinkNoFollow,+ pattern SyncAsStat,+ pattern ForceSync,+ pattern DontSync,+ -- ** Mask+ StatxMask(..),+ defaultStatxMask,+ pattern StatxType,+ pattern StatxMode,+ pattern StatxNlink,+ pattern StatxUid,+ pattern StatxGid,+ pattern StatxAtime,+ pattern StatxMtime,+ pattern StatxCtime,+ pattern StatxIno,+ pattern StatxSize,+ pattern StatxBlocks,+ pattern StatxBasicStats,+ pattern StatxBtime,+ pattern StatxMntId,+ pattern StatxAll,+ -- ** Querying extended file status+ fileBlockSizeX,+ linkCountX,+ fileOwnerX,+ fileGroupX,+ fileModeX,+ fileIDX,+ fileSizeX,+ fileBlocksX,+ accessTimeHiResX,+ creationTimeHiResX,+ statusChangeTimeHiResX,+ modificationTimeHiResX,+ deviceIDX,+ specialDeviceIDX,+ mountIDX,+ fileCompressedX,+ fileImmutableX,+ fileAppendX,+ fileNoDumpX,+ fileEncryptedX,+ fileVerityX,+ fileDaxX,+ isBlockDeviceX,+ isCharacterDeviceX,+ isNamedPipeX,+ isRegularFileX,+ isDirectoryX,+ isSymbolicLinkX,+ isSocketX,+ -- * Creation createNamedPipe, createDevice,@@ -188,6 +252,25 @@ withFilePath path $ \s -> throwErrnoPathIfMinus1Retry_ "getFileStatus" path (c_stat s p) return (FileStatus fp)++-- | Gets extended file status information.+--+-- The target file to open is identified in one of the following ways:+--+-- - If @pathname@ begins with a slash, then it is an absolute pathname that identifies the target file. In this case, @dirfd@ is ignored+-- - If @pathname@ is a string that begins with a character other than a slash and @dirfd@ is a file descriptor that refers to a+-- directory, then pathname is a relative pathname that is interpreted relative to the directory referred to by dirfd.+-- (See @openat(2)@ for an explanation of why this is useful.)+-- - If @pathname@ is an empty string and the 'EmptyPath' flag is specified in flags (see below), then the target file is+-- the one referred to by the file descriptor @dirfd@.+--+-- Note: calls @statx@.+getExtendedFileStatus :: Maybe Fd -- ^ Optional directory file descriptor (@dirfd@)+ -> FilePath -- ^ @pathname@ to open+ -> StatxFlags -- ^ flags+ -> StatxMask -- ^ mask+ -> IO ExtendedFileStatus+getExtendedFileStatus mfd path flags masks = withFilePath path $ \s -> getExtendedFileStatus_ mfd s flags masks -- | Acts as 'getFileStatus' except when the 'FilePath' refers to a symbolic -- link. In that case the @FileStatus@ information of the symbolic link itself
System/Posix/Files/ByteString.hsc view
@@ -1,5 +1,6 @@ {-# LANGUAGE Safe #-} {-# LANGUAGE CApiFFI #-}+{-# LANGUAGE PatternSynonyms #-} ----------------------------------------------------------------------------- -- |@@ -62,6 +63,69 @@ fileBlockSize, fileBlocks, + -- * Extended file status+ ExtendedFileStatus(..),+ CAttributes(..),+ -- ** Obtaining extended file status+ getExtendedFileStatus,+ -- ** Flags+ StatxFlags(..),+ defaultStatxFlags,+ pattern EmptyPath,+ pattern NoAutoMount,+ pattern SymlinkNoFollow,+ pattern SyncAsStat,+ pattern ForceSync,+ pattern DontSync,+ -- ** Mask+ StatxMask(..),+ defaultStatxMask,+ pattern StatxType,+ pattern StatxMode,+ pattern StatxNlink,+ pattern StatxUid,+ pattern StatxGid,+ pattern StatxAtime,+ pattern StatxMtime,+ pattern StatxCtime,+ pattern StatxIno,+ pattern StatxSize,+ pattern StatxBlocks,+ pattern StatxBasicStats,+ pattern StatxBtime,+ pattern StatxMntId,+ pattern StatxAll,+ -- ** Querying extended file status+ fileBlockSizeX,+ linkCountX,+ fileOwnerX,+ fileGroupX,+ fileModeX,+ fileIDX,+ fileSizeX,+ fileBlocksX,+ accessTimeHiResX,+ creationTimeHiResX,+ statusChangeTimeHiResX,+ modificationTimeHiResX,+ deviceIDX,+ specialDeviceIDX,+ mountIDX,+ fileCompressedX,+ fileImmutableX,+ fileAppendX,+ fileNoDumpX,+ fileEncryptedX,+ fileVerityX,+ fileDaxX,+ isBlockDeviceX,+ isCharacterDeviceX,+ isNamedPipeX,+ isRegularFileX,+ isDirectoryX,+ isSymbolicLinkX,+ isSocketX,+ -- * Creation createNamedPipe, createDevice,@@ -183,6 +247,25 @@ withFilePath path $ \s -> throwErrnoPathIfMinus1Retry_ "getFileStatus" path (c_stat s p) return (FileStatus fp)++-- | Gets extended file status information.+--+-- The target file to open is identified in one of the following ways:+--+-- - If @pathname@ begins with a slash, then it is an absolute pathname that identifies the target file. In this case, @dirfd@ is ignored+-- - If @pathname@ is a string that begins with a character other than a slash and @dirfd@ is a file descriptor that refers to a+-- directory, then pathname is a relative pathname that is interpreted relative to the directory referred to by dirfd.+-- (See @openat(2)@ for an explanation of why this is useful.)+-- - If @pathname@ is an empty string and the 'EmptyPath' flag is specified in flags (see below), then the target file is+-- the one referred to by the file descriptor @dirfd@.+--+-- Note: calls @statx@.+getExtendedFileStatus :: Maybe Fd -- ^ Optional directory file descriptor (@dirfd@)+ -> RawFilePath -- ^ @pathname@ to open+ -> StatxFlags -- ^ flags+ -> StatxMask -- ^ mask+ -> IO ExtendedFileStatus+getExtendedFileStatus mfd path flags masks = withFilePath path $ \s -> getExtendedFileStatus_ mfd s flags masks -- | Acts as 'getFileStatus' except when the 'RawFilePath' refers to a symbolic -- link. In that case the @FileStatus@ information of the symbolic link itself
System/Posix/Files/Common.hsc view
@@ -1,5 +1,9 @@ {-# LANGUAGE CApiFFI #-}+{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE Trustworthy #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE NumDecimals #-} ----------------------------------------------------------------------------- -- |@@ -60,6 +64,65 @@ fileBlockSize, fileBlocks, + -- * Extended file status+ StatxFlags(..),+ pattern EmptyPath,+ pattern NoAutoMount,+ pattern SymlinkNoFollow,+ pattern SyncAsStat,+ pattern ForceSync,+ pattern DontSync,+ defaultStatxFlags,+ StatxMask(..),+ pattern StatxType,+ pattern StatxMode,+ pattern StatxNlink,+ pattern StatxUid,+ pattern StatxGid,+ pattern StatxAtime,+ pattern StatxMtime,+ pattern StatxCtime,+ pattern StatxBtime,+ pattern StatxIno,+ pattern StatxSize,+ pattern StatxBlocks,+ pattern StatxMntId,+ pattern StatxBasicStats,+ pattern StatxAll,+ defaultStatxMask,+ ExtendedFileStatus(..),+ CAttributes(..),+ getExtendedFileStatus_,+ fileBlockSizeX,+ linkCountX,+ fileOwnerX,+ fileGroupX,+ fileModeX,+ fileIDX,+ fileSizeX,+ fileBlocksX,+ accessTimeHiResX,+ creationTimeHiResX,+ statusChangeTimeHiResX,+ modificationTimeHiResX,+ deviceIDX,+ specialDeviceIDX,+ mountIDX,+ fileCompressedX,+ fileImmutableX,+ fileAppendX,+ fileNoDumpX,+ fileEncryptedX,+ fileVerityX,+ fileDaxX,+ isBlockDeviceX,+ isCharacterDeviceX,+ isNamedPipeX,+ isRegularFileX,+ isDirectoryX,+ isSymbolicLinkX,+ isSocketX,+ -- * Setting file sizes setFdSize, @@ -93,6 +156,11 @@ import Data.Bits import Data.Int import Data.Ratio+import Data.Word+#ifdef HAVE_STATX+import Data.Time.Clock (secondsToNominalDiffTime)+import Data.Fixed (Fixed(..))+#endif import Data.Time.Clock.POSIX (POSIXTime) import System.Posix.Internals import Foreign.C@@ -103,7 +171,7 @@ import Foreign.Ptr import Foreign.Storable -#if !defined(HAVE_FCHMOD) || !defined(HAVE_CHOWN)+#if !defined(HAVE_FCHMOD) || !defined(HAVE_CHOWN) || !defined(HAVE_STATX) import System.IO.Error ( ioeSetLocation ) import GHC.IO.Exception ( unsupportedOperation ) #endif@@ -688,3 +756,593 @@ foreign import ccall unsafe "fpathconf" c_fpathconf :: CInt -> CInt -> IO CLong+++-- -----------------------------------------------------------------------------+-- statx+--++newtype {-# CTYPE "__u64" #-} CAttributes = CAttributes Word64+ deriving (Read, Show, Eq, Ord, Storable, Num, Bits)++-- | Statx flags.+--+-- See the pattern synonyms for possible flags. These are combined via `(<>)`.+-- Flags can be tested via `(.&.)`.+--+-- The following flags influence pathname-based lookup:+--+-- - 'EmptyPath'+-- - 'NoAutoMount'+-- - 'SymlinkNoFollow'+--+-- The following flags can be used to control what sort of synchronization the kernel will do when querying a file on a remote filesystem:+--+-- - 'SyncAsStat'+-- - 'ForceSync'+-- - 'DontSync'+newtype StatxFlags = StatxFlags CInt deriving (Read, Show, Eq, Ord, Integral, Num, Enum, Bits, Real)++-- | ORs the flags.+instance Semigroup StatxFlags where+ a <> b = a .|. b++instance Monoid StatxFlags where+ mappend = (<>)+ mempty = 0++-- | If pathname to 'getExtendedFileStatus' is an empty string, operate on the file referred to by+-- the 'Maybe Fd' argument.+--+-- In this case, it can refer to any type of file, not just a directory.+pattern EmptyPath :: StatxFlags+#ifdef AT_EMPTY_PATH+pattern EmptyPath = StatxFlags (#const AT_EMPTY_PATH)+#else+pattern EmptyPath = StatxFlags 0+#endif++-- | Don't automount the terminal ("basename") component of pathname if it is a directory that is an automount point.+-- This allows the caller to gather attributes of an automount point (rather than the location it would mount).+-- This flag can be used in tools that scan directories to prevent mass-automounting of a directory of automount points.+-- This flag has no effect if the mount point has already been mounted over.+pattern NoAutoMount :: StatxFlags+#ifdef AT_NO_AUTOMOUNT+pattern NoAutoMount = StatxFlags (#const AT_NO_AUTOMOUNT)+#else+pattern NoAutoMount = StatxFlags 0+#endif++-- | If pathname is a symbolic link, do not dereference it: instead return information about the link itself, like @lstat(2)@.+pattern SymlinkNoFollow :: StatxFlags+#ifdef AT_SYMLINK_NOFOLLOW+pattern SymlinkNoFollow = StatxFlags (#const AT_SYMLINK_NOFOLLOW)+#else+pattern SymlinkNoFollow = StatxFlags 0+#endif++-- | Do whatever @stat(2)@ does. This is the default and is very much filesystem-specific.+pattern SyncAsStat :: StatxFlags+#ifdef AT_STATX_SYNC_AS_STAT+pattern SyncAsStat = StatxFlags (#const AT_STATX_SYNC_AS_STAT)+#else+pattern SyncAsStat = StatxFlags 0+#endif++-- | Force the attributes to be synchronized with the server.+-- This may require that a network filesystem perform a data writeback to get the timestamps correct.+pattern ForceSync :: StatxFlags+#ifdef AT_STATX_FORCE_SYNC+pattern ForceSync = StatxFlags (#const AT_STATX_FORCE_SYNC)+#else+pattern ForceSync = StatxFlags 0+#endif++-- | Don't synchronize anything, but rather just take whatever the system has cached if possible.+-- This may mean that the information returned is approximate, but, on a network filesystem,+-- it may not involve a round trip to the server - even if no lease is held.+pattern DontSync :: StatxFlags+#ifdef AT_STATX_DONT_SYNC+pattern DontSync = StatxFlags (#const AT_STATX_DONT_SYNC)+#else+pattern DontSync = StatxFlags 0+#endif++defaultStatxFlags :: StatxFlags+defaultStatxFlags = mempty++-- | Mask argument to 'statx'. It's used to tell the kernel which fields the caller is interested in.+--+-- See the pattern synonyms for possible masks. These are combined via @(<>)@.+-- Masks can be tested via `(.&.)`.+newtype StatxMask = StatxMask CInt deriving (Read, Show, Eq, Ord, Integral, Num, Enum, Bits, Real)++-- | ORs the masks.+instance Semigroup StatxMask where+ a <> b = a .|. b++instance Monoid StatxMask where+ mappend = (<>)+ mempty = 0++-- | Want @stx_mode & S_IFMT@.+pattern StatxType :: StatxMask+#ifdef STATX_TYPE+pattern StatxType = StatxMask (#const STATX_TYPE)+#else+pattern StatxType = StatxMask 0+#endif++-- | Want @stx_mode & ~S_IFMT@.+pattern StatxMode :: StatxMask+#ifdef STATX_MODE+pattern StatxMode = StatxMask (#const STATX_MODE)+#else+pattern StatxMode = StatxMask 0+#endif++-- | Want @stx_nlink@.+pattern StatxNlink :: StatxMask+#ifdef STATX_NLINK+pattern StatxNlink = StatxMask (#const STATX_NLINK)+#else+pattern StatxNlink = StatxMask 0+#endif++-- | Want @stx_uid@.+pattern StatxUid :: StatxMask+#ifdef STATX_UID+pattern StatxUid = StatxMask (#const STATX_UID)+#else+pattern StatxUid = StatxMask 0+#endif++-- | Want @stx_gid@.+pattern StatxGid :: StatxMask+#ifdef STATX_GID+pattern StatxGid = StatxMask (#const STATX_GID)+#else+pattern StatxGid = StatxMask 0+#endif++-- | Want @stx_atime@.+pattern StatxAtime :: StatxMask+#ifdef STATX_ATIME+pattern StatxAtime = StatxMask (#const STATX_ATIME)+#else+pattern StatxAtime = StatxMask 0+#endif++-- | Want @stx_mtime@.+pattern StatxMtime :: StatxMask+#ifdef STATX_MTIME+pattern StatxMtime = StatxMask (#const STATX_MTIME)+#else+pattern StatxMtime = StatxMask 0+#endif++-- | Want @stx_ctime@.+pattern StatxCtime :: StatxMask+#ifdef STATX_CTIME+pattern StatxCtime = StatxMask (#const STATX_CTIME)+#else+pattern StatxCtime = StatxMask 0+#endif++-- | Want @stx_btime@.+pattern StatxBtime :: StatxMask+#ifdef STATX_BTIME+pattern StatxBtime = StatxMask (#const STATX_BTIME)+#else+pattern StatxBtime = StatxMask 0+#endif++-- | Want @stx_mnt_id@.+pattern StatxMntId :: StatxMask+#ifdef STATX_MNT_ID+pattern StatxMntId = StatxMask (#const STATX_MNT_ID)+#else+pattern StatxMntId = StatxMask 0+#endif++-- | Want @stx_ino@.+pattern StatxIno :: StatxMask+#ifdef STATX_INO+pattern StatxIno = StatxMask (#const STATX_INO)+#else+pattern StatxIno = StatxMask 0+#endif++-- | Want @stx_size@.+pattern StatxSize :: StatxMask+#ifdef STATX_SIZE+pattern StatxSize = StatxMask (#const STATX_SIZE)+#else+pattern StatxSize = StatxMask 0+#endif++-- | Want @stx_blocks@.+pattern StatxBlocks :: StatxMask+#ifdef STATX_BLOCKS+pattern StatxBlocks = StatxMask (#const STATX_BLOCKS)+#else+pattern StatxBlocks = StatxMask 0+#endif++-- | Want all of the above.+pattern StatxBasicStats :: StatxMask+#ifdef STATX_BASIC_STATS+pattern StatxBasicStats = StatxMask (#const STATX_BASIC_STATS)+#else+pattern StatxBasicStats = StatxMask 0+#endif++-- | Want all currently available fields.+pattern StatxAll :: StatxMask+#ifdef STATX_ALL+pattern StatxAll = StatxMask (#const STATX_ALL)+#else+pattern StatxAll = StatxMask 0+#endif+++defaultStatxMask :: StatxMask+defaultStatxMask = mempty++newtype ExtendedFileStatus = ExtendedFileStatus (ForeignPtr CStatx) -- ^ The constructor is considered internal and may change.++-- | The "preferred" block size for efficient filesystem I/O.+-- (Writing to a file in smaller chunks may cause an inefficient read-mod‐ify-rewrite.)+fileBlockSizeX :: ExtendedFileStatus -> CBlkSize+#if HAVE_STATX+-- | Further status information about the file.+fileAttributesX :: ExtendedFileStatus -> CAttributes+#endif+-- | The number of hard links on a file.+linkCountX :: ExtendedFileStatus -> CNlink+-- | Te user ID of the owner of the file.+fileOwnerX :: ExtendedFileStatus -> UserID+-- | The ID of the group owner of the file.+fileGroupX :: ExtendedFileStatus -> GroupID+-- | The file type and mode. See @inode(7)@ for details.+fileModeX :: ExtendedFileStatus -> FileMode+-- | The inode number of the file.+fileIDX :: ExtendedFileStatus -> FileID+-- | The size of the file (if it is a regular file or a symbolic link) in bytes.+-- The size of a symbolic link is the length of the pathname it contains,+-- without a terminating null byte.+fileSizeX :: ExtendedFileStatus -> Word64+-- | The number of blocks allocated to the file on the medium, in 512-byte units.+-- (This may be smaller than stx_size/512 when the file has holes.)+fileBlocksX :: ExtendedFileStatus -> Word64+#if HAVE_STATX+-- | A mask indicating which bits in 'fileAttributesX' are supported by the VFS and the filesystem.+fileAttributesMaskX :: ExtendedFileStatus -> CAttributes+#endif+-- | The file's last access timestamp.+accessTimeHiResX :: ExtendedFileStatus -> POSIXTime+-- | The file's creation timestamp.+creationTimeHiResX :: ExtendedFileStatus -> POSIXTime+-- | The file's last status change timestamp.+statusChangeTimeHiResX :: ExtendedFileStatus -> POSIXTime+-- | The file's last modification timestamp.+modificationTimeHiResX :: ExtendedFileStatus -> POSIXTime+-- | ID of the device on which this file resides.+deviceIDX :: ExtendedFileStatus -> DeviceID+-- | Describes the device that this file represents.+specialDeviceIDX :: ExtendedFileStatus -> DeviceID+-- | The mount ID of the mount containing the file. This is the same number+-- reported by name_to_handle_at(2) and corresponds to the number in the+-- first field in one of the records in /proc/self/mountinfo.+mountIDX :: ExtendedFileStatus -> Word64+-- | The file is compressed by the filesystem and may take extra resources to access.+-- This is an extended attribute.+fileCompressedX :: ExtendedFileStatus -> Bool+-- | The file cannot be modified: it cannot be deleted or renamed, no hard links can+-- be created to this file and no data can be written to it. See @chattr(1)@.+-- This is an extended attribute.+fileImmutableX :: ExtendedFileStatus -> Bool+-- | The file can only be opened in append mode for writing. Random access writing is not permitted. See @chattr(1)@.+-- This is an extended attribute.+fileAppendX :: ExtendedFileStatus -> Bool+-- | File is not a candidate for backup when a backup program such as @dump(8)@ is run. See @chattr(1)@.+-- This is an extended attribute.+fileNoDumpX :: ExtendedFileStatus -> Bool+-- | A key is required for the file to be encrypted by the filesystem.+-- This is an extended attribute.+fileEncryptedX :: ExtendedFileStatus -> Bool+-- | The file has fs-verity enabled. It cannot be written to, and all reads from it+-- will be verified against a cryptographic hash that covers the entire file (e.g., via a Merkle tree).+-- This is an extended attribute.+-- Since Linux 5.5.+fileVerityX :: ExtendedFileStatus -> Bool+-- | The file is in the DAX (cpu direct access) state.+-- This is an extended attribute.+-- Since Linux 5.8.+fileDaxX :: ExtendedFileStatus -> Bool++-- | Checks if this file is a block device.+isBlockDeviceX :: ExtendedFileStatus -> Bool+-- | Checks if this file is a character device.+isCharacterDeviceX :: ExtendedFileStatus -> Bool+-- | Checks if this file is a named pipe device.+isNamedPipeX :: ExtendedFileStatus -> Bool+-- | Checks if this file is a regular file device.+isRegularFileX :: ExtendedFileStatus -> Bool+-- | Checks if this file is a directory device.+isDirectoryX :: ExtendedFileStatus -> Bool+-- | Checks if this file is a symbolic link device.+isSymbolicLinkX :: ExtendedFileStatus -> Bool+-- | Checks if this file is a socket device.+isSocketX :: ExtendedFileStatus -> Bool++isBlockDeviceX statx =+ (fileModeX statx `intersectFileModes` fileTypeModes) == blockSpecialMode+isCharacterDeviceX statx =+ (fileModeX statx `intersectFileModes` fileTypeModes) == characterSpecialMode+isNamedPipeX statx =+ (fileModeX statx `intersectFileModes` fileTypeModes) == namedPipeMode+isRegularFileX statx =+ (fileModeX statx `intersectFileModes` fileTypeModes) == regularFileMode+isDirectoryX statx =+ (fileModeX statx `intersectFileModes` fileTypeModes) == directoryMode+isSymbolicLinkX statx =+ (fileModeX statx `intersectFileModes` fileTypeModes) == symbolicLinkMode+isSocketX statx =+ (fileModeX statx `intersectFileModes` fileTypeModes) == socketMode++#if HAVE_STATX+testFlag :: ExtendedFileStatus -> CAttributes -> Bool+testFlag ex flag =+ let attributes = fileAttributesX ex+ attributes_mask = fileAttributesMaskX ex+ in (attributes .&. attributes_mask .&. flag) /= 0++#ifdef STATX_ATTR_COMPRESSED+fileCompressedX ex = testFlag ex (#const STATX_ATTR_COMPRESSED)+#else+{-# WARNING fileCompressedX "fileCompressedX: not available on this platform, will default to 'False' (CPP guard: @#if STATX_ATTR_COMPRESSED@)" #-}+fileCompressedX _ = False+#endif+#ifdef STATX_ATTR_IMMUTABLE+fileImmutableX ex = testFlag ex (#const STATX_ATTR_IMMUTABLE)+#else+{-# WARNING fileImmutableX "fileImmutableX: not available on this platform, will default to 'False' (CPP guard: @#if STATX_ATTR_IMMUTABLE@)" #-}+fileImmutableX _ = False+#endif+#ifdef STATX_ATTR_APPEND+fileAppendX ex = testFlag ex (#const STATX_ATTR_APPEND)+#else+{-# WARNING fileAppendX "fileAppendX: not available on this platform, will default to 'False' (CPP guard: @#if STATX_ATTR_APPEND@)" #-}+fileAppendX _ = False+#endif+#ifdef STATX_ATTR_NODUMP+fileNoDumpX ex = testFlag ex (#const STATX_ATTR_NODUMP)+#else+{-# WARNING fileNoDumpX "fileNoDumpX: not available on this platform, will default to 'False' (CPP guard: @#if STATX_ATTR_NODUMP@)" #-}+fileNoDumpX _ = False+#endif+#ifdef STATX_ATTR_ENCRYPTED+fileEncryptedX ex = testFlag ex (#const STATX_ATTR_ENCRYPTED)+#else+{-# WARNING fileEncryptedX "fileEncryptedX: not available on this platform, will default to 'False' (CPP guard: @#if STATX_ATTR_ENCRYPTED@)" #-}+fileEncryptedX _ = False+#endif+#ifdef STATX_ATTR_VERITY+fileVerityX ex = testFlag ex (#const STATX_ATTR_VERITY)+#else+{-# WARNING fileVerityX "fileVerityX: not available on this platform, will default to 'False' (CPP guard: @#if STATX_ATTR_VERITY@)" #-}+fileVerityX _ = False+#endif+#ifdef STATX_ATTR_DAX+fileDaxX ex = testFlag ex (#const STATX_ATTR_DAX)+#else+{-# WARNING fileDaxX "fileDaxX: not available on this platform, will default to 'False' (CPP guard: @#if STATX_ATTR_DAX@)" #-}+fileDaxX _ = False+#endif++#ifdef HAVE_SYS_SYSMACROS_H+deviceIDX (ExtendedFileStatus statx) = unsafePerformIO $ do+ major <- withForeignPtr statx $ (#peek struct statx, stx_dev_major) :: IO CUInt+ minor <- withForeignPtr statx $ (#peek struct statx, stx_dev_minor) :: IO CUInt+ c_makedev major minor+#else+{-# WARNING deviceIDX "deviceIDX: not available on this platform, will throw error (CPP guard: @#if HAVE_SYS_SYSMACROS_H@)" #-}+deviceIDX _ = error "deviceIDX not available on this platform"+#endif+#ifdef HAVE_SYS_SYSMACROS_H+specialDeviceIDX (ExtendedFileStatus statx) = unsafePerformIO $ do+ major <- withForeignPtr statx $ (#peek struct statx, stx_rdev_major) :: IO CUInt+ minor <- withForeignPtr statx $ (#peek struct statx, stx_rdev_minor) :: IO CUInt+ c_makedev major minor+#else+{-# WARNING specialDeviceIDX "specialDeviceIDX: not available on this platform, will throw error (CPP guard: @#if HAVE_SYS_SYSMACROS_H@)" #-}+specialDeviceIDX _ = error "specialDeviceIDX not available on this platform"+#endif+#ifdef STATX_MNT_ID+mountIDX (ExtendedFileStatus statx) =+ unsafePerformIO $ withForeignPtr statx $ (#peek struct statx, stx_mnt_id)+#else+{-# WARNING mountIDX "mountIDX: not available on this platform, will throw error (CPP guard: @#if STATX_MNT_ID@)" #-}+mountIDX _ = error "mountIDX not available on this platform"+#endif+fileBlockSizeX (ExtendedFileStatus statx) = unsafePerformIO $ do+ r <- withForeignPtr statx $ (#peek struct statx, stx_blksize) :: IO Word32+ return $ CBlkSize (fromIntegral r)+fileAttributesX (ExtendedFileStatus statx) =+ unsafePerformIO $ withForeignPtr statx $ (#peek struct statx, stx_attributes)+#ifdef STATX_NLINK+linkCountX (ExtendedFileStatus statx) = unsafePerformIO $ do+ links <- withForeignPtr statx $ (#peek struct statx, stx_nlink) :: IO Word32+ return $ CNlink (fromIntegral links)+#else+{-# WARNING linkCountX "linkCountX: not available on this platform, will throw error (CPP guard: @#if STATX_NLINK@)" #-}+linkCountX _ = error "linkCountX not available on this platform"+#endif+#ifdef STATX_UID+fileOwnerX (ExtendedFileStatus statx) =+ unsafePerformIO $ withForeignPtr statx $ (#peek struct statx, stx_uid)+#else+{-# WARNING fileOwnerX "fileOwnerX: not available on this platform, will throw error (CPP guard: @#if STATX_UID@)" #-}+fileOwnerX _ = error "fileOwnerX not available on this platform"+#endif+#ifdef STATX_GID+fileGroupX (ExtendedFileStatus statx) =+ unsafePerformIO $ withForeignPtr statx $ (#peek struct statx, stx_gid)+#else+{-# WARNING fileGroupX "fileGroupX: not available on this platform, will throw error (CPP guard: @#if STATX_GID@)" #-}+fileGroupX _ = error "fileGroupX not available on this platform"+#endif+#ifdef STATX_MODE+fileModeX (ExtendedFileStatus statx) = unsafePerformIO $ do+ r <- withForeignPtr statx $ (#peek struct statx, stx_mode) :: IO Word16+ return $ CMode $ fromIntegral r+#else+{-# WARNING fileModeX "fileModeX: not available on this platform, will throw error (CPP guard: @#if STATX_MODE@)" #-}+fileModeX _ = error "fileModeX not available on this platform"+#endif+#ifdef STATX_INO+fileIDX (ExtendedFileStatus statx) =+ unsafePerformIO $ withForeignPtr statx $ (#peek struct statx, stx_ino)+#else+{-# WARNING fileIDX "fileIDX: not available on this platform, will throw error (CPP guard: @#if STATX_INO@)" #-}+fileIDX _ = error "fileIDX not available on this platform"+#endif+#ifdef STATX_SIZE+fileSizeX (ExtendedFileStatus statx) =+ unsafePerformIO $ withForeignPtr statx $ (#peek struct statx, stx_size) :: Word64+#else+{-# WARNING fileSizeX "fileSizeX: not available on this platform, will throw error (CPP guard: @#if STATX_SIZE@)" #-}+fileSizeX _ = error "fileSizeX not available on this platform"+#endif+#ifdef STATX_BLOCKS+fileBlocksX (ExtendedFileStatus statx) =+ unsafePerformIO $ withForeignPtr statx $ (#peek struct statx, stx_blocks) :: Word64+#else+{-# WARNING fileBlocksX "fileBlocksX: not available on this platform, will throw error (CPP guard: @#if STATX_BLOCKS@)" #-}+fileBlocksX _ = error "fileBlocksX not available on this platform"+#endif+fileAttributesMaskX (ExtendedFileStatus statx) =+ unsafePerformIO $ withForeignPtr statx $ (#peek struct statx, stx_attributes_mask)+#ifdef STATX_ATIME+accessTimeHiResX (ExtendedFileStatus statx) =+ unsafePerformIO $ withForeignPtr statx $ \statx_ptr -> do+ sec <- (#peek struct statx, stx_atime.tv_sec) statx_ptr :: IO EpochTime+ nsec <- (#peek struct statx, stx_atime.tv_nsec) statx_ptr :: IO (#type int)+ return $ timeHiResToNominalDiffTime sec nsec+#else+{-# WARNING accessTimeHiResX "accessTimeHiResX: not available on this platform, will throw error (CPP guard: @#if STATX_ATIME@)" #-}+accessTimeHiResX _ = error "accessTimeHiResX not available on this platform"+#endif+#ifdef STATX_BTIME+creationTimeHiResX (ExtendedFileStatus statx) =+ unsafePerformIO $ withForeignPtr statx $ \statx_ptr -> do+ sec <- (#peek struct statx, stx_btime.tv_sec) statx_ptr :: IO EpochTime+ nsec <- (#peek struct statx, stx_btime.tv_nsec) statx_ptr :: IO (#type int)+ return $ timeHiResToNominalDiffTime sec nsec+#else+{-# WARNING creationTimeHiResX "creationTimeHiResX: not available on this platform, will throw error (CPP guard: @#if STATX_BTIME@)" #-}+creationTimeHiResX _ = error "creationTimeHiResX not available on this platform"+#endif+#ifdef STATX_CTIME+statusChangeTimeHiResX (ExtendedFileStatus statx) =+ unsafePerformIO $ withForeignPtr statx $ \statx_ptr -> do+ sec <- (#peek struct statx, stx_ctime.tv_sec) statx_ptr :: IO EpochTime+ nsec <- (#peek struct statx, stx_ctime.tv_nsec) statx_ptr :: IO (#type int)+ return $ timeHiResToNominalDiffTime sec nsec+#else+{-# WARNING statusChangeTimeHiResX "statusChangeTimeHiResX: not available on this platform, will throw error (CPP guard: @#if STATX_CTIME@)" #-}+statusChangeTimeHiResX _ = error "statusChangeTimeHiResX not available on this platform"+#endif+#ifdef STATX_MTIME+modificationTimeHiResX (ExtendedFileStatus statx) =+ unsafePerformIO $ withForeignPtr statx $ \statx_ptr -> do+ sec <- (#peek struct statx, stx_mtime.tv_sec) statx_ptr :: IO EpochTime+ nsec <- (#peek struct statx, stx_mtime.tv_nsec) statx_ptr :: IO (#type int)+ return $ timeHiResToNominalDiffTime sec nsec+#else+{-# WARNING modificationTimeHiResX "modificationTimeHiResX: not available on this platform, will throw error (CPP guard: @#if STATX_MTIME@)" #-}+modificationTimeHiResX _ = error "modificationTimeHiResX not available on this platform"+#endif++timeHiResToNominalDiffTime :: EpochTime -> Int32 -> POSIXTime+timeHiResToNominalDiffTime (CTime sec) nsec = secondsToNominalDiffTime $ MkFixed $ toInteger sec * 1e12 + toInteger nsec * 1e3++#else+{-# WARNING linkCountX "linkCountX: not available on this platform, will throw error (CPP guard: @#if HAVE_STATX@)" #-}+linkCountX _ = error "linkCountX not available on this platform"+{-# WARNING fileBlockSizeX "fileBlockSizeX: not available on this platform, will throw error (CPP guard: @#if HAVE_STATX@)" #-}+fileBlockSizeX _ = error "fileBlockSizeX not available on this platform"+{-# WARNING deviceIDX "deviceIDX: not available on this platform, will throw error (CPP guard: @#if HAVE_STATX@)" #-}+deviceIDX _ = error "deviceIDX not available on this platform"+{-# WARNING specialDeviceIDX "specialDeviceIDX: not available on this platform, will throw error (CPP guard: @#if HAVE_STATX@)" #-}+specialDeviceIDX _ = error "specialDeviceIDX not available on this platform"+{-# WARNING mountIDX "mountIDX: not available on this platform, will throw error (CPP guard: @#if HAVE_STATX@)" #-}+mountIDX _ = error "mountIDX not available on this platform"+{-# WARNING fileOwnerX "fileOwnerX: not available on this platform, will throw error (CPP guard: @#if HAVE_STATX@)" #-}+fileOwnerX _ = error "fileOwnerX not available on this platform"+{-# WARNING fileGroupX "fileGroupX: not available on this platform, will throw error (CPP guard: @#if HAVE_STATX@)" #-}+fileGroupX _ = error "fileGroupX not available on this platform"+{-# WARNING fileModeX "fileModeX: not available on this platform, will throw error (CPP guard: @#if HAVE_STATX@)" #-}+fileModeX _ = error "fileModeX not available on this platform"+{-# WARNING fileIDX "fileIDX: not available on this platform, will throw error (CPP guard: @#if HAVE_STATX@)" #-}+fileIDX _ = error "fileIDX not available on this platform"+{-# WARNING fileSizeX "fileSizeX: not available on this platform, will throw error (CPP guard: @#if HAVE_STATX@)" #-}+fileSizeX _ = error "fileSizeX not available on this platform"+{-# WARNING fileBlocksX "fileBlocksX: not available on this platform, will throw error (CPP guard: @#if HAVE_STATX@)" #-}+fileBlocksX _ = error "fileBlocksX not available on this platform"+{-# WARNING accessTimeHiResX "accessTimeHiResX: not available on this platform, will throw error (CPP guard: @#if HAVE_STATX@)" #-}+accessTimeHiResX _ = error "accessTimeHiResX not available on this platform"+{-# WARNING creationTimeHiResX "creationTimeHiResX: not available on this platform, will throw error (CPP guard: @#if HAVE_STATX@)" #-}+creationTimeHiResX _ = error "creationTimeHiResX not available on this platform"+{-# WARNING statusChangeTimeHiResX "statusChangeTimeHiResX: not available on this platform, will throw error (CPP guard: @#if HAVE_STATX@)" #-}+statusChangeTimeHiResX _ = error "statusChangeTimeHiResX not available on this platform"+{-# WARNING modificationTimeHiResX "modificationTimeHiResX: not available on this platform, will throw error (CPP guard: @#if HAVE_STATX@)" #-}+modificationTimeHiResX _ = error "modificationTimeHiResX not available on this platform"+{-# WARNING fileCompressedX "fileCompressedX: not available on this platform, will default to 'False' (CPP guard: @#if HAVE_STATX@)" #-}+fileCompressedX _ = False+{-# WARNING fileImmutableX "fileImmutableX: not available on this platform, will default to 'False' (CPP guard: @#if HAVE_STATX@)" #-}+fileImmutableX _ = False+{-# WARNING fileAppendX "fileAppendX: not available on this platform, will default to 'False' (CPP guard: @#if HAVE_STATX@)" #-}+fileAppendX _ = False+{-# WARNING fileNoDumpX "fileNoDumpX: not available on this platform, will default to 'False' (CPP guard: @#if HAVE_STATX@)" #-}+fileNoDumpX _ = False+{-# WARNING fileEncryptedX "fileEncryptedX: not available on this platform, will default to 'False' (CPP guard: @#if HAVE_STATX@)" #-}+fileEncryptedX _ = False+{-# WARNING fileVerityX "fileVerityX: not available on this platform, will default to 'False' (CPP guard: @#if HAVE_STATX@)" #-}+fileVerityX _ = False+{-# WARNING fileDaxX "fileDaxX: not available on this platform, will default to 'False' (CPP guard: @#if HAVE_STATX@)" #-}+fileDaxX _ = False+#endif++data {-# CTYPE "struct statx" #-} CStatx++#ifdef HAVE_STATX+foreign import capi unsafe "sys/stat.h statx"+ c_statx :: CInt -> CFilePath -> CInt -> CInt -> Ptr CStatx -> IO CInt++#ifdef HAVE_SYS_SYSMACROS_H+foreign import capi unsafe "sys/sysmacros.h makedev"+ c_makedev :: CUInt -> CUInt -> IO CDev+#endif+#endif+++getExtendedFileStatus_ :: Maybe Fd -- ^ Optional directory file descriptor+ -> CString -- ^ Pathname to open+ -> StatxFlags+ -> StatxMask+ -> IO ExtendedFileStatus+#ifdef HAVE_STATX+getExtendedFileStatus_ fdMay str (StatxFlags flags) (StatxMask masks) = do+ fp <- mallocForeignPtrBytes (#const sizeof(struct statx))+ withForeignPtr fp $ \p ->+ throwErrnoIfMinus1_ "getExtendedFileStatus_" (c_statx c_fd str flags masks p)+ return (ExtendedFileStatus fp)+ where+ c_fd = maybe (#const AT_FDCWD) (\ (Fd fd) -> fd) fdMay+#else+{-# WARNING getExtendedFileStatus_ "operation will throw 'IOError' \"unsupported operation\" (CPP guard: @#if HAVE_STATX@)" #-}+getExtendedFileStatus_ _ _ _ _ = ioError (ioeSetLocation unsupportedOperation "getExtendedFileStatus")+#endif+
System/Posix/Files/PosixString.hsc view
@@ -1,4 +1,5 @@ {-# LANGUAGE CApiFFI #-}+{-# LANGUAGE PatternSynonyms #-} ----------------------------------------------------------------------------- -- |@@ -58,6 +59,69 @@ isBlockDevice, isCharacterDevice, isNamedPipe, isRegularFile, isDirectory, isSymbolicLink, isSocket, + -- * Extended file status+ ExtendedFileStatus(..),+ CAttributes(..),+ -- ** Obtaining extended file status+ getExtendedFileStatus,+ -- ** Flags+ StatxFlags(..),+ defaultStatxFlags,+ pattern EmptyPath,+ pattern NoAutoMount,+ pattern SymlinkNoFollow,+ pattern SyncAsStat,+ pattern ForceSync,+ pattern DontSync,+ -- ** Mask+ StatxMask(..),+ defaultStatxMask,+ pattern StatxType,+ pattern StatxMode,+ pattern StatxNlink,+ pattern StatxUid,+ pattern StatxGid,+ pattern StatxAtime,+ pattern StatxMtime,+ pattern StatxCtime,+ pattern StatxIno,+ pattern StatxSize,+ pattern StatxBlocks,+ pattern StatxBasicStats,+ pattern StatxBtime,+ pattern StatxMntId,+ pattern StatxAll,+ -- ** Querying extended file status+ fileBlockSizeX,+ linkCountX,+ fileOwnerX,+ fileGroupX,+ fileModeX,+ fileIDX,+ fileSizeX,+ fileBlocksX,+ accessTimeHiResX,+ creationTimeHiResX,+ statusChangeTimeHiResX,+ modificationTimeHiResX,+ deviceIDX,+ specialDeviceIDX,+ mountIDX,+ fileCompressedX,+ fileImmutableX,+ fileAppendX,+ fileNoDumpX,+ fileEncryptedX,+ fileVerityX,+ fileDaxX,+ isBlockDeviceX,+ isCharacterDeviceX,+ isNamedPipeX,+ isRegularFileX,+ isDirectoryX,+ isSymbolicLinkX,+ isSocketX,+ -- * Creation createNamedPipe, createDevice,@@ -102,7 +166,8 @@ throwErrnoPathIfMinus1_ ) import System.OsPath.Types-import System.Posix.Files hiding (getFileStatus, getSymbolicLinkStatus, createNamedPipe, createDevice, createLink, removeLink, createSymbolicLink, readSymbolicLink, rename, setOwnerAndGroup, setSymbolicLinkOwnerAndGroup, setFileTimes, setSymbolicLinkTimesHiRes, touchFile, touchSymbolicLink, setFileSize, getPathVar, setFileMode, fileAccess, fileExist, setFdTimesHiRes, setFileTimesHiRes)+import System.Posix.Files hiding (getFileStatus, getExtendedFileStatus, getSymbolicLinkStatus, createNamedPipe, createDevice, createLink, removeLink, createSymbolicLink, readSymbolicLink, rename, setOwnerAndGroup, setSymbolicLinkOwnerAndGroup, setFileTimes, setSymbolicLinkTimesHiRes, touchFile, touchSymbolicLink, setFileSize, getPathVar, setFileMode, fileAccess, fileExist, setFdTimesHiRes, setFileTimesHiRes)+import System.Posix.Files.Common (getExtendedFileStatus_) import System.Posix.PosixPath.FilePath import Data.Time.Clock.POSIX (POSIXTime)@@ -181,6 +246,25 @@ withFilePath path $ \s -> throwErrnoPathIfMinus1Retry_ "getFileStatus" path (c_stat s p) return (Common.FileStatus fp)++-- | Gets extended file status information.+--+-- The target file to open is identified in one of the following ways:+--+-- - If @pathname@ begins with a slash, then it is an absolute pathname that identifies the target file. In this case, @dirfd@ is ignored+-- - If @pathname@ is a string that begins with a character other than a slash and @dirfd@ is a file descriptor that refers to a+-- directory, then pathname is a relative pathname that is interpreted relative to the directory referred to by dirfd.+-- (See @openat(2)@ for an explanation of why this is useful.)+-- - If @pathname@ is an empty string and the 'EmptyPath' flag is specified in flags (see below), then the target file is+-- the one referred to by the file descriptor @dirfd@.+--+-- Note: calls @statx@.+getExtendedFileStatus :: Maybe Fd -- ^ Optional directory file descriptor (@dirfd@)+ -> PosixPath -- ^ @pathname@ to open+ -> StatxFlags -- ^ flags+ -> StatxMask -- ^ mask+ -> IO ExtendedFileStatus+getExtendedFileStatus mfd path flags masks = withFilePath path $ \s -> getExtendedFileStatus_ mfd s flags masks -- | Acts as 'getFileStatus' except when the 'PosixPath' refers to a symbolic -- link. In that case the @FileStatus@ information of the symbolic link itself
cbits/HsUnix.c view
@@ -8,6 +8,8 @@ #include "HsUnix.h" +char **__hsunix_get_environ (void) {return environ;}+ #ifdef HAVE_RTLDNEXT void *__hsunix_rtldNext (void) {return RTLD_NEXT;} #endif
changelog.md view
@@ -1,5 +1,11 @@ # Changelog for [`unix` package](http://hackage.haskell.org/package/unix) +## 2.8.3.0 *Oct 2023*++ * add `getExtendedFileStatus` (based on `statx`) style functions+ * drop support for GHC < 8.6+ * Don't `foreign import` `environ`, see [GHC #24011](https://gitlab.haskell.org/ghc/ghc/-/issues/24011)+ ## 2.8.2.1 *Sep 2023* * Fix UB bug in `withFilePath` that causes it to error out (introduced in 2.8.2.0) wrt [#295](https://github.com/haskell/unix/issues/295)
configure view
@@ -4039,6 +4039,18 @@ done +for ac_header in sys/sysmacros.h+do :+ ac_fn_c_check_header_mongrel "$LINENO" "sys/sysmacros.h" "ac_cv_header_sys_sysmacros_h" "$ac_includes_default"+if test "x$ac_cv_header_sys_sysmacros_h" = xyes; then :+ cat >>confdefs.h <<_ACEOF+#define HAVE_SYS_SYSMACROS_H 1+_ACEOF++fi++done+ for ac_header in bsd/libutil.h libutil.h pty.h utmp.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`@@ -4253,6 +4265,23 @@ if test "x$ac_cv_type_struct_rlimit" = xyes; then : $as_echo "#define HAVE_STRUCT_RLIMIT 1" >>confdefs.h++fi+++# check for statx+ac_fn_c_check_func "$LINENO" "statx" "ac_cv_func_statx"+if test "x$ac_cv_func_statx" = xyes; then :++$as_echo "#define HAVE_STATX_FUN 1" >>confdefs.h++fi++ac_fn_c_check_type "$LINENO" "struct statx" "ac_cv_type_struct_statx" "#include <sys/stat.h>+"+if test "x$ac_cv_type_struct_statx" = xyes; then :++$as_echo "#define HAVE_STRUCT_STATX 1" >>confdefs.h fi
configure.ac view
@@ -23,6 +23,7 @@ AC_CHECK_HEADERS([dirent.h fcntl.h grp.h limits.h pwd.h signal.h string.h]) AC_CHECK_HEADERS([sys/resource.h sys/stat.h sys/times.h sys/time.h]) AC_CHECK_HEADERS([sys/utsname.h sys/wait.h])+AC_CHECK_HEADERS([sys/sysmacros.h]) AC_CHECK_HEADERS([bsd/libutil.h libutil.h pty.h utmp.h]) AC_CHECK_HEADERS([termios.h time.h unistd.h utime.h]) @@ -45,6 +46,10 @@ AC_CHECK_FUNCS([chown fchdir fchmod]) AC_CHECK_TYPE([struct rlimit],[AC_DEFINE([HAVE_STRUCT_RLIMIT],[1],[HAVE_STRUCT_RLIMIT])],[],[#include <sys/resource.h>])++# check for statx+AC_CHECK_FUNC([statx], [AC_DEFINE([HAVE_STATX_FUN],[1],[HAVE_STATX_FUN])],[],[#include <sys/stat.h>])+AC_CHECK_TYPE([struct statx],[AC_DEFINE([HAVE_STRUCT_STATX],[1],[HAVE_STRUCT_STATX])],[],[#include <sys/stat.h>]) AC_MSG_CHECKING(for F_GETLK from fcntl.h) AC_EGREP_CPP(yes,
include/HsUnix.h view
@@ -90,6 +90,9 @@ #ifdef HAVE_SIGNAL_H #include <signal.h> #endif+#ifdef HAVE_SYS_SYSMACROS_H+#include <sys/sysmacros.h>+#endif /* defined in rts/posix/Signals.c */ extern HsInt nocldstop;@@ -121,6 +124,10 @@ #ifdef HAVE_CLOCKS_PER_SEC clock_t __hsunix_clocks_per_second (void);+#endif++#if defined(HAVE_SYS_STAT_H) && defined(HAVE_STATX_FUN) && defined(HAVE_STRUCT_STATX)+#define HAVE_STATX 1 #endif #endif
include/HsUnixConfig.h.in view
@@ -306,6 +306,9 @@ /* Define to 1 if you have the <signal.h> header file. */ #undef HAVE_SIGNAL_H +/* HAVE_STATX_FUN */+#undef HAVE_STATX_FUN+ /* Define to 1 if you have the <stdint.h> header file. */ #undef HAVE_STDINT_H @@ -321,6 +324,9 @@ /* HAVE_STRUCT_RLIMIT */ #undef HAVE_STRUCT_RLIMIT +/* HAVE_STRUCT_STATX */+#undef HAVE_STRUCT_STATX+ /* Define to 1 if `st_atim' is a member of `struct stat'. */ #undef HAVE_STRUCT_STAT_ST_ATIM @@ -380,6 +386,9 @@ /* Define to 1 if you have the <sys/stat.h> header file. */ #undef HAVE_SYS_STAT_H++/* Define to 1 if you have the <sys/sysmacros.h> header file. */+#undef HAVE_SYS_SYSMACROS_H /* Define to 1 if you have the <sys/times.h> header file. */ #undef HAVE_SYS_TIMES_H
+ tests/FileExtendedStatus.hs view
@@ -0,0 +1,166 @@+{-# OPTIONS_GHC -fno-warn-missing-signatures #-}++module FileExtendedStatus (main) where++import System.Posix.Files+import System.Posix.Directory+import System.Posix.IO+import System.Posix.Types+import Control.Exception as E+import Control.Monad+import Test.Tasty.HUnit++main = do+ cleanup+ fs <- testRegular+ ds <- testDir+ testSymlink fs ds+ testLink+ cleanup++regular = "regular"+dir = "dir"+slink_regular = "link-regular-symlink"+hlink_regular = "link-regular-hardlink"+link_dir = "link-dir"++testRegular = do+ _ <- createFile regular ownerReadMode+ (fs, _) <- getStatus regular+ let expected = (False,False,False,True,False,False,False)+ actual = snd (statusExtendedElements fs)+ when (actual /= expected) $+ fail "unexpected file status bits for regular file"+ return fs++testDir = do+ createDirectory dir ownerReadMode+ (ds, _) <- getStatus dir+ let expected = (False,False,False,False,True,False,False)+ actual = snd (statusExtendedElements ds)+ when (actual /= expected) $+ fail "unexpected file status bits for directory"+ return ds++testSymlink fs ds = do+ createSymbolicLink regular slink_regular+ createSymbolicLink dir link_dir+ (fs', ls) <- getStatus slink_regular+ (ds', lds) <- getStatus link_dir++ let expected = (False,False,False,False,False,True,False)+ actualF = snd (statusExtendedElements ls)+ actualD = snd (statusExtendedElements lds)++ when (actualF /= expected) $+ fail "unexpected file status bits for symlink to regular file"++ when (actualD /= expected) $+ fail "unexpected file status bits for symlink to directory"++ when (statusExtendedElements fs /= statusExtendedElements fs') $+ fail "status for a file does not match when it's accessed via a symlink"++ when (statusExtendedElements ds /= statusExtendedElements ds') $+ fail "status for a directory does not match when it's accessed via a symlink"+++testLink = do+ createLink regular hlink_regular+ (fs, _) <- getStatus regular -- we need to retrieve it again as creating the link causes it to change!+ (fs', ls) <- getStatus hlink_regular+ snd (statusExtendedElements ls) @?= (+ False, -- isBlockDevice+ False, -- isCharacterDevice+ False, -- isNamedPipe+ True, -- isRegularFile+ False, -- isDirectory+ False, -- isSymbolicLink+ False) -- isSocket+ linkCountX fs' @?= 2+ statusExtendedElements fs @?= statusExtendedElements fs' -- status for a file should match when accessed via a link+++cleanup = do+ ignoreIOExceptions $ removeDirectory dir+ mapM_ (ignoreIOExceptions . removeLink)+ [regular, hlink_regular, slink_regular, link_dir]++ignoreIOExceptions io = io `E.catch`+ ((\_ -> return ()) :: IOException -> IO ())++getStatus f = do+ fs <- getExtendedFileStatus Nothing f defaultStatxFlags defaultStatxMask+ ls <- getExtendedFileStatus Nothing f SymlinkNoFollow defaultStatxMask+ fs' <- getFileStatus f++ statusExtendedElementsMinimal fs @?= statusElementsMinimal fs'++ return (fs, ls)++-- Yay for 20-element tuples!+statusExtendedElements fs = (,)+ (fileBlockSizeX fs+ ,linkCountX fs+ ,fileOwnerX fs+ ,fileGroupX fs+ ,fileModeX fs+ ,fileIDX fs+ ,fileSizeX fs+ ,accessTimeHiResX fs+ ,creationTimeHiResX fs+ ,statusChangeTimeHiResX fs+ ,modificationTimeHiResX fs+ )+ (isBlockDeviceX fs+ ,isCharacterDeviceX fs+ ,isNamedPipeX fs+ ,isRegularFileX fs+ ,isDirectoryX fs+ ,isSymbolicLinkX fs+ ,isSocketX fs+ )++statusExtendedElementsMinimal fs = (,)+ (fileModeX fs+ ,deviceIDX fs+ ,specialDeviceIDX fs+ ,linkCountX fs+ ,fileOwnerX fs+ ,fileGroupX fs+ ,COff (fromIntegral (fileSizeX fs))+ ,Just $ CBlkCnt (fromIntegral (fileBlocksX fs))+ ,accessTimeHiResX fs+ ,statusChangeTimeHiResX fs+ ,modificationTimeHiResX fs+ )+ (isBlockDeviceX fs+ ,isCharacterDeviceX fs+ ,isNamedPipeX fs+ ,isRegularFileX fs+ ,isDirectoryX fs+ ,isSymbolicLinkX fs+ ,isSocketX fs+ )++statusElementsMinimal fs = (,)+ (fileMode fs+ ,deviceID fs+ ,specialDeviceID fs+ ,linkCount fs+ ,fileOwner fs+ ,fileGroup fs+ ,fileSize fs+ ,fileBlocks fs+ ,accessTimeHiRes fs+ ,statusChangeTimeHiRes fs+ ,modificationTimeHiRes fs+ )+ (isBlockDevice fs+ ,isCharacterDevice fs+ ,isNamedPipe fs+ ,isRegularFile fs+ ,isDirectory fs+ ,isSymbolicLink fs+ ,isSocket fs+ )
− tests/Test.hs
@@ -1,283 +0,0 @@-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE CPP #-}--{-# OPTIONS_GHC -fno-warn-deprecations #-}-{-# OPTIONS_GHC -fno-warn-unused-imports #-}--module Main (main) where--import Control.Applicative-import Control.Concurrent-import qualified Control.Exception as E-import Control.Monad-import qualified Data.ByteString as B-import qualified Data.ByteString.Short as Sh-import Data.List (sort)-import System.Exit-import System.IO-import System.OsString.Internal.Types-import System.Posix-import qualified System.Posix.Env.ByteString as ByteString-import qualified System.Posix.ByteString.FilePath as BSFP-import qualified System.Posix.PosixPath.FilePath as PPFP-import Test.Tasty-import Test.Tasty.HUnit-import Test.Tasty.QuickCheck--import qualified FileStatus-import qualified FileStatusByteString-import qualified Signals001--main :: IO ()-main = defaultMain $ testGroup "All"- [ testGroup "Common" -- common to JS and native platforms- [ fileExist01- ]-#if !defined(javascript_HOST_ARCH)- , testGroup "Native"- [ executeFile001 -- JS: missing "pipe"- , fileStatus -- JS: missing "openat"- , fileStatusByteString -- JS: missing "openat"- , getEnvironment01 -- JS: missing "environ"- , testSystemPosixEnvByteString -- JS: missing "environ"- , getGroupEntry -- JS: missing "sysconf"- , getUserEntry -- JS: missing "sysconf"- , processGroup001 -- JS: missing "getpgrp"- , processGroup002 -- JS: missing "getppid"- , queryFdOption01 -- JS: unimplemented "fcntl_read/write/lock"- , signals001 -- JS: missing "sigismember"- , t1185 -- JS: missing "pipe"- , t3816 -- JS: missing "setgrent"- , user001 -- JS: missing "getuid"- , posix002 -- JS: missing "pipe"- , posix005 -- JS: missing "environ"- , posix006 -- JS: missing "time"- , posix010 -- JS: missing "sysconf"- ]-#endif- , testWithFilePath- ]--executeFile001 :: TestTree-executeFile001 = testCase "executeFile001" $ do- actual <- captureStdout $- executeFile "echo" True ["arg1", "ar g2"] Nothing- actual @?= "arg1 ar g2\n"--fileExist01 :: TestTree-fileExist01 = testCase "fileExist01" $ do- fileExist "."- @? "file should exist"- not <$> fileExist "does not exist"- @? "file should not exist"--fileStatus :: TestTree-fileStatus = testCase "fileStatus" FileStatus.main--fileStatusByteString :: TestTree-fileStatusByteString = testCase "fileStatusByteString" FileStatusByteString.main--getEnvironment01 :: TestTree-getEnvironment01 = testCase "getEnvironment01" $ do- env <- getEnvironment- not (null env)- @? "environment should be non-empty"--protectEnvironment :: IO a -> IO a-protectEnvironment action = E.bracket ByteString.getEnvironment ByteString.setEnvironment $ \ _ -> action--testSystemPosixEnvByteString :: TestTree-testSystemPosixEnvByteString =- testGroup "System.Posix.Env.ByteString" [- testGroup "getEnvironment" [- testCase "returns the environment" $ do- env <- ByteString.getEnvironment- not (null env)- @? "environment should be non-empty"- ]- , testGroup "clearEnv" [- testCase "clears the environment" $ protectEnvironment $ do- ByteString.clearEnv- ByteString.getEnvironment >>= (@?= [])- ]- ]--getGroupEntry :: TestTree-getGroupEntry = testCase "getGroupEntry" $ do- let act = False <$ getGroupEntryForName "thisIsNotMeantToExist"- act `E.catch` (\(_ :: E.SomeException) -> return True)- @? "group should not exist"--getUserEntry :: TestTree-getUserEntry = testCase "getUserEntry" $ do- let act = False <$ getUserEntryForName "thisIsNotMeantToExist"- act `E.catch` (\(_ :: E.SomeException) -> return True)- @? "user should not exist"--processGroup001 :: TestTree-processGroup001 = testCase "processGroup001" $ do- pgid <- getProcessGroupID- pgid' <- getProcessGroupIDOf =<< getProcessID- pgid @?= pgid'--processGroup002 :: TestTree-processGroup002 = testCase "processGroup002" $ do- pid <- getProcessID- ppid <- getParentProcessID- ppgid <- getProcessGroupIDOf ppid- -- join the parent process- joinProcessGroup ppgid- pgid1 <- getProcessGroupID- ppgid @?= pgid1- -- be a leader- _ <- createProcessGroupFor pid- pgid2 <- getProcessGroupID- pid @?= fromIntegral pgid2- -- and join the parent again- setProcessGroupIDOf pid ppgid- pgid3 <- getProcessGroupID- ppgid @?= pgid3--queryFdOption01 :: TestTree-queryFdOption01 = testCase "queryFdOption01" $ do- not <$> queryFdOption stdOutput NonBlockingRead- @? "should be blocking"- setFdOption stdOutput NonBlockingRead True- queryFdOption stdOutput NonBlockingRead- @? "should be non-blocking"--signals001 :: TestTree-signals001 = testCase "signals001" Signals001.main--t1185 :: TestTree-t1185 = testCase "T1185" $ do- (stdinr, stdinw) <- createPipe- (stdoutr, stdoutw) <- createPipe- pid <- forkProcess $ do- hw <- fdToHandle stdoutw- hr <- fdToHandle stdinr- closeFd stdinw- hGetContents hr >>= hPutStr hw- hClose hr- hClose hw- exitImmediately ExitSuccess- threadDelay 100000- closeFd stdoutw- closeFd stdinw- hr2 <- fdToHandle stdoutr- hGetContents hr2 >>= putStr- actual <- getProcessStatus True False pid- actual @?= Just (Exited ExitSuccess)--t3816 :: TestTree-t3816 = testCase "T3816" $ do- not . null <$> getAllGroupEntries- @? "should be non-empty"- not . null <$> getAllGroupEntries- @? "should be non-empty"--user001 :: TestTree-user001 = testCase "user001" $ do- let force act = do- x <- act- x @?= x- force getRealUserID- force getRealUserID- force getRealGroupID- force getEffectiveUserID- force getEffectiveGroupID- force getGroups- force getEffectiveUserName- force $ getRealGroupID >>= getGroupEntryForID- force $ getRealGroupID >>= getGroupEntryForID >>= getGroupEntryForName . groupName- force getAllGroupEntries- force $ getRealUserID >>= getUserEntryForID- force getAllUserEntries--posix002 :: TestTree-posix002 = testCase "posix002" $ do- actual <- captureStdout $- executeFile "printenv" True [] (Just [("ONE","1"),("TWO","2")])- sort (lines actual) @?= ["ONE=1", "TWO=2"]--posix005 :: TestTree-posix005 = testCase "posix005" $ protectEnvironment $ do- hSetBuffering stdout NoBuffering-- setEnvironment [("one","1"),("two","2")]- env0 <- getEnvironment- sort env0 @?= [("one","1"),("two","2")]-- setEnv "foo" "bar" True- env1 <- getEnvironment- sort env1 @?= [("foo","bar"),("one","1"),("two","2")]-- setEnv "foo" "baz" True- env2 <- getEnvironment- sort env2 @?= [("foo","baz"),("one","1"),("two","2")]-- setEnv "fu" "bar" True- env3 <- getEnvironment- sort env3 @?= [("foo","baz"),("fu","bar"),("one","1"),("two","2")]-- unsetEnv "foo"- env4 <- getEnvironment- sort env4 @?= [("fu","bar"),("one","1"),("two","2")]-- clearEnv- env5 <- getEnvironment- sort env5 @?= []--posix006 :: TestTree-posix006 = testCase "posix006" $ do- start <- epochTime- blockSignals reservedSignals -- see #4504- _ <- sleep 1- finish <- epochTime- let slept = finish - start- (slept >= 1 && slept <= 2)- @? "should have slept between 1 and 2"--posix010 :: TestTree-posix010 = testCase "posix010" $ do- root <- getUserEntryForName "root"- userName root @?= "root"- userID root @?= 0- userGroupID root @?= 0-- root' <- getUserEntryForID (userID root)- userName root' @?= "root"- userID root' @?= 0- userGroupID root' @?= 0-- homeDirectory root @?= homeDirectory root'--testWithFilePath :: TestTree-testWithFilePath =- adjustOption (\(QuickCheckTests n) -> QuickCheckTests (n `max` 10000)) $- testGroup "withFilePath"- [ testProperty "ByteString" $- \xs -> let ys = B.pack $ filter (/= 0) xs in- ioProperty $ BSFP.withFilePath ys- (\ptr -> (=== ys) <$> B.packCString ptr)- , testProperty "PosixPath" $- \xs -> let ys = Sh.pack $ filter (/= 0) xs in- ioProperty $ PPFP.withFilePath (PosixString ys)- (\ptr -> (=== ys) <$> Sh.packCString ptr)- ]------------------------------------------------------------------------------------ Utils--captureStdout :: IO () -> IO String-captureStdout = captureFd stdOutput--captureFd :: Fd -> IO () -> IO String-captureFd fd act = do- (dRead, dWrite) <- createPipe- _ <- forkProcess $ do- _ <- dupTo dWrite fd- act- closeFd dWrite- handle <- fdToHandle dRead- hGetContents handle
+ tests/Test.hsc view
@@ -0,0 +1,292 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE CPP #-}++{-# OPTIONS_GHC -fno-warn-deprecations #-}+{-# OPTIONS_GHC -fno-warn-unused-imports #-}++#include "HsUnix.h"++module Main (main) where++import Control.Applicative+import Control.Concurrent+import qualified Control.Exception as E+import Control.Monad+import qualified Data.ByteString as B+import qualified Data.ByteString.Short as Sh+import Data.List (sort)+import System.Exit+import System.IO+import System.OsString.Internal.Types+import System.Posix+import qualified System.Posix.Env.ByteString as ByteString+import qualified System.Posix.ByteString.FilePath as BSFP+import qualified System.Posix.PosixPath.FilePath as PPFP+import Test.Tasty+import Test.Tasty.HUnit+import Test.Tasty.QuickCheck++import qualified FileStatus+import qualified FileExtendedStatus+import qualified FileStatusByteString+import qualified Signals001++main :: IO ()+main = defaultMain $ testGroup "All"+ [ testGroup "Common" -- common to JS and native platforms+ [ fileExist01+ ]+#if !defined(javascript_HOST_ARCH)+ , testGroup "Native"+ [ executeFile001 -- JS: missing "pipe"+ , fileStatus -- JS: missing "openat"+#ifdef HAVE_STATX+ , fileExtendedStatus -- JS: missing "openat"+#endif+ , fileStatusByteString -- JS: missing "openat"+ , getEnvironment01 -- JS: missing "environ"+ , testSystemPosixEnvByteString -- JS: missing "environ"+ , getGroupEntry -- JS: missing "sysconf"+ , getUserEntry -- JS: missing "sysconf"+ , processGroup001 -- JS: missing "getpgrp"+ , processGroup002 -- JS: missing "getppid"+ , queryFdOption01 -- JS: unimplemented "fcntl_read/write/lock"+ , signals001 -- JS: missing "sigismember"+ , t1185 -- JS: missing "pipe"+ , t3816 -- JS: missing "setgrent"+ , user001 -- JS: missing "getuid"+ , posix002 -- JS: missing "pipe"+ , posix005 -- JS: missing "environ"+ , posix006 -- JS: missing "time"+ , posix010 -- JS: missing "sysconf"+ ]+#endif+ , testWithFilePath+ ]++executeFile001 :: TestTree+executeFile001 = testCase "executeFile001" $ do+ actual <- captureStdout $+ executeFile "echo" True ["arg1", "ar g2"] Nothing+ actual @?= "arg1 ar g2\n"++fileExist01 :: TestTree+fileExist01 = testCase "fileExist01" $ do+ fileExist "."+ @? "file should exist"+ not <$> fileExist "does not exist"+ @? "file should not exist"++fileStatus :: TestTree+fileStatus = testCase "fileStatus" FileStatus.main++fileExtendedStatus :: TestTree+fileExtendedStatus = testCase "fileExtendedStatus" FileExtendedStatus.main++fileStatusByteString :: TestTree+fileStatusByteString = testCase "fileStatusByteString" FileStatusByteString.main++getEnvironment01 :: TestTree+getEnvironment01 = testCase "getEnvironment01" $ do+ env <- getEnvironment+ not (null env)+ @? "environment should be non-empty"++protectEnvironment :: IO a -> IO a+protectEnvironment action = E.bracket ByteString.getEnvironment ByteString.setEnvironment $ \ _ -> action++testSystemPosixEnvByteString :: TestTree+testSystemPosixEnvByteString =+ testGroup "System.Posix.Env.ByteString" [+ testGroup "getEnvironment" [+ testCase "returns the environment" $ do+ env <- ByteString.getEnvironment+ not (null env)+ @? "environment should be non-empty"+ ]+ , testGroup "clearEnv" [+ testCase "clears the environment" $ protectEnvironment $ do+ ByteString.clearEnv+ ByteString.getEnvironment >>= (@?= [])+ ]+ ]++getGroupEntry :: TestTree+getGroupEntry = testCase "getGroupEntry" $ do+ let act = False <$ getGroupEntryForName "thisIsNotMeantToExist"+ act `E.catch` (\(_ :: E.SomeException) -> return True)+ @? "group should not exist"++getUserEntry :: TestTree+getUserEntry = testCase "getUserEntry" $ do+ let act = False <$ getUserEntryForName "thisIsNotMeantToExist"+ act `E.catch` (\(_ :: E.SomeException) -> return True)+ @? "user should not exist"++processGroup001 :: TestTree+processGroup001 = testCase "processGroup001" $ do+ pgid <- getProcessGroupID+ pgid' <- getProcessGroupIDOf =<< getProcessID+ pgid @?= pgid'++processGroup002 :: TestTree+processGroup002 = testCase "processGroup002" $ do+ pid <- getProcessID+ ppid <- getParentProcessID+ ppgid <- getProcessGroupIDOf ppid+ -- join the parent process+ joinProcessGroup ppgid+ pgid1 <- getProcessGroupID+ ppgid @?= pgid1+ -- be a leader+ _ <- createProcessGroupFor pid+ pgid2 <- getProcessGroupID+ pid @?= fromIntegral pgid2+ -- and join the parent again+ setProcessGroupIDOf pid ppgid+ pgid3 <- getProcessGroupID+ ppgid @?= pgid3++queryFdOption01 :: TestTree+queryFdOption01 = testCase "queryFdOption01" $ do+ not <$> queryFdOption stdOutput NonBlockingRead+ @? "should be blocking"+ setFdOption stdOutput NonBlockingRead True+ queryFdOption stdOutput NonBlockingRead+ @? "should be non-blocking"++signals001 :: TestTree+signals001 = testCase "signals001" Signals001.main++t1185 :: TestTree+t1185 = testCase "T1185" $ do+ (stdinr, stdinw) <- createPipe+ (stdoutr, stdoutw) <- createPipe+ pid <- forkProcess $ do+ hw <- fdToHandle stdoutw+ hr <- fdToHandle stdinr+ closeFd stdinw+ hGetContents hr >>= hPutStr hw+ hClose hr+ hClose hw+ exitImmediately ExitSuccess+ threadDelay 100000+ closeFd stdoutw+ closeFd stdinw+ hr2 <- fdToHandle stdoutr+ hGetContents hr2 >>= putStr+ actual <- getProcessStatus True False pid+ actual @?= Just (Exited ExitSuccess)++t3816 :: TestTree+t3816 = testCase "T3816" $ do+ not . null <$> getAllGroupEntries+ @? "should be non-empty"+ not . null <$> getAllGroupEntries+ @? "should be non-empty"++user001 :: TestTree+user001 = testCase "user001" $ do+ let force act = do+ x <- act+ x @?= x+ force getRealUserID+ force getRealUserID+ force getRealGroupID+ force getEffectiveUserID+ force getEffectiveGroupID+ force getGroups+ force getEffectiveUserName+ force $ getRealGroupID >>= getGroupEntryForID+ force $ getRealGroupID >>= getGroupEntryForID >>= getGroupEntryForName . groupName+ force getAllGroupEntries+ force $ getRealUserID >>= getUserEntryForID+ force getAllUserEntries++posix002 :: TestTree+posix002 = testCase "posix002" $ do+ actual <- captureStdout $+ executeFile "printenv" True [] (Just [("ONE","1"),("TWO","2")])+ sort (lines actual) @?= ["ONE=1", "TWO=2"]++posix005 :: TestTree+posix005 = testCase "posix005" $ protectEnvironment $ do+ hSetBuffering stdout NoBuffering++ setEnvironment [("one","1"),("two","2")]+ env0 <- getEnvironment+ sort env0 @?= [("one","1"),("two","2")]++ setEnv "foo" "bar" True+ env1 <- getEnvironment+ sort env1 @?= [("foo","bar"),("one","1"),("two","2")]++ setEnv "foo" "baz" True+ env2 <- getEnvironment+ sort env2 @?= [("foo","baz"),("one","1"),("two","2")]++ setEnv "fu" "bar" True+ env3 <- getEnvironment+ sort env3 @?= [("foo","baz"),("fu","bar"),("one","1"),("two","2")]++ unsetEnv "foo"+ env4 <- getEnvironment+ sort env4 @?= [("fu","bar"),("one","1"),("two","2")]++ clearEnv+ env5 <- getEnvironment+ sort env5 @?= []++posix006 :: TestTree+posix006 = testCase "posix006" $ do+ start <- epochTime+ blockSignals reservedSignals -- see #4504+ _ <- sleep 1+ finish <- epochTime+ let slept = finish - start+ (slept >= 1 && slept <= 2)+ @? "should have slept between 1 and 2"++posix010 :: TestTree+posix010 = testCase "posix010" $ do+ root <- getUserEntryForName "root"+ userName root @?= "root"+ userID root @?= 0+ userGroupID root @?= 0++ root' <- getUserEntryForID (userID root)+ userName root' @?= "root"+ userID root' @?= 0+ userGroupID root' @?= 0++ homeDirectory root @?= homeDirectory root'++testWithFilePath :: TestTree+testWithFilePath =+ adjustOption (\(QuickCheckTests n) -> QuickCheckTests (n `max` 10000)) $+ testGroup "withFilePath"+ [ testProperty "ByteString" $+ \xs -> let ys = B.pack $ filter (/= 0) xs in+ ioProperty $ BSFP.withFilePath ys+ (\ptr -> (=== ys) <$> B.packCString ptr)+ , testProperty "PosixPath" $+ \xs -> let ys = Sh.pack $ filter (/= 0) xs in+ ioProperty $ PPFP.withFilePath (PosixString ys)+ (\ptr -> (=== ys) <$> Sh.packCString ptr)+ ]++-------------------------------------------------------------------------------+-- Utils++captureStdout :: IO () -> IO String+captureStdout = captureFd stdOutput++captureFd :: Fd -> IO () -> IO String+captureFd fd act = do+ (dRead, dWrite) <- createPipe+ _ <- forkProcess $ do+ _ <- dupTo dWrite fd+ act+ closeFd dWrite+ handle <- fdToHandle dRead+ hGetContents handle
unix.cabal view
@@ -1,6 +1,6 @@ cabal-version: 1.12 name: unix-version: 2.8.2.1+version: 2.8.3.0 -- NOTE: Don't forget to update ./changelog.md license: BSD3@@ -11,13 +11,13 @@ synopsis: POSIX functionality category: System build-type: Configure-tested-with: GHC==9.2.4,+tested-with: GHC==9.6.2,+ GHC==9.4.7,+ GHC==9.2.8, GHC==9.0.2, GHC==8.10.7, GHC==8.8.4,- GHC==8.6.5,- GHC==8.4.4,- GHC==8.2.2+ GHC==8.6.5 description: This package gives you access to the set of operating system services standardised by@@ -69,10 +69,10 @@ buildable: False build-depends:- base >= 4.10 && < 4.20,- bytestring >= 0.9.2 && < 0.13,+ base >= 4.12.0.0 && < 4.20,+ bytestring >= 0.9.2 && < 0.13, filepath >= 1.4.100.0 && < 1.5,- time >= 1.2 && < 1.13+ time >= 1.9.1 && < 1.13 exposed-modules: System.Posix@@ -167,6 +167,7 @@ main-is: Test.hs other-modules: FileStatus+ FileExtendedStatus FileStatusByteString Signals001 type: exitcode-stdio-1.0