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