From 59c0245df72e34d0fdd64ee180e5fb3f33782621 Mon Sep 17 00:00:00 2001 From: Alex Sudakov Date: Thu, 24 Feb 2011 23:08:35 +0100 Subject: [PATCH] RT#55953 added readdir implementation --- AUTHORS | 1 + Fuse.pm | 2 +- Fuse.xs | 61 ++++++++++++++++++----- examples/readdir.pl | 116 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 166 insertions(+), 14 deletions(-) create mode 100755 examples/readdir.pl diff --git a/AUTHORS b/AUTHORS index fed923c..ffc2de9 100644 --- a/AUTHORS +++ b/AUTHORS @@ -9,3 +9,4 @@ Csaba Henk - update to API 25 Vladimir V. Kolpakov - contributed cleanup for warnings Andrew Chadwick - fixes for Ubuntu and dh-make-perl (fakeroot) Chris Dolan - fixes for MacFuse 1.1.0 +Alex Sudakov - fixes for readdir support diff --git a/Fuse.pm b/Fuse.pm index 8d9bd27..255bb69 100644 --- a/Fuse.pm +++ b/Fuse.pm @@ -77,7 +77,7 @@ bootstrap Fuse $VERSION; sub main { my @names = qw(getattr readlink getdir mknod mkdir unlink rmdir symlink rename link chmod chown truncate utime open read write statfs - flush release fsync setxattr getxattr listxattr removexattr); + flush release fsync setxattr getxattr listxattr removexattr opendir readdir releasedir fsyncdir); my @subs = map {undef} @names; my @validOpts = qw(ro allow_other default_permissions fsname use_ino nonempty); my $tmp = 0; diff --git a/Fuse.xs b/Fuse.xs index 9e1d671..92b9510 100644 --- a/Fuse.xs +++ b/Fuse.xs @@ -36,7 +36,7 @@ static inline void create_perl_context() { #define DEBUGf(a...) #endif -#define N_CALLBACKS 25 +#define N_CALLBACKS 29 SV *_PLfuse_callbacks[N_CALLBACKS]; int _PLfuse_getattr(const char *file, struct stat *result) { @@ -115,13 +115,47 @@ int _PLfuse_readlink(const char *file,char *buf,size_t buflen) { return rv; } -#if 0 -/* - * This doesn't yet work... we alwas get ENOSYS when trying to use readdir(). - * Well, of course, getdir() is fine as well. - */ - int _PLfuse_readdir(const char *file, void *dirh, fuse_fill_dir_t dirfil, off_t off, struct fuse_file_info *fi) { -#endif +int _PLfuse_opendir(const char *file, struct fuse_file_info *info) { + croak("opendir NOT IMPLEMENTED"); +} +int _PLfuse_releasedir(const char *file, struct fuse_file_info *info) { + croak("releasedir NOT IMPLEMENTED"); +} +int _PLfuse_fsyncdir(const char *file, struct fuse_file_info *info) { + croak("fsyncdir NOT IMPLEMENTED"); +} + +int _PLfuse_readdir(const char *file, void *dirh, fuse_fill_dir_t dirfil, off_t off, struct fuse_file_info *fi) { + int prv, rv, offset; + SV *entry; + FUSE_CONTEXT_PRE; + DEBUGf("readdir begin\n"); + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVpv(file,0))); + XPUSHs(sv_2mortal(newSViv(off))); + PUTBACK; + prv = call_sv(_PLfuse_callbacks[26],G_ARRAY); + SPAGAIN; + if(3 == prv) { + rv = POPi; + offset = POPi; + entry = POPs; + if(SvOK(entry)) + dirfil(dirh,SvPV_nolen(entry),NULL,offset); + } else { + fprintf(stderr,"readdir() handler didn't return 2 values!\n"); + rv = -ENOSYS; + } + FREETMPS; + LEAVE; + PUTBACK; + DEBUGf("readdir end: %i\n",rv); + FUSE_CONTEXT_POST; + return rv; +} + int _PLfuse_getdir(const char *file, fuse_dirh_t dirh, fuse_dirfil_t dirfil) { int prv, rv; FUSE_CONTEXT_PRE; @@ -783,9 +817,6 @@ struct fuse_operations _available_ops = { getattr: _PLfuse_getattr, readlink: _PLfuse_readlink, getdir: _PLfuse_getdir, -#if 0 -readdir: _PLfuse_readdir, -#endif mknod: _PLfuse_mknod, mkdir: _PLfuse_mkdir, unlink: _PLfuse_unlink, @@ -808,6 +839,10 @@ setxattr: _PLfuse_setxattr, getxattr: _PLfuse_getxattr, listxattr: _PLfuse_listxattr, removexattr: _PLfuse_removexattr, +opendir: _PLfuse_opendir, +readdir: _PLfuse_readdir, +releasedir: _PLfuse_releasedir, +fsyncdir: _PLfuse_fsyncdir, }; MODULE = Fuse PACKAGE = Fuse @@ -836,14 +871,14 @@ 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,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}; + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}; int i, fd, debug, threaded; char *mountpoint; char *mountopts; struct fuse_args margs = FUSE_ARGS_INIT(0, NULL); struct fuse_args fargs = FUSE_ARGS_INIT(0, NULL); INIT: - if(items != 29) { + if(items != 4+N_CALLBACKS) { fprintf(stderr,"Perl<->C inconsistency or internal error\n"); XSRETURN_UNDEF; } diff --git a/examples/readdir.pl b/examples/readdir.pl new file mode 100755 index 0000000..4210696 --- /dev/null +++ b/examples/readdir.pl @@ -0,0 +1,116 @@ +#!/usr/bin/perl -w +use strict; + +#use blib; +use Fuse qw(fuse_get_context); +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 + }, + me => { + size => 45, + 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; + $size = $files{$file}{size} if exists $files{$file}{size}; + my ($modes) = ($files{$file}{type}<<9) + $files{$file}{mode}; + my ($dev, $ino, $rdev, $blocks, $gid, $uid, $nlink, $blksize) = (0,0,0,1,0,0,1,1024); + my ($atime, $ctime, $mtime); + $atime = $ctime = $mtime = $files{$file}{ctime}; + # 2 possible types of return values: + #return -ENOENT(); # or any other error you care to + #print(join(",",($dev,$ino,$modes,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks)),"\n"); + return ($dev,$ino,$modes,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks); +} + +sub e_getdir { + # return as many text filenames as you like, followed by the retval. + print((scalar keys %files)."\n"); + return (keys %files),0; +} + +sub e_readdir { +use Data::Dumper; +print Dumper(\@_); + my ($path,$offset) = @_; + # return as many text filenames as you like, followed by the retval. + print((scalar keys %files)."\n"); + my @a = keys %files; + return ($offset<0xFFFF ? $a[0] : undef),1+$offset,0; +} + +sub e_open { + # VFS sanity check; it keeps all the necessary state, not much to do here. + my ($file) = filename_fixup(shift); + print("open called\n"); + return -ENOENT() unless exists($files{$file}); + return -EISDIR() if $files{$file}{type} & 0040; + print("open ok\n"); + 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}); + if(!exists($files{$file}{cont})) { + return -EINVAL() if $off > 0; + my $context = fuse_get_context(); + return sprintf("pid=0x%08x uid=0x%08x gid=0x%08x\n",@$context{'pid','uid','gid'}); + } + return -EINVAL() if $off > length($files{$file}{cont}); + return 0 if $off == length($files{$file}{cont}); + return substr($files{$file}{cont},$off,$buf); +} + +sub e_statfs { return 255, 1, 1, 1, 1, 2 } + +# 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=>"main::e_getattr", +# getdir =>"main::e_getdir", +# getdir =>"main::e_readdir", + readdir=>"main::e_readdir", + open =>"main::e_open", + statfs =>"main::e_statfs", + read =>"main::e_read", + threaded=>0 +); -- 2.20.1