require at least fuse 2.5
[perl-fuse.git] / Fuse.pm
diff --git a/Fuse.pm b/Fuse.pm
index e7bf1e0..d4e09d9 100644 (file)
--- a/Fuse.pm
+++ b/Fuse.pm
@@ -5,6 +5,7 @@ use strict;
 use warnings;
 use Errno;
 use Carp;
 use warnings;
 use Errno;
 use Carp;
+use Config;
 
 require Exporter;
 require DynaLoader;
 
 require Exporter;
 require DynaLoader;
@@ -19,16 +20,15 @@ our @ISA = qw(Exporter DynaLoader);
 # This allows declaration      use Fuse ':all';
 # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
 # will save memory.
 # This allows declaration      use Fuse ':all';
 # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
 # will save memory.
-our %EXPORT_TAGS = ( 'all' => [ qw(
-       FUSE_DEBUG
-) ] );
+our %EXPORT_TAGS = (
+                   'all' => [ qw(XATTR_CREATE XATTR_REPLACE) ],
+                   'xattr' => [ qw(XATTR_CREATE XATTR_REPLACE) ]
+                   );
 
 our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
 
 
 our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
 
-our @EXPORT = qw(
-       FUSE_DEBUG
-);
-our $VERSION = '0.05';
+our @EXPORT = ();
+our $VERSION = '0.07_3';
 
 sub AUTOLOAD {
     # This AUTOLOAD is used to 'autoload' constants from the constant()
 
 sub AUTOLOAD {
     # This AUTOLOAD is used to 'autoload' constants from the constant()
@@ -62,28 +62,65 @@ sub AUTOLOAD {
     goto &$AUTOLOAD;
 }
 
     goto &$AUTOLOAD;
 }
 
+sub XATTR_CREATE {
+    # See <sys/xattr.h>.
+    return 1;
+}
+
+sub XATTR_REPLACE {
+    # See <sys/xattr.h>.
+    return 2;
+}
+
 bootstrap Fuse $VERSION;
 
 sub main {
 bootstrap Fuse $VERSION;
 
 sub main {
-       my (@subs) = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
+       my (@subs) = (undef,undef,undef,undef,undef,undef,undef,undef,undef,undef,
+                     undef,undef,undef,undef,undef,undef,undef,undef,undef,undef,
+                     undef,undef,undef,undef,undef);
        my (@names) = qw(getattr readlink getdir mknod mkdir unlink rmdir symlink
        my (@names) = qw(getattr readlink getdir mknod mkdir unlink rmdir symlink
-                        rename link chmod chown truncate utime open read write statfs);
+                        rename link chmod chown truncate utime open read write statfs
+                        flush release fsync setxattr getxattr listxattr removexattr);
+       my (@validOpts) = qw(allow_other);
        my ($tmp) = 0;
        my (%mapping) = map { $_ => $tmp++ } (@names);
        my ($tmp) = 0;
        my (%mapping) = map { $_ => $tmp++ } (@names);
-       my (%otherargs) = (debug=>0, mountpoint=>"");
+       my (%optmap) = map { $_ => 1 } (@validOpts);
+       my (%otherargs) = (debug=>0, threaded=>0, mountpoint=>"", mountopts=>"");
        while(my $name = shift) {
                my ($subref) = shift;
                if(exists($otherargs{$name})) {
                        $otherargs{$name} = $subref;
                } else {
                        croak "There is no function $name" unless exists($mapping{$name});
        while(my $name = shift) {
                my ($subref) = shift;
                if(exists($otherargs{$name})) {
                        $otherargs{$name} = $subref;
                } else {
                        croak "There is no function $name" unless exists($mapping{$name});
-                       croak "Usage: Fuse::main(getattr => &my_getattr, ...)" unless $subref;
-                       croak "Usage: Fuse::main(getattr => &my_getattr, ...)" unless ref($subref);
-                       croak "Usage: Fuse::main(getattr => &my_getattr, ...)" unless ref($subref) eq "CODE";
+                       croak "Usage: Fuse::main(getattr => \"main::my_getattr\", ...)" unless $subref;
                        $subs[$mapping{$name}] = $subref;
                }
        }
                        $subs[$mapping{$name}] = $subref;
                }
        }
-       perl_fuse_main($otherargs{debug},$otherargs{mountpoint},@subs);
+       foreach my $opt ( split(/,/,$otherargs{mountopts}) ) {
+               if ( ! exists($optmap{$opt}) ) {
+                       croak "Use of an invalid mountopt argument";
+               }
+       }
+       if($otherargs{threaded}) {
+               # make sure threads are both available, and loaded.
+               if($Config{useithreads}) {
+                       if(exists($threads::{VERSION})) {
+                               if(exists($threads::shared::{VERSION})) {
+                                       # threads will work.
+                               } else {
+                                       carp("Thread support requires you to use threads::shared.\nThreads are disabled.\n");
+                                       $otherargs{threaded} = 0;
+                               }
+                       } else {
+                               carp("Thread support requires you to use threads and threads::shared.\nThreads are disabled.\n");
+                               $otherargs{threaded} = 0;
+                       }
+               } else {
+                       carp("Thread support was not compiled into this build of perl.\nThreads are disabled.\n");
+                       $otherargs{threaded} = 0;
+               }
+       }
+       perl_fuse_main($otherargs{debug},$otherargs{threaded},$otherargs{mountpoint},$otherargs{mountopts},@subs);
 }
 
 # Autoload methods go after =cut, and are processed by the autosplit program.
 }
 
 # Autoload methods go after =cut, and are processed by the autosplit program.
@@ -100,7 +137,7 @@ Fuse - write filesystems in Perl using FUSE
   use Fuse;
   my ($mountpoint) = "";
   $mountpoint = shift(@ARGV) if @ARGV;
   use Fuse;
   my ($mountpoint) = "";
   $mountpoint = shift(@ARGV) if @ARGV;
-  Fuse::main(mountpoint=>$mountpoint, getattr=>\&my_getattr, getdir=>\&my_getdir, ...);
+  Fuse::main(mountpoint=>$mountpoint, getattr=>"main::my_getattr", getdir=>"main::my_getdir", ...);
 
 =head1 DESCRIPTION
 
 
 =head1 DESCRIPTION
 
@@ -109,9 +146,6 @@ This lets you implement filesystems in perl, through the FUSE
 
 FUSE expects you to implement callbacks for the various functions.
 
 
 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.
-
 In the following definitions, "errno" can be 0 (for a success),
 -EINVAL, -ENOENT, -EONFIRE, any integer less than 1 really.
 
 In the following definitions, "errno" can be 0 (for a success),
 -EINVAL, -ENOENT, -EONFIRE, any integer less than 1 really.
 
@@ -122,13 +156,14 @@ 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.
 
 etc) can be imported either from POSIX or from Fcntl, often both.
 See their respective documentations, for more information.
 
