From 2778f6cf5d27e40337f163f476fac8f0ffec1130 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Mon, 21 Jun 2004 09:45:30 +0000 Subject: [PATCH] fixes --- ChangeLog | 5 +++ kernel/dir.c | 87 ++++++++++++++++++++++++++++++++----------------- kernel/fuse_i.h | 6 ++++ kernel/inode.c | 23 ++++++++----- lib/fuse.c | 26 ++++++++++++--- 5 files changed, 103 insertions(+), 44 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6a595fb..b6bb446 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2004-06-21 Miklos Szeredi + + * Fix possible inode leak in userspace in case of unfinished + lookup/mknod/mkdir/symlink/link operation. + 2004-06-20 Miklos Szeredi * Fix some races and cleanups in fuse_read_super() diff --git a/kernel/dir.c b/kernel/dir.c index 2cf0676..59cab49 100644 --- a/kernel/dir.c +++ b/kernel/dir.c @@ -102,6 +102,23 @@ struct inode *fuse_iget(struct super_block *sb, ino_t ino, int generation, return inode; } +static int fuse_send_lookup(struct fuse_conn *fc, struct fuse_req *req, + struct inode *dir, struct dentry *entry, + struct fuse_entry_out *outarg, int *version) +{ + req->in.h.opcode = FUSE_LOOKUP; + req->in.h.ino = dir->i_ino; + req->in.numargs = 1; + req->in.args[0].size = entry->d_name.len + 1; + req->in.args[0].value = entry->d_name.name; + req->out.numargs = 1; + req->out.args[0].size = sizeof(struct fuse_entry_out); + req->out.args[0].value = outarg; + request_send(fc, req); + *version = req->out.h.unique; + return req->out.h.error; +} + static int fuse_do_lookup(struct inode *dir, struct dentry *entry, struct fuse_entry_out *outarg, int *version) { @@ -114,17 +131,8 @@ static int fuse_do_lookup(struct inode *dir, struct dentry *entry, req = fuse_get_request(fc); if (!req) return -ERESTARTSYS; - req->in.h.opcode = FUSE_LOOKUP; - req->in.h.ino = dir->i_ino; - req->in.numargs = 1; - req->in.args[0].size = entry->d_name.len + 1; - req->in.args[0].value = entry->d_name.name; - req->out.numargs = 1; - req->out.args[0].size = sizeof(struct fuse_entry_out); - req->out.args[0].value = outarg; - request_send(fc, req); - *version = req->out.h.unique; - err = req->out.h.error; + + err = fuse_send_lookup(fc, req, dir, entry, outarg, version); fuse_put_request(fc, req); return err; } @@ -142,18 +150,30 @@ static inline unsigned long time_to_jiffies(unsigned long sec, static int fuse_lookup_iget(struct inode *dir, struct dentry *entry, struct inode **inodep) { + struct fuse_conn *fc = INO_FC(dir); int err; struct fuse_entry_out outarg; int version; struct inode *inode = NULL; + struct fuse_req *req; + + if (entry->d_name.len > FUSE_NAME_MAX) + return -ENAMETOOLONG; + req = fuse_get_request(fc); + if (!req) + return -ERESTARTSYS; - err = fuse_do_lookup(dir, entry, &outarg, &version); + err = fuse_send_lookup(fc, req, dir, entry, &outarg, &version); if (!err) { inode = fuse_iget(dir->i_sb, outarg.ino, outarg.generation, &outarg.attr, version); - if (!inode) + if (!inode) { + fuse_send_forget(fc, req, outarg.ino, version); return -ENOMEM; - } else if (err != -ENOENT) + } + } + fuse_put_request(fc, req); + if (err && err != -ENOENT) return err; entry->d_time = time_to_jiffies(outarg.entry_valid, @@ -163,15 +183,19 @@ static int fuse_lookup_iget(struct inode *dir, struct dentry *entry, return 0; } -static int lookup_new_entry(struct inode *dir, struct dentry *entry, - struct fuse_entry_out *outarg, int version, - int mode) +static int lookup_new_entry(struct fuse_conn *fc, struct fuse_req *req, + struct inode *dir, struct dentry *entry, + struct fuse_entry_out *outarg, int version, + int mode) { struct inode *inode; inode = fuse_iget(dir->i_sb, outarg->ino, outarg->generation, &outarg->attr, version); - if (!inode) + if (!inode) { + fuse_send_forget(fc, req, outarg->ino, version); return -ENOMEM; + } + fuse_put_request(fc, req); /* Don't allow userspace to do really stupid things... */ if ((inode->i_mode ^ mode) & S_IFMT) { @@ -213,9 +237,10 @@ static int _fuse_mknod(struct inode *dir, struct dentry *entry, int mode, request_send(fc, req); err = req->out.h.error; if (!err) - err = lookup_new_entry(dir, entry, &outarg, req->out.h.unique, - mode); - fuse_put_request(fc, req); + err = lookup_new_entry(fc, req, dir, entry, &outarg, + req->out.h.unique, mode); + else + fuse_put_request(fc, req); return err; } @@ -251,9 +276,10 @@ static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode) request_send(fc, req); err = req->out.h.error; if (!err) - err = lookup_new_entry(dir, entry, &outarg, req->out.h.unique, - S_IFDIR); - fuse_put_request(fc, req); + err = lookup_new_entry(fc, req, dir, entry, &outarg, + req->out.h.unique, S_IFDIR); + else + fuse_put_request(fc, req); return err; } @@ -286,9 +312,10 @@ static int fuse_symlink(struct inode *dir, struct dentry *entry, request_send(fc, req); err = req->out.h.error; if (!err) - err = lookup_new_entry(dir, entry, &outarg, req->out.h.unique, - S_IFLNK); - fuse_put_request(fc, req); + err = lookup_new_entry(fc, req, dir, entry, &outarg, + req->out.h.unique, S_IFLNK); + else + fuse_put_request(fc, req); return err; } @@ -399,10 +426,10 @@ static int fuse_link(struct dentry *entry, struct inode *newdir, if (!err) { /* Invalidate old entry, so attributes are refreshed */ d_invalidate(entry); - err = lookup_new_entry(newdir, newent, &outarg, + err = lookup_new_entry(fc, req, newdir, newent, &outarg, req->out.h.unique, inode->i_mode); - } - fuse_put_request(fc, req); + } else + fuse_put_request(fc, req); return err; } diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h index f9daa7e..7055d17 100644 --- a/kernel/fuse_i.h +++ b/kernel/fuse_i.h @@ -228,6 +228,12 @@ struct inode *fuse_iget(struct super_block *sb, ino_t ino, int generation, struct fuse_attr *attr, int version); +/** + * Send FORGET command + */ +void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req, ino_t ino, + int version); + /** * Initialise operations on regular file */ diff --git a/kernel/inode.c b/kernel/inode.c index d04d636..83ac407 100644 --- a/kernel/inode.c +++ b/kernel/inode.c @@ -44,24 +44,29 @@ static void fuse_read_inode(struct inode *inode) /* No op */ } +void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req, ino_t ino, + int version) +{ + struct fuse_forget_in *inarg = &req->misc.forget_in; + inarg->version = version; + req->in.h.opcode = FUSE_FORGET; + req->in.h.ino = ino; + req->in.numargs = 1; + req->in.args[0].size = sizeof(struct fuse_forget_in); + req->in.args[0].value = inarg; + request_send_noreply(fc, req); +} + static void fuse_clear_inode(struct inode *inode) { struct fuse_conn *fc = INO_FC(inode); struct fuse_req *req; - struct fuse_forget_in *inarg = NULL; if (fc == NULL) return; req = fuse_get_request_nonint(fc); - inarg = &req->misc.forget_in; - inarg->version = inode->i_version; - req->in.h.opcode = FUSE_FORGET; - req->in.h.ino = inode->i_ino; - req->in.numargs = 1; - req->in.args[0].size = sizeof(struct fuse_forget_in); - req->in.args[0].value = inarg; - request_send_noreply(fc, req); + fuse_send_forget(fc, req, inode->i_ino, inode->i_version); } static void fuse_put_super(struct super_block *sb) diff --git a/lib/fuse.c b/lib/fuse.c index 4015d82..eeae295 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -444,6 +444,7 @@ static int lookup_path(struct fuse *f, fino_t ino, int version, char *name, static void do_lookup(struct fuse *f, struct fuse_in_header *in, char *name) { int res; + int res2; char *path; struct fuse_entry_out arg; @@ -459,7 +460,9 @@ static void do_lookup(struct fuse *f, struct fuse_in_header *in, char *name) res = lookup_path(f, in->ino, in->unique, name, path, &arg); free(path); } - send_reply(f, in, res, &arg, sizeof(arg)); + res2 = send_reply(f, in, res, &arg, sizeof(arg)); + if (res == 0 && res2 == -ENOENT) + destroy_node(f, arg.ino, in->unique); } static void do_forget(struct fuse *f, struct fuse_in_header *in, @@ -636,6 +639,7 @@ static void do_mknod(struct fuse *f, struct fuse_in_header *in, struct fuse_mknod_in *inarg) { int res; + int res2; char *path; char *name = PARAM(inarg); struct fuse_entry_out outarg; @@ -655,13 +659,16 @@ static void do_mknod(struct fuse *f, struct fuse_in_header *in, } free(path); } - send_reply(f, in, res, &outarg, sizeof(outarg)); + res2 = send_reply(f, in, res, &outarg, sizeof(outarg)); + if (res == 0 && res2 == -ENOENT) + destroy_node(f, outarg.ino, in->unique); } static void do_mkdir(struct fuse *f, struct fuse_in_header *in, struct fuse_mkdir_in *inarg) { int res; + int res2; char *path; char *name = PARAM(inarg); struct fuse_entry_out outarg; @@ -681,7 +688,9 @@ static void do_mkdir(struct fuse *f, struct fuse_in_header *in, } free(path); } - send_reply(f, in, res, &outarg, sizeof(outarg)); + res2 = send_reply(f, in, res, &outarg, sizeof(outarg)); + if (res == 0 && res2 == -ENOENT) + destroy_node(f, outarg.ino, in->unique); } static void do_unlink(struct fuse *f, struct fuse_in_header *in, char *name) @@ -726,6 +735,7 @@ static void do_symlink(struct fuse *f, struct fuse_in_header *in, char *name, char *link) { int res; + int res2; char *path; struct fuse_entry_out outarg; @@ -744,7 +754,10 @@ static void do_symlink(struct fuse *f, struct fuse_in_header *in, char *name, } free(path); } - send_reply(f, in, res, &outarg, sizeof(outarg)); + res2 = send_reply(f, in, res, &outarg, sizeof(outarg)); + if (res == 0 && res2 == -ENOENT) + destroy_node(f, outarg.ino, in->unique); + } static void do_rename(struct fuse *f, struct fuse_in_header *in, @@ -779,6 +792,7 @@ static void do_link(struct fuse *f, struct fuse_in_header *in, struct fuse_link_in *arg) { int res; + int res2; char *oldpath; char *newpath; char *name = PARAM(arg); @@ -804,7 +818,9 @@ static void do_link(struct fuse *f, struct fuse_in_header *in, } free(oldpath); } - send_reply(f, in, res, &outarg, sizeof(outarg)); + res2 = send_reply(f, in, res, &outarg, sizeof(outarg)); + if (res == 0 && res2 == -ENOENT) + destroy_node(f, outarg.ino, in->unique); } static void do_open(struct fuse *f, struct fuse_in_header *in, -- 2.30.2