packages feed

HFuse 0.2.1 → 0.2.2

raw patch · 5 files changed

+255/−99 lines, 5 filesdep ~basePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: base

API changes (from Hackage documentation)

- System.Fuse: defaultExceptionHandler :: Exception -> IO Errno
+ System.Fuse: defaultExceptionHandler :: SomeException -> IO Errno
- System.Fuse: fuseMain :: FuseOperations fh -> (Exception -> IO Errno) -> IO ()
+ System.Fuse: fuseMain :: (Exception e) => FuseOperations fh -> (e -> IO Errno) -> IO ()

Files

HFuse.cabal view
@@ -1,5 +1,5 @@ Name:                   HFuse-Version:                0.2.1+Version:                0.2.2 License:                BSD3 License-File:		LICENSE Author:                 Jeremy Bobbio@@ -10,18 +10,16 @@ Stability:              Experimental Cabal-Version:          >= 1.2 Build-Type:             Simple+Extra-source-files:     README  Library-  Build-Depends:          base, unix, bytestring+  Build-Depends:          base >= 4 && < 5, unix, bytestring   exposed-Modules:        System.Fuse-  Extensions:             ForeignFunctionInterface PatternSignatures EmptyDataDecls+  Extensions:             ForeignFunctionInterface ScopedTypeVariables EmptyDataDecls   Includes:               sys/statfs.h, dirent.h, fuse.h, fcntl.h   Include-Dirs:           /usr/include, /usr/local/include, .   Extra-Libraries:        fuse   Extra-Lib-Dirs:         /usr/local/lib-  Includes:               fuse_wrappers.h-  Install-Includes:       fuse_wrappers.h-  C-Sources:              fuse_wrappers.c   CC-Options:             -D_FILE_OFFSET_BITS=64   if os(darwin) {     CC-Options:           "-DMACFUSE"
+ README view
@@ -0,0 +1,15 @@+Programs using HFuse should be compiled with -threaded.++HelloFS seems to work with:+* Linux 2.6.24+* The Glorious Glasgow Haskell Compilation System, version 6.8.2+* fuse-2.6++BindFS (?) is known to have a lurking deadlock problem.++LiveFS is broken (from the old distribution).++Added support for MacFUSE. Tested HelloFS and BindFS with:+* OSX 10.4.11 on a PPC G4 mac+* GHC 6.8.3+* MacFUSE 10.4-1.7.0
System/Fuse.hsc view
@@ -1,12 +1,12 @@ ----------------------------------------------------------------------------- -- | -- Module      :  System.Fuse--- Copyright   :  (c) Jérémy Bobbio+-- Copyright   :  (c) Jérémy Bobbio, Taru Karttunen -- License     :  BSD-style -- --- Maintainer  :  jeremy.bobbio@etu.upmc.fr+-- Maintainer  :  taruti@taruti.net, jeremy.bobbio@etu.upmc.fr -- Stability   :  experimental--- Portability :  GHC 6.4+-- Portability :  GHC 6.4-6.12 -- -- A binding for the FUSE (Filesystem in USErspace) library -- (<http://fuse.sourceforge.net/>), which allows filesystems to be implemented@@ -28,7 +28,7 @@       module Foreign.C.Error     , FuseOperations(..)     , defaultFuseOps-    , fuseMain -- :: FuseOperations -> (Exception -> IO Errno) -> IO ()+    , fuseMain -- :: FuseOperations fh -> (Exception -> IO Errno) -> IO ()     , defaultExceptionHandler -- :: Exception -> IO Errno       -- * Operations datatypes     , FileStat(..)@@ -48,7 +48,8 @@  import Prelude hiding ( Read ) -import Control.Exception as E( Exception, handle, finally )+import Control.Monad+import Control.Exception as E(Exception, handle, finally, SomeException) import qualified Data.ByteString.Char8    as B import qualified Data.ByteString.Internal as B import qualified Data.ByteString.Unsafe   as B@@ -57,10 +58,16 @@ import Foreign.C.Error import Foreign.Marshal import System.Environment ( getProgName, getArgs )-import System.IO ( hPutStrLn, stderr )+import System.IO ( hPutStrLn, stderr, withFile, stdin, stdout, IOMode(..) ) import System.Posix.Types import System.Posix.Files ( accessModes, intersectFileModes, unionFileModes )+import System.Posix.Directory(changeWorkingDirectory)+import System.Posix.Process(forkProcess,createSession,exitImmediately) import System.Posix.IO ( OpenMode(..), OpenFileFlags(..) )+import qualified System.Posix.Signals as Signals+import GHC.IO.Handle(hDuplicateTo)+import System.Exit+import qualified System.IO.Error as IO(catch,ioeGetErrorString)  -- TODO: FileMode -> Permissions -- TODO: Arguments !@@ -79,8 +86,6 @@ #include <fuse.h> #include <fcntl.h> -#include "fuse_wrappers.h"- {- $intro 'FuseOperations' contains a field for each filesystem operations that can be called by FUSE. Think like if you were implementing a file system inside the Linux kernel.@@ -426,33 +431,26 @@                    , fuseDestroy = return ()                    } +-- Allocates a fuse_args struct to hold the commandline arguments.+withFuseArgs :: (Ptr CFuseArgs -> IO b) -> IO b+withFuseArgs f =+    do prog <- getProgName+       args <- getArgs+       let allArgs = (prog:args)+           argc = length allArgs+       withMany withCString allArgs (\ cArgs ->+           withArray cArgs $ (\ pArgv ->+               allocaBytes (#size struct fuse_args) (\ fuseArgs ->+                    do (#poke struct fuse_args, argc) fuseArgs argc+                       (#poke struct fuse_args, argv) fuseArgs pArgv+                       (#poke struct fuse_args, allocated) fuseArgs (0::CInt)+                       finally (f fuseArgs)+                               (fuse_opt_free_args fuseArgs)))) --- | Main function of FUSE.--- 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'.--- --- This function does the following:------   * parses command line options (@-d@, @-s@ and @-h@) ;------   * passes all options after @--@ to the fusermount program ;------   * mounts the filesystem by calling @fusermount@ ;------   * installs signal handlers for 'System.Posix.Signals.keyboardSignal',---     'System.Posix.Signals.lostConnection',---     'System.Posix.Signals.softwareTermination' and---     'System.Posix.Signals.openEndedPipe' ;------   * registers an exit handler to unmount the filesystem on program exit ;------   * registers the operations ;------   * calls FUSE event loop.-fuseMain :: FuseOperations fh -> (Exception -> IO Errno) -> IO ()-fuseMain ops handler =+withStructFuse :: forall e fh b. Exception e => Ptr CFuseChan -> Ptr CFuseArgs -> FuseOperations fh -> (e -> IO Errno) -> (Ptr CStructFuse -> IO b) -> IO b+withStructFuse pFuseChan pArgs ops handler f =     allocaBytes (#size struct fuse_operations) $ \ pOps -> do+      bzero pOps (#size struct fuse_operations)       mkGetAttr    wrapGetAttr    >>= (#poke struct fuse_operations, getattr)    pOps       mkReadLink   wrapReadLink   >>= (#poke struct fuse_operations, readlink)   pOps        -- getdir is deprecated and thus unsupported@@ -485,17 +483,17 @@       mkReadDir    wrapReadDir    >>= (#poke struct fuse_operations, readdir)    pOps       mkReleaseDir wrapReleaseDir >>= (#poke struct fuse_operations, releasedir) pOps       mkFSyncDir   wrapFSyncDir   >>= (#poke struct fuse_operations, fsyncdir)   pOps+#if FUSE_MINOR_VERSION > 4       mkAccess     wrapAccess     >>= (#poke struct fuse_operations, access)     pOps+#endif       mkInit       wrapInit       >>= (#poke struct fuse_operations, init)       pOps       mkDestroy    wrapDestroy    >>= (#poke struct fuse_operations, destroy)    pOps-      prog <- getProgName-      args <- getArgs-      let allArgs = (prog:args)-          argc    = length allArgs-      withMany withCString allArgs $ \ pAddrs  ->-          withArray pAddrs $ \ pArgv ->-              do fuse_main_wrapper argc pArgv pOps-    where fuseHandler :: Exception -> IO CInt+      structFuse <- fuse_new pFuseChan pArgs pOps (#size struct fuse_operations) nullPtr +      if structFuse == nullPtr+        then fail ""+        else E.finally (f structFuse)+                       (fuse_destroy structFuse)+    where fuseHandler :: e -> IO CInt           fuseHandler e = handler e >>= return . unErrno           wrapGetAttr :: CGetAttr           wrapGetAttr pFilePath pStat = handle fuseHandler $@@ -715,19 +713,138 @@                  return (- errno)           wrapInit :: CInit           wrapInit pFuseConnInfo =-            handle (\e -> hPutStrLn stderr (show e) >> return nullPtr) $+            handle (\e -> defaultExceptionHandler e >> return nullPtr) $               do fuseInit ops                  return nullPtr           wrapDestroy :: CDestroy-          wrapDestroy _ = handle (\e -> hPutStrLn stderr (show e)) $+          wrapDestroy _ = handle (\e -> defaultExceptionHandler e >> return ()) $               do fuseDestroy ops  -- | Default exception handler. -- Print the exception on error output and returns 'eFAULT'.-defaultExceptionHandler :: (Exception -> IO Errno)+defaultExceptionHandler :: (SomeException -> IO Errno) defaultExceptionHandler e = hPutStrLn stderr (show e) >> return eFAULT +-- Calls fuse_parse_cmdline to parses the part of the commandline arguments that+-- we care about. fuse_parse_cmdline will modify the CFuseArgs struct passed in+-- to remove those arguments; the CFuseArgs struct containing remaining arguments+-- must be passed to fuse_mount/fuse_new.+--+-- The multithreaded runtime will be used regardless of the threading flag!+-- See the comment in fuse_session_exit for why.+fuseParseCommandLine :: Ptr CFuseArgs -> IO (Maybe (Maybe String, Bool, Bool))+fuseParseCommandLine pArgs = +    alloca (\pMountPt -> +        alloca (\pMultiThreaded ->+            alloca (\pFG ->+                do poke pMultiThreaded 0+                   poke pFG 0+                   retval <- fuse_parse_cmdline pArgs pMountPt pMultiThreaded pFG+                   if retval == 0+                     then do cMountPt <- peek pMountPt+                             mountPt <- if cMountPt /= nullPtr+                                          then do a <- peekCString cMountPt+                                                  free cMountPt+                                                  return $ Just a+                                          else return $ Nothing+                             multiThreaded <- peek pMultiThreaded+                             foreground <- peek pFG+                             return $ Just (mountPt, multiThreaded == 1, foreground == 1)+                     else return Nothing))) +-- haskell version of daemon(2)+-- Mimic's daemon()s use of _exit() instead of exit(); we depend on this in fuseMainReal,+-- because otherwise we'll unmount the filesystem when the foreground process exits.+daemon f = forkProcess d >> exitImmediately ExitSuccess+  where d = IO.catch (do createSession+                         changeWorkingDirectory "/"+                         -- need to open /dev/null twice because hDuplicateTo can't dup a ReadWriteMode to a ReadMode handle+                         withFile "/dev/null" WriteMode (\devNullOut ->+                           do hDuplicateTo devNullOut stdout+                              hDuplicateTo devNullOut stderr)+                         withFile "/dev/null" ReadMode (\devNullIn -> hDuplicateTo devNullIn stdin)+                         f+                         exitWith ExitSuccess)+                     (const exitFailure)++-- Installs signal handlers for the duration of the main loop.+withSignalHandlers exitHandler f =+    do let sigHandler = Signals.CatchOnce exitHandler+       Signals.installHandler Signals.keyboardSignal sigHandler Nothing+       Signals.installHandler Signals.lostConnection sigHandler Nothing+       Signals.installHandler Signals.softwareTermination sigHandler Nothing+       Signals.installHandler Signals.openEndedPipe Signals.Ignore Nothing+       E.finally f+                 (do Signals.installHandler Signals.keyboardSignal Signals.Default Nothing+                     Signals.installHandler Signals.lostConnection Signals.Default Nothing+                     Signals.installHandler Signals.softwareTermination Signals.Default Nothing+                     Signals.installHandler Signals.openEndedPipe Signals.Default Nothing)++-- Mounts the filesystem, forks, and then starts fuse+fuseMainReal foreground ops handler pArgs mountPt =+    withCString mountPt (\cMountPt ->+      do pFuseChan <- fuse_mount cMountPt pArgs+         if pFuseChan == nullPtr+           then exitFailure -- fuse will print an error message why this happened+           else (withStructFuse pFuseChan pArgs ops handler (\pFuse ->+                  E.finally +                     (if foreground -- finally ready to fork+                       then changeWorkingDirectory "/" >> (procMain pFuse)+                       else daemon (procMain pFuse))+                     (fuse_unmount cMountPt pFuseChan))))++    -- here, we're finally inside the daemon process, we can run the main loop+    where procMain pFuse = do session <- fuse_get_session pFuse+                              -- calling fuse_session_exit to exit the main loop only+                              -- appears to work with the multithreaded fuse loop.+                              -- In the single-threaded case, FUSE depends on their+                              -- recv() call to finish with EINTR when signals arrive.+                              -- This doesn't happen with GHC's signal handling in place.+                              withSignalHandlers (fuse_session_exit session) $+                                 do retVal <- fuse_loop_mt pFuse+                                    if retVal == 1 +                                      then exitWith ExitSuccess+                                      else exitFailure+                                    return ()++-- | Main function of FUSE.+-- 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'.+-- +-- This function does the following:+--+--   * parses command line options (@-d@, @-s@ and @-h@) ;+--+--   * passes all options after @--@ to the fusermount program ;+--+--   * mounts the filesystem by calling @fusermount@ ;+--+--   * installs signal handlers for 'System.Posix.Signals.keyboardSignal',+--     'System.Posix.Signals.lostConnection',+--     'System.Posix.Signals.softwareTermination' and+--     'System.Posix.Signals.openEndedPipe' ;+--+--   * registers an exit handler to unmount the filesystem on program exit ;+--+--   * registers the operations ;+--+--   * calls FUSE event loop.+fuseMain :: Exception e => FuseOperations fh -> (e -> IO Errno) -> IO ()+fuseMain ops handler =+    -- 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.+    -- Instead, we reimplement fuse_main in Haskell using the forkProcess and the+    -- lower-level fuse_new/fuse_loop_mt API.+    IO.catch+       (withFuseArgs (\pArgs ->+         do cmd <- fuseParseCommandLine pArgs+            case cmd of+              Nothing -> fail ""+              Just (Nothing, _, _) -> fail "Usage error: mount point required"+              Just (Just mountPt, _, foreground) -> fuseMainReal foreground ops handler pArgs mountPt))+       ((\errStr -> when (not $ null errStr) (putStrLn errStr) >> exitFailure) . IO.ioeGetErrorString)+ ----------------------------------------------------------------------------- -- Miscellaneous utilities @@ -752,14 +869,49 @@ -- exported C called from Haskell ---   -data CFuseOperations-foreign import ccall threadsafe "fuse_wrappers.h fuse_main_wrapper"-    fuse_main_wrapper :: Int -> Ptr CString -> Ptr CFuseOperations -> IO ()+data CFuseArgs -- struct fuse_args -data StructFuse-foreign import ccall threadsafe "fuse.h fuse_get_context"-    fuse_get_context :: IO (Ptr StructFuse)+data CFuseChan -- struct fuse_chan+foreign import ccall safe "fuse.h fuse_mount"+    fuse_mount :: CString -> Ptr CFuseArgs -> IO (Ptr CFuseChan) +foreign import ccall safe "fuse.h fuse_unmount"+    fuse_unmount :: CString -> Ptr CFuseChan -> IO ()++data CFuseSession -- struct fuse_session+foreign import ccall safe "fuse.h fuse_get_session"+    fuse_get_session :: Ptr CStructFuse -> IO (Ptr CFuseSession)++foreign import ccall safe "fuse.h fuse_session_exit"+    fuse_session_exit :: Ptr CFuseSession -> IO ()++foreign import ccall safe "fuse.h fuse_set_signal_handlers"+    fuse_set_signal_handlers :: Ptr CFuseSession -> IO Int++foreign import ccall safe "fuse.h fuse_remove_signal_handlers"+    fuse_remove_signal_handlers :: Ptr CFuseSession -> IO ()++foreign import ccall safe "fuse.h fuse_parse_cmdline"+    fuse_parse_cmdline :: Ptr CFuseArgs -> Ptr CString -> Ptr Int -> Ptr Int -> IO Int++data CStructFuse -- struct fuse+data CFuseOperations -- struct fuse_operations+foreign import ccall safe "fuse.h fuse_new"+    fuse_new :: Ptr CFuseChan -> Ptr CFuseArgs -> Ptr CFuseOperations -> Int -> Ptr () -> IO (Ptr CStructFuse)++foreign import ccall safe "fuse.h fuse_destroy"+    fuse_destroy :: Ptr CStructFuse -> IO ()++foreign import ccall safe "fuse.h fuse_opt_free_args"+    fuse_opt_free_args :: Ptr CFuseArgs -> IO ()++foreign import ccall safe "fuse.h fuse_loop_mt"+    fuse_loop_mt :: Ptr CStructFuse -> IO Int++data CFuseContext+foreign import ccall safe "fuse.h fuse_get_context"+    fuse_get_context :: IO (Ptr CFuseContext)+ --- -- dynamic Haskell called from C ---@@ -769,117 +921,117 @@  data CStat -- struct stat type CGetAttr = CString -> Ptr CStat -> IO CInt-foreign import ccall threadsafe "wrapper"+foreign import ccall safe "wrapper"     mkGetAttr :: CGetAttr -> IO (FunPtr CGetAttr)  type CReadLink = CString -> CString -> CSize -> IO CInt-foreign import ccall threadsafe "wrapper"+foreign import ccall safe "wrapper"     mkReadLink :: CReadLink -> IO (FunPtr CReadLink)  type CMkNod = CString -> CMode -> CDev -> IO CInt-foreign import ccall threadsafe "wrapper"+foreign import ccall safe "wrapper"     mkMkNod :: CMkNod -> IO (FunPtr CMkNod)  type CMkDir = CString -> CMode -> IO CInt-foreign import ccall threadsafe "wrapper"+foreign import ccall safe "wrapper"     mkMkDir :: CMkDir -> IO (FunPtr CMkDir)  type CUnlink = CString -> IO CInt-foreign import ccall threadsafe "wrapper"+foreign import ccall safe "wrapper"     mkUnlink :: CUnlink -> IO (FunPtr CUnlink)  type CRmDir = CString -> IO CInt-foreign import ccall threadsafe "wrapper"+foreign import ccall safe "wrapper"     mkRmDir :: CRmDir -> IO (FunPtr CRmDir)  type CSymLink = CString -> CString -> IO CInt-foreign import ccall threadsafe "wrapper"+foreign import ccall safe "wrapper"     mkSymLink :: CSymLink -> IO (FunPtr CSymLink)  type CRename = CString -> CString -> IO CInt-foreign import ccall threadsafe "wrapper"+foreign import ccall safe "wrapper"     mkRename :: CRename -> IO (FunPtr CRename)  type CLink = CString -> CString -> IO CInt-foreign import ccall threadsafe "wrapper"+foreign import ccall safe "wrapper"     mkLink :: CLink -> IO (FunPtr CLink)  type CChMod = CString -> CMode -> IO CInt-foreign import ccall threadsafe "wrapper"+foreign import ccall safe "wrapper"     mkChMod :: CChMod -> IO (FunPtr CChMod)  type CChOwn = CString -> CUid -> CGid -> IO CInt-foreign import ccall threadsafe "wrapper"+foreign import ccall safe "wrapper"     mkChOwn :: CChOwn -> IO (FunPtr CChOwn)  type CTruncate = CString -> COff -> IO CInt-foreign import ccall threadsafe "wrapper"+foreign import ccall safe "wrapper"     mkTruncate :: CTruncate -> IO (FunPtr CTruncate)  data CUTimBuf -- struct utimbuf type CUTime = CString -> Ptr CUTimBuf -> IO CInt-foreign import ccall threadsafe "wrapper"+foreign import ccall safe "wrapper"     mkUTime :: CUTime -> IO (FunPtr CUTime)  type COpen = CString -> Ptr CFuseFileInfo -> IO CInt-foreign import ccall threadsafe "wrapper"+foreign import ccall safe "wrapper"     mkOpen :: COpen -> IO (FunPtr COpen)  type CRead = CString -> CString -> CSize -> COff -> Ptr CFuseFileInfo -> IO CInt-foreign import ccall threadsafe "wrapper"+foreign import ccall safe "wrapper"     mkRead :: CRead -> IO (FunPtr CRead)  type CWrite = CString -> CString -> CSize -> COff -> Ptr CFuseFileInfo -> IO CInt-foreign import ccall threadsafe "wrapper"+foreign import ccall safe "wrapper"     mkWrite :: CWrite -> IO (FunPtr CWrite)  data CStructStatFS -- struct fuse_stat_fs type CStatFS = CString -> Ptr CStructStatFS -> IO CInt-foreign import ccall threadsafe "wrapper"+foreign import ccall safe "wrapper"     mkStatFS :: CStatFS -> IO (FunPtr CStatFS)  type CFlush = CString -> Ptr CFuseFileInfo -> IO CInt-foreign import ccall threadsafe "wrapper"+foreign import ccall safe "wrapper"     mkFlush :: CFlush -> IO (FunPtr CFlush)  type CRelease = CString -> Ptr CFuseFileInfo -> IO CInt-foreign import ccall threadsafe "wrapper"+foreign import ccall safe "wrapper"     mkRelease :: CRelease -> IO (FunPtr CRelease)  type CFSync = CString -> Int -> Ptr CFuseFileInfo -> IO CInt-foreign import ccall threadsafe "wrapper"+foreign import ccall safe "wrapper"     mkFSync :: CFSync -> IO (FunPtr CFSync)   -- XXX add *xattr bindings  type COpenDir = CString -> Ptr CFuseFileInfo -> IO CInt-foreign import ccall threadsafe "wrapper"+foreign import ccall safe "wrapper"     mkOpenDir :: COpenDir -> IO (FunPtr COpenDir)  type CReadDir = CString -> Ptr CFillDirBuf -> FunPtr CFillDir -> COff              -> Ptr CFuseFileInfo -> IO CInt-foreign import ccall threadsafe "wrapper"+foreign import ccall safe "wrapper"     mkReadDir :: CReadDir -> IO (FunPtr CReadDir)  type CReleaseDir = CString -> Ptr CFuseFileInfo -> IO CInt-foreign import ccall threadsafe "wrapper"+foreign import ccall safe "wrapper"     mkReleaseDir :: CReleaseDir -> IO (FunPtr CReleaseDir)  type CFSyncDir = CString -> Int -> Ptr CFuseFileInfo -> IO CInt-foreign import ccall threadsafe "wrapper"+foreign import ccall safe "wrapper"     mkFSyncDir :: CFSyncDir -> IO (FunPtr CFSyncDir)  type CAccess = CString -> CInt -> IO CInt-foreign import ccall threadsafe "wrapper"+foreign import ccall safe "wrapper"     mkAccess :: CAccess -> IO (FunPtr CAccess)  -- CInt because anything would be fine as we don't use them type CInit = Ptr CFuseConnInfo -> IO (Ptr CInt)-foreign import ccall threadsafe "wrapper"+foreign import ccall safe "wrapper"     mkInit :: CInit -> IO (FunPtr CInit)  type CDestroy = Ptr CInt -> IO ()-foreign import ccall threadsafe "wrapper"+foreign import ccall safe "wrapper"     mkDestroy :: CDestroy -> IO (FunPtr CDestroy)  ----@@ -907,10 +1059,15 @@  data CDirHandle -- fuse_dirh_t type CDirFil = Ptr CDirHandle -> CString -> Int -> IO CInt -- fuse_dirfil_t-foreign import ccall threadsafe "dynamic"+foreign import ccall safe "dynamic"     mkDirFil :: FunPtr CDirFil -> CDirFil  data CFillDirBuf -- void type CFillDir = Ptr CFillDirBuf -> CString -> Ptr CStat -> COff -> IO CInt-foreign import ccall threadsafe "dynamic"++foreign import ccall safe "dynamic"     mkFillDir :: FunPtr CFillDir -> CFillDir++foreign import ccall safe "bzero"+    bzero :: Ptr a -> Int -> IO ()+
− fuse_wrappers.c
@@ -1,11 +0,0 @@-#define FUSE_USE_VERSION 26--#include "fuse_wrappers.h"--int-fuse_main_wrapper (int argc,-                   char *argv[],-                   const struct fuse_operations *op)-{-    return (fuse_main (argc, argv, op, NULL));-}
− fuse_wrappers.h
@@ -1,3 +0,0 @@-#include <fuse.h>--int fuse_main_wrapper (int argc, char *argv[], const struct fuse_operations *op);