-=head2 EXPORT
+=head2 EXPORTED SYMBOLS
 
 None by default.
 
 
 None by default.
 
-=head2 EXPORTABLE CONSTANTS
+You can request all exportable symbols by using the tag ":all".
 
 
-None.
+You can request the extended attribute symbols by using the tag ":xattr".
+This will export XATTR_CREATE and XATTR_REPLACE.
 
 =head2 FUNCTIONS
 
 
 =head2 FUNCTIONS
 
@@ -157,23 +192,47 @@ specify this.  An example would be '/mnt'.
 
 =back
 
 
 =back
 
-unthreaded => boolean
+mountopts => string
 
 =over 1
 
 
 =over 1
 
-This turns FUSE multithreading off and on.  NOTE: This perlmodule does not
-currently work properly in multithreaded mode!  The author is unfortunately
-not familiar enough with perl-threads internals, and according to the
-documentation available at time of writing (2002-03-08), those internals are
-subject to changing anyway.  Note that singlethreaded mode also means that
-you will not have to worry about reentrancy, though you will have to worry
-about recursive lookups (since the kernel holds a global lock on your
-filesystem and blocks waiting for one callback to complete before calling
-another).
+This is a comma seperated list of mount options to pass to the FUSE kernel
+module.
+
+At present, it allows the specification of the allow_other
+argument when mounting the new FUSE filesystem. To use this, you will also
+need 'user_allow_other' in /etc/fuse.conf as per the FUSE documention
 
 
-I hope to add full multithreading functionality later, but for now, I
-recommend you leave this option at the default, 1 (which means
-unthreaded, no threads will be used and no reentrancy is needed).
+  mountopts => "allow_other" or
+  mountopts => ""
+
+=back
+
+threaded => boolean
+
+=over 1
+
+This turns FUSE multithreading on and off.  The default is 0, meaning your FUSE
+script will run in single-threaded mode.  Note that single-threaded mode also
+means that you will not have to worry about reentrancy, though you will have to
+worry about recursive lookups.  In single-threaded mode, FUSE holds a global
+lock on your filesystem, and will wait for one callback to return before
+calling another.  This can lead to deadlocks, if your script makes any attempt
+to access files or directories in the filesystem it is providing.  (This
+includes calling stat() on the mount-point, statfs() calls from the 'df'
+command, and so on and so forth.)  It is worth paying a little attention and
+being careful about this.
+
+Enabling multithreading will cause FUSE to make multiple simultaneous calls
+into the various callback functions of your perl script.  If you enable 
+threaded mode, you can enjoy all the parallel execution and interactive
+response benefits of threads, and you get to enjoy all the benefits of race
+conditions and locking bugs, too.  Please also ensure any other perl modules
+you're using are also thread-safe.
+
+(If enabled, this option will cause a warning if your perl interpreter was not
+built with USE_ITHREADS, or if you have failed to use threads or
+threads::shared.)
 
 =back
 
 
 =back
 
