Remove a couple of unnecessary '#if FUSE_VERSION >= 24' blocks in the
[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                 if ((svp = hv_fetch(fihash, "direct_io",    9, 0)) != NULL)
515                         fi->direct_io   = SvIV(*svp);
516                 if ((svp = hv_fetch(fihash, "keep_cache",  10, 0)) != NULL)
517                         fi->keep_cache  = SvIV(*svp);
518 #endif
519 #if FUSE_VERSION >= 29
520                 if ((svp = hv_fetch(fihash, "nonseekable", 11, 0)) != NULL)
521                         fi->nonseekable = SvIV(*svp);
522 #endif
523         }
524         FREETMPS;
525         LEAVE;
526         PUTBACK;
527         DEBUGf("open end: %i\n",rv);
528         FUSE_CONTEXT_POST;
529         return rv;
530 }
531
532 int _PLfuse_read (const char *file, char *buf, size_t buflen, off_t off,
533                 struct fuse_file_info *fi) {
534         int rv;
535 #ifndef PERL_HAS_64BITINT
536         char *temp;
537 #endif
538         FUSE_CONTEXT_PRE;
539         DEBUGf("read begin\n");
540         ENTER;
541         SAVETMPS;
542         PUSHMARK(SP);
543         XPUSHs(file ? sv_2mortal(newSVpv(file,0)) : &PL_sv_undef);
544         XPUSHs(sv_2mortal(newSViv(buflen)));
545 #ifdef PERL_HAS_64BITINT
546         XPUSHs(sv_2mortal(newSViv(off)));
547 #else
548         if (asprintf(&temp, "%llu", off) == -1)
549                 croak("Memory allocation failure!");
550         XPUSHs(sv_2mortal(newSVpv(temp, 0)));
551         free(temp);
552 #endif
553         XPUSHs(FH_GETHANDLE(fi));
554         PUTBACK;
555         rv = call_sv(MY_CXT.callback[15],G_SCALAR);
556         SPAGAIN;
557         if(!rv)
558                 rv = -ENOENT;
559         else {
560                 SV *mysv = POPs;
561                 if(SvTYPE(mysv) == SVt_NV || SvTYPE(mysv) == SVt_IV)
562                         rv = SvIV(mysv);
563                 else {
564                         if(SvPOK(mysv)) {
565                                 rv = SvCUR(mysv);
566                         } else {
567                                 rv = 0;
568                         }
569                         if(rv > buflen)
570                                 croak("read() handler returned more than buflen! (%i > %i)",rv,buflen);
571                         if(rv)
572                                 memcpy(buf,SvPV_nolen(mysv),rv);
573                 }
574         }
575         FREETMPS;
576         LEAVE;
577         PUTBACK;
578         DEBUGf("read end: %i\n",rv);
579         FUSE_CONTEXT_POST;
580         return rv;
581 }
582
583 int _PLfuse_write (const char *file, const char *buf, size_t buflen, off_t off, struct fuse_file_info *fi) {
584         int rv;
585 #ifndef PERL_HAS_64BITINT
586         char *temp;
587 #endif
588         FUSE_CONTEXT_PRE;
589         DEBUGf("write begin\n");
590         ENTER;
591         SAVETMPS;
592         PUSHMARK(SP);
593         XPUSHs(file ? sv_2mortal(newSVpv(file,0)) : &PL_sv_undef);
594         XPUSHs(sv_2mortal(newSVpvn(buf,buflen)));
595 #ifdef PERL_HAS_64BITINT
596         XPUSHs(sv_2mortal(newSViv(off)));
597 #else
598         if (asprintf(&temp, "%llu", off) == -1)
599                 croak("Memory allocation failure!");
600         XPUSHs(sv_2mortal(newSVpv(temp, 0)));
601         free(temp);
602 #endif
603         XPUSHs(FH_GETHANDLE(fi));
604         PUTBACK;
605         rv = call_sv(MY_CXT.callback[16],G_SCALAR);
606         SPAGAIN;
607         rv = (rv ? POPi : 0);
608         FREETMPS;
609         LEAVE;
610         PUTBACK;
611         DEBUGf("write end: %i\n",rv);
612         FUSE_CONTEXT_POST;
613         return rv;
614 }
615
616 int _PLfuse_statfs (const char *file, struct statvfs *st) {
617         int rv;
618         FUSE_CONTEXT_PRE;
619         DEBUGf("statfs begin\n");
620         ENTER;
621         SAVETMPS;
622         PUSHMARK(SP);
623         PUTBACK;
624         rv = call_sv(MY_CXT.callback[17],G_ARRAY);
625         SPAGAIN;
626         DEBUGf("statfs got %i params\n",rv);
627         if(rv == 6 || rv == 7) {
628                 st->f_bsize     = POPi;
629                 st->f_bfree     = POPi;
630                 st->f_blocks    = POPi;
631                 st->f_ffree     = POPi;
632                 st->f_files     = POPi;
633                 st->f_namemax   = POPi;
634                 /* zero and fill-in other */
635                 st->f_fsid = 0;
636                 st->f_frsize = 4096;
637                 st->f_flag = 0;
638                 st->f_bavail = st->f_bfree;
639                 st->f_favail = st->f_ffree;
640
641                 if(rv == 7)
642                         rv = POPi;
643                 else
644                         rv = 0;
645         } else
646         if(rv > 1)
647                 croak("inappropriate number of returned values from statfs");
648         else
649         if(rv)
650                 rv = POPi;
651         else
652                 rv = -ENOSYS;
653         FREETMPS;
654         LEAVE;
655         PUTBACK;
656         DEBUGf("statfs end: %i\n",rv);
657         FUSE_CONTEXT_POST;
658         return rv;
659 }
660
661 int _PLfuse_flush (const char *file, struct fuse_file_info *fi) {
662         int rv;
663         FUSE_CONTEXT_PRE;
664         DEBUGf("flush begin\n");
665         ENTER;
666         SAVETMPS;
667         PUSHMARK(SP);
668         XPUSHs(file ? sv_2mortal(newSVpv(file,0)) : &PL_sv_undef);
669         XPUSHs(FH_GETHANDLE(fi));
670         PUTBACK;
671         rv = call_sv(MY_CXT.callback[18],G_SCALAR);
672         SPAGAIN;
673         rv = (rv ? POPi : 0);
674         FREETMPS;
675         LEAVE;
676         PUTBACK;
677         DEBUGf("flush end: %i\n",rv);
678         FUSE_CONTEXT_POST;
679         return rv;
680 }
681
682 int _PLfuse_release (const char *file, struct fuse_file_info *fi) {
683         int rv;
684         int flags = fi->flags;
685         FUSE_CONTEXT_PRE;
686         DEBUGf("release begin\n");
687         ENTER;
688         SAVETMPS;
689         PUSHMARK(SP);
690         XPUSHs(file ? sv_2mortal(newSVpv(file,0)) : &PL_sv_undef);
691         XPUSHs(sv_2mortal(newSViv(flags)));
692         XPUSHs(FH_GETHANDLE(fi));
693         PUTBACK;
694         rv = call_sv(MY_CXT.callback[19],G_SCALAR);
695         SPAGAIN;
696         rv = (rv ? POPi : 0);
697         FH_RELEASEHANDLE(fi);
698         FREETMPS;
699         LEAVE;
700         PUTBACK;
701         DEBUGf("release end: %i\n",rv);
702         FUSE_CONTEXT_POST;
703         return rv;
704 }
705
706 int _PLfuse_fsync (const char *file, int datasync, struct fuse_file_info *fi) {
707         int rv;
708         int flags = fi->flags;
709         FUSE_CONTEXT_PRE;
710         DEBUGf("fsync begin\n");
711         ENTER;
712         SAVETMPS;
713         PUSHMARK(SP);
714         XPUSHs(file ? sv_2mortal(newSVpv(file,0)) : &PL_sv_undef);
715         XPUSHs(sv_2mortal(newSViv(flags)));
716         XPUSHs(FH_GETHANDLE(fi));
717         PUTBACK;
718         rv = call_sv(MY_CXT.callback[20],G_SCALAR);
719         SPAGAIN;
720         rv = (rv ? POPi : 0);
721         FREETMPS;
722         LEAVE;
723         PUTBACK;
724         DEBUGf("fsync end: %i\n",rv);
725         FUSE_CONTEXT_POST;
726         return rv;
727 }
728
729 int _PLfuse_setxattr (const char *file, const char *name, const char *buf, size_t buflen, int flags) {
730         int rv;
731         FUSE_CONTEXT_PRE;
732         DEBUGf("setxattr begin\n");
733         ENTER;
734         SAVETMPS;
735         PUSHMARK(SP);
736         XPUSHs(sv_2mortal(newSVpv(file,0)));
737         XPUSHs(sv_2mortal(newSVpv(name,0)));
738         XPUSHs(sv_2mortal(newSVpvn(buf,buflen)));
739         XPUSHs(sv_2mortal(newSViv(flags)));
740         PUTBACK;
741         rv = call_sv(MY_CXT.callback[21],G_SCALAR);
742         SPAGAIN;
743         rv = (rv ? POPi : 0);
744         FREETMPS;
745         LEAVE;
746         PUTBACK;
747         DEBUGf("setxattr end: %i\n",rv);
748         FUSE_CONTEXT_POST;
749         return rv;
750 }
751
752 int _PLfuse_getxattr (const char *file, const char *name, char *buf, size_t buflen) {
753         int rv;
754         FUSE_CONTEXT_PRE;
755         DEBUGf("getxattr begin\n");
756         ENTER;
757         SAVETMPS;
758         PUSHMARK(SP);
759         XPUSHs(sv_2mortal(newSVpv(file,0)));
760         XPUSHs(sv_2mortal(newSVpv(name,0)));
761         PUTBACK;
762         rv = call_sv(MY_CXT.callback[22],G_SCALAR);
763         SPAGAIN;
764         if(!rv)
765                 rv = -ENOENT;
766         else {
767                 SV *mysv = POPs;
768
769                 rv = 0;
770                 if(SvTYPE(mysv) == SVt_NV || SvTYPE(mysv) == SVt_IV)
771                         rv = SvIV(mysv);
772                 else {
773                         if(SvPOK(mysv)) {
774                                 rv = SvCUR(mysv);
775                         } else {
776                                 rv = 0;
777                         }
778                         if ((rv > 0) && (buflen > 0))
779                         {
780                                 if(rv > buflen)
781                                         rv = -ERANGE;
782                                 else
783                                         memcpy(buf,SvPV_nolen(mysv),rv);
784                         }
785                 }
786         }
787         FREETMPS;
788         LEAVE;
789         PUTBACK;
790         DEBUGf("getxattr end: %i\n",rv);
791         FUSE_CONTEXT_POST;
792         return rv;
793 }
794
795 int _PLfuse_listxattr (const char *file, char *list, size_t size) {
796         int prv, rv;
797         FUSE_CONTEXT_PRE;
798         DEBUGf("listxattr begin\n");
799         ENTER;
800         SAVETMPS;
801         PUSHMARK(SP);
802         XPUSHs(sv_2mortal(newSVpv(file,0)));
803         PUTBACK;
804         prv = call_sv(MY_CXT.callback[23],G_ARRAY);
805         SPAGAIN;
806         if(!prv)
807                 rv = -ENOENT;
808         else {
809
810                 char *p = list;
811                 int spc = size;
812                 int total_len = 0;
813
814                 rv = POPi;
815                 prv--;
816
817                 /* Always nul terminate */
818                 if (list && (size > 0))
819                         list[0] = '\0';
820
821                 while (prv > 0)
822                 {
823                         SV *mysv = POPs;
824                         prv--;
825
826                         if (SvPOK(mysv)) {
827                                 /* Copy nul too */
828                                 int s = SvCUR(mysv) + 1;
829                                 total_len += s;
830
831                                 if (p && (size > 0) && (spc >= s))
832                                 {
833                                         memcpy(p,SvPV_nolen(mysv),s);
834                                         p += s;
835                                         spc -= s;
836                                 }
837                         }
838                 }
839
840                 /*
841                  * If the Perl returned an error, return that.
842                  * Otherwise check that the buffer was big enough.
843                  */
844                 if (rv == 0)
845                 {
846                         rv = total_len;
847                         if ((size > 0) && (size < total_len))
848                                 rv = -ERANGE;
849                 }
850         }
851         FREETMPS;
852         LEAVE;
853         PUTBACK;
854         DEBUGf("listxattr end: %i\n",rv);
855         FUSE_CONTEXT_POST;
856         return rv;
857 }
858
859 int _PLfuse_removexattr (const char *file, const char *name) {
860         int rv;
861         FUSE_CONTEXT_PRE;
862         DEBUGf("removexattr begin\n");
863         ENTER;
864         SAVETMPS;
865         PUSHMARK(SP);
866         XPUSHs(sv_2mortal(newSVpv(file,0)));
867         XPUSHs(sv_2mortal(newSVpv(name,0)));
868         PUTBACK;
869         rv = call_sv(MY_CXT.callback[24],G_SCALAR);
870         SPAGAIN;
871         rv = (rv ? POPi : 0);
872         FREETMPS;
873         LEAVE;
874         PUTBACK;
875         DEBUGf("removexattr end: %i\n",rv);
876         FUSE_CONTEXT_POST;
877         return rv;
878 }
879
880 #if FUSE_VERSION >= 23
881 int _PLfuse_opendir(const char *file, struct fuse_file_info *fi) {
882         int rv;
883         FUSE_CONTEXT_PRE;
884         DEBUGf("opendir begin\n");
885         ENTER;
886         SAVETMPS;
887         PUSHMARK(SP);
888         XPUSHs(sv_2mortal(newSVpv(file,0)));
889         fi->fh = 0; /* Ensure it starts with 0 - important if they don't set it */
890         PUTBACK;
891         rv = call_sv(MY_CXT.callback[25], G_ARRAY);
892         SPAGAIN;
893         if (rv) {
894                 if (rv > 1) {
895                         FH_STOREHANDLE(fi, POPs);
896                 }
897                 rv = POPi;
898         } else
899                 rv = 0;
900         FREETMPS;
901         LEAVE;
902         PUTBACK;
903         DEBUGf("opendir end: %i\n",rv);
904         FUSE_CONTEXT_POST;
905         return rv;
906
907 }
908
909 int _PLfuse_readdir(const char *file, void *dirh, fuse_fill_dir_t dirfil,
910                     off_t off, struct fuse_file_info *fi) {
911         int prv = 0, rv;
912         SV *sv, **svp, **swp;
913         AV *av, *av2;
914         struct stat st;
915         bool st_filled = 0;
916 #ifndef PERL_HAS_64BITINT
917         char *temp;
918 #endif
919         FUSE_CONTEXT_PRE;
920         DEBUGf("readdir begin\n");
921         ENTER;
922         SAVETMPS;
923         PUSHMARK(SP);
924         XPUSHs(file ? sv_2mortal(newSVpv(file,0)) : &PL_sv_undef);
925 #ifdef PERL_HAS_64BITINT
926         XPUSHs(sv_2mortal(newSViv(off)));
927 #else
928         if (asprintf(&temp, "%llu", off) == -1)
929                 croak("Memory allocation failure!");
930         XPUSHs(sv_2mortal(newSVpv(temp, 0)));
931         free(temp);
932 #endif
933         XPUSHs(FH_GETHANDLE(fi));
934         PUTBACK;
935         prv = call_sv(MY_CXT.callback[26],G_ARRAY);
936         SPAGAIN;
937         if (prv) {
938                 /* Should yield the bottom of the current stack... */
939                 swp = &TOPs - prv + 1;
940                 rv = POPi;
941                 memset(&st, 0, sizeof(struct stat));
942                 /* Sort of a hack to walk the stack in order, instead of reverse
943                  * order - trying to explain to potential users why they need to
944                  * reverse the order of this array would be confusing, at best. */
945                 while (swp <= &TOPs) {
946                         sv = *(swp++);
947                         if (!SvROK(sv) && SvPOK(sv))
948                         /* Just a bare SV (probably a string; hopefully a string) */
949                                 dirfil(dirh, SvPVx_nolen(sv), NULL, 0);
950                         else if (SvROK(sv) && SvTYPE(av = (AV *)SvRV(sv)) == SVt_PVAV) {
951                                 if (av_len(av) >= 2) {
952                                         /* The third element of the array should be the args that
953                                          * would otherwise go to getattr(); a lot of filesystems
954                                          * will, or at least can, return that info as part of the
955                                          * enumeration process... */
956                                         svp = av_fetch(av, 2, FALSE);
957                                         if (SvROK(*svp) &&
958                                                         SvTYPE(av2 = (AV *)SvRV(*svp)) == SVt_PVAV &&
959                                                         av_len(av2) == 12) {
960                                                 st.st_dev     = SvIV(*(av_fetch(av2,  0, FALSE)));
961                                                 st.st_ino     = SvIV(*(av_fetch(av2,  1, FALSE)));
962                                                 st.st_mode    = SvIV(*(av_fetch(av2,  2, FALSE)));
963                                                 st.st_nlink   = SvIV(*(av_fetch(av2,  3, FALSE)));
964                                                 st.st_uid     = SvIV(*(av_fetch(av2,  4, FALSE)));
965                                                 st.st_gid     = SvIV(*(av_fetch(av2,  5, FALSE)));
966                                                 st.st_rdev    = SvIV(*(av_fetch(av2,  6, FALSE)));
967                                                 st.st_size    = SvNV(*(av_fetch(av2,  7, FALSE)));
968                                                 st.st_atime   = SvIV(*(av_fetch(av2,  8, FALSE)));
969                                                 st.st_mtime   = SvIV(*(av_fetch(av2,  9, FALSE)));
970                                                 st.st_ctime   = SvIV(*(av_fetch(av2, 10, FALSE)));
971                                                 st.st_blksize = SvIV(*(av_fetch(av2, 11, FALSE)));
972                                                 st.st_blocks  = SvIV(*(av_fetch(av2, 12, FALSE)));
973                                                 st_filled = 1;
974                                         }
975                                         else
976                                                 fprintf(stderr,"Extra SV didn't appear to be correct, ignoring\n");
977                                         /* For now if the element isn't what we want, just
978                                          * quietly ignore it... */
979                                 }
980                                 if (av_len(av) >= 1) {
981                                         char *entryname = SvPVx_nolen(*(av_fetch(av, 1, FALSE)));
982                                         off_t elemnum = SvNV(*(av_fetch(av, 0, FALSE)));
983                                         dirfil(dirh, entryname, st_filled ? &st : NULL, elemnum);
984                                 }
985                                 if (st_filled) {
986                                         memset(&st, 0, sizeof(struct stat));
987                                         st_filled = 0;
988                                 }
989                         }
990                         else
991                                 fprintf(stderr, "ERROR: Unknown entry passed via readdir\n");
992                 }
993         } else {
994                 fprintf(stderr,"readdir() handler returned nothing!\n");
995                 rv = -ENOSYS;
996         }
997         FREETMPS;
998         LEAVE;
999         PUTBACK;
1000         DEBUGf("readdir end: %i\n",rv);
1001         FUSE_CONTEXT_POST;
1002         return rv;
1003 }
1004
1005 int _PLfuse_releasedir(const char *file, struct fuse_file_info *fi) {
1006         int rv;
1007         FUSE_CONTEXT_PRE;
1008         DEBUGf("releasedir begin\n");
1009         ENTER;
1010         SAVETMPS;
1011         PUSHMARK(SP);
1012         XPUSHs(file ? sv_2mortal(newSVpv(file,0)) : &PL_sv_undef);
1013         XPUSHs(FH_GETHANDLE(fi));
1014         PUTBACK;
1015         rv = call_sv(MY_CXT.callback[27], G_SCALAR);
1016         SPAGAIN;
1017         rv = (rv ? POPi : 0);
1018         FH_RELEASEHANDLE(fi);
1019         FREETMPS;
1020         LEAVE;
1021         PUTBACK;
1022         DEBUGf("releasedir end: %i\n",rv);
1023         FUSE_CONTEXT_POST;
1024         return rv;
1025 }
1026
1027 int _PLfuse_fsyncdir(const char *file, int datasync,
1028                      struct fuse_file_info *fi) {
1029         int rv;
1030         FUSE_CONTEXT_PRE;
1031         DEBUGf("fsyncdir begin\n");
1032         ENTER;
1033         SAVETMPS;
1034         PUSHMARK(SP);
1035         XPUSHs(file ? sv_2mortal(newSVpv(file,0)) : &PL_sv_undef);
1036         XPUSHs(sv_2mortal(newSViv(datasync)));
1037         XPUSHs(FH_GETHANDLE(fi));
1038         PUTBACK;
1039         rv = call_sv(MY_CXT.callback[28], G_SCALAR);
1040         SPAGAIN;
1041         rv = (rv ? POPi : 0);
1042         FREETMPS;
1043         LEAVE;
1044         PUTBACK;
1045         DEBUGf("fsyncdir end: %i\n",rv);
1046         FUSE_CONTEXT_POST;
1047         return rv;
1048 }
1049
1050 #if FUSE_VERSION >= 26
1051 void *_PLfuse_init(struct fuse_conn_info *fc)
1052 #else /* FUSE_VERSION < 26 */
1053 void *_PLfuse_init(void)
1054 #endif /* FUSE_VERSION >= 26 */
1055 {
1056         void *rv = NULL;
1057         int prv;
1058         FUSE_CONTEXT_PRE;
1059         DEBUGf("init begin\n");
1060         ENTER;
1061         SAVETMPS;
1062         PUSHMARK(SP);
1063         PUTBACK;
1064         prv = call_sv(MY_CXT.callback[29], G_SCALAR);
1065         SPAGAIN;
1066         if (prv) {
1067                 rv = POPs;
1068                 if (rv == &PL_sv_undef)
1069                         rv = NULL;
1070                 else
1071                         rv = SvREFCNT_inc((SV *)rv);
1072         }
1073         FREETMPS;
1074         LEAVE;
1075         PUTBACK;
1076         DEBUGf("init end: %p\n", rv);
1077         FUSE_CONTEXT_POST;
1078         return rv;
1079 }
1080
1081 void _PLfuse_destroy(void *private_data) {
1082         FUSE_CONTEXT_PRE;
1083         DEBUGf("destroy begin\n");
1084         ENTER;
1085         SAVETMPS;
1086         PUSHMARK(SP);
1087         XPUSHs(private_data ? (SV *)private_data : &PL_sv_undef);
1088         PUTBACK;
1089         call_sv(MY_CXT.callback[30], G_VOID);
1090         SPAGAIN;
1091         if (private_data)
1092                 SvREFCNT_dec((SV *)private_data);
1093         FREETMPS;
1094         LEAVE;
1095         PUTBACK;
1096         DEBUGf("init end\n");
1097         FUSE_CONTEXT_POST;
1098 }
1099 #endif /* FUSE_VERSION >= 23 */
1100
1101 #if FUSE_VERSION >= 25
1102 int _PLfuse_access(const char *file, int mask) {
1103         int rv;
1104         FUSE_CONTEXT_PRE;
1105         DEBUGf("access begin\n");
1106         ENTER;
1107         SAVETMPS;
1108         PUSHMARK(SP);
1109         XPUSHs(sv_2mortal(newSVpv(file,0)));
1110         XPUSHs(sv_2mortal(newSViv(mask)));
1111         PUTBACK;
1112         rv = call_sv(MY_CXT.callback[31], G_SCALAR);
1113         SPAGAIN;
1114         rv = (rv ? POPi : 0);
1115         FREETMPS;
1116         LEAVE;
1117         PUTBACK;
1118         DEBUGf("access end: %d\n", rv);
1119         FUSE_CONTEXT_POST;
1120         return rv;
1121 }
1122
1123 int _PLfuse_create(const char *file, mode_t mode, struct fuse_file_info *fi) {
1124         int rv;
1125         HV *fihash;
1126         FUSE_CONTEXT_PRE;
1127         DEBUGf("create begin\n");
1128         ENTER;
1129         SAVETMPS;
1130         PUSHMARK(SP);
1131         XPUSHs(sv_2mortal(newSVpv(file,0)));
1132         XPUSHs(sv_2mortal(newSViv(mode)));
1133         XPUSHs(sv_2mortal(newSViv(fi->flags)));
1134         fi->fh = 0; /* Ensure it starts with 0 - important if they don't set it */
1135         /* Create a hashref containing the details from fi
1136          * which we can look at or modify.
1137          */
1138         fihash = newHV();
1139         (void) hv_store(fihash, "direct_io",    9, newSViv(fi->direct_io),   0);
1140         (void) hv_store(fihash, "keep_cache",  10, newSViv(fi->keep_cache),  0);
1141 #if FUSE_VERSION >= 29
1142         (void) hv_store(fihash, "nonseekable", 11, newSViv(fi->nonseekable), 0);
1143 #endif
1144         XPUSHs(sv_2mortal(newRV_noinc((SV*) fihash)));
1145         /* All hashref things done */
1146
1147         PUTBACK;
1148         rv = call_sv(MY_CXT.callback[32], G_ARRAY);
1149         SPAGAIN;
1150         if (rv) {
1151                 if (rv > 1) {
1152                         FH_STOREHANDLE(fi,POPs);
1153                 }
1154                 rv = POPi;
1155         }
1156         else {
1157                 fprintf(stderr, "create() handler returned nothing!\n");
1158                 rv = -ENOSYS;
1159         }
1160         if (rv == 0) {
1161                 /* Success, so copy the file handle which they returned */
1162                 SV **svp;
1163                 if ((svp = hv_fetch(fihash, "direct_io",    9, 0)) != NULL)
1164                         fi->direct_io   = SvIV(*svp);
1165                 if ((svp = hv_fetch(fihash, "keep_cache",  10, 0)) != NULL)
1166                         fi->keep_cache  = SvIV(*svp);
1167 #if FUSE_VERSION >= 29
1168                 if ((svp = hv_fetch(fihash, "nonseekable", 11, 0)) != NULL)
1169                         fi->nonseekable = SvIV(*svp);
1170 #endif
1171         }
1172         FREETMPS;
1173         LEAVE;
1174         PUTBACK;
1175         DEBUGf("create end: %d\n",rv);
1176         FUSE_CONTEXT_POST;
1177         return rv;
1178 }
1179
1180 int _PLfuse_ftruncate(const char *file, off_t off, struct fuse_file_info *fi) {
1181         int rv;
1182 #ifndef PERL_HAS_64BITINT
1183         char *temp;
1184 #endif
1185         FUSE_CONTEXT_PRE;
1186         DEBUGf("ftruncate begin\n");
1187         ENTER;
1188         SAVETMPS;
1189         PUSHMARK(SP);
1190         XPUSHs(file ? sv_2mortal(newSVpv(file,0)) : &PL_sv_undef);
1191 #ifdef PERL_HAS_64BITINT
1192         XPUSHs(sv_2mortal(newSViv(off)));
1193 #else
1194         if (asprintf(&temp, "%llu", off) == -1)
1195                 croak("Memory allocation failure!");
1196         XPUSHs(sv_2mortal(newSVpv(temp, 0)));
1197         free(temp);
1198 #endif
1199         XPUSHs(FH_GETHANDLE(fi));
1200         PUTBACK;
1201         rv = call_sv(MY_CXT.callback[33],G_SCALAR);
1202         SPAGAIN;
1203         rv = (rv ? POPi : 0);
1204         FREETMPS;
1205         LEAVE;
1206         PUTBACK;
1207         DEBUGf("ftruncate end: %i\n",rv);
1208         FUSE_CONTEXT_POST;
1209         return rv;
1210 }
1211
1212 int _PLfuse_fgetattr(const char *file, struct stat *result,
1213                      struct fuse_file_info *fi) {
1214         int rv;
1215         FUSE_CONTEXT_PRE;
1216         DEBUGf("fgetattr begin: %s\n",file);
1217         ENTER;
1218         SAVETMPS;
1219         PUSHMARK(SP);
1220         XPUSHs(file ? sv_2mortal(newSVpv(file,0)) : &PL_sv_undef);
1221         XPUSHs(FH_GETHANDLE(fi));
1222         PUTBACK;
1223         rv = call_sv(MY_CXT.callback[34],G_ARRAY);
1224         SPAGAIN;
1225         if(rv != 13) {
1226                 if(rv > 1) {
1227                         fprintf(stderr,"inappropriate number of returned values from getattr\n");
1228                         rv = -ENOSYS;
1229                 } else if(rv)
1230                         rv = POPi;
1231                 else
1232                         rv = -ENOENT;
1233         } else {
1234                 result->st_blocks = POPi;
1235                 result->st_blksize = POPi;
1236                 result->st_ctime = POPi;
1237                 result->st_mtime = POPi;
1238                 result->st_atime = POPi;
1239                 result->st_size = POPn; // we pop double here to support files larger than 4Gb (long limit)
1240                 result->st_rdev = POPi;
1241                 result->st_gid = POPi;
1242                 result->st_uid = POPi;
1243                 result->st_nlink = POPi;
1244                 result->st_mode = POPi;
1245                 result->st_ino   = POPi;
1246                 result->st_dev = POPi;
1247                 rv = 0;
1248         }
1249         FREETMPS;
1250         LEAVE;
1251         PUTBACK;
1252         DEBUGf("fgetattr end: %i\n",rv);
1253         FUSE_CONTEXT_POST;
1254         return rv;
1255 }
1256 #endif /* FUSE_VERSION >= 25 */
1257
1258 #if FUSE_VERSION >= 26
1259 int _PLfuse_lock(const char *file, struct fuse_file_info *fi, int cmd,
1260                  struct flock *lockinfo) {
1261         int rv;
1262         HV *lihash;
1263         SV *sv;
1264 #ifndef PERL_HAS_64BITINT
1265         char *temp;
1266 #endif
1267         FUSE_CONTEXT_PRE;
1268         DEBUGf("lock begin\n");
1269         ENTER;
1270         SAVETMPS;
1271         PUSHMARK(SP);
1272         XPUSHs(file ? sv_2mortal(newSVpv(file,0)) : &PL_sv_undef);
1273         XPUSHs(sv_2mortal(newSViv(cmd)));
1274         lihash = newHV();
1275         if (lockinfo) {
1276                 (void) hv_store(lihash, "l_type",   6, newSViv(lockinfo->l_type), 0);
1277                 (void) hv_store(lihash, "l_whence", 8, newSViv(lockinfo->l_whence), 0);
1278 #ifdef PERL_HAS_64BITINT
1279                 sv = newSViv(lockinfo->l_start);
1280 #else
1281                 if (asprintf(&temp, "%llu", lockinfo->l_start) == -1)
1282                         croak("Memory allocation failure!");
1283                 sv = newSVpv(temp, 0);
1284                 free(temp);
1285 #endif
1286                 (void) hv_store(lihash, "l_start",  7, sv, 0);
1287 #ifdef PERL_HAS_64BITINT
1288                 sv = newSViv(lockinfo->l_len);
1289 #else
1290                 if (asprintf(&temp, "%llu", lockinfo->l_len) == -1)
1291                         croak("Memory allocation failure!");
1292                 sv = newSVpv(temp, 0);
1293                 free(temp);
1294 #endif
1295                 (void) hv_store(lihash, "l_len",    5, sv, 0);
1296                 (void) hv_store(lihash, "l_pid",    5, newSViv(lockinfo->l_pid), 0);
1297         }
1298         XPUSHs(sv_2mortal(newRV_noinc((SV*) lihash)));
1299         XPUSHs(FH_GETHANDLE(fi));
1300
1301         PUTBACK;
1302         rv = call_sv(MY_CXT.callback[35],G_SCALAR);
1303         SPAGAIN;
1304         rv = (rv ? POPi : 0);
1305         if (lockinfo && !rv) {
1306                 /* Need to copy back any altered values from the hash into
1307                  * the struct... */
1308                 SV **svp;
1309                 if ((svp = hv_fetch(lihash, "l_type",   6, 0)) != NULL)
1310                         lockinfo->l_type   = SvIV(*svp);
1311                 if ((svp = hv_fetch(lihash, "l_whence", 8, 0)) != NULL)
1312                         lockinfo->l_whence = SvIV(*svp);
1313                 if ((svp = hv_fetch(lihash, "l_start",  7, 0)) != NULL)
1314                         lockinfo->l_start  = SvNV(*svp);
1315                 if ((svp = hv_fetch(lihash, "l_len",    5, 0)) != NULL)
1316                         lockinfo->l_len    = SvNV(*svp);
1317                 if ((svp = hv_fetch(lihash, "l_pid",    5, 0)) != NULL)
1318                         lockinfo->l_pid    = SvIV(*svp);
1319         }
1320         FREETMPS;
1321         LEAVE;
1322         PUTBACK;
1323         DEBUGf("lock end: %i\n",rv);
1324         FUSE_CONTEXT_POST;
1325         return rv;
1326 }
1327
1328 int _PLfuse_utimens(const char *file, const struct timespec tv[2]) {
1329         int rv;
1330         FUSE_CONTEXT_PRE;
1331         DEBUGf("utimens begin\n");
1332         ENTER;
1333         SAVETMPS;
1334         PUSHMARK(SP);
1335         XPUSHs(sv_2mortal(newSVpv(file,0)));
1336         XPUSHs(tv ? sv_2mortal(newSVnv(tv[0].tv_sec + (tv[0].tv_nsec / 1000000000.0))) : &PL_sv_undef);
1337         XPUSHs(tv ? sv_2mortal(newSVnv(tv[1].tv_sec + (tv[1].tv_nsec / 1000000000.0))) : &PL_sv_undef);
1338         PUTBACK;
1339         rv = call_sv(MY_CXT.callback[36],G_SCALAR);
1340         SPAGAIN;
1341         rv = (rv ? POPi : 0);
1342         FREETMPS;
1343         LEAVE;
1344         PUTBACK;
1345         DEBUGf("utimens end: %i\n",rv);
1346         FUSE_CONTEXT_POST;
1347         return rv;
1348 }
1349
1350 int _PLfuse_bmap(const char *file, size_t blocksize, uint64_t *idx) {
1351         int rv;
1352 #ifndef PERL_HAS_64BITINT
1353         char *temp;
1354 #endif
1355         FUSE_CONTEXT_PRE;
1356         DEBUGf("bmap begin\n");
1357         ENTER;
1358         SAVETMPS;
1359         PUSHMARK(SP);
1360         XPUSHs(sv_2mortal(newSVpv(file,0)));
1361         XPUSHs(sv_2mortal(newSViv(blocksize)));
1362 #ifdef PERL_HAS_64BITINT
1363         XPUSHs(sv_2mortal(newSViv(*idx)));
1364 #else
1365         if (asprintf(&temp, "%llu", *idx) == -1)
1366                 croak("Memory allocation failure!");
1367         XPUSHs(sv_2mortal(newSVpv(temp, 0)));
1368         free(temp);
1369 #endif
1370         PUTBACK;
1371         rv = call_sv(MY_CXT.callback[37],G_ARRAY);
1372         SPAGAIN;
1373         if (rv > 0 && rv < 3) {
1374                 if (rv == 2)
1375                         *idx = POPn;
1376                 rv = POPi;
1377         }
1378         else {
1379                 fprintf(stderr, "bmap(): wrong number of values returned?\n");
1380                 rv = -ENOSYS;
1381         }
1382         FREETMPS;
1383         LEAVE;
1384         PUTBACK;
1385         DEBUGf("bmap end: %i\n",rv);
1386         FUSE_CONTEXT_POST;
1387         return rv;
1388 }
1389 #endif /* FUSE_VERSION >= 26 */
1390
1391 #if 0
1392 #if FUSE_VERSION >= 28
1393 int _PLfuse_ioctl(const char *file, int cmd, void *arg,
1394                   struct fuse_file_info *fi, unsigned int flags, void *data) {
1395         int rv;
1396         FUSE_CONTEXT_PRE;
1397         DEBUGf("ioctl begin\n");
1398         ENTER;
1399         SAVETMPS;
1400         PUSHMARK(SP);
1401         XPUSHs(sv_2mortal(newSVpv(file,0)));
1402         XPUSHs(sv_2mortal(newSViv(cmd)));
1403         XPUSHs(sv_2mortal(newSViv(flags)));
1404         if (_IOC_DIR(cmd) & _IOC_READ)
1405                 XPUSHs(sv_2mortal(newSVpvn(data, _IOC_SIZE(cmd))));
1406         else
1407                 XPUSHs(&PL_sv_undef);
1408         XPUSHs(FH_GETHANDLE(fi));
1409         PUTBACK;
1410         rv = call_sv(MY_CXT.callback[39],G_ARRAY);
1411         SPAGAIN;
1412         if (_IOC_DIR(cmd) & _IOC_WRITE) {
1413                 if (rv == 2) {
1414                         SV *sv = POPs;
1415                         unsigned int len;
1416                         char *rdata = SvPV(sv, len);
1417                         if (len > _IOC_SIZE(cmd)) {
1418                                 fprintf(stderr, "ioctl(): returned data was too large for data area\n");
1419                                 rv = -EFBIG;
1420                         }
1421                         else {
1422                                 memset(data, 0, _IOC_SIZE(cmd));
1423                                 memcpy(data, rdata, len);
1424                         }
1425
1426                         rv--;
1427                 }
1428                 else {
1429                         fprintf(stderr, "ioctl(): ioctl was a write op, but no data was returned from call?\n");
1430                         rv = -EFAULT;
1431                 }
1432         }
1433         if (rv > 0)
1434                 rv = POPi;
1435         FREETMPS;
1436         LEAVE;
1437         PUTBACK;
1438         DEBUGf("ioctl end: %i\n",rv);
1439         FUSE_CONTEXT_POST;
1440         return rv;
1441 }
1442
1443 int _PLfuse_poll(const char *file, struct fuse_file_info *fi,
1444                  struct fuse_pollhandle *ph, unsigned *reventsp) {
1445
1446 }
1447 #endif /* FUSE_VERSION >= 28 */
1448 #endif
1449
1450 struct fuse_operations _available_ops = {
1451 getattr:                _PLfuse_getattr,
1452 readlink:               _PLfuse_readlink,
1453 getdir:                 _PLfuse_getdir,
1454 mknod:                  _PLfuse_mknod,
1455 mkdir:                  _PLfuse_mkdir,
1456 unlink:                 _PLfuse_unlink,
1457 rmdir:                  _PLfuse_rmdir,
1458 symlink:                _PLfuse_symlink,
1459 rename:                 _PLfuse_rename,
1460 link:                   _PLfuse_link,
1461 chmod:                  _PLfuse_chmod,
1462 chown:                  _PLfuse_chown,
1463 truncate:               _PLfuse_truncate,
1464 utime:                  _PLfuse_utime,
1465 open:                   _PLfuse_open,
1466 read:                   _PLfuse_read,
1467 write:                  _PLfuse_write,
1468 statfs:                 _PLfuse_statfs,
1469 flush:                  _PLfuse_flush,
1470 release:                _PLfuse_release,
1471 fsync:                  _PLfuse_fsync,
1472 setxattr:               _PLfuse_setxattr,
1473 getxattr:               _PLfuse_getxattr,
1474 listxattr:              _PLfuse_listxattr,
1475 removexattr:            _PLfuse_removexattr,
1476 #if FUSE_VERSION >= 23
1477 opendir:                _PLfuse_opendir, 
1478 readdir:                _PLfuse_readdir,
1479 releasedir:             _PLfuse_releasedir,
1480 fsyncdir:               _PLfuse_fsyncdir,
1481 init:                   _PLfuse_init,
1482 destroy:                _PLfuse_destroy,
1483 #endif /* FUSE_VERSION >= 23 */
1484 #if FUSE_VERSION >= 25
1485 access:                 _PLfuse_access,
1486 create:                 _PLfuse_create,
1487 ftruncate:              _PLfuse_ftruncate,
1488 fgetattr:               _PLfuse_fgetattr,
1489 #endif /* FUSE_VERSION >= 25 */
1490 #if FUSE_VERSION >= 26
1491 lock:                   _PLfuse_lock,
1492 utimens:                _PLfuse_utimens,
1493 bmap:                   _PLfuse_bmap,
1494 #endif /* FUSE_VERSION >= 26 */
1495 #if 0
1496 #if FUSE_VERSION >= 28
1497 ioctl:                  _PLfuse_ioctl,
1498 poll:                   _PLfuse_poll,
1499 #endif /* FUSE_VERSION >= 28 */
1500 #endif
1501 };
1502
1503 MODULE = Fuse           PACKAGE = Fuse
1504 PROTOTYPES: DISABLE
1505
1506 BOOT:
1507         MY_CXT_INIT;
1508         MY_CXT.self = aTHX;
1509
1510 void
1511 CLONE(...)
1512         PREINIT:
1513                 int i;
1514                 dTHX;
1515         CODE:
1516                 MY_CXT_CLONE;
1517                 tTHX parent = MY_CXT.self;
1518                 MY_CXT.self = my_perl;
1519 #if (PERL_VERSION < 10) || (PERL_VERSION == 10 && PERL_SUBVERSION <= 0)
1520                 /* CLONE entered without a pointer table, so we can't safely clone static data */
1521                 if(!PL_ptr_table) {
1522                         for(i=0;i<N_CALLBACKS;i++) {
1523                                 MY_CXT.callback[i] = NULL;
1524                         }
1525                         MY_CXT.handles = newHV();
1526                 } else
1527 #endif
1528                 {
1529                         CLONE_PARAMS *clone_param;
1530 #if (PERL_VERSION > 13) || (PERL_VERSION == 13 && PERL_SUBVERSION >= 2)
1531                         clone_param = clone_params_new(parent, aTHX);
1532 #else
1533                         CLONE_PARAMS raw_param;
1534                         raw_param.flags = 0;
1535                         raw_param.proto_perl = parent;
1536                         raw_param.stashes = (AV*)sv_2mortal((SV*)newAV());
1537                         clone_param = &raw_param;
1538 #endif
1539                         for(i=0;i<N_CALLBACKS;i++) {
1540                                 MY_CXT.callback[i] = sv_dup(MY_CXT.callback[i], clone_param);
1541                         }
1542                         MY_CXT.handles = (HV*)sv_dup((SV*)MY_CXT.handles, clone_param);
1543 #if (PERL_VERSION > 13) || (PERL_VERSION == 13 && PERL_SUBVERSION >= 2)
1544                         clone_params_del(clone_param);
1545 #endif
1546                 }
1547
1548 SV*
1549 fuse_get_context()
1550         PREINIT:
1551         struct fuse_context *fc;
1552         CODE:
1553         fc = fuse_get_context();
1554         if(fc) {
1555                 HV *hash = newHV();
1556                 (void) hv_store(hash, "uid",   3, newSViv(fc->uid), 0);
1557                 (void) hv_store(hash, "gid",   3, newSViv(fc->gid), 0);
1558                 (void) hv_store(hash, "pid",   3, newSViv(fc->pid), 0);
1559                 if (fc->private_data)
1560                         (void) hv_store(hash, "private", 7, fc->private_data, 0);
1561 #if FUSE_VERSION >= 28
1562                 (void) hv_store(hash, "umask", 5, newSViv(fc->umask), 0);
1563 #endif /* FUSE_VERSION >= 28 */
1564                 RETVAL = newRV_noinc((SV*)hash);
1565         } else {
1566                 XSRETURN_UNDEF;
1567         }
1568         OUTPUT:
1569         RETVAL
1570
1571 SV *
1572 fuse_version()
1573         CODE:
1574         RETVAL = newSVpvf("%d.%d", FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION);
1575         OUTPUT:
1576         RETVAL
1577
1578 void
1579 perl_fuse_main(...)
1580         PREINIT:
1581         struct fuse_operations fops;
1582         int i, debug;
1583         char *mountpoint;
1584         char *mountopts;
1585         struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
1586         struct fuse_chan *fc;
1587         dMY_CXT;
1588         INIT:
1589         if(items != N_CALLBACKS + 5) {
1590                 fprintf(stderr,"Perl<->C inconsistency or internal error\n");
1591                 XSRETURN_UNDEF;
1592         }
1593         memset(&fops, 0, sizeof(struct fuse_operations));
1594         CODE:
1595         debug = SvIV(ST(0));
1596         MY_CXT.threaded = SvIV(ST(1));
1597         MY_CXT.handles = (HV*)(sv_2mortal((SV*)(newHV())));
1598         if(MY_CXT.threaded) {
1599 #ifdef FUSE_USE_ITHREADS
1600                 master_interp = aTHX;
1601                 MUTEX_INIT(&MY_CXT.mutex);
1602                 SvSHARE((SV*)(MY_CXT.handles));
1603 #else
1604                 fprintf(stderr,"FUSE warning: Your script has requested multithreaded "
1605                                "mode, but your perl was not built with a supported "
1606                                "thread model. Threads are disabled.\n");
1607                 MY_CXT.threaded = 0;
1608 #endif
1609         }
1610         mountpoint = SvPV_nolen(ST(2));
1611         mountopts = SvPV_nolen(ST(3));
1612 #if FUSE_VERSION >= 28
1613         fops.flag_nullpath_ok = SvIV(ST(4));
1614 #endif /* FUSE_VERSION >= 28 */
1615         for(i=0;i<N_CALLBACKS;i++) {
1616                 SV *var = ST(i+5);
1617                 /* allow symbolic references, or real code references. */
1618                 if(SvOK(var) && (SvPOK(var) || (SvROK(var) && SvTYPE(SvRV(var)) == SVt_PVCV))) {
1619                         void **tmp1 = (void**)&_available_ops, **tmp2 = (void**)&fops;
1620                         /* Dirty hack, to keep anything from overwriting the
1621                          * flag area with a pointer. There should never be
1622                          * anything passed as 'junk', but this prevents
1623                          * someone from doing it and screwing things up... */
1624                         if (i == 38)
1625                                 continue;
1626                         tmp2[i] = tmp1[i];
1627                         MY_CXT.callback[i] = var;
1628                 } else if(SvOK(var)) {
1629                         croak("invalid callback (%i) passed to perl_fuse_main "
1630                               "(%s is not a string, code ref, or undef).\n",
1631                               i+4,SvPVbyte_nolen(var));
1632                 } else {
1633                         MY_CXT.callback[i] = NULL;
1634                 }
1635         }
1636         /*
1637          * XXX: What comes here is just a ridiculous use of the option parsing API
1638          * to hack on compatibility with other parts of the new API. First and
1639          * foremost, real C argc/argv would be good to get at...
1640          */
1641         if (mountopts &&
1642             (fuse_opt_add_arg(&args, "") == -1 ||
1643              fuse_opt_add_arg(&args, "-o") == -1 ||
1644              fuse_opt_add_arg(&args, mountopts) == -1)) {
1645                 fuse_opt_free_args(&args);
1646                 croak("out of memory\n");
1647         }
1648         if (debug && fuse_opt_add_arg(&args, "-d") == -1) {
1649                 fuse_opt_free_args(&args);
1650                 croak("out of memory\n");
1651         }
1652         fc = fuse_mount(mountpoint,&args);
1653         if (fc == NULL)
1654                 croak("could not mount fuse filesystem!\n");
1655 #ifndef __NetBSD__
1656         if(MY_CXT.threaded) {
1657                 fuse_loop_mt(fuse_new(fc,&args,&fops,sizeof(fops),NULL));
1658         } else
1659 #endif
1660                 fuse_loop(fuse_new(fc,&args,&fops,sizeof(fops),NULL));
1661         fuse_unmount(mountpoint,fc);
1662         fuse_opt_free_args(&args);