Fold margs and fargs together into args; this fixes the handling of
[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 /* Determine if threads support should be included */
9 #ifdef USE_ITHREADS
10 # ifdef I_PTHREAD
11 #  define FUSE_USE_ITHREADS
12 # else
13 #  warning "Sorry, I don't know how to handle ithreads on this architecture. Building non-threaded version"
14 # endif
15 #endif
16
17 /* Global Data */
18
19 #define MY_CXT_KEY "Fuse::_guts" XS_VERSION
20 #define N_CALLBACKS 29
21
22 typedef struct {
23         SV *callback[N_CALLBACKS];
24         HV *handles;
25         tTHX self;
26         int threaded;
27         perl_mutex mutex;
28 } my_cxt_t;
29 START_MY_CXT;
30
31 #ifdef FUSE_USE_ITHREADS
32 tTHX master_interp = NULL;
33
34 #define CLONE_INTERP(parent) S_clone_interp(parent)
35 tTHX S_clone_interp(tTHX parent) {
36         dMY_CXT_INTERP(parent);
37         if(MY_CXT.threaded) {
38                 MUTEX_LOCK(&MY_CXT.mutex);
39                 PERL_SET_CONTEXT(parent);
40                 dTHX;
41 #if (PERL_VERSION > 10) || (PERL_VERSION == 10 && PERL_SUBVERSION >= 1)
42                 tTHX child = perl_clone(parent, CLONEf_CLONE_HOST);
43 #else
44                 tTHX child = perl_clone(parent, CLONEf_CLONE_HOST | CLONEf_KEEP_PTR_TABLE);
45                 ptr_table_free(PL_ptr_table);
46                 PL_ptr_table = NULL;
47 #endif
48                 MUTEX_UNLOCK(&MY_CXT.mutex);
49                 return child;
50         }
51         return NULL;
52 }
53
54 # define FUSE_CONTEXT_PRE dTHX; if(!aTHX) aTHX = CLONE_INTERP(master_interp); { dMY_CXT; dSP;
55 # define FUSE_CONTEXT_POST }
56 #else
57 # define FUSE_CONTEXT_PRE dTHX; dMY_CXT; dSP;
58 # define FUSE_CONTEXT_POST
59 #endif
60
61 #undef DEBUGf
62 #if 0
63 #define DEBUGf(f, a...) fprintf(stderr, "%s:%d (%i): " f,__BASE_FILE__,__LINE__,sp-PL_stack_base ,##a )
64 #else
65 #define DEBUGf(a...)
66 #endif
67
68 #define FH_KEY(fi) sv_2mortal(newSViv((fi)->fh))
69 #define FH_GETHANDLE(fi) S_fh_get_handle(aTHX_ aMY_CXT_ fi)
70 #define FH_STOREHANDLE(fi,sv) S_fh_store_handle(aTHX_ aMY_CXT_ fi, sv)
71 #define FH_RELEASEHANDLE(fi) S_fh_release_handle(aTHX_ aMY_CXT_ fi)
72
73 SV *S_fh_get_handle(pTHX_ pMY_CXT_ struct fuse_file_info *fi) {
74         SV *val;
75         val = &PL_sv_undef;
76         if(fi->fh != 0) {
77                 HE *he;
78                 if((he = hv_fetch_ent(MY_CXT.handles, FH_KEY(fi), 0, 0))) {
79                         val = HeVAL(he);
80                         SvGETMAGIC(val);
81                 }
82         }
83         return val;
84 }
85
86 void S_fh_release_handle(pTHX_ pMY_CXT_ struct fuse_file_info *fi) {
87         if(fi->fh != 0) {
88                 (void)hv_delete_ent(MY_CXT.handles, FH_KEY(fi), G_DISCARD, 0);
89                 fi->fh = 0;
90         }
91 }
92
93 void S_fh_store_handle(pTHX_ pMY_CXT_ struct fuse_file_info *fi, SV *sv) {
94         if(SvOK(sv)) {
95 #ifdef FUSE_USE_ITHREADS
96                 if(MY_CXT.threaded) {
97                         SvSHARE(sv);
98                 }
99 #endif
100                 MAGIC *mg = (SvTYPE(sv) == SVt_PVMG) ? mg_find(sv, PERL_MAGIC_shared_scalar) : NULL;
101                 fi->fh = mg ? PTR2IV(mg->mg_ptr) : PTR2IV(sv);
102                 if(hv_store_ent(MY_CXT.handles, FH_KEY(fi), SvREFCNT_inc(sv), 0) == NULL) {
103                         SvREFCNT_dec(sv);
104                 }
105                 SvSETMAGIC(sv);
106         }
107 }
108
109 int _PLfuse_getattr(const char *file, struct stat *result) {
110         int rv;
111         FUSE_CONTEXT_PRE;
112         DEBUGf("getattr begin: %s\n",file);
113         ENTER;
114         SAVETMPS;
115         PUSHMARK(SP);
116         XPUSHs(sv_2mortal(newSVpv(file,strlen(file))));
117         PUTBACK;
118         rv = call_sv(MY_CXT.callback[0],G_ARRAY);
119         SPAGAIN;
120         if(rv != 13) {
121                 if(rv > 1) {
122                         fprintf(stderr,"inappropriate number of returned values from getattr\n");
123                         rv = -ENOSYS;
124                 } else if(rv)
125                         rv = POPi;
126                 else
127                         rv = -ENOENT;
128         } else {
129                 result->st_blocks = POPi;
130                 result->st_blksize = POPi;
131                 result->st_ctime = POPi;
132                 result->st_mtime = POPi;
133                 result->st_atime = POPi;
134                 result->st_size = POPn; // we pop double here to support files larger than 4Gb (long limit)
135                 result->st_rdev = POPi;
136                 result->st_gid = POPi;
137                 result->st_uid = POPi;
138                 result->st_nlink = POPi;
139                 result->st_mode = POPi;
140                 result->st_ino   = POPi;
141                 result->st_dev = POPi;
142                 rv = 0;
143         }
144         FREETMPS;
145         LEAVE;
146         PUTBACK;
147         DEBUGf("getattr end: %i\n",rv);
148         FUSE_CONTEXT_POST;
149         return rv;
150 }
151
152 int _PLfuse_readlink(const char *file,char *buf,size_t buflen) {
153         int rv;
154         if(buflen < 1)
155                 return EINVAL;
156         FUSE_CONTEXT_PRE;
157         DEBUGf("readlink begin\n");
158         ENTER;
159         SAVETMPS;
160         PUSHMARK(SP);
161         XPUSHs(sv_2mortal(newSVpv(file,0)));
162         PUTBACK;
163         rv = call_sv(MY_CXT.callback[1],G_SCALAR);
164         SPAGAIN;
165         if(!rv)
166                 rv = -ENOENT;
167         else {
168                 SV *mysv = POPs;
169                 if(SvTYPE(mysv) == SVt_IV || SvTYPE(mysv) == SVt_NV)
170                         rv = SvIV(mysv);
171                 else {
172                         strncpy(buf,SvPV_nolen(mysv),buflen);
173                         rv = 0;
174                 }
175         }
176         FREETMPS;
177         LEAVE;
178         buf[buflen-1] = 0;
179         PUTBACK;
180         DEBUGf("readlink end: %i\n",rv);
181         FUSE_CONTEXT_POST;
182         return rv;
183 }
184
185 int _PLfuse_opendir(const char *file, struct fuse_file_info *info) {
186     croak("opendir NOT IMPLEMENTED");
187 }
188 int _PLfuse_releasedir(const char *file, struct fuse_file_info *info) {
189     croak("releasedir NOT IMPLEMENTED");
190 }
191 int _PLfuse_fsyncdir(const char *file, struct fuse_file_info *info) {
192     croak("fsyncdir NOT IMPLEMENTED");
193 }
194
195 int _PLfuse_readdir(const char *file, void *dirh, fuse_fill_dir_t dirfil, off_t off, struct fuse_file_info *fi) {
196         int prv, rv, offset;
197     SV *entry;
198         FUSE_CONTEXT_PRE;
199         DEBUGf("readdir begin\n");
200         ENTER;
201         SAVETMPS;
202         PUSHMARK(SP);
203         XPUSHs(sv_2mortal(newSVpv(file,0)));
204         XPUSHs(sv_2mortal(newSViv(off)));
205         PUTBACK;
206         prv = call_sv(MY_CXT.callback[26],G_ARRAY);
207         SPAGAIN;
208         if(3 == prv) {
209         rv      = POPi;
210         offset  = POPi;
211         entry   = POPs;
212         if(SvOK(entry))
213             dirfil(dirh,SvPV_nolen(entry),NULL,offset);
214         } else {
215                 fprintf(stderr,"readdir() handler didn't return 2 values!\n");
216                 rv = -ENOSYS;
217         }
218         FREETMPS;
219         LEAVE;
220         PUTBACK;
221         DEBUGf("readdir end: %i\n",rv);
222         FUSE_CONTEXT_POST;
223         return rv;
224 }
225
226 int _PLfuse_getdir(const char *file, fuse_dirh_t dirh, fuse_dirfil_t dirfil) {
227         int prv, rv;
228         SV **swp;
229         FUSE_CONTEXT_PRE;
230         DEBUGf("getdir begin\n");
231         ENTER;
232         SAVETMPS;
233         PUSHMARK(SP);
234         XPUSHs(sv_2mortal(newSVpv(file,0)));
235         PUTBACK;
236         prv = call_sv(MY_CXT.callback[2],G_ARRAY);
237         SPAGAIN;
238         if(prv) {
239                 /* Should yield the bottom of the current stack... */
240                 swp = &TOPs - prv + 1;
241                 rv = POPi;
242                 /* Sort of a hack to walk the stack in order, instead of reverse
243                  * order - trying to explain to potential users why they need to
244                  * reverse the order of this array would be confusing, at best. */
245                 while (swp <= &TOPs)
246                         dirfil(dirh,SvPVx_nolen(*(swp++)),0,0);
247         } else {
248                 fprintf(stderr,"getdir() handler returned nothing!\n");
249                 rv = -ENOSYS;
250         }
251         FREETMPS;
252         LEAVE;
253         PUTBACK;
254         DEBUGf("getdir end: %i\n",rv);
255         FUSE_CONTEXT_POST;
256         return rv;
257 }
258
259 int _PLfuse_mknod (const char *file, mode_t mode, dev_t dev) {
260         int rv;
261         FUSE_CONTEXT_PRE;
262         DEBUGf("mknod begin\n");
263         ENTER;
264         SAVETMPS;
265         PUSHMARK(SP);
266         XPUSHs(sv_2mortal(newSVpv(file,0)));
267         XPUSHs(sv_2mortal(newSViv(mode)));
268         XPUSHs(sv_2mortal(newSViv(dev)));
269         PUTBACK;
270         rv = call_sv(MY_CXT.callback[3],G_SCALAR);
271         SPAGAIN;
272         if(rv)
273                 rv = POPi;
274         else
275                 rv = 0;
276         FREETMPS;
277         LEAVE;
278         PUTBACK;
279         DEBUGf("mknod end: %i\n",rv);
280         FUSE_CONTEXT_POST;
281         return rv;
282 }
283
284 int _PLfuse_mkdir (const char *file, mode_t mode) {
285         int rv;
286         FUSE_CONTEXT_PRE;
287         DEBUGf("mkdir begin\n");
288         ENTER;
289         SAVETMPS;
290         PUSHMARK(SP);
291         XPUSHs(sv_2mortal(newSVpv(file,0)));
292         XPUSHs(sv_2mortal(newSViv(mode)));
293         PUTBACK;
294         rv = call_sv(MY_CXT.callback[4],G_SCALAR);
295         SPAGAIN;
296         if(rv)
297                 rv = POPi;
298         else
299                 rv = 0;
300         FREETMPS;
301         LEAVE;
302         PUTBACK;
303         DEBUGf("mkdir end: %i\n",rv);
304         FUSE_CONTEXT_POST;
305         return rv;
306 }
307
308
309 int _PLfuse_unlink (const char *file) {
310         int rv;
311         FUSE_CONTEXT_PRE;
312         DEBUGf("unlink begin\n");
313         ENTER;
314         SAVETMPS;
315         PUSHMARK(SP);
316         XPUSHs(sv_2mortal(newSVpv(file,0)));
317         PUTBACK;
318         rv = call_sv(MY_CXT.callback[5],G_SCALAR);
319         SPAGAIN;
320         if(rv)
321                 rv = POPi;
322         else
323                 rv = 0;
324         FREETMPS;
325         LEAVE;
326         PUTBACK;
327         DEBUGf("unlink end: %i\n",rv);
328         FUSE_CONTEXT_POST;
329         return rv;
330 }
331
332 int _PLfuse_rmdir (const char *file) {
333         int rv;
334         FUSE_CONTEXT_PRE;
335         DEBUGf("rmdir begin\n");
336         ENTER;
337         SAVETMPS;
338         PUSHMARK(SP);
339         XPUSHs(sv_2mortal(newSVpv(file,0)));
340         PUTBACK;
341         rv = call_sv(MY_CXT.callback[6],G_SCALAR);
342         SPAGAIN;
343         if(rv)
344                 rv = POPi;
345         else
346                 rv = 0;
347         FREETMPS;
348         LEAVE;
349         PUTBACK;
350         DEBUGf("rmdir end: %i\n",rv);
351         FUSE_CONTEXT_POST;
352         return rv;
353 }
354
355 int _PLfuse_symlink (const char *file, const char *new) {
356         int rv;
357         FUSE_CONTEXT_PRE;
358         DEBUGf("symlink begin\n");
359         ENTER;
360         SAVETMPS;
361         PUSHMARK(SP);
362         XPUSHs(sv_2mortal(newSVpv(file,0)));
363         XPUSHs(sv_2mortal(newSVpv(new,0)));
364         PUTBACK;
365         rv = call_sv(MY_CXT.callback[7],G_SCALAR);
366         SPAGAIN;
367         if(rv)
368                 rv = POPi;
369         else
370                 rv = 0;
371         FREETMPS;
372         LEAVE;
373         PUTBACK;
374         DEBUGf("symlink end: %i\n",rv);
375         FUSE_CONTEXT_POST;
376         return rv;
377 }
378
379 int _PLfuse_rename (const char *file, const char *new) {
380         int rv;
381         FUSE_CONTEXT_PRE;
382         DEBUGf("rename begin\n");
383         ENTER;
384         SAVETMPS;
385         PUSHMARK(SP);
386         XPUSHs(sv_2mortal(newSVpv(file,0)));
387         XPUSHs(sv_2mortal(newSVpv(new,0)));
388         PUTBACK;
389         rv = call_sv(MY_CXT.callback[8],G_SCALAR);
390         SPAGAIN;
391         if(rv)
392                 rv = POPi;
393         else
394                 rv = 0;
395         FREETMPS;
396         LEAVE;
397         PUTBACK;
398         DEBUGf("rename end: %i\n",rv);
399         FUSE_CONTEXT_POST;
400         return rv;
401 }
402
403 int _PLfuse_link (const char *file, const char *new) {
404         int rv;
405         FUSE_CONTEXT_PRE;
406         DEBUGf("link begin\n");
407         ENTER;
408         SAVETMPS;
409         PUSHMARK(SP);
410         XPUSHs(sv_2mortal(newSVpv(file,0)));
411         XPUSHs(sv_2mortal(newSVpv(new,0)));
412         PUTBACK;
413         rv = call_sv(MY_CXT.callback[9],G_SCALAR);
414         SPAGAIN;
415         if(rv)
416                 rv = POPi;
417         else
418                 rv = 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         if(rv)
440                 rv = POPi;
441         else
442                 rv = 0;
443         FREETMPS;
444         LEAVE;
445         PUTBACK;
446         DEBUGf("chmod end: %i\n",rv);
447         FUSE_CONTEXT_POST;
448         return rv;
449 }
450
451 int _PLfuse_chown (const char *file, uid_t uid, gid_t gid) {
452         int rv;
453         FUSE_CONTEXT_PRE;
454         DEBUGf("chown begin\n");
455         ENTER;
456         SAVETMPS;
457         PUSHMARK(SP);
458         XPUSHs(sv_2mortal(newSVpv(file,0)));
459         XPUSHs(sv_2mortal(newSViv(uid)));
460         XPUSHs(sv_2mortal(newSViv(gid)));
461         PUTBACK;
462         rv = call_sv(MY_CXT.callback[11],G_SCALAR);
463         SPAGAIN;
464         if(rv)
465                 rv = POPi;
466         else
467                 rv = 0;
468         FREETMPS;
469         LEAVE;
470         PUTBACK;
471         DEBUGf("chown end: %i\n",rv);
472         FUSE_CONTEXT_POST;
473         return rv;
474 }
475
476 int _PLfuse_truncate (const char *file, off_t off) {
477         int rv;
478 #ifndef PERL_HAS_64BITINT
479         char *temp;
480 #endif
481         FUSE_CONTEXT_PRE;
482         DEBUGf("truncate begin\n");
483         ENTER;
484         SAVETMPS;
485         PUSHMARK(SP);
486         XPUSHs(sv_2mortal(newSVpv(file,0)));
487 #ifdef PERL_HAS_64BITINT
488         XPUSHs(sv_2mortal(newSViv(off)));
489 #else
490         asprintf(&temp, "%llu", off);
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         if(rv)
498                 rv = POPi;
499         else
500                 rv = 0;
501         FREETMPS;
502         LEAVE;
503         PUTBACK;
504         DEBUGf("truncate end: %i\n",rv);
505         FUSE_CONTEXT_POST;
506         return rv;
507 }
508
509 int _PLfuse_utime (const char *file, struct utimbuf *uti) {
510         int rv;
511         FUSE_CONTEXT_PRE;
512         DEBUGf("utime begin\n");
513         ENTER;
514         SAVETMPS;
515         PUSHMARK(SP);
516         XPUSHs(sv_2mortal(newSVpv(file,0)));
517         XPUSHs(sv_2mortal(newSViv(uti->actime)));
518         XPUSHs(sv_2mortal(newSViv(uti->modtime)));
519         PUTBACK;
520         rv = call_sv(MY_CXT.callback[13],G_SCALAR);
521         SPAGAIN;
522         if(rv)
523                 rv = POPi;
524         else
525                 rv = 0;
526         FREETMPS;
527         LEAVE;
528         PUTBACK;
529         DEBUGf("utime end: %i\n",rv);
530         FUSE_CONTEXT_POST;
531         return rv;
532 }
533
534 int _PLfuse_open (const char *file, struct fuse_file_info *fi) {
535         int rv;
536         int flags = fi->flags;
537         HV *fihash;
538         FUSE_CONTEXT_PRE;
539         DEBUGf("open begin\n");
540         ENTER;
541         SAVETMPS;
542         PUSHMARK(SP);
543         XPUSHs(sv_2mortal(newSVpv(file,0)));
544         XPUSHs(sv_2mortal(newSViv(flags)));
545         /* Create a hashref containing the details from fi
546          * which we can look at or modify.
547          */
548         fi->fh = 0; /* Ensure it starts with 0 - important if they don't set it */
549         fihash = newHV();
550 #if FUSE_VERSION >= 24
551         hv_store(fihash, "direct_io", 9, newSViv(fi->direct_io), 0);
552         hv_store(fihash, "keep_cache", 10, newSViv(fi->keep_cache), 0);
553 #endif
554 #if FUSE_VERSION >= 29
555         hv_store(fihash, "nonseekable", 11, newSViv(fi->nonseekable), 0);
556 #endif
557         XPUSHs(sv_2mortal(newRV_noinc((SV*) fihash)));
558         /* All hashref things done */
559
560         PUTBACK;
561         /* Open called with filename, flags */
562         rv = call_sv(MY_CXT.callback[14],G_ARRAY);
563         SPAGAIN;
564         if(rv) {
565                 if(rv > 1) {
566                         FH_STOREHANDLE(fi,POPs);
567                 }
568                 rv = POPi;
569         }
570         else
571                 rv = 0;
572         if (rv == 0)
573         {
574                 /* Success, so copy the file handle which they returned */
575 #if FUSE_VERSION >= 24
576                 SV **svp;
577                 svp = hv_fetch(fihash, "direct_io", 9, 0);
578                 if (svp != NULL)
579                 {
580                         fi->direct_io = SvIV(*svp);
581                 }
582                 svp = hv_fetch(fihash, "keep_cache", 10, 0);
583                 if (svp != NULL)
584                 {
585                         fi->keep_cache = SvIV(*svp);
586                 }
587 #endif
588 #if FUSE_VERSION >= 29
589                 svp = hv_fetch(fihash, "nonseekable", 11, 0);
590                 if (svp != NULL)
591                 {
592                         fi->nonseekable = SvIV(*svp);
593                 }
594 #endif
595         }
596         FREETMPS;
597         LEAVE;
598         PUTBACK;
599         DEBUGf("open end: %i\n",rv);
600         FUSE_CONTEXT_POST;
601         return rv;
602 }
603
604 int _PLfuse_read (const char *file, char *buf, size_t buflen, off_t off, struct fuse_file_info *fi) {
605         int rv;
606 #ifndef PERL_HAS_64BITINT
607         char *temp;
608 #endif
609         FUSE_CONTEXT_PRE;
610         DEBUGf("read begin\n");
611         ENTER;
612         SAVETMPS;
613         PUSHMARK(SP);
614         XPUSHs(sv_2mortal(newSVpv(file,0)));
615         XPUSHs(sv_2mortal(newSViv(buflen)));
616 #ifdef PERL_HAS_64BITINT
617         XPUSHs(sv_2mortal(newSViv(off)));
618 #else
619         asprintf(&temp, "%llu", off);
620         XPUSHs(sv_2mortal(newSVpv(temp, 0)));
621         free(temp);
622 #endif
623         XPUSHs(FH_GETHANDLE(fi));
624         PUTBACK;
625         rv = call_sv(MY_CXT.callback[15],G_SCALAR);
626         SPAGAIN;
627         if(!rv)
628                 rv = -ENOENT;
629         else {
630                 SV *mysv = POPs;
631                 if(SvTYPE(mysv) == SVt_NV || SvTYPE(mysv) == SVt_IV)
632                         rv = SvIV(mysv);
633                 else {
634                         if(SvPOK(mysv)) {
635                                 rv = SvCUR(mysv);
636                         } else {
637                                 rv = 0;
638                         }
639                         if(rv > buflen)
640                                 croak("read() handler returned more than buflen! (%i > %i)",rv,buflen);
641                         if(rv)
642                                 memcpy(buf,SvPV_nolen(mysv),rv);
643                 }
644         }
645         FREETMPS;
646         LEAVE;
647         PUTBACK;
648         DEBUGf("read end: %i\n",rv);
649         FUSE_CONTEXT_POST;
650         return rv;
651 }
652
653 int _PLfuse_write (const char *file, const char *buf, size_t buflen, off_t off, struct fuse_file_info *fi) {
654         int rv;
655 #ifndef PERL_HAS_64BITINT
656         char *temp;
657 #endif
658         FUSE_CONTEXT_PRE;
659         DEBUGf("write begin\n");
660         ENTER;
661         SAVETMPS;
662         PUSHMARK(SP);
663         XPUSHs(sv_2mortal(newSVpv(file,0)));
664         XPUSHs(sv_2mortal(newSVpvn(buf,buflen)));
665 #ifdef PERL_HAS_64BITINT
666         XPUSHs(sv_2mortal(newSViv(off)));
667 #else
668         asprintf(&temp, "%llu", off);
669         XPUSHs(sv_2mortal(newSVpv(temp, 0)));
670         free(temp);
671 #endif
672         XPUSHs(FH_GETHANDLE(fi));
673         PUTBACK;
674         rv = call_sv(MY_CXT.callback[16],G_SCALAR);
675         SPAGAIN;
676         if(rv)
677                 rv = POPi;
678         else
679                 rv = 0;
680         FREETMPS;
681         LEAVE;
682         PUTBACK;
683         DEBUGf("write end: %i\n",rv);
684         FUSE_CONTEXT_POST;
685         return rv;
686 }
687
688 int _PLfuse_statfs (const char *file, struct statvfs *st) {
689         int rv;
690         FUSE_CONTEXT_PRE;
691         DEBUGf("statfs begin\n");
692         ENTER;
693         SAVETMPS;
694         PUSHMARK(SP);
695         PUTBACK;
696         rv = call_sv(MY_CXT.callback[17],G_ARRAY);
697         SPAGAIN;
698         DEBUGf("statfs got %i params\n",rv);
699         if(rv == 6 || rv == 7) {
700                 st->f_bsize     = POPi;
701                 st->f_bfree     = POPi;
702                 st->f_blocks    = POPi;
703                 st->f_ffree     = POPi;
704                 st->f_files     = POPi;
705                 st->f_namemax   = POPi;
706                 /* zero and fill-in other */
707                 st->f_fsid = 0;
708                 st->f_frsize = 4096;
709                 st->f_flag = 0;
710                 st->f_bavail = st->f_bfree;
711                 st->f_favail = st->f_ffree;
712
713                 if(rv == 7)
714                         rv = POPi;
715                 else
716                         rv = 0;
717         } else
718         if(rv > 1)
719                 croak("inappropriate number of returned values from statfs");
720         else
721         if(rv)
722                 rv = POPi;
723         else
724                 rv = -ENOSYS;
725         FREETMPS;
726         LEAVE;
727         PUTBACK;
728         DEBUGf("statfs end: %i\n",rv);
729         FUSE_CONTEXT_POST;
730         return rv;
731 }
732
733 int _PLfuse_flush (const char *file, struct fuse_file_info *fi) {
734         int rv;
735         FUSE_CONTEXT_PRE;
736         DEBUGf("flush begin\n");
737         ENTER;
738         SAVETMPS;
739         PUSHMARK(SP);
740         XPUSHs(sv_2mortal(newSVpv(file,0)));
741         XPUSHs(FH_GETHANDLE(fi));
742         PUTBACK;
743         rv = call_sv(MY_CXT.callback[18],G_SCALAR);
744         SPAGAIN;
745         if(rv)
746                 rv = POPi;
747         else
748                 rv = 0;
749         FREETMPS;
750         LEAVE;
751         PUTBACK;
752         DEBUGf("flush end: %i\n",rv);
753         FUSE_CONTEXT_POST;
754         return rv;
755 }
756
757 int _PLfuse_release (const char *file, struct fuse_file_info *fi) {
758         int rv;
759         int flags = fi->flags;
760         FUSE_CONTEXT_PRE;
761         DEBUGf("release begin\n");
762         ENTER;
763         SAVETMPS;
764         PUSHMARK(SP);
765         XPUSHs(sv_2mortal(newSVpv(file,0)));
766         XPUSHs(sv_2mortal(newSViv(flags)));
767         XPUSHs(FH_GETHANDLE(fi));
768         PUTBACK;
769         rv = call_sv(MY_CXT.callback[19],G_SCALAR);
770         SPAGAIN;
771         if(rv)
772                 rv = POPi;
773         else
774                 rv = 0;
775         FH_RELEASEHANDLE(fi);
776         FREETMPS;
777         LEAVE;
778         PUTBACK;
779         DEBUGf("release end: %i\n",rv);
780         FUSE_CONTEXT_POST;
781         return rv;
782 }
783
784 int _PLfuse_fsync (const char *file, int datasync, struct fuse_file_info *fi) {
785         int rv;
786         int flags = fi->flags;
787         FUSE_CONTEXT_PRE;
788         DEBUGf("fsync begin\n");
789         ENTER;
790         SAVETMPS;
791         PUSHMARK(SP);
792         XPUSHs(sv_2mortal(newSVpv(file,0)));
793         XPUSHs(sv_2mortal(newSViv(flags)));
794         XPUSHs(FH_GETHANDLE(fi));
795         PUTBACK;
796         rv = call_sv(MY_CXT.callback[20],G_SCALAR);
797         SPAGAIN;
798         if(rv)
799                 rv = POPi;
800         else
801                 rv = 0;
802         FREETMPS;
803         LEAVE;
804         PUTBACK;
805         DEBUGf("fsync end: %i\n",rv);
806         FUSE_CONTEXT_POST;
807         return rv;
808 }
809
810 int _PLfuse_setxattr (const char *file, const char *name, const char *buf, size_t buflen, int flags) {
811         int rv;
812         FUSE_CONTEXT_PRE;
813         DEBUGf("setxattr begin\n");
814         ENTER;
815         SAVETMPS;
816         PUSHMARK(SP);
817         XPUSHs(sv_2mortal(newSVpv(file,0)));
818         XPUSHs(sv_2mortal(newSVpv(name,0)));
819         XPUSHs(sv_2mortal(newSVpvn(buf,buflen)));
820         XPUSHs(sv_2mortal(newSViv(flags)));
821         PUTBACK;
822         rv = call_sv(MY_CXT.callback[21],G_SCALAR);
823         SPAGAIN;
824         if(rv)
825                 rv = POPi;
826         else
827                 rv = 0;
828         FREETMPS;
829         LEAVE;
830         PUTBACK;
831         DEBUGf("setxattr end: %i\n",rv);
832         FUSE_CONTEXT_POST;
833         return rv;
834 }
835
836 int _PLfuse_getxattr (const char *file, const char *name, char *buf, size_t buflen) {
837         int rv;
838         FUSE_CONTEXT_PRE;
839         DEBUGf("getxattr begin\n");
840         ENTER;
841         SAVETMPS;
842         PUSHMARK(SP);
843         XPUSHs(sv_2mortal(newSVpv(file,0)));
844         XPUSHs(sv_2mortal(newSVpv(name,0)));
845         PUTBACK;
846         rv = call_sv(MY_CXT.callback[22],G_SCALAR);
847         SPAGAIN;
848         if(!rv)
849                 rv = -ENOENT;
850         else {
851                 SV *mysv = POPs;
852
853                 rv = 0;
854                 if(SvTYPE(mysv) == SVt_NV || SvTYPE(mysv) == SVt_IV)
855                         rv = SvIV(mysv);
856                 else {
857                         if(SvPOK(mysv)) {
858                                 rv = SvCUR(mysv);
859                         } else {
860                                 rv = 0;
861                         }
862                         if ((rv > 0) && (buflen > 0))
863                         {
864                                 if(rv > buflen)
865                                         rv = -ERANGE;
866                                 else
867                                         memcpy(buf,SvPV_nolen(mysv),rv);
868                         }
869                 }
870         }
871         FREETMPS;
872         LEAVE;
873         PUTBACK;
874         DEBUGf("getxattr end: %i\n",rv);
875         FUSE_CONTEXT_POST;
876         return rv;
877 }
878
879 int _PLfuse_listxattr (const char *file, char *list, size_t size) {
880         int prv, rv;
881         FUSE_CONTEXT_PRE;
882         DEBUGf("listxattr begin\n");
883         ENTER;
884         SAVETMPS;
885         PUSHMARK(SP);
886         XPUSHs(sv_2mortal(newSVpv(file,0)));
887         PUTBACK;
888         prv = call_sv(MY_CXT.callback[23],G_ARRAY);
889         SPAGAIN;
890         if(!prv)
891                 rv = -ENOENT;
892         else {
893
894                 char *p = list;
895                 int spc = size;
896                 int total_len = 0;
897
898                 rv = POPi;
899                 prv--;
900
901                 /* Always nul terminate */
902                 if (list && (size > 0))
903                         list[0] = '\0';
904
905                 while (prv > 0)
906                 {
907                         SV *mysv = POPs;
908                         prv--;
909
910                         if (SvPOK(mysv)) {
911                                 /* Copy nul too */
912                                 int s = SvCUR(mysv) + 1;
913                                 total_len += s;
914
915                                 if (p && (size > 0) && (spc >= s))
916                                 {
917                                         memcpy(p,SvPV_nolen(mysv),s);
918                                         p += s;
919                                         spc -= s;
920                                 }
921                         }
922                 }
923
924                 /*
925                  * If the Perl returned an error, return that.
926                  * Otherwise check that the buffer was big enough.
927                  */
928                 if (rv == 0)
929                 {
930                         rv = total_len;
931                         if ((size > 0) && (size < total_len))
932                                 rv = -ERANGE;
933                 }
934         }
935         FREETMPS;
936         LEAVE;
937         PUTBACK;
938         DEBUGf("listxattr end: %i\n",rv);
939         FUSE_CONTEXT_POST;
940         return rv;
941 }
942
943 int _PLfuse_removexattr (const char *file, const char *name) {
944         int rv;
945         FUSE_CONTEXT_PRE;
946         DEBUGf("removexattr begin\n");
947         ENTER;
948         SAVETMPS;
949         PUSHMARK(SP);
950         XPUSHs(sv_2mortal(newSVpv(file,0)));
951         XPUSHs(sv_2mortal(newSVpv(name,0)));
952         PUTBACK;
953         rv = call_sv(MY_CXT.callback[24],G_SCALAR);
954         SPAGAIN;
955         if(rv)
956                 rv = POPi;
957         else
958                 rv = 0;
959         FREETMPS;
960         LEAVE;
961         PUTBACK;
962         DEBUGf("removexattr end: %i\n",rv);
963         FUSE_CONTEXT_POST;
964         return rv;
965 }
966
967 struct fuse_operations _available_ops = {
968 getattr:                _PLfuse_getattr,
969 readlink:               _PLfuse_readlink,
970 getdir:                 _PLfuse_getdir,
971 mknod:                  _PLfuse_mknod,
972 mkdir:                  _PLfuse_mkdir,
973 unlink:                 _PLfuse_unlink,
974 rmdir:                  _PLfuse_rmdir,
975 symlink:                _PLfuse_symlink,
976 rename:                 _PLfuse_rename,
977 link:                   _PLfuse_link,
978 chmod:                  _PLfuse_chmod,
979 chown:                  _PLfuse_chown,
980 truncate:               _PLfuse_truncate,
981 utime:                  _PLfuse_utime,
982 open:                   _PLfuse_open,
983 read:                   _PLfuse_read,
984 write:                  _PLfuse_write,
985 statfs:                 _PLfuse_statfs,
986 flush:                  _PLfuse_flush,
987 release:                _PLfuse_release,
988 fsync:                  _PLfuse_fsync,
989 setxattr:               _PLfuse_setxattr,
990 getxattr:               _PLfuse_getxattr,
991 listxattr:              _PLfuse_listxattr,
992 removexattr:            _PLfuse_removexattr,
993 opendir:                _PLfuse_opendir,
994 readdir:                _PLfuse_readdir,
995 releasedir:             _PLfuse_releasedir,
996 fsyncdir:               _PLfuse_fsyncdir,
997 };
998
999 MODULE = Fuse           PACKAGE = Fuse
1000 PROTOTYPES: DISABLE
1001
1002 BOOT:
1003         MY_CXT_INIT;
1004         MY_CXT.self = aTHX;
1005
1006 void
1007 CLONE(...)
1008         PREINIT:
1009                 int i;
1010                 dTHX;
1011         CODE:
1012                 MY_CXT_CLONE;
1013                 tTHX parent = MY_CXT.self;
1014                 MY_CXT.self = my_perl;
1015 #if (PERL_VERSION < 10) || (PERL_VERSION == 10 && PERL_SUBVERSION <= 0)
1016                 /* CLONE entered without a pointer table, so we can't safely clone static data */
1017                 if(!PL_ptr_table) {
1018                         for(i=0;i<N_CALLBACKS;i++) {
1019                                 MY_CXT.callback[i] = NULL;
1020                         }
1021                         MY_CXT.handles = newHV();
1022                 } else
1023 #endif
1024                 {
1025                         CLONE_PARAMS *clone_param;
1026 #if (PERL_VERSION > 13) || (PERL_VERSION == 13 && PERL_SUBVERSION >= 2)
1027                         clone_param = clone_params_new(parent, aTHX);
1028 #else
1029                         CLONE_PARAMS raw_param;
1030                         raw_param.flags = 0;
1031                         raw_param.proto_perl = parent;
1032                         raw_param.stashes = (AV*)sv_2mortal((SV*)newAV());
1033                         clone_param = &raw_param;
1034 #endif
1035                         for(i=0;i<N_CALLBACKS;i++) {
1036                                 MY_CXT.callback[i] = sv_dup(MY_CXT.callback[i], clone_param);
1037                         }
1038                         MY_CXT.handles = (HV*)sv_dup((SV*)MY_CXT.handles, clone_param);
1039 #if (PERL_VERSION > 13) || (PERL_VERSION == 13 && PERL_SUBVERSION >= 2)
1040                         clone_params_del(clone_param);
1041 #endif
1042                 }
1043
1044 SV*
1045 fuse_get_context()
1046         PREINIT:
1047         struct fuse_context *fc;
1048         CODE:
1049         fc = fuse_get_context();
1050         if(fc) {
1051                 HV *hash = newHV();
1052                 hv_store(hash, "uid", 3, newSViv(fc->uid), 0);
1053                 hv_store(hash, "gid", 3, newSViv(fc->gid), 0);
1054                 hv_store(hash, "pid", 3, newSViv(fc->pid), 0);
1055                 RETVAL = newRV_noinc((SV*)hash);
1056         } else {
1057                 XSRETURN_UNDEF;
1058         }
1059         OUTPUT:
1060         RETVAL
1061
1062 SV *
1063 fuse_version()
1064         CODE:
1065         RETVAL = newSVpvf("%d.%d", FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION);
1066         OUTPUT:
1067         RETVAL
1068
1069 void
1070 perl_fuse_main(...)
1071         PREINIT:
1072         struct fuse_operations fops = 
1073                 {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
1074                  NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
1075         int i, debug;
1076         char *mountpoint;
1077         char *mountopts;
1078         struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
1079         struct fuse_chan *fc;
1080         dMY_CXT;
1081         INIT:
1082         if(items != N_CALLBACKS + 5) {
1083                 fprintf(stderr,"Perl<->C inconsistency or internal error\n");
1084                 XSRETURN_UNDEF;
1085         }
1086         CODE:
1087         debug = SvIV(ST(0));
1088         MY_CXT.threaded = SvIV(ST(1));
1089         MY_CXT.handles = (HV*)(sv_2mortal((SV*)(newHV())));
1090         if(MY_CXT.threaded) {
1091 #ifdef FUSE_USE_ITHREADS
1092                 master_interp = aTHX;
1093                 MUTEX_INIT(&MY_CXT.mutex);
1094                 SvSHARE((SV*)(MY_CXT.handles));
1095 #else
1096                 fprintf(stderr,"FUSE warning: Your script has requested multithreaded "
1097                                "mode, but your perl was not built with a supported "
1098                                "thread model. Threads are disabled.\n");
1099                 MY_CXT.threaded = 0;
1100 #endif
1101         }
1102         mountpoint = SvPV_nolen(ST(2));
1103         mountopts = SvPV_nolen(ST(3));
1104 #if FUSE_VERSION >= 28
1105         fops.flag_nullpath_ok = SvIV(ST(4));
1106 #endif /* FUSE_VERSION >= 28 */
1107         for(i=0;i<N_CALLBACKS;i++) {
1108                 SV *var = ST(i+5);
1109                 /* allow symbolic references, or real code references. */
1110                 if(SvOK(var) && (SvPOK(var) || (SvROK(var) && SvTYPE(SvRV(var)) == SVt_PVCV))) {
1111                         void **tmp1 = (void**)&_available_ops, **tmp2 = (void**)&fops;
1112                         /* Dirty hack, to keep anything from overwriting the
1113                          * flag area with a pointer. There should never be
1114                          * anything passed as 'junk', but this prevents
1115                          * someone from doing it and screwing things up... */
1116                         if (i == 38)
1117                                 continue;
1118                         tmp2[i] = tmp1[i];
1119                         MY_CXT.callback[i] = var;
1120                 } else if(SvOK(var)) {
1121                         croak("invalid callback (%i) passed to perl_fuse_main "
1122                               "(%s is not a string, code ref, or undef).\n",
1123                               i+4,SvPVbyte_nolen(var));
1124                 } else {
1125                         MY_CXT.callback[i] = NULL;
1126                 }
1127         }
1128         /*
1129          * XXX: What comes here is just a ridiculous use of the option parsing API
1130          * to hack on compatibility with other parts of the new API. First and
1131          * foremost, real C argc/argv would be good to get at...
1132          */
1133         if (mountopts &&
1134             (fuse_opt_add_arg(&args, "") == -1 ||
1135              fuse_opt_add_arg(&args, "-o") == -1 ||
1136              fuse_opt_add_arg(&args, mountopts) == -1)) {
1137                 fuse_opt_free_args(&args);
1138                 croak("out of memory\n");
1139         }
1140         if (debug && fuse_opt_add_arg(&args, "-d") == -1) {
1141                 fuse_opt_free_args(&args);
1142                 croak("out of memory\n");
1143         }
1144         fc = fuse_mount(mountpoint,&args);
1145         if (fc == NULL)
1146                 croak("could not mount fuse filesystem!\n");
1147 #ifndef __NetBSD__
1148         if(MY_CXT.threaded) {
1149                 fuse_loop_mt(fuse_new(fc,&args,&fops,sizeof(fops),NULL));
1150         } else
1151 #endif
1152                 fuse_loop(fuse_new(fc,&args,&fops,sizeof(fops),NULL));
1153         fuse_unmount(mountpoint,fc);
1154         fuse_opt_free_args(&args);