fuser-perl changes
authorMiklos Szeredi <miklos@szeredi.hu>
Thu, 13 Dec 2001 10:55:07 +0000 (10:55 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Thu, 13 Dec 2001 10:55:07 +0000 (10:55 +0000)
perl/Changes
perl/Fuse.pm
perl/Fuse.xs
perl/example.pl
perl/loopback.pl [new file with mode: 0644]

index 39812037d8dfc01a4f0a6f302ed8c1151a5f7ca2..56b4733244e13f75a6028c832053f58307e9cbf2 100644 (file)
@@ -6,3 +6,7 @@ Revision history for Perl extension Fuse.
 
 0.02 Sun Dec 2 18:59:56 2001
     - works well enough to release, but still needs testing
+
+0.03 Wed Dec 5 02:17:52 2001
+    - changed getattr() to smell like perl's stat()
+       - fleshed out the documentation a bit
index 2f4742a810a9decd53a0be13baa07d1d37a73063..93acfb69ea2785c6fdb9bd97d070a260615197d9 100644 (file)
@@ -99,116 +99,185 @@ Fuse - write filesystems in Perl using FUSE
   use Fuse;
   my ($mountpoint) = "";
   $mountpoint = shift(@ARGV) if @ARGV;
-  Fuse::main(mountpoint=>$mountpoint,getattr=>\&my_getattr,getdir=>\&my_getdir, ...);
+  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.
+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.
+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);".
+In the following definitions, "errno" can be 0 (for a success),
+-EINVAL, -ENOENT, -EONFIRE, any integer less than 1 really.
 
-=head2 FUNCTIONS
+You can import standard error constants by saying something like
+"use POSIX qw(EDOTDOT ENOANO);".
 
-=head3 getattr
-
-Arguments:  filename.
-Returns a list, one of the following 4 possibilities:
-
-$errno or
+Every constant you need (file types, open() flags, error values,
+etc) can be imported either from POSIX or from Fcntl, often both.
+See their respective documentations, for more information.
 
-($blocks,$size,$gid,$uid,$nlink,$modes,$time) or
+=head2 FUNCTIONS YOUR FILESYSTEM MAY IMPLEMENT
 
-($errno,$blocks,$size,$gid,$uid,$nlink,$modes,$time) or
-
-($errno,$blksize,$blocks,$size,$gid,$uid,$nlink,$modes,$time)
+=head3 getattr
 
-B<FIXME>: device numeric, for filesystems that implement mknod?
+Arguments:  filename.
+Returns a list, very similar to the 'stat' function (see
+perlfunc).  On error, simply return a single numeric scalar
+value (e.g. "return -ENOENT();").
+
+FIXME: the "ino" field is currently ignored.  I tried setting it to 0
+in an example script, which consistently caused segfaults.
+
+Fields (the following was stolen from perlfunc(1) with apologies):
+
+($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
+        $atime,$mtime,$ctime,$blksize,$blocks)
+                         = getattr($filename);
+
+Here are the meaning of the fields:
+
+ 0 dev      device number of filesystem
+ 1 ino      inode number
+ 2 mode     file mode  (type and permissions)
+ 3 nlink    number of (hard) links to the file
+ 4 uid      numeric user ID of file's owner
+ 5 gid      numeric group ID of file's owner
+ 6 rdev     the device identifier (special files only)
+ 7 size     total size of file, in bytes
+ 8 atime    last access time in seconds since the epoch
+ 9 mtime    last modify time in seconds since the epoch
+10 ctime    inode change time (NOT creation time!) in seconds
+            since the epoch
+11 blksize  preferred block size for file system I/O
+12 blocks   actual number of blocks allocated
+
+(The epoch was at 00:00 January 1, 1970 GMT.)
 
 =head3 readlink
 
 Arguments:  link pathname.
 Returns a scalar: either a numeric constant, or a text string.
 
