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