2012-04-18 17:46:30

by Anna Schumaker

[permalink] [raw]
Subject: [PATCH 0/6] Fix SECINFO procedure

From: Bryan Schumaker <[email protected]>

My initial SECINFO implementation was merged in 2.6.39, but accidentally
got disabled in 3.0. These patches re-enable SECINFO as part of LOOKUP
and push it down to the v4 layer, instead of existing as an rpc_op (I
create a "submount" rpc_op instead, see patch 3).

I also noticed that SECINFO_NO_NAME was never run with the current xdr
decoding logic. Patch 6 fixes this problem.

Comments are appreciated!

- Bryan

Bryan Schumaker (6):
NFS: pre-clone the rpc_clnt
NFS: Pass alternate rpc_clnt to fs_locations()
NFS: Create a submount rpc_op
NFS: Do secinfo as part of lookup
NFS: Remove extra rpc_clnt argument to proc_lookup
NFS: Fix SECINFO_NO_NAME

fs/nfs/client.c | 50 ++++++++-------
fs/nfs/dir.c | 6 +-
fs/nfs/internal.h | 22 ++-----
fs/nfs/namespace.c | 154 +++++++++--------------------------------------
fs/nfs/nfs3proc.c | 3 +-
fs/nfs/nfs4_fs.h | 12 +++-
fs/nfs/nfs4namespace.c | 110 ++++++++++++++++++++++++++++++++-
fs/nfs/nfs4proc.c | 93 ++++++++++++++++++++--------
fs/nfs/nfs4xdr.c | 26 +++++---
fs/nfs/proc.c | 3 +-
fs/nfs/super.c | 4 +-
include/linux/nfs_xdr.h | 6 +-
12 files changed, 282 insertions(+), 207 deletions(-)

--
1.7.10



2012-04-18 17:46:37

by Anna Schumaker

[permalink] [raw]
Subject: [PATCH 5/6] NFS: Remove extra rpc_clnt argument to proc_lookup

From: Bryan Schumaker <[email protected]>

Now that I'm doing secinfo automatically in the v4 code this extra
argument isn't needed.

Signed-off-by: Bryan Schumaker <[email protected]>
---
fs/nfs/dir.c | 6 +++---
fs/nfs/namespace.c | 3 +--
fs/nfs/nfs3proc.c | 2 +-
fs/nfs/nfs4_fs.h | 3 +--
fs/nfs/nfs4proc.c | 2 +-
fs/nfs/proc.c | 2 +-
include/linux/nfs_xdr.h | 3 +--
7 files changed, 9 insertions(+), 12 deletions(-)

diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 4aaf031..c2a80c0 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -1143,7 +1143,7 @@ static int nfs_lookup_revalidate(struct dentry *dentry, struct nameidata *nd)
if (fhandle == NULL || fattr == NULL)
goto out_error;

- error = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, &dentry->d_name, fhandle, fattr);
+ error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
if (error)
goto out_bad;
if (nfs_compare_fh(NFS_FH(inode), fhandle))
@@ -1299,7 +1299,7 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
parent = dentry->d_parent;
/* Protect against concurrent sillydeletes */
nfs_block_sillyrename(parent);
- error = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, &dentry->d_name, fhandle, fattr);
+ error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
if (error == -ENOENT)
goto no_entry;
if (error < 0) {
@@ -1646,7 +1646,7 @@ int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
if (dentry->d_inode)
goto out;
if (fhandle->size == 0) {
- error = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, &dentry->d_name, fhandle, fattr);
+ error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
if (error)
goto out_error;
}
diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c
index 3138077..884aa78 100644
--- a/fs/nfs/namespace.c
+++ b/fs/nfs/namespace.c
@@ -262,8 +262,7 @@ struct vfsmount *nfs_submount(struct nfs_server *server, struct dentry *dentry,
struct rpc_clnt *client;
struct vfsmount *mnt;

- err = server->nfs_client->rpc_ops->lookup(server->client, parent->d_inode,
- &dentry->d_name, fh, fattr);
+ err = server->nfs_client->rpc_ops->lookup(parent->d_inode, &dentry->d_name, fh, fattr);
dput(parent);

if (err != 0)
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
index 0ac098a..a8dbec0 100644
--- a/fs/nfs/nfs3proc.c
+++ b/fs/nfs/nfs3proc.c
@@ -142,7 +142,7 @@ nfs3_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
}

