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