moved examples into a subdirectory
authorMark Glines <mark@glines.org>
Wed, 26 Jun 2002 05:32:13 +0000 (05:32 +0000)
committerMark Glines <mark@glines.org>
Wed, 26 Jun 2002 05:32:13 +0000 (05:32 +0000)
updated the README

perl/README
perl/example.pl [deleted file]
perl/examples/example.pl [new file with mode: 0755]
perl/examples/loopback.pl [new file with mode: 0755]
perl/examples/rmount.pl [new file with mode: 0755]
perl/examples/rmount_remote.pl [new file with mode: 0755]
perl/loopback.pl [deleted file]

index 87d5a7d54ff1df86df20afae7085f02e4b24f6cb..fb49cd700e22dae81f11fb37a1eae7596720232e 100644 (file)
@@ -1,23 +1,25 @@
-Fuse version 0.02
+Fuse version 0.03
 =================
 
-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.
+This is a test release.  It seems to work quite well.  In fact, I can't
+find any problems with it whatsoever.  If you do, I want to know.
+
 
 INSTALLATION
 
-To install this module type the following:
+To install this module type the standard commands as root:
 
    perl Makefile.PL
    make
-   make test  # currently this just makes sure the lib can link
+   make test
    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>,
@@ -25,6 +27,31 @@ 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.
 
