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