Tizen 2.1 base
[framework/base/fuse.git] / lib / modules / subdir.c
1 /*
2   fuse subdir module: offset paths with a base directory
3   Copyright (C) 2007  Miklos Szeredi <miklos@szeredi.hu>
4
5   This program can be distributed under the terms of the GNU LGPLv2.
6   See the file COPYING.LIB
7 */
8
9 #define FUSE_USE_VERSION 26
10
11 #include <fuse.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <stddef.h>
15 #include <string.h>
16 #include <errno.h>
17
18 struct subdir {
19         char *base;
20         size_t baselen;
21         int rellinks;
22         struct fuse_fs *next;
23 };
24
25 static struct subdir *subdir_get(void)
26 {
27         return fuse_get_context()->private_data;
28 }
29
30 static int subdir_addpath(struct subdir *d, const char *path, char **newpathp)
31 {
32         char *newpath = NULL;
33
34         if (path != NULL) {
35                 unsigned newlen = d->baselen + strlen(path);
36
37                 newpath = malloc(newlen + 2);
38                 if (!newpath)
39                         return -ENOMEM;
40
41                 if (path[0] == '/')
42                         path++;
43                 strcpy(newpath, d->base);
44                 strcpy(newpath + d->baselen, path);
45                 if (!newpath[0])
46                         strcpy(newpath, ".");
47         }
48         *newpathp = newpath;
49
50         return 0;
51 }
52
53 static int subdir_getattr(const char *path, struct stat *stbuf)
54 {
55         struct subdir *d = subdir_get();
56         char *newpath;
57         int err = subdir_addpath(d, path, &newpath);
58         if (!err) {
59                 err = fuse_fs_getattr(d->next, newpath, stbuf);
60                 free(newpath);
61         }
62         return err;
63 }
64
65 static int subdir_fgetattr(const char *path, struct stat *stbuf,
66                            struct fuse_file_info *fi)
67 {
68         struct subdir *d = subdir_get();
69         char *newpath;
70         int err = subdir_addpath(d, path, &newpath);
71         if (!err) {
72                 err = fuse_fs_fgetattr(d->next, newpath, stbuf, fi);
73                 free(newpath);
74         }
75         return err;
76 }
77
78 static int subdir_access(const char *path, int mask)
79 {
80         struct subdir *d = subdir_get();
81         char *newpath;
82         int err = subdir_addpath(d, path, &newpath);
83         if (!err) {
84                 err = fuse_fs_access(d->next, newpath, mask);
85                 free(newpath);
86         }
87         return err;
88 }
89
90
91 static int count_components(const char *p)
92 {
93         int ctr;
94
95         for (; *p == '/'; p++);
96         for (ctr = 0; *p; ctr++) {
97                 for (; *p && *p != '/'; p++);
98                 for (; *p == '/'; p++);
99         }
100         return ctr;
101 }
102
103 static void strip_common(const char **sp, const char **tp)
104 {
105         const char *s = *sp;
106         const char *t = *tp;
107         do {
108                 for (; *s == '/'; s++);
109                 for (; *t == '/'; t++);
110                 *tp = t;
111                 *sp = s;
112                 for (; *s == *t && *s && *s != '/'; s++, t++);
113         } while ((*s == *t && *s) || (!*s && *t == '/') || (*s == '/' && !*t));
114 }
115
116 static void transform_symlink(struct subdir *d, const char *path,
117                               char *buf, size_t size)
118 {
119         const char *l = buf;
120         size_t llen;
121         char *s;
122         int dotdots;
123         int i;
124
125         if (l[0] != '/' || d->base[0] != '/')
126                 return;
127
128         strip_common(&l, &path);
129         if (l - buf < (long) d->baselen)
130                 return;
131
132         dotdots = count_components(path);
133         if (!dotdots)
134                 return;
135         dotdots--;
136
137         llen = strlen(l);
138         if (dotdots * 3 + llen + 2 > size)
139                 return;
140
141         s = buf + dotdots * 3;
142         if (llen)
143                 memmove(s, l, llen + 1);
144         else if (!dotdots)
145                 strcpy(s, ".");
146         else
147                 *s = '\0';
148
149         for (s = buf, i = 0; i < dotdots; i++, s += 3)
150                 memcpy(s, "../", 3);
151 }
152
153
154 static int subdir_readlink(const char *path, char *buf, size_t size)
155 {
156         struct subdir *d = subdir_get();
157         char *newpath;
158         int err = subdir_addpath(d, path, &newpath);
159         if (!err) {
160                 err = fuse_fs_readlink(d->next, newpath, buf, size);
161                 if (!err && d->rellinks)
162                         transform_symlink(d, newpath, buf, size);
163                 free(newpath);
164         }
165         return err;
166 }
167
168 static int subdir_opendir(const char *path, struct fuse_file_info *fi)
169 {
170         struct subdir *d = subdir_get();
171         char *newpath;
172         int err = subdir_addpath(d, path, &newpath);
173         if (!err) {
174                 err = fuse_fs_opendir(d->next, newpath, fi);
175                 free(newpath);
176         }
177         return err;
178 }
179
180 static int subdir_readdir(const char *path, void *buf,
181                           fuse_fill_dir_t filler, off_t offset,
182                           struct fuse_file_info *fi)
183 {
184         struct subdir *d = subdir_get();
185         char *newpath;
186         int err = subdir_addpath(d, path, &newpath);
187         if (!err) {
188                 err = fuse_fs_readdir(d->next, newpath, buf, filler, offset,
189                                       fi);
190                 free(newpath);
191         }
192         return err;
193 }
194
195 static int subdir_releasedir(const char *path, struct fuse_file_info *fi)
196 {
197         struct subdir *d = subdir_get();
198         char *newpath;
199         int err = subdir_addpath(d, path, &newpath);
200         if (!err) {
201                 err = fuse_fs_releasedir(d->next, newpath, fi);
202                 free(newpath);
203         }
204         return err;
205 }
206
207 static int subdir_mknod(const char *path, mode_t mode, dev_t rdev)
208 {
209         struct subdir *d = subdir_get();
210         char *newpath;
211         int err = subdir_addpath(d, path, &newpath);
212         if (!err) {
213                 err = fuse_fs_mknod(d->next, newpath, mode, rdev);
214                 free(newpath);
215         }
216         return err;
217 }
218
219 static int subdir_mkdir(const char *path, mode_t mode)
220 {
221         struct subdir *d = subdir_get();
222         char *newpath;
223         int err = subdir_addpath(d, path, &newpath);
224         if (!err) {
225                 err = fuse_fs_mkdir(d->next, newpath, mode);
226                 free(newpath);
227         }
228         return err;
229 }
230
231 static int subdir_unlink(const char *path)
232 {
233         struct subdir *d = subdir_get();
234         char *newpath;
235         int err = subdir_addpath(d, path, &newpath);
236         if (!err) {
237                 err = fuse_fs_unlink(d->next, newpath);
238                 free(newpath);
239         }
240         return err;
241 }
242
243 static int subdir_rmdir(const char *path)
244 {
245         struct subdir *d = subdir_get();
246         char *newpath;
247         int err = subdir_addpath(d, path, &newpath);
248         if (!err) {
249                 err = fuse_fs_rmdir(d->next, newpath);
250                 free(newpath);
251         }
252         return err;
253 }
254
255 static int subdir_symlink(const char *from, const char *path)
256 {
257         struct subdir *d = subdir_get();
258         char *newpath;
259         int err = subdir_addpath(d, path, &newpath);
260         if (!err) {
261                 err = fuse_fs_symlink(d->next, from, newpath);
262                 free(newpath);
263         }
264         return err;
265 }
266
267 static int subdir_rename(const char *from, const char *to)
268 {
269         struct subdir *d = subdir_get();
270         char *newfrom;
271         char *newto;
272         int err = subdir_addpath(d, from, &newfrom);
273         if (!err) {
274                 err = subdir_addpath(d, to, &newto);
275                 if (!err) {
276                         err = fuse_fs_rename(d->next, newfrom, newto);
277                         free(newto);
278                 }
279                 free(newfrom);
280         }
281         return err;
282 }
283
284 static int subdir_link(const char *from, const char *to)
285 {
286         struct subdir *d = subdir_get();
287         char *newfrom;
288         char *newto;
289         int err = subdir_addpath(d, from, &newfrom);
290         if (!err) {
291                 err = subdir_addpath(d, to, &newto);
292                 if (!err) {
293                         err = fuse_fs_link(d->next, newfrom, newto);
294                         free(newto);
295                 }
296                 free(newfrom);
297         }
298         return err;
299 }
300
301 static int subdir_chmod(const char *path, mode_t mode)
302 {
303         struct subdir *d = subdir_get();
304         char *newpath;
305         int err = subdir_addpath(d, path, &newpath);
306         if (!err) {
307                 err = fuse_fs_chmod(d->next, newpath, mode);
308                 free(newpath);
309         }
310         return err;
311 }
312
313 static int subdir_chown(const char *path, uid_t uid, gid_t gid)
314 {
315         struct subdir *d = subdir_get();
316         char *newpath;
317         int err = subdir_addpath(d, path, &newpath);
318         if (!err) {
319                 err = fuse_fs_chown(d->next, newpath, uid, gid);
320                 free(newpath);
321         }
322         return err;
323 }
324
325 static int subdir_truncate(const char *path, off_t size)
326 {
327         struct subdir *d = subdir_get();
328         char *newpath;
329         int err = subdir_addpath(d, path, &newpath);
330         if (!err) {
331                 err = fuse_fs_truncate(d->next, newpath, size);
332                 free(newpath);
333         }
334         return err;
335 }
336
337 static int subdir_ftruncate(const char *path, off_t size,
338                             struct fuse_file_info *fi)
339 {
340         struct subdir *d = subdir_get();
341         char *newpath;
342         int err = subdir_addpath(d, path, &newpath);
343         if (!err) {
344                 err = fuse_fs_ftruncate(d->next, newpath, size, fi);
345                 free(newpath);
346         }
347         return err;
348 }
349
350 static int subdir_utimens(const char *path, const struct timespec ts[2])
351 {
352         struct subdir *d = subdir_get();
353         char *newpath;
354         int err = subdir_addpath(d, path, &newpath);
355         if (!err) {
356                 err = fuse_fs_utimens(d->next, newpath, ts);
357                 free(newpath);
358         }
359         return err;
360 }
361
362 static int subdir_create(const char *path, mode_t mode,
363                          struct fuse_file_info *fi)
364 {
365         struct subdir *d = subdir_get();
366         char *newpath;
367         int err = subdir_addpath(d, path, &newpath);
368         if (!err) {
369                 err = fuse_fs_create(d->next, newpath, mode, fi);
370                 free(newpath);
371         }
372         return err;
373 }
374
375 static int subdir_open(const char *path, struct fuse_file_info *fi)
376 {
377         struct subdir *d = subdir_get();
378         char *newpath;
379         int err = subdir_addpath(d, path, &newpath);
380         if (!err) {
381                 err = fuse_fs_open(d->next, newpath, fi);
382                 free(newpath);
383         }
384         return err;
385 }
386
387 static int subdir_read_buf(const char *path, struct fuse_bufvec **bufp,
388                            size_t size, off_t offset, struct fuse_file_info *fi)
389 {
390         struct subdir *d = subdir_get();
391         char *newpath;
392         int err = subdir_addpath(d, path, &newpath);
393         if (!err) {
394                 err = fuse_fs_read_buf(d->next, newpath, bufp, size, offset, fi);
395                 free(newpath);
396         }
397         return err;
398 }
399
400 static int subdir_write_buf(const char *path, struct fuse_bufvec *buf,
401                         off_t offset, struct fuse_file_info *fi)
402 {
403         struct subdir *d = subdir_get();
404         char *newpath;
405         int err = subdir_addpath(d, path, &newpath);
406         if (!err) {
407                 err = fuse_fs_write_buf(d->next, newpath, buf, offset, fi);
408                 free(newpath);
409         }
410         return err;
411 }
412
413 static int subdir_statfs(const char *path, struct statvfs *stbuf)
414 {
415         struct subdir *d = subdir_get();
416         char *newpath;
417         int err = subdir_addpath(d, path, &newpath);
418         if (!err) {
419                 err = fuse_fs_statfs(d->next, newpath, stbuf);
420                 free(newpath);
421         }
422         return err;
423 }
424
425 static int subdir_flush(const char *path, struct fuse_file_info *fi)
426 {
427         struct subdir *d = subdir_get();
428         char *newpath;
429         int err = subdir_addpath(d, path, &newpath);
430         if (!err) {
431                 err = fuse_fs_flush(d->next, newpath, fi);
432                 free(newpath);
433         }
434         return err;
435 }
436
437 static int subdir_release(const char *path, struct fuse_file_info *fi)
438 {
439         struct subdir *d = subdir_get();
440         char *newpath;
441         int err = subdir_addpath(d, path, &newpath);
442         if (!err) {
443                 err = fuse_fs_release(d->next, newpath, fi);
444                 free(newpath);
445         }
446         return err;
447 }
448
449 static int subdir_fsync(const char *path, int isdatasync,
450                         struct fuse_file_info *fi)
451 {
452         struct subdir *d = subdir_get();
453         char *newpath;
454         int err = subdir_addpath(d, path, &newpath);
455         if (!err) {
456                 err = fuse_fs_fsync(d->next, newpath, isdatasync, fi);
457                 free(newpath);
458         }
459         return err;
460 }
461
462 static int subdir_fsyncdir(const char *path, int isdatasync,
463                            struct fuse_file_info *fi)
464 {
465         struct subdir *d = subdir_get();
466         char *newpath;
467         int err = subdir_addpath(d, path, &newpath);
468         if (!err) {
469                 err = fuse_fs_fsyncdir(d->next, newpath, isdatasync, fi);
470                 free(newpath);
471         }
472         return err;
473 }
474
475 static int subdir_setxattr(const char *path, const char *name,
476                            const char *value, size_t size, int flags)
477 {
478         struct subdir *d = subdir_get();
479         char *newpath;
480         int err = subdir_addpath(d, path, &newpath);
481         if (!err) {
482                 err = fuse_fs_setxattr(d->next, newpath, name, value, size,
483                                        flags);
484                 free(newpath);
485         }
486         return err;
487 }
488
489 static int subdir_getxattr(const char *path, const char *name, char *value,
490                            size_t size)
491 {
492         struct subdir *d = subdir_get();
493         char *newpath;
494         int err = subdir_addpath(d, path, &newpath);
495         if (!err) {
496                 err = fuse_fs_getxattr(d->next, newpath, name, value, size);
497                 free(newpath);
498         }
499         return err;
500 }
501
502 static int subdir_listxattr(const char *path, char *list, size_t size)
503 {
504         struct subdir *d = subdir_get();
505         char *newpath;
506         int err = subdir_addpath(d, path, &newpath);
507         if (!err) {
508                 err = fuse_fs_listxattr(d->next, newpath, list, size);
509                 free(newpath);
510         }
511         return err;
512 }
513
514 static int subdir_removexattr(const char *path, const char *name)
515 {
516         struct subdir *d = subdir_get();
517         char *newpath;
518         int err = subdir_addpath(d, path, &newpath);
519         if (!err) {
520                 err = fuse_fs_removexattr(d->next, newpath, name);
521                 free(newpath);
522         }
523         return err;
524 }
525
526 static int subdir_lock(const char *path, struct fuse_file_info *fi, int cmd,
527                        struct flock *lock)
528 {
529         struct subdir *d = subdir_get();
530         char *newpath;
531         int err = subdir_addpath(d, path, &newpath);
532         if (!err) {
533                 err = fuse_fs_lock(d->next, newpath, fi, cmd, lock);
534                 free(newpath);
535         }
536         return err;
537 }
538
539 static int subdir_flock(const char *path, struct fuse_file_info *fi, int op)
540 {
541         struct subdir *d = subdir_get();
542         char *newpath;
543         int err = subdir_addpath(d, path, &newpath);
544         if (!err) {
545                 err = fuse_fs_flock(d->next, newpath, fi, op);
546                 free(newpath);
547         }
548         return err;
549 }
550
551 static int subdir_bmap(const char *path, size_t blocksize, uint64_t *idx)
552 {
553         struct subdir *d = subdir_get();
554         char *newpath;
555         int err = subdir_addpath(d, path, &newpath);
556         if (!err) {
557                 err = fuse_fs_bmap(d->next, newpath, blocksize, idx);
558                 free(newpath);
559         }
560         return err;
561 }
562
563 static void *subdir_init(struct fuse_conn_info *conn)
564 {
565         struct subdir *d = subdir_get();
566         fuse_fs_init(d->next, conn);
567         return d;
568 }
569
570 static void subdir_destroy(void *data)
571 {
572         struct subdir *d = data;
573         fuse_fs_destroy(d->next);
574         free(d->base);
575         free(d);
576 }
577
578 static const struct fuse_operations subdir_oper = {
579         .destroy        = subdir_destroy,
580         .init           = subdir_init,
581         .getattr        = subdir_getattr,
582         .fgetattr       = subdir_fgetattr,
583         .access         = subdir_access,
584         .readlink       = subdir_readlink,
585         .opendir        = subdir_opendir,
586         .readdir        = subdir_readdir,
587         .releasedir     = subdir_releasedir,
588         .mknod          = subdir_mknod,
589         .mkdir          = subdir_mkdir,
590         .symlink        = subdir_symlink,
591         .unlink         = subdir_unlink,
592         .rmdir          = subdir_rmdir,
593         .rename         = subdir_rename,
594         .link           = subdir_link,
595         .chmod          = subdir_chmod,
596         .chown          = subdir_chown,
597         .truncate       = subdir_truncate,
598         .ftruncate      = subdir_ftruncate,
599         .utimens        = subdir_utimens,
600         .create         = subdir_create,
601         .open           = subdir_open,
602         .read_buf       = subdir_read_buf,
603         .write_buf      = subdir_write_buf,
604         .statfs         = subdir_statfs,
605         .flush          = subdir_flush,
606         .release        = subdir_release,
607         .fsync          = subdir_fsync,
608         .fsyncdir       = subdir_fsyncdir,
609         .setxattr       = subdir_setxattr,
610         .getxattr       = subdir_getxattr,
611         .listxattr      = subdir_listxattr,
612         .removexattr    = subdir_removexattr,
613         .lock           = subdir_lock,
614         .flock          = subdir_flock,
615         .bmap           = subdir_bmap,
616
617         .flag_nullpath_ok = 1,
618         .flag_nopath = 1,
619 };
620
621 static const struct fuse_opt subdir_opts[] = {
622         FUSE_OPT_KEY("-h", 0),
623         FUSE_OPT_KEY("--help", 0),
624         { "subdir=%s", offsetof(struct subdir, base), 0 },
625         { "rellinks", offsetof(struct subdir, rellinks), 1 },
626         { "norellinks", offsetof(struct subdir, rellinks), 0 },
627         FUSE_OPT_END
628 };
629
630 static void subdir_help(void)
631 {
632         fprintf(stderr,
633 "    -o subdir=DIR          prepend this directory to all paths (mandatory)\n"
634 "    -o [no]rellinks        transform absolute symlinks to relative\n");
635 }
636
637 static int subdir_opt_proc(void *data, const char *arg, int key,
638                            struct fuse_args *outargs)
639 {
640         (void) data; (void) arg; (void) outargs;
641
642         if (!key) {
643                 subdir_help();
644                 return -1;
645         }
646
647         return 1;
648 }
649
650 static struct fuse_fs *subdir_new(struct fuse_args *args,
651                                   struct fuse_fs *next[])
652 {
653         struct fuse_fs *fs;
654         struct subdir *d;
655
656         d = calloc(1, sizeof(struct subdir));
657         if (d == NULL) {
658                 fprintf(stderr, "fuse-subdir: memory allocation failed\n");
659                 return NULL;
660         }
661
662         if (fuse_opt_parse(args, d, subdir_opts, subdir_opt_proc) == -1)
663                 goto out_free;
664
665         if (!next[0] || next[1]) {
666                 fprintf(stderr, "fuse-subdir: exactly one next filesystem required\n");
667                 goto out_free;
668         }
669
670         if (!d->base) {
671                 fprintf(stderr, "fuse-subdir: missing 'subdir' option\n");
672                 goto out_free;
673         }
674
675         if (d->base[0] && d->base[strlen(d->base)-1] != '/') {
676                 char *tmp = realloc(d->base, strlen(d->base) + 2);
677                 if (!tmp) {
678                         fprintf(stderr, "fuse-subdir: memory allocation failed\n");
679                         goto out_free;
680                 }
681                 d->base = tmp;
682                 strcat(d->base, "/");
683         }
684         d->baselen = strlen(d->base);
685         d->next = next[0];
686         fs = fuse_fs_new(&subdir_oper, sizeof(subdir_oper), d);
687         if (!fs)
688                 goto out_free;
689         return fs;
690
691 out_free:
692         free(d->base);
693         free(d);
694         return NULL;
695 }
696
697 FUSE_REGISTER_MODULE(subdir, subdir_new);