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