packages feed

libfuse3 0.1.1.1 → 0.1.2.0

raw patch · 11 files changed

+208/−62 lines, 11 filesdep +criteriondep ~basedep ~bytestringdep ~timePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: criterion

Dependency ranges changed: base, bytestring, time, unix

API changes (from Hackage documentation)

- System.LibFuse3.Internal: _dummy :: StablePtr a -> b
+ System.LibFuse3: defaultExceptionHandler :: ExceptionHandler SomeException
+ System.LibFuse3: type ExceptionHandler e = e -> IO Errno
+ System.LibFuse3.Internal: defaultExceptionHandler :: ExceptionHandler SomeException
+ System.LibFuse3.Internal: type ExceptionHandler e = e -> IO Errno
+ System.LibFuse3.Utils: c_pread :: CInt -> Ptr a -> CSize -> COff -> IO CSsize
+ System.LibFuse3.Utils: c_pwrite :: CInt -> Ptr a -> CSize -> COff -> IO CSsize
+ System.LibFuse3.Utils: pread :: Fd -> ByteCount -> FileOffset -> IO ByteString
+ System.LibFuse3.Utils: pwrite :: Fd -> ByteString -> FileOffset -> IO CSsize
+ System.LibFuse3.Utils: throwErrnoOf :: String -> Errno -> IO a
+ System.LibFuse3.Utils: tryErrno' :: IO a -> IO (Either Errno a)
+ System.LibFuse3.Utils: tryErrno_' :: IO a -> IO Errno
- System.LibFuse3: fuseMain :: Exception e => FuseOperations fh dh -> (e -> IO Errno) -> IO ()
+ System.LibFuse3: fuseMain :: Exception e => FuseOperations fh dh -> ExceptionHandler e -> IO ()
- System.LibFuse3.Internal: fuseMain :: Exception e => FuseOperations fh dh -> (e -> IO Errno) -> IO ()
+ System.LibFuse3.Internal: fuseMain :: Exception e => FuseOperations fh dh -> ExceptionHandler e -> IO ()
- System.LibFuse3.Internal: fuseRun :: Exception e => String -> [String] -> FuseOperations fh dh -> (e -> IO Errno) -> IO a
+ System.LibFuse3.Internal: fuseRun :: Exception e => String -> [String] -> FuseOperations fh dh -> ExceptionHandler e -> IO a
- System.LibFuse3.Internal: resCFuseOperations :: forall fh dh e. Exception e => FuseOperations fh dh -> (e -> IO Errno) -> ResourceT IO (Ptr FuseOperations)
+ System.LibFuse3.Internal: resCFuseOperations :: forall fh dh e. Exception e => FuseOperations fh dh -> ExceptionHandler e -> ResourceT IO (Ptr FuseOperations)

Files

