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

14 files changed:
AUTHORS
Changes
Fuse.pm [changed mode: 0644->0755]
Fuse.xs [changed mode: 0644->0755]
Makefile.PL
README
examples/example.pl [changed mode: 0644->0755]
examples/example_t.pl [changed mode: 0644->0755]
examples/filter_attr_fs.pl [new file with mode: 0755]
examples/loopback.pl [changed mode: 0644->0755]
examples/loopback_t.pl [changed mode: 0644->0755]
examples/rmount.pl [changed mode: 0644->0755]
examples/rmount_remote.pl [changed mode: 0644->0755]
test/getattr.t

diff --git a/AUTHORS b/AUTHORS
index ffc2de9..0599877 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -9,4 +9,9 @@ Csaba Henk <csaba.henk@creo.hu> - update to API 25
 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 --git a/Changes b/Changes
index ca2fa50..b3ff105 100644 (file)
--- a/Changes
+++ b/Changes
@@ -46,3 +46,22 @@ Revision history for Perl extension Fuse.
     - allow Fuse to be run from a non-master thread
     - CPANPLUS doesn't report errors anymore if fuse isn't installed
     - fix to test helper
+
+0.09
+    - support dh-make-perl with fakeroot
+    - added fuse_get_context
+    - works with MacFUSE http://code.google.com/p/macfuse/
+    - added example filter_attr_fs.pl
+
+0.09_3
+    - really fix 2+ Gb file bug, RT #32639, RT #33903
+
+0.09_4
+       - Justin Fletcher addition of file handles on open files, RT #57517
+
+0.10_1
+       - cleanup options
+       - 64 bit perl support submitted by Derrik Pates
+
+0.11
+       - make static callbacks thread-safe, contributed by Daniel Frett
diff --git a/Fuse.pm b/Fuse.pm
old mode 100644 (file)
new mode 100755 (executable)
index 255bb69..436d998
--- a/Fuse.pm
+++ b/Fuse.pm
@@ -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()
@@ -79,10 +79,8 @@ sub main {
                        rename link chmod chown truncate utime open read write statfs
                        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) {
@@ -95,10 +93,6 @@ sub main {
                        $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}) {
@@ -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
@@ -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);
 
@@ -370,26 +373,31 @@ Called to change access/modification times for a file/directory/device/symlink.
 =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
 
@@ -408,7 +416,7 @@ or
 
 =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
@@ -416,7 +424,7 @@ is closed. It may be called multiple times before a file is closed.
 
 =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 --git a/Fuse.xs b/Fuse.xs
