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