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