On Perl 5.8, lchown() sometimes ends up with leaked errno; declare $! local.
[perl-fuse.git] / examples / loopback.pl
old mode 100644 (file)
new mode 100755 (executable)
index bdc8c22..88d81b9
-#!/usr/bin/perl
-
+#!/usr/bin/perl -w
 use strict;
+
+use Carp ();
+local $SIG{'__WARN__'} = \&Carp::cluck;
+
+my $has_threads = 0;
+eval {
+    require threads;
+    require threads::shared;
+    1;
+} and do {
+    $has_threads = 1;
+    threads->import();
+    threads::shared->import();
+};
+
+my $has_Filesys__Statvfs = 0;
+eval {
+    require Filesys::Statvfs;
+    1;
+} and do {
+    $has_Filesys__Statvfs = 1;
+    Filesys::Statvfs->import();
+};
+
+use blib;
 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
+use Fcntl qw(S_ISBLK S_ISCHR S_ISFIFO SEEK_SET S_ISREG S_ISFIFO S_IMODE S_ISCHR S_ISBLK S_ISSOCK);
+use Getopt::Long;
+use Lchown;
+use Unix::Mknod qw(:all);
+
+my %extraopts = ( 'threaded' => 0, 'debug' => 0 );
+my($use_real_statfs, $pidfile);
+GetOptions(
+    'use-threads'       => sub {
+        if ($has_threads) {
+            $extraopts{'threaded'} = 1;
+        }
+    },
+    'debug'             => sub {
+        $extraopts{'debug'} = 1;
+    },
+    'use-real-statfs'   => \$use_real_statfs,
+    'pidfile=s'         => \$pidfile,
+) || die('Error parsing options');
 
-sub fixup { return "/tmp/fusetest" . shift }
+sub fixup { return "/tmp/fusetest-" . $ENV{LOGNAME} . shift }
 
 sub x_getattr {
-       my ($file) = fixup(shift);
-       my (@list) = lstat($file);
-       return -$! unless @list;
-       return @list;
+    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);
+    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;
+    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;
+    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);
+    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_readlink { return readlink(fixup(shift));         }
+sub x_unlink   { return unlink(fixup(shift)) ? 0 : -$!; }
 
 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;
+    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;
+    my ($fn) = fixup(shift);
+    local $!;
+    print "nonexistent $fn\n" unless -e $fn;
+    my ($uid,$gid) = @_;
+    lchown($uid, $gid, $fn);
+    return -$!;
 }
 sub x_chmod {
-       my ($fn) = fixup(shift);
-       my ($mode) = shift;
-       my ($err) = chmod($mode,$fn) ? 0 : -$!;
-       return $err;
+    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:-$!; }
@@ -100,37 +137,84 @@ sub x_mkdir { my ($name, $perm) = @_; return 0 if mkdir(fixup($name),$perm); ret
 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 -$!;
+    # 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);
+    undef $!;
+    if (S_ISREG($modes)) {
+        open(FILE, '>', $file) || return -$!;
+        print FILE '';
+        close(FILE);
+        chmod S_IMODE($modes), $file;
+        return 0;
+    }
+    elsif (S_ISFIFO($modes)) {
+        my ($rv) = POSIX::mkfifo($file, S_IMODE($modes));
+        return $rv ? 0 : -POSIX::errno();
+    }
+    elsif (S_ISCHR($modes) || S_ISBLK($modes)) {
+        mknod($file, $modes, $dev);
+        return -$!;
+    }
+    # S_ISSOCK maybe should be handled; however, for our test it should
+    # not really matter.
+    else {
+        return -&ENOSYS;
+    }
+    return -$!;
 }
 
 # kludge
-sub x_statfs {return 255,1000000,500000,1000000,500000,4096}
-my ($mountpoint) = "";
+sub x_statfs {
+    if ($has_Filesys__Statvfs && $use_real_statfs) {
+        (my($bsize, $frsize, $blocks, $bfree, $bavail,
+            $files, $ffree, $favail, $flag,
+            $namemax) = statvfs('/tmp')) || return -$!;
+        return ($namemax, $files, $ffree, $blocks, $bavail, $bsize);
+    }
+    return 255,1000000,500000,1000000,500000,4096;
+}
+my ($mountpoint) = '';
 $mountpoint = shift(@ARGV) if @ARGV;
+
+if (! -d $mountpoint) {
+    print STDERR "ERROR: attempted to mount to non-directory\n";
+    return -&ENOTDIR
+}
+my $pid = fork();
+die("fork() failed: $!") unless defined $pid;
+
+if ($pid > 0) {
+    # parent process
+    exit(0);
+}
+if ($pidfile) {
+    open(PIDFILE, '>', $pidfile);
+    print PIDFILE $$, "\n";
+    close(PIDFILE);
+}
 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,
+    'mountpoint'    => $mountpoint,
+    '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',
+    %extraopts,
 );
+
+# vim: ts=4 ai et hls