CHANGELOG.md view
@@ -1,5 +1,12 @@ # Revision history for libfuse3 +## 0.1.2.0 -- 2020-11-09++* Added `throwErrnoOf`, `tryErrno'` and `tryErrno_'` to `System.Libfuse3.Utils` (#5)+* Added `ExceptionHandler` and `defaultExceptionHandler` (#6)+* Fixed a bug in `resCFuseOperations` to prevent Haskell exceptions from escaping to C land (#7)+* Added `pread` and `pwrite` to `System.Libfuse3.Utils` (#8)+ ## 0.1.1.1 -- 2020-10-06  * Minor improvements on the documentations
+ bench/microbench/Main.hs view
@@ -0,0 +1,40 @@+module Main where++import Control.Exception (bracket, onException)+import Criterion.Main (bench, bgroup, defaultMain, nfIO)+import Data.ByteString (ByteString)+import Foreign (allocaBytes, free, mallocBytes)+import System.LibFuse3.Utils+import System.Posix.IO (OpenMode(ReadOnly), closeFd, defaultFileFlags, openFd)+import System.Posix.Types (ByteCount, Fd(Fd), FileOffset)++import qualified Data.ByteString as B+import qualified Data.ByteString.Unsafe as BU++preadAlloca :: Fd -> ByteCount -> FileOffset -> IO ByteString+preadAlloca (Fd fd) size off =+  allocaBytes (fromIntegral size) $ \buf -> do+    readBytes <- c_pread fd buf size off+    B.packCStringLen (buf, fromIntegral readBytes)++preadMalloc :: Fd -> ByteCount -> FileOffset -> IO ByteString+preadMalloc (Fd fd) size off = do+  buf <- mallocBytes (fromIntegral size)+  let mkBS = do+        readBytes <- c_pread fd buf size off+        BU.unsafePackMallocCStringLen (buf, fromIntegral readBytes)+  mkBS `onException` free buf++main :: IO ()+main = bracket+  (openFd "/dev/zero" ReadOnly Nothing defaultFileFlags)+  closeFd+  $ \fd -> defaultMain+    [ bgroup "pread"+      [ bench "alloca" $ nfIO (preadAlloca fd size off)+      , bench "malloc" $ nfIO (preadMalloc fd size off)+      ]+    ]+  where+  size = 1048576+  off = 1024
configure view
@@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles.-# Generated by GNU Autoconf 2.69 for Haskell libfuse3 package 0.1.1.1.+# Generated by GNU Autoconf 2.69 for Haskell libfuse3 package 0.1.2.0. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.@@ -576,8 +576,8 @@ # Identity of this package. PACKAGE_NAME='Haskell libfuse3 package' PACKAGE_TARNAME='haskell-libfuse3-package'-PACKAGE_VERSION='0.1.1.1'-PACKAGE_STRING='Haskell libfuse3 package 0.1.1.1'+PACKAGE_VERSION='0.1.2.0'+PACKAGE_STRING='Haskell libfuse3 package 0.1.2.0' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -1192,7 +1192,7 @@   # Omit some internal or obsolete options to make the list less imposing.   # This message is too long to be a string in the A/UX 3.1 sh.   cat <<_ACEOF-\`configure' configures Haskell libfuse3 package 0.1.1.1 to adapt to many kinds of systems.+\`configure' configures Haskell libfuse3 package 0.1.2.0 to adapt to many kinds of systems.  Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1254,7 +1254,7 @@  if test -n "$ac_init_help"; then   case $ac_init_help in-     short | recursive ) echo "Configuration of Haskell libfuse3 package 0.1.1.1:";;+     short | recursive ) echo "Configuration of Haskell libfuse3 package 0.1.2.0:";;    esac   cat <<\_ACEOF @@ -1341,7 +1341,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then   cat <<\_ACEOF-Haskell libfuse3 package configure 0.1.1.1+Haskell libfuse3 package configure 0.1.2.0 generated by GNU Autoconf 2.69  Copyright (C) 2012 Free Software Foundation, Inc.@@ -1453,7 +1453,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by Haskell libfuse3 package $as_me 0.1.1.1, which was+It was created by Haskell libfuse3 package $as_me 0.1.2.0, which was generated by GNU Autoconf 2.69.  Invocation command line was    $ $0 $@@@ -3340,7 +3340,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log="-This file was extended by Haskell libfuse3 package $as_me 0.1.1.1, which was+This file was extended by Haskell libfuse3 package $as_me 0.1.2.0, which was generated by GNU Autoconf 2.69.  Invocation command line was    CONFIG_FILES    = $CONFIG_FILES@@ -3402,7 +3402,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\-Haskell libfuse3 package config.status 0.1.1.1+Haskell libfuse3 package config.status 0.1.2.0 configured by $0, generated by GNU Autoconf 2.69,   with options \\"\$ac_cs_config\\" 
configure.ac view
@@ -1,4 +1,4 @@-AC_INIT([Haskell libfuse3 package], [0.1.1.1])+AC_INIT([Haskell libfuse3 package], [0.1.2.0])  # Safety check: Ensure that we are in the correct source directory. AC_CONFIG_SRCDIR([libfuse3.cabal])
example/passthrough.hs view
@@ -19,7 +19,7 @@ import Data.Function (fix) import Data.Time.Clock.POSIX (POSIXTime) import Data.Void (Void)-import Foreign (Ptr, allocaBytes, with)+import Foreign (Ptr, with) import Foreign.C (CInt(CInt), CSize(CSize), CUInt(CUInt), Errno(Errno), eIO, eOK, eOPNOTSUPP) import System.IO (SeekMode, hPrint, stderr) import System.LibFuse3@@ -29,16 +29,8 @@ import System.Posix.IO (OpenFileFlags, OpenMode(WriteOnly), closeFd, defaultFileFlags, exclusive, fdSeek, openFd) import System.Posix.Types (ByteCount, COff(COff), CSsize(CSsize), DeviceID, Fd(Fd), FileMode, FileOffset, GroupID, UserID) -import qualified Data.ByteString as B-import qualified Data.ByteString.Unsafe as BU import qualified System.LibFuse3.FuseConfig as FuseConfig -foreign import ccall "pread"-  c_pread :: CInt -> Ptr a -> CSize -> COff -> IO CSsize--foreign import ccall "pwrite"-  c_pwrite :: CInt -> Ptr a -> CSize -> COff -> IO CSsize- foreign import ccall "posix_fallocate"   c_posix_fallocate :: CInt -> COff -> COff -> IO CInt @@ -125,18 +117,10 @@ xmpOpen path mode flags = tryErrno $ openFd path mode Nothing flags  xmpRead :: Fd -> ByteCount -> FileOffset -> IO (Either Errno ByteString)-xmpRead (Fd fd) size offset = tryErrno $-  -- instead of allocaBytes + packCStringLen, a pair of mallocBytes + unsafePackMallocCString-  -- can be used to gain a small (~1%) decrease in running time-  allocaBytes (fromIntegral size) $ \buf -> do-    readBytes <- c_pread fd buf size offset-    B.packCStringLen (buf, fromIntegral readBytes)+xmpRead fd size offset = tryErrno $ pread fd size offset  xmpWrite :: Fd -> ByteString -> FileOffset -> IO (Either Errno CInt)-xmpWrite (Fd fd) bs offset = tryErrno $-  BU.unsafeUseAsCStringLen bs $ \(buf, size) ->-    -- truncate CSsize (ssize_t = 64bit) to CInt (32bit) because there is no other way-    fromIntegral <$> c_pwrite fd buf (fromIntegral size) offset+xmpWrite fd bs offset = tryErrno $ fromIntegral <$> pwrite fd bs offset  xmpStatfs :: FilePath -> IO (Either Errno FileSystemStats) xmpStatfs = tryErrno . getFileSystemStats
example/statjson.hs view
@@ -46,8 +46,7 @@ import Data.Bits ((.&.), (.|.), complement) import Data.ByteString (ByteString) import Data.Void (Void)-import Foreign (Ptr, allocaBytes)-import Foreign.C (CInt(CInt), CSize(CSize), Errno, eIO, eNOSYS, eOPNOTSUPP)+import Foreign.C (Errno, eIO, eNOSYS, eOPNOTSUPP) import Numeric (showHex) import System.Clock (TimeSpec(TimeSpec)) import System.Directory (doesPathExist, listDirectory, makeAbsolute)@@ -57,16 +56,12 @@ import System.IO (hPrint, stderr) import System.Posix.IO (OpenFileFlags, OpenMode(ReadOnly), closeFd, openFd) import System.Posix.Files (regularFileMode, stdFileMode)-import System.Posix.Types (COff(COff), CSsize(CSsize), ByteCount, Fd(Fd), FileOffset)+import System.Posix.Types (ByteCount, Fd, FileOffset)  import qualified Data.Aeson as Aeson-import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL import qualified System.Clock -foreign import ccall "pread"-  c_pread :: CInt -> Ptr a -> CSize -> COff -> IO CSsize- -- | An outcome of an individual operation in a layer. data Outcome a   -- | The request is forwarded to the next layer.@@ -107,10 +102,7 @@       case mode of         ReadOnly -> tryErrno $ fmap Respond $ openFd (srcDir <> path) mode Nothing flags         _ -> pure $ Left eOPNOTSUPP-  , layerRead = \(Fd fd) size offset -> tryErrno $-      allocaBytes (fromIntegral size) $ \buf -> do-        readBytes <- c_pread fd buf size offset-        B.packCStringLen (buf, fromIntegral readBytes)+  , layerRead = \fd size offset -> tryErrno $ pread fd size offset   , layerRelease = closeFd   } 
libfuse3.cabal view
@@ -3,7 +3,7 @@ -- For further documentation, see http://haskell.org/cabal/users-guide/  name:                libfuse3-version:             0.1.1.1+version:             0.1.2.0 synopsis:            A Haskell binding for libfuse-3.x description:         Bindings for libfuse, the FUSE userspace reference implementation, of version 3.x. Compatible with Linux. -- bug-reports:@@ -53,6 +53,17 @@   default-language:    Haskell2010   ghc-options:         -Wall -fdefer-typed-holes +test-suite unittest+  type:                exitcode-stdio-1.0+  main-is:             Main.hs+  build-depends:       libfuse3, base+                     , hspec+  -- other-modules:+  -- other-extensions:+  hs-source-dirs:      test/unittest+  default-language:    Haskell2010+  ghc-options:         -Wall -fdefer-typed-holes -threaded+ test-suite integtest   type:                exitcode-stdio-1.0   main-is:             Main.hs@@ -67,6 +78,18 @@   -- other-modules:   -- other-extensions:   hs-source-dirs:      test/integtest+  default-language:    Haskell2010+  ghc-options:         -Wall -fdefer-typed-holes -threaded++benchmark microbench+  type:                exitcode-stdio-1.0+  main-is:             Main.hs+  -- other-modules:+  build-depends:       libfuse3, base+                     , bytestring+                     , criterion+                     , unix+  hs-source-dirs:      bench/microbench   default-language:    Haskell2010   ghc-options:         -Wall -fdefer-typed-holes -threaded 
src/System/LibFuse3.hs view
@@ -29,6 +29,8 @@   , module System.LibFuse3.Utils    , fuseMain+  , ExceptionHandler+  , defaultExceptionHandler   )   where 
src/System/LibFuse3/Internal.hsc view
@@ -7,7 +7,7 @@ module System.LibFuse3.Internal where  import Control.Applicative ((<|>))-import Control.Exception (Exception, bracket_, finally, handle)+import Control.Exception (Exception, SomeException, bracket_, catch, finally, fromException, handle) import Control.Monad (unless, void) import Control.Monad.IO.Class (liftIO) import Control.Monad.Trans.Resource (ResourceT, runResourceT)@@ -37,12 +37,12 @@   , pokeByteOff   , with   )-import Foreign.C (CInt(CInt), CString, Errno, eINVAL, eNOSYS, eOK, getErrno, peekCString, resetErrno, throwErrno, withCStringLen)+import Foreign.C (CInt(CInt), CString, Errno, eFAULT, eINVAL, eIO, eNOSYS, eOK, getErrno, peekCString, resetErrno, throwErrno, withCStringLen) import GHC.IO.Handle (hDuplicateTo) import System.Clock (TimeSpec) import System.Environment (getArgs, getProgName) import System.Exit (ExitCode(ExitFailure, ExitSuccess), exitFailure, exitSuccess, exitWith)-import System.IO (IOMode(ReadMode, WriteMode), SeekMode(AbsoluteSeek, RelativeSeek, SeekFromEnd), stderr, stdin, stdout, withFile)+import System.IO (IOMode(ReadMode, WriteMode), SeekMode(AbsoluteSeek, RelativeSeek, SeekFromEnd), hPutStrLn, stderr, stdin, stdout, withFile) import System.LibFuse3.FileStat (FileStat) import System.LibFuse3.FileSystemStats (FileSystemStats) import System.LibFuse3.FuseConfig (FuseConfig, fromCFuseConfig, toCFuseConfig)@@ -395,21 +395,23 @@ -- -- The created `C.FuseOperations` has the following invariants: -----   - The content of @fuse_operations.fh@ is a Haskell value of type @StablePtr fh@ or+--   - The content of @fuse_file_info.fh@ is a Haskell value of type @StablePtr fh@ or --     @StablePtr dh@, depending on operations. It is created with `newFH`, accessed with --     `getFH` and released with `delFH`. -----   - Every methods handle Haskell exception with the supplied error handler.+--   - Every methods handle Haskell exception with the supplied error handler. Any exceptions+--     not catched by it are catched, logged and returns `eIO`. This means that `exitSuccess`+--     /does not work/ inside `FuseOperations`. -- --   - NULL filepaths (passed from libfuse if `FuseConfig.nullpathOk` is set) are --     translated to empty strings. resCFuseOperations   :: forall fh dh e    . Exception e-  => FuseOperations fh dh           -- ^ A set of file system operations.-  -> (e -> IO Errno)                -- ^ An error handler that converts a Haskell exception to @errno@.+  => FuseOperations fh dh+  -> ExceptionHandler e   -> ResourceT IO (Ptr C.FuseOperations)-resCFuseOperations ops handler = do+resCFuseOperations ops handlerRaw = do   fuseGetattr       <- resC C.mkGetattr       wrapGetattr       (fuseGetattr ops)   fuseReadlink      <- resC C.mkReadlink      wrapReadlink      (fuseReadlink ops)   fuseMknod         <- resC C.mkMknod         wrapMknod         (fuseMknod ops)@@ -457,6 +459,12 @@     , ..     }   where+  -- wraps the supplied handler to make sure no Haskell exceptions are propagated to the C land+  handler :: ExceptionHandler SomeException+  handler se = case fromException se of+    Nothing -> defaultExceptionHandler se+    Just e -> handlerRaw e `catch` defaultExceptionHandler+   -- convert a Haskell function to C one with @wrapMeth@, get its @FunPtr@, and associate it with freeHaskellFunPtr   resC :: (cfunc -> IO (FunPtr cfunc)) -> (hsfunc -> cfunc) -> Maybe hsfunc -> ResourceT IO (FunPtr cfunc)   resC _ _ Nothing = pure nullFunPtr@@ -792,6 +800,9 @@           _ -> Left eINVAL     either (pure . Left) (go filePath fh offset) emode +  _dummyToSuppressWarnings :: StablePtr a+  _dummyToSuppressWarnings = error "dummy" eNOSYS+ -- | Allocates a @fuse_args@ struct to hold commandline arguments. resFuseArgs :: String -> [String] -> ResourceT IO (Ptr C.FuseArgs) resFuseArgs prog args = do@@ -937,7 +948,7 @@         else exitFailure  -- | Parses the commandline arguments and runs fuse.-fuseRun :: Exception e => String -> [String] -> FuseOperations fh dh -> (e -> IO Errno) -> IO a+fuseRun :: Exception e => String -> [String] -> FuseOperations fh dh -> ExceptionHandler e -> IO a fuseRun prog args ops handler = runResourceT $ do   pArgs <- resFuseArgs prog args   mainArgs <- liftIO $ fuseParseCommandLineOrExit pArgs@@ -954,7 +965,7 @@ -- This is all that has to be called from the @main@ function. On top of -- the `FuseOperations` record with filesystem implementation, you must give -- an exception handler converting Haskell exceptions to `Errno`.-fuseMain :: Exception e => FuseOperations fh dh -> (e -> IO Errno) -> IO ()+fuseMain :: Exception e => FuseOperations fh dh -> ExceptionHandler e -> IO () fuseMain ops handler = do   -- this used to be implemented using libfuse's fuse_main. Doing this will fork()   -- from C behind the GHC runtime's back, which deadlocks in GHC 6.8.@@ -964,6 +975,22 @@   args <- getArgs   fuseRun prog args ops handler +-- | An exception handler which converts Haskell exceptions from `FuseOperations` methods to `Errno`.+type ExceptionHandler e = e -> IO Errno++-- | Catches any exception, logs it to stderr, and returns `eIO`.+--+-- Suitable as a default exception handler.+--+-- __NOTE 1__ This differs from the one in the @HFuse@ package which returns `eFAULT`.+--+-- __NOTE 2__ If the filesystem is daemonized (as default), the exceptions will not be logged because+-- stderr is redirected to @\/dev\/null@.+defaultExceptionHandler :: ExceptionHandler SomeException+defaultExceptionHandler e = hPutStrLn stderr (show e) >> pure eIO+  where+  _dummyToSuppressWarnings = error "dummy" eFAULT+ -- | Gets a file handle from `C.FuseFileInfo` which is embedded with `newFH`. -- -- If either the @Ptr `C.FuseFileInfo`@ itself or its @fh@ field is NULL, returns @Nothing@.@@ -1018,7 +1045,3 @@ -- | Materializes the callback of @readdir@ to marshal `fuseReaddir`. foreign import ccall "dynamic"   peekFuseFillDir :: FunPtr C.FuseFillDir -> C.FuseFillDir---- | the dummy to please both ghc and haddock; don't use-_dummy :: StablePtr a -> b-_dummy _ = undefined eNOSYS
src/System/LibFuse3/Utils.hs view
@@ -6,8 +6,11 @@     testBitSet    , -- * Errno-    unErrno, ioErrorToErrno, tryErrno, tryErrno_+    unErrno, ioErrorToErrno, throwErrnoOf, tryErrno, tryErrno_, tryErrno', tryErrno_' +  , -- * File I/O+    pread, pwrite, c_pread, c_pwrite+   , -- * Marshalling strings     pokeCStringLen0 @@ -16,20 +19,24 @@   )   where -import Control.Exception (tryJust)+import Control.Exception (SomeException, try, tryJust) import Data.Bits ((.&.), Bits)+import Data.ByteString (ByteString) import Data.Ratio ((%)) import Data.Time.Clock.POSIX (POSIXTime)-import Foreign (copyArray, pokeElemOff)-import Foreign.C (CInt, CStringLen, Errno(Errno), eOK, errnoToIOError, throwErrno, withCStringLen)+import Foreign (Ptr, allocaBytes, copyArray, pokeElemOff)+import Foreign.C (CInt(CInt), CSize(CSize), CStringLen, Errno(Errno), eIO, eOK, errnoToIOError, getErrno, throwErrno, throwErrnoIfMinus1, withCStringLen) import GHC.IO.Exception (IOException(IOError, ioe_errno)) import System.Clock (TimeSpec)+import System.Posix.Types (ByteCount, COff(COff), CSsize(CSsize), Fd(Fd), FileOffset) +import qualified Data.ByteString as B+import qualified Data.ByteString.Unsafe as BU import qualified System.Clock as TimeSpec --- to have haddock link to proper entities-_dummy :: dummy-_dummy = error "dummy" errnoToIOError throwErrno+-- | Identical to @extra@'s @try_@+try_ :: IO a -> IO (Either SomeException a)+try_ = try  -- | Unwraps the newtype `Errno`. unErrno :: Errno -> CInt@@ -41,6 +48,21 @@ ioErrorToErrno IOError{ioe_errno=Just e} = Just $ Errno e ioErrorToErrno _ = Nothing +-- | Like `throwErrno` but takes an `Errno` as a parameter instead of reading from `getErrno`.+--+-- This is an inverse of `tryErrno`:+--+-- @+-- tryErrno (throwErrnoOf _ e) ≡ pure (Left e)+-- @+throwErrnoOf+  :: String -- ^ textual description of the error location+  -> Errno+  -> IO a+throwErrnoOf loc errno = ioError (errnoToIOError loc errno Nothing Nothing)+  where+  _dummyToSuppressWarnings = error "dummy" getErrno throwErrno+ -- | Catches an exception constructed with `errnoToIOError` and extracts `Errno` from it. tryErrno :: IO a -> IO (Either Errno a) tryErrno = tryJust ioErrorToErrno@@ -51,6 +73,14 @@ tryErrno_ :: IO a -> IO Errno tryErrno_ = fmap (either id (const eOK)) . tryErrno +-- | Like `tryErrno` but also catches non-Errno errors to return `eIO`.+tryErrno' :: IO a -> IO (Either Errno a)+tryErrno' = fmap (either (const $ Left eIO) id) . try_ . tryErrno++-- | Like `tryErrno_` but also catches non-Errno errors to return `eIO`.+tryErrno_' :: IO a -> IO Errno+tryErrno_' = fmap (either (const eIO) id) . try_ . tryErrno_+ -- | Converts a `TimeSpec` to a `POSIXTime`. -- -- This is the same conversion as the @unix@ package does (as of writing).@@ -80,3 +110,30 @@ -- @ testBitSet :: Bits a => a -> a -> Bool testBitSet bits mask = bits .&. mask == mask++-- | Reads from a file descriptor at a given offset.+--+-- Fewer bytes may be read than requested.+-- On error, throws an t`IOError` corresponding to the errno.+pread :: Fd -> ByteCount -> FileOffset -> IO ByteString+pread (Fd fd) size off =+  allocaBytes (fromIntegral size) $ \buf -> do+    readBytes <- throwErrnoIfMinus1 "pread" $ c_pread fd buf size off+    B.packCStringLen (buf, fromIntegral readBytes)++-- | Writes to a file descriptor at a given offset.+--+-- Returns the number of bytes written. Fewer bytes may be written than requested.+-- On error, throws an t`IOError` corresponding to the errno.+pwrite :: Fd -> ByteString -> FileOffset -> IO CSsize+pwrite (Fd fd) bs off =+  BU.unsafeUseAsCStringLen bs $ \(buf, size) ->+    throwErrnoIfMinus1 "pwrite" $ c_pwrite fd buf (fromIntegral size) off++-- | A foreign import of @pread(2)@+foreign import ccall "pread"+  c_pread :: CInt -> Ptr a -> CSize -> COff -> IO CSsize++-- | A foreign import of @pwrite(2)@+foreign import ccall "pwrite"+  c_pwrite :: CInt -> Ptr a -> CSize -> COff -> IO CSsize
+ test/unittest/Main.hs view
@@ -0,0 +1,18 @@+{-# LANGUAGE StandaloneDeriving #-}+{-# OPTIONS_GHC -Wno-orphans #-}+module Main where++import Foreign.C (Errno(Errno), eIO)+import System.LibFuse3+import Test.Hspec (describe, hspec, it, shouldReturn)++deriving instance Show Errno++main :: IO ()+main = hspec $ do+  describe "throwErrnoOf" $ do+    it "should be an inverse of `tryErrno`" $+      tryErrno (throwErrnoOf "" eIO) `shouldReturn` (Left eIO :: Either Errno ())++    it "should be an inverse of `tryErrno_`" $+      tryErrno_ (throwErrnoOf "" eIO) `shouldReturn` eIO