diff options
Diffstat (limited to 'fuse/modules/subdir.c')
-rw-r--r-- | fuse/modules/subdir.c | 697 |
1 files changed, 697 insertions, 0 deletions
diff --git a/fuse/modules/subdir.c b/fuse/modules/subdir.c new file mode 100644 index 000000000..05b3379af --- /dev/null +++ b/fuse/modules/subdir.c @@ -0,0 +1,697 @@ +/* + fuse subdir module: offset paths with a base directory + Copyright (C) 2007 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB +*/ + +#define FUSE_USE_VERSION 26 + +#include <fuse.h> +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <errno.h> + +struct subdir { + char *base; + size_t baselen; + int rellinks; + struct fuse_fs *next; +}; + +static struct subdir *subdir_get(void) +{ + return fuse_get_context()->private_data; +} + +static int subdir_addpath(struct subdir *d, const char *path, char **newpathp) +{ + char *newpath = NULL; + + if (path != NULL) { + unsigned newlen = d->baselen + strlen(path); + + newpath = malloc(newlen + 2); + if (!newpath) + return -ENOMEM; + + if (path[0] == '/') + path++; + strcpy(newpath, d->base); + strcpy(newpath + d->baselen, path); + if (!newpath[0]) + strcpy(newpath, "."); + } + *newpathp = newpath; + + return 0; +} + +static int subdir_getattr(const char *path, struct stat *stbuf) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_getattr(d->next, newpath, stbuf); + free(newpath); + } + return err; +} + +static int subdir_fgetattr(const char *path, struct stat *stbuf, + struct fuse_file_info *fi) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_fgetattr(d->next, newpath, stbuf, fi); + free(newpath); + } + return err; +} + +static int subdir_access(const char *path, int mask) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_access(d->next, newpath, mask); + free(newpath); + } + return err; +} + + +static int count_components(const char *p) +{ + int ctr; + + for (; *p == '/'; p++); + for (ctr = 0; *p; ctr++) { + for (; *p && *p != '/'; p++); + for (; *p == '/'; p++); + } + return ctr; +} + +static void strip_common(const char **sp, const char **tp) +{ + const char *s = *sp; + const char *t = *tp; + do { + for (; *s == '/'; s++); + for (; *t == '/'; t++); + *tp = t; + *sp = s; + for (; *s == *t && *s && *s != '/'; s++, t++); + } while ((*s == *t && *s) || (!*s && *t == '/') || (*s == '/' && !*t)); +} + +static void transform_symlink(struct subdir *d, const char *path, + char *buf, size_t size) +{ + const char *l = buf; + size_t llen; + char *s; + int dotdots; + int i; + + if (l[0] != '/' || d->base[0] != '/') + return; + + strip_common(&l, &path); + if (l - buf < (long) d->baselen) + return; + + dotdots = count_components(path); + if (!dotdots) + return; + dotdots--; + + llen = strlen(l); + if (dotdots * 3 + llen + 2 > size) + return; + + s = buf + dotdots * 3; + if (llen) + memmove(s, l, llen + 1); + else if (!dotdots) + strcpy(s, "."); + else + *s = '\0'; + + for (s = buf, i = 0; i < dotdots; i++, s += 3) + memcpy(s, "../", 3); +} + + +static int subdir_readlink(const char *path, char *buf, size_t size) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_readlink(d->next, newpath, buf, size); + if (!err && d->rellinks) + transform_symlink(d, newpath, buf, size); + free(newpath); + } + return err; +} + +static int subdir_opendir(const char *path, struct fuse_file_info *fi) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_opendir(d->next, newpath, fi); + free(newpath); + } + return err; +} + +static int subdir_readdir(const char *path, void *buf, + fuse_fill_dir_t filler, loff_t offset, + struct fuse_file_info *fi) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_readdir(d->next, newpath, buf, filler, offset, + fi); + free(newpath); + } + return err; +} + +static int subdir_releasedir(const char *path, struct fuse_file_info *fi) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_releasedir(d->next, newpath, fi); + free(newpath); + } + return err; +} + +static int subdir_mknod(const char *path, mode_t mode, dev_t rdev) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_mknod(d->next, newpath, mode, rdev); + free(newpath); + } + return err; +} + +static int subdir_mkdir(const char *path, mode_t mode) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_mkdir(d->next, newpath, mode); + free(newpath); + } + return err; +} + +static int subdir_unlink(const char *path) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_unlink(d->next, newpath); + free(newpath); + } + return err; +} + +static int subdir_rmdir(const char *path) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_rmdir(d->next, newpath); + free(newpath); + } + return err; +} + +static int subdir_symlink(const char *from, const char *path) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_symlink(d->next, from, newpath); + free(newpath); + } + return err; +} + +static int subdir_rename(const char *from, const char *to) +{ + struct subdir *d = subdir_get(); + char *newfrom; + char *newto; + int err = subdir_addpath(d, from, &newfrom); + if (!err) { + err = subdir_addpath(d, to, &newto); + if (!err) { + err = fuse_fs_rename(d->next, newfrom, newto); + free(newto); + } + free(newfrom); + } + return err; +} + +static int subdir_link(const char *from, const char *to) +{ + struct subdir *d = subdir_get(); + char *newfrom; + char *newto; + int err = subdir_addpath(d, from, &newfrom); + if (!err) { + err = subdir_addpath(d, to, &newto); + if (!err) { + err = fuse_fs_link(d->next, newfrom, newto); + free(newto); + } + free(newfrom); + } + return err; +} + +static int subdir_chmod(const char *path, mode_t mode) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_chmod(d->next, newpath, mode); + free(newpath); + } + return err; +} + +static int subdir_chown(const char *path, uid_t uid, gid_t gid) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_chown(d->next, newpath, uid, gid); + free(newpath); + } + return err; +} + +static int subdir_truncate(const char *path, loff_t size) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_truncate(d->next, newpath, size); + free(newpath); + } + return err; +} + +static int subdir_ftruncate(const char *path, loff_t size, + struct fuse_file_info *fi) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_ftruncate(d->next, newpath, size, fi); + free(newpath); + } + return err; +} + +static int subdir_utimens(const char *path, const struct timespec ts[2]) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_utimens(d->next, newpath, ts); + free(newpath); + } + return err; +} + +static int subdir_create(const char *path, mode_t mode, + struct fuse_file_info *fi) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_create(d->next, newpath, mode, fi); + free(newpath); + } + return err; +} + +static int subdir_open(const char *path, struct fuse_file_info *fi) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_open(d->next, newpath, fi); + free(newpath); + } + return err; +} + +static int subdir_read_buf(const char *path, struct fuse_bufvec **bufp, + size_t size, loff_t offset, struct fuse_file_info *fi) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_read_buf(d->next, newpath, bufp, size, offset, fi); + free(newpath); + } + return err; +} + +static int subdir_write_buf(const char *path, struct fuse_bufvec *buf, + loff_t offset, struct fuse_file_info *fi) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_write_buf(d->next, newpath, buf, offset, fi); + free(newpath); + } + return err; +} + +static int subdir_statfs(const char *path, struct statvfs *stbuf) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_statfs(d->next, newpath, stbuf); + free(newpath); + } + return err; +} + +static int subdir_flush(const char *path, struct fuse_file_info *fi) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_flush(d->next, newpath, fi); + free(newpath); + } + return err; +} + +static int subdir_release(const char *path, struct fuse_file_info *fi) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_release(d->next, newpath, fi); + free(newpath); + } + return err; +} + +static int subdir_fsync(const char *path, int isdatasync, + struct fuse_file_info *fi) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_fsync(d->next, newpath, isdatasync, fi); + free(newpath); + } + return err; +} + +static int subdir_fsyncdir(const char *path, int isdatasync, + struct fuse_file_info *fi) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_fsyncdir(d->next, newpath, isdatasync, fi); + free(newpath); + } + return err; +} + +static int subdir_setxattr(const char *path, const char *name, + const char *value, size_t size, int flags) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_setxattr(d->next, newpath, name, value, size, + flags); + free(newpath); + } + return err; +} + +static int subdir_getxattr(const char *path, const char *name, char *value, + size_t size) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_getxattr(d->next, newpath, name, value, size); + free(newpath); + } + return err; +} + +static int subdir_listxattr(const char *path, char *list, size_t size) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_listxattr(d->next, newpath, list, size); + free(newpath); + } + return err; +} + +static int subdir_removexattr(const char *path, const char *name) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_removexattr(d->next, newpath, name); + free(newpath); + } + return err; +} + +static int subdir_lock(const char *path, struct fuse_file_info *fi, int cmd, + struct flock *lock) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_lock(d->next, newpath, fi, cmd, lock); + free(newpath); + } + return err; +} + +static int subdir_flock(const char *path, struct fuse_file_info *fi, int op) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_flock(d->next, newpath, fi, op); + free(newpath); + } + return err; +} + +static int subdir_bmap(const char *path, size_t blocksize, uint64_t *idx) +{ + struct subdir *d = subdir_get(); + char *newpath; + int err = subdir_addpath(d, path, &newpath); + if (!err) { + err = fuse_fs_bmap(d->next, newpath, blocksize, idx); + free(newpath); + } + return err; +} + +static void *subdir_init(struct fuse_conn_info *conn) +{ + struct subdir *d = subdir_get(); + fuse_fs_init(d->next, conn); + return d; +} + +static void subdir_destroy(void *data) +{ + struct subdir *d = data; + fuse_fs_destroy(d->next); + free(d->base); + free(d); +} + +static const struct fuse_operations subdir_oper = { + .destroy = subdir_destroy, + .init = subdir_init, + .getattr = subdir_getattr, + .fgetattr = subdir_fgetattr, + .access = subdir_access, + .readlink = subdir_readlink, + .opendir = subdir_opendir, + .readdir = subdir_readdir, + .releasedir = subdir_releasedir, + .mknod = subdir_mknod, + .mkdir = subdir_mkdir, + .symlink = subdir_symlink, + .unlink = subdir_unlink, + .rmdir = subdir_rmdir, + .rename = subdir_rename, + .link = subdir_link, + .chmod = subdir_chmod, + .chown = subdir_chown, + .truncate = subdir_truncate, + .ftruncate = subdir_ftruncate, + .utimens = subdir_utimens, + .create = subdir_create, + .open = subdir_open, + .read_buf = subdir_read_buf, + .write_buf = subdir_write_buf, + .statfs = subdir_statfs, + .flush = subdir_flush, + .release = subdir_release, + .fsync = subdir_fsync, + .fsyncdir = subdir_fsyncdir, + .setxattr = subdir_setxattr, + .getxattr = subdir_getxattr, + .listxattr = subdir_listxattr, + .removexattr = subdir_removexattr, + .lock = subdir_lock, + .flock = subdir_flock, + .bmap = subdir_bmap, + + .flag_nullpath_ok = 1, + .flag_nopath = 1, +}; + +static const struct fuse_opt subdir_opts[] = { + FUSE_OPT_KEY("-h", 0), + FUSE_OPT_KEY("--help", 0), + { "subdir=%s", offsetof(struct subdir, base), 0 }, + { "rellinks", offsetof(struct subdir, rellinks), 1 }, + { "norellinks", offsetof(struct subdir, rellinks), 0 }, + FUSE_OPT_END +}; + +static void subdir_help(void) +{ + fprintf(stderr, +" -o subdir=DIR prepend this directory to all paths (mandatory)\n" +" -o [no]rellinks transform absolute symlinks to relative\n"); +} + +static int subdir_opt_proc(void *data, const char *arg, int key, + struct fuse_args *outargs) +{ + (void) data; (void) arg; (void) outargs; + + if (!key) { + subdir_help(); + return -1; + } + + return 1; +} + +static struct fuse_fs *subdir_new(struct fuse_args *args, + struct fuse_fs *next[]) +{ + struct fuse_fs *fs; + struct subdir *d; + + d = calloc(1, sizeof(struct subdir)); + if (d == NULL) { + fprintf(stderr, "fuse-subdir: memory allocation failed\n"); + return NULL; + } + + if (fuse_opt_parse(args, d, subdir_opts, subdir_opt_proc) == -1) + goto out_free; + + if (!next[0] || next[1]) { + fprintf(stderr, "fuse-subdir: exactly one next filesystem required\n"); + goto out_free; + } + + if (!d->base) { + fprintf(stderr, "fuse-subdir: missing 'subdir' option\n"); + goto out_free; + } + + if (d->base[0] && d->base[strlen(d->base)-1] != '/') { + char *tmp = realloc(d->base, strlen(d->base) + 2); + if (!tmp) { + fprintf(stderr, "fuse-subdir: memory allocation failed\n"); + goto out_free; + } + d->base = tmp; + strcat(d->base, "/"); + } + d->baselen = strlen(d->base); + d->next = next[0]; + fs = fuse_fs_new(&subdir_oper, sizeof(subdir_oper), d); + if (!fs) + goto out_free; + return fs; + +out_free: + free(d->base); + free(d); + return NULL; +} + +FUSE_REGISTER_MODULE(subdir, subdir_new); |