Update change list.
[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_SEC(st, st_xtim) ((st)->st_xtim.tv_sec)
17 # define STAT_NSEC(st, st_xtim) ((st)->st_xtim.tv_nsec)
18 #else
19 # define STAT_SEC(st, st_xtim) ((st)->st_xtim##espec.tv_sec)
20 # define STAT_NSEC(st, st_xtim) ((st)->st_xtim##espec.tv_nsec)
21 #endif
22
23 /* Implement a macro to handle multiple formats (integer, float, and array
24  * containing seconds and nanoseconds). */
25 #define PULL_TIME(st, st_xtim, svp)                                     \
26 {                                                                       \
27         SV *sv = svp;                                                   \
28         if (SvROK(sv)) {                                                \
29                 AV *av = (AV *)SvRV(sv);                                \
30                 if (SvTYPE((SV *)av) != SVt_PVAV) {                     \
31                         Perl_croak_nocontext("Reference was not array ref"); \
32                 }                                                       \
33                 if (av_len(av) != 1) {                                  \
34                         Perl_croak_nocontext("Array of incorrect dimension"); \
35                 }                                                       \
36                 STAT_SEC(st, st_xtim) = SvIV(*(av_fetch(av, 0, FALSE))); \
37                 STAT_NSEC(st, st_xtim) = SvIV(*(av_fetch(av, 1, FALSE))); \
38         }                                                               \
39         else if (SvNOK(sv) || SvIOK(sv)) {                              \
40                 double tm = SvNV(sv);                                   \
41                 STAT_SEC(st, st_xtim) = (int)tm;                        \
42                 STAT_NSEC(st, st_xtim) = (tm - (int)tm) * 1000000000;   \
43         }                                                               \
44         else {                                                          \
45                 Perl_croak_nocontext("Invalid data type passed");       \
46         }                                                               \
47 }
48
49 /* Determine if threads support should be included */
50 #ifdef USE_ITHREADS
51 # ifdef I_PTHREAD
52 #  define FUSE_USE_ITHREADS
53 #  if (PERL_VERSION < 8) || (PERL_VERSION == 8 && PERL_SUBVERSION < 9)
54 #    define tTHX PerlInterpreter*
55 #    define STR_WITH_LEN(s)  ("" s ""), (sizeof(s)-1)
56 #    define hv_fetchs(hv,key,lval) Perl_hv_fetch(aTHX_ hv, STR_WITH_LEN(key), lval)
57 #    define dMY_CXT_INTERP(interp) \
58         SV *my_cxt_sv = *hv_fetchs(interp->Imodglobal, MY_CXT_KEY, TRUE); \
59         my_cxt_t *my_cxtp = INT2PTR(my_cxt_t*, SvUV(my_cxt_sv))
60 #  endif
61 # else
62 #  warning "Sorry, I don't know how to handle ithreads on this architecture. Building non-threaded version"
63 # endif
64 #endif
65
66 /* Global Data */
67
68 #define MY_CXT_KEY "Fuse::_guts" XS_VERSION
69 #if FUSE_VERSION >= 28
70 # define N_CALLBACKS 41
71 #else
72 # define N_CALLBACKS 38
73 #endif
74
75 typedef struct {
76         SV *callback[N_CALLBACKS];
77         HV *handles;
78 #ifdef USE_ITHREADS
79         tTHX self;
80 #endif
81         int threaded;
82 #ifdef USE_ITHREADS
83         perl_mutex mutex;
84 #endif
85         int utimens_as_array;
86 } my_cxt_t;
87 START_MY_CXT;
88
89 #ifdef FUSE_USE_ITHREADS
90 tTHX master_interp = NULL;
91
92 #define CLONE_INTERP(parent) S_clone_interp(parent)
93 tTHX S_clone_interp(tTHX parent) {
94 #  if (PERL_VERSION < 10)
95         tTHX my_perl = parent;
96 #endif
97         dMY_CXT_INTERP(parent);
98         if(MY_CXT.threaded) {
99                 MUTEX_LOCK(&MY_CXT.mutex);
100                 PERL_SET_CONTEXT(parent);
101                 dTHX;
102 #if (PERL_VERSION > 10) || (PERL_VERSION == 10 && PERL_SUBVERSION >= 1)
103                 tTHX child = perl_clone(parent, CLONEf_CLONE_HOST);
104 #else
105                 tTHX child = perl_clone(parent, CLONEf_CLONE_HOST | CLONEf_KEEP_PTR_TABLE);
106                 ptr_table_free(PL_ptr_table);
107                 PL_ptr_table = NULL;
108 #endif
109                 MUTEX_UNLOCK(&MY_CXT.mutex);
110                 return child;
111         }
112         return NULL;
113 }
114
115 # define FUSE_CONTEXT_PRE dTHX; if(!aTHX) aTHX = CLONE_INTERP(master_interp); { dMY_CXT; dSP;
116 # define FUSE_CONTEXT_POST }
117 #else
118 # define FUSE_CONTEXT_PRE dTHX; dMY_CXT; dSP;
119 # define FUSE_CONTEXT_POST
120 #endif
121
122 #undef DEBUGf
123 #if 0
124 #define DEBUGf(f, a...) fprintf(stderr, "%s:%d (%i): " f,__BASE_FILE__,__LINE__,sp-PL_stack_base ,##a )
125 #else
126 #define DEBUGf(a...)
127 #endif
128
129 #define FH_KEY(fi) sv_2mortal(newSViv((fi)->fh))
130 #define FH_GETHANDLE(fi) S_fh_get_handle(aTHX_ aMY_CXT_ fi)
131 #define FH_STOREHANDLE(fi,sv) S_fh_store_handle(aTHX_ aMY_CXT_ fi, sv)
132 #define FH_RELEASEHANDLE(fi) S_fh_release_handle(aTHX_ aMY_CXT_ fi)
133
134 SV *S_fh_get_handle(pTHX_ pMY_CXT_ struct fuse_file_info *fi) {
135         SV *val;
136         val = &PL_sv_undef;
137         if(fi->fh != 0) {
138                 HE *he;
139                 if((he = hv_fetch_ent(MY_CXT.handles, FH_KEY(fi), 0, 0))) {
140                         val = HeVAL(he);
141                         SvGETMAGIC(val);
142                 }
143         }
144         return val;
145 }
146
147 void S_fh_release_handle(pTHX_ pMY_CXT_ struct fuse_file_info *fi) {
148         if(fi->fh != 0) {
149                 (void)hv_delete_ent(MY_CXT.handles, FH_KEY(fi), G_DISCARD, 0);
150                 fi->fh = 0;
151         }
152 }
153
154 void S_fh_store_handle(pTHX_ pMY_CXT_ struct fuse_file_info *fi, SV *sv) {
155         if(SvOK(sv)) {
156 #ifdef FUSE_USE_ITHREADS
157                 if(MY_CXT.threaded) {
158                         SvSHARE(sv);
159                 }
160 #endif
161         /* This seems to be screwing things up... */
162                 // MAGIC *mg = (SvTYPE(sv) == SVt_PVMG) ? mg_find(sv, PERL_MAGIC_shared_scalar) : NULL;
163                 // fi->fh = mg ? PTR2IV(mg->mg_ptr) : PTR2IV(sv);
164                 fi->fh = PTR2IV(sv);
165                 if(hv_store_ent(MY_CXT.handles, FH_KEY(fi), SvREFCNT_inc(sv), 0) == NULL) {
166                         SvREFCNT_dec(sv);
167                 }
168                 SvSETMAGIC(sv);
169         }
170 }
171
172 int _PLfuse_getattr(const char *file, struct stat *result) {
173         int rv;
174         FUSE_CONTEXT_PRE;
175         DEBUGf("getattr begin: %s\n",file);
176         ENTER;
177         SAVETMPS;
178         PUSHMARK(SP);
179         XPUSHs(sv_2mortal(newSVpv(file,strlen(file))));
180         PUTBACK;
181         rv = call_sv(MY_CXT.callback[0],G_ARRAY);
182         SPAGAIN;
183         if(rv != 13) {
184                 if(rv > 1) {
185                         fprintf(stderr,"inappropriate number of returned values from getattr\n");
186                         rv = -ENOSYS;
187                 } else if(rv)
188                         rv = POPi;
189                 else
190                         rv = -ENOENT;
191         } else {
192                 result->st_blocks = POPi;
193                 result->st_blksize = POPi;
194                 PULL_TIME(result, st_ctim, POPs);
195                 PULL_TIME(result, st_mtim, POPs);
196                 PULL_TIME(result, st_atim, POPs);
197                 result->st_size = POPn; // we pop double here to support files larger than 4Gb (long limit)
198                 result->st_rdev = POPi;
199                 result->st_gid = POPi;
200                 result->st_uid = POPi;
201                 result->st_nlink = POPi;
202                 result->st_mode = POPi;
203                 result->st_ino   = POPi;
204                 result->st_dev = POPi;
205                 rv = 0;
206         }
207         FREETMPS;
208         LEAVE;
209         PUTBACK;
210         DEBUGf("getattr end: %i\n",rv);
211         FUSE_CONTEXT_POST;
212         return rv;
213 }
214
215 int _PLfuse_readlink(const char *file,char *buf,size_t buflen) {
216         int rv;
217         if(buflen < 1)
218                 return EINVAL;
219         FUSE_CONTEXT_PRE;
220         DEBUGf("readlink begin\n");
221         ENTER;
222         SAVETMPS;
223         PUSHMARK(SP);
224         XPUSHs(sv_2mortal(newSVpv(file,0)));
225         PUTBACK;
226         rv = call_sv(MY_CXT.callback[1],G_SCALAR);
227         SPAGAIN;
228         if(!rv)
229                 rv = -ENOENT;
230         else {
231                 SV *mysv = POPs;
232                 if(SvTYPE(mysv) == SVt_IV || SvTYPE(mysv) == SVt_NV)
233                         rv = SvIV(mysv);
234                 else {
235                         strncpy(buf,SvPV_nolen(mysv),buflen);
236                         rv = 0;
237                 }
238         }
239         FREETMPS;
240         LEAVE;
241         buf[buflen-1] = 0;
242         PUTBACK;
243         DEBUGf("readlink end: %i\n",rv);
244         FUSE_CONTEXT_POST;
245         return rv;
246 }
247
248 int _PLfuse_getdir(const char *file, fuse_dirh_t dirh, fuse_dirfil_t dirfil) {
249         int prv, rv;
250         SV **swp;
251         FUSE_CONTEXT_PRE;
252         DEBUGf("getdir begin\n");
253         ENTER;
254         SAVETMPS;
255         PUSHMARK(SP);
256         XPUSHs(sv_2mortal(newSVpv(file,0)));
257         PUTBACK;
258         prv = call_sv(MY_CXT.callback[2],G_ARRAY);
259         SPAGAIN;
260         if(prv) {
261                 /* Should yield the bottom of the current stack... */
262                 swp = &TOPs - prv + 1;
263                 rv = POPi;
264                 /* Sort of a hack to walk the stack in order, instead of reverse
265                  * order - trying to explain to potential users why they need to
266                  * reverse the order of this array would be confusing, at best. */
267                 while (swp <= &TOPs)
268                         dirfil(dirh,SvPVx_nolen(*(swp++)),0,0);
269         } else {
270                 fprintf(stderr,"getdir() handler returned nothing!\n");
271                 rv = -ENOSYS;
272         }
273         FREETMPS;
274         LEAVE;
275         PUTBACK;
276         DEBUGf("getdir end: %i\n",rv);
277         FUSE_CONTEXT_POST;
278         return rv;
279 }
280
281 int _PLfuse_mknod (const char *file, mode_t mode, dev_t dev) {
282         int rv;
283         FUSE_CONTEXT_PRE;
284         DEBUGf("mknod begin\n");
285         ENTER;
286         SAVETMPS;
287         PUSHMARK(SP);
288         XPUSHs(sv_2mortal(newSVpv(file,0)));
289         XPUSHs(sv_2mortal(newSViv(mode)));
290         XPUSHs(sv_2mortal(newSViv(dev)));
291         PUTBACK;
292         rv = call_sv(MY_CXT.callback[3],G_SCALAR);
293         SPAGAIN;
294         rv = (rv ? POPi : 0);
295         FREETMPS;
296         LEAVE;
297         PUTBACK;
298         DEBUGf("mknod end: %i\n",rv);
299         FUSE_CONTEXT_POST;
300         return rv;
301 }
302
303 int _PLfuse_mkdir (const char *file, mode_t mode) {
304         int rv;
305         FUSE_CONTEXT_PRE;
306         DEBUGf("mkdir begin\n");
307         ENTER;
308         SAVETMPS;
309         PUSHMARK(SP);
310         XPUSHs(sv_2mortal(newSVpv(file,0)));
311         XPUSHs(sv_2mortal(newSViv(mode)));
312         PUTBACK;
313         rv = call_sv(MY_CXT.callback[4],G_SCALAR);
314         SPAGAIN;
315         rv = (rv ? POPi : 0);
316         FREETMPS;
317         LEAVE;
318         PUTBACK;
319         DEBUGf("mkdir end: %i\n",rv);
320         FUSE_CONTEXT_POST;
321         return rv;
322 }
323
324 int _PLfuse_unlink (const char *file) {
325         int rv;
326         FUSE_CONTEXT_PRE;
327         DEBUGf("unlink begin\n");
328         ENTER;
329         SAVETMPS;
330         PUSHMARK(SP);
331         XPUSHs(sv_2mortal(newSVpv(file,0)));
332         PUTBACK;
333         rv = call_sv(MY_CXT.callback[5],G_SCALAR);
334         SPAGAIN;
335         rv = (rv ? POPi : 0);
336         FREETMPS;
337         LEAVE;
338         PUTBACK;
339         DEBUGf("unlink end: %i\n",rv);
340         FUSE_CONTEXT_POST;
341         return rv;
342 }
343
344 int _PLfuse_rmdir (const char *file) {
345         int rv;
346         FUSE_CONTEXT_PRE;
347         DEBUGf("rmdir begin\n");
348         ENTER;
349         SAVETMPS;
350         PUSHMARK(SP);
351         XPUSHs(sv_2mortal(newSVpv(file,0)));
352         PUTBACK;
353         rv = call_sv(MY_CXT.callback[6],G_SCALAR);
354         SPAGAIN;
355         rv = (rv ? POPi : 0);
356         FREETMPS;
357         LEAVE;
358         PUTBACK;
359         DEBUGf("rmdir end: %i\n",rv);
360         FUSE_CONTEXT_POST;
361         return rv;
362 }
363
364 int _PLfuse_symlink (const char *file, const char *new) {
365         int rv;
366         FUSE_CONTEXT_PRE;
367         DEBUGf("symlink begin\n");
368         ENTER;
369         SAVETMPS;
370         PUSHMARK(SP);
371         XPUSHs(sv_2mortal(newSVpv(file,0)));
372         XPUSHs(sv_2mortal(newSVpv(new,0)));
373         PUTBACK;
374         rv = call_sv(MY_CXT.callback[7],G_SCALAR);
375         SPAGAIN;
376         rv = (rv ? POPi : 0);
377         FREETMPS;
378         LEAVE;
379         PUTBACK;
380         DEBUGf("symlink end: %i\n",rv);
381         FUSE_CONTEXT_POST;
382         return rv;
383 }
384
385 int _PLfuse_rename (const char *file, const char *new) {
386         int rv;
387         FUSE_CONTEXT_PRE;
388         DEBUGf("rename begin\n");
389         ENTER;
390         SAVETMPS;
391         PUSHMARK(SP);
392         XPUSHs(sv_2mortal(newSVpv(file,0)));
393         XPUSHs(sv_2mortal(newSVpv(new,0)));
394         PUTBACK;
395         rv = call_sv(MY_CXT.callback[8],G_SCALAR);
396         SPAGAIN;
397         rv = (rv ? POPi : 0);
398         FREETMPS;
399         LEAVE;
400         PUTBACK;
401         DEBUGf("rename end: %i\n",rv);
402         FUSE_CONTEXT_POST;
403         return rv;
404 }
405
406 int _PLfuse_link (const char *file, const char *new) {
407         int rv;
408         FUSE_CONTEXT_PRE;
409         DEBUGf("link begin\n");
410         ENTER;
411         SAVETMPS;
412         PUSHMARK(SP);
413         XPUSHs(sv_2mortal(newSVpv(file,0)));
414         XPUSHs(sv_2mortal(newSVpv(new,0)));
415         PUTBACK;
416         rv = call_sv(MY_CXT.callback[9],G_SCALAR);
417         SPAGAIN;
418         rv = (rv ? POPi : 0);
419         FREETMPS;
420         LEAVE;
421         PUTBACK;
422         DEBUGf("link end: %i\n",rv);
423         FUSE_CONTEXT_POST;
424         return rv;
425 }
426
427 int _PLfuse_chmod (const char *file, mode_t mode) {
428         int rv;
429         FUSE_CONTEXT_PRE;
430         DEBUGf("chmod begin\n");
431         ENTER;
432         SAVETMPS;
433         PUSHMARK(SP);
434         XPUSHs(sv_2mortal(newSVpv(file,0)));
435         XPUSHs(sv_2mortal(newSViv(mode)));
436         PUTBACK;
437         rv = call_sv(MY_CXT.callback[10],G_SCALAR);
438         SPAGAIN;
439         rv = (rv ? POPi : 0);
440         FREETMPS;
441         LEAVE;
442         PUTBACK;
443         DEBUGf("chmod end: %i\n",rv);
444         FUSE_CONTEXT_POST;
445         return rv;
446 }
447
448 int _PLfuse_chown (const char *file, uid_t uid, gid_t gid) {
449         int rv;
450         FUSE_CONTEXT_PRE;
451         DEBUGf("chown begin\n");
452         ENTER;
453         SAVETMPS;
454         PUSHMARK(SP);
455         XPUSHs(sv_2mortal(newSVpv(file,0)));
456         XPUSHs(sv_2mortal(newSViv(uid)));
457         XPUSHs(sv_2mortal(newSViv(gid)));
458         PUTBACK;
459         rv = call_sv(MY_CXT.callback[11],G_SCALAR);
460         SPAGAIN;
461         rv = (rv ? POPi : 0);
462         FREETMPS;
463         LEAVE;
464         PUTBACK;
465         DEBUGf("chown end: %i\n",rv);
466         FUSE_CONTEXT_POST;
467         return rv;
468 }
469
470 int _PLfuse_truncate (const char *file, off_t off) {
471         int rv;
472 #ifndef PERL_HAS_64BITINT
473         char *temp;
474 #endif
475         FUSE_CONTEXT_PRE;
476         DEBUGf("truncate begin\n");
477         ENTER;
478         SAVETMPS;
479         PUSHMARK(SP);
480         XPUSHs(sv_2mortal(newSVpv(file,0)));
481 #ifdef PERL_HAS_64BITINT
482         XPUSHs(sv_2mortal(newSViv(off)));
483 #else
484         if (asprintf(&temp, "%llu", off) == -1)
485                 croak("Memory allocation failure!");
486         XPUSHs(sv_2mortal(newSVpv(temp, 0)));
487         free(temp);
488 #endif
489         PUTBACK;
490         rv = call_sv(MY_CXT.callback[12],G_SCALAR);
491         SPAGAIN;
492         rv = (rv ? POPi : 0);
493         FREETMPS;
494         LEAVE;
495         PUTBACK;
496         DEBUGf("truncate end: %i\n",rv);
497         FUSE_CONTEXT_POST;
498         return rv;
499 }
500
501 int _PLfuse_utime (const char *file, struct utimbuf *uti) {
502         int rv;
503         FUSE_CONTEXT_PRE;
504         DEBUGf("utime begin\n");
505         ENTER;
506         SAVETMPS;
507         PUSHMARK(SP);
508         XPUSHs(sv_2mortal(newSVpv(file,0)));
509         XPUSHs(sv_2mortal(newSViv(uti->actime)));
510         XPUSHs(sv_2mortal(newSViv(uti->modtime)));
511         PUTBACK;
512         rv = call_sv(MY_CXT.callback[13],G_SCALAR);
513         SPAGAIN;
514         rv = (rv ? POPi : 0);
515         FREETMPS;
516         LEAVE;
517         PUTBACK;
518         DEBUGf("utime end: %i\n",rv);
519         FUSE_CONTEXT_POST;
520         return rv;
521 }
522
523 int _PLfuse_open (const char *file, struct fuse_file_info *fi) {
524         int rv;
525         int flags = fi->flags;
526         HV *fihash;
527         FUSE_CONTEXT_PRE;
528         DEBUGf("open begin\n");
529         ENTER;
530         SAVETMPS;
531         PUSHMARK(SP);
532         XPUSHs(sv_2mortal(newSVpv(file,0)));
533         XPUSHs(sv_2mortal(newSViv(flags)));
534         /* Create a hashref containing the details from fi
535          * which we can look at or modify.
536          */
537         fi->fh = 0; /* Ensure it starts with 0 - important if they don't set it */
538         fihash = newHV();
539         (void) hv_store(fihash, "direct_io",    9, newSViv(fi->direct_io),   0);
540         (void) hv_store(fihash, "keep_cache",  10, newSViv(fi->keep_cache),  0);
541 #if FUSE_VERSION >= 28
542         (void) hv_store(fihash, "nonseekable", 11, newSViv(fi->nonseekable), 0);
543 #endif
544         XPUSHs(sv_2mortal(newRV_noinc((SV*) fihash)));
545         /* All hashref things done */
546
547         PUTBACK;
548         /* Open called with filename, flags */
549         rv = call_sv(MY_CXT.callback[14],G_ARRAY);
550         SPAGAIN;
551         if(rv) {
552                 if(rv > 1) {
553                         FH_STOREHANDLE(fi,POPs);
554                 }
555                 rv = POPi;
556         }
557         else
558                 rv = 0;
559         if (rv == 0)
560         {
561                 /* Success, so copy the file handle which they returned */
562                 SV **svp;
563                 if ((svp = hv_fetch(fihash, "direct_io",    9, 0)) != NULL)
564                         fi->direct_io   = SvIV(*svp);
565                 if ((svp = hv_fetch(fihash, "keep_cache",  10, 0)) != NULL)
566                         fi->keep_cache  = SvIV(*svp);
567 #if FUSE_VERSION >= 28
568                 if ((svp = hv_fetch(fihash, "nonseekable", 11, 0)) != NULL)
569                         fi->nonseekable = SvIV(*svp);
570 #endif
571         }
572         FREETMPS;
573         LEAVE;
574         PUTBACK;
575         DEBUGf("open end: %i\n",rv);
576         FUSE_CONTEXT_POST;
577         return rv;
578 }
579
580 int _PLfuse_read (const char *file, char *buf, size_t buflen, off_t off,
581                 struct fuse_file_info *fi) {
582         int rv;
583 #ifndef PERL_HAS_64BITINT
584         char *temp;
585 #endif
586         FUSE_CONTEXT_PRE;
587         DEBUGf("read begin\n");
588         ENTER;
589         SAVETMPS;
590         PUSHMARK(SP);
591         XPUSHs(file ? sv_2mortal(newSVpv(file,0)) : &PL_sv_undef);
592         XPUSHs(sv_2mortal(newSViv(buflen)));
593 #ifdef PERL_HAS_64BITINT
594         XPUSHs(sv_2mortal(newSViv(off)));
595 #else
596         if (asprintf(&temp, "%llu", off) == -1)
597                 croak("Memory allocation failure!");
598         XPUSHs(sv_2mortal(newSVpv(temp, 0)));
599         free(temp);
600 #endif
601         XPUSHs(FH_GETHANDLE(fi));
602         PUTBACK;
603         rv = call_sv(MY_CXT.callback[15],G_SCALAR);
604         SPAGAIN;
605         if(!rv)
606                 rv = -ENOENT;
607         else {
608                 SV *mysv = POPs;
609                 if(SvTYPE(mysv) == SVt_NV || SvTYPE(mysv) == SVt_IV)
610                         rv = SvIV(mysv);
611                 else {
612                         if(SvPOK(mysv)) {
613                                 rv = SvCUR(mysv);
614                         } else {
615                                 rv = 0;
616                         }
617                         if(rv > buflen)
618                                 croak("read() handler returned more than buflen! (%i > %i)",rv,buflen);
619                         if(rv)
620                                 memcpy(buf,SvPV_nolen(mysv),rv);
621                 }
622         }
623         FREETMPS;
624         LEAVE;
625         PUTBACK;
626         DEBUGf("read end: %i\n",rv);
627         FUSE_CONTEXT_POST;
628         return rv;
629 }
630
631 int _PLfuse_write (const char *file, const char *buf, size_t buflen, off_t off, struct fuse_file_info *fi) {
632         int rv;
633 #ifndef PERL_HAS_64BITINT
634         char *temp;
635 #endif
636         FUSE_CONTEXT_PRE;
637         DEBUGf("write begin\n");
638         ENTER;
639         SAVETMPS;
640         PUSHMARK(SP);
641         XPUSHs(file ? sv_2mortal(newSVpv(file,0)) : &PL_sv_undef);
642         XPUSHs(sv_2mortal(newSVpvn(buf,buflen)));
643 #ifdef PERL_HAS_64BITINT
644         XPUSHs(sv_2mortal(newSViv(off)));
645 #else
646         if (asprintf(&temp, "%llu", off) == -1)
647                 croak("Memory allocation failure!");
648         XPUSHs(sv_2mortal(newSVpv(temp, 0)));
649         free(temp);
650 #endif
651         XPUSHs(FH_GETHANDLE(fi));
652         PUTBACK;
653         rv = call_sv(MY_CXT.callback[16],G_SCALAR);
654         SPAGAIN;
655         rv = (rv ? POPi : 0);
656         FREETMPS;
657         LEAVE;
658         PUTBACK;
659         DEBUGf("write end: %i\n",rv);
660         FUSE_CONTEXT_POST;
661         return rv;
662 }
663
664 int _PLfuse_statfs (const char *file, struct statvfs *st) {
665         int rv;
666         FUSE_CONTEXT_PRE;
667         DEBUGf("statfs begin\n");
668         ENTER;
669         SAVETMPS;
670         PUSHMARK(SP);
671         PUTBACK;
672         rv = call_sv(MY_CXT.callback[17],G_ARRAY);
673         SPAGAIN;
674         DEBUGf("statfs got %i params\n",rv);
675         if(rv == 6 || rv == 7) {
676                 st->f_bsize     = POPi;
677                 st->f_bfree     = POPi;
678                 st->f_blocks    = POPi;
679                 st->f_ffree     = POPi;
680                 st->f_files     = POPi;
681                 st->f_namemax   = POPi;
682                 /* zero and fill-in other */
683                 st->f_fsid = 0;
684                 st->f_frsize = 4096;
685                 st->f_flag = 0;
686                 st->f_bavail = st->f_bfree;
687                 st->f_favail = st->f_ffree;
688
689                 if(rv == 7)
690                         rv = POPi;
691                 else
692                         rv = 0;
693         } else
694         if(rv > 1)
695                 croak("inappropriate number of returned values from statfs");
696         else
697         if(rv)
698                 rv = POPi;
699         else
700                 rv = -ENOSYS;
701         FREETMPS;
702         LEAVE;
703         PUTBACK;
704         DEBUGf("statfs end: %i\n",rv);
705         FUSE_CONTEXT_POST;
706         return rv;
707 }
708
709 int _PLfuse_flush (const char *file, struct fuse_file_info *fi) {
710         int rv;
711         FUSE_CONTEXT_PRE;
712         DEBUGf("flush begin\n");
713         ENTER;
714         SAVETMPS;
715         PUSHMARK(SP);
716         XPUSHs(file ? sv_2mortal(newSVpv(file,0)) : &PL_sv_undef);
717         XPUSHs(FH_GETHANDLE(fi));
718         PUTBACK;
719         rv = call_sv(MY_CXT.callback[18],G_SCALAR);
720         SPAGAIN;
721         rv = (rv ? POPi : 0);
722         FREETMPS;
723         LEAVE;
724         PUTBACK;
725         DEBUGf("flush end: %i\n",rv);
726         FUSE_CONTEXT_POST;
727         return rv;
728 }
729
730 int _PLfuse_release (const char *file, struct fuse_file_info *fi) {
731         int rv;
732         int flags = fi->flags;
733         FUSE_CONTEXT_PRE;
734         DEBUGf("release begin\n");
735         ENTER;
736         SAVETMPS;
737         PUSHMARK(SP);
738         XPUSHs(file ? sv_2mortal(newSVpv(file,0)) : &PL_sv_undef);
739         XPUSHs(sv_2mortal(newSViv(flags)));
740         XPUSHs(FH_GETHANDLE(fi));
741         PUTBACK;
742         rv = call_sv(MY_CXT.callback[19],G_SCALAR);
743         SPAGAIN;
744         rv = (rv ? POPi : 0);
745         FH_RELEASEHANDLE(fi);
746         FREETMPS;
747         LEAVE;
748         PUTBACK;
749         DEBUGf("release end: %i\n",rv);
750         FUSE_CONTEXT_POST;
751         return rv;
752 }
753
754 int _PLfuse_fsync (const char *file, int datasync, struct fuse_file_info *fi) {
755         int rv;
756         int flags = fi->flags;
757         FUSE_CONTEXT_PRE;
758         DEBUGf("fsync begin\n");
759         ENTER;
760         SAVETMPS;
761         PUSHMARK(SP);
762         XPUSHs(file ? sv_2mortal(newSVpv(file,0)) : &PL_sv_undef);
763         XPUSHs(sv_2mortal(newSViv(flags)));
764         XPUSHs(FH_GETHANDLE(fi));
765         PUTBACK;
766         rv = call_sv(MY_CXT.callback[20],G_SCALAR);
767         SPAGAIN;
768         rv = (rv ? POPi : 0);
769         FREETMPS;
770         LEAVE;
771         PUTBACK;
772         DEBUGf("fsync end: %i\n",rv);
773         FUSE_CONTEXT_POST;
774         return rv;
775 }
776
777 #ifdef __APPLE__
778 int _PLfuse_setxattr (const char *file, const char *name, const char *buf, size_t buflen, int flags, uint32_t position) {
779 #else
780 int _PLfuse_setxattr (const char *file, const char *name, const char *buf, size_t buflen, int flags) {
781 #endif
782         int rv;
783         FUSE_CONTEXT_PRE;
784         DEBUGf("setxattr begin\n");
785         ENTER;
786         SAVETMPS;
787         PUSHMARK(SP);
788         XPUSHs(sv_2mortal(newSVpv(file,0)));
789         XPUSHs(sv_2mortal(newSVpv(name,0)));
790         XPUSHs(sv_2mortal(newSVpvn(buf,buflen)));
791         XPUSHs(sv_2mortal(newSViv(flags)));
792         PUTBACK;
793         rv = call_sv(MY_CXT.callback[21],G_SCALAR);
794         SPAGAIN;
795         rv = (rv ? POPi : 0);
796         FREETMPS;
797         LEAVE;
798         PUTBACK;
799         DEBUGf("setxattr end: %i\n",rv);
800         FUSE_CONTEXT_POST;
801         return rv;
802 }
803
804 #ifdef __APPLE__
805 int _PLfuse_getxattr (const char *file, const char *name, char *buf, size_t buflen, uint32_t position) {
806 #else
807 int _PLfuse_getxattr (const char *file, const char *name, char *buf, size_t buflen) {
808 #endif
809         int rv;
810         FUSE_CONTEXT_PRE;
811         DEBUGf("getxattr begin\n");
812         ENTER;
813         SAVETMPS;
814         PUSHMARK(SP);
815         XPUSHs(sv_2mortal(newSVpv(file,0)));
816         XPUSHs(sv_2mortal(newSVpv(name,0)));
817         PUTBACK;
818         rv = call_sv(MY_CXT.callback[22],G_SCALAR);
819         SPAGAIN;
820         if(!rv)
821                 rv = -ENOENT;
822         else {
823                 SV *mysv = POPs;
824
825                 rv = 0;
826                 if(SvTYPE(mysv) == SVt_NV || SvTYPE(mysv) == SVt_IV)
827                         rv = SvIV(mysv);
828                 else {
829                         if(SvPOK(mysv)) {
830                                 rv = SvCUR(mysv);
831                         } else {
832                                 rv = 0;
833                         }
834                         if ((rv > 0) && (buflen > 0))
835                         {
836                                 if(rv > buflen)
837                                         rv = -ERANGE;
838                                 else
839                                         memcpy(buf,SvPV_nolen(mysv),rv);
840                         }
841                 }
842         }
843         FREETMPS;
844         LEAVE;
845         PUTBACK;
846         DEBUGf("getxattr end: %i\n",rv);
847         FUSE_CONTEXT_POST;
848         return rv;
849 }
850
851 int _PLfuse_listxattr (const char *file, char *list, size_t size) {
852         int prv, rv;
853         FUSE_CONTEXT_PRE;
854         DEBUGf("listxattr begin\n");
855         ENTER;
856         SAVETMPS;
857         PUSHMARK(SP);
858         XPUSHs(sv_2mortal(newSVpv(file,0)));
859         PUTBACK;
860         prv = call_sv(MY_CXT.callback[23],G_ARRAY);
861         SPAGAIN;
862         if(!prv)
863                 rv = -ENOENT;
864         else {
865
866                 char *p = list;
867                 int spc = size;
868                 int total_len = 0;
869
870                 rv = POPi;
871                 prv--;
872
873                 /* Always nul terminate */
874                 if (list && (size > 0))
875                         list[0] = '\0';
876
877                 while (prv > 0)
878                 {
879                         SV *mysv = POPs;
880                         prv--;
881
882                         if (SvPOK(mysv)) {
883                                 /* Copy nul too */
884                                 int s = SvCUR(mysv) + 1;
885                                 total_len += s;
886
887                                 if (p && (size > 0) && (spc >= s))
888                                 {
889                                         memcpy(p,SvPV_nolen(mysv),s);
890                                         p += s;
891                                         spc -= s;
892                                 }
893                         }
894                 }
895
896                 /*
897                  * If the Perl returned an error, return that.
898                  * Otherwise check that the buffer was big enough.
899                  */
900                 if (rv == 0)
901                 {
902                         rv = total_len;
903                         if ((size > 0) && (size < total_len))
904                                 rv = -ERANGE;
905                 }
906         }
907         FREETMPS;
908         LEAVE;
909         PUTBACK;
910         DEBUGf("listxattr end: %i\n",rv);
911         FUSE_CONTEXT_POST;
912         return rv;
913 }
914
915 int _PLfuse_removexattr (const char *file, const char *name) {
916         int rv;
917         FUSE_CONTEXT_PRE;
918         DEBUGf("removexattr begin\n");
919         ENTER;
920         SAVETMPS;
921         PUSHMARK(SP);
922         XPUSHs(sv_2mortal(newSVpv(file,0)));
923         XPUSHs(sv_2mortal(newSVpv(name,0)));
924         PUTBACK;
925         rv = call_sv(MY_CXT.callback[24],G_SCALAR);
926         SPAGAIN;
927         rv = (rv ? POPi : 0);
928         FREETMPS;
929         LEAVE;
930         PUTBACK;
931         DEBUGf("removexattr end: %i\n",rv);
932         FUSE_CONTEXT_POST;
933         return rv;
934 }
935
936 int _PLfuse_opendir(const char *file, struct fuse_file_info *fi) {
937         int rv;
938         FUSE_CONTEXT_PRE;
939         DEBUGf("opendir begin\n");
940         ENTER;
941         SAVETMPS;
942         PUSHMARK(SP);
943         XPUSHs(sv_2mortal(newSVpv(file,0)));
944         fi->fh = 0; /* Ensure it starts with 0 - important if they don't set it */
945         PUTBACK;
946         rv = call_sv(MY_CXT.callback[25], G_ARRAY);
947         SPAGAIN;
948         if (rv) {
949                 if (rv > 1) {
950                         FH_STOREHANDLE(fi, POPs);
951                 }
952                 rv = POPi;
953         } else
954                 rv = 0;
955         FREETMPS;
956         LEAVE;
957         PUTBACK;
958         DEBUGf("opendir end: %i\n",rv);
959         FUSE_CONTEXT_POST;
960         return rv;
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
1460 # ifndef __linux__
1461 #  define _IOC_SIZE(n) IOCPARM_LEN(n)
1462 # endif
1463
1464 int _PLfuse_ioctl(const char *file, int cmd, void *arg,
1465                   struct fuse_file_info *fi, unsigned int flags, void *data) {
1466         int rv;
1467         SV *sv = NULL;
1468         FUSE_CONTEXT_PRE;
1469         DEBUGf("ioctl begin\n");
1470         ENTER;
1471         SAVETMPS;
1472         PUSHMARK(SP);
1473         XPUSHs(sv_2mortal(newSVpv(file,0)));
1474         /* I don't know why cmd is a signed int in the first place;
1475          * casting as unsigned so stupid tricks don't have to be done on
1476          * the perl side */
1477         XPUSHs(sv_2mortal(newSVuv((unsigned int)cmd)));
1478         XPUSHs(sv_2mortal(newSViv(flags)));
1479         if (cmd & IOC_IN)
1480                 XPUSHs(sv_2mortal(newSVpvn(data, _IOC_SIZE(cmd))));
1481         else
1482                 XPUSHs(&PL_sv_undef);
1483         XPUSHs(FH_GETHANDLE(fi));
1484         PUTBACK;
1485         rv = call_sv(MY_CXT.callback[39],G_ARRAY);
1486         SPAGAIN;
1487         if ((cmd & IOC_OUT) && (rv == 2)) {
1488                 sv = POPs;
1489                 rv--;
1490         }
1491
1492         if (rv > 0)
1493                 rv = POPi;
1494
1495         if ((cmd & IOC_OUT) && !rv) {
1496                 if (sv) {
1497                         size_t len;
1498                         char *rdata = SvPV(sv, len);
1499
1500                         if (len > _IOC_SIZE(cmd)) {
1501                                 fprintf(stderr, "ioctl(): returned data was too large for data area\n");
1502                                 rv = -EFBIG;
1503                         }
1504                         else {
1505                                 memset(data, 0, _IOC_SIZE(cmd));
1506                                 memcpy(data, rdata, len);
1507                         }
1508                 }
1509                 else {
1510                         fprintf(stderr, "ioctl(): ioctl was a read op, but no data was returned from call?\n");
1511                         rv = -EFAULT;
1512                 }
1513         }
1514         FREETMPS;
1515         LEAVE;
1516         PUTBACK;
1517         DEBUGf("ioctl end: %i\n",rv);
1518         FUSE_CONTEXT_POST;
1519         return rv;
1520 }
1521
1522 int _PLfuse_poll(const char *file, struct fuse_file_info *fi,
1523                  struct fuse_pollhandle *ph, unsigned *reventsp) {
1524         int rv;
1525         SV *sv = NULL;
1526         FUSE_CONTEXT_PRE;
1527         DEBUGf("poll begin\n");
1528         ENTER;
1529         SAVETMPS;
1530         PUSHMARK(SP);
1531         XPUSHs(sv_2mortal(newSVpv(file,0)));
1532         if (ph) {
1533                 /* Still gotta figure out how to do this right... */
1534                 sv = newSViv(PTR2IV(ph));
1535                 SvREADONLY_on(sv);
1536                 SvSHARE(sv);
1537                 XPUSHs(sv);
1538         }
1539         else
1540                 XPUSHs(&PL_sv_undef);
1541         XPUSHs(sv_2mortal(newSViv(*reventsp)));
1542         XPUSHs(FH_GETHANDLE(fi));
1543         PUTBACK;
1544         rv = call_sv(MY_CXT.callback[40],G_ARRAY);
1545         SPAGAIN;
1546         if (rv > 1) {
1547                 *reventsp = POPi;
1548                 rv--;
1549         }
1550         rv = (rv ? POPi : 0);
1551         FREETMPS;
1552         LEAVE;
1553         PUTBACK;
1554         DEBUGf("poll end: %i\n", rv);
1555         FUSE_CONTEXT_POST;
1556         return rv;
1557 }
1558 #endif /* FUSE_VERSION >= 28 */
1559
1560 struct fuse_operations _available_ops = {
1561 .getattr                = _PLfuse_getattr,
1562 .readlink               = _PLfuse_readlink,
1563 .getdir                 = _PLfuse_getdir,
1564 .mknod                  = _PLfuse_mknod,
1565 .mkdir                  = _PLfuse_mkdir,
1566 .unlink                 = _PLfuse_unlink,
1567 .rmdir                  = _PLfuse_rmdir,
1568 .symlink                = _PLfuse_symlink,
1569 .rename                 = _PLfuse_rename,
1570 .link                   = _PLfuse_link,
1571 .chmod                  = _PLfuse_chmod,
1572 .chown                  = _PLfuse_chown,
1573 .truncate               = _PLfuse_truncate,
1574 .utime                  = _PLfuse_utime,
1575 .open                   = _PLfuse_open,
1576 .read                   = _PLfuse_read,
1577 .write                  = _PLfuse_write,
1578 .statfs                 = _PLfuse_statfs,
1579 .flush                  = _PLfuse_flush,
1580 .release                = _PLfuse_release,
1581 .fsync                  = _PLfuse_fsync,
1582 .setxattr               = _PLfuse_setxattr,
1583 .getxattr               = _PLfuse_getxattr,
1584 .listxattr              = _PLfuse_listxattr,
1585 .removexattr            = _PLfuse_removexattr,
1586 .opendir                = _PLfuse_opendir, 
1587 .readdir                = _PLfuse_readdir,
1588 .releasedir             = _PLfuse_releasedir,
1589 .fsyncdir               = _PLfuse_fsyncdir,
1590 .init                   = _PLfuse_init,
1591 .destroy                = _PLfuse_destroy,
1592 .access                 = _PLfuse_access,
1593 .create                 = _PLfuse_create,
1594 .ftruncate              = _PLfuse_ftruncate,
1595 .fgetattr               = _PLfuse_fgetattr,
1596 .lock                   = _PLfuse_lock,
1597 .utimens                = _PLfuse_utimens,
1598 .bmap                   = _PLfuse_bmap,
1599 #if FUSE_VERSION >= 28
1600 .ioctl                  = _PLfuse_ioctl,
1601 .poll                   = _PLfuse_poll,
1602 #endif /* FUSE_VERSION >= 28 */
1603 };
1604
1605 MODULE = Fuse           PACKAGE = Fuse
1606 PROTOTYPES: DISABLE
1607
1608 BOOT:
1609         MY_CXT_INIT;
1610 #ifdef USE_ITHREADS
1611         MY_CXT.self = aTHX;
1612 #endif
1613
1614 void
1615 CLONE(...)
1616         PREINIT:
1617 #ifdef USE_ITHREADS
1618                 int i;
1619                 dTHX;
1620 #endif
1621         CODE:
1622 #ifdef USE_ITHREADS
1623                 MY_CXT_CLONE;
1624                 tTHX parent = MY_CXT.self;
1625                 MY_CXT.self = my_perl;
1626 #if (PERL_VERSION < 10) || (PERL_VERSION == 10 && PERL_SUBVERSION <= 0)
1627                 /* CLONE entered without a pointer table, so we can't safely clone static data */
1628                 if(!PL_ptr_table) {
1629                         for(i=0;i<N_CALLBACKS;i++) {
1630                                 MY_CXT.callback[i] = NULL;
1631                         }
1632                         MY_CXT.handles = newHV();
1633                 } else
1634 #endif
1635                 {
1636                         CLONE_PARAMS *clone_param;
1637 #if (PERL_VERSION > 13) || (PERL_VERSION == 13 && PERL_SUBVERSION >= 2)
1638                         clone_param = Perl_clone_params_new(parent, aTHX);
1639 #else
1640                         CLONE_PARAMS raw_param;
1641                         raw_param.flags = 0;
1642                         raw_param.proto_perl = parent;
1643                         raw_param.stashes = (AV*)sv_2mortal((SV*)newAV());
1644                         clone_param = &raw_param;
1645 #endif
1646                         for(i=0;i<N_CALLBACKS;i++) {
1647                                 MY_CXT.callback[i] = sv_dup(MY_CXT.callback[i], clone_param);
1648                         }
1649                         MY_CXT.handles = (HV*)sv_dup((SV*)MY_CXT.handles, clone_param);
1650 #if (PERL_VERSION > 13) || (PERL_VERSION == 13 && PERL_SUBVERSION >= 2)
1651                         Perl_clone_params_del(clone_param);
1652 #endif
1653                 }
1654 #endif
1655
1656 SV*
1657 fuse_get_context()
1658         PREINIT:
1659         struct fuse_context *fc;
1660         CODE:
1661         fc = fuse_get_context();
1662         if(fc) {
1663                 HV *hash = newHV();
1664                 (void) hv_store(hash, "uid",   3, newSViv(fc->uid), 0);
1665                 (void) hv_store(hash, "gid",   3, newSViv(fc->gid), 0);
1666                 (void) hv_store(hash, "pid",   3, newSViv(fc->pid), 0);
1667                 if (fc->private_data)
1668                         (void) hv_store(hash, "private", 7, fc->private_data, 0);
1669 #if FUSE_VERSION >= 28
1670                 (void) hv_store(hash, "umask", 5, newSViv(fc->umask), 0);
1671 #endif /* FUSE_VERSION >= 28 */
1672                 RETVAL = newRV_noinc((SV*)hash);
1673         } else {
1674                 XSRETURN_UNDEF;
1675         }
1676         OUTPUT:
1677         RETVAL
1678
1679 SV *
1680 fuse_version()
1681         CODE:
1682         RETVAL = newSVpvf("%d.%d", FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION);
1683         OUTPUT:
1684         RETVAL
1685
1686 SV *
1687 XATTR_CREATE()
1688         CODE:
1689         RETVAL = newSViv(XATTR_CREATE);
1690         OUTPUT:
1691         RETVAL
1692
1693 SV *
1694 XATTR_REPLACE()
1695         CODE:
1696         RETVAL = newSViv(XATTR_REPLACE);
1697         OUTPUT:
1698         RETVAL
1699
1700 void
1701 perl_fuse_main(...)
1702         PREINIT:
1703         struct fuse_operations fops;
1704         int i, debug;
1705         char *mountpoint;
1706         char *mountopts;
1707         struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
1708         struct fuse_chan *fc;
1709         dMY_CXT;
1710         INIT:
1711         if(items != N_CALLBACKS + 6) {
1712                 fprintf(stderr,"Perl<->C inconsistency or internal error\n");
1713                 XSRETURN_UNDEF;
1714         }
1715         memset(&fops, 0, sizeof(struct fuse_operations));
1716         CODE:
1717         debug = SvIV(ST(0));
1718         MY_CXT.threaded = SvIV(ST(1));
1719         MY_CXT.handles = (HV*)(sv_2mortal((SV*)(newHV())));
1720         if(MY_CXT.threaded) {
1721 #ifdef FUSE_USE_ITHREADS
1722                 master_interp = aTHX;
1723                 MUTEX_INIT(&MY_CXT.mutex);
1724                 SvSHARE((SV*)(MY_CXT.handles));
1725 #else
1726                 fprintf(stderr,"FUSE warning: Your script has requested multithreaded "
1727                                "mode, but your perl was not built with a supported "
1728                                "thread model. Threads are disabled.\n");
1729                 MY_CXT.threaded = 0;
1730 #endif
1731         }
1732         mountpoint = SvPV_nolen(ST(2));
1733         mountopts = SvPV_nolen(ST(3));
1734 #if FUSE_VERSION >= 28
1735         fops.flag_nullpath_ok = SvIV(ST(4));
1736 #endif /* FUSE_VERSION >= 28 */
1737         MY_CXT.utimens_as_array = SvIV(ST(5));
1738         for(i=0;i<N_CALLBACKS;i++) {
1739                 SV *var = ST(i+6);
1740                 /* allow symbolic references, or real code references. */
1741                 if(SvOK(var) && (SvPOK(var) || (SvROK(var) && SvTYPE(SvRV(var)) == SVt_PVCV))) {
1742                         void **tmp1 = (void**)&_available_ops, **tmp2 = (void**)&fops;
1743                         /* Dirty hack, to keep anything from overwriting the
1744                          * flag area with a pointer. There should never be
1745                          * anything passed as 'junk', but this prevents
1746                          * someone from doing it and screwing things up... */
1747                         if (i == 38)
1748                                 continue;
1749                         tmp2[i] = tmp1[i];
1750                         MY_CXT.callback[i] = var;
1751                 } else if(SvOK(var)) {
1752                         croak("invalid callback (%i) passed to perl_fuse_main "
1753                               "(%s is not a string, code ref, or undef).\n",
1754                               i+6,SvPVbyte_nolen(var));
1755                 } else {
1756                         MY_CXT.callback[i] = NULL;
1757                 }
1758         }
1759         /*
1760          * XXX: What comes here is just a ridiculous use of the option parsing API
1761          * to hack on compatibility with other parts of the new API. First and
1762          * foremost, real C argc/argv would be good to get at...
1763          */
1764         if ((mountopts || debug) && fuse_opt_add_arg(&args, "") == -1) {
1765                 fuse_opt_free_args(&args);
1766                 croak("out of memory\n");
1767         }
1768         if (mountopts && strcmp("", mountopts) &&
1769              (fuse_opt_add_arg(&args, "-o") == -1 ||
1770              fuse_opt_add_arg(&args, mountopts) == -1)) {
1771                 fuse_opt_free_args(&args);
1772                 croak("out of memory\n");
1773         }
1774         if (debug && fuse_opt_add_arg(&args, "-d") == -1) {
1775                 fuse_opt_free_args(&args);
1776                 croak("out of memory\n");
1777         }
1778         fc = fuse_mount(mountpoint,&args);
1779         if (fc == NULL)
1780                 croak("could not mount fuse filesystem!\n");
1781 #ifndef __NetBSD__
1782         if(MY_CXT.threaded) {
1783                 fuse_loop_mt(fuse_new(fc,&args,&fops,sizeof(fops),NULL));
1784         } else
1785 #endif
1786                 fuse_loop(fuse_new(fc,&args,&fops,sizeof(fops),NULL));
1787         fuse_unmount(mountpoint,fc);
1788         fuse_opt_free_args(&args);
1789
1790 #if FUSE_VERSION >= 28
1791
1792 void
1793 pollhandle_destroy(...)
1794     PREINIT:
1795         struct fuse_pollhandle *ph;
1796     INIT:
1797         if (items != 1) {
1798                 fprintf(stderr, "No pollhandle passed?\n");
1799                 XSRETURN_UNDEF;
1800         }
1801     CODE:
1802         ph = INT2PTR(struct fuse_pollhandle*, SvIV(ST(0)));
1803         fuse_pollhandle_destroy(ph);
1804
1805 int 
1806 notify_poll(...)
1807     PREINIT:
1808         struct fuse_pollhandle *ph;
1809     INIT:
1810         if (items != 1) {
1811                 fprintf(stderr, "No pollhandle passed?\n");
1812                 XSRETURN_UNDEF;
1813         }
1814     CODE:
1815         ph = INT2PTR(struct fuse_pollhandle*, SvIV(ST(0)));
1816         RETVAL = fuse_notify_poll(ph);
1817     OUTPUT:
1818         RETVAL
1819
1820 #endif