packages feed

libfuse3-0.1.0.0: example/null.hs

{-# LANGUAGE DisambiguateRecordFields #-}
{-# LANGUAGE NamedFieldPuns #-}
-- |
-- Copyright : (The original C)   2001-2007  Miklos Szeredi <miklos@szeredi.hu>
--             (The Haskell port) 2020 yohashi
-- License   : GPL-2
--
-- This \"filesystem\" provides only a single file. The mountpoint
-- needs to be a file rather than a directory. All writes to the
-- file will be discarded, and reading the file always returns \\0.
--
-- This is a port of the C program distributed with the official libfuse.
-- See \"example/null.c\" in the distribution.
module Main where

import Control.Exception (SomeException)
import Data.ByteString (ByteString)
import Data.List (foldl')
import Foreign.C (CInt, Errno, eIO, eNOENT, eOK)
import System.Clock (Clock(Realtime), getTime)
import System.IO (hPrint, stderr)
import System.LibFuse3 (FileStat, defaultFileStat, defaultFuseOperations, fuseMain)
import System.Posix.Files (groupReadMode, otherReadMode, ownerReadMode, ownerWriteMode, regularFileMode, unionFileModes)
import System.Posix.Types (ByteCount, FileOffset)
import System.Posix.User (getRealGroupID, getRealUserID)

import qualified Data.ByteString as B
import qualified System.LibFuse3 as Fuse

nullGetattr :: FilePath -> IO (Either Errno FileStat)
nullGetattr "/" = do
  fileOwner <- getRealUserID
  fileGroup <- getRealGroupID
  now <- getTime Realtime
  pure $ Right $ defaultFileStat
    { Fuse.fileMode = foldl' unionFileModes regularFileMode [ownerReadMode, ownerWriteMode, groupReadMode, otherReadMode]
    , Fuse.linkCount = 1
    , Fuse.fileOwner
    , Fuse.fileGroup
    , Fuse.fileSize = 2 ^ (32 :: Int) -- 4G
    , Fuse.accessTimeHiRes = now
    , Fuse.modificationTimeHiRes = now
    , Fuse.statusChangeTimeHiRes = now
    }
nullGetattr _ = pure $ Left eNOENT

nullTruncate :: FilePath -> IO Errno
nullTruncate "/" = pure eOK
nullTruncate _   = pure eNOENT

nullOpen :: FilePath -> IO (Either Errno ())
nullOpen "/" = pure $ Right ()
nullOpen _   = pure $ Left eNOENT

nullRead :: FilePath -> ByteCount -> FileOffset -> IO (Either Errno ByteString)
nullRead "/" size offset
  | offset >= 2 ^ (32 :: Int) = pure $ Right mempty
  | otherwise = pure $ Right $ B.replicate (fromIntegral size) 0
nullRead _ _ _ = pure $ Left eNOENT

nullWrite :: FilePath -> ByteString -> IO (Either Errno CInt)
nullWrite "/" buf = pure $ Right $ fromIntegral $ B.length buf
nullWrite _   _   = pure $ Left eNOENT

main :: IO ()
main = fuseMain ops (\e -> hPrint stderr (e :: SomeException) >> pure eIO)
  where
  ops = defaultFuseOperations
    { Fuse.fuseGetattr = Just $ \fp _ -> nullGetattr fp
    , Fuse.fuseTruncate = Just $ \fp _ _ -> nullTruncate fp
    , Fuse.fuseOpen = Just $ \fp _ _ -> nullOpen fp
    , Fuse.fuseRead = Just $ \fp _ sz off -> nullRead fp sz off
    , Fuse.fuseWrite = Just $ \fp _ buf _ -> nullWrite fp buf
    }