Merge branch 'rt-55953-readdir' into master
authorDaniel Frett <daniel.frett@ccci.org>
Fri, 25 Feb 2011 05:04:25 +0000 (00:04 -0500)
committerDaniel Frett <daniel.frett@ccci.org>
Fri, 25 Feb 2011 05:07:28 +0000 (00:07 -0500)
Conflicts:
AUTHORS
Fuse.xs

1  2 
AUTHORS
Fuse.pm
Fuse.xs

diff --combined AUTHORS
+++ b/AUTHORS
@@@ -9,8 -9,4 +9,9 @@@ Csaba Henk <csaba.henk@creo.hu> - updat
  Vladimir V. Kolpakov <w@sfgate.com> - contributed cleanup for warnings
  Andrew Chadwick <andrewc@cpan.org> - fixes for Ubuntu and dh-make-perl (fakeroot)
  Chris Dolan <cdolan@cpan.org> - fixes for MacFuse 1.1.0
 +Reuben Thomas <rrt@sc3d.org> - contributed examples/filter_attr_fs.pl
 +jaslong from CPAN::Forum - documentation patch for fuse_get_context
 +Justin Fletcher <gerph@gerph.org> - added file handles on open files
 +Derrik Pates <demon@now.ai> - added 64-bit support and fuse 2.6 binding
 +Daniel Frett <daniel.frett@ccci.org> - make static callbacks thread-safe
