patch from Chris Dolan via rt.cpan.org #30631
authorDobrica Pavlinusic <dpavlin@rot13.org>
Thu, 15 Nov 2007 09:32:08 +0000 (09:32 +0000)
committerDobrica Pavlinusic <dpavlin@rot13.org>
Thu, 15 Nov 2007 09:32:08 +0000 (09:32 +0000)
This patch gets Fuse.pm to half-work on MacOSX with the current release
of MacFuse (v1.1.0).  By half-work, I mean that all of the directory
actions and file read actions work, but anything that involves writing a
file fails.  This appears to be because the latest MacFUSE implements
FUSE 2.6, which prefers to call CREATE instead of MKNOD.  Nonetheless,
recommend that something like this patch be included because it makes
read-only filesystems usable on Darwin systems.  Some of my changes
(like kill() instead of system("kill")) are improvements on any system.
    I've tested only on my PowerPC G5 iMac running 10.4.

I intend to also try MacFUSE v0.4 via Fink, but that version is
reportedly less stable than the latest MacFUSE.

git-svn-id: svn+ssh://llin/home/dpavlin/private/svn/fuse/perl-llin@112 6e4b0b00-1209-0410-87b2-b275959b5705

Makefile.PL
examples/loopback.pl
examples/loopback_t.pl
test/helper.pm
test/s/mount.t
test/s/umount.t
test/statfs.t

index 4e42885..1e05f5d 100644 (file)
@@ -11,7 +11,8 @@ $ver3 =~ s/^.*?version\s+//;
 if (! $ver && ! $ver2 && ! $ver3) {
        # make CPANPLUS happy and don't report errors if fuse isn't installed
        die("No support for os: $^O\n",
-               "You need to have fuse-dev (or similar) package installed and have sufficient permissions in order to install this module\n"
+               "You need to have fuse-dev (or similar) package installed and have sufficient permissions in order to install this module\n",
+               $^O eq 'darwin' ? ("One option on Mac is http://code.google.com/p/macfuse/\n") : (),
        );
 }
 if ($ver && $ver + 0 < 2.5) {
index 7a88876..81ee2e1 100644 (file)
@@ -6,14 +6,33 @@ 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
+my $can_syscall = eval {
+       require 'syscall.ph'; # for SYS_mknod and SYS_lchown
+};
+if (!$can_syscall && open my $fh, '<', '/usr/include/sys/syscall.h') {
+       local $/ = undef;
+       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;
+       }
+}
 
-my $tmp_path = "/tmp/fusetest-" . $ENV{LOGNAME};
+my $tmp = -d '/private' ? '/private/tmp' : '/tmp';
+my $tmp_path = "$tmp/fusetest-" . $ENV{LOGNAME};
 if (! -e $tmp_path) {
        mkdir($tmp_path) || die "can't create $tmp_path: $!";
 }
 
