perl bindings
authorMiklos Szeredi <miklos@szeredi.hu>
Mon, 3 Dec 2001 08:35:41 +0000 (08:35 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Mon, 3 Dec 2001 08:35:41 +0000 (08:35 +0000)
AUTHORS
perl/Changes [new file with mode: 0644]
perl/Fuse.pm [new file with mode: 0644]
perl/Fuse.xs [new file with mode: 0644]
perl/MANIFEST [new file with mode: 0644]
perl/Makefile.PL [new file with mode: 0644]
perl/README [new file with mode: 0644]
perl/example.pl [new file with mode: 0755]
perl/test.pl [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
index 5d0a86a98d24d0ff0f145693b01ee55e9fffe2b1..a81e3c043aae903e4918d4c9ae341810d9501958 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -8,3 +8,9 @@ Python bindings
 ---------------
 
 Jeff Epler         <jepler@unpythonic.dhs.org>
+
+
+Perl bindings
+-------------
+
+Mark Glines        <mark@glines.org>
diff --git a/perl/Changes b/perl/Changes
new file mode 100644 (file)
index 0000000..3981203
--- /dev/null
@@ -0,0 +1,8 @@
+Revision history for Perl extension Fuse.
+
+0.01  Wed Nov 28 21:45:20 2001
+       - original version; created by h2xs 1.21 with options
+               include/fuse.h
+
+0.02 Sun Dec 2 18:59:56 2001
+    - works well enough to release, but still needs testing
diff --git a/perl/Fuse.pm b/perl/Fuse.pm
new file mode 100644 (file)
index 0000000..2f4742a
--- /dev/null
@@ -0,0 +1,228 @@
+package Fuse;
+
+use 5.006;
+use strict;
+use warnings;
+use Errno;
+use Carp;
+
+require Exporter;
+require DynaLoader;
+use AutoLoader;
+use Data::Dumper;
+our @ISA = qw(Exporter DynaLoader);
+
+# Items to export into callers namespace by default. Note: do not export
+# names by default without a very good reason. Use EXPORT_OK instead.
+# Do not simply export all your public functions/methods/constants.
+
+# This allows declaration      use Fuse ':all';
+# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
+# will save memory.
+our %EXPORT_TAGS = ( 'all' => [ qw(
+       FUSE_DEBUG
+) ] );
+
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+
+our @EXPORT = qw(
+       FUSE_DEBUG
+);
+our $VERSION = '0.01';
+
+sub AUTOLOAD {
+    # This AUTOLOAD is used to 'autoload' constants from the constant()
+    # XS function.  If a constant is not found then control is passed
+    # to the AUTOLOAD in AutoLoader.
+
+    my $constname;
+    our $AUTOLOAD;
+    ($constname = $AUTOLOAD) =~ s/.*:://;
+    croak "& not defined" if $constname eq 'constant';
+    my $val = constant($constname, @_ ? $_[0] : 0);
+    if ($! != 0) {
+       if ($!{EINVAL}) {
+           $AutoLoader::AUTOLOAD = $AUTOLOAD;
+           goto &AutoLoader::AUTOLOAD;
+       }
+       else {
+           croak "Your vendor has not defined Fuse macro $constname";
+       }
+    }
+    {
+       no strict 'refs';
+       # Fixed between 5.005_53 and 5.005_61
+       if ($] >= 5.00561) {
+           *$AUTOLOAD = sub () { $val };
+       }
+       else {
+           *$AUTOLOAD = sub { $val };
+       }
+    }
+    goto &$AUTOLOAD;
+}
+
+bootstrap Fuse $VERSION;
+
+sub main {
+       my (@subs) = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
+       my (@names) = qw(getattr readlink getdir mknod mkdir unlink rmdir symlink rename link chmod chown truncate utime open read write);
+       my ($tmp) = 0;
+       my (%mapping) = map { $_ => $tmp++ } (@names);
+       my (%otherargs) = (debug=>0, threaded=>1, mountpoint=>"");
+       while(my $name = shift) {
+               my ($subref) = shift;
+               if(exists($otherargs{$name})) {
+                       $otherargs{$name} = $subref;
+               } else {
+                       croak "There is no function $name" unless exists($mapping{$name});
+                       croak "Usage: Fuse::main(getattr => &my_getattr, ...)" unless $subref;
+                       croak "Usage: Fuse::main(getattr => &my_getattr, ...)" unless ref($subref);
+                       croak "Usage: Fuse::main(getattr => &my_getattr, ...)" unless ref($subref) eq "CODE";
+                       $subs[$mapping{$name}] = $subref;
+               }
+       }
+       perl_fuse_main($0,$otherargs{threaded},$otherargs{debug},$otherargs{mountpoint},@subs);
+}
+
+# Autoload methods go after =cut, and are processed by the autosplit program.
+
+1;
+__END__
+
+=head1 NAME
+
+Fuse - write filesystems in Perl using FUSE
+
+=head1 SYNOPSIS
+
+  use Fuse;
+  my ($mountpoint) = "";
+  $mountpoint = shift(@ARGV) if @ARGV;
+  Fuse::main(mountpoint=>$mountpoint,getattr=>\&my_getattr,getdir=>\&my_getdir, ...);
+
+=head1 DESCRIPTION
+
+This lets you implement filesystems in perl, through the FUSE (Filesystem in USErspace) kernel/lib interface.
+
+FUSE expects you to implement callbacks for the various functions.
+
+NOTE:  I have only tested the things implemented in example.pl!  It should work, but some things may not.
+
+In the following definitions, "errno" can be 0 (for a success), -EINVAL, -ENOENT, -EONFIRE, any integer less than 1 really.
+You can import standard error constants by saying something like "use POSIX qw(EDOTDOT ENOANO);".
+
+=head2 FUNCTIONS
+
+=head3 getattr
+
+Arguments:  filename.
+Returns a list, one of the following 4 possibilities:
+
+$errno or
+
+($blocks,$size,$gid,$uid,$nlink,$modes,$time) or
+
+($errno,$blocks,$size,$gid,$uid,$nlink,$modes,$time) or
+
+($errno,$blksize,$blocks,$size,$gid,$uid,$nlink,$modes,$time)
+
+B<FIXME>: device numeric, for filesystems that implement mknod?
+
+=head3 readlink
+
+Arguments:  link pathname.
+Returns a scalar: either a numeric constant, or a text string.
+
+=head3 getdir
+
+Arguments:  Containing directory name.
+Returns a list: 0 or more text strings (the filenames), followed by errno (usually 0).
+
+=head3 mknod
+
+Arguments:  Filename, numeric modes, numeric device
+Returns an errno (0 upon success, as usual).
+
+=head3 mkdir
+
+Arguments:  New directory pathname, numeric modes.
+Returns an errno.
+
+=head3 unlink
+
+Arguments:  Filename.
+Returns an errno.
+
+=head3 rmdir
+
+Arguments:  Pathname.
+Returns an errno.
+
+=head3 symlink
+
+Arguments:  Existing filename, symlink name.
+Returns an errno.
+
+=head3 rename
+
+Arguments:  old filename, new filename.
+Returns an errno.
+
+=head3 link
+
+Arguments:  Existing filename, hardlink name.
+Returns an errno.
+
+=head3 chmod
+
+Arguments:  Pathname, numeric modes.
+Returns an errno.
+
+=head3 chown
+
+Arguments:  Pathname, numeric uid, numeric gid.
+Returns an errno.
+
+=head3 truncate
+
+Arguments:  Pathname, numeric offset.
+Returns an errno.
+
+=head3 utime
+
+Arguments:  Pathname, numeric actime, numeric modtime.
+Returns an errno.
+
+=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.
+
+=head3 read
+
+Arguments:  Pathname, numeric requestedsize, numeric offset.
+Returns a numeric errno, or a string scalar with up to $requestedsize bytes of data.
+
+=head3 write
+
+Arguments:  Pathname, scalar buffer, numeric offset.  You can use length($buffer) to find the buffersize.
+Returns an errno.
+
+=head2 EXPORT
+
+None by default.
+
+=head2 Exportable constants
+
+None.
+
+=head1 AUTHOR
+
+Mark Glines, E<lt>mark@glines.orgE<gt>
+
+=head1 SEE ALSO
+
+L<perl>, the FUSE documentation.
+
+=cut
diff --git a/perl/Fuse.xs b/perl/Fuse.xs
new file mode 100644 (file)
index 0000000..9770c3a
--- /dev/null
@@ -0,0 +1,521 @@
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+
+#include <fuse.h>
+
+static int
+not_here(char *s)
+{
+    croak("%s not implemented on this architecture", s);
+    return -1;
+}
+
+static double
+constant(char *name, int len, int arg)
+{
+    errno = ENOENT;
+    return 0;
+}
+
+SV *_PLfuse_callbacks[17];
+
+int _PLfuse_getattr(const char *file, struct stat *result) {
+       dSP;
+       int rv, statcount;
+       ENTER;
+       SAVETMPS;
+       PUSHMARK(SP);
+       XPUSHs(sv_2mortal(newSVpv(file,strlen(file))));
+       PUTBACK;
+       rv = call_sv(_PLfuse_callbacks[0],G_ARRAY);
+       SPAGAIN;
+       if(rv < 7) {
+               if(rv > 1)
+                       croak("inappropriate number of returned values from getattr");
+               if(rv)
+                       rv = POPi;
+               else
+                       rv = -EINVAL;
+       } else {
+               if(rv > 9)
+                       croak("inappropriate number of returned values from getattr");
+               result->st_ctime = result->st_mtime = result->st_atime = POPi;
+               result->st_mode = POPi;
+               result->st_nlink = POPi;
+               result->st_uid = POPi;
+               result->st_gid = POPi;
+               result->st_size = POPi;
+               result->st_blocks = POPi;
+               if(rv > 7) {
+                       if(rv > 8)
+                               result->st_blksize = POPi;
+                       else
+                               result->st_blksize = 1024;
+                       rv = POPi;
+               } else {
+                       result->st_blksize = 1024;
+                       rv = 0;
+               }
+       }
+       FREETMPS;
+       LEAVE;
+       return rv;
+}
+
+int _PLfuse_readlink(const char *file,char *buf,size_t buflen) {
+       int rv;
+       char *rvstr;
+       dXSARGS;
+       ENTER;
+       SAVETMPS;
+       PUSHMARK(SP);
+       XPUSHs(sv_2mortal(newSVpv(file,0)));
+       PUTBACK;
+       rv = call_sv(_PLfuse_callbacks[1],G_SCALAR);
+       SPAGAIN;
+       if(!rv)
+               rv = -ENOENT;
+       else
+               if(SvTYPE(ST(0)) == SVt_IV)
+                       rv = POPi;
+               else {
+                       strncpy(buf,POPp,buflen);
+                       rv = 0;
+               }
+       FREETMPS;
+       LEAVE;
+       return rv;
+}
+
+int _PLfuse_getdir(const char *file, fuse_dirh_t dirh, fuse_dirfil_t dirfil) {
+       int prv, rv;
+       dXSARGS;
+       ENTER;
+       SAVETMPS;
+       PUSHMARK(SP);
+       XPUSHs(sv_2mortal(newSVpv(file,0)));
+       PUTBACK;
+       prv = call_sv(_PLfuse_callbacks[2],G_ARRAY);
+       SPAGAIN;
+       if(prv) {
+               SV *mysv = POPs;
+               if(!SvIOK(mysv)) {
+                       fprintf(stderr,"last getdir retval needs to be numeric (e.g. 0 or -ENOENT) (%s)\n",SvPV_nolen(mysv));
+                       rv = -ENOSYS;
+               } else {
+                       rv = SvIV(mysv);
+                       while(--prv)
+                               dirfil(dirh,POPp,0);
+               }
+       } else {
+               fprintf(stderr,"getdir() handler returned nothing!\n");
+               rv = -ENOSYS;
+       }
+       FREETMPS;
+       LEAVE;
+       return rv;
+}
+
+int _PLfuse_mknod (const char *file, mode_t mode, dev_t dev) {
+       int rv;
+       SV *rvsv;
+       char *rvstr;
+       dSP;
+       ENTER;
+       SAVETMPS;
+       PUSHMARK(SP);
+       XPUSHs(sv_2mortal(newSVpv(file,0)));
+       XPUSHs(sv_2mortal(newSViv(mode)));
+       XPUSHs(sv_2mortal(newSViv(dev)));
+       PUTBACK;
+       rv = call_sv(_PLfuse_callbacks[3],G_SCALAR);
+       SPAGAIN;
+       if(rv)
+               rv = POPi;
+       else
+               rv = 0;
+       FREETMPS;
+       LEAVE;
+       return rv;
+}
+
+int _PLfuse_mkdir (const char *file, mode_t mode) {
+       int rv;
+       SV *rvsv;
+       char *rvstr;
+       dSP;
+       ENTER;
+       SAVETMPS;
+       PUSHMARK(SP);
+       XPUSHs(sv_2mortal(newSVpv(file,0)));
+       XPUSHs(sv_2mortal(newSViv(mode)));
+       PUTBACK;
+       rv = call_sv(_PLfuse_callbacks[4],G_SCALAR);
+       SPAGAIN;
+       if(rv)
+               rv = POPi;
+       else
+               rv = 0;
+       FREETMPS;
+       LEAVE;
+       return rv;
+}
+
+
+int _PLfuse_unlink (const char *file) {
+       int rv;
+       SV *rvsv;
+       char *rvstr;
+       dSP;
+       ENTER;
+       SAVETMPS;
+       PUSHMARK(SP);
+       XPUSHs(sv_2mortal(newSVpv(file,0)));
+       PUTBACK;
+       rv = call_sv(_PLfuse_callbacks[5],G_SCALAR);
+       SPAGAIN;
+       if(rv)
+               rv = POPi;
+       else
+               rv = 0;
+       FREETMPS;
+       LEAVE;
+       return rv;
+}
+
+int _PLfuse_rmdir (const char *file) {
+       int rv;
+       SV *rvsv;
+       char *rvstr;
+       dSP;
+       ENTER;
+       SAVETMPS;
+       PUSHMARK(SP);
+       XPUSHs(sv_2mortal(newSVpv(file,0)));
+       PUTBACK;
+       rv = call_sv(_PLfuse_callbacks[6],G_SCALAR);
+       SPAGAIN;
+       if(rv)
+               rv = POPi;
+       else
+               rv = 0;
+       FREETMPS;
+       LEAVE;
+       return rv;
+}
+
+int _PLfuse_symlink (const char *file, const char *new) {
+       int rv;
+       SV *rvsv;
+       char *rvstr;
+       dSP;
+       ENTER;
+       SAVETMPS;
+       PUSHMARK(SP);
+       XPUSHs(sv_2mortal(newSVpv(file,0)));
+       XPUSHs(sv_2mortal(newSVpv(new,0)));
+       PUTBACK;
+       rv = call_sv(_PLfuse_callbacks[7],G_SCALAR);
+       SPAGAIN;
+       if(rv)
+               rv = POPi;
+       else
+               rv = 0;
+       FREETMPS;
+       LEAVE;
+       return rv;
+}
+
+int _PLfuse_rename (const char *file, const char *new) {
+       int rv;
+       SV *rvsv;
+       char *rvstr;
+       dSP;
+       ENTER;
+       SAVETMPS;
+       PUSHMARK(SP);
+       XPUSHs(sv_2mortal(newSVpv(file,0)));
+       XPUSHs(sv_2mortal(newSVpv(new,0)));
+       PUTBACK;
+       rv = call_sv(_PLfuse_callbacks[8],G_SCALAR);
+       SPAGAIN;
+       if(rv)
+               rv = POPi;
+       else
+               rv = 0;
+       FREETMPS;
+       LEAVE;
+       return rv;
+}
+
+int _PLfuse_link (const char *file, const char *new) {
+       int rv;
+       SV *rvsv;
+       char *rvstr;
+       dSP;
+       ENTER;
+       SAVETMPS;
+       PUSHMARK(SP);
+       XPUSHs(sv_2mortal(newSVpv(file,0)));
+       XPUSHs(sv_2mortal(newSVpv(new,0)));
+       PUTBACK;
+       rv = call_sv(_PLfuse_callbacks[9],G_SCALAR);
+       SPAGAIN;
+       if(rv)
+               rv = POPi;
+       else
+               rv = 0;
+       FREETMPS;
+       LEAVE;
+       return rv;
+}
+
+int _PLfuse_chmod (const char *file, mode_t mode) {
+       int rv;
+       SV *rvsv;
+       char *rvstr;
+       dSP;
+       ENTER;
+       SAVETMPS;
+       PUSHMARK(SP);
+       XPUSHs(sv_2mortal(newSVpv(file,0)));
+       XPUSHs(sv_2mortal(newSViv(mode)));
+       PUTBACK;
+       rv = call_sv(_PLfuse_callbacks[10],G_SCALAR);
+       SPAGAIN;
+       if(rv)
+               rv = POPi;
+       else
+               rv = 0;
+       FREETMPS;
+       LEAVE;
+       return rv;
+}
+
+int _PLfuse_chown (const char *file, uid_t uid, gid_t gid) {
+       int rv;
+       SV *rvsv;
+       char *rvstr;
+       dSP;
+       ENTER;
+       SAVETMPS;
+       PUSHMARK(SP);
+       XPUSHs(sv_2mortal(newSVpv(file,0)));
+       XPUSHs(sv_2mortal(newSViv(uid)));
+       XPUSHs(sv_2mortal(newSViv(gid)));
+       PUTBACK;
+       rv = call_sv(_PLfuse_callbacks[11],G_SCALAR);
+       SPAGAIN;
+       if(rv)
+               rv = POPi;
+       else
+               rv = 0;
+       FREETMPS;
+       LEAVE;
+       return rv;
+}
+
+int _PLfuse_truncate (const char *file, off_t off) {
+       int rv;
+       SV *rvsv;
+       char *rvstr;
+       dSP;
+       ENTER;
+       SAVETMPS;
+       PUSHMARK(SP);
+       XPUSHs(sv_2mortal(newSVpv(file,0)));
+       XPUSHs(sv_2mortal(newSViv(off)));
+       PUTBACK;
+       rv = call_sv(_PLfuse_callbacks[12],G_SCALAR);
+       SPAGAIN;
+       if(rv)
+               rv = POPi;
+       else
+               rv = 0;
+       FREETMPS;
+       LEAVE;
+       return rv;
+}
+
+int _PLfuse_utime (const char *file, struct utimbuf *uti) {
+       int rv;
+       SV *rvsv;
+       char *rvstr;
+       dSP;
+       ENTER;
+       SAVETMPS;
+       PUSHMARK(SP);
+       XPUSHs(sv_2mortal(newSVpv(file,0)));
+       XPUSHs(sv_2mortal(newSViv(uti->actime)));
+       XPUSHs(sv_2mortal(newSViv(uti->modtime)));
+       PUTBACK;
+       rv = call_sv(_PLfuse_callbacks[13],G_SCALAR);
+       SPAGAIN;
+       if(rv)
+               rv = POPi;
+       else
+               rv = 0;
+       FREETMPS;
+       LEAVE;
+       return rv;
+}
+
+int _PLfuse_open (const char *file, int flags) {
+       int rv;
+       SV *rvsv;
+       char *rvstr;
+       dSP;
+       ENTER;
+       SAVETMPS;
+       PUSHMARK(SP);
+       XPUSHs(sv_2mortal(newSVpv(file,0)));
+       XPUSHs(sv_2mortal(newSViv(flags)));
+       PUTBACK;
+       rv = call_sv(_PLfuse_callbacks[14],G_SCALAR);
+       SPAGAIN;
+       if(rv)
+               rv = POPi;
+       else
+               rv = 0;
+       FREETMPS;
+       LEAVE;
+       return rv;
+}
+
+int _PLfuse_read (const char *file, char *buf, size_t buflen, off_t off) {
+       int rv;
+       char *rvstr;
+       dXSARGS;
+       ENTER;
+       SAVETMPS;
+       PUSHMARK(SP);
+       XPUSHs(sv_2mortal(newSVpv(file,0)));
+       XPUSHs(sv_2mortal(newSViv(buflen)));
+       XPUSHs(sv_2mortal(newSViv(off)));
+       PUTBACK;
+       rv = call_sv(_PLfuse_callbacks[15],G_SCALAR);
+       SPAGAIN;
+       if(!rv)
+               rv = -ENOENT;
+       else {
+               SV *mysv = sv_2mortal(POPs);
+               if(SvTYPE(mysv) == SVt_IV)
+                       rv = SvIV(mysv);
+               else {
+                       rv = SvLEN(mysv);
+                       if(rv > buflen)
+                               croak("read() handler returned more than buflen!");
+                       if(rv)
+                               memcpy(buf,SvPV_nolen(mysv),rv);
+               }
+       }
+       return rv;
+}
+
+int _PLfuse_write (const char *file, const char *buf, size_t buflen, off_t off) {
+       int rv;
+       char *rvstr;
+       dSP;
+       ENTER;
+       SAVETMPS;
+       PUSHMARK(SP);
+       XPUSHs(sv_2mortal(newSVpv(file,0)));
+       XPUSHs(sv_2mortal(newSVpvn(buf,buflen)));
+       XPUSHs(sv_2mortal(newSViv(off)));
+       PUTBACK;
+       rv = call_sv(_PLfuse_callbacks[16],G_SCALAR);
+       SPAGAIN;
+       if(rv)
+               rv = POPi;
+       else
+               rv = 0;
+       FREETMPS;
+       LEAVE;
+       return rv;
+}
+
+struct fuse_operations _available_ops = {
+getattr:       _PLfuse_getattr,
+                       _PLfuse_readlink,
+                       _PLfuse_getdir,
+                       _PLfuse_mknod,
+                       _PLfuse_mkdir,
+                       _PLfuse_unlink,
+                       _PLfuse_rmdir,
+                       _PLfuse_symlink,
+                       _PLfuse_rename,
+                       _PLfuse_link,
+                       _PLfuse_chmod,
+                       _PLfuse_chown,
+                       _PLfuse_truncate,
+                       _PLfuse_utime,
+                       _PLfuse_open,
+                       _PLfuse_read,
+                       _PLfuse_write
+};
+
+MODULE = Fuse          PACKAGE = Fuse
+PROTOTYPES: DISABLE
+
+
+double
+constant(sv,arg)
+    PREINIT:
+       STRLEN          len;
+    INPUT:
+       SV *            sv
+       char *          s = SvPV(sv, len);
+       int             arg
+    CODE:
+       RETVAL = constant(s,len,arg);
+    OUTPUT:
+       RETVAL
+
+void
+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};
+       int i, varnum = 0, threads, debug, argc;
+       char **argv;
+       STRLEN n_a;
+       STRLEN l;
+       INIT:
+       if(items != 21) {
+               fprintf(stderr,"Perl<->C inconsistency or internal error\n");
+               XSRETURN_UNDEF;
+       }
+       CODE:
+       threads = !SvIV(ST(1));
+       debug = SvIV(ST(2));
+       if(threads && debug) {
+               argc = 4;
+               argv = ((char*[]){NULL,NULL,"-s","-d"});
+       } else if(threads) {
+               argc = 3;
+               argv = ((char*[]){NULL,NULL,"-s"});
+       } else if(debug) {
+               argc = 3;
+               argv = ((char*[]){NULL,NULL,"-d"});
+       } else {
+               argc = 2;
+               argv = ((char*[]){"str","mnt"});
+       }
+       argv[0] = SvPV(ST(0),n_a);
+       if(strlen(SvPV(ST(3),n_a)))
+               argv[1] = SvPV(ST(3),n_a);
+       else
+               argc--;
+       
+       for(i=0;i<17;i++) {
+               SV *var = ST(i+4);
+               if((var != &PL_sv_undef) && SvROK(var)) {
+                       if(SvTYPE(SvRV(var)) == SVt_PVCV) {
+                               void **tmp1 = (void**)&_available_ops, **tmp2 = (void**)&fops;
+                               tmp2[i] = tmp1[i];
+                               _PLfuse_callbacks[i] = var;
+                       } else
+                               croak("arg is not a code reference!");
+               }
+       }
+       fuse_main(argc,argv,&fops);
diff --git a/perl/MANIFEST b/perl/MANIFEST
new file mode 100644 (file)
index 0000000..9d7f036
--- /dev/null
@@ -0,0 +1,8 @@
+Changes
+Fuse.pm
+Fuse.xs
+Makefile.PL
+MANIFEST
+README
+test.pl
+example.pl
diff --git a/perl/Makefile.PL b/perl/Makefile.PL
new file mode 100644 (file)
index 0000000..d495683
--- /dev/null
@@ -0,0 +1,17 @@
+use ExtUtils::MakeMaker;
+# See lib/ExtUtils/MakeMaker.pm for details of how to influence
+# the contents of the Makefile that is written.
+WriteMakefile(
+    'NAME'             => 'Fuse',
+    'VERSION_FROM'     => 'Fuse.pm', # finds $VERSION
+    'PREREQ_PM'                => {}, # e.g., Module::Name => 1.1
+    ($] >= 5.005 ?    ## Add these new keywords supported since 5.005
+      (ABSTRACT_FROM => 'Fuse.pm', # retrieve abstract from module
+       AUTHOR     => 'Mark Glines <mark@glines.org>') : ()),
+    'LIBS'             => [''], # e.g., '-lm'
+    'DEFINE'           => '-g -ggdb', # e.g., '-DHAVE_SOMETHING'
+       # Insert -I. if you add *.h files later:
+    'INC'              => '-I../include', # e.g., '-I/usr/include/other'
+       # Un-comment this if you add C files to link with later:
+    'OBJECT'           => 'Fuse.o ../lib/libfuse.a -lpthread', # link all the C files too
+);
diff --git a/perl/README b/perl/README
new file mode 100644 (file)
index 0000000..b22b87e
--- /dev/null
@@ -0,0 +1,26 @@
+Fuse version 0.01
+=================
+
+This is a test release.  It seems to work thus far, but still has a few
+iffy areas, as well as a few rough edges.  There will be future
+releases.
+
+INSTALLATION
+
+To install this module type the following:
+
+   perl Makefile.PL
+   make
+   make test  # currently this just makes sure the lib can link
+   make install
+
+DEPENDENCIES
+
+This module requires the FUSE userspace library and the FUSE kernel module.
+
+COPYRIGHT AND LICENCE
+
+This is contributed to the FUSE project by Mark Glines <mark@glines.org>,
+and is therefore subject to the same license and copyright as FUSE itself.
+Please see the AUTHORS and COPYING files from the FUSE distribution for
+more information.
diff --git a/perl/example.pl b/perl/example.pl
new file mode 100755 (executable)
index 0000000..257f2c9
--- /dev/null
@@ -0,0 +1,83 @@
+#!/usr/bin/perl
+
+use Fuse;
+use POSIX qw(ENOENT EISDIR EINVAL);
+
+my (%files) = (
+       '.' => {
+               type => 0040,
+               mode => 0755,
+               ctime => time()-1000
+       },
+       a => {
+               cont => "File 'a'.\n",
+               type => 0100,
+               mode => 0755,
+               ctime => time()-2000
+       },
+       b => {
+               cont => "This is file 'b'.\n",
+               type => 0100,
+               mode => 0644,
+               ctime => time()-1000
+       },
+);
+
+sub filename_fixup {
+       my ($file) = shift;
+       $file =~ s,^/,,;
+       $file = '.' unless length($file);
+       return $file;
+}
+
+sub e_getattr {
+       my ($file) = filename_fixup(shift);
+       $file =~ s,^/,,;
+       $file = '.' unless length($file);
+       return -ENOENT() unless exists($files{$file});
+       my ($size) = exists($files{$file}{cont}) ? length($files{$file}{cont}) : 0;
+       my ($modes) = ($files{$file}{type}<<9) + $files{$file}{mode};
+       my ($blocks, $gid, $uid, $nlink) = (1,0,0,1);
+       # 4 possible return values:
+       #return -ENOENT(); # or any other error you care to
+       return ($blocks,$size,$gid,$uid,$nlink,$modes,$files{$file}{ctime});
+       # return ($errno,$blocks,$size,$gid,$uid,$nlink,$modes,$time);
+       # return ($errno,$blksize,$blocks,$size,$gid,$uid,$nlink,$modes,$time);
+       # if omitted, errno defaults to 0, and blksize defaults to 1024.
+}
+
+sub e_getdir {
+       # return as many text filenames as you like, followed by the retval.
+       return (keys %files),0;
+}
+
+sub e_open {
+       # VFS sanity check; it keeps all the necessary state, not much to do here.
+       my ($file) = filename_fixup(shift);
+       return -ENOENT() unless exists($files{$file});
+       return -EISDIR() unless exists($files{$file}{cont});
+       return 0;
+}
+
+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) = @_;
+       return -ENOENT() unless exists($files{$file});
+       return -EINVAL() if $off > length($files{$file}{cont});
+       return 0 if $off == length($files{$file}{cont});
+       return substr($files{$file}{cont},$off,$buf);
+}
+
+# If you run the script directly, it will run fusermount, which will in turn
+# re-run this script.  Hence the funky semantics.
+my ($mountpoint) = "";
+$mountpoint = shift(@ARGV) if @ARGV;
+Fuse::main(
+       mountpoint=>$mountpoint,
+       getattr=>\&e_getattr,
+       getdir=>\&e_getdir,
+       open=>\&e_open,
+       read=>\&e_read,
+);
diff --git a/perl/test.pl b/perl/test.pl
new file mode 100644 (file)
index 0000000..eee3421
--- /dev/null
@@ -0,0 +1,18 @@
+#!/usr/bin/perl
+# Before `make install' is performed this script should be runnable with
+# `make test'. After `make install' it should work as `perl test.pl'
+
+#########################
+
+# change 'tests => 1' to 'tests => last_test_to_print';
+
+use Test;
+BEGIN { plan tests => 1 };
+use Fuse;
+ok(1); # If we made it this far, we're ok.
+#########################
+
+# Insert your test code below, the Test module is use()ed here so read
+# its man page ( perldoc Test ) for help writing this test script.
+
+# haven't written anything, because I don't want to require root