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