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 +4/−6
- README +15/−0
- System/Fuse.hsc +236/−79
- fuse_wrappers.c +0/−11
- fuse_wrappers.h +0/−3
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);