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