+This is called when dereferencing symbolic links, to learn the target.
+
+example rv: return "/proc/self/fd/stdin";
+
 =head3 getdir
 
 Arguments:  Containing directory name.
-Returns a list: 0 or more text strings (the filenames), followed by errno (usually 0).
+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.
+
+example rv: return ('.', 'a', 'b', 0);
 
 =head3 mknod
 
 Arguments:  Filename, numeric modes, numeric device
 Returns an errno (0 upon success, as usual).
 
+This function is called for all non-directory, non-symlink nodes,
+not just devices.
+
 =head3 mkdir
 
 Arguments:  New directory pathname, numeric modes.
 Returns an errno.
 
+Called to create a directory.
+
 =head3 unlink
 
 Arguments:  Filename.
 Returns an errno.
 
+Called to remove a file, device, or symlink.
+
 =head3 rmdir
 
 Arguments:  Pathname.
 Returns an errno.
 
+Called to remove a directory.
+
 =head3 symlink
 
 Arguments:  Existing filename, symlink name.
 Returns an errno.
 
+Called to create a symbolic link.
+
 =head3 rename
 
 Arguments:  old filename, new filename.
 Returns an errno.
 
+Called to rename a file, and/or move a file from one directory to another.
+
 =head3 link
 
 Arguments:  Existing filename, hardlink name.
 Returns an errno.
 
+Called to create hard links.
+
 =head3 chmod
 
 Arguments:  Pathname, numeric modes.
 Returns an errno.
 
+Called to change permissions on a file/directory/device/symlink.
+
 =head3 chown
 
 Arguments:  Pathname, numeric uid, numeric gid.
 Returns an errno.
 
+Called to change ownership of a file/directory/device/symlink.
+
 =head3 truncate
 
 Arguments:  Pathname, numeric offset.
 Returns an errno.
 
+Called to truncate a file, at the given offset.
+
 =head3 utime
 
 Arguments:  Pathname, numeric actime, numeric modtime.
 Returns an errno.
 
+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).
+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.
 
+No creation, or trunctation flags (O_CREAT, O_EXCL, O_TRUNC) will be passed to open().
+Your open() method needs only check if the operation is permitted for the given flags, and return 0 for success.
+
 =head3 read
 
 Arguments:  Pathname, numeric requestedsize, numeric offset.
 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 find the buffersize.
+Arguments:  Pathname, scalar buffer, numeric offset.  You can use length($buffer) to
+find the buffersize.
 Returns an errno.
 
+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.
+
 =head2 EXPORT
 
 None by default.
index 9770c3a85da86f1f87259320111c2cdabdd0432f..ca0459d7bfa2424558b6dd61d7372f566dcf4203 100644 (file)
@@ -4,6 +4,12 @@
 
 #include <fuse.h>
 
+#undef DEBUGf
+#if 1
+#define DEBUGf(a...) fprintf(stderr, ##a)
+#else
+#define DEBUGf(a...)
+#endif
 static int
 not_here(char *s)
 {
@@ -30,33 +36,29 @@ int _PLfuse_getattr(const char *file, struct stat *result) {
        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)
+       if(rv != 13) {
+               if(rv > 1) {
+                       fprintf(stderr,"inappropriate number of returned values from getattr\n");
+                       rv = -ENOSYS;
+               } else if(rv)
                        rv = POPi;
                else
-                       rv = -EINVAL;
+                       rv = -ENOENT;
        } 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;
-               }
+               result->st_blksize = POPi;
+               result->st_ctime = POPi;
+               result->st_mtime = POPi;
+               result->st_atime = POPi;
+               result->st_size = POPi;
+               result->st_rdev = POPi;
+               result->st_gid = POPi;
+               result->st_uid = POPi;
+               result->st_nlink = POPi;
+               result->st_mode = POPi;
+               /* result->st_ino = */ POPi;
+               result->st_dev = POPi;
+               rv = 0;
        }
        FREETMPS;
        LEAVE;
