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