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