aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--kernel/dir.c87
-rw-r--r--kernel/fuse_i.h6
-rw-r--r--kernel/inode.c23
-rw-r--r--lib/fuse.c26
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 <mszeredi@inf.bme.hu>
+
+ * Fix possible inode leak in userspace in case of unfinished
+ lookup/mknod/mkdir/symlink/link operation.
+
2004-06-20 Miklos Szeredi <mszeredi@inf.bme.hu>
* 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
@@ -229,6 +229,12 @@ struct inode *fuse_iget(struct super_block *sb, ino_t ino, int generation,
/**
+ * 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
*/
void fuse_init_file_inode(struct inode *inode);
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,