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