static int
-nfs3_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qstr *name,
+nfs3_proc_lookup(struct inode *dir, struct qstr *name,
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
struct nfs3_diropargs arg = {
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index a3471234..6488a01 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -220,8 +220,7 @@ extern int nfs4_do_close(struct nfs4_state *state, gfp_t gfp_mask, int wait, boo
extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle);
extern int nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir, const struct qstr *name,
struct nfs4_fs_locations *fs_locations, struct page *page);
-extern int nfs4_proc_lookup(struct rpc_clnt *, struct inode *, struct qstr *,
- struct nfs_fh *, struct nfs_fattr *);
+extern int nfs4_proc_lookup(struct inode *, struct qstr *, struct nfs_fh *, struct nfs_fattr *);
struct rpc_clnt *nfs4_proc_lookup_mountpoint(struct inode *, struct qstr *,
struct nfs_fh *, struct nfs_fattr *);
int nfs4_proc_secinfo(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 9f83a22..b4d8010 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -2566,7 +2566,7 @@ out:
return err;
}

-int nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qstr *name,
+int nfs4_proc_lookup(struct inode *dir, struct qstr *name,
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
int status;
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index da18f86..a72ee08 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -178,7 +178,7 @@ nfs_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
}

static int
-nfs_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qstr *name,
+nfs_proc_lookup(struct inode *dir, struct qstr *name,
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
struct nfs_diropargs arg = {
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 24c11b2..cfc907d 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -1233,8 +1233,7 @@ struct nfs_rpc_ops {
struct nfs_fattr *);
int (*setattr) (struct dentry *, struct nfs_fattr *,
struct iattr *);
- int (*lookup) (struct rpc_clnt *clnt, struct inode *, struct qstr *,
- struct nfs_fh *, struct nfs_fattr *);
+ int (*lookup) (struct inode *, struct qstr *, struct nfs_fh *, struct nfs_fattr *);
int (*access) (struct inode *, struct nfs_access_entry *);
int (*readlink)(struct inode *, struct page *, unsigned int,
unsigned int);
--
1.7.10


2012-04-18 17:46:38

by Anna Schumaker

[permalink] [raw]
Subject: [PATCH 6/6] NFS: Fix SECINFO_NO_NAME

From: Bryan Schumaker <[email protected]>

I was using the same decoder function for SECINFO and SECINFO_NO_NAME,
so it was returning an error when it tried to decode an OP_SECINFO_NO_NAME
header as OP_SECINFO.

Signed-off-by: Bryan Schumaker <[email protected]>
---
fs/nfs/nfs4xdr.c | 24 +++++++++++++++++++-----
1 file changed, 19 insertions(+), 5 deletions(-)

diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index eca81b1..78b71a6 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -5087,16 +5087,13 @@ out_err:
return -EINVAL;
}

-static int decode_secinfo(struct xdr_stream *xdr, struct nfs4_secinfo_res *res)
+static int decode_secinfo_common(struct xdr_stream *xdr, struct nfs4_secinfo_res *res)
{
struct nfs4_secinfo_flavor *sec_flavor;
int status;
__be32 *p;
int i, num_flavors;

- status = decode_op_hdr(xdr, OP_SECINFO);
- if (status)
- goto out;
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
@@ -5122,6 +5119,7 @@ static int decode_secinfo(struct xdr_stream *xdr, struct nfs4_secinfo_res *res)
res->flavors->num_flavors++;
}

+ status = 0;
out:
return status;
out_overflow:
@@ -5129,6 +5127,22 @@ out_overflow:
return -EIO;
}

+static int decode_secinfo(struct xdr_stream *xdr, struct nfs4_secinfo_res *res)
+{
+ int status = decode_op_hdr(xdr, OP_SECINFO);
+ if (status)
+ return status;
+ return decode_secinfo_common(xdr, res);
+}
+
+static int decode_secinfo_no_name(struct xdr_stream *xdr, struct nfs4_secinfo_res *res)
+{
+ int status = decode_op_hdr(xdr, OP_SECINFO_NO_NAME);
+ if (status)
+ return status;
+ return decode_secinfo_common(xdr, res);
+}
+
#if defined(CONFIG_NFS_V4_1)
static int decode_exchange_id(struct xdr_stream *xdr,
struct nfs41_exchange_id_res *res)
@@ -6814,7 +6828,7 @@ static int nfs4_xdr_dec_secinfo_no_name(struct rpc_rqst *rqstp,
status = decode_putrootfh(xdr);
if (status)
goto out;
- status = decode_secinfo(xdr, res);
+ status = decode_secinfo_no_name(xdr, res);
out:
return status;
}
--
1.7.10


2012-04-18 17:46:36

by Anna Schumaker

[permalink] [raw]
Subject: [PATCH 4/6] NFS: Do secinfo as part of lookup

From: Bryan Schumaker <[email protected]>

Whenever lookup sees wrongsec do a secinfo and retry the lookup to find
important attributes, such as "is this a referral mountpoint". This
means that secinfo is done in v4 code only, we can remove the rpc_op.

Signed-off-by: Bryan Schumaker <[email protected]>
---
fs/nfs/internal.h | 8 ----
fs/nfs/namespace.c | 84 --------------------------------------
fs/nfs/nfs4_fs.h | 5 +++
fs/nfs/nfs4namespace.c | 102 ++++++++++++++++++++++++++++++++++++++++-------
fs/nfs/nfs4proc.c | 77 ++++++++++++++++++++++++++---------
fs/nfs/nfs4xdr.c | 2 -
include/linux/nfs_xdr.h | 1 -
7 files changed, 151 insertions(+), 128 deletions(-)

diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index a7995b3..9d7d4d1 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -224,7 +224,6 @@ extern const u32 nfs41_maxwrite_overhead;
/* nfs4proc.c */
#ifdef CONFIG_NFS_V4
extern struct rpc_procinfo nfs4_procedures[];
-void nfs_fixup_secinfo_attributes(struct nfs_fattr *, struct nfs_fh *);
#endif

extern int nfs4_init_ds_session(struct nfs_client *clp);
@@ -275,13 +274,6 @@ struct vfsmount *nfs_do_submount(struct dentry *, struct nfs_fh *,
struct vfsmount *nfs_submount(struct nfs_server *, struct dentry *,
struct nfs_fh *, struct nfs_fattr *);

-#ifdef CONFIG_NFS_V4
-rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *);
-int nfs_lookup_with_sec(struct nfs_server *, struct dentry *, struct dentry *,
- struct nfs_fh *, struct nfs_fattr *);
-
-#endif
-
/* getroot.c */
extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *,
const char *);
diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c
index 5f401fa..3138077 100644
--- a/fs/nfs/namespace.c
+++ b/fs/nfs/namespace.c
@@ -113,90 +113,6 @@ Elong:
return ERR_PTR(-ENAMETOOLONG);
}

-#ifdef CONFIG_NFS_V4
-rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors)
-{
- struct gss_api_mech *mech;
- struct xdr_netobj oid;
- int i;
- rpc_authflavor_t pseudoflavor = RPC_AUTH_UNIX;
-
- for (i = 0; i < flavors->num_flavors; i++) {
- struct nfs4_secinfo_flavor *flavor;
- flavor = &flavors->flavors[i];
-
- if (flavor->flavor == RPC_AUTH_NULL || flavor->flavor == RPC_AUTH_UNIX) {
- pseudoflavor = flavor->flavor;
- break;
- } else if (flavor->flavor == RPC_AUTH_GSS) {
- oid.len = flavor->gss.sec_oid4.len;
- oid.data = flavor->gss.sec_oid4.data;
- mech = gss_mech_get_by_OID(&oid);
- if (!mech)
- continue;
- pseudoflavor = gss_svc_to_pseudoflavor(mech, flavor->gss.service);
- gss_mech_put(mech);
- break;
- }
- }
-
- return pseudoflavor;
-}
-
-static int nfs_negotiate_security(const struct dentry *parent,
- const struct dentry *dentry,
- rpc_authflavor_t *flavor)
-{
- struct page *page;
- struct nfs4_secinfo_flavors *flavors;
- int (*secinfo)(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *);
- int ret = -EPERM;
-
- secinfo = NFS_PROTO(parent->d_inode)->secinfo;
- if (secinfo != NULL) {
- page = alloc_page(GFP_KERNEL);
- if (!page) {
- ret = -ENOMEM;
- goto out;
- }
- flavors = page_address(page);
- ret = secinfo(parent->d_inode, &dentry->d_name, flavors);
- *flavor = nfs_find_best_sec(flavors);
- put_page(page);
- }
-
-out:
- return ret;
-}
-
-int nfs_lookup_with_sec(struct nfs_server *server, struct dentry *parent,
- struct dentry *dentry, struct nfs_fh *fh,
- struct nfs_fattr *fattr)
-{
- struct rpc_clnt *clone;
- struct rpc_auth *auth;
- rpc_authflavor_t flavor;
- int err;
-
- err = nfs_negotiate_security(parent, dentry, &flavor);
- if (err < 0)
- goto out;
- clone = rpc_clone_client(server->client);
- auth = rpcauth_create(flavor, clone);
- if (!auth) {
- err = -EIO;
- goto out_shutdown;
- }
- err = server->nfs_client->rpc_ops->lookup(clone, parent->d_inode,
- &dentry->d_name,
- fh, fattr);
-out_shutdown:
- rpc_shutdown_client(clone);
-out:
- return err;
-}
-#endif /* CONFIG_NFS_V4 */
-
/*
* nfs_d_automount - Handle crossing a mountpoint on the server
* @path - The mountpoint
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index e761268..a3471234 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -207,6 +207,8 @@ extern const struct inode_operations nfs4_dir_inode_operations;
/* nfs4namespace.c */
struct vfsmount *nfs4_submount(struct nfs_server *, struct dentry *,
struct nfs_fh *, struct nfs_fattr *);
+struct rpc_clnt *nfs4_create_sec_client(struct rpc_clnt *, struct inode *, struct qstr *);
+rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *);

