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