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