1 #define PERL_NO_GET_CONTEXT
8 /* Determine if threads support should be included */
11 # define FUSE_USE_ITHREADS
13 # warning "Sorry, I don't know how to handle ithreads on this architecture. Building non-threaded version"
19 #define MY_CXT_KEY "Fuse::_guts" XS_VERSION
20 #define N_CALLBACKS 25
23 SV *callback[N_CALLBACKS];
31 #ifdef FUSE_USE_ITHREADS
32 tTHX master_interp = NULL;
33 # define FUSE_CONTEXT_PRE dTHX; if(!aTHX) { \
34 dMY_CXT_INTERP(master_interp); \
35 if(MY_CXT.threaded) { \
36 MUTEX_LOCK(&MY_CXT.mutex); \
37 PERL_SET_CONTEXT(master_interp); \
38 my_perl = perl_clone(MY_CXT.self, CLONEf_CLONE_HOST); \
39 MUTEX_UNLOCK(&MY_CXT.mutex); \
42 # define FUSE_CONTEXT_POST }
44 # define FUSE_CONTEXT_PRE dTHX; dMY_CXT; dSP;
45 # define FUSE_CONTEXT_POST
50 #define DEBUGf(f, a...) fprintf(stderr, "%s:%d (%i): " f,__BASE_FILE__,__LINE__,sp-PL_stack_base ,##a )
55 #define FH_KEY(fi) sv_2mortal(newSViv((fi)->fh))
56 #define FH_GETHANDLE(fi) S_fh_get_handle(aTHX_ aMY_CXT_ fi)
57 #define FH_STOREHANDLE(fi,sv) S_fh_store_handle(aTHX_ aMY_CXT_ fi, sv)
58 #define FH_RELEASEHANDLE(fi) S_fh_release_handle(aTHX_ aMY_CXT_ fi)
60 SV *S_fh_get_handle(pTHX_ pMY_CXT_ struct fuse_file_info *fi) {
65 if((he = hv_fetch_ent(MY_CXT.handles, FH_KEY(fi), 0, 0))) {
73 void S_fh_release_handle(pTHX_ pMY_CXT_ struct fuse_file_info *fi) {
75 (void)hv_delete_ent(MY_CXT.handles, FH_KEY(fi), 0, G_DISCARD);
80 void S_fh_store_handle(pTHX_ pMY_CXT_ struct fuse_file_info *fi, SV *sv) {
82 #ifdef FUSE_USE_ITHREADS
87 MAGIC *mg = mg_find(sv, PERL_MAGIC_shared_scalar);
88 fi->fh = mg ? PTR2IV(mg->mg_ptr) : PTR2IV(sv);
89 (void)hv_store_ent(MY_CXT.handles, FH_KEY(fi), sv, 0);
94 int _PLfuse_getattr(const char *file, struct stat *result) {
97 DEBUGf("getattr begin: %s\n",file);
101 XPUSHs(sv_2mortal(newSVpv(file,strlen(file))));
103 rv = call_sv(MY_CXT.callback[0],G_ARRAY);
107 fprintf(stderr,"inappropriate number of returned values from getattr\n");
114 result->st_blocks = POPi;
115 result->st_blksize = POPi;
116 result->st_ctime = POPi;
117 result->st_mtime = POPi;
118 result->st_atime = POPi;
119 result->st_size = POPn; // we pop double here to support files larger than 4Gb (long limit)
120 result->st_rdev = POPi;
121 result->st_gid = POPi;
122 result->st_uid = POPi;
123 result->st_nlink = POPi;
124 result->st_mode = POPi;
125 result->st_ino = POPi;
126 result->st_dev = POPi;
132 DEBUGf("getattr end: %i\n",rv);
137 int _PLfuse_readlink(const char *file,char *buf,size_t buflen) {
142 DEBUGf("readlink begin\n");
146 XPUSHs(sv_2mortal(newSVpv(file,0)));
148 rv = call_sv(MY_CXT.callback[1],G_SCALAR);
154 if(SvTYPE(mysv) == SVt_IV || SvTYPE(mysv) == SVt_NV)
157 strncpy(buf,SvPV_nolen(mysv),buflen);
165 DEBUGf("readlink end: %i\n",rv);
172 * This doesn't yet work... we alwas get ENOSYS when trying to use readdir().
173 * Well, of course, getdir() is fine as well.
175 int _PLfuse_readdir(const char *file, void *dirh, fuse_fill_dir_t dirfil, off_t off, struct fuse_file_info *fi) {
177 int _PLfuse_getdir(const char *file, fuse_dirh_t dirh, fuse_dirfil_t dirfil) {
180 DEBUGf("getdir begin\n");
184 XPUSHs(sv_2mortal(newSVpv(file,0)));
186 prv = call_sv(MY_CXT.callback[2],G_ARRAY);
191 dirfil(dirh,POPp,0,0);
193 fprintf(stderr,"getdir() handler returned nothing!\n");
199 DEBUGf("getdir end: %i\n",rv);
204 int _PLfuse_mknod (const char *file, mode_t mode, dev_t dev) {
207 DEBUGf("mknod begin\n");
211 XPUSHs(sv_2mortal(newSVpv(file,0)));
212 XPUSHs(sv_2mortal(newSViv(mode)));
213 XPUSHs(sv_2mortal(newSViv(dev)));
215 rv = call_sv(MY_CXT.callback[3],G_SCALAR);
224 DEBUGf("mknod end: %i\n",rv);
229 int _PLfuse_mkdir (const char *file, mode_t mode) {
232 DEBUGf("mkdir begin\n");
236 XPUSHs(sv_2mortal(newSVpv(file,0)));
237 XPUSHs(sv_2mortal(newSViv(mode)));
239 rv = call_sv(MY_CXT.callback[4],G_SCALAR);
248 DEBUGf("mkdir end: %i\n",rv);
254 int _PLfuse_unlink (const char *file) {
257 DEBUGf("unlink begin\n");
261 XPUSHs(sv_2mortal(newSVpv(file,0)));
263 rv = call_sv(MY_CXT.callback[5],G_SCALAR);
272 DEBUGf("unlink end: %i\n",rv);
277 int _PLfuse_rmdir (const char *file) {
280 DEBUGf("rmdir begin\n");
284 XPUSHs(sv_2mortal(newSVpv(file,0)));
286 rv = call_sv(MY_CXT.callback[6],G_SCALAR);
295 DEBUGf("rmdir end: %i\n",rv);
300 int _PLfuse_symlink (const char *file, const char *new) {
303 DEBUGf("symlink begin\n");
307 XPUSHs(sv_2mortal(newSVpv(file,0)));
308 XPUSHs(sv_2mortal(newSVpv(new,0)));
310 rv = call_sv(MY_CXT.callback[7],G_SCALAR);
319 DEBUGf("symlink end: %i\n",rv);
324 int _PLfuse_rename (const char *file, const char *new) {
327 DEBUGf("rename begin\n");
331 XPUSHs(sv_2mortal(newSVpv(file,0)));
332 XPUSHs(sv_2mortal(newSVpv(new,0)));
334 rv = call_sv(MY_CXT.callback[8],G_SCALAR);
343 DEBUGf("rename end: %i\n",rv);
348 int _PLfuse_link (const char *file, const char *new) {
351 DEBUGf("link begin\n");
355 XPUSHs(sv_2mortal(newSVpv(file,0)));
356 XPUSHs(sv_2mortal(newSVpv(new,0)));
358 rv = call_sv(MY_CXT.callback[9],G_SCALAR);
367 DEBUGf("link end: %i\n",rv);
372 int _PLfuse_chmod (const char *file, mode_t mode) {
375 DEBUGf("chmod begin\n");
379 XPUSHs(sv_2mortal(newSVpv(file,0)));
380 XPUSHs(sv_2mortal(newSViv(mode)));
382 rv = call_sv(MY_CXT.callback[10],G_SCALAR);
391 DEBUGf("chmod end: %i\n",rv);
396 int _PLfuse_chown (const char *file, uid_t uid, gid_t gid) {
399 DEBUGf("chown begin\n");
403 XPUSHs(sv_2mortal(newSVpv(file,0)));
404 XPUSHs(sv_2mortal(newSViv(uid)));
405 XPUSHs(sv_2mortal(newSViv(gid)));
407 rv = call_sv(MY_CXT.callback[11],G_SCALAR);
416 DEBUGf("chown end: %i\n",rv);
421 int _PLfuse_truncate (const char *file, off_t off) {
423 #ifndef PERL_HAS_64BITINT
427 DEBUGf("truncate begin\n");
431 XPUSHs(sv_2mortal(newSVpv(file,0)));
432 #ifdef PERL_HAS_64BITINT
433 XPUSHs(sv_2mortal(newSViv(off)));
435 asprintf(&temp, "%llu", off);
436 XPUSHs(sv_2mortal(newSVpv(temp, 0)));
440 rv = call_sv(MY_CXT.callback[12],G_SCALAR);
449 DEBUGf("truncate end: %i\n",rv);
454 int _PLfuse_utime (const char *file, struct utimbuf *uti) {
457 DEBUGf("utime begin\n");
461 XPUSHs(sv_2mortal(newSVpv(file,0)));
462 XPUSHs(sv_2mortal(newSViv(uti->actime)));
463 XPUSHs(sv_2mortal(newSViv(uti->modtime)));
465 rv = call_sv(MY_CXT.callback[13],G_SCALAR);
474 DEBUGf("utime end: %i\n",rv);
479 int _PLfuse_open (const char *file, struct fuse_file_info *fi) {
481 int flags = fi->flags;
484 DEBUGf("open begin\n");
488 XPUSHs(sv_2mortal(newSVpv(file,0)));
489 XPUSHs(sv_2mortal(newSViv(flags)));
490 /* Create a hashref containing the details from fi
491 * which we can look at or modify.
493 fi->fh = 0; /* Ensure it starts with 0 - important if they don't set it */
495 #if FUSE_VERSION >= 24
496 hv_store(fihash, "direct_io", 9, newSViv(fi->direct_io), 0);
497 hv_store(fihash, "keep_cache", 10, newSViv(fi->keep_cache), 0);
499 #if FUSE_VERSION >= 29
500 hv_store(fihash, "nonseekable", 11, newSViv(fi->nonseekable), 0);
502 XPUSHs(sv_2mortal(newRV_noinc((SV*) fihash)));
503 /* All hashref things done */
506 /* Open called with filename, flags */
507 rv = call_sv(MY_CXT.callback[14],G_ARRAY);
511 FH_STOREHANDLE(fi,POPs);
519 /* Success, so copy the file handle which they returned */
520 #if FUSE_VERSION >= 24
522 svp = hv_fetch(fihash, "direct_io", 9, 0);
525 fi->direct_io = SvIV(*svp);
527 svp = hv_fetch(fihash, "keep_cache", 10, 0);
530 fi->keep_cache = SvIV(*svp);
533 #if FUSE_VERSION >= 29
534 svp = hv_fetch(fihash, "nonseekable", 11, 0);
537 fi->nonseekable = SvIV(*svp);
544 DEBUGf("open end: %i\n",rv);
549 int _PLfuse_read (const char *file, char *buf, size_t buflen, off_t off, struct fuse_file_info *fi) {
551 #ifndef PERL_HAS_64BITINT
555 DEBUGf("read begin\n");
559 XPUSHs(sv_2mortal(newSVpv(file,0)));
560 XPUSHs(sv_2mortal(newSViv(buflen)));
561 #ifdef PERL_HAS_64BITINT
562 XPUSHs(sv_2mortal(newSViv(off)));
564 asprintf(&temp, "%llu", off);
565 XPUSHs(sv_2mortal(newSVpv(temp, 0)));
568 XPUSHs(FH_GETHANDLE(fi));
570 rv = call_sv(MY_CXT.callback[15],G_SCALAR);
576 if(SvTYPE(mysv) == SVt_NV || SvTYPE(mysv) == SVt_IV)
585 croak("read() handler returned more than buflen! (%i > %i)",rv,buflen);
587 memcpy(buf,SvPV_nolen(mysv),rv);
593 DEBUGf("read end: %i\n",rv);
598 int _PLfuse_write (const char *file, const char *buf, size_t buflen, off_t off, struct fuse_file_info *fi) {
600 #ifndef PERL_HAS_64BITINT
604 DEBUGf("write begin\n");
608 XPUSHs(sv_2mortal(newSVpv(file,0)));
609 XPUSHs(sv_2mortal(newSVpvn(buf,buflen)));
610 #ifdef PERL_HAS_64BITINT
611 XPUSHs(sv_2mortal(newSViv(off)));
613 asprintf(&temp, "%llu", off);
614 XPUSHs(sv_2mortal(newSVpv(temp, 0)));
617 XPUSHs(FH_GETHANDLE(fi));
619 rv = call_sv(MY_CXT.callback[16],G_SCALAR);
628 DEBUGf("write end: %i\n",rv);
633 int _PLfuse_statfs (const char *file, struct statvfs *st) {
636 DEBUGf("statfs begin\n");
641 rv = call_sv(MY_CXT.callback[17],G_ARRAY);
643 DEBUGf("statfs got %i params\n",rv);
644 if(rv == 6 || rv == 7) {
650 st->f_namemax = POPi;
651 /* zero and fill-in other */
655 st->f_bavail = st->f_bfree;
656 st->f_favail = st->f_ffree;
664 croak("inappropriate number of returned values from statfs");
673 DEBUGf("statfs end: %i\n",rv);
678 int _PLfuse_flush (const char *file, struct fuse_file_info *fi) {
681 DEBUGf("flush begin\n");
685 XPUSHs(sv_2mortal(newSVpv(file,0)));
686 XPUSHs(FH_GETHANDLE(fi));
688 rv = call_sv(MY_CXT.callback[18],G_SCALAR);
697 DEBUGf("flush end: %i\n",rv);
702 int _PLfuse_release (const char *file, struct fuse_file_info *fi) {
704 int flags = fi->flags;
706 DEBUGf("release begin\n");
710 XPUSHs(sv_2mortal(newSVpv(file,0)));
711 XPUSHs(sv_2mortal(newSViv(flags)));
712 XPUSHs(FH_GETHANDLE(fi));
714 rv = call_sv(MY_CXT.callback[19],G_SCALAR);
720 FH_RELEASEHANDLE(fi);
724 DEBUGf("release end: %i\n",rv);
729 int _PLfuse_fsync (const char *file, int datasync, struct fuse_file_info *fi) {
731 int flags = fi->flags;
733 DEBUGf("fsync begin\n");
737 XPUSHs(sv_2mortal(newSVpv(file,0)));
738 XPUSHs(sv_2mortal(newSViv(flags)));
739 XPUSHs(FH_GETHANDLE(fi));
741 rv = call_sv(MY_CXT.callback[20],G_SCALAR);
750 DEBUGf("fsync end: %i\n",rv);
755 int _PLfuse_setxattr (const char *file, const char *name, const char *buf, size_t buflen, int flags) {
758 DEBUGf("setxattr begin\n");
762 XPUSHs(sv_2mortal(newSVpv(file,0)));
763 XPUSHs(sv_2mortal(newSVpv(name,0)));
764 XPUSHs(sv_2mortal(newSVpvn(buf,buflen)));
765 XPUSHs(sv_2mortal(newSViv(flags)));
767 rv = call_sv(MY_CXT.callback[21],G_SCALAR);
776 DEBUGf("setxattr end: %i\n",rv);
781 int _PLfuse_getxattr (const char *file, const char *name, char *buf, size_t buflen) {
784 DEBUGf("getxattr begin\n");
788 XPUSHs(sv_2mortal(newSVpv(file,0)));
789 XPUSHs(sv_2mortal(newSVpv(name,0)));
791 rv = call_sv(MY_CXT.callback[22],G_SCALAR);
799 if(SvTYPE(mysv) == SVt_NV || SvTYPE(mysv) == SVt_IV)
807 if ((rv > 0) && (buflen > 0))
812 memcpy(buf,SvPV_nolen(mysv),rv);
819 DEBUGf("getxattr end: %i\n",rv);
824 int _PLfuse_listxattr (const char *file, char *list, size_t size) {
827 DEBUGf("listxattr begin\n");
831 XPUSHs(sv_2mortal(newSVpv(file,0)));
833 prv = call_sv(MY_CXT.callback[23],G_ARRAY);
846 /* Always nul terminate */
847 if (list && (size > 0))
857 int s = SvCUR(mysv) + 1;
860 if (p && (size > 0) && (spc >= s))
862 memcpy(p,SvPV_nolen(mysv),s);
870 * If the Perl returned an error, return that.
871 * Otherwise check that the buffer was big enough.
876 if ((size > 0) && (size < total_len))
883 DEBUGf("listxattr end: %i\n",rv);
888 int _PLfuse_removexattr (const char *file, const char *name) {
891 DEBUGf("removexattr begin\n");
895 XPUSHs(sv_2mortal(newSVpv(file,0)));
896 XPUSHs(sv_2mortal(newSVpv(name,0)));
898 rv = call_sv(MY_CXT.callback[24],G_SCALAR);
907 DEBUGf("removexattr end: %i\n",rv);
912 struct fuse_operations _available_ops = {
913 getattr: _PLfuse_getattr,
914 readlink: _PLfuse_readlink,
915 getdir: _PLfuse_getdir,
917 readdir: _PLfuse_readdir,
919 mknod: _PLfuse_mknod,
920 mkdir: _PLfuse_mkdir,
921 unlink: _PLfuse_unlink,
922 rmdir: _PLfuse_rmdir,
923 symlink: _PLfuse_symlink,
924 rename: _PLfuse_rename,
926 chmod: _PLfuse_chmod,
927 chown: _PLfuse_chown,
928 truncate: _PLfuse_truncate,
929 utime: _PLfuse_utime,
932 write: _PLfuse_write,
933 statfs: _PLfuse_statfs,
934 flush: _PLfuse_flush,
935 release: _PLfuse_release,
936 fsync: _PLfuse_fsync,
937 setxattr: _PLfuse_setxattr,
938 getxattr: _PLfuse_getxattr,
939 listxattr: _PLfuse_listxattr,
940 removexattr: _PLfuse_removexattr,
943 MODULE = Fuse PACKAGE = Fuse
957 tTHX parent = MY_CXT.self;
958 MY_CXT.self = my_perl;
959 #if (PERL_VERSION < 13) || (PERL_VERSION == 13 && PERL_SUBVERSION <= 1)
960 CLONE_PARAMS clone_param;
961 clone_param.flags = 0;
962 for(i=0;i<N_CALLBACKS;i++) {
963 if(MY_CXT.callback[i]) {
964 MY_CXT.callback[i] = sv_dup(MY_CXT.callback[i], &clone_param);
968 CLONE_PARAMS *clone_param = clone_params_new(parent, aTHX);
969 for(i=0;i<N_CALLBACKS;i++) {
970 if(MY_CXT.callback[i]) {
971 MY_CXT.callback[i] = sv_dup(MY_CXT.callback[i], clone_param);
974 clone_params_del(clone_param);
980 struct fuse_context *fc;
982 fc = fuse_get_context();
985 hv_store(hash, "uid", 3, newSViv(fc->uid), 0);
986 hv_store(hash, "gid", 3, newSViv(fc->gid), 0);
987 hv_store(hash, "pid", 3, newSViv(fc->pid), 0);
988 RETVAL = newRV_noinc((SV*)hash);
998 struct fuse_operations fops =
999 {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
1000 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
1004 struct fuse_args margs = FUSE_ARGS_INIT(0, NULL);
1005 struct fuse_args fargs = FUSE_ARGS_INIT(0, NULL);
1006 struct fuse_chan *fc;
1010 fprintf(stderr,"Perl<->C inconsistency or internal error\n");
1014 debug = SvIV(ST(0));
1015 MY_CXT.threaded = SvIV(ST(1));
1016 MY_CXT.handles = MUTABLE_HV(sv_2mortal(MUTABLE_SV(newHV())));
1017 if(MY_CXT.threaded) {
1018 #ifdef FUSE_USE_ITHREADS
1019 master_interp = aTHX;
1020 MUTEX_INIT(&MY_CXT.mutex);
1021 SvSHARE(MUTABLE_SV(MY_CXT.handles));
1023 fprintf(stderr,"FUSE warning: Your script has requested multithreaded "
1024 "mode, but your perl was not built with a supported "
1025 "thread model. Threads are disabled.\n");
1026 MY_CXT.threaded = 0;
1029 mountpoint = SvPV_nolen(ST(2));
1030 mountopts = SvPV_nolen(ST(3));
1031 for(i=0;i<N_CALLBACKS;i++) {
1033 /* allow symbolic references, or real code references. */
1034 if(SvOK(var) && (SvPOK(var) || (SvROK(var) && SvTYPE(SvRV(var)) == SVt_PVCV))) {
1035 void **tmp1 = (void**)&_available_ops, **tmp2 = (void**)&fops;
1037 MY_CXT.callback[i] = var;
1038 } else if(SvOK(var)) {
1039 croak("invalid callback (%i) passed to perl_fuse_main "
1040 "(%s is not a string, code ref, or undef).\n",
1041 i+4,SvPVbyte_nolen(var));
1043 MY_CXT.callback[i] = NULL;
1047 * XXX: What comes here is just a ridiculous use of the option parsing API
1048 * to hack on compatibility with other parts of the new API. First and
1049 * foremost, real C argc/argv would be good to get at...
1052 (fuse_opt_add_arg(&margs, "") == -1 ||
1053 fuse_opt_add_arg(&margs, "-o") == -1 ||
1054 fuse_opt_add_arg(&margs, mountopts) == -1)) {
1055 fuse_opt_free_args(&margs);
1056 croak("out of memory\n");
1058 fc = fuse_mount(mountpoint,&margs);
1059 fuse_opt_free_args(&margs);
1061 croak("could not mount fuse filesystem!\n");
1063 if ( fuse_opt_add_arg(&fargs, "") == -1 ||
1064 fuse_opt_add_arg(&fargs, "-d") == -1) {
1065 fuse_opt_free_args(&fargs);
1066 croak("out of memory\n");
1069 if (fuse_opt_add_arg(&fargs, "") == -1)
1070 croak("out of memory\n");
1073 if(MY_CXT.threaded) {
1074 fuse_loop_mt(fuse_new(fc,&fargs,&fops,sizeof(fops),NULL));
1077 fuse_loop(fuse_new(fc,&fargs,&fops,sizeof(fops),NULL));
1078 fuse_unmount(mountpoint,fc);
1079 fuse_opt_free_args(&fargs);