module Main where import Control.OldException import qualified Data.ByteString.Char8 as B import Foreign.C.Error import System.Directory ( getDirectoryContents ) import System.IO import System.IO.Error import System.Posix import System.Fuse type HT = Fd main :: IO () main = fuseMain bindFSOps (\e -> print e >> bindExceptionHandler e) bindExceptionHandler :: Exception -> IO Errno bindExceptionHandler (IOException ioe) | isAlreadyExistsError ioe = return eALREADY | isDoesNotExistError ioe = return eNOENT | isAlreadyInUseError ioe = return eBUSY | isFullError ioe = return eAGAIN | isEOFError ioe = return eIO | isIllegalOperation ioe = return eNOTTY | isPermissionError ioe = return ePERM | otherwise = return eFAULT bindExceptionHandler e = return eFAULT bindFSOps :: FuseOperations HT bindFSOps = defaultFuseOps { fuseGetFileStat = bindGetFileStat , fuseReadSymbolicLink = bindReadSymbolicLink , fuseCreateDevice = bindCreateDevice , fuseCreateDirectory = bindCreateDirectory , fuseRemoveLink = bindRemoveLink , fuseRemoveDirectory = bindRemoveDirectory , fuseCreateSymbolicLink = bindCreateSymbolicLink , fuseRename = bindRename , fuseCreateLink = bindCreateLink , fuseSetFileMode = bindSetFileMode , fuseSetOwnerAndGroup = bindSetOwnerAndGroup , fuseSetFileSize = bindSetFileSize , fuseSetFileTimes = bindSetFileTimes , fuseOpen = bindOpen , fuseRead = bindRead , fuseWrite = bindWrite , fuseGetFileSystemStats = bindGetFileSystemStats , fuseFlush = bindFlush , fuseRelease = bindRelease , fuseSynchronizeFile = bindSynchronizeFile , fuseOpenDirectory = bindOpenDirectory , fuseReadDirectory = bindReadDirectory } fileStatusToEntryType :: FileStatus -> EntryType fileStatusToEntryType status | isSymbolicLink status = SymbolicLink | isNamedPipe status = NamedPipe | isCharacterDevice status = CharacterSpecial | isDirectory status = Directory | isBlockDevice status = BlockSpecial | isRegularFile status = RegularFile | isSocket status = Socket | otherwise = Unknown fileStatusToFileStat :: FileStatus -> FileStat fileStatusToFileStat status = FileStat { statEntryType = fileStatusToEntryType status , statFileMode = fileMode status , statLinkCount = linkCount status , statFileOwner = fileOwner status , statFileGroup = fileGroup status , statSpecialDeviceID = specialDeviceID status , statFileSize = fileSize status -- fixme: 1024 is not always the size of a block , statBlocks = fromIntegral (fileSize status `div` 1024) , statAccessTime = accessTime status , statModificationTime = modificationTime status , statStatusChangeTime = statusChangeTime status } bindGetFileStat :: FilePath -> IO (Either Errno FileStat) bindGetFileStat path = do status <- getSymbolicLinkStatus path return $ Right $ fileStatusToFileStat status bindReadSymbolicLink :: FilePath -> IO (Either Errno FilePath) bindReadSymbolicLink path = do target <- readSymbolicLink path return (Right target) bindOpenDirectory :: FilePath -> IO Errno bindOpenDirectory path = do openDirStream path >>= closeDirStream return eOK bindReadDirectory :: FilePath -> IO (Either Errno [(FilePath, FileStat)]) bindReadDirectory path = do names <- getDirectoryContents path mapM pairType names >>= return . Right where pairType name = do status <- getSymbolicLinkStatus (path ++ "/" ++ name) return (name, fileStatusToFileStat status) bindCreateDevice :: FilePath -> EntryType -> FileMode -> DeviceID -> IO Errno bindCreateDevice path entryType mode dev = do let combinedMode = entryTypeToFileMode entryType `unionFileModes` mode createDevice path combinedMode dev return eOK bindCreateDirectory :: FilePath -> FileMode -> IO Errno bindCreateDirectory path mode = do createDirectory path mode return eOK bindRemoveLink :: FilePath -> IO Errno bindRemoveLink path = do removeLink path return eOK bindRemoveDirectory :: FilePath -> IO Errno bindRemoveDirectory path = do removeDirectory path return eOK bindCreateSymbolicLink :: FilePath -> FilePath -> IO Errno bindCreateSymbolicLink src dest = do createSymbolicLink src dest return eOK bindRename :: FilePath -> FilePath -> IO Errno bindRename src dest = do rename src dest return eOK bindCreateLink :: FilePath -> FilePath -> IO Errno bindCreateLink src dest = do createLink src dest return eOK bindSetFileMode :: FilePath -> FileMode -> IO Errno bindSetFileMode path mode = do setFileMode path mode return eOK bindSetOwnerAndGroup :: FilePath -> UserID -> GroupID -> IO Errno bindSetOwnerAndGroup path uid gid = do setOwnerAndGroup path uid gid return eOK bindSetFileSize :: FilePath -> FileOffset -> IO Errno bindSetFileSize path off = do setFileSize path off return eOK bindSetFileTimes :: FilePath -> EpochTime -> EpochTime -> IO Errno bindSetFileTimes path accessTime modificationTime = do setFileTimes path accessTime modificationTime return eOK bindOpen :: FilePath -> OpenMode -> OpenFileFlags -> IO (Either Errno HT) bindOpen path mode flags = do fd <- openFd path mode Nothing flags return (Right fd) bindRead :: FilePath -> HT -> ByteCount -> FileOffset -> IO (Either Errno B.ByteString) bindRead path fd count off = do newOff <- fdSeek fd AbsoluteSeek off if off /= newOff then do return (Left eINVAL) else do (content, bytesRead) <- fdRead fd count return (Right $ B.pack content) bindWrite :: FilePath -> HT -> B.ByteString -> FileOffset -> IO (Either Errno ByteCount) bindWrite path fd buf off = do newOff <- fdSeek fd AbsoluteSeek off if off /= newOff then do return (Left eINVAL) else do res <- fdWrite fd $ B.unpack buf return (Right res) bindGetFileSystemStats :: String -> IO (Either Errno FileSystemStats) bindGetFileSystemStats _ = return (Left eOK) bindFlush :: FilePath -> HT -> IO Errno bindFlush _ _ = return eOK bindRelease :: FilePath -> HT -> IO () bindRelease _ fd = closeFd fd bindSynchronizeFile :: FilePath -> SyncType -> IO Errno bindSynchronizeFile _ _ = return eOK