/* nfs4proc.c */
extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, struct rpc_cred *, struct nfs4_setclientid_res *);
@@ -220,6 +222,9 @@ extern int nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir, co
struct nfs4_fs_locations *fs_locations, struct page *page);
extern int nfs4_proc_lookup(struct rpc_clnt *, struct inode *, struct qstr *,
struct nfs_fh *, struct nfs_fattr *);
+struct rpc_clnt *nfs4_proc_lookup_mountpoint(struct inode *, struct qstr *,
+ struct nfs_fh *, struct nfs_fattr *);
+int nfs4_proc_secinfo(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *);
extern int nfs4_release_lockowner(struct nfs4_lock_state *);
extern const struct xattr_handler *nfs4_xattr_handlers[];

diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c
index a12b747..a2d08bd 100644
--- a/fs/nfs/nfs4namespace.c
+++ b/fs/nfs/nfs4namespace.c
@@ -108,6 +108,84 @@ static size_t nfs_parse_server_name(char *string, size_t len,
return ret;
}

+rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors)
+{
+ struct gss_api_mech *mech;
+ struct xdr_netobj oid;
+ int i;
+ rpc_authflavor_t pseudoflavor = RPC_AUTH_UNIX;
+
+ for (i = 0; i < flavors->num_flavors; i++) {
+ struct nfs4_secinfo_flavor *flavor;
+ flavor = &flavors->flavors[i];
+
+ if (flavor->flavor == RPC_AUTH_NULL || flavor->flavor == RPC_AUTH_UNIX) {
+ pseudoflavor = flavor->flavor;
+ break;
+ } else if (flavor->flavor == RPC_AUTH_GSS) {
+ oid.len = flavor->gss.sec_oid4.len;
+ oid.data = flavor->gss.sec_oid4.data;
+ mech = gss_mech_get_by_OID(&oid);
+ if (!mech)
+ continue;
+ pseudoflavor = gss_svc_to_pseudoflavor(mech, flavor->gss.service);
+ gss_mech_put(mech);
+ break;
+ }
+ }
+
+ return pseudoflavor;
+}
+
+static rpc_authflavor_t nfs4_negotiate_security(struct inode *inode, struct qstr *name)
+{
+ struct page *page;
+ struct nfs4_secinfo_flavors *flavors;
+ rpc_authflavor_t flavor;
+ int err;
+
+ page = alloc_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+ flavors = page_address(page);
+
+ err = nfs4_proc_secinfo(inode, name, flavors);
+ if (err < 0) {
+ flavor = err;
+ goto out;
+ }
+
+ flavor = nfs_find_best_sec(flavors);
+
+out:
+ put_page(page);
+ return flavor;
+}
+
+/*
+ * Please call rpc_shutdown_client() when you are done with this client.
+ */
+struct rpc_clnt *nfs4_create_sec_client(struct rpc_clnt *clnt, struct inode *inode,
+ struct qstr *name)
+{
+ struct rpc_clnt *clone;
+ struct rpc_auth *auth;
+ rpc_authflavor_t flavor;
+
+ flavor = nfs4_negotiate_security(inode, name);
+ if (flavor < 0)
+ return ERR_PTR(flavor);
+
+ clone = rpc_clone_client(clnt);
+ auth = rpcauth_create(flavor, clone);
+ if (!auth) {
+ rpc_shutdown_client(clone);
+ clone = ERR_PTR(-EIO);
+ }
+
+ return clone;
+}
+
static struct vfsmount *try_location(struct nfs_clone_mount *mountdata,
char *page, char *page2,
const struct nfs4_fs_location *location)
@@ -224,7 +302,7 @@ out:
* @dentry - dentry of referral
*
*/
-static struct vfsmount *nfs_do_refmount(struct dentry *dentry)
+static struct vfsmount *nfs_do_refmount(struct dentry *dentry, struct rpc_clnt *client)
{
struct vfsmount *mnt = ERR_PTR(-ENOMEM);
struct dentry *parent;
@@ -250,7 +328,7 @@ static struct vfsmount *nfs_do_refmount(struct dentry *dentry)
dprintk("%s: getting locations for %s/%s\n",
__func__, parent->d_name.name, dentry->d_name.name);

- err = nfs4_proc_fs_locations(NFS_CLIENT(parent->d_inode), parent->d_inode,
+ err = nfs4_proc_fs_locations(client, parent->d_inode,
&dentry->d_name, fs_locations, page);
dput(parent);
if (err != 0 ||
@@ -270,29 +348,23 @@ out:
struct vfsmount *nfs4_submount(struct nfs_server *server, struct dentry *dentry,
struct nfs_fh *fh, struct nfs_fattr *fattr)
{
- int err;
struct dentry *parent = dget_parent(dentry);
struct rpc_clnt *client;
struct vfsmount *ret;

/* Look it up again to get its attributes */
- err = nfs4_proc_lookup(server->client, parent->d_inode, &dentry->d_name, fh, fattr);
- if (err == -EPERM)
- err = nfs_lookup_with_sec(server, parent, dentry, fh, fattr);
+ client = nfs4_proc_lookup_mountpoint(parent->d_inode, &dentry->d_name, fh, fattr);
dput(parent);

- if (err != 0)
- return ERR_PTR(err);
-
- if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)
- return nfs_do_refmount(dentry);
-
- client = rpc_clone_client(server->client);
if (IS_ERR(client))
return ERR_CAST(client);

- ret = nfs_do_submount(dentry, fh, fattr, client);
- if (IS_ERR(ret))
+ if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)
+ ret = nfs_do_refmount(dentry, client);
+ else
+ ret = nfs_do_submount(dentry, fh, fattr, client);
+
+ if (IS_ERR(ret) || (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL))
rpc_shutdown_client(client);

return ret;
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 9e21e25..9f83a22 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -2520,39 +2520,81 @@ static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir,
return status;
}

-void nfs_fixup_secinfo_attributes(struct nfs_fattr *fattr, struct nfs_fh *fh)
+static void nfs_fixup_secinfo_attributes(struct nfs_fattr *fattr)
{
- memset(fh, 0, sizeof(struct nfs_fh));
- fattr->fsid.major = 1;
fattr->valid |= NFS_ATTR_FATTR_TYPE | NFS_ATTR_FATTR_MODE |
- NFS_ATTR_FATTR_NLINK | NFS_ATTR_FATTR_FSID | NFS_ATTR_FATTR_MOUNTPOINT;
+ NFS_ATTR_FATTR_NLINK | NFS_ATTR_FATTR_MOUNTPOINT;
fattr->mode = S_IFDIR | S_IRUGO | S_IXUGO;
fattr->nlink = 2;
}

-int nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qstr *name,
- struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+static int nfs4_proc_lookup_common(struct rpc_clnt **clnt, struct inode *dir,
+ struct qstr *name, struct nfs_fh *fhandle,
+ struct nfs_fattr *fattr)
{
struct nfs4_exception exception = { };
+ struct rpc_clnt *client = *clnt;
int err;
- do {
- int status;

- status = _nfs4_proc_lookup(clnt, dir, name, fhandle, fattr);
- switch (status) {
+ do {
+ err = _nfs4_proc_lookup(client, dir, name, fhandle, fattr);
+ switch (err) {
case -NFS4ERR_BADNAME:
- return -ENOENT;
+ err = -ENOENT;
+ goto out;
case -NFS4ERR_MOVED:
- return nfs4_get_referral(clnt, dir, name, fattr, fhandle);
+ err = nfs4_get_referral(client, dir, name, fattr, fhandle);
+ goto out;
case -NFS4ERR_WRONGSEC:
- nfs_fixup_secinfo_attributes(fattr, fhandle);
+ err = -EPERM;
+ if (client != *clnt)
+ goto out;
+ client = nfs4_create_sec_client(client, dir, name);
+ exception.retry = 1;
+ break;
+ default:
+ err = nfs4_handle_exception(NFS_SERVER(dir), err, &exception);
}
- err = nfs4_handle_exception(NFS_SERVER(dir),
- status, &exception);
} while (exception.retry);
+
+out:
+ if (err == 0)
+ *clnt = client;
+ else if (client != *clnt)
+ rpc_shutdown_client(client);
+
return err;
}

+int nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qstr *name,
+ struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+{
+ int status;
+ struct rpc_clnt *client = NFS_CLIENT(dir);
+
+ status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr);
+ if (client != NFS_CLIENT(dir)) {
+ rpc_shutdown_client(client);
+ nfs_fixup_secinfo_attributes(fattr);
+ }
+ return status;
+}
+
+struct rpc_clnt *
+nfs4_proc_lookup_mountpoint(struct inode *dir, struct qstr *name,
+ struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+{
+ int status;
+ struct rpc_clnt *client = rpc_clone_client(NFS_CLIENT(dir));
+
+ status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr);
+ if (status < 0) {
+ rpc_shutdown_client(client);
+ return ERR_PTR(status);
+ }
+ return client;
+}
+
static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry)
{
struct nfs_server *server = NFS_SERVER(inode);
@@ -4954,8 +4996,8 @@ static int _nfs4_proc_secinfo(struct inode *dir, const struct qstr *name, struct
return status;
}

-static int nfs4_proc_secinfo(struct inode *dir, const struct qstr *name,
- struct nfs4_secinfo_flavors *flavors)
+int nfs4_proc_secinfo(struct inode *dir, const struct qstr *name,
+ struct nfs4_secinfo_flavors *flavors)
{
struct nfs4_exception exception = { };
int err;
@@ -6506,7 +6548,6 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
.open_context = nfs4_atomic_open,
.submount = nfs4_submount,
.init_client = nfs4_init_client,
- .secinfo = nfs4_proc_secinfo,
};

static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = {
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index c74fdb1..eca81b1 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -4257,8 +4257,6 @@ static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
status = decode_attr_error(xdr, bitmap, &err);
if (status < 0)
goto xdr_error;
- if (err == -NFS4ERR_WRONGSEC)
- nfs_fixup_secinfo_attributes(fattr, fh);

status = decode_attr_filehandle(xdr, bitmap, fh);
if (status < 0)
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 3c94c80..24c11b2 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -1286,7 +1286,6 @@ struct nfs_rpc_ops {
struct nfs_fh *, struct nfs_fattr *);
int (*init_client) (struct nfs_client *, const struct rpc_timeout *,
const char *, rpc_authflavor_t, int);
- int (*secinfo)(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *);
};

/*
--
1.7.10


2012-04-18 17:46:34

by Anna Schumaker

[permalink] [raw]
Subject: [PATCH 2/6] NFS: Pass alternate rpc_clnt to fs_locations()

From: Bryan Schumaker <[email protected]>

nfs4_proc_lookup() will soon create a new rpc_clnt with the results of a
SECINFO call. This patch prepares for a referral hidden behind a
mountpoint with a different secflavor.

Signed-off-by: Bryan Schumaker <[email protected]>
---
fs/nfs/nfs4_fs.h | 2 +-
fs/nfs/nfs4namespace.c | 3 ++-
fs/nfs/nfs4proc.c | 17 +++++++++--------
3 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index 97ecc86..41a89d0 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -212,7 +212,7 @@ extern int nfs4_init_clientid(struct nfs_client *, struct rpc_cred *);
extern int nfs41_init_clientid(struct nfs_client *, struct rpc_cred *);
extern int nfs4_do_close(struct nfs4_state *state, gfp_t gfp_mask, int wait, bool roc);
extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle);
-extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
+extern int nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir, const struct qstr *name,
struct nfs4_fs_locations *fs_locations, struct page *page);
extern int nfs4_release_lockowner(struct nfs4_lock_state *);
extern const struct xattr_handler *nfs4_xattr_handlers[];
diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c
index 28a4771..be5268d 100644
--- a/fs/nfs/nfs4namespace.c
+++ b/fs/nfs/nfs4namespace.c
@@ -250,7 +250,8 @@ struct vfsmount *nfs_do_refmount(struct dentry *dentry)
dprintk("%s: getting locations for %s/%s\n",
__func__, parent->d_name.name, dentry->d_name.name);

- err = nfs4_proc_fs_locations(parent->d_inode, &dentry->d_name, fs_locations, page);
+ err = nfs4_proc_fs_locations(NFS_CLIENT(parent->d_inode), parent->d_inode,
+ &dentry->d_name, fs_locations, page);
dput(parent);
if (err != 0 ||
fs_locations->nlocations <= 0 ||
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index f82bde0..826c3d5 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -2368,8 +2368,9 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
* Note that we'll actually follow the referral later when
* we detect fsid mismatch in inode revalidation
*/
-static int nfs4_get_referral(struct inode *dir, const struct qstr *name,
- struct nfs_fattr *fattr, struct nfs_fh *fhandle)
+static int nfs4_get_referral(struct rpc_clnt *client, struct inode *dir,
+ const struct qstr *name, struct nfs_fattr *fattr,
+ struct nfs_fh *fhandle)
{
int status = -ENOMEM;
struct page *page = NULL;
@@ -2382,7 +2383,7 @@ static int nfs4_get_referral(struct inode *dir, const struct qstr *name,
if (locations == NULL)
goto out;

- status = nfs4_proc_fs_locations(dir, name, locations, page);
+ status = nfs4_proc_fs_locations(client, dir, name, locations, page);
if (status != 0)
goto out;
/* Make sure server returned a different fsid for the referral */
@@ -2542,7 +2543,7 @@ static int nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qst
case -NFS4ERR_BADNAME:
return -ENOENT;
case -NFS4ERR_MOVED:
- return nfs4_get_referral(dir, name, fattr, fhandle);
+ return nfs4_get_referral(clnt, dir, name, fattr, fhandle);
case -NFS4ERR_WRONGSEC:
nfs_fixup_secinfo_attributes(fattr, fhandle);
}
@@ -4891,8 +4892,8 @@ static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr)
fattr->nlink = 2;
}

-int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
- struct nfs4_fs_locations *fs_locations, struct page *page)
+int nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir, const struct qstr *name,
+ struct nfs4_fs_locations *fs_locations, struct page *page)
{
struct nfs_server *server = NFS_SERVER(dir);
u32 bitmask[2] = {
@@ -4918,7 +4919,7 @@ int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,

/* Ask for the fileid of the absent filesystem if mounted_on_fileid
* is not supported */
- if (NFS_SERVER(dir)->attr_bitmask[1] & FATTR4_WORD1_MOUNTED_ON_FILEID)
+ if (server->attr_bitmask[1] & FATTR4_WORD1_MOUNTED_ON_FILEID)
bitmask[1] |= FATTR4_WORD1_MOUNTED_ON_FILEID;
else
bitmask[0] |= FATTR4_WORD0_FILEID;
@@ -4926,7 +4927,7 @@ int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
nfs_fattr_init(&fs_locations->fattr);
fs_locations->server = server;
fs_locations->nlocations = 0;
- status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
+ status = nfs4_call_sync(client, server, &msg, &args.seq_args, &res.seq_res, 0);
dprintk("%s: returned status = %d\n", __func__, status);
return status;
}
--
1.7.10


2012-04-18 17:46:35

by Anna Schumaker

[permalink] [raw]
Subject: [PATCH 3/6] NFS: Create a submount rpc_op

From: Bryan Schumaker <[email protected]>

This simplifies the code for v2 and v3 and gives v4 a chance to decide
on referrals without needing to modify the generic client.

Signed-off-by: Bryan Schumaker <[email protected]>
---
fs/nfs/internal.h | 19 +++++-----
fs/nfs/namespace.c | 89 ++++++++++++++++++++---------------------------
fs/nfs/nfs3proc.c | 1 +
fs/nfs/nfs4_fs.h | 6 ++++
fs/nfs/nfs4namespace.c | 33 +++++++++++++++++-
fs/nfs/nfs4proc.c | 5 +--
fs/nfs/proc.c | 1 +
include/linux/nfs_xdr.h | 2 ++
8 files changed, 90 insertions(+), 66 deletions(-)

diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index b499cd0..a7995b3 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -185,17 +185,6 @@ static inline void nfs_fs_proc_exit(void)
}
#endif

