From bf36016741c473b476aadef46ec9d7869523b8c0 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Mon, 3 Dec 2001 08:35:41 +0000 Subject: perl bindings --- AUTHORS | 6 + perl/Changes | 8 + perl/Fuse.pm | 228 ++++++++++++++++++++++++ perl/Fuse.xs | 521 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ perl/MANIFEST | 8 + perl/Makefile.PL | 17 ++ perl/README | 26 +++ perl/example.pl | 83 +++++++++ perl/test.pl | 18 ++ 9 files changed, 915 insertions(+) create mode 100644 perl/Changes create mode 100644 perl/Fuse.pm create mode 100644 perl/Fuse.xs create mode 100644 perl/MANIFEST create mode 100644 perl/Makefile.PL create mode 100644 perl/README create mode 100755 perl/example.pl create mode 100644 perl/test.pl diff --git a/AUTHORS b/AUTHORS index 5d0a86a..a81e3c0 100644 --- a/AUTHORS +++ b/AUTHORS @@ -8,3 +8,9 @@ Python bindings --------------- Jeff Epler + + +Perl bindings +------------- + +Mark Glines diff --git a/perl/Changes b/perl/Changes new file mode 100644 index 0000000..3981203 --- /dev/null +++ b/perl/Changes @@ -0,0 +1,8 @@ +Revision history for Perl extension Fuse. + +0.01 Wed Nov 28 21:45:20 2001 + - original version; created by h2xs 1.21 with options + include/fuse.h + +0.02 Sun Dec 2 18:59:56 2001 + - works well enough to release, but still needs testing diff --git a/perl/Fuse.pm b/perl/Fuse.pm new file mode 100644 index 0000000..2f4742a --- /dev/null +++ b/perl/Fuse.pm @@ -0,0 +1,228 @@ +package Fuse; + +use 5.006; +use strict; +use warnings; +use Errno; +use Carp; + +require Exporter; +require DynaLoader; +use AutoLoader; +use Data::Dumper; +our @ISA = qw(Exporter DynaLoader); + +# Items to export into callers namespace by default. Note: do not export +# names by default without a very good reason. Use EXPORT_OK instead. +# Do not simply export all your public functions/methods/constants. + +# 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_OK = ( @{ $EXPORT_TAGS{'all'} } ); + +our @EXPORT = qw( + FUSE_DEBUG +); +our $VERSION = '0.01'; + +sub AUTOLOAD { + # This AUTOLOAD is used to 'autoload' constants from the constant() + # XS function. If a constant is not found then control is passed + # to the AUTOLOAD in AutoLoader. + + my $constname; + our $AUTOLOAD; + ($constname = $AUTOLOAD) =~ s/.*:://; + croak "& not defined" if $constname eq 'constant'; + my $val = constant($constname, @_ ? $_[0] : 0); + if ($! != 0) { + if ($!{EINVAL}) { + $AutoLoader::AUTOLOAD = $AUTOLOAD; + goto &AutoLoader::AUTOLOAD; + } + else { + croak "Your vendor has not defined Fuse macro $constname"; + } + } + { + no strict 'refs'; + # Fixed between 5.005_53 and 5.005_61 + if ($] >= 5.00561) { + *$AUTOLOAD = sub () { $val }; + } + else { + *$AUTOLOAD = sub { $val }; + } + } + goto &$AUTOLOAD; +} + +bootstrap Fuse $VERSION; + +sub main { + my (@subs) = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); + my (@names) = qw(getattr readlink getdir mknod mkdir unlink rmdir symlink rename link chmod chown truncate utime open read write); + my ($tmp) = 0; + my (%mapping) = map { $_ => $tmp++ } (@names); + my (%otherargs) = (debug=>0, threaded=>1, mountpoint=>""); + 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"; + $subs[$mapping{$name}] = $subref; + } + } + perl_fuse_main($0,$otherargs{threaded},$otherargs{debug},$otherargs{mountpoint},@subs); +} + +# Autoload methods go after =cut, and are processed by the autosplit program. + +1; +__END__ + +=head1 NAME + +Fuse - write filesystems in Perl using FUSE + +=head1 SYNOPSIS + + use Fuse; + my ($mountpoint) = ""; + $mountpoint = shift(@ARGV) if @ARGV; + Fuse::main(mountpoint=>$mountpoint,getattr=>\&my_getattr,getdir=>\&my_getdir, ...); + +=head1 DESCRIPTION + +This lets you implement filesystems in perl, through the FUSE (Filesystem in USErspace) kernel/lib interface. + +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. +You can import standard error constants by saying something like "use POSIX qw(EDOTDOT ENOANO);". + +=head2 FUNCTIONS + +=head3 getattr + +Arguments: filename. +Returns a list, one of the following 4 possibilities: + +$errno or + +($blocks,$size,$gid,$uid,$nlink,$modes,$time) or + +($errno,$blocks,$size,$gid,$uid,$nlink,$modes,$time) or + +($errno,$blksize,$blocks,$size,$gid,$uid,$nlink,$modes,$time) + +B: device numeric, for filesystems that implement mknod? + +=head3 readlink + +Arguments: link pathname. +Returns a scalar: either a numeric constant, or a text string. + +=head3 getdir + +Arguments: Containing directory name. +Returns a list: 0 or more text strings (the filenames), followed by errno (usually 0). + +=head3 mknod + +Arguments: Filename, numeric modes, numeric device +Returns an errno (0 upon success, as usual). + +=head3 mkdir + +Arguments: New directory pathname, numeric modes. +Returns an errno. + +=head3 unlink + +Arguments: Filename. +Returns an errno. + +=head3 rmdir + +Arguments: Pathname. +Returns an errno. + +=head3 symlink + +Arguments: Existing filename, symlink name. +Returns an errno. + +=head3 rename + +Arguments: old filename, new filename. +Returns an errno. + +=head3 link + +Arguments: Existing filename, hardlink name. +Returns an errno. + +=head3 chmod + +Arguments: Pathname, numeric modes. +Returns an errno. + +=head3 chown + +Arguments: Pathname, numeric uid, numeric gid. +Returns an errno. + +=head3 truncate + +Arguments: Pathname, numeric offset. +Returns an errno. + +=head3 utime + +Arguments: Pathname, numeric actime, numeric modtime. +Returns an errno. + +=head3 open + +Arguments: Pathname, numeric flags (which is an OR-ing of stuff like O_RDONLY and O_SYNC, constants you can import from POSIX). +Returns an errno. + +=head3 read + +Arguments: Pathname, numeric requestedsize, numeric offset. +Returns a numeric errno, or a string scalar with up to $requestedsize bytes of data. + +=head3 write + +Arguments: Pathname, scalar buffer, numeric offset. You can use length($buffer) to find the buffersize. +Returns an errno. + +=head2 EXPORT + +None by default. + +=head2 Exportable constants + +None. + +=head1 AUTHOR + +Mark Glines, Emark@glines.orgE + +=head1 SEE ALSO + +L, the FUSE documentation. + +=cut diff --git a/perl/Fuse.xs b/perl/Fuse.xs new file mode 100644 index 0000000..9770c3a --- /dev/null +++ b/perl/Fuse.xs @@ -0,0 +1,521 @@ +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" + +#include + +static int +not_here(char *s) +{ + croak("%s not implemented on this architecture", s); + return -1; +} + +static double +constant(char *name, int len, int arg) +{ + errno = ENOENT; + return 0; +} + +SV *_PLfuse_callbacks[17]; + +int _PLfuse_getattr(const char *file, struct stat *result) { + dSP; + int rv, statcount; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVpv(file,strlen(file)))); + PUTBACK; + rv = call_sv(_PLfuse_callbacks[0],G_ARRAY); + SPAGAIN; + if(rv < 7) { + if(rv > 1) + croak("inappropriate number of returned values from getattr"); + if(rv) + rv = POPi; + else + rv = -EINVAL; + } else { + if(rv > 9) + croak("inappropriate number of returned values from getattr"); + result->st_ctime = result->st_mtime = result->st_atime = POPi; + result->st_mode = POPi; + result->st_nlink = POPi; + result->st_uid = POPi; + result->st_gid = POPi; + result->st_size = POPi; + result->st_blocks = POPi; + if(rv > 7) { + if(rv > 8) + result->st_blksize = POPi; + else + result->st_blksize = 1024; + rv = POPi; + } else { + result->st_blksize = 1024; + rv = 0; + } + } + FREETMPS; + LEAVE; + return rv; +} + +int _PLfuse_readlink(const char *file,char *buf,size_t buflen) { + int rv; + char *rvstr; + dXSARGS; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVpv(file,0))); + PUTBACK; + rv = call_sv(_PLfuse_callbacks[1],G_SCALAR); + SPAGAIN; + if(!rv) + rv = -ENOENT; + else + if(SvTYPE(ST(0)) == SVt_IV) + rv = POPi; + else { + strncpy(buf,POPp,buflen); + rv = 0; + } + FREETMPS; + LEAVE; + return rv; +} + +int _PLfuse_getdir(const char *file, fuse_dirh_t dirh, fuse_dirfil_t dirfil) { + int prv, rv; + dXSARGS; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVpv(file,0))); + PUTBACK; + prv = call_sv(_PLfuse_callbacks[2],G_ARRAY); + SPAGAIN; + if(prv) { + SV *mysv = POPs; + if(!SvIOK(mysv)) { + fprintf(stderr,"last getdir retval needs to be numeric (e.g. 0 or -ENOENT) (%s)\n",SvPV_nolen(mysv)); + rv = -ENOSYS; + } else { + rv = SvIV(mysv); + while(--prv) + dirfil(dirh,POPp,0); + } + } else { + fprintf(stderr,"getdir() handler returned nothing!\n"); + rv = -ENOSYS; + } + FREETMPS; + LEAVE; + return rv; +} + +int _PLfuse_mknod (const char *file, mode_t mode, dev_t dev) { + int rv; + SV *rvsv; + char *rvstr; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVpv(file,0))); + XPUSHs(sv_2mortal(newSViv(mode))); + XPUSHs(sv_2mortal(newSViv(dev))); + PUTBACK; + rv = call_sv(_PLfuse_callbacks[3],G_SCALAR); + SPAGAIN; + if(rv) + rv = POPi; + else + rv = 0; + FREETMPS; + LEAVE; + return rv; +} + +int _PLfuse_mkdir (const char *file, mode_t mode) { + int rv; + SV *rvsv; + char *rvstr; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVpv(file,0))); + XPUSHs(sv_2mortal(newSViv(mode))); + PUTBACK; + rv = call_sv(_PLfuse_callbacks[4],G_SCALAR); + SPAGAIN; + if(rv) + rv = POPi; + else + rv = 0; + FREETMPS; + LEAVE; + return rv; +} + + +int _PLfuse_unlink (const char *file) { + int rv; + SV *rvsv; + char *rvstr; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVpv(file,0))); + PUTBACK; + rv = call_sv(_PLfuse_callbacks[5],G_SCALAR); + SPAGAIN; + if(rv) + rv = POPi; + else + rv = 0; + FREETMPS; + LEAVE; + return rv; +} + +int _PLfuse_rmdir (const char *file) { + int rv; + SV *rvsv; + char *rvstr; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVpv(file,0))); + PUTBACK; + rv = call_sv(_PLfuse_callbacks[6],G_SCALAR); + SPAGAIN; + if(rv) + rv = POPi; + else + rv = 0; + FREETMPS; + LEAVE; + return rv; +} + +int _PLfuse_symlink (const char *file, const char *new) { + int rv; + SV *rvsv; + char *rvstr; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVpv(file,0))); + XPUSHs(sv_2mortal(newSVpv(new,0))); + PUTBACK; + rv = call_sv(_PLfuse_callbacks[7],G_SCALAR); + SPAGAIN; + if(rv) + rv = POPi; + else + rv = 0; + FREETMPS; + LEAVE; + return rv; +} + +int _PLfuse_rename (const char *file, const char *new) { + int rv; + SV *rvsv; + char *rvstr; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVpv(file,0))); + XPUSHs(sv_2mortal(newSVpv(new,0))); + PUTBACK; + rv = call_sv(_PLfuse_callbacks[8],G_SCALAR); + SPAGAIN; + if(rv) + rv = POPi; + else + rv = 0; + FREETMPS; + LEAVE; + return rv; +} + +int _PLfuse_link (const char *file, const char *new) { + int rv; + SV *rvsv; + char *rvstr; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVpv(file,0))); + XPUSHs(sv_2mortal(newSVpv(new,0))); + PUTBACK; + rv = call_sv(_PLfuse_callbacks[9],G_SCALAR); + SPAGAIN; + if(rv) + rv = POPi; + else + rv = 0; + FREETMPS; + LEAVE; + return rv; +} + +int _PLfuse_chmod (const char *file, mode_t mode) { + int rv; + SV *rvsv; + char *rvstr; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVpv(file,0))); + XPUSHs(sv_2mortal(newSViv(mode))); + PUTBACK; + rv = call_sv(_PLfuse_callbacks[10],G_SCALAR); + SPAGAIN; + if(rv) + rv = POPi; + else + rv = 0; + FREETMPS; + LEAVE; + return rv; +} + +int _PLfuse_chown (const char *file, uid_t uid, gid_t gid) { + int rv; + SV *rvsv; + char *rvstr; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVpv(file,0))); + XPUSHs(sv_2mortal(newSViv(uid))); + XPUSHs(sv_2mortal(newSViv(gid))); + PUTBACK; + rv = call_sv(_PLfuse_callbacks[11],G_SCALAR); + SPAGAIN; + if(rv) + rv = POPi; + else + rv = 0; + FREETMPS; + LEAVE; + return rv; +} + +int _PLfuse_truncate (const char *file, off_t off) { + int rv; + SV *rvsv; + char *rvstr; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVpv(file,0))); + XPUSHs(sv_2mortal(newSViv(off))); + PUTBACK; + rv = call_sv(_PLfuse_callbacks[12],G_SCALAR); + SPAGAIN; + if(rv) + rv = POPi; + else + rv = 0; + FREETMPS; + LEAVE; + return rv; +} + +int _PLfuse_utime (const char *file, struct utimbuf *uti) { + int rv; + SV *rvsv; + char *rvstr; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVpv(file,0))); + XPUSHs(sv_2mortal(newSViv(uti->actime))); + XPUSHs(sv_2mortal(newSViv(uti->modtime))); + PUTBACK; + rv = call_sv(_PLfuse_callbacks[13],G_SCALAR); + SPAGAIN; + if(rv) + rv = POPi; + else + rv = 0; + FREETMPS; + LEAVE; + return rv; +} + +int _PLfuse_open (const char *file, int flags) { + int rv; + SV *rvsv; + char *rvstr; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVpv(file,0))); + XPUSHs(sv_2mortal(newSViv(flags))); + PUTBACK; + rv = call_sv(_PLfuse_callbacks[14],G_SCALAR); + SPAGAIN; + if(rv) + rv = POPi; + else + rv = 0; + FREETMPS; + LEAVE; + return rv; +} + +int _PLfuse_read (const char *file, char *buf, size_t buflen, off_t off) { + int rv; + char *rvstr; + dXSARGS; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVpv(file,0))); + XPUSHs(sv_2mortal(newSViv(buflen))); + XPUSHs(sv_2mortal(newSViv(off))); + PUTBACK; + rv = call_sv(_PLfuse_callbacks[15],G_SCALAR); + SPAGAIN; + if(!rv) + rv = -ENOENT; + else { + SV *mysv = sv_2mortal(POPs); + if(SvTYPE(mysv) == SVt_IV) + rv = SvIV(mysv); + else { + rv = SvLEN(mysv); + if(rv > buflen) + croak("read() handler returned more than buflen!"); + if(rv) + memcpy(buf,SvPV_nolen(mysv),rv); + } + } + return rv; +} + +int _PLfuse_write (const char *file, const char *buf, size_t buflen, off_t off) { + int rv; + char *rvstr; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVpv(file,0))); + XPUSHs(sv_2mortal(newSVpvn(buf,buflen))); + XPUSHs(sv_2mortal(newSViv(off))); + PUTBACK; + rv = call_sv(_PLfuse_callbacks[16],G_SCALAR); + SPAGAIN; + if(rv) + rv = POPi; + else + rv = 0; + FREETMPS; + LEAVE; + return rv; +} + +struct fuse_operations _available_ops = { +getattr: _PLfuse_getattr, + _PLfuse_readlink, + _PLfuse_getdir, + _PLfuse_mknod, + _PLfuse_mkdir, + _PLfuse_unlink, + _PLfuse_rmdir, + _PLfuse_symlink, + _PLfuse_rename, + _PLfuse_link, + _PLfuse_chmod, + _PLfuse_chown, + _PLfuse_truncate, + _PLfuse_utime, + _PLfuse_open, + _PLfuse_read, + _PLfuse_write +}; + +MODULE = Fuse PACKAGE = Fuse +PROTOTYPES: DISABLE + + +double +constant(sv,arg) + PREINIT: + STRLEN len; + INPUT: + SV * sv + char * s = SvPV(sv, len); + int arg + CODE: + RETVAL = constant(s,len,arg); + OUTPUT: + RETVAL + +void +perl_fuse_main(...) + PREINIT: + struct fuse_operations fops = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}; + int i, varnum = 0, threads, debug, argc; + char **argv; + STRLEN n_a; + STRLEN l; + INIT: + if(items != 21) { + fprintf(stderr,"Perl<->C inconsistency or internal error\n"); + XSRETURN_UNDEF; + } + CODE: + threads = !SvIV(ST(1)); + debug = SvIV(ST(2)); + if(threads && debug) { + argc = 4; + argv = ((char*[]){NULL,NULL,"-s","-d"}); + } else if(threads) { + argc = 3; + argv = ((char*[]){NULL,NULL,"-s"}); + } else if(debug) { + argc = 3; + argv = ((char*[]){NULL,NULL,"-d"}); + } else { + argc = 2; + argv = ((char*[]){"str","mnt"}); + } + argv[0] = SvPV(ST(0),n_a); + if(strlen(SvPV(ST(3),n_a))) + argv[1] = SvPV(ST(3),n_a); + else + argc--; + + for(i=0;i<17;i++) { + SV *var = ST(i+4); + if((var != &PL_sv_undef) && SvROK(var)) { + if(SvTYPE(SvRV(var)) == SVt_PVCV) { + void **tmp1 = (void**)&_available_ops, **tmp2 = (void**)&fops; + tmp2[i] = tmp1[i]; + _PLfuse_callbacks[i] = var; + } else + croak("arg is not a code reference!"); + } + } + fuse_main(argc,argv,&fops); diff --git a/perl/MANIFEST b/perl/MANIFEST new file mode 100644 index 0000000..9d7f036 --- /dev/null +++ b/perl/MANIFEST @@ -0,0 +1,8 @@ +Changes +Fuse.pm +Fuse.xs +Makefile.PL +MANIFEST +README +test.pl +example.pl diff --git a/perl/Makefile.PL b/perl/Makefile.PL new file mode 100644 index 0000000..d495683 --- /dev/null +++ b/perl/Makefile.PL @@ -0,0 +1,17 @@ +use ExtUtils::MakeMaker; +# See lib/ExtUtils/MakeMaker.pm for details of how to influence +# the contents of the Makefile that is written. +WriteMakefile( + 'NAME' => 'Fuse', + 'VERSION_FROM' => 'Fuse.pm', # finds $VERSION + 'PREREQ_PM' => {}, # e.g., Module::Name => 1.1 + ($] >= 5.005 ? ## Add these new keywords supported since 5.005 + (ABSTRACT_FROM => 'Fuse.pm', # retrieve abstract from module + AUTHOR => 'Mark Glines ') : ()), + 'LIBS' => [''], # e.g., '-lm' + 'DEFINE' => '-g -ggdb', # e.g., '-DHAVE_SOMETHING' + # Insert -I. if you add *.h files later: + 'INC' => '-I../include', # e.g., '-I/usr/include/other' + # Un-comment this if you add C files to link with later: + 'OBJECT' => 'Fuse.o ../lib/libfuse.a -lpthread', # link all the C files too +); diff --git a/perl/README b/perl/README new file mode 100644 index 0000000..b22b87e --- /dev/null +++ b/perl/README @@ -0,0 +1,26 @@ +Fuse version 0.01 +================= + +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. + +INSTALLATION + +To install this module type the following: + + perl Makefile.PL + make + make test # currently this just makes sure the lib can link + 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 , +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. diff --git a/perl/example.pl b/perl/example.pl new file mode 100755 index 0000000..257f2c9 --- /dev/null +++ b/perl/example.pl @@ -0,0 +1,83 @@ +#!/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 ($blocks, $gid, $uid, $nlink) = (1,0,0,1); + # 4 possible return values: + #return -ENOENT(); # or any other error you care to + return ($blocks,$size,$gid,$uid,$nlink,$modes,$files{$file}{ctime}); + # return ($errno,$blocks,$size,$gid,$uid,$nlink,$modes,$time); + # return ($errno,$blksize,$blocks,$size,$gid,$uid,$nlink,$modes,$time); + # if omitted, errno defaults to 0, and blksize defaults to 1024. +} + +sub e_getdir { + # return as many text filenames as you like, followed by the retval. + 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); + return -ENOENT() unless exists($files{$file}); + return -EISDIR() unless exists($files{$file}{cont}); + 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); +} + +# 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, + read=>\&e_read, +); diff --git a/perl/test.pl b/perl/test.pl new file mode 100644 index 0000000..eee3421 --- /dev/null +++ b/perl/test.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +# Before `make install' is performed this script should be runnable with +# `make test'. After `make install' it should work as `perl test.pl' + +######################### + +# change 'tests => 1' to 'tests => last_test_to_print'; + +use Test; +BEGIN { plan tests => 1 }; +use Fuse; +ok(1); # If we made it this far, we're ok. +######################### + +# Insert your test code below, the Test module is use()ed here so read +# its man page ( perldoc Test ) for help writing this test script. + +# haven't written anything, because I don't want to require root -- cgit v1.2.3