blob: 05b3379af8825530a40c3aae0b0ba8162148f0c2 [file] [log] [blame]
Matt Mower523a0592015-12-13 11:31:00 -06001/*
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
18struct subdir {
19 char *base;
20 size_t baselen;
21 int rellinks;
22 struct fuse_fs *next;
23};
24
25static struct subdir *subdir_get(void)
26{
27 return fuse_get_context()->private_data;
28}
29
30static 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
53static 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
65static 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
78static 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
91static 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
103static 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
116static 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
154static 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
168static 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
180static int subdir_readdir(const char *path, void *buf,
181 fuse_fill_dir_t filler, loff_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
195static 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
207static 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
219static 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
231static 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
243static 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
255static 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
267static 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
284static 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
301static 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
313static 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
325static int subdir_truncate(const char *path, loff_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
337static int subdir_ftruncate(const char *path, loff_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
350static 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
362static 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
375static 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
387static int subdir_read_buf(const char *path, struct fuse_bufvec **bufp,
388 size_t size, loff_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
400static int subdir_write_buf(const char *path, struct fuse_bufvec *buf,
401 loff_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
413static 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
425static 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
437static 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
449static 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
462static 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
475static 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
489static 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
502static 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
514static 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
526static 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
539static 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
551static 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
563static 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
570static 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
578static 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
621static 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
630static 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
637static 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
650static 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
691out_free:
692 free(d->base);
693 free(d);
694 return NULL;
695}
696
697FUSE_REGISTER_MODULE(subdir, subdir_new);