diff options
author | Martin Pärtel <martin.partel@gmail.com> | 2015-09-13 18:46:31 +0100 |
---|---|---|
committer | Martin Pärtel <martin.partel@gmail.com> | 2015-09-13 18:46:31 +0100 |
commit | 938fca9e085b5cced50800abba06b5300df5ac4c (patch) | |
tree | de909073f5d826524bdceb728a5913c4a5a612d2 /src | |
parent | c2a1a6a6215b370b76a41fcc388c9240571ddc0c (diff) | |
parent | db20a1bfae369f619f9ee2afcafffb27b9897fa4 (diff) | |
download | bindfs-938fca9e085b5cced50800abba06b5300df5ac4c.tar.gz |
Merge remote-tracking branch 'hstern/resolve-symlinks'
Diffstat (limited to 'src')
-rw-r--r-- | src/bindfs.1 | 5 | ||||
-rw-r--r-- | src/bindfs.c | 344 |
2 files changed, 266 insertions, 83 deletions
diff --git a/src/bindfs.1 b/src/bindfs.1 index 1f90240..55fc349 100644 --- a/src/bindfs.1 +++ b/src/bindfs.1 @@ -263,6 +263,11 @@ The underlying file's ctime will still be updated normally. Shows the hard link count of all files as 1. .TP +.B \-\-resolve\-symlinks, \-o resolve-symlinks +Transparently resolves symbolic links. Disables creation of new symbolic +links. + +.TP .B \-\-multithreaded, \-o multithreaded Run bindfs in multithreaded mode. While bindfs is designed to be otherwise thread-safe, there is currently a race condition that may pose diff --git a/src/bindfs.c b/src/bindfs.c index 73312b3..48a8693 100644 --- a/src/bindfs.c +++ b/src/bindfs.c @@ -40,6 +40,7 @@ #include <stdlib.h> #include <stddef.h> #include <stdio.h> +#include <stdbool.h> #include <string.h> #include <ctype.h> #ifdef HAVE_SYS_TYPES_H @@ -150,6 +151,7 @@ static struct Settings { int ctime_from_mtime; int hide_hard_links; + int resolve_symlinks; } settings; @@ -162,8 +164,8 @@ static int is_mirroring_enabled(); /* Checks whether the uid is to be the mirrored owner of all files. */ static int is_mirrored_user(uid_t uid); -/* Processes the virtual path to a real path. Don't free() the result. */ -static const char *process_path(const char *path); +/* Processes the virtual path to a real path. Always free() the result. */ +static char *process_path(const char *path, bool resolve_symlinks); /* The common parts of getattr and fgetattr. */ static int getattr_common(const char *path, struct stat *stbuf); @@ -245,18 +247,28 @@ static int is_mirrored_user(uid_t uid) } -static const char *process_path(const char *path) +static char * process_path(const char *path, bool resolve_symlinks) { - if (path == NULL) /* possible? */ + char * res; + + if (path == NULL) { /* possible? */ + errno = EINVAL; return NULL; + } while (*path == '/') ++path; if (*path == '\0') - return "."; - else - return path; + path = "."; + + if (resolve_symlinks && settings.resolve_symlinks) { + res = realpath(path, NULL); + if (res) + return res; + } + + return strdup(path); } static int getattr_common(const char *procpath, struct stat *stbuf) @@ -384,34 +396,57 @@ static void bindfs_destroy(void *private_data) static int bindfs_getattr(const char *path, struct stat *stbuf) { - path = process_path(path); + int res; + char *real_path; - if (lstat(path, stbuf) == -1) + real_path = process_path(path, true); + if (real_path == NULL) return -errno; - return getattr_common(path, stbuf); + + if (lstat(real_path, stbuf) == -1) { + free (real_path); + return -errno; + } + + res = getattr_common(real_path, stbuf); + free(real_path); + return res; } static int bindfs_fgetattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) { - path = process_path(path); + int res; + char *real_path; + + real_path = process_path(path, true); + if (real_path == NULL) + return -errno; - if (fstat(fi->fh, stbuf) == -1) + if (fstat(fi->fh, stbuf) == -1) { + free(real_path); return -errno; - return getattr_common(path, stbuf); + } + res = getattr_common(real_path, stbuf); + free(real_path); + return res; } static int bindfs_readlink(const char *path, char *buf, size_t size) { int res; + char *real_path; - path = process_path(path); + real_path = process_path(path, true); + if (real_path == NULL) + return -errno; /* No need to check for access to the link itself, since symlink permissions don't matter. Access to the path components of the symlink are automatically queried by FUSE. */ - res = readlink(path, buf, size - 1); + res = readlink(real_path, buf, size - 1); + free(real_path); if (res == -1) return -errno; @@ -422,10 +457,14 @@ static int bindfs_readlink(const char *path, char *buf, size_t size) static int bindfs_opendir(const char *path, struct fuse_file_info *fi) { DIR *dp; + char *real_path; - path = process_path(path); + real_path = process_path(path, true); + if (real_path == NULL) + return -errno; - dp = opendir(path); + dp = opendir(real_path); + free(real_path); if (dp == NULL) return -errno; @@ -447,10 +486,14 @@ static int bindfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, struct stat st; int result = 0; long pc_ret; + char *real_path; - path = process_path(path); + real_path = process_path(path, true); + if (real_path == NULL) + return -errno; - pc_ret = pathconf(path, _PC_NAME_MAX); + pc_ret = pathconf(real_path, _PC_NAME_MAX); + free(real_path); if (pc_ret < 0) { DPRINTF("pathconf failed: %s (%d)", strerror(errno), errno); pc_ret = NAME_MAX; @@ -491,20 +534,26 @@ static int bindfs_mknod(const char *path, mode_t mode, dev_t rdev) { int res; struct fuse_context *fc; + char *real_path; - path = process_path(path); + real_path = process_path(path, true); + if (real_path == NULL) + return -errno; mode = permchain_apply(settings.create_permchain, mode); if (S_ISFIFO(mode)) - res = mkfifo(path, mode); + res = mkfifo(real_path, mode); else - res = mknod(path, mode, rdev); - if (res == -1) + res = mknod(real_path, mode, rdev); + if (res == -1) { + free(real_path); return -errno; + } fc = fuse_get_context(); - chown_new_file(path, fc, &chown); + chown_new_file(real_path, fc, &chown); + free(real_path); return 0; } @@ -513,18 +562,24 @@ static int bindfs_mkdir(const char *path, mode_t mode) { int res; struct fuse_context *fc; + char *real_path; - path = process_path(path); + real_path = process_path(path, true); + if (real_path == NULL) + return -errno; mode |= S_IFDIR; /* tell permchain_apply this is a directory */ mode = permchain_apply(settings.create_permchain, mode); - res = mkdir(path, mode & 0777); - if (res == -1) + res = mkdir(real_path, mode & 0777); + if (res == -1) { + free(real_path); return -errno; + } fc = fuse_get_context(); - chown_new_file(path, fc, &chown); + chown_new_file(real_path, fc, &chown); + free(real_path); return 0; } @@ -532,10 +587,14 @@ static int bindfs_mkdir(const char *path, mode_t mode) static int bindfs_unlink(const char *path) { int res; + char *real_path; - path = process_path(path); + real_path = process_path(path, false); + if (real_path == NULL) + return -errno; - res = unlink(path); + res = unlink(real_path); + free(real_path); if (res == -1) return -errno; @@ -545,10 +604,26 @@ static int bindfs_unlink(const char *path) static int bindfs_rmdir(const char *path) { int res; + char *real_path; + struct stat st; - path = process_path(path); + real_path = process_path(path, false); + if (real_path == NULL) + return -errno; + + if (lstat(real_path, &st) == -1) + return -errno; - res = rmdir(path); + if (S_ISLNK(st.st_mode)) { + res = unlink(real_path); + free(real_path); + if (res == -1) + return -errno; + return 0; + } + + res = rmdir(real_path); + free(real_path); if (res == -1) return -errno; @@ -559,15 +634,32 @@ static int bindfs_symlink(const char *from, const char *to) { int res; struct fuse_context *fc; + char *real_from, *real_to; - to = process_path(to); + if (settings.resolve_symlinks) + return -EPERM; - res = symlink(from, to); - if (res == -1) + real_from = process_path(from, false); + if (real_from == NULL) return -errno; + real_to = process_path(to, false); + if (real_to == NULL) { + free(real_from); + return -errno; + } + + res = symlink(from, real_to); + if (res == -1) { + free(real_from); + free(real_to); + return -errno; + } + fc = fuse_get_context(); - chown_new_file(to, fc, &lchown); + chown_new_file(real_to, fc, &lchown); + free(real_from); + free(real_to); return 0; } @@ -575,11 +667,21 @@ static int bindfs_symlink(const char *from, const char *to) static int bindfs_rename(const char *from, const char *to) { int res; + char *real_from, *real_to; + + real_from = process_path(from, false); + if (real_from == NULL) + return -errno; - from = process_path(from); - to = process_path(to); + real_to = process_path(to, true); + if (real_to == NULL) { + free(real_from); + return -errno; + } - res = rename(from, to); + res = rename(real_from, real_to); + free(real_from); + free(real_to); if (res == -1) return -errno; @@ -589,11 +691,21 @@ static int bindfs_rename(const char *from, const char *to) static int bindfs_link(const char *from, const char *to) { int res; + char *real_from, *real_to; - from = process_path(from); - to = process_path(to); + real_from = process_path(from, true); + if (real_from == NULL) + return -errno; - res = link(from, to); + real_to = process_path(to, true); + if (real_to == NULL) { + free(real_from); + return -errno; + } + + res = link(real_from, real_to); + free(real_from); + free(real_to); if (res == -1) return -errno; @@ -605,13 +717,18 @@ static int bindfs_chmod(const char *path, mode_t mode) int file_execute_only = 0; struct stat st; mode_t diff = 0; + char *real_path; - path = process_path(path); + real_path = process_path(path, true); + if (real_path == NULL) + return -errno; if (settings.chmod_allow_x) { /* Get the old permission bits and see which bits would change. */ - if (lstat(path, &st) == -1) + if (lstat(real_path, &st) == -1) { + free(real_path); return -errno; + } if (S_ISREG(st.st_mode)) { diff = (st.st_mode & 07777) ^ (mode & 07777); @@ -622,26 +739,36 @@ static int bindfs_chmod(const char *path, mode_t mode) switch (settings.chmod_policy) { case CHMOD_NORMAL: mode = permchain_apply(settings.chmod_permchain, mode); - if (chmod(path, mode) == -1) + if (chmod(real_path, mode) == -1) { + free(real_path); return -errno; + } + free(real_path); return 0; case CHMOD_IGNORE: if (file_execute_only) { diff &= 00111; /* See which execute bits were flipped. Forget about other differences. */ - if (chmod(path, st.st_mode ^ diff) == -1) + if (chmod(real_path, st.st_mode ^ diff) == -1) { + free(real_path); return -errno; + } } + free(real_path); return 0; case CHMOD_DENY: if (file_execute_only) { if ((diff & 07666) == 0) { /* Only execute bits have changed, so we can allow this. */ - if (chmod(path, mode) == -1) + if (chmod(real_path, mode) == -1) { + free(real_path); return -errno; + } + free(real_path); return 0; } } + free(real_path); return -EPERM; default: assert(0); @@ -651,6 +778,7 @@ static int bindfs_chmod(const char *path, mode_t mode) static int bindfs_chown(const char *path, uid_t uid, gid_t gid) { int res; + char *real_path; if (uid != -1) { switch (settings.chown_policy) { @@ -679,8 +807,12 @@ static int bindfs_chown(const char *path, uid_t uid, gid_t gid) } if (uid != -1 || gid != -1) { - path = process_path(path); - res = lchown(path, uid, gid); + real_path = process_path(path, true); + if (real_path == NULL) + return -errno; + + res = lchown(real_path, uid, gid); + free(real_path); if (res == -1) return -errno; } @@ -691,10 +823,14 @@ static int bindfs_chown(const char *path, uid_t uid, gid_t gid) static int bindfs_truncate(const char *path, off_t size) { int res; + char *real_path; - path = process_path(path); + real_path = process_path(path, true); + if (real_path == NULL) + return -errno; - res = truncate(path, size); + res = truncate(real_path, size); + free(real_path); if (res == -1) return -errno; @@ -717,22 +853,26 @@ static int bindfs_ftruncate(const char *path, off_t size, static int bindfs_utimens(const char *path, const struct timespec ts[2]) { int res; + char *real_path; - path = process_path(path); + real_path = process_path(path, true); + if (real_path == NULL ) + return -errno; #ifdef HAVE_UTIMENSAT - res = utimensat(settings.mntsrc_fd, path, ts, AT_SYMLINK_NOFOLLOW); + res = utimensat(settings.mntsrc_fd, real_path, ts, AT_SYMLINK_NOFOLLOW); #elif HAVE_LUTIMES struct timeval tv[2]; tv[0].tv_sec = ts[0].tv_sec; tv[0].tv_usec = ts[0].tv_nsec / 1000; tv[1].tv_sec = ts[1].tv_sec; tv[1].tv_usec = ts[1].tv_nsec / 1000; - res = lutimes(path, tv); + res = lutimes(real_path, tv); #else #error "No symlink-compatible utime* function available." #endif - + + free(real_path); if (res == -1) return -errno; @@ -743,18 +883,24 @@ static int bindfs_create(const char *path, mode_t mode, struct fuse_file_info *f { int fd; struct fuse_context *fc; + char *real_path; - path = process_path(path); + real_path = process_path(path, true); + if (real_path == NULL) + return -errno; mode |= S_IFREG; /* tell permchain_apply this is a regular file */ mode = permchain_apply(settings.create_permchain, mode); - fd = open(path, fi->flags, mode & 0777); - if (fd == -1) + fd = open(real_path, fi->flags, mode & 0777); + if (fd == -1) { + free(real_path); return -errno; + } fc = fuse_get_context(); - chown_new_file(path, fc, &chown); + chown_new_file(real_path, fc, &chown); + free(real_path); fi->fh = fd; return 0; @@ -763,10 +909,14 @@ static int bindfs_create(const char *path, mode_t mode, struct fuse_file_info *f static int bindfs_open(const char *path, struct fuse_file_info *fi) { int fd; + char *real_path; - path = process_path(path); + real_path = process_path(path, true); + if (real_path == NULL) + return -errno; - fd = open(path, fi->flags); + fd = open(real_path, fi->flags); + free(real_path); if (fd == -1) return -errno; @@ -811,10 +961,14 @@ static int bindfs_write(const char *path, const char *buf, size_t size, static int bindfs_statfs(const char *path, struct statvfs *stbuf) { int res; + char *real_path; - path = process_path(path); + real_path = process_path(path, true); + if (real_path == NULL) + return -errno; - res = statvfs(path, stbuf); + res = statvfs(real_path, stbuf); + free(real_path); if (res == -1) return -errno; @@ -864,13 +1018,16 @@ static int bindfs_setxattr(const char *path, const char *name, const char *value #endif { int res; + char *real_path; DPRINTF("setxattr %s %s=%s", path, name, value); if (settings.xattr_policy == XATTR_READ_ONLY) return -EACCES; - path = process_path(path); + real_path = process_path(path, true); + if (real_path == NULL) + return -errno; #if defined(__APPLE__) if (!strncmp(name, XATTR_APPLE_PREFIX, sizeof(XATTR_APPLE_PREFIX) - 1)) { @@ -881,16 +1038,17 @@ static int bindfs_setxattr(const char *path, const char *name, const char *value char new_name[MAXPATHLEN]; memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR)); memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1); - res = setxattr(path, new_name, value, size, position, flags); + res = setxattr(real_path, new_name, value, size, position, flags); } else { - res = setxattr(path, name, value, size, position, flags); + res = setxattr(real_path, name, value, size, position, flags); } #elif defined(HAVE_LSETXATTR) - res = lsetxattr(path, name, value, size, flags); + res = lsetxattr(real_path, name, value, size, flags); #else - res = setxattr(path, name, value, size, 0, flags | XATTR_NOFOLLOW); + res = setxattr(real_path, name, value, size, 0, flags | XATTR_NOFOLLOW); #endif + free(real_path); if (res == -1) return -errno; return 0; @@ -905,25 +1063,29 @@ static int bindfs_getxattr(const char *path, const char *name, char *value, #endif { int res; + char *real_path; DPRINTF("getxattr %s %s", path, name); - path = process_path(path); + real_path = process_path(path, true); + if (real_path == NULL) + return -errno; #if defined(__APPLE__) if (strcmp(name, A_KAUTH_FILESEC_XATTR) == 0) { char new_name[MAXPATHLEN]; memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR)); memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1); - res = getxattr(path, new_name, value, size, position, XATTR_NOFOLLOW); + res = getxattr(real_path, new_name, value, size, position, XATTR_NOFOLLOW); } else { - res = getxattr(path, name, value, size, position, XATTR_NOFOLLOW); + res = getxattr(real_path, name, value, size, position, XATTR_NOFOLLOW); } #elif defined(HAVE_LGETXATTR) - res = lgetxattr(path, name, value, size); + res = lgetxattr(real_path, name, value, size); #else - res = getxattr(path, name, value, size, 0, XATTR_NOFOLLOW); + res = getxattr(real_path, name, value, size, 0, XATTR_NOFOLLOW); #endif + free(real_path); if (res == -1) return -errno; return res; @@ -931,12 +1093,16 @@ static int bindfs_getxattr(const char *path, const char *name, char *value, static int bindfs_listxattr(const char *path, char* list, size_t size) { + char *real_path; + DPRINTF("listxattr %s", path); - path = process_path(path); + real_path = process_path(path, true); + if (real_path == NULL) + return -errno; #if defined(__APPLE__) - ssize_t res = listxattr(path, list, size, XATTR_NOFOLLOW); + ssize_t res = listxattr(real_path, list, size, XATTR_NOFOLLOW); if (res > 0) { if (list) { size_t len = 0; @@ -955,7 +1121,7 @@ static int bindfs_listxattr(const char *path, char* list, size_t size) // TODO: https://github.com/osxfuse/fuse/blob/master/example/fusexmp_fh.c // had this commented out bit here o_O /* - ssize_t res2 = getxattr(path, G_KAUTH_FILESEC_XATTR, NULL, 0, 0, + ssize_t res2 = getxattr(real_path, G_KAUTH_FILESEC_XATTR, NULL, 0, 0, XATTR_NOFOLLOW); if (res2 >= 0) { res -= sizeof(G_KAUTH_FILESEC_XATTR); @@ -964,10 +1130,11 @@ static int bindfs_listxattr(const char *path, char* list, size_t size) } } #elif defined(HAVE_LLISTXATTR) - int res = llistxattr(path, list, size); + int res = llistxattr(real_path, list, size); #else - int res = listxattr(path, list, size, XATTR_NOFOLLOW); + int res = listxattr(real_path, list, size, XATTR_NOFOLLOW); #endif + free(real_path); if (res == -1) return -errno; return res; @@ -976,29 +1143,33 @@ static int bindfs_listxattr(const char *path, char* list, size_t size) static int bindfs_removexattr(const char *path, const char *name) { int res; + char *real_path; DPRINTF("removexattr %s %s", path, name); if (settings.xattr_policy == XATTR_READ_ONLY) return -EACCES; - path = process_path(path); + real_path = process_path(path, true); + if (real_path == NULL) + return -errno; #if defined(__APPLE__) if (strcmp(name, A_KAUTH_FILESEC_XATTR) == 0) { char new_name[MAXPATHLEN]; memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR)); memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1); - res = removexattr(path, new_name, XATTR_NOFOLLOW); + res = removexattr(real_path, new_name, XATTR_NOFOLLOW); } else { - res = removexattr(path, name, XATTR_NOFOLLOW); + res = removexattr(real_path, name, XATTR_NOFOLLOW); } #elif defined(HAVE_LREMOVEXATTR) - res = lremovexattr(path, name); + res = lremovexattr(real_path, name); #else - res = removexattr(path, name, XATTR_NOFOLLOW); + res = removexattr(real_path, name, XATTR_NOFOLLOW); #endif + free(real_path); if (res == -1) return -errno; return 0; @@ -1106,6 +1277,7 @@ static void print_usage(const char *progname) " --ctime-from-mtime Read file properties' change time\n" " from file content modification time.\n" " --hide-hard-links Always report a hard link count of 1.\n" + " --resolve-symlinks Resolve symbolic links.\n" " --multithreaded Enable multithreaded mode. See man page\n" " for security issue with current implementation.\n" "\n" @@ -1144,6 +1316,7 @@ enum OptionKey { OPTKEY_REALISTIC_PERMISSIONS, OPTKEY_CTIME_FROM_MTIME, OPTKEY_HIDE_HARD_LINKS, + OPTKEY_RESOLVE_SYMLINKS, OPTKEY_MULTITHREADED }; @@ -1225,6 +1398,9 @@ static int process_option(void *data, const char *arg, int key, case OPTKEY_HIDE_HARD_LINKS: settings.hide_hard_links = 1; return 0; + case OPTKEY_RESOLVE_SYMLINKS: + settings.resolve_symlinks = 1; + return 0; case OPTKEY_NONOPTION: if (!settings.mntsrc) { @@ -1540,6 +1716,7 @@ int main(int argc, char *argv[]) OPT2("--realistic-permissions", "realistic-permissions", OPTKEY_REALISTIC_PERMISSIONS), OPT2("--ctime-from-mtime", "ctime-from-mtime", OPTKEY_CTIME_FROM_MTIME), OPT2("--hide-hard-links", "hide-hard-links", OPTKEY_HIDE_HARD_LINKS), + OPT2("--resolve-symlinks", "resolve-symlinks", OPTKEY_RESOLVE_SYMLINKS), OPT_OFFSET2("--multithreaded", "multithreaded", multithreaded, -1), FUSE_OPT_END }; @@ -1578,6 +1755,7 @@ int main(int argc, char *argv[]) settings.realistic_permissions = 0; settings.ctime_from_mtime = 0; settings.hide_hard_links = 0; + settings.resolve_symlinks = 0; atexit(&atexit_func); /* Parse options */ |