{-# 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 )