01fcafbb3f460fa8ef9e95bc3c88628530cab8d3
[perl-fuse.git] / Fuse.xs
1 #define PERL_NO_GET_CONTEXT
2 #include "EXTERN.h"
3 #include "perl.h"
4 #include "XSUB.h"
5
6 /*
7  * XXX: Fuse on FreeBSD does not support extended attributes (see
8  * /usr/local/share/doc/fusefs/kmod/doc.text). Also, 'extattr_set_file' syscall
9  * declared in 'sys/extattr.h' does not accept any flags. We could either skip
10  * defining XATTR_CREATE and XATTR_REPLACE constants (as further below, and
11  * which breaks their usage if Perl module exports them), set them to some
12  * dummy values, or not allow them to be exported at all on FreeBSD.
13  */
14 #ifndef __FreeBSD__
15 # include <sys/xattr.h>
16 #endif
17 #include <fuse.h>
18
19 /* Determine if threads support should be included */
20 #ifdef USE_ITHREADS
21 # ifdef I_PTHREAD
22 #  define FUSE_USE_ITHREADS
23 # else
24 #  warning "Sorry, I don't know how to handle ithreads on this architecture. Building non-threaded version"
25 # endif
26 #endif
27
28 /* Global Data */
29
30 #define MY_CXT_KEY "Fuse::_guts" XS_VERSION
31 /* #if FUSE_VERSION >= 28
32 # define N_CALLBACKS 41 */
33 #if FUSE_VERSION >= 26
34 # define N_CALLBACKS 38
35 #elif FUSE_VERSION >= 25
36 # define N_CALLBACKS 35
37 #elif FUSE_VERSION >= 23
38 # define N_CALLBACKS 31
39 #else
40 # define N_CALLBACKS 25
41 #endif
42
43 typedef struct {
44         SV *callback[N_CALLBACKS];
45         HV *handles;
46         tTHX self;
47         int threaded;
48         perl_mutex mutex;
49 } my_cxt_t;
50 START_MY_CXT;
51
52 #ifdef FUSE_USE_ITHREADS
53 tTHX master_interp = NULL;
54
55 #define CLONE_INTERP(parent) S_clone_interp(parent)
56 tTHX S_clone_interp(tTHX parent) {
57         dMY_CXT_INTERP(parent);
58         if(MY_CXT.threaded) {
59                 MUTEX_LOCK(&MY_CXT.mutex);
60                 PERL_SET_CONTEXT(parent);
61                 dTHX;
62 #if (PERL_VERSION > 10) || (PERL_VERSION == 10 && PERL_SUBVERSION >= 1)
63                 tTHX child = perl_clone(parent, CLONEf_CLONE_HOST);
64 #else
65                 tTHX child = perl_clone(parent, CLONEf_CLONE_HOST | CLONEf_KEEP_PTR_TABLE);
66                 ptr_table_free(PL_ptr_table);
67                 PL_ptr_table = NULL;
68 #endif
69                 MUTEX_UNLOCK(&MY_CXT.mutex);
70                 return child;
71         }
72         return NULL;
73 }
74
75 # define FUSE_CONTEXT_PRE dTHX; if(!aTHX) aTHX = CLONE_INTERP(master_interp); { dMY_CXT; dSP;
76 # define FUSE_CONTEXT_POST }
77 #else
78 # define FUSE_CONTEXT_PRE dTHX; dMY_CXT; dSP;
79 # define FUSE_CONTEXT_POST
80 #endif
81
82 #undef DEBUGf
83 #if 0
84 #define DEBUGf(f, a...) fprintf(stderr, "%s:%d (%i): " f,__BASE_FILE__,__LINE__,sp-PL_stack_base ,##a )
85 #else
86 #define DEBUGf(a...)
87 #endif
88
89 #define FH_KEY(fi) sv_2mortal(newSViv((fi)->fh))
90 #define FH_GETHANDLE(fi) S_fh_get_handle(aTHX_ aMY_CXT_ fi)
91 #define FH_STOREHANDLE(fi,sv) S_fh_store_handle(aTHX_ aMY_CXT_ fi, sv)
92 #define FH_RELEASEHANDLE(fi) S_fh_release_handle(aTHX_ aMY_CXT_ fi)
93
94 SV *S_fh_get_handle(pTHX_ pMY_CXT_ struct fuse_file_info *fi) {
95         SV *val;
96         val = &PL_sv_undef;
97         if(fi->fh != 0) {
98                 HE *he;
99                 if((he = hv_fetch_ent(MY_CXT.handles, FH_KEY(fi), 0, 0))) {
100                         val = HeVAL(he);
101                         SvGETMAGIC(val);
102                 }
103         }
104         return val;
105 }
106
107 void S_fh_release_handle(pTHX_ pMY_CXT_ struct fuse_file_info *fi) {
108         if(fi->fh != 0) {
109                 (void)hv_delete_ent(MY_CXT.handles, FH_KEY(fi), G_DISCARD, 0);
110                 fi->fh = 0;
111         }
112 }
113
114 void S_fh_store_handle(pTHX_ pMY_CXT_ struct fuse_file_info *fi, SV *sv) {
115         if(SvOK(sv)) {
116 #ifdef FUSE_USE_ITHREADS
117                 if(MY_CXT.threaded) {
118                         SvSHARE(sv);
119                 }
120 #endif
121                 MAGIC *mg = (SvTYPE(sv) == SVt_PVMG) ? mg_find(sv, PERL_MAGIC_shared_scalar) : NULL;
122                 fi->fh = mg ? PTR2IV(mg->mg_ptr) : PTR2IV(sv);
123                 if(hv_store_ent(MY_CXT.handles, FH_KEY(fi), SvREFCNT_inc(sv), 0) == NULL) {
124                         SvREFCNT_dec(sv);
125                 }
126                 SvSETMAGIC(sv);
127         }
128 }
129
130 int _PLfuse_getattr(const char *file, struct stat *result) {
131         int rv;
132         FUSE_CONTEXT_PRE;
133         DEBUGf("getattr begin: %s\n",file);
134         ENTER;
135         SAVETMPS;
136         PUSHMARK(SP);
137         XPUSHs(sv_2mortal(newSVpv(file,strlen(file))));
138         PUTBACK;
139         rv = call_sv(MY_CXT.callback[0],G_ARRAY);
140         SPAGAIN;
141         if(rv != 13) {
142                 if(rv > 1) {
143                         fprintf(stderr,"inappropriate number of returned values from getattr\n");
144                         rv = -ENOSYS;
145                 } else if(rv)
146                         rv = POPi;
147                 else
148                         rv = -ENOENT;
149         } else {
150                 result->st_blocks = POPi;
151                 result->st_blksize = POPi;
152                 result->st_ctime = POPi;
153                 result->st_mtime = POPi;
154                 result->st_atime = POPi;
155                 result->st_size = POPn; // we pop double here to support files larger than 4Gb (long limit)
156                 result->st_rdev = POPi;
157                 result->st_gid = POPi;
158                 result->st_uid = POPi;
159                 result->st_nlink = POPi;
160                 result->st_mode = POPi;
161                 result->st_ino   = POPi;
162                 result->st_dev = POPi;
163                 rv = 0;
164         }
165         FREETMPS;
166         LEAVE;
167         PUTBACK;
168         DEBUGf("getattr end: %i\n",rv);
169         FUSE_CONTEXT_POST;
170         return rv;
171 }
172
173 int _PLfuse_readlink(const char *file,char *buf,size_t buflen) {
174         int rv;
175         if(buflen < 1)
176                 return EINVAL;
177         FUSE_CONTEXT_PRE;
178         DEBUGf("readlink begin\n");
179         ENTER;
180         SAVETMPS;
181         PUSHMARK(SP);
182         XPUSHs(sv_2mortal(newSVpv(file,0)));
183         PUTBACK;
184         rv = call_sv(MY_CXT.callback[1],G_SCALAR);
185         SPAGAIN;
186         if(!rv)
187                 rv = -ENOENT;
188         else {
189                 SV *mysv = POPs;
190                 if(SvTYPE(mysv) == SVt_IV || SvTYPE(mysv) == SVt_NV)
191                         rv = SvIV(mysv);
192                 else {
193                         strncpy(buf,SvPV_nolen(mysv),buflen);
194                         rv = 0;
195                 }
196         }
197         FREETMPS;
198         LEAVE;
199         buf[buflen-1] = 0;
200         PUTBACK;
201         DEBUGf("readlink end: %i\n",rv);
202         FUSE_CONTEXT_POST;
203         return rv;
204 }
205
206 int _PLfuse_getdir(const char *file, fuse_dirh_t dirh, fuse_dirfil_t dirfil) {
207         int prv, rv;
208         SV **swp;
209         FUSE_CONTEXT_PRE;
210         DEBUGf("getdir begin\n");
211         ENTER;
212         SAVETMPS;
213         PUSHMARK(SP);
214         XPUSHs(sv_2mortal(newSVpv(file,0)));
215         PUTBACK;
216         prv = call_sv(MY_CXT.callback[2],G_ARRAY);
217         SPAGAIN;
218         if(prv) {
219                 /* Should yield the bottom of the current stack... */
220                 swp = &TOPs - prv + 1;
221                 rv = POPi;
222                 /* Sort of a hack to walk the stack in order, instead of reverse
223                  * order - trying to explain to potential users why they need to
224                  * reverse the order of this array would be confusing, at best. */
225                 while (swp <= &TOPs)
226                         dirfil(dirh,SvPVx_nolen(*(swp++)),0,0);
227         } else {
228                 fprintf(stderr,"getdir() handler returned nothing!\n");
229                 rv = -ENOSYS;
230         }
231         FREETMPS;
232         LEAVE;
233         PUTBACK;
234         DEBUGf("getdir end: %i\n",rv);
235         FUSE_CONTEXT_POST;
236         return rv;
237 }
238
239 int _PLfuse_mknod (const char *file, mode_t mode, dev_t dev) {
240         int rv;
241         FUSE_CONTEXT_PRE;
242         DEBUGf("mknod begin\n");
243         ENTER;
244         SAVETMPS;
245         PUSHMARK(SP);
246         XPUSHs(sv_2mortal(newSVpv(file,0)));
247         XPUSHs(sv_2mortal(newSViv(mode)));
248         XPUSHs(sv_2mortal(newSViv(dev)));
249         PUTBACK;
250         rv = call_sv(MY_CXT.callback[3],G_SCALAR);
251         SPAGAIN;
252         rv = (rv ? POPi : 0);
253         FREETMPS;
254         LEAVE;
255         PUTBACK;
256         DEBUGf("mknod end: %i\n",rv);
257         FUSE_CONTEXT_POST;
258         return rv;
259 }
260
261 int _PLfuse_mkdir (const char *file, mode_t mode) {
262         int rv;
263         FUSE_CONTEXT_PRE;
264         DEBUGf("mkdir begin\n");
265         ENTER;
266         SAVETMPS;
267         PUSHMARK(SP);
268         XPUSHs(sv_2mortal(newSVpv(file,0)));
269         XPUSHs(sv_2mortal(newSViv(mode)));
270         PUTBACK;
271         rv = call_sv(MY_CXT.callback[4],G_SCALAR);
272         SPAGAIN;
273         rv = (rv ? POPi : 0);
274         FREETMPS;
275         LEAVE;
276         PUTBACK;
277         DEBUGf("mkdir end: %i\n",rv);
278         FUSE_CONTEXT_POST;
279         return rv;
280 }
281
282
283 int _PLfuse_unlink (const char *file) {
284         int rv;
285         FUSE_CONTEXT_PRE;
286         DEBUGf("unlink begin\n");
287         ENTER;
288         SAVETMPS;
289         PUSHMARK(SP);
290         XPUSHs(sv_2mortal(newSVpv(file,0)));
291         PUTBACK;
292         rv = call_sv(MY_CXT.callback[5],G_SCALAR);
293         SPAGAIN;
294         rv = (rv ? POPi : 0);
295         FREETMPS;
296         LEAVE;
297         PUTBACK;
298         DEBUGf("unlink end: %i\n",rv);
299         FUSE_CONTEXT_POST;
300         return rv;
301 }
302
303 int _PLfuse_rmdir (const char *file) {
304         int rv;
305         FUSE_CONTEXT_PRE;
306         DEBUGf("rmdir begin\n");
307         ENTER;
308         SAVETMPS;
309         PUSHMARK(SP);
310         XPUSHs(sv_2mortal(newSVpv(file,0)));
311         PUTBACK;
312         rv = call_sv(MY_CXT.callback[6],G_SCALAR);
313         SPAGAIN;
314         rv = (rv ? POPi : 0);
315         FREETMPS;
316         LEAVE;
317         PUTBACK;
318         DEBUGf("rmdir end: %i\n",rv);
319         FUSE_CONTEXT_POST;
320         return rv;
321 }
322
323 int _PLfuse_symlink (const char *file, const char *new) {
324         int rv;
325         FUSE_CONTEXT_PRE;
326         DEBUGf("symlink begin\n");
327         ENTER;
328         SAVETMPS;
329         PUSHMARK(SP);
330         XPUSHs(sv_2mortal(newSVpv(file,0)));
331         XPUSHs(sv_2mortal(newSVpv(new,0)));
332         PUTBACK;
333         rv = call_sv(MY_CXT.callback[7],G_SCALAR);
334         SPAGAIN;
335         rv = (rv ? POPi : 0);
336         FREETMPS;
337         LEAVE;
338         PUTBACK;
339         DEBUGf("symlink end: %i\n",rv);
340         FUSE_CONTEXT_POST;
341         return rv;
342 }
343
344 int _PLfuse_rename (const char *file, const char *new) {
345         int rv;
346         FUSE_CONTEXT_PRE;
347         DEBUGf("rename begin\n");
348         ENTER;
349         SAVETMPS;
350         PUSHMARK(SP);
351         XPUSHs(sv_2mortal(newSVpv(file,0)));
352         XPUSHs(sv_2mortal(newSVpv(new,0)));
353         PUTBACK;
354         rv = call_sv(MY_CXT.callback[8],G_SCALAR);
355         SPAGAIN;
356         rv = (rv ? POPi : 0);
357         FREETMPS;
358         LEAVE;
359         PUTBACK;
360         DEBUGf("rename end: %i\n",rv);
361         FUSE_CONTEXT_POST;
362         return rv;
363 }
364
365 int _PLfuse_link (const char *file, const char *new) {
366         int rv;
367         FUSE_CONTEXT_PRE;
368         DEBUGf("link begin\n");
369         ENTER;
370         SAVETMPS;
371         PUSHMARK(SP);
372         XPUSHs(sv_2mortal(newSVpv(file,0)));
373         XPUSHs(sv_2mortal(newSVpv(new,0)));
374         PUTBACK;
375         rv = call_sv(MY_CXT.callback[9],G_SCALAR);
376         SPAGAIN;
377         rv = (rv ? POPi : 0);
378         FREETMPS;
379         LEAVE;
380         PUTBACK;
381         DEBUGf("link end: %i\n",rv);
382         FUSE_CONTEXT_POST;
383         return rv;
384 }
385
386 int _PLfuse_chmod (const char *file, mode_t mode) {
387         int rv;
388         FUSE_CONTEXT_PRE;
389         DEBUGf("chmod begin\n");
390         ENTER;
391         SAVETMPS;
392         PUSHMARK(SP);
393         XPUSHs(sv_2mortal(newSVpv(file,0)));
394         XPUSHs(sv_2mortal(newSViv(mode)));
395         PUTBACK;
396         rv = call_sv(MY_CXT.callback[10],G_SCALAR);
397         SPAGAIN;
398         rv = (rv ? POPi : 0);
399         FREETMPS;
400         LEAVE;
401         PUTBACK;
402         DEBUGf("chmod end: %i\n",rv);
403         FUSE_CONTEXT_POST;
404         return rv;
405 }
406
407 int _PLfuse_chown (const char *file, uid_t uid, gid_t gid) {
408         int rv;
409         FUSE_CONTEXT_PRE;
410         DEBUGf("chown begin\n");
411         ENTER;
412         SAVETMPS;
413         PUSHMARK(SP);
414         XPUSHs(sv_2mortal(newSVpv(file,0)));
415         XPUSHs(sv_2mortal(newSViv(uid)));
416         XPUSHs(sv_2mortal(newSViv(gid)));
417         PUTBACK;
418         rv = call_sv(MY_CXT.callback[11],G_SCALAR);
419         SPAGAIN;
420         rv = (rv ? POPi : 0);
421         FREETMPS;
422         LEAVE;
423         PUTBACK;
424         DEBUGf("chown end: %i\n",rv);
425         FUSE_CONTEXT_POST;
426         return rv;
427 }
428
429 int _PLfuse_truncate (const char *file, off_t off) {
430         int rv;
431 #ifndef PERL_HAS_64BITINT
432         char *temp;
433 #endif
434         FUSE_CONTEXT_PRE;
435         DEBUGf("truncate begin\n");
436         ENTER;
437         SAVETMPS;
438         PUSHMARK(SP);
439         XPUSHs(sv_2mortal(newSVpv(file,0)));
440 #ifdef PERL_HAS_64BITINT
441         XPUSHs(sv_2mortal(newSViv(off)));
442 #else
443         if (asprintf(&temp, "%llu", off) == -1)
444                 croak("Memory allocation failure!");
445         XPUSHs(sv_2mortal(newSVpv(temp, 0)));
446         free(temp);
447 #endif
448         PUTBACK;
449         rv = call_sv(MY_CXT.callback[12],G_SCALAR);
450         SPAGAIN;
451         rv = (rv ? POPi : 0);
452         FREETMPS;
453         LEAVE;
454         PUTBACK;
455         DEBUGf("truncate end: %i\n",rv);
456         FUSE_CONTEXT_POST;
457         return rv;
458 }
459
460 int _PLfuse_utime (const char *file, struct utimbuf *uti) {
461         int rv;
462         FUSE_CONTEXT_PRE;
463         DEBUGf("utime begin\n");
464         ENTER;
465         SAVETMPS;
466         PUSHMARK(SP);
467         XPUSHs(sv_2mortal(newSVpv(file,0)));
468         XPUSHs(sv_2mortal(newSViv(uti->actime)));
469         XPUSHs(sv_2mortal(newSViv(uti->modtime)));
470         PUTBACK;
471         rv = call_sv(MY_CXT.callback[13],G_SCALAR);
472         SPAGAIN;
473         rv = (rv ? POPi : 0);
474         FREETMPS;
475         LEAVE;
476         PUTBACK;
477         DEBUGf("utime end: %i\n",rv);
478         FUSE_CONTEXT_POST;
479         return rv;
480 }
481
482 int _PLfuse_open (const char *file, struct fuse_file_info *fi) {
483         int rv;
484         int flags = fi->flags;
485         HV *fihash;
486         FUSE_CONTEXT_PRE;
487         DEBUGf("open begin\n");
488         ENTER;
489         SAVETMPS;
490         PUSHMARK(SP);
491         XPUSHs(sv_2mortal(newSVpv(file,0)));
492         XPUSHs(sv_2mortal(newSViv(flags)));
493         /* Create a hashref containing the details from fi
494          * which we can look at or modify.
495          */
496         fi->fh = 0; /* Ensure it starts with 0 - important if they don't set it */
497         fihash = newHV();
498 #if FUSE_VERSION >= 24
499         (void) hv_store(fihash, "direct_io",    9, newSViv(fi->direct_io),   0);
500         (void) hv_store(fihash, "keep_cache",  10, newSViv(fi->keep_cache),  0);
501 #endif
502 #if FUSE_VERSION >= 29
503         (void) hv_store(fihash, "nonseekable", 11, newSViv(fi->nonseekable), 0);
504 #endif
505         XPUSHs(sv_2mortal(newRV_noinc((SV*) fihash)));
506         /* All hashref things done */
507
508         PUTBACK;
509         /* Open called with filename, flags */
510         rv = call_sv(MY_CXT.callback[14],G_ARRAY);
511         SPAGAIN;
512         if(rv) {
513                 if(rv > 1) {
514                         FH_STOREHANDLE(fi,POPs);
515                 }
516                 rv = POPi;
517         }
518         else
519                 rv = 0;
520         if (rv == 0)
521         {
522                 /* Success, so copy the file handle which they returned */
523 #if FUSE_VERSION >= 24
524                 SV **svp;
525                 if ((svp = hv_fetch(fihash, "direct_io",    9, 0)) != NULL)
526                         fi->direct_io   = SvIV(*svp);
527                 if ((svp = hv_fetch(fihash, "keep_cache",  10, 0)) != NULL)
528                         fi->keep_cache  = SvIV(*svp);
529 #endif
530 #if FUSE_VERSION >= 29
531                 if ((svp = hv_fetch(fihash, "nonseekable", 11, 0)) != NULL)
532                         fi->nonseekable = SvIV(*svp);
533 #endif
534         }
535         FREETMPS;
536         LEAVE;
537         PUTBACK;
538         DEBUGf("open end: %i\n",rv);
539         FUSE_CONTEXT_POST;
540         return rv;
541 }
542
543 int _PLfuse_read (const char *file, char *buf, size_t buflen, off_t off,
544                 struct fuse_file_info *fi) {
545         int rv;
546 #ifndef PERL_HAS_64BITINT
547         char *temp;
548 #endif
549         FUSE_CONTEXT_PRE;
550         DEBUGf("read begin\n");
551         ENTER;
552         SAVETMPS;
553         PUSHMARK(SP);
554         XPUSHs(file ? sv_2mortal(newSVpv(file,0)) : &PL_sv_undef);
555         XPUSHs(sv_2mortal(newSViv(buflen)));
556 #ifdef PERL_HAS_64BITINT
557         XPUSHs(sv_2mortal(newSViv(off)));
558 #else
559         if (asprintf(&temp, "%llu", off) == -1)
560                 croak("Memory allocation failure!");
561         XPUSHs(sv_2mortal(newSVpv(temp, 0)));
562         free(temp);
563 #endif
564         XPUSHs(FH_GETHANDLE(fi));
565         PUTBACK;
566         rv = call_sv(MY_CXT.callback[15],G_SCALAR);
567         SPAGAIN;
568         if(!rv)
569                 rv = -ENOENT;
570         else {
571                 SV *mysv = POPs;
572                 if(SvTYPE(mysv) == SVt_NV || SvTYPE(mysv) == SVt_IV)
573                         rv = SvIV(mysv);
574                 else {
575                         if(SvPOK(mysv)) {
576                                 rv = SvCUR(mysv);
577                         } else {
578                                 rv = 0;
579                         }
580                         if(rv > buflen)
581                                 croak("read() handler returned more than buflen! (%i > %i)",rv,buflen);
582                         if(rv)
583                                 memcpy(buf,SvPV_nolen(mysv),rv);
584                 }
585         }
586         FREETMPS;
587         LEAVE;
588         PUTBACK;
589         DEBUGf("read end: %i\n",rv);
590         FUSE_CONTEXT_POST;
591         return rv;
592 }
593
594 int _PLfuse_write (const char *file, const char *buf, size_t buflen, off_t off, struct fuse_file_info *fi) {
595         int rv;
596 #ifndef PERL_HAS_64BITINT
597         char *temp;
598 #endif
599         FUSE_CONTEXT_PRE;
600         DEBUGf("write begin\n");
601         ENTER;
602         SAVETMPS;
603         PUSHMARK(SP);
604         XPUSHs(file ? sv_2mortal(newSVpv(file,0)) : &PL_sv_undef);
605         XPUSHs(sv_2mortal(newSVpvn(buf,buflen)));
606 #ifdef PERL_HAS_64BITINT
607         XPUSHs(sv_2mortal(newSViv(off)));
608 #else
609         if (asprintf(&temp, "%llu", off) == -1)
610                 croak("Memory allocation failure!");
611         XPUSHs(sv_2mortal(newSVpv(temp, 0)));
612         free(temp);
613 #endif
614         XPUSHs(FH_GETHANDLE(fi));
615         PUTBACK;
616         rv = call_sv(MY_CXT.callback[16],G_SCALAR);
617         SPAGAIN;
618         rv = (rv ? POPi : 0);
619         FREETMPS;
620         LEAVE;
621         PUTBACK;
622         DEBUGf("write end: %i\n",rv);
623         FUSE_CONTEXT_POST;
624         return rv;
625 }
626
627 int _PLfuse_statfs (const char *file, struct statvfs *st) {
628         int rv;
629         FUSE_CONTEXT_PRE;
630         DEBUGf("statfs begin\n");
631         ENTER;
632         SAVETMPS;
633         PUSHMARK(SP);
634         PUTBACK;
635         rv = call_sv(MY_CXT.callback[17],G_ARRAY);
636         SPAGAIN;
637         DEBUGf("statfs got %i params\n",rv);
638         if(rv == 6 || rv == 7) {
639                 st->f_bsize     = POPi;
640                 st->f_bfree     = POPi;
641                 st->f_blocks    = POPi;
642                 st->f_ffree     = POPi;
643                 st->f_files     = POPi;
644                 st->f_namemax   = POPi;
645                 /* zero and fill-in other */
646                 st->f_fsid = 0;
647                 st->f_frsize = 4096;
648                 st->f_flag = 0;
649                 st->f_bavail = st->f_bfree;
650                 st->f_favail = st->f_ffree;
651
652                 if(rv == 7)
653                         rv = POPi;
654                 else
655                         rv = 0;
656         } else
657         if(rv > 1)
658                 croak("inappropriate number of returned values from statfs");
659         else
660         if(rv)
661                 rv = POPi;
662         else
663                 rv = -ENOSYS;
664         FREETMPS;
665         LEAVE;
666         PUTBACK;
667         DEBUGf("statfs end: %i\n",rv);
668         FUSE_CONTEXT_POST;
669         return rv;
670 }
671
672 int _PLfuse_flush (const char *file, struct fuse_file_info *fi) {
673         int rv;
674         FUSE_CONTEXT_PRE;
675         DEBUGf("flush begin\n");
676         ENTER;
677         SAVETMPS;
678         PUSHMARK(SP);
679         XPUSHs(file ? sv_2mortal(newSVpv(file,0)) : &PL_sv_undef);
680         XPUSHs(FH_GETHANDLE(fi));
681         PUTBACK;
682         rv = call_sv(MY_CXT.callback[18],G_SCALAR);
683         SPAGAIN;
684         rv = (rv ? POPi : 0);
685         FREETMPS;
686         LEAVE;
687         PUTBACK;
688         DEBUGf("flush end: %i\n",rv);
689         FUSE_CONTEXT_POST;
690         return rv;
691 }
692
693 int _PLfuse_release (const char *file, struct fuse_file_info *fi) {
694         int rv;
695         int flags = fi->flags;
696         FUSE_CONTEXT_PRE;
697         DEBUGf("release begin\n");
698         ENTER;
699         SAVETMPS;
700         PUSHMARK(SP);
701         XPUSHs(file ? sv_2mortal(newSVpv(file,0)) : &PL_sv_undef);
702         XPUSHs(sv_2mortal(newSViv(flags)));
703         XPUSHs(FH_GETHANDLE(fi));
704         PUTBACK;
705         rv = call_sv(MY_CXT.callback[19],G_SCALAR);
706         SPAGAIN;
707         rv = (rv ? POPi : 0);
708         FH_RELEASEHANDLE(fi);
709         FREETMPS;
710         LEAVE;
711         PUTBACK;
712         DEBUGf("release end: %i\n",rv);
713         FUSE_CONTEXT_POST;
714         return rv;
715 }
716
717 int _PLfuse_fsync (const char *file, int datasync, struct fuse_file_info *fi) {
718         int rv;
719         int flags = fi->flags;
720         FUSE_CONTEXT_PRE;
721         DEBUGf("fsync begin\n");
722         ENTER;
723         SAVETMPS;
724         PUSHMARK(SP);
725         XPUSHs(file ? sv_2mortal(newSVpv(file,0)) : &PL_sv_undef);
726         XPUSHs(sv_2mortal(newSViv(flags)));
727         XPUSHs(FH_GETHANDLE(fi));
728         PUTBACK;
729         rv = call_sv(MY_CXT.callback[20],G_SCALAR);
730         SPAGAIN;
731         rv = (rv ? POPi : 0);
732         FREETMPS;
733         LEAVE;
734         PUTBACK;
735         DEBUGf("fsync end: %i\n",rv);
736         FUSE_CONTEXT_POST;
737         return rv;
738 }
739
740 int _PLfuse_setxattr (const char *file, const char *name, const char *buf, size_t buflen, int flags) {
741         int rv;
742         FUSE_CONTEXT_PRE;
743         DEBUGf("setxattr begin\n");
744         ENTER;
745         SAVETMPS;
746         PUSHMARK(SP);
747         XPUSHs(sv_2mortal(newSVpv(file,0)));
748         XPUSHs(sv_2mortal(newSVpv(name,0)));
749         XPUSHs(sv_2mortal(newSVpvn(buf,buflen)));
750         XPUSHs(sv_2mortal(newSViv(flags)));
751         PUTBACK;
752         rv = call_sv(MY_CXT.callback[21],G_SCALAR);
753         SPAGAIN;
754         rv = (rv ? POPi : 0);
755         FREETMPS;
756         LEAVE;
757         PUTBACK;
758         DEBUGf("setxattr end: %i\n",rv);
759         FUSE_CONTEXT_POST;
760         return rv;
761 }
762
763 int _PLfuse_getxattr (const char *file, const char *name, char *buf, size_t buflen) {
764         int rv;
765         FUSE_CONTEXT_PRE;
766         DEBUGf("getxattr begin\n");
767         ENTER;
768         SAVETMPS;
769         PUSHMARK(SP);
770         XPUSHs(sv_2mortal(newSVpv(file,0)));
771         XPUSHs(sv_2mortal(newSVpv(name,0)));
772         PUTBACK;
773         rv = call_sv(MY_CXT.callback[22],G_SCALAR);
774         SPAGAIN;
775         if(!rv)
776                 rv = -ENOENT;
777         else {
778                 SV *mysv = POPs;
779
780                 rv = 0;
781                 if(SvTYPE(mysv) == SVt_NV || SvTYPE(mysv) == SVt_IV)
782                         rv = SvIV(mysv);
783                 else {
784                         if(SvPOK(mysv)) {
785                                 rv = SvCUR(mysv);
786                         } else {
787                                 rv = 0;
788                         }
789                         if ((rv > 0) && (buflen > 0))
790                         {
791                                 if(rv > buflen)
792                                         rv = -ERANGE;
793                                 else
794                                         memcpy(buf,SvPV_nolen(mysv),rv);
795                         }
796                 }
797         }
798         FREETMPS;
799         LEAVE;
800         PUTBACK;
801         DEBUGf("getxattr end: %i\n",rv);
802         FUSE_CONTEXT_POST;
803         return rv;
804 }
805
806 int _PLfuse_listxattr (const char *file, char *list, size_t size) {
807         int prv, rv;
808         FUSE_CONTEXT_PRE;
809         DEBUGf("listxattr begin\n");
810         ENTER;
811         SAVETMPS;
812         PUSHMARK(SP);
813         XPUSHs(sv_2mortal(newSVpv(file,0)));
814         PUTBACK;
815         prv = call_sv(MY_CXT.callback[23],G_ARRAY);
816         SPAGAIN;
817         if(!prv)
818                 rv = -ENOENT;
819         else {
820
821                 char *p = list;
822                 int spc = size;
823                 int total_len = 0;
824
825                 rv = POPi;
826                 prv--;
827
828                 /* Always nul terminate */
829                 if (list && (size > 0))
830                         list[0] = '\0';
831
832                 while (prv > 0)
833                 {
834                         SV *mysv = POPs;
835                         prv--;
836
837                         if (SvPOK(mysv)) {
838                                 /* Copy nul too */
839                                 int s = SvCUR(mysv) + 1;
840                                 total_len += s;
841
842                                 if (p && (size > 0) && (spc >= s))
843                                 {
844                                         memcpy(p,SvPV_nolen(mysv),s);
845                                         p += s;
846                                         spc -= s;
847                                 }
848                         }
849                 }
850
851                 /*
852                  * If the Perl returned an error, return that.
853                  * Otherwise check that the buffer was big enough.
854                  */
855                 if (rv == 0)
856                 {
857                         rv = total_len;
858                         if ((size > 0) && (size < total_len))
859                                 rv = -ERANGE;
860                 }
861         }
862         FREETMPS;
863         LEAVE;
864         PUTBACK;
865         DEBUGf("listxattr end: %i\n",rv);
866         FUSE_CONTEXT_POST;
867         return rv;
868 }
869
870 int _PLfuse_removexattr (const char *file, const char *name) {
871         int rv;
872         FUSE_CONTEXT_PRE;
873         DEBUGf("removexattr begin\n");
874         ENTER;
875         SAVETMPS;
876         PUSHMARK(SP);
877         XPUSHs(sv_2mortal(newSVpv(file,0)));
878         XPUSHs(sv_2mortal(newSVpv(name,0)));
879         PUTBACK;
880         rv = call_sv(MY_CXT.callback[24],G_SCALAR);
881         SPAGAIN;
882         rv = (rv ? POPi : 0);
883         FREETMPS;
884         LEAVE;
885         PUTBACK;
886         DEBUGf("removexattr end: %i\n",rv);
887         FUSE_CONTEXT_POST;
888         return rv;
889 }
890
891 #if FUSE_VERSION >= 23
892 int _PLfuse_opendir(const char *file, struct fuse_file_info *fi) {
893         int rv;
894         FUSE_CONTEXT_PRE;
895         DEBUGf("opendir begin\n");
896         ENTER;
897         SAVETMPS;
898         PUSHMARK(SP);
899         XPUSHs(sv_2mortal(newSVpv(file,0)));
900         fi->fh = 0; /* Ensure it starts with 0 - important if they don't set it */
901         PUTBACK;
902         rv = call_sv(MY_CXT.callback[25], G_ARRAY);
903         SPAGAIN;
904         if (rv) {
905                 if (rv > 1) {
906                         FH_STOREHANDLE(fi, POPs);
907                 }
908                 rv = POPi;
909         } else
910                 rv = 0;
911         FREETMPS;
912         LEAVE;
913         PUTBACK;
914         DEBUGf("opendir end: %i\n",rv);
915         FUSE_CONTEXT_POST;
916         return rv;
917
918 }
919
920 int _PLfuse_readdir(const char *file, void *dirh, fuse_fill_dir_t dirfil,
921                     off_t off, struct fuse_file_info *fi) {
922         int prv = 0, rv;
923         SV *sv, **svp, **swp;
924         AV *av, *av2;
925         struct stat st;
926         bool st_filled = 0;
927 #ifndef PERL_HAS_64BITINT
928         char *temp;
929 #endif
930         FUSE_CONTEXT_PRE;
931         DEBUGf("readdir begin\n");
932         ENTER;
933         SAVETMPS;
934         PUSHMARK(SP);
935         XPUSHs(file ? sv_2mortal(newSVpv(file,0)) : &PL_sv_undef);
936 #ifdef PERL_HAS_64BITINT
937         XPUSHs(sv_2mortal(newSViv(off)));
938 #else
939         if (asprintf(&temp, "%llu", off) == -1)
940                 croak("Memory allocation failure!");
941         XPUSHs(sv_2mortal(newSVpv(temp, 0)));
942         free(temp);
943 #endif
944         XPUSHs(FH_GETHANDLE(fi));
945         PUTBACK;
946         prv = call_sv(MY_CXT.callback[26],G_ARRAY);
947         SPAGAIN;
948         if (prv) {
949                 /* Should yield the bottom of the current stack... */
950                 swp = &TOPs - prv + 1;
951                 rv = POPi;
952                 memset(&st, 0, sizeof(struct stat));
953                 /* Sort of a hack to walk the stack in order, instead of reverse
954                  * order - trying to explain to potential users why they need to
955                  * reverse the order of this array would be confusing, at best. */
956                 while (swp <= &TOPs) {
957                         sv = *(swp++);
958                         if (!SvROK(sv) && SvPOK(sv))
959                         /* Just a bare SV (probably a string; hopefully a string) */
960                                 dirfil(dirh, SvPVx_nolen(sv), NULL, 0);
961                         else if (SvROK(sv) && SvTYPE(av = (AV *)SvRV(sv)) == SVt_PVAV) {
962                                 if (av_len(av) >= 2) {
963                                         /* The third element of the array should be the args that
964                                          * would otherwise go to getattr(); a lot of filesystems
965                                          * will, or at least can, return that info as part of the
966                                          * enumeration process... */
967                                         svp = av_fetch(av, 2, FALSE);
968                                         if (SvROK(*svp) &&
969                                                         SvTYPE(av2 = (AV *)SvRV(*svp)) == SVt_PVAV &&
970                                                         av_len(av2) == 12) {
971                                                 st.st_dev     = SvIV(*(av_fetch(av2,  0, FALSE)));
972                                                 st.st_ino     = SvIV(*(av_fetch(av2,  1, FALSE)));
973                                                 st.st_mode    = SvIV(*(av_fetch(av2,  2, FALSE)));
974                                                 st.st_nlink   = SvIV(*(av_fetch(av2,  3, FALSE)));
975                                                 st.st_uid     = SvIV(*(av_fetch(av2,  4, FALSE)));
976                                                 st.st_gid     = SvIV(*(av_fetch(av2,  5, FALSE)));
977                                                 st.st_rdev    = SvIV(*(av_fetch(av2,  6, FALSE)));
978                                                 st.st_size    = SvNV(*(av_fetch(av2,  7, FALSE)));
979                                                 st.st_atime   = SvIV(*(av_fetch(av2,  8, FALSE)));
980                                                 st.st_mtime   = SvIV(*(av_fetch(av2,  9, FALSE)));
981                                                 st.st_ctime   = SvIV(*(av_fetch(av2, 10, FALSE)));
982                                                 st.st_blksize = SvIV(*(av_fetch(av2, 11, FALSE)));
983                                                 st.st_blocks  = SvIV(*(av_fetch(av2, 12, FALSE)));
984                                                 st_filled = 1;
985                                         }
986                                         else
987                                                 fprintf(stderr,"Extra SV didn't appear to be correct, ignoring\n");
988                                         /* For now if the element isn't what we want, just
989                                          * quietly ignore it... */
990                                 }
991                                 if (av_len(av) >= 1) {
992                                         char *entryname = SvPVx_nolen(*(av_fetch(av, 1, FALSE)));
993                                         off_t elemnum = SvNV(*(av_fetch(av, 0, FALSE)));
994                                         dirfil(dirh, entryname, st_filled ? &st : NULL, elemnum);
995                                 }
996                                 if (st_filled) {
997                                         memset(&st, 0, sizeof(struct stat));
998                                         st_filled = 0;
999                                 }
1000                         }
1001                         else
1002                                 fprintf(stderr, "ERROR: Unknown entry passed via readdir\n");
1003                 }
1004         } else {
1005                 fprintf(stderr,"readdir() handler returned nothing!\n");
1006                 rv = -ENOSYS;
1007         }
1008         FREETMPS;
1009         LEAVE;
1010         PUTBACK;
1011         DEBUGf("readdir end: %i\n",rv);
1012         FUSE_CONTEXT_POST;
1013         return rv;
1014 }
1015
1016 int _PLfuse_releasedir(const char *file, struct fuse_file_info *fi) {
1017         int rv;
1018         FUSE_CONTEXT_PRE;
1019         DEBUGf("releasedir begin\n");
1020         ENTER;
1021         SAVETMPS;
1022         PUSHMARK(SP);
1023         XPUSHs(file ? sv_2mortal(newSVpv(file,0)) : &PL_sv_undef);
1024         XPUSHs(FH_GETHANDLE(fi));
1025         PUTBACK;
1026         rv = call_sv(MY_CXT.callback[27], G_SCALAR);
1027         SPAGAIN;
1028         rv = (rv ? POPi : 0);
1029         FH_RELEASEHANDLE(fi);
1030         FREETMPS;
1031         LEAVE;
1032         PUTBACK;
1033         DEBUGf("releasedir end: %i\n",rv);
1034         FUSE_CONTEXT_POST;
1035         return rv;
1036 }
1037
1038 int _PLfuse_fsyncdir(const char *file, int datasync,
1039                      struct fuse_file_info *fi) {
1040         int rv;
1041         FUSE_CONTEXT_PRE;
1042         DEBUGf("fsyncdir begin\n");
1043         ENTER;
1044         SAVETMPS;
1045         PUSHMARK(SP);
1046         XPUSHs(file ? sv_2mortal(newSVpv(file,0)) : &PL_sv_undef);
1047         XPUSHs(sv_2mortal(newSViv(datasync)));
1048         XPUSHs(FH_GETHANDLE(fi));
1049         PUTBACK;
1050         rv = call_sv(MY_CXT.callback[28], G_SCALAR);
1051         SPAGAIN;
1052         rv = (rv ? POPi : 0);
1053         FREETMPS;
1054         LEAVE;
1055         PUTBACK;
1056         DEBUGf("fsyncdir end: %i\n",rv);
1057         FUSE_CONTEXT_POST;
1058         return rv;
1059 }
1060
1061 #if FUSE_VERSION >= 26
1062 void *_PLfuse_init(struct fuse_conn_info *fc)
1063 #else /* FUSE_VERSION < 26 */
1064 void *_PLfuse_init(void)
1065 #endif /* FUSE_VERSION >= 26 */
1066 {
1067         void *rv = NULL;
1068         int prv;
1069         FUSE_CONTEXT_PRE;
1070         DEBUGf("init begin\n");
1071         ENTER;
1072         SAVETMPS;
1073         PUSHMARK(SP);
1074         PUTBACK;
1075         prv = call_sv(MY_CXT.callback[29], G_SCALAR);
1076         SPAGAIN;
1077         if (prv) {
1078                 rv = POPs;
1079                 if (rv == &PL_sv_undef)
1080                         rv = NULL;
1081                 else
1082                         rv = SvREFCNT_inc((SV *)rv);
1083         }
1084         FREETMPS;
1085         LEAVE;
1086         PUTBACK;
1087         DEBUGf("init end: %p\n", rv);
1088         FUSE_CONTEXT_POST;
1089         return rv;
1090 }
1091
1092 void _PLfuse_destroy(void *private_data) {
1093         FUSE_CONTEXT_PRE;
1094         DEBUGf("destroy begin\n");
1095         ENTER;
1096         SAVETMPS;
1097         PUSHMARK(SP);
1098         XPUSHs(private_data ? (SV *)private_data : &PL_sv_undef);
1099         PUTBACK;
1100         call_sv(MY_CXT.callback[30], G_VOID);
1101         SPAGAIN;
1102         if (private_data)
1103                 SvREFCNT_dec((SV *)private_data);
1104         FREETMPS;
1105         LEAVE;
1106         PUTBACK;
1107         DEBUGf("init end\n");
1108         FUSE_CONTEXT_POST;
1109 }
1110 #endif /* FUSE_VERSION >= 23 */
1111
1112 #if FUSE_VERSION >= 25
1113 int _PLfuse_access(const char *file, int mask) {
1114         int rv;
1115         FUSE_CONTEXT_PRE;
1116         DEBUGf("access begin\n");
1117         ENTER;
1118         SAVETMPS;
1119         PUSHMARK(SP);
1120         XPUSHs(sv_2mortal(newSVpv(file,0)));
1121         XPUSHs(sv_2mortal(newSViv(mask)));
1122         PUTBACK;
1123         rv = call_sv(MY_CXT.callback[31], G_SCALAR);
1124         SPAGAIN;
1125         rv = (rv ? POPi : 0);
1126         FREETMPS;
1127         LEAVE;
1128         PUTBACK;
1129         DEBUGf("access end: %d\n", rv);
1130         FUSE_CONTEXT_POST;
1131         return rv;
1132 }
1133
1134 int _PLfuse_create(const char *file, mode_t mode, struct fuse_file_info *fi) {
1135         int rv;
1136         HV *fihash;
1137         FUSE_CONTEXT_PRE;
1138         DEBUGf("create begin\n");
1139         ENTER;
1140         SAVETMPS;
1141         PUSHMARK(SP);
1142         XPUSHs(sv_2mortal(newSVpv(file,0)));
1143         XPUSHs(sv_2mortal(newSViv(mode)));
1144         XPUSHs(sv_2mortal(newSViv(fi->flags)));
1145         fi->fh = 0; /* Ensure it starts with 0 - important if they don't set it */
1146         /* Create a hashref containing the details from fi
1147          * which we can look at or modify.
1148          */
1149         fihash = newHV();
1150         (void) hv_store(fihash, "direct_io",    9, newSViv(fi->direct_io),   0);
1151         (void) hv_store(fihash, "keep_cache",  10, newSViv(fi->keep_cache),  0);
1152 #if FUSE_VERSION >= 29
1153         (void) hv_store(fihash, "nonseekable", 11, newSViv(fi->nonseekable), 0);
1154 #endif
1155         XPUSHs(sv_2mortal(newRV_noinc((SV*) fihash)));
1156         /* All hashref things done */
1157
1158         PUTBACK;
1159         rv = call_sv(MY_CXT.callback[32], G_ARRAY);
1160         SPAGAIN;
1161         if (rv) {
1162                 if (rv > 1) {
1163                         FH_STOREHANDLE(fi,POPs);
1164                 }
1165                 rv = POPi;
1166         }
1167         else {
1168                 fprintf(stderr, "create() handler returned nothing!\n");
1169                 rv = -ENOSYS;
1170         }
1171         if (rv == 0) {
1172                 /* Success, so copy the file handle which they returned */
1173                 SV **svp;
1174                 if ((svp = hv_fetch(fihash, "direct_io",    9, 0)) != NULL)
1175                         fi->direct_io   = SvIV(*svp);
1176                 if ((svp = hv_fetch(fihash, "keep_cache",  10, 0)) != NULL)
1177                         fi->keep_cache  = SvIV(*svp);
1178 #if FUSE_VERSION >= 29
1179                 if ((svp = hv_fetch(fihash, "nonseekable", 11, 0)) != NULL)
1180                         fi->nonseekable = SvIV(*svp);
1181 #endif
1182         }
1183         FREETMPS;
1184         LEAVE;
1185         PUTBACK;
1186         DEBUGf("create end: %d\n",rv);
1187         FUSE_CONTEXT_POST;
1188         return rv;
1189 }
1190
1191 int _PLfuse_ftruncate(const char *file, off_t off, struct fuse_file_info *fi) {
1192         int rv;
1193 #ifndef PERL_HAS_64BITINT
1194         char *temp;
1195 #endif
1196         FUSE_CONTEXT_PRE;
1197         DEBUGf("ftruncate begin\n");
1198         ENTER;
1199         SAVETMPS;
1200         PUSHMARK(SP);
1201         XPUSHs(file ? sv_2mortal(newSVpv(file,0)) : &PL_sv_undef);
1202 #ifdef PERL_HAS_64BITINT
1203         XPUSHs(sv_2mortal(newSViv(off)));
1204 #else
1205         if (asprintf(&temp, "%llu", off) == -1)
1206                 croak("Memory allocation failure!");
1207         XPUSHs(sv_2mortal(newSVpv(temp, 0)));
1208         free(temp);
1209 #endif
1210         XPUSHs(FH_GETHANDLE(fi));
1211         PUTBACK;
1212         rv = call_sv(MY_CXT.callback[33],G_SCALAR);
1213         SPAGAIN;
1214         rv = (rv ? POPi : 0);
1215         FREETMPS;
1216         LEAVE;
1217         PUTBACK;
1218         DEBUGf("ftruncate end: %i\n",rv);
1219         FUSE_CONTEXT_POST;
1220         return rv;
1221 }
1222
1223 int _PLfuse_fgetattr(const char *file, struct stat *result,
1224                      struct fuse_file_info *fi) {
1225         int rv;
1226         FUSE_CONTEXT_PRE;
1227         DEBUGf("fgetattr begin: %s\n",file);
1228         ENTER;
1229         SAVETMPS;
1230         PUSHMARK(SP);
1231         XPUSHs(file ? sv_2mortal(newSVpv(file,0)) : &PL_sv_undef);
1232         XPUSHs(FH_GETHANDLE(fi));
1233         PUTBACK;
1234         rv = call_sv(MY_CXT.callback[34],G_ARRAY);
1235         SPAGAIN;
1236         if(rv != 13) {
1237                 if(rv > 1) {
1238                         fprintf(stderr,"inappropriate number of returned values from getattr\n");
1239                         rv = -ENOSYS;
1240                 } else if(rv)
1241                         rv = POPi;
1242                 else
1243                         rv = -ENOENT;
1244         } else {
1245                 result->st_blocks = POPi;
1246                 result->st_blksize = POPi;
1247                 result->st_ctime = POPi;
1248                 result->st_mtime = POPi;
1249                 result->st_atime = POPi;
1250                 result->st_size = POPn; // we pop double here to support files larger than 4Gb (long limit)
1251                 result->st_rdev = POPi;
1252                 result->st_gid = POPi;
1253                 result->st_uid = POPi;
1254                 result->st_nlink = POPi;
1255                 result->st_mode = POPi;
1256                 result->st_ino   = POPi;
1257                 result->st_dev = POPi;
1258                 rv = 0;
1259         }
1260         FREETMPS;
1261         LEAVE;
1262         PUTBACK;
1263         DEBUGf("fgetattr end: %i\n",rv);
1264         FUSE_CONTEXT_POST;
1265         return rv;
1266 }
1267 #endif /* FUSE_VERSION >= 25 */
1268
1269 #if FUSE_VERSION >= 26
1270 int _PLfuse_lock(const char *file, struct fuse_file_info *fi, int cmd,
1271                  struct flock *lockinfo) {
1272         int rv;
1273         HV *lihash;
1274         SV *sv;
1275 #ifndef PERL_HAS_64BITINT
1276         char *temp;
1277 #endif
1278         FUSE_CONTEXT_PRE;
1279         DEBUGf("lock begin\n");
1280         ENTER;
1281         SAVETMPS;
1282         PUSHMARK(SP);
1283         XPUSHs(file ? sv_2mortal(newSVpv(file,0)) : &PL_sv_undef);
1284         XPUSHs(sv_2mortal(newSViv(cmd)));
1285         lihash = newHV();
1286         if (lockinfo) {
1287                 (void) hv_store(lihash, "l_type",   6, newSViv(lockinfo->l_type), 0);
1288                 (void) hv_store(lihash, "l_whence", 8, newSViv(lockinfo->l_whence), 0);
1289 #ifdef PERL_HAS_64BITINT
1290                 sv = newSViv(lockinfo->l_start);
1291 #else
1292                 if (asprintf(&temp, "%llu", lockinfo->l_start) == -1)
1293                         croak("Memory allocation failure!");
1294                 sv = newSVpv(temp, 0);
1295                 free(temp);
1296 #endif
1297                 (void) hv_store(lihash, "l_start",  7, sv, 0);
1298 #ifdef PERL_HAS_64BITINT
1299                 sv = newSViv(lockinfo->l_len);
1300 #else
1301                 if (asprintf(&temp, "%llu", lockinfo->l_len) == -1)
1302                         croak("Memory allocation failure!");
1303                 sv = newSVpv(temp, 0);
1304                 free(temp);
1305 #endif
1306                 (void) hv_store(lihash, "l_len",    5, sv, 0);
1307                 (void) hv_store(lihash, "l_pid",    5, newSViv(lockinfo->l_pid), 0);
1308         }
1309         XPUSHs(sv_2mortal(newRV_noinc((SV*) lihash)));
1310         XPUSHs(FH_GETHANDLE(fi));
1311
1312         PUTBACK;
1313         rv = call_sv(MY_CXT.callback[35],G_SCALAR);
1314         SPAGAIN;
1315         rv = (rv ? POPi : 0);
1316         if (lockinfo && !rv) {
1317                 /* Need to copy back any altered values from the hash into
1318                  * the struct... */
1319                 SV **svp;
1320                 if ((svp = hv_fetch(lihash, "l_type",   6, 0)) != NULL)
1321                         lockinfo->l_type   = SvIV(*svp);
1322                 if ((svp = hv_fetch(lihash, "l_whence", 8, 0)) != NULL)
1323                         lockinfo->l_whence = SvIV(*svp);
1324                 if ((svp = hv_fetch(lihash, "l_start",  7, 0)) != NULL)
1325                         lockinfo->l_start  = SvNV(*svp);
1326                 if ((svp = hv_fetch(lihash, "l_len",    5, 0)) != NULL)
1327                         lockinfo->l_len    = SvNV(*svp);
1328                 if ((svp = hv_fetch(lihash, "l_pid",    5, 0)) != NULL)
1329                         lockinfo->l_pid    = SvIV(*svp);
1330         }
1331         FREETMPS;
1332         LEAVE;
1333         PUTBACK;
1334         DEBUGf("lock end: %i\n",rv);
1335         FUSE_CONTEXT_POST;
1336         return rv;
1337 }
1338
1339 int _PLfuse_utimens(const char *file, const struct timespec tv[2]) {
1340         int rv;
1341         FUSE_CONTEXT_PRE;
1342         DEBUGf("utimens begin\n");
1343         ENTER;
1344         SAVETMPS;
1345         PUSHMARK(SP);
1346         XPUSHs(sv_2mortal(newSVpv(file,0)));
1347         XPUSHs(tv ? sv_2mortal(newSVnv(tv[0].tv_sec + (tv[0].tv_nsec / 1000000000.0))) : &PL_sv_undef);
1348         XPUSHs(tv ? sv_2mortal(newSVnv(tv[1].tv_sec + (tv[1].tv_nsec / 1000000000.0))) : &PL_sv_undef);
1349         PUTBACK;
1350         rv = call_sv(MY_CXT.callback[36],G_SCALAR);
1351         SPAGAIN;
1352         rv = (rv ? POPi : 0);
1353         FREETMPS;
1354         LEAVE;
1355         PUTBACK;
1356         DEBUGf("utimens end: %i\n",rv);
1357         FUSE_CONTEXT_POST;
1358         return rv;
1359 }
1360
1361 int _PLfuse_bmap(const char *file, size_t blocksize, uint64_t *idx) {
1362         int rv;
1363 #ifndef PERL_HAS_64BITINT
1364         char *temp;
1365 #endif
1366         FUSE_CONTEXT_PRE;
1367         DEBUGf("bmap begin\n");
1368         ENTER;
1369         SAVETMPS;
1370         PUSHMARK(SP);
1371         XPUSHs(sv_2mortal(newSVpv(file,0)));
1372         XPUSHs(sv_2mortal(newSViv(blocksize)));
1373 #ifdef PERL_HAS_64BITINT
1374         XPUSHs(sv_2mortal(newSViv(*idx)));
1375 #else
1376         if (asprintf(&temp, "%llu", *idx) == -1)
1377                 croak("Memory allocation failure!");
1378         XPUSHs(sv_2mortal(newSVpv(temp, 0)));
1379         free(temp);
1380 #endif
1381         PUTBACK;
1382         rv = call_sv(MY_CXT.callback[37],G_ARRAY);
1383         SPAGAIN;
1384         if (rv > 0 && rv < 3) {
1385                 if (rv == 2)
1386                         *idx = POPn;
1387                 rv = POPi;
1388         }
1389         else {
1390                 fprintf(stderr, "bmap(): wrong number of values returned?\n");
1391                 rv = -ENOSYS;
1392         }
1393         FREETMPS;
1394         LEAVE;
1395         PUTBACK;
1396         DEBUGf("bmap end: %i\n",rv);
1397         FUSE_CONTEXT_POST;
1398         return rv;
1399 }
1400 #endif /* FUSE_VERSION >= 26 */
1401
1402 #if 0
1403 #if FUSE_VERSION >= 28
1404 int _PLfuse_ioctl(const char *file, int cmd, void *arg,
1405                   struct fuse_file_info *fi, unsigned int flags, void *data) {
1406         int rv;
1407         FUSE_CONTEXT_PRE;
1408         DEBUGf("ioctl begin\n");
1409         ENTER;
1410         SAVETMPS;
1411         PUSHMARK(SP);
1412         XPUSHs(sv_2mortal(newSVpv(file,0)));
1413         XPUSHs(sv_2mortal(newSViv(cmd)));
1414         XPUSHs(sv_2mortal(newSViv(flags)));
1415         if (_IOC_DIR(cmd) & _IOC_READ)
1416                 XPUSHs(sv_2mortal(newSVpvn(data, _IOC_SIZE(cmd))));
1417         else
1418                 XPUSHs(&PL_sv_undef);
1419         XPUSHs(FH_GETHANDLE(fi));
1420         PUTBACK;
1421         rv = call_sv(MY_CXT.callback[39],G_ARRAY);
1422         SPAGAIN;
1423         if (_IOC_DIR(cmd) & _IOC_WRITE) {
1424                 if (rv == 2) {
1425                         SV *sv = POPs;
1426                         unsigned int len;
1427                         char *rdata = SvPV(sv, len);
1428                         if (len > _IOC_SIZE(cmd)) {
1429                                 fprintf(stderr, "ioctl(): returned data was too large for data area\n");
1430                                 rv = -EFBIG;
1431                         }
1432                         else {
1433                                 memset(data, 0, _IOC_SIZE(cmd));
1434                                 memcpy(data, rdata, len);
1435                         }
1436
1437                         rv--;
1438                 }
1439                 else {
1440                         fprintf(stderr, "ioctl(): ioctl was a write op, but no data was returned from call?\n");
1441                         rv = -EFAULT;
1442                 }
1443         }
1444         if (rv > 0)
1445                 rv = POPi;
1446         FREETMPS;
1447         LEAVE;
1448         PUTBACK;
1449         DEBUGf("ioctl end: %i\n",rv);
1450         FUSE_CONTEXT_POST;
1451         return rv;
1452 }
1453
1454 int _PLfuse_poll(const char *file, struct fuse_file_info *fi,
1455                  struct fuse_pollhandle *ph, unsigned *reventsp) {
1456
1457 }
1458 #endif /* FUSE_VERSION >= 28 */
1459 #endif
1460
1461 struct fuse_operations _available_ops = {
1462 getattr:                _PLfuse_getattr,
1463 readlink:               _PLfuse_readlink,
1464 getdir:                 _PLfuse_getdir,
1465 mknod:                  _PLfuse_mknod,
1466 mkdir:                  _PLfuse_mkdir,
1467 unlink:                 _PLfuse_unlink,
1468 rmdir:                  _PLfuse_rmdir,
1469 symlink:                _PLfuse_symlink,
1470 rename:                 _PLfuse_rename,
1471 link:                   _PLfuse_link,
1472 chmod:                  _PLfuse_chmod,
1473 chown:                  _PLfuse_chown,
1474 truncate:               _PLfuse_truncate,
1475 utime:                  _PLfuse_utime,
1476 open:                   _PLfuse_open,
1477 read:                   _PLfuse_read,
1478 write:                  _PLfuse_write,
1479 statfs:                 _PLfuse_statfs,
1480 flush:                  _PLfuse_flush,
1481 release:                _PLfuse_release,
1482 fsync:                  _PLfuse_fsync,
1483 setxattr:               _PLfuse_setxattr,
1484 getxattr:               _PLfuse_getxattr,
1485 listxattr:              _PLfuse_listxattr,
1486 removexattr:            _PLfuse_removexattr,
1487 #if FUSE_VERSION >= 23
1488 opendir:                _PLfuse_opendir, 
1489 readdir:                _PLfuse_readdir,
1490 releasedir:             _PLfuse_releasedir,
1491 fsyncdir:               _PLfuse_fsyncdir,
1492 init:                   _PLfuse_init,
1493 destroy:                _PLfuse_destroy,
1494 #endif /* FUSE_VERSION >= 23 */
1495 #if FUSE_VERSION >= 25
1496 access:                 _PLfuse_access,
1497 create:                 _PLfuse_create,
1498 ftruncate:              _PLfuse_ftruncate,
1499 fgetattr:               _PLfuse_fgetattr,
1500 #endif /* FUSE_VERSION >= 25 */
1501 #if FUSE_VERSION >= 26
1502 lock:                   _PLfuse_lock,
1503 utimens:                _PLfuse_utimens,
1504 bmap:                   _PLfuse_bmap,
1505 #endif /* FUSE_VERSION >= 26 */
1506 #if 0
1507 #if FUSE_VERSION >= 28
1508 ioctl:                  _PLfuse_ioctl,
1509 poll:                   _PLfuse_poll,
1510 #endif /* FUSE_VERSION >= 28 */
1511 #endif
1512 };
1513
1514 MODULE = Fuse           PACKAGE = Fuse
1515 PROTOTYPES: DISABLE
1516
1517 BOOT:
1518         MY_CXT_INIT;
1519         MY_CXT.self = aTHX;
1520
1521 void
1522 CLONE(...)
1523         PREINIT:
1524                 int i;
1525                 dTHX;
1526         CODE:
1527                 MY_CXT_CLONE;
1528                 tTHX parent = MY_CXT.self;
1529                 MY_CXT.self = my_perl;
1530 #if (PERL_VERSION < 10) || (PERL_VERSION == 10 && PERL_SUBVERSION <= 0)
1531                 /* CLONE entered without a pointer table, so we can't safely clone static data */
1532                 if(!PL_ptr_table) {
1533                         for(i=0;i<N_CALLBACKS;i++) {
1534                                 MY_CXT.callback[i] = NULL;
1535                         }
1536                         MY_CXT.handles = newHV();
1537                 } else
1538 #endif
1539                 {
1540                         CLONE_PARAMS *clone_param;
1541 #if (PERL_VERSION > 13) || (PERL_VERSION == 13 && PERL_SUBVERSION >= 2)
1542                         clone_param = clone_params_new(parent, aTHX);
1543 #else
1544                         CLONE_PARAMS raw_param;
1545                         raw_param.flags = 0;
1546                         raw_param.proto_perl = parent;
1547                         raw_param.stashes = (AV*)sv_2mortal((SV*)newAV());
1548                         clone_param = &raw_param;
1549 #endif
1550                         for(i=0;i<N_CALLBACKS;i++) {
1551                                 MY_CXT.callback[i] = sv_dup(MY_CXT.callback[i], clone_param);
1552                         }
1553                         MY_CXT.handles = (HV*)sv_dup((SV*)MY_CXT.handles, clone_param);
1554 #if (PERL_VERSION > 13) || (PERL_VERSION == 13 && PERL_SUBVERSION >= 2)
1555                         clone_params_del(clone_param);
1556 #endif
1557                 }
1558
1559 SV*
1560 fuse_get_context()
1561         PREINIT:
1562         struct fuse_context *fc;
1563         CODE:
1564         fc = fuse_get_context();
1565         if(fc) {
1566                 HV *hash = newHV();
1567                 (void) hv_store(hash, "uid",   3, newSViv(fc->uid), 0);
1568                 (void) hv_store(hash, "gid",   3, newSViv(fc->gid), 0);
1569                 (void) hv_store(hash, "pid",   3, newSViv(fc->pid), 0);
1570                 if (fc->private_data)
1571                         (void) hv_store(hash, "private", 7, fc->private_data, 0);
1572 #if FUSE_VERSION >= 28
1573                 (void) hv_store(hash, "umask", 5, newSViv(fc->umask), 0);
1574 #endif /* FUSE_VERSION >= 28 */
1575                 RETVAL = newRV_noinc((SV*)hash);
1576         } else {
1577                 XSRETURN_UNDEF;
1578         }
1579         OUTPUT:
1580         RETVAL
1581
1582 SV *
1583 fuse_version()
1584         CODE:
1585         RETVAL = newSVpvf("%d.%d", FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION);
1586         OUTPUT:
1587         RETVAL
1588
1589 #ifndef __FreeBSD__
1590 SV *
1591 XATTR_CREATE()
1592         CODE:
1593         RETVAL = newSViv(XATTR_CREATE);
1594         OUTPUT:
1595         RETVAL
1596
1597 SV *
1598 XATTR_REPLACE()
1599         CODE:
1600         RETVAL = newSViv(XATTR_REPLACE);
1601         OUTPUT:
1602         RETVAL
1603
1604 #endif
1605
1606 void
1607 perl_fuse_main(...)
1608         PREINIT:
1609         struct fuse_operations fops;
1610         int i, debug;
1611         char *mountpoint;
1612         char *mountopts;
1613         struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
1614         struct fuse_chan *fc;
1615         dMY_CXT;
1616         INIT:
1617         if(items != N_CALLBACKS + 5) {
1618                 fprintf(stderr,"Perl<->C inconsistency or internal error\n");
1619                 XSRETURN_UNDEF;
1620         }
1621         memset(&fops, 0, sizeof(struct fuse_operations));
1622         CODE:
1623         debug = SvIV(ST(0));
1624         MY_CXT.threaded = SvIV(ST(1));
1625         MY_CXT.handles = (HV*)(sv_2mortal((SV*)(newHV())));
1626         if(MY_CXT.threaded) {
1627 #ifdef FUSE_USE_ITHREADS
1628                 master_interp = aTHX;
1629                 MUTEX_INIT(&MY_CXT.mutex);
1630                 SvSHARE((SV*)(MY_CXT.handles));
1631 #else
1632                 fprintf(stderr,"FUSE warning: Your script has requested multithreaded "
1633                                "mode, but your perl was not built with a supported "
1634                                "thread model. Threads are disabled.\n");
1635                 MY_CXT.threaded = 0;
1636 #endif
1637         }
1638         mountpoint = SvPV_nolen(ST(2));
1639         mountopts = SvPV_nolen(ST(3));
1640 #if FUSE_VERSION >= 28
1641         fops.flag_nullpath_ok = SvIV(ST(4));
1642 #endif /* FUSE_VERSION >= 28 */
1643         for(i=0;i<N_CALLBACKS;i++) {
1644                 SV *var = ST(i+5);
1645                 /* allow symbolic references, or real code references. */
1646                 if(SvOK(var) && (SvPOK(var) || (SvROK(var) && SvTYPE(SvRV(var)) == SVt_PVCV))) {
1647                         void **tmp1 = (void**)&_available_ops, **tmp2 = (void**)&fops;
1648                         /* Dirty hack, to keep anything from overwriting the
1649                          * flag area with a pointer. There should never be
1650                          * anything passed as 'junk', but this prevents
1651                          * someone from doing it and screwing things up... */
1652                         if (i == 38)
1653                                 continue;
1654                         tmp2[i] = tmp1[i];
1655                         MY_CXT.callback[i] = var;
1656                 } else if(SvOK(var)) {
1657                         croak("invalid callback (%i) passed to perl_fuse_main "
1658                               "(%s is not a string, code ref, or undef).\n",
1659                               i+5,SvPVbyte_nolen(var));
1660                 } else {
1661                         MY_CXT.callback[i] = NULL;
1662                 }
1663         }
1664         /*
1665          * XXX: What comes here is just a ridiculous use of the option parsing API
1666          * to hack on compatibility with other parts of the new API. First and
1667          * foremost, real C argc/argv would be good to get at...
1668          */
1669         if ((mountopts || debug) && fuse_opt_add_arg(&args, "") == -1) {
1670                 fuse_opt_free_args(&args);
1671                 croak("out of memory\n");
1672         }
1673         if (mountopts && strcmp("", mountopts) &&
1674              (fuse_opt_add_arg(&args, "-o") == -1 ||
1675              fuse_opt_add_arg(&args, mountopts) == -1)) {
1676                 fuse_opt_free_args(&args);
1677                 croak("out of memory\n");
1678         }
1679         if (debug && fuse_opt_add_arg(&args, "-d") == -1) {
1680                 fuse_opt_free_args(&args);
1681                 croak("out of memory\n");
1682         }
1683         fc = fuse_mount(mountpoint,&args);
1684         if (fc == NULL)
1685                 croak("could not mount fuse filesystem!\n");
1686 #ifndef __NetBSD__
1687         if(MY_CXT.threaded) {
1688                 fuse_loop_mt(fuse_new(fc,&args,&fops,sizeof(fops),NULL));
1689         } else
1690 #endif
1691                 fuse_loop(fuse_new(fc,&args,&fops,sizeof(fops),NULL));
1692         fuse_unmount(mountpoint,fc);
1693         fuse_opt_free_args(&args);