#include "EXTERN.h" #include "perl.h" #include "XSUB.h" #ifdef USE_ITHREADS # ifdef I_PTHREAD /* perl implements threads with pthread. So, we use the pthread API for * handling thread-local storage. */ # include PerlInterpreter *master_interp = NULL; static inline void create_perl_context() { if(master_interp) { PerlInterpreter *me = PERL_GET_CONTEXT; if(!me) { PERL_SET_CONTEXT(master_interp); me = perl_clone(master_interp, CLONEf_CLONE_HOST); } } } # define FUSE_CONTEXT_PRE create_perl_context(); { dSP # define FUSE_CONTEXT_POST } # define FUSE_USE_ITHREADS # else # error "Sorry, I don't know how to handle ithreads on this architecture." # endif #else # define FUSE_CONTEXT_PRE dSP # define FUSE_CONTEXT_POST #endif #include #undef DEBUGf #if 0 #define DEBUGf(f, a...) fprintf(stderr, "%s:%d (%i): " f,__BASE_FILE__,__LINE__,sp-PL_stack_base ,##a ) #else #define DEBUGf(a...) #endif #define N_CALLBACKS 25 SV *_PLfuse_callbacks[N_CALLBACKS]; int _PLfuse_getattr(const char *file, struct stat *result) { int rv; FUSE_CONTEXT_PRE; DEBUGf("getattr begin: %s\n",file); ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(sv_2mortal(newSVpv(file,strlen(file)))); PUTBACK; rv = call_sv(_PLfuse_callbacks[0],G_ARRAY); SPAGAIN; if(rv != 13) { if(rv > 1) { fprintf(stderr,"inappropriate number of returned values from getattr\n"); rv = -ENOSYS; } else if(rv) rv = POPi; else rv = -ENOENT; } else { result->st_blocks = POPi; result->st_blksize = POPi; result->st_ctime = POPi; result->st_mtime = POPi; result->st_atime = POPi; result->st_size = POPi; result->st_rdev = POPi; result->st_gid = POPi; result->st_uid = POPi; result->st_nlink = POPi; result->st_mode = POPi; result->st_ino = POPi; result->st_dev = POPi; rv = 0; } FREETMPS; LEAVE; PUTBACK; DEBUGf("getattr end: %i\n",rv); FUSE_CONTEXT_POST; return rv; } int _PLfuse_readlink(const char *file,char *buf,size_t buflen) { int rv; FUSE_CONTEXT_PRE; if(buflen < 1) return EINVAL; DEBUGf("readlink begin\n"); 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 { SV *mysv = POPs; if(SvTYPE(mysv) == SVt_IV || SvTYPE(mysv) == SVt_NV) rv = SvIV(mysv); else { strncpy(buf,SvPV_nolen(mysv),buflen); rv = 0; } } FREETMPS; LEAVE; buf[buflen-1] = 0; PUTBACK; DEBUGf("readlink end: %i\n",rv); FUSE_CONTEXT_POST; 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_getdir(const char *file, fuse_dirh_t dirh, fuse_dirfil_t dirfil) { int prv, rv; FUSE_CONTEXT_PRE; DEBUGf("getdir begin\n"); ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(sv_2mortal(newSVpv(file,0))); PUTBACK; prv = call_sv(_PLfuse_callbacks[2],G_ARRAY); SPAGAIN; if(prv) { rv = POPi; while(--prv) dirfil(dirh,POPp,0,0); } else { fprintf(stderr,"getdir() handler returned nothing!\n"); rv = -ENOSYS; } FREETMPS; LEAVE; PUTBACK; DEBUGf("getdir end: %i\n",rv); FUSE_CONTEXT_POST; return rv; } int _PLfuse_mknod (const char *file, mode_t mode, dev_t dev) { int rv; FUSE_CONTEXT_PRE; DEBUGf("mknod begin\n"); 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; PUTBACK; DEBUGf("mknod end: %i\n",rv); FUSE_CONTEXT_POST; return rv; } int _PLfuse_mkdir (const char *file, mode_t mode) { int rv; FUSE_CONTEXT_PRE; DEBUGf("mkdir begin\n"); 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; PUTBACK; DEBUGf("mkdir end: %i\n",rv); FUSE_CONTEXT_POST; return rv; } int _PLfuse_unlink (const char *file) { int rv; FUSE_CONTEXT_PRE; DEBUGf("unlink begin\n"); 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; PUTBACK; DEBUGf("unlink end: %i\n",rv); FUSE_CONTEXT_POST; return rv; } int _PLfuse_rmdir (const char *file) { int rv; FUSE_CONTEXT_PRE; DEBUGf("rmdir begin\n"); 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; PUTBACK; DEBUGf("rmdir end: %i\n",rv); FUSE_CONTEXT_POST; return rv; } int _PLfuse_symlink (const char *file, const char *new) { int rv; FUSE_CONTEXT_PRE; DEBUGf("symlink begin\n"); 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; PUTBACK; DEBUGf("symlink end: %i\n",rv); FUSE_CONTEXT_POST; return rv; } int _PLfuse_rename (const char *file, const char *new) { int rv; FUSE_CONTEXT_PRE; DEBUGf("rename begin\n"); 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; PUTBACK; DEBUGf("rename end: %i\n",rv); FUSE_CONTEXT_POST; return rv; } int _PLfuse_link (const char *file, const char *new) { int rv; FUSE_CONTEXT_PRE; DEBUGf("link begin\n"); 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; PUTBACK; DEBUGf("link end: %i\n",rv); FUSE_CONTEXT_POST; return rv; } int _PLfuse_chmod (const char *file, mode_t mode) { int rv; FUSE_CONTEXT_PRE; DEBUGf("chmod begin\n"); 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; PUTBACK; DEBUGf("chmod end: %i\n",rv); FUSE_CONTEXT_POST; return rv; } int _PLfuse_chown (const char *file, uid_t uid, gid_t gid) { int rv; FUSE_CONTEXT_PRE; DEBUGf("chown begin\n"); 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; PUTBACK; DEBUGf("chown end: %i\n",rv); FUSE_CONTEXT_POST; return rv; } int _PLfuse_truncate (const char *file, off_t off) { int rv; FUSE_CONTEXT_PRE; DEBUGf("truncate begin\n"); 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; PUTBACK; DEBUGf("truncate end: %i\n",rv); FUSE_CONTEXT_POST; return rv; } int _PLfuse_utime (const char *file, struct utimbuf *uti) { int rv; FUSE_CONTEXT_PRE; DEBUGf("utime begin\n"); 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; PUTBACK; DEBUGf("utime end: %i\n",rv); FUSE_CONTEXT_POST; return rv; } int _PLfuse_open (const char *file, struct fuse_file_info *fi) { int rv; int flags = fi->flags; FUSE_CONTEXT_PRE; DEBUGf("open begin\n"); 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; PUTBACK; DEBUGf("open end: %i\n",rv); FUSE_CONTEXT_POST; return rv; } int _PLfuse_read (const char *file, char *buf, size_t buflen, off_t off, struct fuse_file_info *fi) { int rv; FUSE_CONTEXT_PRE; DEBUGf("read begin\n"); 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 = POPs; if(SvTYPE(mysv) == SVt_NV || SvTYPE(mysv) == SVt_IV) rv = SvIV(mysv); else { if(SvPOK(mysv)) { rv = SvCUR(mysv); } else { rv = 0; } if(rv > buflen) croak("read() handler returned more than buflen! (%i > %i)",rv,buflen); if(rv) memcpy(buf,SvPV_nolen(mysv),rv); } } FREETMPS; LEAVE; PUTBACK; DEBUGf("read end: %i\n",rv); FUSE_CONTEXT_POST; return rv; } int _PLfuse_write (const char *file, const char *buf, size_t buflen, off_t off, struct fuse_file_info *fi) { int rv; FUSE_CONTEXT_PRE; DEBUGf("write begin\n"); 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; PUTBACK; DEBUGf("write end: %i\n",rv); FUSE_CONTEXT_POST; return rv; } int _PLfuse_statfs (const char *file, struct statvfs *st) { int rv; FUSE_CONTEXT_PRE; DEBUGf("statfs begin\n"); ENTER; SAVETMPS; PUSHMARK(SP); PUTBACK; rv = call_sv(_PLfuse_callbacks[17],G_ARRAY); SPAGAIN; DEBUGf("statfs got %i params\n",rv); if(rv == 6 || rv == 7) { st->f_bsize = POPi; st->f_bfree = POPi; st->f_blocks = POPi; st->f_ffree = POPi; st->f_files = POPi; st->f_namemax = POPi; /* zero and fill-in other */ st->f_fsid = 0; st->f_frsize = 4096; st->f_flag = 0; st->f_bavail = st->f_bfree; st->f_favail = st->f_ffree; if(rv == 7) rv = POPi; else rv = 0; } else if(rv > 1) croak("inappropriate number of returned values from statfs"); else if(rv) rv = POPi; else rv = -ENOSYS; FREETMPS; LEAVE; PUTBACK; DEBUGf("statfs end: %i\n",rv); FUSE_CONTEXT_POST; return rv; } int _PLfuse_flush (const char *file, struct fuse_file_info *fi) { int rv; FUSE_CONTEXT_PRE; DEBUGf("flush begin\n"); ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(sv_2mortal(newSVpv(file,0))); PUTBACK; rv = call_sv(_PLfuse_callbacks[18],G_SCALAR); SPAGAIN; if(rv) rv = POPi; else rv = 0; FREETMPS; LEAVE; PUTBACK; DEBUGf("flush end: %i\n",rv); FUSE_CONTEXT_POST; return rv; } int _PLfuse_release (const char *file, struct fuse_file_info *fi) { int rv; int flags = fi->flags; FUSE_CONTEXT_PRE; DEBUGf("release begin\n"); ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(sv_2mortal(newSVpv(file,0))); XPUSHs(sv_2mortal(newSViv(flags))); PUTBACK; rv = call_sv(_PLfuse_callbacks[19],G_SCALAR); SPAGAIN; if(rv) rv = POPi; else rv = 0; FREETMPS; LEAVE; PUTBACK; DEBUGf("release end: %i\n",rv); FUSE_CONTEXT_POST; return rv; } int _PLfuse_fsync (const char *file, int datasync, struct fuse_file_info *fi) { int rv; int flags = fi->flags; FUSE_CONTEXT_PRE; DEBUGf("fsync begin\n"); ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(sv_2mortal(newSVpv(file,0))); XPUSHs(sv_2mortal(newSViv(flags))); PUTBACK; rv = call_sv(_PLfuse_callbacks[20],G_SCALAR); SPAGAIN; if(rv) rv = POPi; else rv = 0; FREETMPS; LEAVE; PUTBACK; DEBUGf("fsync end: %i\n",rv); FUSE_CONTEXT_POST; return rv; } int _PLfuse_setxattr (const char *file, const char *name, const char *buf, size_t buflen, int flags) { int rv; FUSE_CONTEXT_PRE; DEBUGf("setxattr begin\n"); ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(sv_2mortal(newSVpv(file,0))); XPUSHs(sv_2mortal(newSVpv(name,0))); XPUSHs(sv_2mortal(newSVpvn(buf,buflen))); XPUSHs(sv_2mortal(newSViv(flags))); PUTBACK; rv = call_sv(_PLfuse_callbacks[21],G_SCALAR); SPAGAIN; if(rv) rv = POPi; else rv = 0; FREETMPS; LEAVE; PUTBACK; DEBUGf("setxattr end: %i\n",rv); FUSE_CONTEXT_POST; return rv; } int _PLfuse_getxattr (const char *file, const char *name, char *buf, size_t buflen) { int rv; FUSE_CONTEXT_PRE; DEBUGf("getxattr begin\n"); ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(sv_2mortal(newSVpv(file,0))); XPUSHs(sv_2mortal(newSVpv(name,0))); PUTBACK; rv = call_sv(_PLfuse_callbacks[22],G_SCALAR); SPAGAIN; if(!rv) rv = -ENOENT; else { SV *mysv = POPs; rv = 0; if(SvTYPE(mysv) == SVt_NV || SvTYPE(mysv) == SVt_IV) rv = SvIV(mysv); else { if(SvPOK(mysv)) { rv = SvCUR(mysv); } else { rv = 0; } if ((rv > 0) && (buflen > 0)) { if(rv > buflen) rv = -ERANGE; else memcpy(buf,SvPV_nolen(mysv),rv); } } } FREETMPS; LEAVE; PUTBACK; DEBUGf("getxattr end: %i\n",rv); FUSE_CONTEXT_POST; return rv; } int _PLfuse_listxattr (const char *file, char *list, size_t size) { int prv, rv; FUSE_CONTEXT_PRE; DEBUGf("listxattr begin\n"); ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(sv_2mortal(newSVpv(file,0))); PUTBACK; prv = call_sv(_PLfuse_callbacks[23],G_ARRAY); SPAGAIN; if(!prv) rv = -ENOENT; else { char *p = list; int spc = size; int total_len = 0; rv = POPi; prv--; /* Always nul terminate */ if (list && (size > 0)) list[0] = '\0'; while (prv > 0) { SV *mysv = POPs; prv--; if (SvPOK(mysv)) { /* Copy nul too */ int s = SvCUR(mysv) + 1; total_len += s; if (p && (size > 0) && (spc >= s)) { memcpy(p,SvPV_nolen(mysv),s); p += s; spc -= s; } } } /* * If the Perl returned an error, return that. * Otherwise check that the buffer was big enough. */ if (rv == 0) { rv = total_len; if ((size > 0) && (size < total_len)) rv = -ERANGE; } } FREETMPS; LEAVE; PUTBACK; DEBUGf("listxattr end: %i\n",rv); FUSE_CONTEXT_POST; return rv; } int _PLfuse_removexattr (const char *file, const char *name) { int rv; FUSE_CONTEXT_PRE; DEBUGf("removexattr begin\n"); ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(sv_2mortal(newSVpv(file,0))); XPUSHs(sv_2mortal(newSVpv(name,0))); PUTBACK; rv = call_sv(_PLfuse_callbacks[24],G_SCALAR); SPAGAIN; if(rv) rv = POPi; else rv = 0; FREETMPS; LEAVE; PUTBACK; DEBUGf("removexattr end: %i\n",rv); FUSE_CONTEXT_POST; return rv; } 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, rmdir: _PLfuse_rmdir, symlink: _PLfuse_symlink, rename: _PLfuse_rename, link: _PLfuse_link, chmod: _PLfuse_chmod, chown: _PLfuse_chown, truncate: _PLfuse_truncate, utime: _PLfuse_utime, open: _PLfuse_open, read: _PLfuse_read, write: _PLfuse_write, statfs: _PLfuse_statfs, flush: _PLfuse_flush, release: _PLfuse_release, fsync: _PLfuse_fsync, setxattr: _PLfuse_setxattr, getxattr: _PLfuse_getxattr, listxattr: _PLfuse_listxattr, removexattr: _PLfuse_removexattr, }; MODULE = Fuse PACKAGE = Fuse PROTOTYPES: DISABLE 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,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) { fprintf(stderr,"Perl<->C inconsistency or internal error\n"); XSRETURN_UNDEF; } CODE: debug = SvIV(ST(0)); threaded = SvIV(ST(1)); if(threaded) { #ifdef FUSE_USE_ITHREADS master_interp = PERL_GET_INTERP; #else fprintf(stderr,"FUSE warning: Your script has requested multithreaded " "mode, but your perl was not built with -Dusethreads. " "Threads are disabled.\n"); threaded = 0; #endif } mountpoint = SvPV_nolen(ST(2)); mountopts = SvPV_nolen(ST(3)); for(i=0;i