If the LOOKUP or GETATTR in nfs_instantiate fail, nfs_instantiate will do a
d_drop before returning. But some callers already do a d_drop in the case
of an error return. Make certain we do only one d_drop in all error paths.
This issue was introduced because over time, the symlink proc API diverged
slightly from the create/mkdir/mknod proc API. To prevent other coding
mistakes of this type, change the symlink proc API to be more like
create/mkdir/mknod and move the nfs_instantiate call into the symlink proc
routines so it is used in exactly the same way for create, mkdir, mknod,
and symlink.
Test plan:
Connectathon, all versions of NFS.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
struct inode *dir = dentry->d_parent->d_inode;
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
if (error)
struct inode *dir = dentry->d_parent->d_inode;
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
if (error)
}
if (!(fattr->valid & NFS_ATTR_FATTR)) {
struct nfs_server *server = NFS_SB(dentry->d_sb);
error = server->nfs_client->rpc_ops->getattr(server, fhandle, fattr);
if (error < 0)
}
if (!(fattr->valid & NFS_ATTR_FATTR)) {
struct nfs_server *server = NFS_SB(dentry->d_sb);
error = server->nfs_client->rpc_ops->getattr(server, fhandle, fattr);
if (error < 0)
}
inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
error = PTR_ERR(inode);
if (IS_ERR(inode))
}
inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
error = PTR_ERR(inode);
if (IS_ERR(inode))
d_instantiate(dentry, inode);
return 0;
d_instantiate(dentry, inode);
return 0;
-out_err:
- d_drop(dentry);
- return error;
nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
{
struct iattr attr;
nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
{
struct iattr attr;
- struct nfs_fattr sym_attr;
- struct nfs_fh sym_fh;
struct qstr qsymname;
int error;
struct qstr qsymname;
int error;
lock_kernel();
nfs_begin_data_update(dir);
lock_kernel();
nfs_begin_data_update(dir);
- error = NFS_PROTO(dir)->symlink(dir, &dentry->d_name, &qsymname,
- &attr, &sym_fh, &sym_attr);
+ error = NFS_PROTO(dir)->symlink(dir, dentry, &qsymname, &attr);
nfs_end_data_update(dir);
if (!error)
nfs_end_data_update(dir);
if (!error)
- error = nfs_instantiate(dentry, &sym_fh, &sym_attr);
- else
d_drop(dentry);
unlock_kernel();
return error;
d_drop(dentry);
unlock_kernel();
return error;
-nfs3_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path,
- struct iattr *sattr, struct nfs_fh *fhandle,
- struct nfs_fattr *fattr)
+nfs3_proc_symlink(struct inode *dir, struct dentry *dentry, struct qstr *path,
+ struct iattr *sattr)
- struct nfs_fattr dir_attr;
+ struct nfs_fh fhandle;
+ struct nfs_fattr fattr, dir_attr;
struct nfs3_symlinkargs arg = {
.fromfh = NFS_FH(dir),
struct nfs3_symlinkargs arg = {
.fromfh = NFS_FH(dir),
- .fromname = name->name,
- .fromlen = name->len,
+ .fromname = dentry->d_name.name,
+ .fromlen = dentry->d_name.len,
.topath = path->name,
.tolen = path->len,
.sattr = sattr
};
struct nfs3_diropres res = {
.dir_attr = &dir_attr,
.topath = path->name,
.tolen = path->len,
.sattr = sattr
};
struct nfs3_diropres res = {
.dir_attr = &dir_attr,
- .fh = fhandle,
- .fattr = fattr
+ .fh = &fhandle,
+ .fattr = &fattr
};
struct rpc_message msg = {
.rpc_proc = &nfs3_procedures[NFS3PROC_SYMLINK],
};
struct rpc_message msg = {
.rpc_proc = &nfs3_procedures[NFS3PROC_SYMLINK],
if (path->len > NFS3_MAXPATHLEN)
return -ENAMETOOLONG;
if (path->len > NFS3_MAXPATHLEN)
return -ENAMETOOLONG;
- dprintk("NFS call symlink %s -> %s\n", name->name, path->name);
+
+ dprintk("NFS call symlink %s -> %s\n", dentry->d_name.name,
+ path->name);
nfs_fattr_init(&dir_attr);
nfs_fattr_init(&dir_attr);
+ nfs_fattr_init(&fattr);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
nfs_post_op_update_inode(dir, &dir_attr);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
nfs_post_op_update_inode(dir, &dir_attr);
+ if (status != 0)
+ goto out;
+ status = nfs_instantiate(dentry, &fhandle, &fattr);
+out:
dprintk("NFS reply symlink: %d\n", status);
return status;
}
dprintk("NFS reply symlink: %d\n", status);
return status;
}
-static int _nfs4_proc_symlink(struct inode *dir, struct qstr *name,
- struct qstr *path, struct iattr *sattr, struct nfs_fh *fhandle,
- struct nfs_fattr *fattr)
+static int _nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
+ struct qstr *path, struct iattr *sattr)
{
struct nfs_server *server = NFS_SERVER(dir);
{
struct nfs_server *server = NFS_SERVER(dir);
- struct nfs_fattr dir_fattr;
+ struct nfs_fh fhandle;
+ struct nfs_fattr fattr, dir_fattr;
struct nfs4_create_arg arg = {
.dir_fh = NFS_FH(dir),
.server = server,
struct nfs4_create_arg arg = {
.dir_fh = NFS_FH(dir),
.server = server,
+ .name = &dentry->d_name,
.attrs = sattr,
.ftype = NF4LNK,
.bitmask = server->attr_bitmask,
};
struct nfs4_create_res res = {
.server = server,
.attrs = sattr,
.ftype = NF4LNK,
.bitmask = server->attr_bitmask,
};
struct nfs4_create_res res = {
.server = server,
- .fh = fhandle,
- .fattr = fattr,
+ .fh = &fhandle,
+ .fattr = &fattr,
.dir_fattr = &dir_fattr,
};
struct rpc_message msg = {
.dir_fattr = &dir_fattr,
};
struct rpc_message msg = {
if (path->len > NFS4_MAXPATHLEN)
return -ENAMETOOLONG;
if (path->len > NFS4_MAXPATHLEN)
return -ENAMETOOLONG;
+ nfs_fattr_init(&fattr);
nfs_fattr_init(&dir_fattr);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
nfs_fattr_init(&dir_fattr);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
update_changeattr(dir, &res.dir_cinfo);
update_changeattr(dir, &res.dir_cinfo);
- nfs_post_op_update_inode(dir, res.dir_fattr);
+ nfs_post_op_update_inode(dir, res.dir_fattr);
+ status = nfs_instantiate(dentry, &fhandle, &fattr);
+ }
-static int nfs4_proc_symlink(struct inode *dir, struct qstr *name,
- struct qstr *path, struct iattr *sattr, struct nfs_fh *fhandle,
- struct nfs_fattr *fattr)
+static int nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
+ struct qstr *path, struct iattr *sattr)
{
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(NFS_SERVER(dir),
{
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(NFS_SERVER(dir),
- _nfs4_proc_symlink(dir, name, path, sattr,
- fhandle, fattr),
+ _nfs4_proc_symlink(dir, dentry, path, sattr),
&exception);
} while (exception.retry);
return err;
&exception);
} while (exception.retry);
return err;
-nfs_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path,
- struct iattr *sattr, struct nfs_fh *fhandle,
- struct nfs_fattr *fattr)
+nfs_proc_symlink(struct inode *dir, struct dentry *dentry, struct qstr *path,
+ struct iattr *sattr)
+ struct nfs_fh fhandle;
+ struct nfs_fattr fattr;
struct nfs_symlinkargs arg = {
.fromfh = NFS_FH(dir),
struct nfs_symlinkargs arg = {
.fromfh = NFS_FH(dir),
- .fromname = name->name,
- .fromlen = name->len,
+ .fromname = dentry->d_name.name,
+ .fromlen = dentry->d_name.len,
.topath = path->name,
.tolen = path->len,
.sattr = sattr
.topath = path->name,
.tolen = path->len,
.sattr = sattr
if (path->len > NFS2_MAXPATHLEN)
return -ENAMETOOLONG;
if (path->len > NFS2_MAXPATHLEN)
return -ENAMETOOLONG;
- dprintk("NFS call symlink %s -> %s\n", name->name, path->name);
- nfs_fattr_init(fattr);
- fhandle->size = 0;
+
+ dprintk("NFS call symlink %s -> %s\n", dentry->d_name.name,
+ path->name);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
nfs_mark_for_revalidate(dir);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
nfs_mark_for_revalidate(dir);
+
+ /*
+ * V2 SYMLINK requests don't return any attributes. Setting the
+ * filehandle size to zero indicates to nfs_instantiate that it
+ * should fill in the data with a LOOKUP call on the wire.
+ */
+ if (status == 0) {
+ nfs_fattr_init(&fattr);
+ fhandle.size = 0;
+ status = nfs_instantiate(dentry, &fhandle, &fattr);
+ }
+
dprintk("NFS reply symlink: %d\n", status);
return status;
}
dprintk("NFS reply symlink: %d\n", status);
return status;
}
int (*rename) (struct inode *, struct qstr *,
struct inode *, struct qstr *);
int (*link) (struct inode *, struct inode *, struct qstr *);
int (*rename) (struct inode *, struct qstr *,
struct inode *, struct qstr *);
int (*link) (struct inode *, struct inode *, struct qstr *);
- int (*symlink) (struct inode *, struct qstr *, struct qstr *,
- struct iattr *, struct nfs_fh *,
- struct nfs_fattr *);
+ int (*symlink) (struct inode *, struct dentry *, struct qstr *,
+ struct iattr *);
int (*mkdir) (struct inode *, struct dentry *, struct iattr *);
int (*rmdir) (struct inode *, struct qstr *);
int (*readdir) (struct dentry *, struct rpc_cred *,
int (*mkdir) (struct inode *, struct dentry *, struct iattr *);
int (*rmdir) (struct inode *, struct qstr *);
int (*readdir) (struct dentry *, struct rpc_cred *,