-/* nfs4namespace.c */
-#ifdef CONFIG_NFS_V4
-extern struct vfsmount *nfs_do_refmount(struct dentry *dentry);
-#else
-static inline
-struct vfsmount *nfs_do_refmount(struct dentry *dentry)
-{
- return ERR_PTR(-ENOENT);
-}
-#endif
-
/* callback_xdr.c */
extern struct svc_version nfs4_callback_version1;
extern struct svc_version nfs4_callback_version4;
@@ -281,8 +270,16 @@ extern void nfs_sb_deactive(struct super_block *sb);
extern char *nfs_path(char **p, struct dentry *dentry,
char *buffer, ssize_t buflen);
extern struct vfsmount *nfs_d_automount(struct path *path);
+struct vfsmount *nfs_do_submount(struct dentry *, struct nfs_fh *,
+ struct nfs_fattr *, struct rpc_clnt *);
+struct vfsmount *nfs_submount(struct nfs_server *, struct dentry *,
+ struct nfs_fh *, struct nfs_fattr *);
+
#ifdef CONFIG_NFS_V4
rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *);
+int nfs_lookup_with_sec(struct nfs_server *, struct dentry *, struct dentry *,
+ struct nfs_fh *, struct nfs_fattr *);
+
#endif

/* getroot.c */
diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c
index 3e40460..5f401fa 100644
--- a/fs/nfs/namespace.c
+++ b/fs/nfs/namespace.c
@@ -26,11 +26,6 @@ static LIST_HEAD(nfs_automount_list);
static DECLARE_DELAYED_WORK(nfs_automount_task, nfs_expire_automounts);
int nfs_mountpoint_expiry_timeout = 500 * HZ;

