1 #define PERL_NO_GET_CONTEXT
8 /* Determine if threads support should be included */
11 # define FUSE_USE_ITHREADS
13 # error "Sorry, I don't know how to handle ithreads on this architecture."
19 #define MY_CXT_KEY "Fuse::_guts" XS_VERSION
20 #define N_CALLBACKS 25
23 SV *callback[N_CALLBACKS];
30 #ifdef FUSE_USE_ITHREADS
31 tTHX master_interp = NULL;
32 # define FUSE_CONTEXT_PRE dTHX; if(!aTHX) { \
33 dMY_CXT_INTERP(master_interp); \
34 if(MY_CXT.threaded) { \
35 MUTEX_LOCK(&MY_CXT.mutex); \
36 PERL_SET_CONTEXT(master_interp); \
37 my_perl = perl_clone(MY_CXT.self, CLONEf_CLONE_HOST); \
38 MUTEX_UNLOCK(&MY_CXT.mutex); \
41 # define FUSE_CONTEXT_POST }
43 # define FUSE_CONTEXT_PRE dTHX; dMY_CXT; dSP;
44 # define FUSE_CONTEXT_POST
49 #define DEBUGf(f, a...) fprintf(stderr, "%s:%d (%i): " f,__BASE_FILE__,__LINE__,sp-PL_stack_base ,##a )
54 int _PLfuse_getattr(const char *file, struct stat *result) {
57 DEBUGf("getattr begin: %s\n",file);
61 XPUSHs(sv_2mortal(newSVpv(file,strlen(file))));
63 rv = call_sv(MY_CXT.callback[0],G_ARRAY);
67 fprintf(stderr,"inappropriate number of returned values from getattr\n");
74 result->st_blocks = POPi;
75 result->st_blksize = POPi;
76 result->st_ctime = POPi;
77 result->st_mtime = POPi;
78 result->st_atime = POPi;
79 result->st_size = POPn; // we pop double here to support files larger than 4Gb (long limit)
80 result->st_rdev = POPi;
81 result->st_gid = POPi;
82 result->st_uid = POPi;
83 result->st_nlink = POPi;
84 result->st_mode = POPi;
85 result->st_ino = POPi;
86 result->st_dev = POPi;
92 DEBUGf("getattr end: %i\n",rv);
97 int _PLfuse_readlink(const char *file,char *buf,size_t buflen) {
102 DEBUGf("readlink begin\n");
106 XPUSHs(sv_2mortal(newSVpv(file,0)));
108 rv = call_sv(MY_CXT.callback[1],G_SCALAR);
114 if(SvTYPE(mysv) == SVt_IV || SvTYPE(mysv) == SVt_NV)
117 strncpy(buf,SvPV_nolen(mysv),buflen);
125 DEBUGf("readlink end: %i\n",rv);
132 * This doesn't yet work... we alwas get ENOSYS when trying to use readdir().
133 * Well, of course, getdir() is fine as well.
135 int _PLfuse_readdir(const char *file, void *dirh, fuse_fill_dir_t dirfil, off_t off, struct fuse_file_info *fi) {
137 int _PLfuse_getdir(const char *file, fuse_dirh_t dirh, fuse_dirfil_t dirfil) {
140 DEBUGf("getdir begin\n");
144 XPUSHs(sv_2mortal(newSVpv(file,0)));
146 prv = call_sv(MY_CXT.callback[2],G_ARRAY);
151 dirfil(dirh,POPp,0,0);
153 fprintf(stderr,"getdir() handler returned nothing!\n");
159 DEBUGf("getdir end: %i\n",rv);
164 int _PLfuse_mknod (const char *file, mode_t mode, dev_t dev) {
167 DEBUGf("mknod begin\n");
171 XPUSHs(sv_2mortal(newSVpv(file,0)));
172 XPUSHs(sv_2mortal(newSViv(mode)));
173 XPUSHs(sv_2mortal(newSViv(dev)));
175 rv = call_sv(MY_CXT.callback[3],G_SCALAR);
184 DEBUGf("mknod end: %i\n",rv);
189 int _PLfuse_mkdir (const char *file, mode_t mode) {
192 DEBUGf("mkdir begin\n");
196 XPUSHs(sv_2mortal(newSVpv(file,0)));
197 XPUSHs(sv_2mortal(newSViv(mode)));
199 rv = call_sv(MY_CXT.callback[4],G_SCALAR);
208 DEBUGf("mkdir end: %i\n",rv);
214 int _PLfuse_unlink (const char *file) {
217 DEBUGf("unlink begin\n");
221 XPUSHs(sv_2mortal(newSVpv(file,0)));
223 rv = call_sv(MY_CXT.callback[5],G_SCALAR);
232 DEBUGf("unlink end: %i\n",rv);
237 int _PLfuse_rmdir (const char *file) {
240 DEBUGf("rmdir begin\n");
244 XPUSHs(sv_2mortal(newSVpv(file,0)));
246 rv = call_sv(MY_CXT.callback[6],G_SCALAR);
255 DEBUGf("rmdir end: %i\n",rv);
260 int _PLfuse_symlink (const char *file, const char *new) {
263 DEBUGf("symlink begin\n");
267 XPUSHs(sv_2mortal(newSVpv(file,0)));
268 XPUSHs(sv_2mortal(newSVpv(new,0)));
270 rv = call_sv(MY_CXT.callback[7],G_SCALAR);
279 DEBUGf("symlink end: %i\n",rv);
284 int _PLfuse_rename (const char *file, const char *new) {
287 DEBUGf("rename begin\n");
291 XPUSHs(sv_2mortal(newSVpv(file,0)));
292 XPUSHs(sv_2mortal(newSVpv(new,0)));
294 rv = call_sv(MY_CXT.callback[8],G_SCALAR);
303 DEBUGf("rename end: %i\n",rv);
308 int _PLfuse_link (const char *file, const char *new) {
311 DEBUGf("link begin\n");
315 XPUSHs(sv_2mortal(newSVpv(file,0)));
316 XPUSHs(sv_2mortal(newSVpv(new,0)));
318 rv = call_sv(MY_CXT.callback[9],G_SCALAR);
327 DEBUGf("link end: %i\n",rv);
332 int _PLfuse_chmod (const char *file, mode_t mode) {
335 DEBUGf("chmod begin\n");
339 XPUSHs(sv_2mortal(newSVpv(file,0)));
340 XPUSHs(sv_2mortal(newSViv(mode)));
342 rv = call_sv(MY_CXT.callback[10],G_SCALAR);
351 DEBUGf("chmod end: %i\n",rv);
356 int _PLfuse_chown (const char *file, uid_t uid, gid_t gid) {
359 DEBUGf("chown begin\n");
363 XPUSHs(sv_2mortal(newSVpv(file,0)));
364 XPUSHs(sv_2mortal(newSViv(uid)));
365 XPUSHs(sv_2mortal(newSViv(gid)));
367 rv = call_sv(MY_CXT.callback[11],G_SCALAR);
376 DEBUGf("chown end: %i\n",rv);
381 int _PLfuse_truncate (const char *file, off_t off) {
383 #ifndef PERL_HAS_64BITINT
387 DEBUGf("truncate begin\n");
391 XPUSHs(sv_2mortal(newSVpv(file,0)));
392 #ifdef PERL_HAS_64BITINT
393 XPUSHs(sv_2mortal(newSViv(off)));
395 asprintf(&temp, "%llu", off);
396 XPUSHs(sv_2mortal(newSVpv(temp, 0)));
400 rv = call_sv(MY_CXT.callback[12],G_SCALAR);
409 DEBUGf("truncate end: %i\n",rv);
414 int _PLfuse_utime (const char *file, struct utimbuf *uti) {
417 DEBUGf("utime begin\n");
421 XPUSHs(sv_2mortal(newSVpv(file,0)));
422 XPUSHs(sv_2mortal(newSViv(uti->actime)));
423 XPUSHs(sv_2mortal(newSViv(uti->modtime)));
425 rv = call_sv(MY_CXT.callback[13],G_SCALAR);
434 DEBUGf("utime end: %i\n",rv);
439 int _PLfuse_open (const char *file, struct fuse_file_info *fi) {
441 int flags = fi->flags;
444 DEBUGf("open begin\n");
448 XPUSHs(sv_2mortal(newSVpv(file,0)));
449 XPUSHs(sv_2mortal(newSViv(flags)));
450 /* Create a hashref containing the details from fi
451 * which we can look at or modify.
453 fi->fh = 0; /* Ensure it starts with 0 - important if they don't set it */
455 #if FUSE_VERSION >= 24
456 hv_store(fihash, "direct_io", 9, newSViv(fi->direct_io), 0);
457 hv_store(fihash, "keep_cache", 10, newSViv(fi->keep_cache), 0);
459 #if FUSE_VERSION >= 29
460 hv_store(fihash, "nonseekable", 11, newSViv(fi->nonseekable), 0);
462 XPUSHs(sv_2mortal(newRV_noinc((SV*) fihash)));
463 /* All hashref things done */
466 /* Open called with filename, flags */
467 rv = call_sv(MY_CXT.callback[14],G_ARRAY);
477 /* We're holding on to the sv reference until
478 * after exit of this function, so we need to
479 * increment its reference count
481 fi->fh = SvREFCNT_inc(sv);
490 /* Success, so copy the file handle which they returned */
491 #if FUSE_VERSION >= 24
493 svp = hv_fetch(fihash, "direct_io", 9, 0);
496 fi->direct_io = SvIV(*svp);
498 svp = hv_fetch(fihash, "keep_cache", 10, 0);
501 fi->keep_cache = SvIV(*svp);
504 #if FUSE_VERSION >= 29
505 svp = hv_fetch(fihash, "nonseekable", 11, 0);
508 fi->nonseekable = SvIV(*svp);
515 DEBUGf("open end: %i\n",rv);
520 int _PLfuse_read (const char *file, char *buf, size_t buflen, off_t off, struct fuse_file_info *fi) {
522 #ifndef PERL_HAS_64BITINT
526 DEBUGf("read begin\n");
530 XPUSHs(sv_2mortal(newSVpv(file,0)));
531 XPUSHs(sv_2mortal(newSViv(buflen)));
532 #ifdef PERL_HAS_64BITINT
533 XPUSHs(sv_2mortal(newSViv(off)));
535 asprintf(&temp, "%llu", off);
536 XPUSHs(sv_2mortal(newSVpv(temp, 0)));
539 XPUSHs(fi->fh==0 ? &PL_sv_undef : (SV *)fi->fh);
541 rv = call_sv(MY_CXT.callback[15],G_SCALAR);
547 if(SvTYPE(mysv) == SVt_NV || SvTYPE(mysv) == SVt_IV)
556 croak("read() handler returned more than buflen! (%i > %i)",rv,buflen);
558 memcpy(buf,SvPV_nolen(mysv),rv);
564 DEBUGf("read end: %i\n",rv);
569 int _PLfuse_write (const char *file, const char *buf, size_t buflen, off_t off, struct fuse_file_info *fi) {
571 #ifndef PERL_HAS_64BITINT
575 DEBUGf("write begin\n");
579 XPUSHs(sv_2mortal(newSVpv(file,0)));
580 XPUSHs(sv_2mortal(newSVpvn(buf,buflen)));
581 #ifdef PERL_HAS_64BITINT
582 XPUSHs(sv_2mortal(newSViv(off)));
584 asprintf(&temp, "%llu", off);
585 XPUSHs(sv_2mortal(newSVpv(temp, 0)));
588 XPUSHs(fi->fh==0 ? &PL_sv_undef : (SV *)fi->fh);
590 rv = call_sv(MY_CXT.callback[16],G_SCALAR);
599 DEBUGf("write end: %i\n",rv);
604 int _PLfuse_statfs (const char *file, struct statvfs *st) {
607 DEBUGf("statfs begin\n");
612 rv = call_sv(MY_CXT.callback[17],G_ARRAY);
614 DEBUGf("statfs got %i params\n",rv);
615 if(rv == 6 || rv == 7) {
621 st->f_namemax = POPi;
622 /* zero and fill-in other */
626 st->f_bavail = st->f_bfree;
627 st->f_favail = st->f_ffree;
635 croak("inappropriate number of returned values from statfs");
644 DEBUGf("statfs end: %i\n",rv);
649 int _PLfuse_flush (const char *file, struct fuse_file_info *fi) {
652 DEBUGf("flush begin\n");
656 XPUSHs(sv_2mortal(newSVpv(file,0)));
657 XPUSHs(fi->fh==0 ? &PL_sv_undef : (SV *)fi->fh);
659 rv = call_sv(MY_CXT.callback[18],G_SCALAR);
668 DEBUGf("flush end: %i\n",rv);
673 int _PLfuse_release (const char *file, struct fuse_file_info *fi) {
675 int flags = fi->flags;
677 DEBUGf("release begin\n");
681 XPUSHs(sv_2mortal(newSVpv(file,0)));
682 XPUSHs(sv_2mortal(newSViv(flags)));
683 XPUSHs(fi->fh==0 ? &PL_sv_undef : (SV *)fi->fh);
685 rv = call_sv(MY_CXT.callback[19],G_SCALAR);
691 /* We're now finished with the handle that we were given, so
692 * we should decrement its count so that it can be freed.
696 SvREFCNT_dec((SV *)fi->fh);
702 DEBUGf("release end: %i\n",rv);
707 int _PLfuse_fsync (const char *file, int datasync, struct fuse_file_info *fi) {
709 int flags = fi->flags;
711 DEBUGf("fsync begin\n");
715 XPUSHs(sv_2mortal(newSVpv(file,0)));
716 XPUSHs(sv_2mortal(newSViv(flags)));
717 XPUSHs(fi->fh==0 ? &PL_sv_undef : (SV *)fi->fh);
719 rv = call_sv(MY_CXT.callback[20],G_SCALAR);
728 DEBUGf("fsync end: %i\n",rv);
733 int _PLfuse_setxattr (const char *file, const char *name, const char *buf, size_t buflen, int flags) {
736 DEBUGf("setxattr begin\n");
740 XPUSHs(sv_2mortal(newSVpv(file,0)));
741 XPUSHs(sv_2mortal(newSVpv(name,0)));
742 XPUSHs(sv_2mortal(newSVpvn(buf,buflen)));
743 XPUSHs(sv_2mortal(newSViv(flags)));
745 rv = call_sv(MY_CXT.callback[21],G_SCALAR);
754 DEBUGf("setxattr end: %i\n",rv);
759 int _PLfuse_getxattr (const char *file, const char *name, char *buf, size_t buflen) {
762 DEBUGf("getxattr begin\n");
766 XPUSHs(sv_2mortal(newSVpv(file,0)));
767 XPUSHs(sv_2mortal(newSVpv(name,0)));
769 rv = call_sv(MY_CXT.callback[22],G_SCALAR);
777 if(SvTYPE(mysv) == SVt_NV || SvTYPE(mysv) == SVt_IV)
785 if ((rv > 0) && (buflen > 0))
790 memcpy(buf,SvPV_nolen(mysv),rv);
797 DEBUGf("getxattr end: %i\n",rv);
802 int _PLfuse_listxattr (const char *file, char *list, size_t size) {
805 DEBUGf("listxattr begin\n");
809 XPUSHs(sv_2mortal(newSVpv(file,0)));
811 prv = call_sv(MY_CXT.callback[23],G_ARRAY);
824 /* Always nul terminate */
825 if (list && (size > 0))
835 int s = SvCUR(mysv) + 1;
838 if (p && (size > 0) && (spc >= s))
840 memcpy(p,SvPV_nolen(mysv),s);
848 * If the Perl returned an error, return that.
849 * Otherwise check that the buffer was big enough.
854 if ((size > 0) && (size < total_len))
861 DEBUGf("listxattr end: %i\n",rv);
866 int _PLfuse_removexattr (const char *file, const char *name) {
869 DEBUGf("removexattr begin\n");
873 XPUSHs(sv_2mortal(newSVpv(file,0)));
874 XPUSHs(sv_2mortal(newSVpv(name,0)));
876 rv = call_sv(MY_CXT.callback[24],G_SCALAR);
885 DEBUGf("removexattr end: %i\n",rv);
890 struct fuse_operations _available_ops = {
891 getattr: _PLfuse_getattr,
892 readlink: _PLfuse_readlink,
893 getdir: _PLfuse_getdir,
895 readdir: _PLfuse_readdir,
897 mknod: _PLfuse_mknod,
898 mkdir: _PLfuse_mkdir,
899 unlink: _PLfuse_unlink,
900 rmdir: _PLfuse_rmdir,
901 symlink: _PLfuse_symlink,
902 rename: _PLfuse_rename,
904 chmod: _PLfuse_chmod,
905 chown: _PLfuse_chown,
906 truncate: _PLfuse_truncate,
907 utime: _PLfuse_utime,
910 write: _PLfuse_write,
911 statfs: _PLfuse_statfs,
912 flush: _PLfuse_flush,
913 release: _PLfuse_release,
914 fsync: _PLfuse_fsync,
915 setxattr: _PLfuse_setxattr,
916 getxattr: _PLfuse_getxattr,
917 listxattr: _PLfuse_listxattr,
918 removexattr: _PLfuse_removexattr,
921 MODULE = Fuse PACKAGE = Fuse
935 tTHX parent = MY_CXT.self;
936 MY_CXT.self = my_perl;
937 #if (PERL_VERSION < 13) || (PERL_VERSION == 13 && PERL_SUBVERSION <= 1)
938 CLONE_PARAMS clone_param;
939 clone_param.flags = 0;
940 for(i=0;i<N_CALLBACKS;i++) {
941 if(MY_CXT.callback[i]) {
942 MY_CXT.callback[i] = sv_dup(MY_CXT.callback[i], &clone_param);
946 CLONE_PARAMS *clone_param = clone_params_new(parent, aTHX);
947 for(i=0;i<N_CALLBACKS;i++) {
948 if(MY_CXT.callback[i]) {
949 MY_CXT.callback[i] = sv_dup(MY_CXT.callback[i], clone_param);
952 clone_params_del(clone_param);
958 struct fuse_context *fc;
960 fc = fuse_get_context();
963 hv_store(hash, "uid", 3, newSViv(fc->uid), 0);
964 hv_store(hash, "gid", 3, newSViv(fc->gid), 0);
965 hv_store(hash, "pid", 3, newSViv(fc->pid), 0);
966 RETVAL = newRV_noinc((SV*)hash);
976 struct fuse_operations fops =
977 {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
978 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
982 struct fuse_args margs = FUSE_ARGS_INIT(0, NULL);
983 struct fuse_args fargs = FUSE_ARGS_INIT(0, NULL);
984 struct fuse_chan *fc;
988 fprintf(stderr,"Perl<->C inconsistency or internal error\n");
993 MY_CXT.threaded = SvIV(ST(1));
994 if(MY_CXT.threaded) {
995 #ifdef FUSE_USE_ITHREADS
996 master_interp = aTHX;
997 MUTEX_INIT(&MY_CXT.mutex);
999 fprintf(stderr,"FUSE warning: Your script has requested multithreaded "
1000 "mode, but your perl was not built with -Dusethreads. "
1001 "Threads are disabled.\n");
1002 MY_CXT.threaded = 0;
1005 mountpoint = SvPV_nolen(ST(2));
1006 mountopts = SvPV_nolen(ST(3));
1007 for(i=0;i<N_CALLBACKS;i++) {
1009 /* allow symbolic references, or real code references. */
1010 if(SvOK(var) && (SvPOK(var) || (SvROK(var) && SvTYPE(SvRV(var)) == SVt_PVCV))) {
1011 void **tmp1 = (void**)&_available_ops, **tmp2 = (void**)&fops;
1013 MY_CXT.callback[i] = var;
1014 } else if(SvOK(var)) {
1015 croak("invalid callback (%i) passed to perl_fuse_main "
1016 "(%s is not a string, code ref, or undef).\n",
1017 i+4,SvPVbyte_nolen(var));
1019 MY_CXT.callback[i] = NULL;
1023 * XXX: What comes here is just a ridiculous use of the option parsing API
1024 * to hack on compatibility with other parts of the new API. First and
1025 * foremost, real C argc/argv would be good to get at...
1028 (fuse_opt_add_arg(&margs, "") == -1 ||
1029 fuse_opt_add_arg(&margs, "-o") == -1 ||
1030 fuse_opt_add_arg(&margs, mountopts) == -1)) {
1031 fuse_opt_free_args(&margs);
1032 croak("out of memory\n");
1034 fc = fuse_mount(mountpoint,&margs);
1035 fuse_opt_free_args(&margs);
1037 croak("could not mount fuse filesystem!\n");
1039 if ( fuse_opt_add_arg(&fargs, "") == -1 ||
1040 fuse_opt_add_arg(&fargs, "-d") == -1) {
1041 fuse_opt_free_args(&fargs);
1042 croak("out of memory\n");
1045 if (fuse_opt_add_arg(&fargs, "") == -1)
1046 croak("out of memory\n");
1049 if(MY_CXT.threaded) {
1050 fuse_loop_mt(fuse_new(fc,&fargs,&fops,sizeof(fops),NULL));
1053 fuse_loop(fuse_new(fc,&fargs,&fops,sizeof(fops),NULL));
1054 fuse_unmount(mountpoint,fc);
1055 fuse_opt_free_args(&fargs);