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