-static struct vfsmount *nfs_do_submount(struct dentry *dentry,
- struct nfs_fh *fh,
- struct nfs_fattr *fattr,
- rpc_authflavor_t authflavor);
-
/*
* nfs_path - reconstruct the path given an arbitrary dentry
* @base - used to return pointer to the end of devname part of path
@@ -174,41 +169,32 @@ out:
return ret;
}

-static int nfs_lookup_with_sec(struct nfs_server *server, struct dentry *parent,
- struct dentry *dentry, struct path *path,
- struct nfs_fh *fh, struct nfs_fattr *fattr,
- rpc_authflavor_t *flavor)
+int nfs_lookup_with_sec(struct nfs_server *server, struct dentry *parent,
+ struct dentry *dentry, struct nfs_fh *fh,
+ struct nfs_fattr *fattr)
{
struct rpc_clnt *clone;
struct rpc_auth *auth;
+ rpc_authflavor_t flavor;
int err;

- err = nfs_negotiate_security(parent, path->dentry, flavor);
+ err = nfs_negotiate_security(parent, dentry, &flavor);
if (err < 0)
goto out;
clone = rpc_clone_client(server->client);
- auth = rpcauth_create(*flavor, clone);
+ auth = rpcauth_create(flavor, clone);
if (!auth) {
err = -EIO;
goto out_shutdown;
}
err = server->nfs_client->rpc_ops->lookup(clone, parent->d_inode,
- &path->dentry->d_name,
+ &dentry->d_name,
fh, fattr);
out_shutdown:
rpc_shutdown_client(clone);
out:
return err;
}
-#else /* CONFIG_NFS_V4 */
-static inline int nfs_lookup_with_sec(struct nfs_server *server,
- struct dentry *parent, struct dentry *dentry,
- struct path *path, struct nfs_fh *fh,
- struct nfs_fattr *fattr,
- rpc_authflavor_t *flavor)
-{
- return -EPERM;
-}
#endif /* CONFIG_NFS_V4 */

