mirror of
https://github.com/libfuse/libfuse.git
synced 2024-11-23 12:14:15 +08:00
fix lookup Oops
This commit is contained in:
parent
4398858b86
commit
fe621f9ae1
@ -1,3 +1,11 @@
|
||||
2006-11-19 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix bug in certain error paths of lookup routines. The request
|
||||
object was reused for sending FORGET, which is illegal. This bug
|
||||
could cause an Oops in linux-2.6.18 or in fuse-2.6.0, and might
|
||||
silently corrupt memory in earlier versions. Report and test
|
||||
program by Russ Cox
|
||||
|
||||
2006-11-11 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Print an error if an incompatible kernel interface version is
|
||||
|
52
kernel/dir.c
52
kernel/dir.c
@ -138,6 +138,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
|
||||
struct fuse_entry_out outarg;
|
||||
struct fuse_conn *fc;
|
||||
struct fuse_req *req;
|
||||
struct fuse_req *forget_req;
|
||||
struct dentry *parent;
|
||||
|
||||
/* For negative dentries, always do a fresh lookup */
|
||||
@ -149,25 +150,33 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
|
||||
if (IS_ERR(req))
|
||||
return 0;
|
||||
|
||||
forget_req = fuse_get_req(fc);
|
||||
if (IS_ERR(forget_req)) {
|
||||
fuse_put_request(fc, req);
|
||||
return 0;
|
||||
}
|
||||
|
||||
parent = dget_parent(entry);
|
||||
fuse_lookup_init(req, parent->d_inode, entry, &outarg);
|
||||
request_send(fc, req);
|
||||
dput(parent);
|
||||
err = req->out.h.error;
|
||||
fuse_put_request(fc, req);
|
||||
/* Zero nodeid is same as -ENOENT */
|
||||
if (!err && !outarg.nodeid)
|
||||
err = -ENOENT;
|
||||
if (!err) {
|
||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||
if (outarg.nodeid != get_node_id(inode)) {
|
||||
fuse_send_forget(fc, req, outarg.nodeid, 1);
|
||||
fuse_send_forget(fc, forget_req,
|
||||
outarg.nodeid, 1);
|
||||
return 0;
|
||||
}
|
||||
spin_lock(&fc->lock);
|
||||
fi->nlookup ++;
|
||||
spin_unlock(&fc->lock);
|
||||
}
|
||||
fuse_put_request(fc, req);
|
||||
fuse_put_request(fc, forget_req);
|
||||
if (err || (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
|
||||
return 0;
|
||||
|
||||
@ -220,6 +229,7 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
|
||||
struct dentry *newent;
|
||||
struct fuse_conn *fc = get_fuse_conn(dir);
|
||||
struct fuse_req *req;
|
||||
struct fuse_req *forget_req;
|
||||
|
||||
if (entry->d_name.len > FUSE_NAME_MAX)
|
||||
return ERR_PTR(-ENAMETOOLONG);
|
||||
@ -228,9 +238,16 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
|
||||
if (IS_ERR(req))
|
||||
return ERR_PTR(PTR_ERR(req));
|
||||
|
||||
forget_req = fuse_get_req(fc);
|
||||
if (IS_ERR(forget_req)) {
|
||||
fuse_put_request(fc, req);
|
||||
return ERR_PTR(PTR_ERR(forget_req));
|
||||
}
|
||||
|
||||
fuse_lookup_init(req, dir, entry, &outarg);
|
||||
request_send(fc, req);
|
||||
err = req->out.h.error;
|
||||
fuse_put_request(fc, req);
|
||||
/* Zero nodeid is same as -ENOENT, but with valid timeout */
|
||||
if (!err && outarg.nodeid &&
|
||||
(invalid_nodeid(outarg.nodeid) || !valid_mode(outarg.attr.mode)))
|
||||
@ -239,11 +256,11 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
|
||||
inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
|
||||
&outarg.attr);
|
||||
if (!inode) {
|
||||
fuse_send_forget(fc, req, outarg.nodeid, 1);
|
||||
fuse_send_forget(fc, forget_req, outarg.nodeid, 1);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
}
|
||||
fuse_put_request(fc, req);
|
||||
fuse_put_request(fc, forget_req);
|
||||
if (err && err != -ENOENT)
|
||||
return ERR_PTR(err);
|
||||
|
||||
@ -390,6 +407,13 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req,
|
||||
struct fuse_entry_out outarg;
|
||||
struct inode *inode;
|
||||
int err;
|
||||
struct fuse_req *forget_req;
|
||||
|
||||
forget_req = fuse_get_req(fc);
|
||||
if (IS_ERR(forget_req)) {
|
||||
fuse_put_request(fc, req);
|
||||
return PTR_ERR(forget_req);
|
||||
}
|
||||
|
||||
req->in.h.nodeid = get_node_id(dir);
|
||||
req->out.numargs = 1;
|
||||
@ -397,24 +421,24 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req,
|
||||
req->out.args[0].value = &outarg;
|
||||
request_send(fc, req);
|
||||
err = req->out.h.error;
|
||||
if (err) {
|
||||
fuse_put_request(fc, req);
|
||||
return err;
|
||||
}
|
||||
fuse_put_request(fc, req);
|
||||
if (err)
|
||||
goto out_put_forget_req;
|
||||
|
||||
err = -EIO;
|
||||
if (invalid_nodeid(outarg.nodeid))
|
||||
goto out_put_request;
|
||||
goto out_put_forget_req;
|
||||
|
||||
if ((outarg.attr.mode ^ mode) & S_IFMT)
|
||||
goto out_put_request;
|
||||
goto out_put_forget_req;
|
||||
|
||||
inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
|
||||
&outarg.attr);
|
||||
if (!inode) {
|
||||
fuse_send_forget(fc, req, outarg.nodeid, 1);
|
||||
fuse_send_forget(fc, forget_req, outarg.nodeid, 1);
|
||||
return -ENOMEM;
|
||||
}
|
||||
fuse_put_request(fc, req);
|
||||
fuse_put_request(fc, forget_req);
|
||||
|
||||
if (S_ISDIR(inode->i_mode)) {
|
||||
struct dentry *alias;
|
||||
@ -436,8 +460,8 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req,
|
||||
fuse_invalidate_attr(dir);
|
||||
return 0;
|
||||
|
||||
out_put_request:
|
||||
fuse_put_request(fc, req);
|
||||
out_put_forget_req:
|
||||
fuse_put_request(fc, forget_req);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user