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