/*
@@ -227,11 +213,8 @@ struct vfsmount *nfs_d_automount(struct path *path)
{
struct vfsmount *mnt;
struct nfs_server *server = NFS_SERVER(path->dentry->d_inode);
- struct dentry *parent;
struct nfs_fh *fh = NULL;
struct nfs_fattr *fattr = NULL;
- int err;
- rpc_authflavor_t flavor = RPC_AUTH_UNIX;

dprintk("--> nfs_d_automount()\n");

@@ -246,24 +229,7 @@ struct vfsmount *nfs_d_automount(struct path *path)
goto out;

dprintk("%s: enter\n", __func__);
-
- /* Look it up again to get its attributes */
- parent = dget_parent(path->dentry);
- err = server->nfs_client->rpc_ops->lookup(server->client, parent->d_inode,
- &path->dentry->d_name,
- fh, fattr);
- if (err == -EPERM && NFS_PROTO(parent->d_inode)->secinfo != NULL)
- err = nfs_lookup_with_sec(server, parent, path->dentry, path, fh, fattr, &flavor);
- dput(parent);
- if (err != 0) {
- mnt = ERR_PTR(err);
- goto out;
- }
-
- if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)
- mnt = nfs_do_refmount(path->dentry);
- else
- mnt = nfs_do_submount(path->dentry, fh, fattr, flavor);
+ mnt = server->nfs_client->rpc_ops->submount(server, path->dentry, fh, fattr);
if (IS_ERR(mnt))
goto out;

@@ -336,14 +302,13 @@ static struct vfsmount *nfs_do_clone_mount(struct nfs_server *server,
* @authflavor - security flavor to use when performing the mount
*
*/
-static struct vfsmount *nfs_do_submount(struct dentry *dentry,
- struct nfs_fh *fh,
- struct nfs_fattr *fattr,
- rpc_authflavor_t authflavor)
+struct vfsmount *nfs_do_submount(struct dentry *dentry, struct nfs_fh *fh,
+ struct nfs_fattr *fattr, struct rpc_clnt *client)
{
struct nfs_clone_mount mountdata = {
.sb = dentry->d_sb,
.dentry = dentry,
+ .client = client,
.fh = fh,
.fattr = fattr,
};
@@ -363,13 +328,7 @@ static struct vfsmount *nfs_do_submount(struct dentry *dentry,
if (IS_ERR(devname))
goto free_page;

- mountdata.client = rpc_clone_client(NFS_SB(dentry->d_sb)->client);
- if (IS_ERR(mountdata.client))
- goto free_page;
-
mnt = nfs_do_clone_mount(NFS_SB(dentry->d_sb), devname, &mountdata);
- if (IS_ERR(mnt))
- rpc_shutdown_client(mountdata.client);
free_page:
free_page((unsigned long)page);
out:
@@ -378,3 +337,29 @@ out:
dprintk("<-- nfs_do_submount() = %p\n", mnt);
return mnt;
}
+
+struct vfsmount *nfs_submount(struct nfs_server *server, struct dentry *dentry,
+ struct nfs_fh *fh, struct nfs_fattr *fattr)
+{
+ int err;
+ struct dentry *parent = dget_parent(dentry);
+ struct rpc_clnt *client;
+ struct vfsmount *mnt;
+
+ err = server->nfs_client->rpc_ops->lookup(server->client, parent->d_inode,
+ &dentry->d_name, fh, fattr);
+ dput(parent);
+
+ if (err != 0)
+ return ERR_PTR(err);
+
+ client = rpc_clone_client(server->client);
+ if (IS_ERR(client))
+ return ERR_CAST(client);
+
+ mnt = nfs_do_submount(dentry, fh, fattr, client);
+ if (IS_ERR(mnt))
+ rpc_shutdown_client(client);
+
+ return mnt;
+}
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
index 5242eae..0ac098a 100644
--- a/fs/nfs/nfs3proc.c
+++ b/fs/nfs/nfs3proc.c
@@ -911,5 +911,6 @@ const struct nfs_rpc_ops nfs_v3_clientops = {
.lock = nfs3_proc_lock,
.clear_acl_cache = nfs3_forget_cached_acls,
.close_context = nfs_close_context,
+ .submount = nfs_submount,
.init_client = nfs_init_client,
};
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index 41a89d0..e761268 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -204,6 +204,10 @@ struct nfs4_state_maintenance_ops {
extern const struct dentry_operations nfs4_dentry_operations;
extern const struct inode_operations nfs4_dir_inode_operations;

+/* nfs4namespace.c */
+struct vfsmount *nfs4_submount(struct nfs_server *, struct dentry *,
+ struct nfs_fh *, struct nfs_fattr *);
+
/* nfs4proc.c */
extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, struct rpc_cred *, struct nfs4_setclientid_res *);
extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct nfs4_setclientid_res *arg, struct rpc_cred *);
@@ -214,6 +218,8 @@ extern int nfs4_do_close(struct nfs4_state *state, gfp_t gfp_mask, int wait, boo
extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle);
extern int nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir, const struct qstr *name,
struct nfs4_fs_locations *fs_locations, struct page *page);
+extern int nfs4_proc_lookup(struct rpc_clnt *, struct inode *, struct qstr *,
+ struct nfs_fh *, struct nfs_fattr *);
extern int nfs4_release_lockowner(struct nfs4_lock_state *);
extern const struct xattr_handler *nfs4_xattr_handlers[];

diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c
index be5268d..a12b747 100644
--- a/fs/nfs/nfs4namespace.c
+++ b/fs/nfs/nfs4namespace.c
@@ -224,7 +224,7 @@ out:
* @dentry - dentry of referral
*
*/
-struct vfsmount *nfs_do_refmount(struct dentry *dentry)
+static struct vfsmount *nfs_do_refmount(struct dentry *dentry)
{
struct vfsmount *mnt = ERR_PTR(-ENOMEM);
struct dentry *parent;
@@ -266,3 +266,34 @@ out:
dprintk("%s: done\n", __func__);
return mnt;
}
+
+struct vfsmount *nfs4_submount(struct nfs_server *server, struct dentry *dentry,
+ struct nfs_fh *fh, struct nfs_fattr *fattr)
+{
+ int err;
+ struct dentry *parent = dget_parent(dentry);
+ struct rpc_clnt *client;
+ struct vfsmount *ret;
+
+ /* Look it up again to get its attributes */
+ err = nfs4_proc_lookup(server->client, parent->d_inode, &dentry->d_name, fh, fattr);
+ if (err == -EPERM)
+ err = nfs_lookup_with_sec(server, parent, dentry, fh, fattr);
+ dput(parent);
+
+ if (err != 0)
+ return ERR_PTR(err);
+
+ if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)
+ return nfs_do_refmount(dentry);
+
+ client = rpc_clone_client(server->client);
+ if (IS_ERR(client))
+ return ERR_CAST(client);
+
+ ret = nfs_do_submount(dentry, fh, fattr, client);
+ if (IS_ERR(ret))
+ rpc_shutdown_client(client);
+
+ return ret;
+}
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 826c3d5..9e21e25 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -2530,8 +2530,8 @@ void nfs_fixup_secinfo_attributes(struct nfs_fattr *fattr, struct nfs_fh *fh)
fattr->nlink = 2;
}

-static int nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qstr *name,
- struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+int nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qstr *name,
+ struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
struct nfs4_exception exception = { };
int err;
@@ -6504,6 +6504,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
.clear_acl_cache = nfs4_zap_acl_attr,
.close_context = nfs4_close_context,
.open_context = nfs4_atomic_open,
+ .submount = nfs4_submount,
.init_client = nfs4_init_client,
.secinfo = nfs4_proc_secinfo,
};
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index b63b6f4..da18f86 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -767,5 +767,6 @@ const struct nfs_rpc_ops nfs_v2_clientops = {
.lock = nfs_proc_lock,
.lock_check_bounds = nfs_lock_check_bounds,
.close_context = nfs_close_context,
+ .submount = nfs_submount,
.init_client = nfs_init_client,
};
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index bfd0d1b..3c94c80 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -1282,6 +1282,8 @@ struct nfs_rpc_ops {
struct nfs_open_context *ctx,
int open_flags,
struct iattr *iattr);
+ struct vfsmount *(*submount)(struct nfs_server *, struct dentry *,
+ struct nfs_fh *, struct nfs_fattr *);
int (*init_client) (struct nfs_client *, const struct rpc_timeout *,
const char *, rpc_authflavor_t, int);
int (*secinfo)(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *);
--
1.7.10


2012-04-18 17:46:32

by Anna Schumaker

[permalink] [raw]
Subject: [PATCH 1/6] NFS: pre-clone the rpc_clnt

From: Bryan Schumaker <[email protected]>

A later patch will need an rpc_clnt with the correct authflavor to find
properties of a submount. It's easiest to set up the rpc_clnt once and
pass it to nfs_clone_server() instead of cloning the clnt multiple times.

Signed-off-by: Bryan Schumaker <[email protected]>
---
fs/nfs/client.c | 50 ++++++++++++++++++++++++++++--------------------
fs/nfs/internal.h | 3 ++-
fs/nfs/namespace.c | 8 +++++++-
fs/nfs/nfs4namespace.c | 2 +-
fs/nfs/super.c | 4 ++--
5 files changed, 41 insertions(+), 26 deletions(-)

diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index da7b5e4..1faa091 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -772,36 +772,45 @@ static inline void nfs_init_server_aclclient(struct nfs_server *server)
/*
* Create a general RPC client
*/
-static int nfs_init_server_rpcclient(struct nfs_server *server,
+static void nfs_init_server_rpcclient(struct nfs_server *server,
+ struct rpc_clnt *client,
+ const struct rpc_timeout *timeo)
+{
+ memcpy(&client->cl_timeout_default, timeo, sizeof(client->cl_timeout_default));
+
+ client->cl_timeout = &client->cl_timeout_default;
+ client->cl_softrtry = 0;
+ if (server->flags & NFS_MOUNT_SOFT)
+ client->cl_softrtry = 1;
+
+ server->client = client;
+}
+
+static int nfs_alloc_server_rpcclient(struct nfs_server *server,
const struct rpc_timeout *timeo,
rpc_authflavor_t pseudoflavour)
{
+ struct rpc_clnt *client;
struct nfs_client *clp = server->nfs_client;

- server->client = rpc_clone_client(clp->cl_rpcclient);
- if (IS_ERR(server->client)) {
+ client = rpc_clone_client(clp->cl_rpcclient);
+ if (IS_ERR(client)) {
dprintk("%s: couldn't create rpc_client!\n", __func__);
return PTR_ERR(server->client);
}

- memcpy(&server->client->cl_timeout_default,
- timeo,
- sizeof(server->client->cl_timeout_default));
- server->client->cl_timeout = &server->client->cl_timeout_default;
-
if (pseudoflavour != clp->cl_rpcclient->cl_auth->au_flavor) {
struct rpc_auth *auth;

- auth = rpcauth_create(pseudoflavour, server->client);
+ auth = rpcauth_create(pseudoflavour, client);
if (IS_ERR(auth)) {
dprintk("%s: couldn't create credcache!\n", __func__);
+ rpc_shutdown_client(client);
return PTR_ERR(auth);
}
}
- server->client->cl_softrtry = 0;
- if (server->flags & NFS_MOUNT_SOFT)
- server->client->cl_softrtry = 1;

+ nfs_init_server_rpcclient(server, client, timeo);
return 0;
}

@@ -899,7 +908,7 @@ static int nfs_init_server(struct nfs_server *server,

server->port = data->nfs_server.port;

- error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]);
+ error = nfs_alloc_server_rpcclient(server, &timeparms, data->auth_flavors[0]);
if (error < 0)
goto error;

@@ -1626,7 +1635,7 @@ static int nfs4_init_server(struct nfs_server *server,

server->port = data->nfs_server.port;

- error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]);
+ error = nfs_alloc_server_rpcclient(server, &timeparms, data->auth_flavors[0]);

