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