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