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