@@ -349,6 +408,70 @@ or
 
 -ENOANO(), $namelen, $files, $files_free, $blocks, $blocks_avail, $blocksize
 
 
 -ENOANO(), $namelen, $files, $files_free, $blocks, $blocks_avail, $blocksize
 
+=head3 flush
+
+Arguments: Pathname
+Returns an errno or 0 on success.
+
+Called to synchronise any cached data. This is called before the file
+is closed. It may be called multiple times before a file is closed.
+
+=head3 release
+
+Arguments: Pathname, numeric flags passed to open
+Returns an errno or 0 on success.
+
+Called to indicate that there are no more references to the file. Called once
+for every file with the same pathname and flags as were passed to open.
+
+=head3 fsync
+
+Arguments: Pathname, numeric flags
+Returns an errno or 0 on success.
+
+Called to synchronise the file's contents. If flags is non-zero,
+only synchronise the user data. Otherwise synchronise the user and meta data.
+
+=head3 setxattr
+
+Arguments: Pathname, extended attribute's name, extended attribute's value, numeric flags (which is an OR-ing of XATTR_CREATE and XATTR_REPLACE 
+Returns an errno or 0 on success.
+
+Called to set the value of the named extended attribute.
+
+If you wish to reject setting of a particular form of extended attribute name
+(e.g.: regexps matching user\..* or security\..*), then return - EOPNOTSUPP.
+
+If flags is set to XATTR_CREATE and the extended attribute already exists,
+this should fail with - EEXIST. If flags is set to XATTR_REPLACE
+and the extended attribute doesn't exist, this should fail with - ENOATTR.
+
+XATTR_CREATE and XATTR_REPLACE are provided by this module, but not exported
+by default. To import them:
+
+    use Fuse ':xattr';
+
+or:
+
+    use Fuse ':all';
+
+=head3 getxattr
+
+Arguments: Pathname, extended attribute's name
+Returns an errno, 0 if there was no value, or the extended attribute's value.
+
+Called to get the value of the named extended attribute.
+
+=head3 listxattr
+
+Arguments: Pathname
+Returns a list: 0 or more text strings (the extended attribute names), followed by a numeric errno (usually 0).
+
+=head3 removexattr
+
+Arguments: Pathname, extended attribute's name
+Returns an errno or 0 on success.
+
 =head1 AUTHOR
 
 Mark Glines, E<lt>mark@glines.orgE<gt>
 =head1 AUTHOR
 
 Mark Glines, E<lt>mark@glines.orgE<gt>