From: Miklos Szeredi Date: Fri, 22 Apr 2005 12:04:55 +0000 (+0000) Subject: fix X-Git-Tag: fuse_2_3_pre5~2 X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=0111f9dbe43a8c4ae516d3eb470186770894c5bf;p=qemu-gpiodev%2Flibfuse.git fix --- diff --git a/ChangeLog b/ChangeLog index 94327f4..5415803 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,6 +6,12 @@ * Fix buggy behavior of open(..., O_CREAT|O_EXCL) if interrupted. Reported by David Shaw + * Remove "allow_root" option from kernel module, and implement + it's functionality in the library + + * Fix Oops caused by premature release of fuse_conn. Clean up + related code, to be more readable + 2005-04-08 Miklos Szeredi * Fix Oops in case of nfs export. Spotted by David Shaw diff --git a/kernel/dev.c b/kernel/dev.c index 29a9b63..d64423e 100644 --- a/kernel/dev.c +++ b/kernel/dev.c @@ -24,7 +24,7 @@ static inline struct fuse_conn *fuse_get_conn(struct file *file) struct fuse_conn *fc; spin_lock(&fuse_lock); fc = file->private_data; - if (fc && !fc->sb) + if (fc && !fc->mounted) fc = NULL; spin_unlock(&fuse_lock); return fc; @@ -214,7 +214,7 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req) spin_unlock(&fuse_lock); if (req->background) { down_read(&fc->sbput_sem); - if (fc->sb) + if (fc->mounted) fuse_release_background(req); up_read(&fc->sbput_sem); } @@ -371,7 +371,7 @@ static void request_send_wait(struct fuse_conn *fc, struct fuse_req *req, { req->isreply = 1; spin_lock(&fuse_lock); - if (!fc->file) + if (!fc->connected) req->out.h.error = -ENOTCONN; else if (fc->conn_error) req->out.h.error = -ECONNREFUSED; @@ -404,7 +404,7 @@ void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req) static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req) { spin_lock(&fuse_lock); - if (fc->file) { + if (fc->connected) { queue_request(fc, req); spin_unlock(&fuse_lock); } else { @@ -659,7 +659,7 @@ static void request_wait(struct fuse_conn *fc) DECLARE_WAITQUEUE(wait, current); add_wait_queue_exclusive(&fc->waitq, &wait); - while (fc->sb && list_empty(&fc->pending)) { + while (fc->mounted && list_empty(&fc->pending)) { set_current_state(TASK_INTERRUPTIBLE); if (signal_pending(current)) break; @@ -709,7 +709,7 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov, goto err_unlock; request_wait(fc); err = -ENODEV; - if (!fc->sb) + if (!fc->mounted) goto err_unlock; err = -ERESTARTSYS; if (list_empty(&fc->pending)) @@ -917,7 +917,7 @@ static int fuse_dev_release(struct inode *inode, struct file *file) spin_lock(&fuse_lock); fc = file->private_data; if (fc) { - fc->file = NULL; + fc->connected = 0; end_requests(fc, &fc->pending); end_requests(fc, &fc->processing); fuse_release_conn(fc); diff --git a/kernel/dir.c b/kernel/dir.c index 6ae498c..947d538 100644 --- a/kernel/dir.c +++ b/kernel/dir.c @@ -441,9 +441,7 @@ static int fuse_revalidate(struct dentry *entry) if (get_node_id(inode) == FUSE_ROOT_ID) { if (!(fc->flags & FUSE_ALLOW_OTHER) && - current->fsuid != fc->user_id && - (!(fc->flags & FUSE_ALLOW_ROOT) || - !capable(CAP_DAC_OVERRIDE))) + current->fsuid != fc->user_id) return -EACCES; } else if (time_before_eq(jiffies, fi->i_time)) return 0; @@ -455,8 +453,7 @@ static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) { struct fuse_conn *fc = get_fuse_conn(inode); - if (!(fc->flags & FUSE_ALLOW_OTHER) && current->fsuid != fc->user_id && - (!(fc->flags & FUSE_ALLOW_ROOT) || !capable(CAP_DAC_OVERRIDE))) + if (!(fc->flags & FUSE_ALLOW_OTHER) && current->fsuid != fc->user_id) return -EACCES; else if (fc->flags & FUSE_DEFAULT_PERMISSIONS) { #ifdef KERNEL_2_6_10_PLUS diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h index 373f36d..164eaa7 100644 --- a/kernel/fuse_i.h +++ b/kernel/fuse_i.h @@ -101,10 +101,6 @@ static inline void set_page_dirty_lock(struct page *page) /** Bypass the page cache for read and write operations */ #define FUSE_DIRECT_IO (1 << 3) -/** Allow root and setuid-root programs to access fuse-mounted - filesystems */ -#define FUSE_ALLOW_ROOT (1 << 4) - /** FUSE inode */ struct fuse_inode { /** Inode data */ @@ -260,11 +256,8 @@ struct fuse_req { * unmounted. */ struct fuse_conn { - /** The superblock of the mounted filesystem */ - struct super_block *sb; - - /** The opened client device */ - struct file *file; + /** Reference count */ + int count; /** The user id for this mount */ uid_t user_id; @@ -307,6 +300,12 @@ struct fuse_conn { /** The next unique request id */ int reqctr; + /** Mount is active */ + unsigned mounted : 1; + + /** Connection established */ + unsigned connected : 1; + /** Connection failed (version mismatch) */ unsigned conn_error : 1; diff --git a/kernel/inode.c b/kernel/inode.c index 1feaba9..9b894f5 100644 --- a/kernel/inode.c +++ b/kernel/inode.c @@ -99,8 +99,8 @@ void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req, static void fuse_clear_inode(struct inode *inode) { - struct fuse_conn *fc = get_fuse_conn(inode); - if (fc && (inode->i_sb->s_flags & MS_ACTIVE)) { + if (inode->i_sb->s_flags & MS_ACTIVE) { + struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_inode *fi = get_fuse_inode(inode); fuse_send_forget(fc, fi->forget_req, fi->nodeid, inode->i_version); fi->forget_req = NULL; @@ -260,14 +260,13 @@ static void fuse_put_super(struct super_block *sb) struct fuse_req, bg_entry)); spin_lock(&fuse_lock); - fc->sb = NULL; + fc->mounted = 0; fc->user_id = 0; fc->flags = 0; /* Flush all readers on this fs */ wake_up_all(&fc->waitq); up_write(&fc->sbput_sem); fuse_release_conn(fc); - *get_fuse_conn_super_p(sb) = NULL; spin_unlock(&fuse_lock); } @@ -314,7 +313,6 @@ enum { OPT_USER_ID, OPT_DEFAULT_PERMISSIONS, OPT_ALLOW_OTHER, - OPT_ALLOW_ROOT, OPT_KERNEL_CACHE, #ifndef KERNEL_2_6 OPT_LARGE_READ, @@ -330,7 +328,6 @@ static match_table_t tokens = { {OPT_USER_ID, "user_id=%u"}, {OPT_DEFAULT_PERMISSIONS, "default_permissions"}, {OPT_ALLOW_OTHER, "allow_other"}, - {OPT_ALLOW_ROOT, "allow_root"}, {OPT_KERNEL_CACHE, "kernel_cache"}, #ifndef KERNEL_2_6 {OPT_LARGE_READ, "large_read"}, @@ -382,10 +379,6 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d) d->flags |= FUSE_ALLOW_OTHER; break; - case OPT_ALLOW_ROOT: - d->flags |= FUSE_ALLOW_ROOT; - break; - case OPT_KERNEL_CACHE: d->flags |= FUSE_KERNEL_CACHE; break; @@ -424,8 +417,6 @@ static int fuse_show_options(struct seq_file *m, struct vfsmount *mnt) seq_puts(m, ",default_permissions"); if (fc->flags & FUSE_ALLOW_OTHER) seq_puts(m, ",allow_other"); - if (fc->flags & FUSE_ALLOW_ROOT) - seq_puts(m, ",allow_root"); if (fc->flags & FUSE_KERNEL_CACHE) seq_puts(m, ",kernel_cache"); #ifndef KERNEL_2_6 @@ -453,7 +444,8 @@ static void free_conn(struct fuse_conn *fc) /* Must be called with the fuse lock held */ void fuse_release_conn(struct fuse_conn *fc) { - if (!fc->sb && !fc->file) + fc->count--; + if (!fc->count) free_conn(fc); } @@ -465,10 +457,6 @@ static struct fuse_conn *new_conn(void) if (fc != NULL) { int i; memset(fc, 0, sizeof(*fc)); - fc->sb = NULL; - fc->file = NULL; - fc->flags = 0; - fc->user_id = 0; init_waitqueue_head(&fc->waitq); INIT_LIST_HEAD(&fc->pending); INIT_LIST_HEAD(&fc->processing); @@ -508,8 +496,10 @@ static struct fuse_conn *get_conn(struct file *file, struct super_block *sb) fc = ERR_PTR(-EINVAL); } else { file->private_data = fc; - fc->sb = sb; - fc->file = file; + *get_fuse_conn_super_p(sb) = fc; + fc->mounted = 1; + fc->connected = 1; + fc->count = 2; } spin_unlock(&fuse_lock); return fc; @@ -636,8 +626,6 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) #endif fc->max_write = FUSE_MAX_IN / 2; - *get_fuse_conn_super_p(sb) = fc; - err = -ENOMEM; root = get_root_inode(sb, d.rootmode); if (root == NULL) @@ -653,10 +641,8 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) err: spin_lock(&fuse_lock); - fc->sb = NULL; fuse_release_conn(fc); spin_unlock(&fuse_lock); - *get_fuse_conn_super_p(sb) = NULL; return err; } diff --git a/lib/fuse.c b/lib/fuse.c index 82adeb2..9659a0d 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -35,6 +35,9 @@ /** Use st_ino field in getattr instead of generating inode numbers */ #define FUSE_USE_INO (1 << 3) +/** Only allow root or the owner to access the filesystem */ +#define FUSE_ALLOW_ROOT (1 << 4) + #define FUSE_KERNEL_MINOR_VERSION_NEED 1 #define FUSE_VERSION_FILE_OLD "/proc/fs/fuse/version" #define FUSE_VERSION_FILE_NEW "/sys/fs/fuse/version" @@ -1765,6 +1768,15 @@ void fuse_process_cmd(struct fuse *f, struct fuse_cmd *cmd) goto out; } + if ((f->flags & FUSE_ALLOW_ROOT) && in->uid != f->owner && in->uid != 0 && + in->opcode != FUSE_INIT && in->opcode != FUSE_READ && + in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC && + in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR && + in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR) { + send_reply(f, in, -EACCES, NULL, 0); + goto out; + } + ctx->fuse = f; ctx->uid = in->uid; ctx->gid = in->gid; @@ -2000,7 +2012,8 @@ int fuse_is_lib_option(const char *opt) { if (strcmp(opt, "debug") == 0 || strcmp(opt, "hard_remove") == 0 || - strcmp(opt, "use_ino") == 0) + strcmp(opt, "use_ino") == 0 || + strcmp(opt, "allow_root") == 0) return 1; else return 0; @@ -2025,6 +2038,8 @@ static int parse_lib_opts(struct fuse *f, const char *opts) f->flags |= FUSE_HARD_REMOVE; else if (strcmp(opt, "use_ino") == 0) f->flags |= FUSE_USE_INO; + else if (strcmp(opt, "allow_root") == 0) + f->flags |= FUSE_ALLOW_ROOT; else fprintf(stderr, "fuse: warning: unknown option `%s'\n", opt); } @@ -2100,6 +2115,8 @@ struct fuse *fuse_new_common(int fd, const char *opts, root->refctr = 1; hash_id(f, root); + f->owner = getuid(); + return f; out_free_root: diff --git a/lib/fuse_i.h b/lib/fuse_i.h index 4a9f04a..3bdfb00 100644 --- a/lib/fuse_i.h +++ b/lib/fuse_i.h @@ -32,6 +32,7 @@ struct fuse { void *user_data; int major; int minor; + uid_t owner; }; struct fuse *fuse_new_common(int fd, const char *opts, diff --git a/lib/helper.c b/lib/helper.c index 42fe44c..d1a3d1f 100644 --- a/lib/helper.c +++ b/lib/helper.c @@ -138,8 +138,12 @@ static int add_options(char **lib_optp, char **kernel_optp, const char *opts) while((opt = strsep(&s, ",")) != NULL) { int res; - if (fuse_is_lib_option(opt)) + if (fuse_is_lib_option(opt)) { res = add_option_to(opt, lib_optp); + /* Compatibility hack */ + if (strcmp(opt, "allow_root") == 0 && res != -1) + res = add_option_to("allow_other", kernel_optp); + } else res = add_option_to(opt, kernel_optp); if (res == -1) {