a5cc6c7a6d922dc1a5e41717afc1db96d578e799
[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 = POPn; // we pop double here to support files larger than 4Gb (long limit)
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         HV *fihash;
422         FUSE_CONTEXT_PRE;
423         DEBUGf("open begin\n");
424         ENTER;
425         SAVETMPS;
426         PUSHMARK(SP);
427         XPUSHs(sv_2mortal(newSVpv(file,0)));
428         XPUSHs(sv_2mortal(newSViv(flags)));
429         /* Create a hashref containing the details from fi
430          * which we can look at or modify.
431          */
432         fi->fh = 0; /* Ensure it starts with 0 - important if they don't set it */
433         fihash = newHV();
434 #if FUSE_VERSION >= 24
435         hv_store(fihash, "direct_io", 9, newSViv(fi->direct_io), 0);
436         hv_store(fihash, "keep_cache", 10, newSViv(fi->keep_cache), 0);
437 #endif
438 #if FUSE_VERSION >= 29
439         hv_store(fihash, "nonseekable", 11, newSViv(fi->nonseekable), 0);
440 #endif
441         XPUSHs(sv_2mortal(newRV_noinc((SV*) fihash)));
442         /* All hashref things done */
443
444         PUTBACK;
445         /* Open called with filename, flags */
446         rv = call_sv(_PLfuse_callbacks[14],G_ARRAY);
447         SPAGAIN;
448         if(rv)
449         {
450                 SV *sv;
451                 if (rv > 1)
452                 {
453                         sv = POPs;
454                         if (SvOK(sv))
455                         {
456                                 /* We're holding on to the sv reference until
457                                  * after exit of this function, so we need to
458                                  * increment its reference count
459                                  */
460                                 fi->fh = SvREFCNT_inc(sv);
461                         }
462                 }
463                 rv = POPi;
464         }
465         else
466                 rv = 0;
467         if (rv == 0)
468         {
469                 /* Success, so copy the file handle which they returned */
470 #if FUSE_VERSION >= 24
471                 SV **svp;
472                 svp = hv_fetch(fihash, "direct_io", 9, 0);
473                 if (svp != NULL)
474                 {
475                         fi->direct_io = SvIV(*svp);
476                 }
477                 svp = hv_fetch(fihash, "keep_cache", 10, 0);
478                 if (svp != NULL)
479                 {
480                         fi->keep_cache = SvIV(*svp);
481                 }
482 #endif
483 #if FUSE_VERSION >= 29
484                 svp = hv_fetch(fihash, "nonseekable", 11, 0);
485                 if (svp != NULL)
486                 {
487                         fi->nonseekable = SvIV(*svp);
488                 }
489 #endif
490         }
491         FREETMPS;
492         LEAVE;
493         PUTBACK;
494         DEBUGf("open end: %i\n",rv);
495         FUSE_CONTEXT_POST;
496         return rv;
497 }
498
499 int _PLfuse_read (const char *file, char *buf, size_t buflen, off_t off, struct fuse_file_info *fi) {
500         int rv;
501         FUSE_CONTEXT_PRE;
502         DEBUGf("read begin\n");
503         ENTER;
504         SAVETMPS;
505         PUSHMARK(SP);
506         XPUSHs(sv_2mortal(newSVpv(file,0)));
507         XPUSHs(sv_2mortal(newSViv(buflen)));
508         XPUSHs(sv_2mortal(newSViv(off)));
509         XPUSHs(fi->fh==0 ? &PL_sv_undef : (SV *)fi->fh);
510         PUTBACK;
511         rv = call_sv(_PLfuse_callbacks[15],G_SCALAR);
512         SPAGAIN;
513         if(!rv)
514                 rv = -ENOENT;
515         else {
516                 SV *mysv = POPs;
517                 if(SvTYPE(mysv) == SVt_NV || SvTYPE(mysv) == SVt_IV)
518                         rv = SvIV(mysv);
519                 else {
520                         if(SvPOK(mysv)) {
521                                 rv = SvCUR(mysv);
522                         } else {
523                                 rv = 0;
524                         }
525                         if(rv > buflen)
526                                 croak("read() handler returned more than buflen! (%i > %i)",rv,buflen);
527                         if(rv)
528                                 memcpy(buf,SvPV_nolen(mysv),rv);
529                 }
530         }
531         FREETMPS;
532         LEAVE;
533         PUTBACK;
534         DEBUGf("read end: %i\n",rv);
535         FUSE_CONTEXT_POST;
536         return rv;
537 }
538
539 int _PLfuse_write (const char *file, const char *buf, size_t buflen, off_t off, struct fuse_file_info *fi) {
540         int rv;
541         FUSE_CONTEXT_PRE;
542         DEBUGf("write begin\n");
543         ENTER;
544         SAVETMPS;
545         PUSHMARK(SP);
546         XPUSHs(sv_2mortal(newSVpv(file,0)));
547         XPUSHs(sv_2mortal(newSVpvn(buf,buflen)));
548         XPUSHs(sv_2mortal(newSViv(off)));
549         XPUSHs(fi->fh==0 ? &PL_sv_undef : (SV *)fi->fh);
550         PUTBACK;
551         rv = call_sv(_PLfuse_callbacks[16],G_SCALAR);
552         SPAGAIN;
553         if(rv)
554                 rv = POPi;
555         else
556                 rv = 0;
557         FREETMPS;
558         LEAVE;
559         PUTBACK;
560         DEBUGf("write end: %i\n",rv);
561         FUSE_CONTEXT_POST;
562         return rv;
563 }
564
565 int _PLfuse_statfs (const char *file, struct statvfs *st) {
566         int rv;
567         FUSE_CONTEXT_PRE;
568         DEBUGf("statfs begin\n");
569         ENTER;
570         SAVETMPS;
571         PUSHMARK(SP);
572         PUTBACK;
573         rv = call_sv(_PLfuse_callbacks[17],G_ARRAY);
574         SPAGAIN;
575         DEBUGf("statfs got %i params\n",rv);
576         if(rv == 6 || rv == 7) {
577                 st->f_bsize     = POPi;
578                 st->f_bfree     = POPi;
579                 st->f_blocks    = POPi;
580                 st->f_ffree     = POPi;
581                 st->f_files     = POPi;
582                 st->f_namemax   = POPi;
583                 /* zero and fill-in other */
584                 st->f_fsid = 0;
585                 st->f_frsize = 4096;
586                 st->f_flag = 0;
587                 st->f_bavail = st->f_bfree;
588                 st->f_favail = st->f_ffree;
589
590                 if(rv == 7)
591                         rv = POPi;
592                 else
593                         rv = 0;
594         } else
595         if(rv > 1)
596                 croak("inappropriate number of returned values from statfs");
597         else
598         if(rv)
599                 rv = POPi;
600         else
601                 rv = -ENOSYS;
602         FREETMPS;
603         LEAVE;
604         PUTBACK;
605         DEBUGf("statfs end: %i\n",rv);
606         FUSE_CONTEXT_POST;
607         return rv;
608 }
609
610 int _PLfuse_flush (const char *file, struct fuse_file_info *fi) {
611         int rv;
612         FUSE_CONTEXT_PRE;
613         DEBUGf("flush begin\n");
614         ENTER;
615         SAVETMPS;
616         PUSHMARK(SP);
617         XPUSHs(sv_2mortal(newSVpv(file,0)));
618         XPUSHs(fi->fh==0 ? &PL_sv_undef : (SV *)fi->fh);
619         PUTBACK;
620         rv = call_sv(_PLfuse_callbacks[18],G_SCALAR);
621         SPAGAIN;
622         if(rv)
623                 rv = POPi;
624         else
625                 rv = 0;
626         FREETMPS;
627         LEAVE;
628         PUTBACK;
629         DEBUGf("flush end: %i\n",rv);
630         FUSE_CONTEXT_POST;
631         return rv;
632 }
633
634 int _PLfuse_release (const char *file, struct fuse_file_info *fi) {
635         int rv;
636         int flags = fi->flags;
637         FUSE_CONTEXT_PRE;
638         DEBUGf("release begin\n");
639         ENTER;
640         SAVETMPS;
641         PUSHMARK(SP);
642         XPUSHs(sv_2mortal(newSVpv(file,0)));
643         XPUSHs(sv_2mortal(newSViv(flags)));
644         XPUSHs(fi->fh==0 ? &PL_sv_undef : (SV *)fi->fh);
645         PUTBACK;
646         rv = call_sv(_PLfuse_callbacks[19],G_SCALAR);
647         SPAGAIN;
648         if(rv)
649                 rv = POPi;
650         else
651                 rv = 0;
652         /* We're now finished with the handle that we were given, so
653          * we should decrement its count so that it can be freed.
654          */
655         if (fi->fh != 0)
656         {
657                 SvREFCNT_dec((SV *)fi->fh);
658                 fi->fh = 0;
659         }
660         FREETMPS;
661         LEAVE;
662         PUTBACK;
663         DEBUGf("release end: %i\n",rv);
664         FUSE_CONTEXT_POST;
665         return rv;
666 }
667
668 int _PLfuse_fsync (const char *file, int datasync, struct fuse_file_info *fi) {
669         int rv;
670         int flags = fi->flags;
671         FUSE_CONTEXT_PRE;
672         DEBUGf("fsync begin\n");
673         ENTER;
674         SAVETMPS;
675         PUSHMARK(SP);
676         XPUSHs(sv_2mortal(newSVpv(file,0)));
677         XPUSHs(sv_2mortal(newSViv(flags)));
678         XPUSHs(fi->fh==0 ? &PL_sv_undef : (SV *)fi->fh);
679         PUTBACK;
680         rv = call_sv(_PLfuse_callbacks[20],G_SCALAR);
681         SPAGAIN;
682         if(rv)
683                 rv = POPi;
684         else
685                 rv = 0;
686         FREETMPS;
687         LEAVE;
688         PUTBACK;
689         DEBUGf("fsync end: %i\n",rv);
690         FUSE_CONTEXT_POST;
691         return rv;
692 }
693
694 int _PLfuse_setxattr (const char *file, const char *name, const char *buf, size_t buflen, int flags) {
695         int rv;
696         FUSE_CONTEXT_PRE;
697         DEBUGf("setxattr begin\n");
698         ENTER;
699         SAVETMPS;
700         PUSHMARK(SP);
701         XPUSHs(sv_2mortal(newSVpv(file,0)));
702         XPUSHs(sv_2mortal(newSVpv(name,0)));
703         XPUSHs(sv_2mortal(newSVpvn(buf,buflen)));
704         XPUSHs(sv_2mortal(newSViv(flags)));
705         PUTBACK;
706         rv = call_sv(_PLfuse_callbacks[21],G_SCALAR);
707         SPAGAIN;
708         if(rv)
709                 rv = POPi;
710         else
711                 rv = 0;
712         FREETMPS;
713         LEAVE;
714         PUTBACK;
715         DEBUGf("setxattr end: %i\n",rv);
716         FUSE_CONTEXT_POST;
717         return rv;
718 }
719
720 int _PLfuse_getxattr (const char *file, const char *name, char *buf, size_t buflen) {
721         int rv;
722         FUSE_CONTEXT_PRE;
723         DEBUGf("getxattr begin\n");
724         ENTER;
725         SAVETMPS;
726         PUSHMARK(SP);
727         XPUSHs(sv_2mortal(newSVpv(file,0)));
728         XPUSHs(sv_2mortal(newSVpv(name,0)));
729         PUTBACK;
730         rv = call_sv(_PLfuse_callbacks[22],G_SCALAR);
731         SPAGAIN;
732         if(!rv)
733                 rv = -ENOENT;
734         else {
735                 SV *mysv = POPs;
736
737                 rv = 0;
738                 if(SvTYPE(mysv) == SVt_NV || SvTYPE(mysv) == SVt_IV)
739                         rv = SvIV(mysv);
740                 else {
741                         if(SvPOK(mysv)) {
742                                 rv = SvCUR(mysv);
743                         } else {
744                                 rv = 0;
745                         }
746                         if ((rv > 0) && (buflen > 0))
747                         {
748                                 if(rv > buflen)
749                                         rv = -ERANGE;
750                                 else
751                                         memcpy(buf,SvPV_nolen(mysv),rv);
752                         }
753                 }
754         }
755         FREETMPS;
756         LEAVE;
757         PUTBACK;
758         DEBUGf("getxattr end: %i\n",rv);
759         FUSE_CONTEXT_POST;
760         return rv;
761 }
762
763 int _PLfuse_listxattr (const char *file, char *list, size_t size) {
764         int prv, rv;
765         FUSE_CONTEXT_PRE;
766         DEBUGf("listxattr begin\n");
767         ENTER;
768         SAVETMPS;
769         PUSHMARK(SP);
770         XPUSHs(sv_2mortal(newSVpv(file,0)));
771         PUTBACK;
772         prv = call_sv(_PLfuse_callbacks[23],G_ARRAY);
773         SPAGAIN;
774         if(!prv)
775                 rv = -ENOENT;
776         else {
777
778                 char *p = list;
779                 int spc = size;
780                 int total_len = 0;
781
782                 rv = POPi;
783                 prv--;
784
785                 /* Always nul terminate */
786                 if (list && (size > 0))
787                         list[0] = '\0';
788
789                 while (prv > 0)
790                 {
791                         SV *mysv = POPs;
792                         prv--;
793
794                         if (SvPOK(mysv)) {
795                                 /* Copy nul too */
796                                 int s = SvCUR(mysv) + 1;
797                                 total_len += s;
798
799                                 if (p && (size > 0) && (spc >= s))
800                                 {
801                                         memcpy(p,SvPV_nolen(mysv),s);
802                                         p += s;
803                                         spc -= s;
804                                 }
805                         }
806                 }
807
808                 /*
809                  * If the Perl returned an error, return that.
810                  * Otherwise check that the buffer was big enough.
811                  */
812                 if (rv == 0)
813                 {
814                         rv = total_len;
815                         if ((size > 0) && (size < total_len))
816                                 rv = -ERANGE;
817                 }
818         }
819         FREETMPS;
820         LEAVE;
821         PUTBACK;
822         DEBUGf("listxattr end: %i\n",rv);
823         FUSE_CONTEXT_POST;
824         return rv;
825 }
826
827 int _PLfuse_removexattr (const char *file, const char *name) {
828         int rv;
829         FUSE_CONTEXT_PRE;
830         DEBUGf("removexattr begin\n");
831         ENTER;
832         SAVETMPS;
833         PUSHMARK(SP);
834         XPUSHs(sv_2mortal(newSVpv(file,0)));
835         XPUSHs(sv_2mortal(newSVpv(name,0)));
836         PUTBACK;
837         rv = call_sv(_PLfuse_callbacks[24],G_SCALAR);
838         SPAGAIN;
839         if(rv)
840                 rv = POPi;
841         else
842                 rv = 0;
843         FREETMPS;
844         LEAVE;
845         PUTBACK;
846         DEBUGf("removexattr end: %i\n",rv);
847         FUSE_CONTEXT_POST;
848         return rv;
849 }
850
851 struct fuse_operations _available_ops = {
852 getattr:                _PLfuse_getattr,
853 readlink:               _PLfuse_readlink,
854 getdir:                 _PLfuse_getdir,
855 #if 0
856 readdir:                _PLfuse_readdir,
857 #endif
858 mknod:                  _PLfuse_mknod,
859 mkdir:                  _PLfuse_mkdir,
860 unlink:                 _PLfuse_unlink,
861 rmdir:                  _PLfuse_rmdir,
862 symlink:                _PLfuse_symlink,
863 rename:                 _PLfuse_rename,
864 link:                   _PLfuse_link,
865 chmod:                  _PLfuse_chmod,
866 chown:                  _PLfuse_chown,
867 truncate:               _PLfuse_truncate,
868 utime:                  _PLfuse_utime,
869 open:                   _PLfuse_open,
870 read:                   _PLfuse_read,
871 write:                  _PLfuse_write,
872 statfs:                 _PLfuse_statfs,
873 flush:                  _PLfuse_flush,
874 release:                _PLfuse_release,
875 fsync:                  _PLfuse_fsync,
876 setxattr:               _PLfuse_setxattr,
877 getxattr:               _PLfuse_getxattr,
878 listxattr:              _PLfuse_listxattr,
879 removexattr:            _PLfuse_removexattr,
880 };
881
882 MODULE = Fuse           PACKAGE = Fuse
883 PROTOTYPES: DISABLE
884
885 SV*
886 fuse_get_context()
887         PREINIT:
888         struct fuse_context *fc;
889         CODE:
890         fc = fuse_get_context();
891         if(fc) {
892                 HV *hash = newHV();
893                 hv_store(hash, "uid", 3, newSViv(fc->uid), 0);
894                 hv_store(hash, "gid", 3, newSViv(fc->gid), 0);
895                 hv_store(hash, "pid", 3, newSViv(fc->pid), 0);
896                 RETVAL = newRV_noinc((SV*)hash);
897         } else {
898                 XSRETURN_UNDEF;
899         }
900         OUTPUT:
901         RETVAL
902
903 void
904 perl_fuse_main(...)
905         PREINIT:
906         struct fuse_operations fops = 
907                 {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
908                  NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
909         int i, fd, debug, threaded;
910         char *mountpoint;
911         char *mountopts;
912         struct fuse_args margs = FUSE_ARGS_INIT(0, NULL);
913         struct fuse_args fargs = FUSE_ARGS_INIT(0, NULL);
914         INIT:
915         if(items != 29) {
916                 fprintf(stderr,"Perl<->C inconsistency or internal error\n");
917                 XSRETURN_UNDEF;
918         }
919         CODE:
920         debug = SvIV(ST(0));
921         threaded = SvIV(ST(1));
922         if(threaded) {
923 #ifdef FUSE_USE_ITHREADS
924                 master_interp = PERL_GET_CONTEXT;
925 #else
926                 fprintf(stderr,"FUSE warning: Your script has requested multithreaded "
927                                "mode, but your perl was not built with -Dusethreads.  "
928                                "Threads are disabled.\n");
929                 threaded = 0;
930 #endif
931         }
932         mountpoint = SvPV_nolen(ST(2));
933         mountopts = SvPV_nolen(ST(3));
934         for(i=0;i<N_CALLBACKS;i++) {
935                 SV *var = ST(i+4);
936                 /* allow symbolic references, or real code references. */
937                 if(SvOK(var) && (SvPOK(var) || (SvROK(var) && SvTYPE(SvRV(var)) == SVt_PVCV))) {
938                         void **tmp1 = (void**)&_available_ops, **tmp2 = (void**)&fops;
939                         tmp2[i] = tmp1[i];
940 #ifdef FUSE_USE_ITHREADS
941                         if(threaded)
942                 /* note: under 5.8.7, this croaks for code references. */
943                 SvSHARE(var);
944 #endif
945                         _PLfuse_callbacks[i] = var;
946                 } else
947                 if(SvOK(var)) {
948                         croak("invalid callback (%i) passed to perl_fuse_main "
949                               "(%s is not a string, code ref, or undef).\n",
950                               i+4,SvPVbyte_nolen(var));
951                 }
952         }
953         /*
954          * XXX: What comes here is just a ridiculous use of the option parsing API
955          * to hack on compatibility with other parts of the new API. First and
956          * foremost, real C argc/argv would be good to get at...
957          */
958         if (mountopts &&
959             (fuse_opt_add_arg(&margs, "") == -1 ||
960              fuse_opt_add_arg(&margs, "-o") == -1 ||
961              fuse_opt_add_arg(&margs, mountopts) == -1)) {
962                 fuse_opt_free_args(&margs);
963                 croak("out of memory\n");
964         }
965         fd = fuse_mount(mountpoint,&margs);
966         fuse_opt_free_args(&margs);        
967         if(fd < 0)
968                 croak("could not mount fuse filesystem!\n");
969         if (debug) {
970                 if ( fuse_opt_add_arg(&fargs, "") == -1 ||
971                         fuse_opt_add_arg(&fargs, "-d") == -1) {
972                         fuse_opt_free_args(&fargs);
973                         croak("out of memory\n");
974                 }
975         } else {
976                 if (fuse_opt_add_arg(&fargs, "") == -1)
977                         croak("out of memory\n");
978         }
979
980         if(threaded) {
981                 fuse_loop_mt(fuse_new(fd,&fargs,&fops,sizeof(fops)));
982         } else
983                 fuse_loop(fuse_new(fd,&fargs,&fops,sizeof(fops)));
984         fuse_opt_free_args(&fargs);