+
+EXAMPLES
+
+There are a few example scripts.  You can find them in the examples/
+subdirectory.  These are:
+
+* example.pl, a simple "Hello world" type of script
+
+* loopback.pl, a filesystem loopback-device.  like fusexmp from
+               the main FUSE dist, it simply recurses file operations
+               into the real filesystem.  Unlike fusexmp, it only
+               re-shares files under the /tmp/test directory.
+
+* rmount.pl, an NFS-workalike which tunnels through SSH.  It requires
+             an account on some ssh server (obviously), with public-key
+             authentication enabled.  (if you have to type in a password,
+             you don't have this.  man ssh_keygen.).  Copy rmount_remote.pl
+             to your home directory on the remote machine, and create a
+             subdir somewhere, and then run it like:
+             ./rmount.pl host /remote/dir /local/dir
+
+* rmount_remote.pl, a ripoff of loopback.pl meant to be used as a backend
+                    for rmount.pl.
+
+
 BUGS
 
 I've begun to build a formal testing framework.  Currently it can mount
@@ -35,8 +62,8 @@ The current test framework seems to work well, but the underlying mount/
 unmount infrastructure is a crock.  I am not pleased with that code.
 
 While most things work, I do still have a TODO list:
-* while "ln -s" works as expected, "cp -a" kicks out an error on symlinks.
 * "du -sb" reports a couple orders of magnitude too large a size.
 * need to sort out cleaner mount semantics for the test framework
 * figure out how to un-linuxcentrify the statfs tests
 * test everything on other architectures and OS's
+
diff --git a/perl/example.pl b/perl/example.pl
deleted file mode 100755 (executable)
index 9ba1117..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-#!/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 ($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
-       #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 {
-       # return as many text filenames as you like, followed by the retval.
-       print((scalar keys %files)."\n");
-       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);
-       print("open called\n");
-       return -ENOENT() unless exists($files{$file});
-       return -EISDIR() unless exists($files{$file}{cont});
-       print("open ok\n");
-       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);
-}
-
-sub e_statfs { return 255, 1, 1, 1, 1, 2 }
-
-# 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,
-       statfs=>\&e_statfs,
-       read=>\&e_read,
-       #debug=>1, threaded=>0
-);
diff --git a/perl/examples/example.pl b/perl/examples/example.pl
new file mode 100755 (executable)
index 0000000..9ba1117
--- /dev/null
@@ -0,0 +1,90 @@
+#!/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 ($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
+       #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 {
+       # return as many text filenames as you like, followed by the retval.
+       print((scalar keys %files)."\n");
+       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);
+       print("open called\n");
+       return -ENOENT() unless exists($files{$file});
+       return -EISDIR() unless exists($files{$file}{cont});
+       print("open ok\n");
+       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);
+}
+
+sub e_statfs { return 255, 1, 1, 1, 1, 2 }
+
+# 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,
+       statfs=>\&e_statfs,
+       read=>\&e_read,
+       #debug=>1, threaded=>0
+);
diff --git a/perl/examples/loopback.pl b/perl/examples/loopback.pl
new file mode 100755 (executable)
index 0000000..bdc8c22
--- /dev/null
@@ -0,0 +1,136 @@
+#!/usr/bin/perl
+
+use strict;
+use Fuse;
+use IO::File;
+use POSIX qw(ENOENT ENOSYS EEXIST EPERM O_RDONLY O_RDWR O_APPEND O_CREAT);
+use Fcntl qw(S_ISBLK S_ISCHR S_ISFIFO SEEK_SET);
+require 'syscall.ph'; # for SYS_mknod and SYS_lchown
+
+sub fixup { return "/tmp/fusetest" . shift }
+
+sub x_getattr {
+       my ($file) = fixup(shift);
+       my (@list) = lstat($file);
+       return -$! unless @list;
+       return @list;
+}
+
+sub x_getdir {
+       my ($dirname) = fixup(shift);
+       unless(opendir(DIRHANDLE,$dirname)) {
+               return -ENOENT();
+       }
+       my (@files) = readdir(DIRHANDLE);
+       closedir(DIRHANDLE);
+       return (@files, 0);
+}
+
+sub x_open {
+       my ($file) = fixup(shift);
+       my ($mode) = shift;
+       return -$! unless sysopen(FILE,$file,$mode);
+       close(FILE);
+       return 0;
+}
+
+sub x_read {
+       my ($file,$bufsize,$off) = @_;
+       my ($rv) = -ENOSYS();
+       my ($handle) = new IO::File;
+       return -ENOENT() unless -e ($file = fixup($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 {
+       my ($file,$buf,$off) = @_;
+       my ($rv);
+       return -ENOENT() unless -e ($file = fixup($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 err { return (-shift || -$!) }
+
+sub x_readlink { return readlink(fixup(shift)                 ); }
+sub x_unlink { return unlink(fixup(shift)) ? 0 : -$!;          }
+sub x_rmdir { return err(rmdir(fixup(shift))               ); }
+
+sub x_symlink { print "symlink\n"; return symlink(shift,fixup(shift)) ? 0 : -$!; }
+
+sub x_rename {
+       my ($old) = fixup(shift);
+       my ($new) = fixup(shift);
+       my ($err) = rename($old,$new) ? 0 : -ENOENT();
+       return $err;
+}
+sub x_link { return link(fixup(shift),fixup(shift)) ? 0 : -$! }
+sub x_chown {
+       my ($fn) = fixup(shift);
+       print "nonexistent $fn\n" unless -e $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 {
+       my ($fn) = fixup(shift);
+       my ($mode) = shift;
+       my ($err) = chmod($mode,$fn) ? 0 : -$!;
+       return $err;
+}
+sub x_truncate { return truncate(fixup(shift),shift) ? 0 : -$! ; }
+sub x_utime { return utime($_[1],$_[2],fixup($_[0])) ? 0:-$!; }
+
+sub x_mkdir { my ($name, $perm) = @_; return 0 if mkdir(fixup($name),$perm); return -$!; }
+sub x_rmdir { return 0 if rmdir fixup(shift); return -$!; }
+
+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) = @_;
+       $file = fixup($file);
+       $! = 0;
+       syscall(&SYS_mknod,$file,$modes,$dev);
+       return -$!;
+}
+
+# kludge
+sub x_statfs {return 255,1000000,500000,1000000,500000,4096}
+my ($mountpoint) = "";
+$mountpoint = shift(@ARGV) if @ARGV;
+Fuse::main(
+       mountpoint=>$mountpoint,
+       getattr=>\&x_getattr,
+       readlink=>\&x_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,
+       statfs=>\&x_statfs,
+);
diff --git a/perl/examples/rmount.pl b/perl/examples/rmount.pl
new file mode 100755 (executable)
index 0000000..eef8ed8
--- /dev/null
@@ -0,0 +1,83 @@
+#!/usr/bin/perl
+
+use strict;
+use Net::SSH 'sshopen2';
+use IPC::Open2;
+use Fuse;
+use Data::Dumper;
+
+my ($host, $dir, $mount) = @ARGV;
+if(!defined($mount)) {
+       $mount = $dir;
+       if($host =~ /^(.*):(.*)$/) {
+               ($host,$dir) = ($1,$2);
+       } else {
+               die "usage: $0 user\@host remotedir mountpoint\n".
+                   "or   : $0 user\@host:remotedir mountpoint\n";
+       }
+}
+
+`umount $mount` unless -d $mount;
+die "mountpoint $mount isn't a directory!\n" unless -d $mount;
+
+my (%args) = (mountpoint => $mount);
+
+map { my ($str) = $_; $args{$str} = sub { netlink($str,@_) } }
+       qw(getattr getdir open read write readlink unlink rmdir
+          symlink rename link chown chmod truncate utime mkdir
+          rmdir mknod statfs);
+
+sub connect_remote {
+       sshopen2($host, *READER, *WRITER, "./rmount_remote.pl $dir")
+               or die "ssh: $!\n";
+#      open2(*READER,*WRITER,"./rmount_remote.pl $dir");
+       select WRITER;
+       $| = 1;
+       select STDOUT;
+}
+
+$SIG{CHLD} = sub {
+       use POSIX ":sys_wait_h";
+       my $kid;
+       do {
+               $kid = waitpid(-1,WNOHANG);
+       } until $kid < 1;
+};
+
+connect_remote;
+
+sub netlink {
+       my ($str) = join("\n",map {" $_"} (split("\n",Dumper(\@_))))."\n";
+       $str = sprintf("%08i\n%s",length($str),$str);
+       while(1) { # retry as necessary
+               my ($sig) = $SIG{ALRM};
+               my ($VAR1);
+               $VAR1 = undef;
+               eval {
+                       $SIG{ALRM} = sub { die "timeout\n" };
+                       alarm 10;
+                       print WRITER $str;
+                       my ($len, $data);
+                       if(read(READER,$len,9) == 9) {
+                               read(READER,$data,$len-length($data),length($data))
+                                       while(length($data) < $len);
+                               eval $data;
+                       }
+               };
+               alarm 0;
+               $SIG{ALRM} = $sig;
+               if(defined $VAR1) {
+                       return wantarray ? @{$VAR1} : $$VAR1[0];
+               }
+               print STDERR "failed to send command; reconnecting ssh\n";
+               close(READER);
+               close(WRITER);
+               connect_remote();
+       }
+}
+
+Fuse::main(%args);
+
+netlink("bye");
+close(READER);
+close(WRITER);
diff --git a/perl/examples/rmount_remote.pl b/perl/examples/rmount_remote.pl
new file mode 100755 (executable)
index 0000000..8a8be40
--- /dev/null
@@ -0,0 +1,143 @@
+#!/usr/bin/perl
+
+use strict;
+use IO::File;
+use POSIX qw(ENOENT ENOSYS EEXIST EPERM O_RDONLY O_RDWR O_APPEND O_CREAT);
+use Fcntl qw(S_ISBLK S_ISCHR S_ISFIFO SEEK_SET);
+use Data::Dumper;
+require 'syscall.ph'; # for SYS_mknod and SYS_lchown
+
+my ($rootdir) = @ARGV;
+
+# strip leading and trailing slashes
+$rootdir = $1 if($rootdir =~ /^\/?(.*)\/?$/);
+
+sub fixup { return "/$rootdir" . shift }
+
+sub x_getattr {
+       my ($file) = fixup(shift);
+       my (@list) = lstat($file);
+       return -$! unless @list;
+       return @list;
+}
+
+sub x_getdir {
+       my ($dirname) = fixup(shift);
+       unless(opendir(DIRHANDLE,$dirname)) {
+               return -ENOENT();
+       }
+       my (@files) = readdir(DIRHANDLE);
+       closedir(DIRHANDLE);
+       return (@files, 0);
+}
+
+sub x_open {
+       my ($file) = fixup(shift);
+       my ($mode) = shift;
+       return -$! unless sysopen(FILE,$file,$mode);
+       close(FILE);
+       return 0;
+}
+
+sub x_read {
+       my ($file,$bufsize,$off) = @_;
+       my ($rv) = -ENOSYS();
+       my ($handle) = new IO::File;
+       return -ENOENT() unless -e ($file = fixup($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 {
+       my ($file,$buf,$off) = @_;
+       my ($rv);
+       return -ENOENT() unless -e ($file = fixup($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 err { return (-shift || -$!) }
+
+sub x_readlink { return readlink(fixup(shift)                 ); }
+sub x_unlink { return unlink(fixup(shift)) ? 0 : -$!;          }
+sub x_rmdir { return err(rmdir(fixup(shift))               ); }
+
+sub x_symlink { print "symlink\n"; return symlink(shift,fixup(shift)) ? 0 : -$!; }
+
+sub x_rename {
+       my ($old) = fixup(shift);
+       my ($new) = fixup(shift);
+       my ($err) = rename($old,$new) ? 0 : -ENOENT();
+       return $err;
+}
+sub x_link { return link(fixup(shift),fixup(shift)) ? 0 : -$! }
+sub x_chown {
+       my ($fn) = fixup(shift);
+       print "nonexistent $fn\n" unless -e $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 {
+       my ($fn) = fixup(shift);
+       my ($mode) = shift;
+       my ($err) = chmod($mode,$fn) ? 0 : -$!;
+       return $err;
+}
+sub x_truncate { return truncate(fixup(shift),shift) ? 0 : -$! ; }
+sub x_utime { return utime($_[1],$_[2],fixup($_[0])) ? 0:-$!; }
+
+sub x_mkdir { my ($name, $perm) = @_; return 0 if mkdir(fixup($name),$perm); return -$!; }
+sub x_rmdir { return 0 if rmdir fixup(shift); return -$!; }
+
+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) = @_;
+       $file = fixup($file);
+       $! = 0;
+       syscall(&SYS_mknod,$file,$modes,$dev);
+       return -$!;
+}
+
+# kludge
+sub x_statfs {return 255,1000000,500000,1000000,500000,4096}
+
+$| = 1;
+my ($len);
+while(read(STDIN,$len,9) == 9) {
+       chomp $len;
+       my ($data,$VAR1,@args);
+       eval {
+               $SIG{ALRM} = sub { die "timeout\n"};
+               $data = "";
+               alarm 5;
+               read(STDIN,$data,$len-length($data),length($data))
+                       while(length($data) < $len);
+               alarm 0;
+       };
+       die $@ if $@;
+       eval $data;
+       @args = @{$VAR1};
+       my $cmd = shift(@args);
+       exit 0 if $cmd eq "bye";
+       die "cannot find command $cmd\n" unless exists($main::{"x_$cmd"});
+       @args = $main::{"x_$cmd"}(@args);
+       $cmd = join("\n",map {" $_"} (split("\n",Dumper(\@args))))."\n";
+       $cmd = sprintf("%08i\n%s",length($cmd),$cmd);
+       print $cmd;
+}
diff --git a/perl/loopback.pl b/perl/loopback.pl
deleted file mode 100644 (file)
index bdc8c22..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-#!/usr/bin/perl
-
-use strict;
-use Fuse;
-use IO::File;
-use POSIX qw(ENOENT ENOSYS EEXIST EPERM O_RDONLY O_RDWR O_APPEND O_CREAT);
-use Fcntl qw(S_ISBLK S_ISCHR S_ISFIFO SEEK_SET);
-require 'syscall.ph'; # for SYS_mknod and SYS_lchown
-
-sub fixup { return "/tmp/fusetest" . shift }
-
-sub x_getattr {
-       my ($file) = fixup(shift);
-       my (@list) = lstat($file);
-       return -$! unless @list;
-       return @list;
-}
-
-sub x_getdir {
-       my ($dirname) = fixup(shift);
-       unless(opendir(DIRHANDLE,$dirname)) {
-               return -ENOENT();
-       }
-       my (@files) = readdir(DIRHANDLE);
-       closedir(DIRHANDLE);
-       return (@files, 0);
-}
-
-sub x_open {
-       my ($file) = fixup(shift);
-       my ($mode) = shift;
-       return -$! unless sysopen(FILE,$file,$mode);
-       close(FILE);
-       return 0;
-}
-
-sub x_read {
-       my ($file,$bufsize,$off) = @_;
-       my ($rv) = -ENOSYS();
-       my ($handle) = new IO::File;
-       return -ENOENT() unless -e ($file = fixup($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 {
-       my ($file,$buf,$off) = @_;
-       my ($rv);
-       return -ENOENT() unless -e ($file = fixup($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 err { return (-shift || -$!) }
-
-sub x_readlink { return readlink(fixup(shift)                 ); }
-sub x_unlink { return unlink(fixup(shift)) ? 0 : -$!;          }
-sub x_rmdir { return err(rmdir(fixup(shift))               ); }
-
-sub x_symlink { print "symlink\n"; return symlink(shift,fixup(shift)) ? 0 : -$!; }
-
-sub x_rename {
-       my ($old) = fixup(shift);
-       my ($new) = fixup(shift);
-       my ($err) = rename($old,$new) ? 0 : -ENOENT();
-       return $err;
-}
-sub x_link { return link(fixup(shift),fixup(shift)) ? 0 : -$! }
-sub x_chown {
-       my ($fn) = fixup(shift);
-       print "nonexistent $fn\n" unless -e $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 {
-       my ($fn) = fixup(shift);
-       my ($mode) = shift;
-       my ($err) = chmod($mode,$fn) ? 0 : -$!;
-       return $err;
-}
-sub x_truncate { return truncate(fixup(shift),shift) ? 0 : -$! ; }
-sub x_utime { return utime($_[1],$_[2],fixup($_[0])) ? 0:-$!; }
-
-sub x_mkdir { my ($name, $perm) = @_; return 0 if mkdir(fixup($name),$perm); return -$!; }
-sub x_rmdir { return 0 if rmdir fixup(shift); return -$!; }
-
-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) = @_;
-       $file = fixup($file);
-       $! = 0;
-       syscall(&SYS_mknod,$file,$modes,$dev);
-       return -$!;
-}
-
-# kludge
-sub x_statfs {return 255,1000000,500000,1000000,500000,4096}
-my ($mountpoint) = "";
-$mountpoint = shift(@ARGV) if @ARGV;
-Fuse::main(
-       mountpoint=>$mountpoint,
-       getattr=>\&x_getattr,
-       readlink=>\&x_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,
-       statfs=>\&x_statfs,
-);