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
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) {
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);
}
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) = @_;
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) = @_;
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 }
}
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) = @_;
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) = @_;
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;
}
}
}
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()) {
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);
}
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";
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);
#!/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;