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