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