libfuse
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 #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 
25 static struct subdir *subdir_get(void)
26 {
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  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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 },
620 };
621 
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 
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 
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);
struct fuse_fs * fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *private_data)
Definition: fuse.c:4804
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition: fuse.h:85
struct fuse_context * fuse_get_context(void)
Definition: fuse.c:4598
fuse_readdir_flags
Definition: fuse.h:42
#define FUSE_REGISTER_MODULE(name_, factory_)
Definition: fuse.h:1320
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition: fuse_log.c:33
#define FUSE_OPT_KEY(templ, key)
Definition: fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition: fuse_opt.c:398
#define FUSE_OPT_END
Definition: fuse_opt.h:104
void * private_data
Definition: fuse.h:849
void(* destroy)(void *private_data)
Definition: fuse.h:624