@@ -67,6 +69,9 @@ int _PLfuse_readlink(const char *file,char *buf,size_t buflen) {
        int rv;
        char *rvstr;
        dXSARGS;
+       DEBUGf("readlink begin\n");
+       if(buflen < 1)
+               return EINVAL;
        ENTER;
        SAVETMPS;
        PUSHMARK(SP);
@@ -85,12 +90,15 @@ int _PLfuse_readlink(const char *file,char *buf,size_t buflen) {
                }
        FREETMPS;
        LEAVE;
+       buf[buflen-1] = 0;
+       DEBUGf("readlink end\n");
        return rv;
 }
 
 int _PLfuse_getdir(const char *file, fuse_dirh_t dirh, fuse_dirfil_t dirfil) {
        int prv, rv;
        dXSARGS;
+       DEBUGf("getdir begin\n");
        ENTER;
        SAVETMPS;
        PUSHMARK(SP);
@@ -99,7 +107,7 @@ int _PLfuse_getdir(const char *file, fuse_dirh_t dirh, fuse_dirfil_t dirfil) {
        prv = call_sv(_PLfuse_callbacks[2],G_ARRAY);
        SPAGAIN;
        if(prv) {
-               SV *mysv = POPs;
+               SV *mysv = sv_2mortal(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;
@@ -114,6 +122,7 @@ int _PLfuse_getdir(const char *file, fuse_dirh_t dirh, fuse_dirfil_t dirfil) {
        }
        FREETMPS;
        LEAVE;
+       DEBUGf("getdir end\n");
        return rv;
 }
 
@@ -122,6 +131,7 @@ int _PLfuse_mknod (const char *file, mode_t mode, dev_t dev) {
        SV *rvsv;
        char *rvstr;
        dSP;
+       DEBUGf("mknod begin\n");
        ENTER;
        SAVETMPS;
        PUSHMARK(SP);
@@ -137,6 +147,7 @@ int _PLfuse_mknod (const char *file, mode_t mode, dev_t dev) {
                rv = 0;
        FREETMPS;
        LEAVE;
+       DEBUGf("mknod end: %i\n",rv);
        return rv;
 }
 
@@ -145,6 +156,7 @@ int _PLfuse_mkdir (const char *file, mode_t mode) {
        SV *rvsv;
        char *rvstr;
        dSP;
+       DEBUGf("mkdir begin\n");
        ENTER;
        SAVETMPS;
        PUSHMARK(SP);
@@ -159,6 +171,7 @@ int _PLfuse_mkdir (const char *file, mode_t mode) {
                rv = 0;
        FREETMPS;
        LEAVE;
+       DEBUGf("mkdir end\n");
        return rv;
 }
 
@@ -168,6 +181,7 @@ int _PLfuse_unlink (const char *file) {
        SV *rvsv;
        char *rvstr;
        dSP;
+       DEBUGf("unlink begin\n");
        ENTER;
        SAVETMPS;
        PUSHMARK(SP);
@@ -181,6 +195,7 @@ int _PLfuse_unlink (const char *file) {
                rv = 0;
        FREETMPS;
        LEAVE;
+       DEBUGf("unlink end\n");
        return rv;
 }
 
@@ -189,6 +204,7 @@ int _PLfuse_rmdir (const char *file) {
        SV *rvsv;
        char *rvstr;
        dSP;
+       DEBUGf("rmdir begin\n");
        ENTER;
        SAVETMPS;
        PUSHMARK(SP);
@@ -202,6 +218,7 @@ int _PLfuse_rmdir (const char *file) {
                rv = 0;
        FREETMPS;
        LEAVE;
+       DEBUGf("rmdir end\n");
        return rv;
 }
 
@@ -210,6 +227,7 @@ int _PLfuse_symlink (const char *file, const char *new) {
        SV *rvsv;
        char *rvstr;
        dSP;
+       DEBUGf("symlink begin\n");
        ENTER;
        SAVETMPS;
        PUSHMARK(SP);
@@ -224,6 +242,7 @@ int _PLfuse_symlink (const char *file, const char *new) {
                rv = 0;
        FREETMPS;
        LEAVE;
+       DEBUGf("symlink end\n");
        return rv;
 }
 
@@ -232,6 +251,7 @@ int _PLfuse_rename (const char *file, const char *new) {
        SV *rvsv;
        char *rvstr;
        dSP;
+       DEBUGf("rename begin\n");
        ENTER;
        SAVETMPS;
        PUSHMARK(SP);
@@ -246,6 +266,7 @@ int _PLfuse_rename (const char *file, const char *new) {
                rv = 0;
        FREETMPS;
        LEAVE;
+       DEBUGf("rename end\n");
        return rv;
 }
 
@@ -254,6 +275,7 @@ int _PLfuse_link (const char *file, const char *new) {
        SV *rvsv;
        char *rvstr;
        dSP;
+       DEBUGf("link begin\n");
        ENTER;
        SAVETMPS;
        PUSHMARK(SP);
@@ -268,6 +290,7 @@ int _PLfuse_link (const char *file, const char *new) {
                rv = 0;
        FREETMPS;
        LEAVE;
+       DEBUGf("link end\n");
        return rv;
 }
 
@@ -276,6 +299,7 @@ int _PLfuse_chmod (const char *file, mode_t mode) {
        SV *rvsv;
        char *rvstr;
        dSP;
+       DEBUGf("chmod begin\n");
        ENTER;
        SAVETMPS;
        PUSHMARK(SP);
@@ -290,6 +314,7 @@ int _PLfuse_chmod (const char *file, mode_t mode) {
                rv = 0;
        FREETMPS;
        LEAVE;
+       DEBUGf("chmod end\n");
        return rv;
 }
 
@@ -298,6 +323,7 @@ int _PLfuse_chown (const char *file, uid_t uid, gid_t gid) {
        SV *rvsv;
        char *rvstr;
        dSP;
+       DEBUGf("chown begin\n");
        ENTER;
        SAVETMPS;
        PUSHMARK(SP);
@@ -313,6 +339,7 @@ int _PLfuse_chown (const char *file, uid_t uid, gid_t gid) {
                rv = 0;
        FREETMPS;
        LEAVE;
+       DEBUGf("chown end\n");
        return rv;
 }
 
@@ -321,6 +348,7 @@ int _PLfuse_truncate (const char *file, off_t off) {
        SV *rvsv;
        char *rvstr;
        dSP;
+       DEBUGf("truncate begin\n");
        ENTER;
        SAVETMPS;
        PUSHMARK(SP);
@@ -335,6 +363,7 @@ int _PLfuse_truncate (const char *file, off_t off) {
                rv = 0;
        FREETMPS;
        LEAVE;
+       DEBUGf("truncate end\n");
        return rv;
 }
 
@@ -343,6 +372,7 @@ int _PLfuse_utime (const char *file, struct utimbuf *uti) {
        SV *rvsv;
        char *rvstr;
        dSP;
+       DEBUGf("utime begin\n");
        ENTER;
        SAVETMPS;
        PUSHMARK(SP);
@@ -358,6 +388,7 @@ int _PLfuse_utime (const char *file, struct utimbuf *uti) {
                rv = 0;
        FREETMPS;
        LEAVE;
+       DEBUGf("utime end\n");
        return rv;
 }
 
@@ -366,6 +397,7 @@ int _PLfuse_open (const char *file, int flags) {
        SV *rvsv;
        char *rvstr;
        dSP;
+       DEBUGf("open begin\n");
        ENTER;
        SAVETMPS;
        PUSHMARK(SP);
@@ -380,6 +412,7 @@ int _PLfuse_open (const char *file, int flags) {
                rv = 0;
        FREETMPS;
        LEAVE;
+       DEBUGf("open end: %i\n",rv);
        return rv;
 }
 
@@ -387,6 +420,7 @@ int _PLfuse_read (const char *file, char *buf, size_t buflen, off_t off) {
        int rv;
        char *rvstr;
        dXSARGS;
+       DEBUGf("read begin\n");
        ENTER;
        SAVETMPS;
        PUSHMARK(SP);
@@ -403,13 +437,14 @@ int _PLfuse_read (const char *file, char *buf, size_t buflen, off_t off) {
                if(SvTYPE(mysv) == SVt_IV)
                        rv = SvIV(mysv);
                else {
-                       rv = SvLEN(mysv);
+                       rv = SvCUR(mysv);
                        if(rv > buflen)
-                               croak("read() handler returned more than buflen!");
+                               croak("read() handler returned more than buflen! (%i > %i)",rv,buflen);
                        if(rv)
                                memcpy(buf,SvPV_nolen(mysv),rv);
                }
        }
+       DEBUGf("read end\n");
        return rv;
 }
 
@@ -417,6 +452,7 @@ int _PLfuse_write (const char *file, const char *buf, size_t buflen, off_t off)
        int rv;
        char *rvstr;
        dSP;
+       DEBUGf("write begin\n");
        ENTER;
        SAVETMPS;
        PUSHMARK(SP);
@@ -432,6 +468,7 @@ int _PLfuse_write (const char *file, const char *buf, size_t buflen, off_t off)
                rv = 0;
        FREETMPS;
        LEAVE;
+       DEBUGf("write end\n");
        return rv;
 }
 
index 257f2c9cb1d0d0959457787377fdc9094bf5d2fb..bc62cadeffe5743f4b1cb052c0a7b3caeb3f2e67 100755 (executable)
@@ -37,13 +37,13 @@ sub e_getattr {
        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:
+       my ($dev, $ino, $rdev, $blocks, $gid, $uid, $nlink, $blksize) = (0,0,0,1,0,0,1,1024);
+       my ($atime, $ctime, $mtime);
+       $atime = $ctime = $mtime = $files{$file}{ctime};
+       # 2 possible types of 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.
+       #print(join(",",($dev,$ino,$modes,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks)),"\n");
+       return ($dev,$ino,$modes,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks);
 }
 
 sub e_getdir {
@@ -79,5 +79,6 @@ Fuse::main(
        getattr=>\&e_getattr,
        getdir=>\&e_getdir,
        open=>\&e_open,
-       read=>\&e_read,
+       #read=>\&e_read,
+       #debug=>1, threaded=>0
 );
diff --git a/perl/loopback.pl b/perl/loopback.pl
new file mode 100644 (file)
index 0000000..2a8496a
--- /dev/null
@@ -0,0 +1,117 @@
+#!/usr/bin/perl
+
+use strict;
+use Fuse;
+use POSIX qw(ENOENT ENOSYS EEXIST EPERM O_RDONLY O_RDWR O_APPEND O_CREAT);
+use Fcntl qw(S_ISBLK S_ISCHR S_ISFIFO);
+
+sub debug {
+       print(STDERR join(",",@_),"\n");
+}
+
+sub fixup { return "/tmp/test" . shift }
+
+sub x_getattr {
+       my ($file) = fixup(shift);
+       debug("getattr $file");
+       return -ENOENT() unless -e $file;
+       debug(stat($file));
+       return (stat($file));
+}
+
+sub x_getdir {
+       my ($dirname) = fixup(shift);
+       debug("getdir >$dirname<");
+       unless(opendir(DIRHANDLE,$dirname)) {
+               debug("ENOENT");
+               return -ENOENT();
+       }
+       debug("ok");
+       my (@files) = readdir(DIRHANDLE);
+       closedir(DIRHANDLE);
+       debug(@files);
+       return (@files, 0);
+}
+
+sub x_open {
+       my ($file) = fixup(shift);
+       debug("open flags = $_[0]");
+       my ($fd) = POSIX::open($file,@_);
+       if(!defined($fd)) {
+               debug("POSIX::open(".join(",",$file,@_).") returned undef");
+               return -ENOSYS();
+       }
+       debug("open $file = $fd");
+       return $fd if $fd < 0;
+       POSIX::close($fd);
+       debug("good: $fd");
+       return 0;
+}
+
+sub x_read {
+       my ($file,$bufsize,$off) = @_;
+       debug("read",@_);
+       my ($rv) = -ENOSYS();
+       return -ENOENT() unless -e ($file = fixup($file));
+       return -ENOSYS() unless sysopen(FILE,$file,O_RDONLY());
+       if(sysseek(FILE,$off,0)) { sysread(FILE,$rv,$bufsize); }
+       close(FILE);
+       debug("good");
+       return $rv;
+}
+
+sub x_write {
+       my ($file,$buf,$off) = @_;
+       debug("write",@_);
+       my ($rv);
+       return -ENOENT() unless -e ($file = fixup($file));
+       return -ENOSYS() unless sysopen(FILE,$file,O_RDWR()|O_APPEND()|O_CREAT());
+       if(sysseek(FILE,$off,0)) { $rv = syswrite(FILE,$buf); }
+       $rv = -ENOSYS() unless $rv;
+       close(FILE);
+       debug("good");
+       return $rv;
+}
+
+sub err { return (-shift || -$!) }
+
+sub x_readlink { return err(readlink(fixup(shift))            ); }
+sub x_unlink   { return unlink(fixup(shift)) ? 0 : -$!;          }
+sub x_rmdir    { return err(rmdir(fixup(shift))               ); }
+sub x_symlink  { return err(symlink(fixup(shift),fixup(shift))); }
+sub x_rename   { return err(rename(fixup(shift),fixup(shift)) ); }
+sub x_link     { return err(link(fixup(shift),fixup(shift))   ); }
+sub x_mkdir    { return err(mkdir(fixup(shift),shift)         ); }
+sub x_chmod    { return err(chmod(fixup(shift),shift)         ); }
+sub x_chown    { return err(chown(fixup(shift),shift,shift)   ); }
+sub x_chmod    { return err(chmod(fixup(shift),shift)         ); }
+sub x_truncate { return err(truncate(fixup(shift),shift)      ); }
+sub x_utime    { return utime($_[1],$_[2],fixup($_[0])) ? 0:-$!; }
+
+sub x_mknod {
+       # since this is called for ALL files, not just devices, I'll do some checks
+       # and possibly run the real mknod command.
+       my ($file, $modes, $dev) = @_;
+       return -EEXIST() if -e ($file = fixup($file));
+       return -EPERM() if (system("touch $file 2>/dev/null") >> 8);
+       if(S_ISBLK($modes) || S_ISCHR($modes) || S_ISFIFO($modes)) {
+               system("rm -f $file 2>/dev/null");
+               my ($chr) = 'c';
+               my ($omodes) = sprintf("%o",$modes & 0x1ff);
+               $chr = 'b' if S_ISBLK($modes);
+               if(S_ISFIFO($modes)) {
+                       $chr = 'p';
+                       $dev = "";
+               } else {
+                       $dev = (($dev>>8) & 255) . " " . ($dev & 255);
+               }
+               system("mknod --mode=$omodes '$file' $chr $dev");
+       }
+}
+
+my ($mountpoint) = "";
+$mountpoint = shift(@ARGV) if @ARGV;
+Fuse::main(mountpoint=>$mountpoint, getattr=>\&x_getattr, readlink=>\&readlink, getdir=>\&x_getdir, mknod=>\&x_mknod,
+       mkdir=>\&x_mkdir, unlink=>\&x_unlink, rmdir=>\&x_rmdir, symlink=>\&x_symlink, rename=>\&x_rename, link=>\&x_link,
+       chmod=>\&x_chmod, chown=>\&x_chown, truncate=>\&x_truncate, utime=>\&x_utime, open=>\&x_open, read=>\&x_read, write=>\&x_write
+);