Skip Menu |

This queue is for tickets about the Fuse CPAN distribution.

Report information
The Basics
Id: 93472
Status: new
Priority: 0/
Queue: Fuse

People
Owner: Nobody in particular
Requestors: hamann.w [...] t-online.de
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: (no value)
Fixed in: (no value)



Subject: fuse suggestion
Date: 2 Mar 2014 15:01:42 -0000
To: bug-Fuse [...] rt.cpan.org
From: hamann.w [...] t-online.de
Hi, may I suggest to supplement the normal "closed loop" interface by an event-driven one So the user might call my ($fuse, $ch) = Fuse::setup( mountpoint=>$mountpoint, getattr=>"main::e_getattr", ); and then later select on input from the fuse channel my ($rin,$ein); my ($rout, $eout); while(1) { $rin = ""; vec($rin, $ch, 1) = 1; select($rout = $rin, undef, $eout = $ein, 30); if(vec($rout, $ch, 1)) { Fuse::process($fuse); } This opens a chance for a filesystem to accept "control" input via a different channel. Possible applications might include file systems that act as daemons like usual, but open a channel for users to interact with them - filter file system that allows the user to add/remove folders without restarting the fuse process - cryptdir file system where the other channel is used to pick up unlock key (This is actually what I am using this for: if something asks for the contents of my .netrc, and the ui process is running, it prompts for a decrypt key. The daemon will remember it for several minutes so the next ftp request can work too) The enclosed diff is for version 0.15, running on a system with fuse-2.7.2 installed Best regards Wolfgang Hamann --- Fuse.pm.orig 2013-10-17 06:02:38.000000000 +0200 +++ Fuse.pm 2013-10-17 06:06:41.000000000 +0200 @@ -130,6 +130,77 @@ perl_fuse_main(@otherargs{@otherargs},@subs); } +sub setup { + 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 + opendir readdir releasedir fsyncdir init destroy access + create ftruncate fgetattr lock utimens bmap); + my $fuse_version = fuse_version(); + if ($fuse_version >= 2.8) { + # junk doesn't contain a function pointer, and hopefully + # never will; it's a "dead" zone in the struct + # fuse_operations where a flag bit is declared. we don't + # need to concern ourselves with it, and it appears any + # arch with a 64 bit pointer will align everything to + # 8 bytes, making the question of pointer alignment for + # the last 2 wrapper functions no big thing. + push(@names, qw/junk ioctl poll/); + } + my @subs = map {undef} @names; + my $tmp = 0; + my %mapping = map { $_ => $tmp++ } @names; + my @otherargs = qw(debug threaded mountpoint mountopts nullpath_ok utimens_as_array); + my %otherargs = ( + debug => 0, + threaded => 0, + mountpoint => "", + mountopts => "", + nullpath_ok => 0, + utimens_as_array => 0, + ); + 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 => \"main::my_getattr\", ...)" unless $subref; + $subs[$mapping{$name}] = $subref; + } + } + 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; + } + } + return perl_fuse_setup(@otherargs{@otherargs},@subs); +} + +sub process { + my $fuse = shift; + perl_fuse_process($fuse); +} + +sub shutdown { + my $fuse = shift; + perl_fuse_shutdown($fuse); +} + # Autoload methods go after =cut, and are processed by the autosplit program. 1; --- Fuse.xs.orig 2013-10-17 06:02:32.000000000 +0200 +++ Fuse.xs 2013-10-18 23:37:25.000000000 +0200 @@ -4,6 +4,18 @@ #include "XSUB.h" #include <fuse.h> +#include <fuse/fuse_common.h> +#include <fuse/fuse_lowlevel.h> + +typedef struct fusedata +{ struct fuse *fuse; + char *buf; + struct fuse_chan *ch; + struct fuse_session *se; + char *mountpoint; + int bufsize; +} * Fusedata; +extern struct fuse_session *fuse_get_session(struct fuse *f); #if (defined(__FreeBSD__) && !defined(__APPLE__)) || defined(__NetBSD__) # define XATTR_CREATE 1 @@ -1787,6 +1799,138 @@ fuse_unmount(mountpoint,fc); fuse_opt_free_args(&args); +void +perl_fuse_setup(...) + PREINIT: + struct fuse_operations fops; + int i, debug; + char *mountpoint; + char *mountopts; + struct fuse_args args = FUSE_ARGS_INIT(0, NULL); + struct fuse *fuse; + struct fusedata *fusedata= malloc(sizeof (struct fusedata)); + struct fuse_chan *fc; + dMY_CXT; + INIT: + if(items != N_CALLBACKS + 6) { + fprintf(stderr,"Perl<->C inconsistency or internal error\n"); + XSRETURN_UNDEF; + } + memset(&fops, 0, sizeof(struct fuse_operations)); + PPCODE: + debug = SvIV(ST(0)); + MY_CXT.threaded = SvIV(ST(1)); + MY_CXT.handles = (HV*)(sv_2mortal((SV*)(newHV()))); + if(MY_CXT.threaded) { +#ifdef FUSE_USE_ITHREADS + master_interp = aTHX; + MUTEX_INIT(&MY_CXT.mutex); + SvSHARE((SV*)(MY_CXT.handles)); +#else + fprintf(stderr,"FUSE warning: Your script has requested multithreaded " + "mode, but your perl was not built with a supported " + "thread model. Threads are disabled.\n"); + MY_CXT.threaded = 0; +#endif + } + mountpoint = SvPV_nolen(ST(2)); + mountopts = SvPV_nolen(ST(3)); +#if FUSE_VERSION >= 28 + fops.flag_nullpath_ok = SvIV(ST(4)); +#endif /* FUSE_VERSION >= 28 */ + MY_CXT.utimens_as_array = SvIV(ST(5)); + for(i=0;i<N_CALLBACKS;i++) { + SV *var = ST(i+6); + /* allow symbolic references, or real code references. */ + if(SvOK(var) && (SvPOK(var) || (SvROK(var) && SvTYPE(SvRV(var)) == SVt_PVCV))) { + void **tmp1 = (void**)&_available_ops, **tmp2 = (void**)&fops; + /* Dirty hack, to keep anything from overwriting the + * flag area with a pointer. There should never be + * anything passed as 'junk', but this prevents + * someone from doing it and screwing things up... */ + if (i == 38) + continue; + tmp2[i] = tmp1[i]; + /* it is important to protect these values until shutdown */ + MY_CXT.callback[i] = SvREFCNT_inc(var); + } else if(SvOK(var)) { + croak("invalid callback (%i) passed to perl_fuse_main " + "(%s is not a string, code ref, or undef).\n", + i+6,SvPVbyte_nolen(var)); + } else { + MY_CXT.callback[i] = NULL; + } + } + /* + * XXX: What comes here is just a ridiculous use of the option parsing API + * to hack on compatibility with other parts of the new API. First and + * foremost, real C argc/argv would be good to get at... + */ + if ((mountopts || debug) && fuse_opt_add_arg(&args, "") == -1) { + fuse_opt_free_args(&args); + croak("out of memory\n"); + } + if (mountopts && strcmp("", mountopts) && + (fuse_opt_add_arg(&args, "-o") == -1 || + fuse_opt_add_arg(&args, mountopts) == -1)) { + fuse_opt_free_args(&args); + croak("out of memory\n"); + } + if (debug && fuse_opt_add_arg(&args, "-d") == -1) { + fuse_opt_free_args(&args); + croak("out of memory\n"); + } + fc = fuse_mount(mountpoint,&args); + if (fc == NULL) + croak("could not mount fuse filesystem!\n"); + fuse = fuse_new(fc,&args,&fops,sizeof(fops),NULL); + fuse_opt_free_args(&args); +// fusedata = malloc(sizeof (struct fusedata)); + fusedata->fuse = fuse; + fusedata->se = fuse_get_session(fuse); + fusedata->ch = fuse_session_next_chan(fusedata->se, NULL); + fusedata->bufsize = fuse_chan_bufsize(fusedata->ch); + fusedata->buf = malloc(fusedata->bufsize); + if(!fusedata->buf) + croak("not enough mem"); + fusedata->mountpoint = strdup(mountpoint); +// fprintf(stderr, "fusedata %x perl_fuse_process %x\n", fusedata, &XS_Fuse_perl_fuse_process); +// sleep (30); + XPUSHs(sv_setref_pv(sv_newmortal(), "Fusedata", fusedata)); + XPUSHs(sv_2mortal(newSViv(fuse_chan_fd(fc)))); + +void +perl_fuse_process(Fusedata fusedata) +CODE: + int res; +// fprintf(stderr, "fusedata %x ch %x se %x buf %x\n", fusedata, fusedata->ch, fusedata->se, fusedata->buf); + while (!fuse_session_exited(fusedata->se)) { + struct fuse_chan *tmpch = fusedata->ch; + res = fuse_chan_recv(&tmpch, fusedata->buf, fusedata->bufsize); + if (res == -EINTR) + continue; + if (res <= 0) + break; + fuse_session_process(fusedata->se, fusedata->buf, res, tmpch); + break; + } + +void +perl_fuse_shutdown(Fusedata fusedata) +PREINIT: + int i; + dMY_CXT; +CODE: + for(i = 0 ; i < N_CALLBACKS ; i++) + { SV *var = MY_CXT.callback[i]; + if(var) + SvREFCNT_dec(var); + } + free(fusedata->buf); + fuse_unmount(fusedata->mountpoint, fusedata->fuse); + fuse_destroy(fusedata->fuse); + free(fusedata); + #if FUSE_VERSION >= 28 void --- typemap.orig 2013-10-17 07:44:31.000000000 +0200 +++ typemap 2013-10-17 07:44:43.000000000 +0200 @@ -0,0 +1,2 @@ +TYPEMAP +Fusedata T_PTROBJ