+ Alex Sudakov <cygakob@gmail.com> - fixes for readdir support
diff --combined Fuse.pm
index 8530154,255bb69..436d998
mode 100755,100644..100755
+++ b/Fuse.pm
@@@ -28,7 -28,7 +28,7 @@@ our %EXPORT_TAGS = 
  our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
  
  our @EXPORT = ();
 -our $VERSION = '0.09';
 +our $VERSION = '0.11';
  
  sub AUTOLOAD {
      # This AUTOLOAD is used to 'autoload' constants from the constant()
@@@ -77,10 -77,12 +77,10 @@@ bootstrap Fuse $VERSION
  sub main {
        my @names = qw(getattr readlink getdir mknod mkdir unlink rmdir symlink
                        rename link chmod chown truncate utime open read write statfs
-                       flush release fsync setxattr getxattr listxattr removexattr);
+                       flush release fsync setxattr getxattr listxattr removexattr opendir readdir releasedir fsyncdir);
        my @subs = map {undef} @names;
 -      my @validOpts = qw(ro allow_other default_permissions fsname use_ino nonempty);
        my $tmp = 0;
        my %mapping = map { $_ => $tmp++ } @names;
 -      my %optmap  = map { $_ => 1 } @validOpts;
        my @otherargs = qw(debug threaded mountpoint mountopts);
        my %otherargs = (debug=>0, threaded=>0, mountpoint=>"", mountopts=>"");
        while(my $name = shift) {
                        $subs[$mapping{$name}] = $subref;
                }
        }
 -      foreach my $opt ( map {m/^([^=]*)/; $1} split(/,/,$otherargs{mountopts}) ) {
 -        next if exists($optmap{$opt});
 -        croak "Fuse::main: invalid '$opt' argument in mountopts";
 -      }
        if($otherargs{threaded}) {
                # make sure threads are both available, and loaded.
                if($Config{useithreads}) {
@@@ -228,15 -234,6 +228,15 @@@ threads::shared.
  
  =back
  
 +=head3 Fuse::fuse_get_context
 + 
 + use Fuse "fuse_get_context";
 + my $caller_uid = fuse_get_context()->{"uid"};
 + my $caller_gid = fuse_get_context()->{"gid"};
 + my $caller_pid = fuse_get_context()->{"pid"};
 + 
 +Access context information about the current Fuse operation. 
 +
  =head2 FUNCTIONS YOUR FILESYSTEM MAY IMPLEMENT
  
  =head3 getattr
@@@ -288,7 -285,7 +288,7 @@@ example rv: return "/proc/self/fd/stdin
  Arguments:  Containing directory name.
  Returns a list: 0 or more text strings (the filenames), followed by a numeric errno (usually 0).
  
 -This is used to obtain directory listings.  Its opendir(), readdir(), filldir() and closedir() all in one call.
 +This is used to obtain directory listings.  It's opendir(), readdir(), filldir() and closedir() all in one call.
  
  example rv: return ('.', 'a', 'b', 0);
  
@@@ -373,31 -370,26 +373,31 @@@ Called to change access/modification ti
  =head3 open
  
  Arguments:  Pathname, numeric flags (which is an OR-ing of stuff like O_RDONLY
 -and O_SYNC, constants you can import from POSIX).
 -Returns an errno.
 +and O_SYNC, constants you can import from POSIX), fileinfo hash reference.
 +Returns an errno, a file handle (optional).
  
  No creation, or trunctation flags (O_CREAT, O_EXCL, O_TRUNC) will be passed to open().
 +The fileinfo hash reference contains flags from the Fuse open call which may be modified by the module. The only fields presently supported are:
 + direct_io (version 2.4 onwards)
 + keep_cache (version 2.4 onwards)
 + nonseekable (version 2.9 onwards)
  Your open() method needs only check if the operation is permitted for the given flags, and return 0 for success.
 +Optionally a file handle may be returned, which will be passed to subsequent read, write, flush, fsync and release calls.
  
  =head3 read
  
 -Arguments:  Pathname, numeric requestedsize, numeric offset.
 +Arguments:  Pathname, numeric requested size, numeric offset, file handle
  Returns a numeric errno, or a string scalar with up to $requestedsize bytes of data.
  
  Called in an attempt to fetch a portion of the file.
  
  =head3 write
  
 -Arguments:  Pathname, scalar buffer, numeric offset.  You can use length($buffer) to
 +Arguments:  Pathname, scalar buffer, numeric offset, file handle.  You can use length($buffer) to
  find the buffersize.
 -Returns an errno.
 +Returns length($buffer) if successful (number of bytes written).
  
 -Called in an attempt to write (or overwrite) a portion of the file.  Be prepared because $buffer could contain random binary data with NULLs and all sorts of other wonderful stuff.
 +Called in an attempt to write (or overwrite) a portion of the file.  Be prepared because $buffer could contain random binary data with NULs and all sorts of other wonderful stuff.
  
  =head3 statfs
  
@@@ -416,7 -408,7 +416,7 @@@ o
  
  =head3 flush
  
 -Arguments: Pathname
 +Arguments: Pathname, file handle
  Returns an errno or 0 on success.
  
  Called to synchronise any cached data. This is called before the file
@@@ -424,7 -416,7 +424,7 @@@ is closed. It may be called multiple ti
  
  =head3 release
  
 -Arguments: Pathname, numeric flags passed to open
 +Arguments: Pathname, numeric flags passed to open, file handle
  Returns an errno or 0 on success.
  
  Called to indicate that there are no more references to the file. Called once
diff --combined Fuse.xs
index 9b6e4b5,92b9510..04c9543
mode 100755,100644..100755
+++ b/Fuse.xs
@@@ -1,62 -1,33 +1,62 @@@
 +#define PERL_NO_GET_CONTEXT
  #include "EXTERN.h"
  #include "perl.h"
  #include "XSUB.h"
  
 +#include <fuse.h>
 +
 +/* Determine if threads support should be included */
  #ifdef USE_ITHREADS
  # ifdef I_PTHREAD
 -/* perl implements threads with pthread.  So, we use the pthread API for
 - * handling thread-local storage. */
 -#  include <pthread.h>
 -PerlInterpreter *master_interp = NULL;
 -static inline void create_perl_context() {
 -      if(master_interp) {
 -              PerlInterpreter *me = PERL_GET_CONTEXT;
 -              if(!me) {
 -                      PERL_SET_CONTEXT(master_interp);
 -                      me = perl_clone(master_interp, CLONEf_CLONE_HOST);
 -              }
 -      }
 -}
 -#  define FUSE_CONTEXT_PRE create_perl_context(); { dSP
 -#  define FUSE_CONTEXT_POST }
  #  define FUSE_USE_ITHREADS
  # else
 -#  error "Sorry, I don't know how to handle ithreads on this architecture."
 +#  warning "Sorry, I don't know how to handle ithreads on this architecture. Building non-threaded version"
  # endif
- #define N_CALLBACKS 25
 +#endif
 +
 +/* Global Data */
 +
 +#define MY_CXT_KEY "Fuse::_guts" XS_VERSION
++#define N_CALLBACKS 29
 +
 +typedef struct {
 +      SV *callback[N_CALLBACKS];
 +      HV *handles;
 +      tTHX self;
 +      int threaded;
 +      perl_mutex mutex;
 +} my_cxt_t;
 +START_MY_CXT;
 +
 +#ifdef FUSE_USE_ITHREADS
 +tTHX master_interp = NULL;
 +
 +#define CLONE_INTERP(parent) S_clone_interp(parent)
 +tTHX S_clone_interp(tTHX parent) {
 +      dMY_CXT_INTERP(parent);
 +      if(MY_CXT.threaded) {
 +              MUTEX_LOCK(&MY_CXT.mutex);
 +              PERL_SET_CONTEXT(parent);
 +              dTHX;
 +#if (PERL_VERSION > 10) || (PERL_VERSION == 10 && PERL_SUBVERSION >= 1)
 +              tTHX child = perl_clone(parent, CLONEf_CLONE_HOST);
 +#else
 +              tTHX child = perl_clone(parent, CLONEf_CLONE_HOST | CLONEf_KEEP_PTR_TABLE);
 +              ptr_table_free(PL_ptr_table);
 +              PL_ptr_table = NULL;
 +#endif
 +              MUTEX_UNLOCK(&MY_CXT.mutex);
 +              return child;
 +      }
 +      return NULL;
 +}
 +
 +# define FUSE_CONTEXT_PRE dTHX; if(!aTHX) aTHX = CLONE_INTERP(master_interp); { dMY_CXT; dSP;
 +# define FUSE_CONTEXT_POST }
  #else
 -# define FUSE_CONTEXT_PRE dSP
 +# define FUSE_CONTEXT_PRE dTHX; dMY_CXT; dSP;
  # define FUSE_CONTEXT_POST
  #endif
 -#include <fuse.h>
  
  #undef DEBUGf
  #if 0
  #define DEBUGf(a...)
  #endif
  
 -#define N_CALLBACKS 29
 -SV *_PLfuse_callbacks[N_CALLBACKS];
 +#define FH_KEY(fi) sv_2mortal(newSViv((fi)->fh))
 +#define FH_GETHANDLE(fi) S_fh_get_handle(aTHX_ aMY_CXT_ fi)
 +#define FH_STOREHANDLE(fi,sv) S_fh_store_handle(aTHX_ aMY_CXT_ fi, sv)
 +#define FH_RELEASEHANDLE(fi) S_fh_release_handle(aTHX_ aMY_CXT_ fi)
 +
 +SV *S_fh_get_handle(pTHX_ pMY_CXT_ struct fuse_file_info *fi) {
 +      SV *val;
 +      val = &PL_sv_undef;
 +      if(fi->fh != 0) {
 +              HE *he;
 +              if((he = hv_fetch_ent(MY_CXT.handles, FH_KEY(fi), 0, 0))) {
 +                      val = HeVAL(he);
 +                      mg_get(val);
 +              }
 +      }
 +      return val;
 +}
 +
 +void S_fh_release_handle(pTHX_ pMY_CXT_ struct fuse_file_info *fi) {
 +      if(fi->fh != 0) {
 +              (void)hv_delete_ent(MY_CXT.handles, FH_KEY(fi), 0, G_DISCARD);
 +              fi->fh = 0;
 +      }
 +}
 +
 +void S_fh_store_handle(pTHX_ pMY_CXT_ struct fuse_file_info *fi, SV *sv) {
 +      if(SvOK(sv)) {
 +#ifdef FUSE_USE_ITHREADS
 +              if(MY_CXT.threaded) {
 +                      SvSHARE(sv);
 +              }
 +#endif
 +              MAGIC *mg = mg_find(sv, PERL_MAGIC_shared_scalar);
 +              fi->fh = mg ? PTR2IV(mg->mg_ptr) : PTR2IV(sv);
 +              (void)hv_store_ent(MY_CXT.handles, FH_KEY(fi), sv, 0);
 +              mg_set(sv);
 +      }
 +}
  
  int _PLfuse_getattr(const char *file, struct stat *result) {
        int rv;
        PUSHMARK(SP);
        XPUSHs(sv_2mortal(newSVpv(file,strlen(file))));
        PUTBACK;
 -      rv = call_sv(_PLfuse_callbacks[0],G_ARRAY);
 +      rv = call_sv(MY_CXT.callback[0],G_ARRAY);
        SPAGAIN;
        if(rv != 13) {
                if(rv > 1) {
                result->st_ctime = POPi;
                result->st_mtime = POPi;
                result->st_atime = POPi;
 -              result->st_size = POPi;
 +              result->st_size = POPn; // we pop double here to support files larger than 4Gb (long limit)
                result->st_rdev = POPi;
                result->st_gid = POPi;
                result->st_uid = POPi;
  
  int _PLfuse_readlink(const char *file,char *buf,size_t buflen) {
        int rv;
 -      FUSE_CONTEXT_PRE;
        if(buflen < 1)
                return EINVAL;
 +      FUSE_CONTEXT_PRE;
        DEBUGf("readlink begin\n");
        ENTER;
        SAVETMPS;
        PUSHMARK(SP);
        XPUSHs(sv_2mortal(newSVpv(file,0)));
        PUTBACK;
 -      rv = call_sv(_PLfuse_callbacks[1],G_SCALAR);
 +      rv = call_sv(MY_CXT.callback[1],G_SCALAR);
        SPAGAIN;
        if(!rv)
                rv = -ENOENT;
        return rv;
  }
  
- #if 0
- /*
-  * This doesn't yet work... we alwas get ENOSYS when trying to use readdir().
-  * Well, of course, getdir() is fine as well.
-  */
-  int _PLfuse_readdir(const char *file, void *dirh, fuse_fill_dir_t dirfil, off_t off, struct fuse_file_info *fi) {
- #endif
+ int _PLfuse_opendir(const char *file, struct fuse_file_info *info) {
+     croak("opendir NOT IMPLEMENTED");
+ }
+ int _PLfuse_releasedir(const char *file, struct fuse_file_info *info) {
+     croak("releasedir NOT IMPLEMENTED");
+ }
+ int _PLfuse_fsyncdir(const char *file, struct fuse_file_info *info) {
+     croak("fsyncdir NOT IMPLEMENTED");
+ }
+ int _PLfuse_readdir(const char *file, void *dirh, fuse_fill_dir_t dirfil, off_t off, struct fuse_file_info *fi) {
+       int prv, rv, offset;
+     SV *entry;
+       FUSE_CONTEXT_PRE;
+       DEBUGf("readdir begin\n");
+       ENTER;
+       SAVETMPS;
+       PUSHMARK(SP);
+       XPUSHs(sv_2mortal(newSVpv(file,0)));
+       XPUSHs(sv_2mortal(newSViv(off)));
+       PUTBACK;
 -      prv = call_sv(_PLfuse_callbacks[26],G_ARRAY);
++      prv = call_sv(MY_CXT.callback[26],G_ARRAY);
+       SPAGAIN;
+       if(3 == prv) {
+         rv      = POPi;
+         offset  = POPi;
+         entry   = POPs;
+         if(SvOK(entry))
+             dirfil(dirh,SvPV_nolen(entry),NULL,offset);
+       } else {
+               fprintf(stderr,"readdir() handler didn't return 2 values!\n");
+               rv = -ENOSYS;
+       }
+       FREETMPS;
+       LEAVE;
+       PUTBACK;
+       DEBUGf("readdir end: %i\n",rv);
+       FUSE_CONTEXT_POST;
+       return rv;
+ }
  int _PLfuse_getdir(const char *file, fuse_dirh_t dirh, fuse_dirfil_t dirfil) {
        int prv, rv;
        FUSE_CONTEXT_PRE;
        PUSHMARK(SP);
        XPUSHs(sv_2mortal(newSVpv(file,0)));
        PUTBACK;
 -      prv = call_sv(_PLfuse_callbacks[2],G_ARRAY);
 +      prv = call_sv(MY_CXT.callback[2],G_ARRAY);
        SPAGAIN;
        if(prv) {
                rv = POPi;
@@@ -225,7 -194,7 +259,7 @@@ int _PLfuse_mknod (const char *file, mo
        XPUSHs(sv_2mortal(newSViv(mode)));
        XPUSHs(sv_2mortal(newSViv(dev)));
        PUTBACK;
 -      rv = call_sv(_PLfuse_callbacks[3],G_SCALAR);
 +      rv = call_sv(MY_CXT.callback[3],G_SCALAR);
        SPAGAIN;
        if(rv)
                rv = POPi;
@@@ -249,7 -218,7 +283,7 @@@ int _PLfuse_mkdir (const char *file, mo
        XPUSHs(sv_2mortal(newSVpv(file,0)));
        XPUSHs(sv_2mortal(newSViv(mode)));
        PUTBACK;
 -      rv = call_sv(_PLfuse_callbacks[4],G_SCALAR);
 +      rv = call_sv(MY_CXT.callback[4],G_SCALAR);
        SPAGAIN;
        if(rv)
                rv = POPi;
@@@ -273,7 -242,7 +307,7 @@@ int _PLfuse_unlink (const char *file) 
        PUSHMARK(SP);
        XPUSHs(sv_2mortal(newSVpv(file,0)));
        PUTBACK;
 -      rv = call_sv(_PLfuse_callbacks[5],G_SCALAR);
 +      rv = call_sv(MY_CXT.callback[5],G_SCALAR);
        SPAGAIN;
        if(rv)
                rv = POPi;
@@@ -296,7 -265,7 +330,7 @@@ int _PLfuse_rmdir (const char *file) 
        PUSHMARK(SP);
        XPUSHs(sv_2mortal(newSVpv(file,0)));
        PUTBACK;
 -      rv = call_sv(_PLfuse_callbacks[6],G_SCALAR);
 +      rv = call_sv(MY_CXT.callback[6],G_SCALAR);
        SPAGAIN;
        if(rv)
                rv = POPi;
@@@ -320,7 -289,7 +354,7 @@@ int _PLfuse_symlink (const char *file, 
        XPUSHs(sv_2mortal(newSVpv(file,0)));
        XPUSHs(sv_2mortal(newSVpv(new,0)));
        PUTBACK;
 -      rv = call_sv(_PLfuse_callbacks[7],G_SCALAR);
 +      rv = call_sv(MY_CXT.callback[7],G_SCALAR);
        SPAGAIN;
        if(rv)
                rv = POPi;
@@@ -344,7 -313,7 +378,7 @@@ int _PLfuse_rename (const char *file, c
        XPUSHs(sv_2mortal(newSVpv(file,0)));
        XPUSHs(sv_2mortal(newSVpv(new,0)));
        PUTBACK;
 -      rv = call_sv(_PLfuse_callbacks[8],G_SCALAR);
 +      rv = call_sv(MY_CXT.callback[8],G_SCALAR);
        SPAGAIN;
        if(rv)
                rv = POPi;
@@@ -368,7 -337,7 +402,7 @@@ int _PLfuse_link (const char *file, con
        XPUSHs(sv_2mortal(newSVpv(file,0)));
        XPUSHs(sv_2mortal(newSVpv(new,0)));
        PUTBACK;
 -      rv = call_sv(_PLfuse_callbacks[9],G_SCALAR);
 +      rv = call_sv(MY_CXT.callback[9],G_SCALAR);
        SPAGAIN;
        if(rv)
                rv = POPi;
@@@ -392,7 -361,7 +426,7 @@@ int _PLfuse_chmod (const char *file, mo
        XPUSHs(sv_2mortal(newSVpv(file,0)));
        XPUSHs(sv_2mortal(newSViv(mode)));
        PUTBACK;
 -      rv = call_sv(_PLfuse_callbacks[10],G_SCALAR);
 +      rv = call_sv(MY_CXT.callback[10],G_SCALAR);
        SPAGAIN;
        if(rv)
                rv = POPi;
@@@ -417,7 -386,7 +451,7 @@@ int _PLfuse_chown (const char *file, ui
        XPUSHs(sv_2mortal(newSViv(uid)));
        XPUSHs(sv_2mortal(newSViv(gid)));
        PUTBACK;
 -      rv = call_sv(_PLfuse_callbacks[11],G_SCALAR);
 +      rv = call_sv(MY_CXT.callback[11],G_SCALAR);
        SPAGAIN;
        if(rv)
                rv = POPi;
  
  int _PLfuse_truncate (const char *file, off_t off) {
        int rv;
 +#ifndef PERL_HAS_64BITINT
 +      char *temp;
 +#endif
        FUSE_CONTEXT_PRE;
        DEBUGf("truncate begin\n");
        ENTER;
        SAVETMPS;
        PUSHMARK(SP);
        XPUSHs(sv_2mortal(newSVpv(file,0)));
 +#ifdef PERL_HAS_64BITINT
        XPUSHs(sv_2mortal(newSViv(off)));
 +#else
 +      asprintf(&temp, "%llu", off);
 +      XPUSHs(sv_2mortal(newSVpv(temp, 0)));
 +      free(temp);
 +#endif
        PUTBACK;
 -      rv = call_sv(_PLfuse_callbacks[12],G_SCALAR);
 +      rv = call_sv(MY_CXT.callback[12],G_SCALAR);
        SPAGAIN;
        if(rv)
                rv = POPi;
@@@ -475,7 -435,7 +509,7 @@@ int _PLfuse_utime (const char *file, st
        XPUSHs(sv_2mortal(newSViv(uti->actime)));
        XPUSHs(sv_2mortal(newSViv(uti->modtime)));
        PUTBACK;
 -      rv = call_sv(_PLfuse_callbacks[13],G_SCALAR);
 +      rv = call_sv(MY_CXT.callback[13],G_SCALAR);
        SPAGAIN;
        if(rv)
                rv = POPi;
  int _PLfuse_open (const char *file, struct fuse_file_info *fi) {
        int rv;
        int flags = fi->flags;
 +      HV *fihash;
        FUSE_CONTEXT_PRE;
        DEBUGf("open begin\n");
        ENTER;
        PUSHMARK(SP);
        XPUSHs(sv_2mortal(newSVpv(file,0)));
        XPUSHs(sv_2mortal(newSViv(flags)));
 +      /* Create a hashref containing the details from fi
 +       * which we can look at or modify.
 +       */
 +      fi->fh = 0; /* Ensure it starts with 0 - important if they don't set it */
 +      fihash = newHV();
 +#if FUSE_VERSION >= 24
 +      hv_store(fihash, "direct_io", 9, newSViv(fi->direct_io), 0);
 +      hv_store(fihash, "keep_cache", 10, newSViv(fi->keep_cache), 0);
 +#endif
 +#if FUSE_VERSION >= 29
 +      hv_store(fihash, "nonseekable", 11, newSViv(fi->nonseekable), 0);
 +#endif
 +      XPUSHs(sv_2mortal(newRV_noinc((SV*) fihash)));
 +      /* All hashref things done */
 +
        PUTBACK;
 -      rv = call_sv(_PLfuse_callbacks[14],G_SCALAR);
 +      /* Open called with filename, flags */
 +      rv = call_sv(MY_CXT.callback[14],G_ARRAY);
        SPAGAIN;
 -      if(rv)
 +      if(rv) {
 +              if(rv > 1) {
 +                      FH_STOREHANDLE(fi,POPs);
 +              }
                rv = POPi;
 +      }
        else
                rv = 0;
 +      if (rv == 0)
 +      {
 +              /* Success, so copy the file handle which they returned */
 +#if FUSE_VERSION >= 24
 +              SV **svp;
 +              svp = hv_fetch(fihash, "direct_io", 9, 0);
 +              if (svp != NULL)
 +              {
 +                      fi->direct_io = SvIV(*svp);
 +              }
 +              svp = hv_fetch(fihash, "keep_cache", 10, 0);
 +              if (svp != NULL)
 +              {
 +                      fi->keep_cache = SvIV(*svp);
 +              }
 +#endif
 +#if FUSE_VERSION >= 29
 +              svp = hv_fetch(fihash, "nonseekable", 11, 0);
 +              if (svp != NULL)
 +              {
 +                      fi->nonseekable = SvIV(*svp);
 +              }
 +#endif
 +      }
        FREETMPS;
        LEAVE;
        PUTBACK;
  
  int _PLfuse_read (const char *file, char *buf, size_t buflen, off_t off, struct fuse_file_info *fi) {
        int rv;
 +#ifndef PERL_HAS_64BITINT
 +      char *temp;
 +#endif
        FUSE_CONTEXT_PRE;
        DEBUGf("read begin\n");
        ENTER;
        PUSHMARK(SP);
        XPUSHs(sv_2mortal(newSVpv(file,0)));
        XPUSHs(sv_2mortal(newSViv(buflen)));
 +#ifdef PERL_HAS_64BITINT
        XPUSHs(sv_2mortal(newSViv(off)));
 +#else
 +      asprintf(&temp, "%llu", off);
 +      XPUSHs(sv_2mortal(newSVpv(temp, 0)));
 +      free(temp);
 +#endif
 +      XPUSHs(FH_GETHANDLE(fi));
        PUTBACK;
 -      rv = call_sv(_PLfuse_callbacks[15],G_SCALAR);
 +      rv = call_sv(MY_CXT.callback[15],G_SCALAR);
        SPAGAIN;
        if(!rv)
                rv = -ENOENT;
  
  int _PLfuse_write (const char *file, const char *buf, size_t buflen, off_t off, struct fuse_file_info *fi) {
        int rv;
 +#ifndef PERL_HAS_64BITINT
 +      char *temp;
 +#endif
        FUSE_CONTEXT_PRE;
        DEBUGf("write begin\n");
        ENTER;
        PUSHMARK(SP);
        XPUSHs(sv_2mortal(newSVpv(file,0)));
        XPUSHs(sv_2mortal(newSVpvn(buf,buflen)));
 +#ifdef PERL_HAS_64BITINT
        XPUSHs(sv_2mortal(newSViv(off)));
 +#else
 +      asprintf(&temp, "%llu", off);
 +      XPUSHs(sv_2mortal(newSVpv(temp, 0)));
 +      free(temp);
 +#endif
 +      XPUSHs(FH_GETHANDLE(fi));
        PUTBACK;
 -      rv = call_sv(_PLfuse_callbacks[16],G_SCALAR);
 +      rv = call_sv(MY_CXT.callback[16],G_SCALAR);
        SPAGAIN;
        if(rv)
                rv = POPi;
@@@ -651,7 -546,7 +685,7 @@@ int _PLfuse_statfs (const char *file, s
        SAVETMPS;
        PUSHMARK(SP);
        PUTBACK;
 -      rv = call_sv(_PLfuse_callbacks[17],G_ARRAY);
 +      rv = call_sv(MY_CXT.callback[17],G_ARRAY);
        SPAGAIN;
        DEBUGf("statfs got %i params\n",rv);
        if(rv == 6 || rv == 7) {
@@@ -696,9 -591,8 +730,9 @@@ int _PLfuse_flush (const char *file, st
        SAVETMPS;
        PUSHMARK(SP);
        XPUSHs(sv_2mortal(newSVpv(file,0)));
 +      XPUSHs(FH_GETHANDLE(fi));
        PUTBACK;
 -      rv = call_sv(_PLfuse_callbacks[18],G_SCALAR);
 +      rv = call_sv(MY_CXT.callback[18],G_SCALAR);
        SPAGAIN;
        if(rv)
                rv = POPi;
@@@ -722,15 -616,13 +756,15 @@@ int _PLfuse_release (const char *file, 
        PUSHMARK(SP);
        XPUSHs(sv_2mortal(newSVpv(file,0)));
        XPUSHs(sv_2mortal(newSViv(flags)));
 +      XPUSHs(FH_GETHANDLE(fi));
        PUTBACK;
 -      rv = call_sv(_PLfuse_callbacks[19],G_SCALAR);
 +      rv = call_sv(MY_CXT.callback[19],G_SCALAR);
        SPAGAIN;
        if(rv)
                rv = POPi;
        else
                rv = 0;
 +      FH_RELEASEHANDLE(fi);
        FREETMPS;
        LEAVE;
        PUTBACK;
@@@ -749,9 -641,8 +783,9 @@@ int _PLfuse_fsync (const char *file, in
        PUSHMARK(SP);
        XPUSHs(sv_2mortal(newSVpv(file,0)));
        XPUSHs(sv_2mortal(newSViv(flags)));
 +      XPUSHs(FH_GETHANDLE(fi));
        PUTBACK;
 -      rv = call_sv(_PLfuse_callbacks[20],G_SCALAR);
 +      rv = call_sv(MY_CXT.callback[20],G_SCALAR);
        SPAGAIN;
        if(rv)
                rv = POPi;
@@@ -777,7 -668,7 +811,7 @@@ int _PLfuse_setxattr (const char *file
        XPUSHs(sv_2mortal(newSVpvn(buf,buflen)));
        XPUSHs(sv_2mortal(newSViv(flags)));
        PUTBACK;
 -      rv = call_sv(_PLfuse_callbacks[21],G_SCALAR);
 +      rv = call_sv(MY_CXT.callback[21],G_SCALAR);
        SPAGAIN;
        if(rv)
                rv = POPi;
@@@ -801,7 -692,7 +835,7 @@@ int _PLfuse_getxattr (const char *file
        XPUSHs(sv_2mortal(newSVpv(file,0)));
        XPUSHs(sv_2mortal(newSVpv(name,0)));
        PUTBACK;
 -      rv = call_sv(_PLfuse_callbacks[22],G_SCALAR);
 +      rv = call_sv(MY_CXT.callback[22],G_SCALAR);
        SPAGAIN;
        if(!rv)
                rv = -ENOENT;
@@@ -843,7 -734,7 +877,7 @@@ int _PLfuse_listxattr (const char *file
        PUSHMARK(SP);
        XPUSHs(sv_2mortal(newSVpv(file,0)));
        PUTBACK;
 -      prv = call_sv(_PLfuse_callbacks[23],G_ARRAY);
 +      prv = call_sv(MY_CXT.callback[23],G_ARRAY);
        SPAGAIN;
        if(!prv)
                rv = -ENOENT;
@@@ -908,7 -799,7 +942,7 @@@ int _PLfuse_removexattr (const char *fi
        XPUSHs(sv_2mortal(newSVpv(file,0)));
        XPUSHs(sv_2mortal(newSVpv(name,0)));
        PUTBACK;
 -      rv = call_sv(_PLfuse_callbacks[24],G_SCALAR);
 +      rv = call_sv(MY_CXT.callback[24],G_SCALAR);
        SPAGAIN;
        if(rv)
                rv = POPi;
@@@ -926,9 -817,6 +960,6 @@@ struct fuse_operations _available_ops 
  getattr:              _PLfuse_getattr,
  readlink:             _PLfuse_readlink,
  getdir:                       _PLfuse_getdir,
- #if 0
- readdir:              _PLfuse_readdir,
- #endif
  mknod:                        _PLfuse_mknod,
  mkdir:                        _PLfuse_mkdir,
  unlink:                       _PLfuse_unlink,
@@@ -951,53 -839,15 +982,57 @@@ setxattr:               _PLfuse_setxattr
  getxattr:             _PLfuse_getxattr,
  listxattr:            _PLfuse_listxattr,
  removexattr:          _PLfuse_removexattr,
+ opendir:              _PLfuse_opendir,
+ readdir:              _PLfuse_readdir,
+ releasedir:           _PLfuse_releasedir,
+ fsyncdir:             _PLfuse_fsyncdir,
  };
  
  MODULE = Fuse         PACKAGE = Fuse
  PROTOTYPES: DISABLE
  
 +BOOT:
 +      MY_CXT_INIT;
 +      MY_CXT.self = aTHX;
 +
 +void
 +CLONE(...)
 +      PREINIT:
 +              int i;
 +              dTHX;
 +      CODE:
 +              MY_CXT_CLONE;
 +              tTHX parent = MY_CXT.self;
 +              MY_CXT.self = my_perl;
 +#if (PERL_VERSION < 10) || (PERL_VERSION == 10 && PERL_SUBVERSION <= 0)
 +              /* CLONE entered without a pointer table, so we can't safely clone static data */
 +              if(!PL_ptr_table) {
 +                      for(i=0;i<N_CALLBACKS;i++) {
 +                              MY_CXT.callback[i] = NULL;
 +                      }
 +                      MY_CXT.handles = newHV();
 +              } else
 +#endif
 +              {
 +                      CLONE_PARAMS *clone_param;
 +#if (PERL_VERSION > 13) || (PERL_VERSION == 13 && PERL_SUBVERSION >= 2)
 +                      clone_param = clone_params_new(parent, aTHX);
 +#else
 +                      CLONE_PARAMS raw_param;
 +                      raw_param.flags = 0;
 +                      raw_param.proto_perl = parent;
 +                      raw_param.stashes = (AV*)sv_2mortal((SV*)newAV());
 +                      clone_param = &raw_param;
 +#endif
 +                      for(i=0;i<N_CALLBACKS;i++) {
 +                              MY_CXT.callback[i] = sv_dup(MY_CXT.callback[i], clone_param);
 +                      }
 +                      MY_CXT.handles = (HV*)sv_dup((SV*)MY_CXT.handles, clone_param);
 +#if (PERL_VERSION > 13) || (PERL_VERSION == 13 && PERL_SUBVERSION >= 2)
 +                      clone_params_del(clone_param);
 +#endif
 +              }
 +
  SV*
  fuse_get_context()
        PREINIT:
@@@ -1021,33 -871,28 +1056,33 @@@ perl_fuse_main(...
        PREINIT:
        struct fuse_operations fops = 
                {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
-                NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
+                NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
 -      int i, fd, debug, threaded;
 +      int i, debug;
        char *mountpoint;
        char *mountopts;
        struct fuse_args margs = FUSE_ARGS_INIT(0, NULL);
        struct fuse_args fargs = FUSE_ARGS_INIT(0, NULL);
 +      struct fuse_chan *fc;
 +      dMY_CXT;
        INIT:
-       if(items != 29) {
+       if(items != 4+N_CALLBACKS) {
                fprintf(stderr,"Perl<->C inconsistency or internal error\n");
                XSRETURN_UNDEF;
        }
        CODE:
        debug = SvIV(ST(0));
 -      threaded = SvIV(ST(1));
 -      if(threaded) {
 +      MY_CXT.threaded = SvIV(ST(1));
 +      MY_CXT.handles = (HV*)(sv_2mortal((SV*)(newHV())));
 +      if(MY_CXT.threaded) {
  #ifdef FUSE_USE_ITHREADS
 -              master_interp = PERL_GET_CONTEXT;
 +              master_interp = aTHX;
 +              MUTEX_INIT(&MY_CXT.mutex);
 +              SvSHARE((SV*)(MY_CXT.handles));
  #else
                fprintf(stderr,"FUSE warning: Your script has requested multithreaded "
 -                             "mode, but your perl was not built with -Dusethreads.  "
 -                             "Threads are disabled.\n");
 -              threaded = 0;
 +                             "mode, but your perl was not built with a supported "
 +                             "thread model. Threads are disabled.\n");
 +              MY_CXT.threaded = 0;
  #endif
        }
        mountpoint = SvPV_nolen(ST(2));
                if(SvOK(var) && (SvPOK(var) || (SvROK(var) && SvTYPE(SvRV(var)) == SVt_PVCV))) {
                        void **tmp1 = (void**)&_available_ops, **tmp2 = (void**)&fops;
                        tmp2[i] = tmp1[i];
 -#ifdef FUSE_USE_ITHREADS
 -                      if(threaded)
 -                /* note: under 5.8.7, this croaks for code references. */
 -                SvSHARE(var);
 -#endif
 -                      _PLfuse_callbacks[i] = var;
 -              } else
 -              if(SvOK(var)) {
 -                      croak("invalid callback passed to perl_fuse_main "
 +                      MY_CXT.callback[i] = var;
 +              } else if(SvOK(var)) {
 +                      croak("invalid callback (%i) passed to perl_fuse_main "
                              "(%s is not a string, code ref, or undef).\n",
                              i+4,SvPVbyte_nolen(var));
 +              } else {
 +                      MY_CXT.callback[i] = NULL;
                }
        }
        /*
                fuse_opt_free_args(&margs);
                croak("out of memory\n");
        }
 -      fd = fuse_mount(mountpoint,&margs);
 +      fc = fuse_mount(mountpoint,&margs);
        fuse_opt_free_args(&margs);        
 -      if(fd < 0)
 +      if (fc == NULL)
                croak("could not mount fuse filesystem!\n");
          if (debug) {
                if ( fuse_opt_add_arg(&fargs, "") == -1 ||
                if (fuse_opt_add_arg(&fargs, "") == -1)
                        croak("out of memory\n");
        }
 -
 -      if(threaded) {
 -              fuse_loop_mt(fuse_new(fd,&fargs,&fops,sizeof(fops)));
 +#ifndef __NetBSD__
 +      if(MY_CXT.threaded) {
 +              fuse_loop_mt(fuse_new(fc,&fargs,&fops,sizeof(fops),NULL));
        } else
 -              fuse_loop(fuse_new(fd,&fargs,&fops,sizeof(fops)));
 +#endif
 +              fuse_loop(fuse_new(fc,&fargs,&fops,sizeof(fops),NULL));
 +      fuse_unmount(mountpoint,fc);
        fuse_opt_free_args(&fargs);