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