From c180eebe1390c2076ead6a9bc95a02efb994edb7 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 25 Jul 2008 01:49:01 -0700 Subject: [PATCH] fuse: add fuse_lookup_name() helper Add a new helper function which sends a LOOKUP request with the supplied name. This will be used by the next patch to send special LOOKUP requests with "." and ".." as the name. Signed-off-by: Miklos Szeredi Cc: Christoph Hellwig Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/fuse/dir.c | 141 +++++++++++++++++++++++++++++++------------------- 1 file changed, 89 insertions(+), 52 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index be5450dd6387..51d0035ff07e 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -112,18 +112,16 @@ static void fuse_invalidate_entry(struct dentry *entry) fuse_invalidate_entry_cache(entry); } -static void fuse_lookup_init(struct fuse_req *req, struct inode *dir, - struct dentry *entry, +static void fuse_lookup_init(struct fuse_conn *fc, struct fuse_req *req, + u64 nodeid, struct qstr *name, struct fuse_entry_out *outarg) { - struct fuse_conn *fc = get_fuse_conn(dir); - memset(outarg, 0, sizeof(struct fuse_entry_out)); req->in.h.opcode = FUSE_LOOKUP; - req->in.h.nodeid = get_node_id(dir); + req->in.h.nodeid = nodeid; req->in.numargs = 1; - req->in.args[0].size = entry->d_name.len + 1; - req->in.args[0].value = entry->d_name.name; + req->in.args[0].size = name->len + 1; + req->in.args[0].value = name->name; req->out.numargs = 1; if (fc->minor < 9) req->out.args[0].size = FUSE_COMPAT_ENTRY_OUT_SIZE; @@ -189,7 +187,8 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) attr_version = fuse_get_attr_version(fc); parent = dget_parent(entry); - fuse_lookup_init(req, parent->d_inode, entry, &outarg); + fuse_lookup_init(fc, req, get_node_id(parent->d_inode), + &entry->d_name, &outarg); request_send(fc, req); dput(parent); err = req->out.h.error; @@ -255,73 +254,111 @@ static struct dentry *fuse_d_add_directory(struct dentry *entry, return d_splice_alias(inode, entry); } +int fuse_lookup_name(struct super_block *sb, u64 nodeid, struct qstr *name, + struct fuse_entry_out *outarg, struct inode **inode) +{ + struct fuse_conn *fc = get_fuse_conn_super(sb); + struct fuse_req *req; + struct fuse_req *forget_req; + u64 attr_version; + int err; + + *inode = NULL; + err = -ENAMETOOLONG; + if (name->len > FUSE_NAME_MAX) + goto out; + + req = fuse_get_req(fc); + err = PTR_ERR(req); + if (IS_ERR(req)) + goto out; + + forget_req = fuse_get_req(fc); + err = PTR_ERR(forget_req); + if (IS_ERR(forget_req)) { + fuse_put_request(fc, req); + goto out; + } + + attr_version = fuse_get_attr_version(fc); + + fuse_lookup_init(fc, req, nodeid, name, 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) + goto out_put_forget; + + err = -EIO; + if (!outarg->nodeid) + goto out_put_forget; + if (!fuse_valid_type(outarg->attr.mode)) + goto out_put_forget; + + *inode = fuse_iget(sb, outarg->nodeid, outarg->generation, + &outarg->attr, entry_attr_timeout(outarg), + attr_version); + err = -ENOMEM; + if (!*inode) { + fuse_send_forget(fc, forget_req, outarg->nodeid, 1); + goto out; + } + err = 0; + + out_put_forget: + fuse_put_request(fc, forget_req); + out: + return err; +} + static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, struct nameidata *nd) { int err; struct fuse_entry_out outarg; - struct inode *inode = NULL; + struct inode *inode; struct dentry *newent; struct fuse_conn *fc = get_fuse_conn(dir); - struct fuse_req *req; - struct fuse_req *forget_req; - u64 attr_version; + bool outarg_valid = true; - if (entry->d_name.len > FUSE_NAME_MAX) - return ERR_PTR(-ENAMETOOLONG); - - req = fuse_get_req(fc); - if (IS_ERR(req)) - return ERR_CAST(req); - - forget_req = fuse_get_req(fc); - if (IS_ERR(forget_req)) { - fuse_put_request(fc, req); - return ERR_CAST(forget_req); + err = fuse_lookup_name(dir->i_sb, get_node_id(dir), &entry->d_name, + &outarg, &inode); + if (err == -ENOENT) { + outarg_valid = false; + err = 0; } + if (err) + goto out_err; - attr_version = fuse_get_attr_version(fc); - - 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) || - !fuse_valid_type(outarg.attr.mode))) - err = -EIO; - if (!err && outarg.nodeid) { - inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, - &outarg.attr, entry_attr_timeout(&outarg), - attr_version); - if (!inode) { - fuse_send_forget(fc, forget_req, outarg.nodeid, 1); - return ERR_PTR(-ENOMEM); - } - } - fuse_put_request(fc, forget_req); - if (err && err != -ENOENT) - return ERR_PTR(err); + err = -EIO; + if (inode && get_node_id(inode) == FUSE_ROOT_ID) + goto out_iput; if (inode && S_ISDIR(inode->i_mode)) { mutex_lock(&fc->inst_mutex); newent = fuse_d_add_directory(entry, inode); mutex_unlock(&fc->inst_mutex); - if (IS_ERR(newent)) { - iput(inode); - return newent; - } - } else + err = PTR_ERR(newent); + if (IS_ERR(newent)) + goto out_iput; + } else { newent = d_splice_alias(inode, entry); + } entry = newent ? newent : entry; entry->d_op = &fuse_dentry_operations; - if (!err) + if (outarg_valid) fuse_change_entry_timeout(entry, &outarg); else fuse_invalidate_entry_cache(entry); + return newent; + + out_iput: + iput(inode); + out_err: + return ERR_PTR(err); } /*