-sub fixup { return $tmp_path . shift }
+sub fixup { print STDERR "fixup $_[0] from @{[caller]}\n";
+            my ($path) = @_;
+            return $tmp_path if $path eq '/';
+            return $tmp_path . $path;
+}
 
 sub x_getattr {
        my ($file) = fixup(shift);
@@ -82,6 +101,7 @@ sub x_rename {
 }
 sub x_link { return link(fixup(shift),fixup(shift)) ? 0 : -$! }
 sub x_chown {
+       return -ENOSYS() if ! $can_syscall;
        my ($fn) = fixup(shift);
        print "nonexistent $fn\n" unless -e $fn;
        my ($uid,$gid) = @_;
@@ -105,6 +125,7 @@ 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 {
+       return -ENOSYS() if ! $can_syscall;
        # 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) = @_;
index a37daba..8302aa8 100644 (file)
@@ -8,7 +8,20 @@ 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
+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 fixup { return "/tmp/fusetest-" . $ENV{LOGNAME} . shift }
 
@@ -79,6 +92,7 @@ sub x_rename {
 }
 sub x_link { return link(fixup(shift),fixup(shift)) ? 0 : -$! }
 sub x_chown {
+       return -ENOSYS() if ! $can_syscall;
        my ($fn) = fixup(shift);
        print "nonexistent $fn\n" unless -e $fn;
        my ($uid,$gid) = @_;
@@ -102,6 +116,7 @@ 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 {
+       return -ENOSYS() if ! $can_syscall;
        # 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) = @_;
index ba179a1..32e629a 100644 (file)
@@ -7,16 +7,19 @@ use POSIX qw(WEXITSTATUS);
 our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
 @ISA = "Exporter";
 @EXPORT_OK = qw($_loop $_point $_pidfile $_real);
-our($_loop, $_point, $_pidfile, $_real) = ("","/tmp/fusemnt-".$ENV{LOGNAME},"test/s/mounted.pid","/tmp/fusetest-".$ENV{LOGNAME});
-$_loop = $Config{useithreads} ? "examples/loopback_t.pl" : "examples/loopback.pl";
+my $tmp = -d '/private' ? '/private/tmp' : '/tmp';
+our($_loop, $_point, $_pidfile, $_real) = ("","$tmp/fusemnt-".$ENV{LOGNAME},"test/s/mounted.pid","$tmp/fusetest-".$ENV{LOGNAME});
+$_loop = $^O ne 'darwin' && $Config{useithreads} ? "examples/loopback_t.pl" : "examples/loopback.pl";
 if($0 !~ qr|s/u?mount\.t$|) {
        my ($reject) = 1;
-       if(-f $_pidfile) {
-               unless(POSIX::WEXITSTATUS(system("ps `cat $_pidfile` | grep \"$_loop $_point\" >/dev/null"))) {
-                       if(`mount | grep "on $_point"`) {
+       if(open my $fh, '<', $_pidfile) {
+               my $pid = do {local $/ = undef; <$fh>};
+               close $fh;
+               if(kill 0, $pid) {
+                       if(`mount` =~ m{on (?:/private)?$_point }) {
                                $reject = 0;
                        } else {
-                               system("kill `cat $_pidfile`");
+                               kill 1, $pid;
                        }
                }
        }
index 2275e18..c8bc61d 100644 (file)
@@ -2,7 +2,13 @@
 use test::helper qw($_point $_loop $_real $_pidfile);
 use strict;
 use Test::More tests => 3;
-ok(!(scalar grep(/ on $_point /,`cat /proc/mounts`)),"already mounted");
+
+sub is_mounted {
+       my $diag = -d '/proc' ? `cat /proc/mounts` : `mount`;
+       return $diag =~ m{ (?:/private)?$_point };
+}
+
+ok(!is_mounted(),"already mounted");
 ok(-f $_loop,"loopback exists");
 
 if(!fork()) {
@@ -13,6 +19,8 @@ if(!fork()) {
        mkdir $_real;
        `echo $$ >test/s/mounted.pid`;
        diag "mounting $_loop to $_point";
+       open STDOUT, '>', '/tmp/fusemnt.log';
+       open STDERR, '>&', \*STDOUT;
        exec("perl -Iblib/lib -Iblib/arch $_loop $_point");
        exit(1);
 }
@@ -20,7 +28,7 @@ if(!fork()) {
 my ($success, $count) = (0,0);
 while ($count++ < 50 && !$success) {
        select(undef, undef, undef, 0.1);
-          ($success) = `mount` =~ / $_point /;
+       ($success) = is_mounted();
 }
 diag "Mounted in ", $count/10, " secs";
 
index 28757e2..c77301a 100644 (file)
@@ -4,6 +4,9 @@ use strict;
 use Test::More tests => 1;
 use POSIX qw(WEXITSTATUS);
 system("fusermount -u $_point");
+if(POSIX::WEXITSTATUS($?) != 0) {
+       system("umount $_point");
+}
 ok(POSIX::WEXITSTATUS($?) == 0,"unmount");
 system("rm -rf $_real $_pidfile");
 rmdir($_point);
index 7905b80..2335546 100644 (file)
@@ -1,7 +1,10 @@
 #!/usr/bin/perl
 use test::helper qw($_real $_point);
 use Test::More;
-require 'syscall.ph'; # for SYS_statfs
+eval {
+   require 'syscall.ph'; # for SYS_statfs
+} or plan skip_all => 'No syscall.ph';
+
 plan tests => 7;
 my ($statfs_data) = 0x00 x 8 x 16;
 my ($tmp) = $_point;