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
- 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
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()
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) {
$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}) {
=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
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);
=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
=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
=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
+#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
#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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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) {
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;
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;
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;
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;
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;
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;
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;
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:
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");
}
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);
use ExtUtils::MakeMaker;
+use Config;
# See lib/ExtUtils/MakeMaker.pm for details of how to influence
# the contents of the Makefile that is written.
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',
($] >= 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:
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
-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
#!/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);
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;
--- /dev/null
+#!/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",
+);
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 );