packages feed

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 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