old mode 100644 (file)
new mode 100755 (executable)
index 92b9510..04c9543
--- a/Fuse.xs
+++ b/Fuse.xs
@@ -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
+#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
@@ -36,8 +65,44 @@ static inline void create_perl_context() {
 #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;
@@ -48,7 +113,7 @@ int _PLfuse_getattr(const char *file, struct stat *result) {
        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) {
@@ -64,7 +129,7 @@ int _PLfuse_getattr(const char *file, struct stat *result) {
                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;
@@ -84,16 +149,16 @@ int _PLfuse_getattr(const char *file, struct stat *result) {
 
 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;
@@ -136,7 +201,7 @@ int _PLfuse_readdir(const char *file, void *dirh, fuse_fill_dir_t dirfil, off_t
        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;
@@ -165,7 +230,7 @@ int _PLfuse_getdir(const char *file, fuse_dirh_t dirh, fuse_dirfil_t dirfil) {
        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;
@@ -194,7 +259,7 @@ int _PLfuse_mknod (const char *file, mode_t mode, dev_t dev) {
        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;
@@ -218,7 +283,7 @@ int _PLfuse_mkdir (const char *file, mode_t mode) {
        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;
@@ -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;
@@ -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;
@@ -289,7 +354,7 @@ int _PLfuse_symlink (const char *file, const char *new) {
        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;
@@ -313,7 +378,7 @@ int _PLfuse_rename (const char *file, const char *new) {
        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;
@@ -337,7 +402,7 @@ int _PLfuse_link (const char *file, const char *new) {
        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;
@@ -361,7 +426,7 @@ int _PLfuse_chmod (const char *file, mode_t mode) {
        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;
@@ -386,7 +451,7 @@ int _PLfuse_chown (const char *file, uid_t uid, gid_t gid) {
        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;
@@ -402,15 +467,24 @@ int _PLfuse_chown (const char *file, uid_t uid, gid_t gid) {
 
 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;
@@ -435,7 +509,7 @@ int _PLfuse_utime (const char *file, struct utimbuf *uti) {
        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;
@@ -452,6 +526,7 @@ int _PLfuse_utime (const char *file, struct utimbuf *uti) {
 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;
@@ -459,13 +534,57 @@ int _PLfuse_open (const char *file, struct fuse_file_info *fi) {
        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;
@@ -476,6 +595,9 @@ int _PLfuse_open (const char *file, struct fuse_file_info *fi) {
 
 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;
@@ -483,9 +605,16 @@ int _PLfuse_read (const char *file, char *buf, size_t buflen, off_t off, struct
        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;
@@ -515,6 +644,9 @@ int _PLfuse_read (const char *file, char *buf, size_t buflen, off_t off, struct
 
 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;
@@ -522,9 +654,16 @@ int _PLfuse_write (const char *file, const char *buf, size_t buflen, off_t off,
        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;
@@ -546,7 +685,7 @@ int _PLfuse_statfs (const char *file, struct statvfs *st) {
        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) {
@@ -591,8 +730,9 @@ int _PLfuse_flush (const char *file, struct fuse_file_info *fi) {
        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;
@@ -616,13 +756,15 @@ int _PLfuse_release (const char *file, struct fuse_file_info *fi) {
        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;
@@ -641,8 +783,9 @@ int _PLfuse_fsync (const char *file, int datasync, struct fuse_file_info *fi) {
        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;
@@ -668,7 +811,7 @@ int _PLfuse_setxattr (const char *file, const char *name, const char *buf, size_
        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;
@@ -692,7 +835,7 @@ int _PLfuse_getxattr (const char *file, const char *name, char *buf, size_t bufl
        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;
@@ -734,7 +877,7 @@ int _PLfuse_listxattr (const char *file, char *list, size_t size) {
        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;
@@ -799,7 +942,7 @@ int _PLfuse_removexattr (const char *file, const char *name) {
        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;
@@ -848,6 +991,48 @@ 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:
@@ -872,11 +1057,13 @@ perl_fuse_main(...)
        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};
-       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 != 4+N_CALLBACKS) {
                fprintf(stderr,"Perl<->C inconsistency or internal error\n");
@@ -884,15 +1071,18 @@ perl_fuse_main(...)
        }
        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));
@@ -903,17 +1093,13 @@ perl_fuse_main(...)
                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;
                }
        }
        /*
@@ -928,9 +1114,9 @@ perl_fuse_main(...)
                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 ||
@@ -942,9 +1128,11 @@ perl_fuse_main(...)
                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);
index 1e05f5d..1522ff5 100644 (file)
@@ -1,4 +1,5 @@
 use ExtUtils::MakeMaker;
+use Config;
 # See lib/ExtUtils/MakeMaker.pm for details of how to influence
 # the contents of the Makefile that is written.
 
@@ -27,10 +28,11 @@ if ($ver && $ver + 0 < 2.5) {
        warn "fuse version found: ", $ver || $ver2 || $ver3, "\n";
 }
 
-my $inc = '-DFUSE_USE_VERSION=25 ' . `pkg-config --cflags fuse` || '-I ../include -D_FILE_OFFSET_BITS=64';
-my $obj = `pkg-config --libs fuse` || '-lfuse';
+my $inc = '-DFUSE_USE_VERSION=26 ' . `pkg-config --cflags fuse` || '-I ../include -D_FILE_OFFSET_BITS=64';
+my $obj = `pkg-config --libs fuse` || (($^O eq 'netbsd') ? '-lrefuse' : '-lfuse');
 my $def = '-Wall -g -ggdb';
 $def .= ' -D__FreeBSD__=10 -D_FILE_OFFSET_BITS=64' if $^O eq 'darwin';
+$def .= ' -DPERL_HAS_64BITINT' if $Config{'use64bitint'};
 
 WriteMakefile(
        'NAME'                  => 'Fuse',
@@ -39,6 +41,14 @@ WriteMakefile(
        ($] >= 5.005 ?  ## Add these new keywords supported since 5.005
                (ABSTRACT_FROM  => 'Fuse.pm', # retrieve abstract from module
                AUTHOR                  => 'Mark Glines <mark@glines.org>') : ()),
+               ($ExtUtils::MakeMaker::VERSION < 6.46 ? () : (
+                       META_MERGE => {
+                               resources => {
+                               bugtracker => 'https://rt.cpan.org/Public/Dist/Display.html?Name=Fuse',
+                               repository => 'http://github.com/dpavlin/perl-fuse'
+                       }
+               })
+       ),
        'LIBS'                  => [''], # e.g., '-lm'
        'DEFINE'                => $def, # e.g., '-DHAVE_SOMETHING'
        # Insert -I. if you add *.h files later:
@@ -50,6 +60,18 @@ WriteMakefile(
 sub MY::postamble {
        return <<'MAKE_MORE';
 
+cpan:
+       make clean
+       rm -f Fuse-*.tar.gz
+       perl Makefile.PL
+       make dist
+       make disttest
+       @echo
+       @echo -n "Upload" Fuse-*.tar.gz "to CPAN? [y/N]:"
+       @read upload && test "$$upload" == "y" && cpan-upload -verbose Fuse-*.tar.gz
+
+
+
 sf:
        svn2cvs.pl file:///home/dpavlin/private/svn/fuse/perl-llin :ext:dpavlin@fuse.cvs.sourceforge.net:/cvsroot/fuse perl
 
diff --git a/README b/README
index 1fc07ac..0987f39 100644 (file)
--- a/README
+++ b/README
@@ -1,5 +1,5 @@
-Fuse version 0.07
-=================
+Fuse perl bindings
+==================
 
 Fuse is combination of Linux kernel module and user space library which
 enables you to write user-space filesystems. This module enables you to
old mode 100644 (file)
new mode 100755 (executable)
index ed7ce57..f25a997
@@ -1,7 +1,9 @@
 #!/usr/bin/perl -w
 use strict;
 
-use blib;
+use Data::Dumper;
+
+#use blib;
 use Fuse qw(fuse_get_context);
 use POSIX qw(ENOENT EISDIR EINVAL);
 
@@ -63,19 +65,25 @@ sub e_getdir {
 
 sub e_open {
        # VFS sanity check; it keeps all the necessary state, not much to do here.
-       my ($file) = filename_fixup(shift);
-       print("open called\n");
+    my $file = filename_fixup(shift);
+    my ($flags, $fileinfo) = @_;
+    print("open called $file, $flags, $fileinfo\n");
        return -ENOENT() unless exists($files{$file});
        return -EISDIR() if $files{$file}{type} & 0040;
-       print("open ok\n");
-       return 0;
+    
+    my $fh = [ rand() ];
+    
+    print("open ok (handle $fh)\n");
+    return (0, $fh);
 }
 
 sub e_read {
        # return an error numeric, or binary/text string.  (note: 0 means EOF, "0" will
        # give a byte (ascii "0") to the reading program)
        my ($file) = filename_fixup(shift);
-       my ($buf,$off) = @_;
+    my ($buf, $off, $fh) = @_;
+    print "read from $file, $buf \@ $off\n";
+    print "file handle:\n", Dumper($fh);
        return -ENOENT() unless exists($files{$file});
        if(!exists($files{$file}{cont})) {
                return -EINVAL() if $off > 0;
old mode 100644 (file)
new mode 100755 (executable)
diff --git a/examples/filter_attr_fs.pl b/examples/filter_attr_fs.pl
new file mode 100755 (executable)
index 0000000..e3410c6
--- /dev/null
@@ -0,0 +1,279 @@
+#!/usr/bin/perl -w
+# filter_attr_t.pl
+# Loopback fs that shows only files with a particular xattr
+
+# (c) Reuben Thomas  29/11/2007-5/1/2008, based on example code from Fuse package
+
+use strict;
+#use blib;
+
+use Fuse;
+use File::ExtAttr ':all';
+use IO::File;
+use POSIX qw(ENOENT ENOSYS EEXIST EPERM O_RDONLY O_RDWR O_APPEND O_CREAT O_ACCMODE);
+use Fcntl qw(S_ISBLK S_ISCHR S_ISFIFO SEEK_SET);
+
+# Debug flag
+my $debug = 0;
+
+# Global settings
+my ($tag, $real_root, $mountpoint);
+
+
+sub debug {
+       print STDERR shift if $debug ne 0;
+}
+
+my $can_syscall = eval {
+       require 'syscall.ph'; # for SYS_mknod and SYS_lchown
+};
+
+if (!$can_syscall && open my $fh, '<', '/usr/include/sys/syscall.h') {
+       my %sys = do { local $/ = undef;
+                       <$fh> =~ m/\#define \s+ (\w+) \s+ (\d+)/gxms;
+               };
+       close $fh;
+       if ($sys{SYS_mknod} && $sys{SYS_lchown}) {
+               *SYS_mknod  = sub { $sys{SYS_mknod}  };
+               *SYS_lchown = sub { $sys{SYS_lchown} };
+               $can_syscall = 1;
+       }
+}
+
+sub tagged {
+       my ($file) = @_;
+       $file =~ s|/$||;
+       my $ret = getfattr($file, $tag);
+       debug("tagged: $file $tag " . defined($ret) . "\n");
+       return $ret;
+}
+
+sub tag {
+       return setfattr(shift, $tag, "");
+}
+
+sub detag {
+       return delfattr(shift, $tag);
+}
+
+sub append_root {
+       return $real_root . shift;
+}
+
+sub err {
+       my ($err) = @_;
+       return $err ? 0 : -$!;
+}
+
+sub x_getattr {
+       debug("x_getattr ");
+       my ($file) = append_root(shift);
+       return -ENOENT() unless tagged($file);
+       my (@list) = lstat($file);
+       return -$! unless @list;
+       return @list;
+}
+
+sub x_readlink {
+       debug("x_readlink ");
+       return readlink(append_root(shift));
+}
+
+sub x_getdir {
+       debug("x_getdir ");
+       my ($dirname) = append_root(shift);
+       return -ENOENT() unless tagged($dirname) && opendir(DIRHANDLE, $dirname);
+       my (@files) = readdir(DIRHANDLE);
+       closedir(DIRHANDLE);
+       my @psifiles = grep {tagged("$dirname/$_")} @files;
+       return (@psifiles, 0);
+}
+
+sub x_mknod {
+       my ($file, $modes, $dev) = @_;
+       return -ENOSYS() if !$can_syscall;
+       debug("x_mknod ");
+       $file = append_root($file);
+       return -EEXIST() if -e $file && !tagged($file);
+       $! = 0;
+       syscall(&SYS_mknod, $file, $modes, $dev);
+       return -$! if $! != 0;
+       return err(tag($file));
+}
+
+sub x_mkdir {
+       debug("x_mkdir ");
+       my ($name, $perm) = @_;
+       $name = append_root($name);
+       debug("$name");
+       my $ret = err(mkdir $name, $perm);
+       return $ret if $ret != 0;
+       return err(tag($name));
+}
+
+sub x_open {
+       my ($file) = append_root(shift);
+       my ($mode) = shift;
+       my $accmode = $mode & O_ACCMODE;
+       debug("x_open $accmode " . O_ACCMODE . " " . O_WRONLY . " " . O_RDWR . " ");
+       if ($accmode == O_WRONLY || $accmode == O_RDWR) {
+               return -EEXIST() if -e $file && !tagged($file);
+       } else {
+               return -ENOENT() unless tagged($file);
+       }
+       return -$! unless sysopen(FILE, $file, $mode);
+       close(FILE);
+       return 0;
+}
+
+sub x_read {
+       debug("x_read ");
+       my ($file, $bufsize, $off) = @_;
+       my ($rv) = -ENOSYS();
+       my ($handle) = new IO::File;
+       $file = append_root($file);
+       return -ENOENT() unless tagged($file);
+       my ($fsize) = -s $file;
+       return -ENOSYS() unless open($handle, $file);
+       if(seek($handle, $off, SEEK_SET)) {
+               read($handle, $rv, $bufsize);
+       }
+       return $rv;
+}
+
+sub x_write {
+       debug("x_write ");
+       my ($file, $buf, $off) = @_;
+       my ($rv);
+       $file = append_root($file);
+       return -ENOENT() unless tagged($file);
+       my ($fsize) = -s $file;
+       return -ENOSYS() unless open(FILE, '+<', $file);
+       if ($rv = seek(FILE, $off, SEEK_SET)) {
+               $rv = print(FILE $buf);
+       }
+       $rv = -ENOSYS() unless $rv;
+       close(FILE);
+       return length($buf);
+}
+
+sub x_unlink {
+       debug("x_unlink ");
+       my ($file) = append_root(shift);
+       return -ENOENT() unless tagged($file);
+       return err(detag($file));
+}
+
+sub x_symlink {
+       debug("x_symlink ");
+       my ($old) = shift;
+       my ($new) = append_root(shift);
+       return -EEXIST() if -e $new && !tagged($new);
+       return err(symlink($old, $new));
+}
+
+sub x_rename {
+       debug("x_rename ");
+       my ($old) = append_root(shift);
+       my ($new) = append_root(shift);
+       return -ENOENT() unless tagged($old);
+       return -EEXIST() unless !-e $new || tagged($new);
+       my ($err) = rename($old, $new) ? 0 : -ENOENT();
+       return $err;
+}
+
+sub x_link {
+       debug("x_link ");
+       my ($old) = append_root(shift);
+       my ($new) = append_root(shift);
+       return -ENOENT() unless tagged($old);
+       return -EEXIST() unless !-e $new || tagged($new);
+       return err(link($old, $new));
+}
+
+sub x_chown {
+       return -ENOSYS() if !$can_syscall;
+       debug("x_chown ");
+       my ($fn) = append_root(shift);
+       return -ENOENT() unless tagged($fn);
+       my ($uid, $gid) = @_;
+       # perl's chown() does not chown symlinks, it chowns the symlink's
+       # target. It fails when the link's target doesn't exist, because
+       # the stat64() syscall fails.
+       # This causes error messages when unpacking symlinks in tarballs.
+       my ($err) = syscall(&SYS_lchown, $fn, $uid, $gid, $fn) ? -$! : 0;
+       return $err;
+}
+
+sub x_chmod {
+       debug("x_chmod ");
+       my ($fn) = append_root(shift);
+       return -ENOENT() unless tagged($fn);
+       my ($mode) = shift;
+       return err(chmod($mode, $fn));
+}
+
+sub x_truncate {
+       debug("x_truncate ");
+       my ($fn) = append_root(shift);
+       return -ENOENT() unless tagged($fn);
+       return err(truncate($fn, shift));
+}
+
+sub x_utime {
+       debug("x_utime ");
+       my ($fn) = append_root($_[0]);
+       return -ENOENT() unless tagged($fn);
+       return err(utime($_[1], $_[2], $fn));
+}
+
+sub x_rmdir {
+       debug("x_rmdir ");
+       my $dir = append_root(shift);
+       return -ENOENT() unless tagged($dir);
+       return err(detag($dir));
+}
+
+sub x_statfs {
+       debug("x_statfs\n");
+       my $name = append_root(shift);
+       my($bsize, $frsize, $blocks, $bfree, $bavail,
+               $files, $ffree, $favail, $fsid, $basetype, $flag,
+               $namemax, $fstr) = statvfs($real_root) || return -$!;
+       return ($namemax, $files, $ffree, $blocks, $bavail, $bsize);
+}
+
+# If you run the script directly, it will run fusermount, which will in turn
+# re-run this script.  Hence the funky semantics.
+
+# Parse command-line arguments
+$mountpoint = "";
+if (@ARGV) {
+       $tag = shift(@ARGV);
+       $real_root = shift(@ARGV);
+       $mountpoint = shift(@ARGV);
+}
+
+# Start up FUSE
+Fuse::main(
+       mountpoint=>$mountpoint,
+#      debug   => 1,
+       getattr =>"main::x_getattr",
+       readlink=>"main::x_readlink",
+       getdir  =>"main::x_getdir",
+       mknod   =>"main::x_mknod",
+       mkdir   =>"main::x_mkdir",
+       unlink  =>"main::x_unlink",
+       rmdir   =>"main::x_rmdir",
+       symlink =>"main::x_symlink",
+       rename  =>"main::x_rename",
+       link    =>"main::x_link",
+       chmod   =>"main::x_chmod",
+       chown   =>"main::x_chown",
+       truncate=>"main::x_truncate",
+       utime   =>"main::x_utime",
+       open    =>"main::x_open",
+       read    =>"main::x_read",
+       write   =>"main::x_write",
+       statfs  =>"main::x_statfs",
+);
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
index 2e6f017..a945cdc 100644 (file)
@@ -2,41 +2,50 @@
 use test::helper qw($_real $_point);
 use Test::More;
 use Data::Dumper;
-plan tests => 28;
-my ($a, $b) = ("$_real/wibble","$_point/wibble");
-`touch $b`;
-is(-A "$a", -A "$b", '-A'); # 1
-is(-B "$a", -B "$b", '-B'); # 2
-is(-C "$a", -C "$b", '-C'); # 3
-is(-M "$a", -M "$b", '-M'); # 4
-is(-O "$a", -O "$b", '-O'); # 5
-is(-R "$a", -R "$b", '-R'); # 6
-is(-S "$a", -S "$b", '-S'); # 7
-is(-T "$a", -T "$b", '-T'); # 8
-is(-W "$a", -W "$b", '-W'); # 9
-is(-X "$a", -X "$b", '-X'); # 10
-is(-b "$a", -b "$b", '-b'); # 11
-is(-c "$a", -c "$b", '-c'); # 12
-is(-d "$a", -d "$b", '-d'); # 13
-is(-e "$a", -e "$b", '-e'); # 14
-is(-f "$a", -f "$b", '-f'); # 15
-is(-g "$a", -g "$b", '-g'); # 16
-is(-k "$a", -k "$b", '-k'); # 17
-is(-l "$a", -l "$b", '-l'); # 18
-is(-o "$a", -o "$b", '-o'); # 19
-is(-p "$a", -p "$b", '-p'); # 20
-is(-r "$a", -r "$b", '-r'); # 21
-is(-s "$a", -s "$b", '-s'); # 22
-is(-t "$a", -t "$b", '-t'); # 23
-is(-u "$a", -u "$b", '-u'); # 24
-is(-w "$a", -w "$b", '-w'); # 25
-is(-x "$a", -x "$b", '-x'); # 26
-is(-z "$a", -z "$b", '-z'); # 27
-my (@astat, @bstat);
-@astat = stat("$a");
-@bstat = stat("$b");
-# dev, inode and blksize can legally change
-@astat = @astat[2..10,12];
-@bstat = @bstat[2..10,12];
-is(join(" ",@astat),join(" ",@bstat),"stat()");
-`rm -f $a`;
+plan tests => 203;
+sub test_file {
+       my $size = shift;
+       my ($a, $b) = ("$_real/wibble-$size","$_point/wibble-$size");
+#      diag "test $size Gb file";
+       open(my $fh, '>', $a) || die "can't open $b: $!";
+       seek($fh, $size * 1024 * 1024 * 1024, 0);
+       print $fh ' ';
+       close($fh);
+#      diag "size $b = ",-s $b, " $a = ", -s $a;
+       is(-A "$a", -A "$b", '-A'); # 1
+       is(-B "$a", -B "$b", '-B'); # 2
+       is(-C "$a", -C "$b", '-C'); # 3
+       is(-M "$a", -M "$b", '-M'); # 4
+       is(-O "$a", -O "$b", '-O'); # 5
+       is(-R "$a", -R "$b", '-R'); # 6
+       is(-S "$a", -S "$b", '-S'); # 7
+       is(-T "$a", -T "$b", '-T'); # 8
+       is(-W "$a", -W "$b", '-W'); # 9
+       is(-X "$a", -X "$b", '-X'); # 10
+       is(-b "$a", -b "$b", '-b'); # 11
+       is(-c "$a", -c "$b", '-c'); # 12
+       is(-d "$a", -d "$b", '-d'); # 13
+       is(-e "$a", -e "$b", '-e'); # 14
+       is(-f "$a", -f "$b", '-f'); # 15
+       is(-g "$a", -g "$b", '-g'); # 16
+       is(-k "$a", -k "$b", '-k'); # 17
+       is(-l "$a", -l "$b", '-l'); # 18
+       is(-o "$a", -o "$b", '-o'); # 19
+       is(-p "$a", -p "$b", '-p'); # 20
+       is(-r "$a", -r "$b", '-r'); # 21
+       is(-s "$a", -s "$b", '-s'); # 22
+       is(-t "$a", -t "$b", '-t'); # 23
+       is(-u "$a", -u "$b", '-u'); # 24
+       is(-w "$a", -w "$b", '-w'); # 25
+       is(-x "$a", -x "$b", '-x'); # 26
+       is(-z "$a", -z "$b", '-z'); # 27
+       my (@astat, @bstat);
+       @astat = stat("$a");
+       @bstat = stat("$b");
+       # dev, inode and blksize can legally change
+       @astat = @astat[2..10,12];
+       @bstat = @bstat[2..10,12];
+       is(join(" ",@astat),join(" ",@bstat),"stat()");
+       ok( unlink($a), 'unlink' );
+}
+test_file( $_ ) foreach ( 1, 2, 4, 8, 16, 32, 64 );