error:
/* Done */
@@ -1697,7 +1706,7 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
data->addr,
data->addrlen,
parent_client->cl_ipaddr,
- data->authflavor,
+ data->client->cl_auth->au_flavor,
rpc_protocol(parent_server->client),
parent_server->client->cl_timeout,
parent_client->cl_mvops->minor_version,
@@ -1705,7 +1714,8 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
if (error < 0)
goto error;

- error = nfs_init_server_rpcclient(server, parent_server->client->cl_timeout, data->authflavor);
+ error = nfs_alloc_server_rpcclient(server, parent_server->client->cl_timeout,
+ data->client->cl_auth->au_flavor);
if (error < 0)
goto error;

@@ -1728,6 +1738,7 @@ error:
* Clone an NFS2, NFS3 or NFS4 server record
*/
struct nfs_server *nfs_clone_server(struct nfs_server *source,
+ struct rpc_clnt *client,
struct nfs_fh *fh,
struct nfs_fattr *fattr)
{
@@ -1756,11 +1767,7 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source,

server->fsid = fattr->fsid;

- error = nfs_init_server_rpcclient(server,
- source->client->cl_timeout,
- source->client->cl_auth->au_flavor);
- if (error < 0)
- goto out_free_server;
+ nfs_init_server_rpcclient(server, client, source->client->cl_timeout);
if (!IS_ERR(source->client_acl))
nfs_init_server_aclclient(server);

@@ -1788,6 +1795,7 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source,
return server;

out_free_server:
+ server->client = NULL;
nfs_free_fattr(fattr_fsinfo);
nfs_free_server(server);
dprintk("<-- nfs_clone_server() = error %d\n", error);
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 2476dc6..b499cd0 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -59,13 +59,13 @@ static inline int nfs_attr_use_mounted_on_fileid(struct nfs_fattr *fattr)
struct nfs_clone_mount {
const struct super_block *sb;
const struct dentry *dentry;
+ struct rpc_clnt *client;
struct nfs_fh *fh;
struct nfs_fattr *fattr;
char *hostname;
char *mnt_path;
struct sockaddr *addr;
size_t addrlen;
- rpc_authflavor_t authflavor;
};

/*
@@ -164,6 +164,7 @@ extern struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *,
struct nfs_fh *);
extern void nfs_free_server(struct nfs_server *server);
extern struct nfs_server *nfs_clone_server(struct nfs_server *,
+ struct rpc_clnt *,
struct nfs_fh *,
struct nfs_fattr *);
extern void nfs_mark_client_ready(struct nfs_client *clp, int state);
diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c
index 1807866..3e40460 100644
--- a/fs/nfs/namespace.c
+++ b/fs/nfs/namespace.c
@@ -346,7 +346,6 @@ static struct vfsmount *nfs_do_submount(struct dentry *dentry,
.dentry = dentry,
.fh = fh,
.fattr = fattr,
- .authflavor = authflavor,
};
struct vfsmount *mnt = ERR_PTR(-ENOMEM);
char *page = (char *) __get_free_page(GFP_USER);
@@ -363,7 +362,14 @@ static struct vfsmount *nfs_do_submount(struct dentry *dentry,
mnt = (struct vfsmount *)devname;
if (IS_ERR(devname))
goto free_page;
+
+ mountdata.client = rpc_clone_client(NFS_SB(dentry->d_sb)->client);
+ if (IS_ERR(mountdata.client))
+ goto free_page;
+
mnt = nfs_do_clone_mount(NFS_SB(dentry->d_sb), devname, &mountdata);
+ if (IS_ERR(mnt))
+ rpc_shutdown_client(mountdata.client);
free_page:
free_page((unsigned long)page);
out:
diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c
index 9c8eca3..28a4771 100644
--- a/fs/nfs/nfs4namespace.c
+++ b/fs/nfs/nfs4namespace.c
@@ -174,7 +174,7 @@ static struct vfsmount *nfs_follow_referral(struct dentry *dentry,
struct nfs_clone_mount mountdata = {
.sb = dentry->d_sb,
.dentry = dentry,
- .authflavor = NFS_SB(dentry->d_sb)->client->cl_auth->au_flavor,
+ .client = NFS_SB(dentry->d_sb)->client,
};
char *page = NULL, *page2 = NULL;
int loc, error;
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 37412f7..ce9c0c1 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -2428,7 +2428,7 @@ nfs_xdev_mount(struct file_system_type *fs_type, int flags,
dprintk("--> nfs_xdev_mount()\n");

/* create a new volume representation */
- server = nfs_clone_server(NFS_SB(data->sb), data->fh, data->fattr);
+ server = nfs_clone_server(NFS_SB(data->sb), data->client, data->fh, data->fattr);
if (IS_ERR(server)) {
error = PTR_ERR(server);
goto out_err_noserver;
@@ -2951,7 +2951,7 @@ nfs4_xdev_mount(struct file_system_type *fs_type, int flags,
dprintk("--> nfs4_xdev_mount()\n");

/* create a new volume representation */
- server = nfs_clone_server(NFS_SB(data->sb), data->fh, data->fattr);
+ server = nfs_clone_server(NFS_SB(data->sb), data->client, data->fh, data->fattr);
if (IS_ERR(server)) {
error = PTR_ERR(server);
goto out_err_noserver;
--
1.7.10