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