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