NFS: Add SECINFO_NO_NAME procedure
authorBryan Schumaker <bjschuma@netapp.com>
Thu, 2 Jun 2011 18:59:07 +0000 (14:59 -0400)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Tue, 12 Jul 2011 17:40:27 +0000 (13:40 -0400)
If the client is using NFS v4.1, then we can use SECINFO_NO_NAME to find
the secflavor for the initial mount.  If the server doesn't support
SECINFO_NO_NAME then I fall back on the "guess and check" method used
for v4.0 mounts.

Signed-off-by: Bryan Schumaker <bjschuma@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
fs/nfs/internal.h
fs/nfs/namespace.c
fs/nfs/nfs4_fs.h
fs/nfs/nfs4proc.c
fs/nfs/nfs4xdr.c
include/linux/nfs4.h
include/linux/nfs_xdr.h

index 2a55347..fc017ea 100644 (file)
@@ -277,6 +277,9 @@ 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);
+#ifdef CONFIG_NFS_V4
+rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *);
+#endif
 
 /* getroot.c */
 extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *,
index 1f063ba..8102391 100644 (file)
@@ -119,7 +119,7 @@ Elong:
 }
 
 #ifdef CONFIG_NFS_V4
-static rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors)
+rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors)
 {
        struct gss_api_mech *mech;
        struct xdr_netobj oid;
index b47f0d4..c30aed2 100644 (file)
@@ -67,6 +67,8 @@ struct nfs4_minor_version_ops {
                        int cache_reply);
        int     (*validate_stateid)(struct nfs_delegation *,
                        const nfs4_stateid *);
+       int     (*find_root_sec)(struct nfs_server *, struct nfs_fh *,
+                       struct nfs_fsinfo *);
        const struct nfs4_state_recovery_ops *reboot_recovery_ops;
        const struct nfs4_state_recovery_ops *nograce_recovery_ops;
        const struct nfs4_state_maintenance_ops *state_renewal_ops;
index 5f4912f..892bff5 100644 (file)
@@ -2251,13 +2251,14 @@ static int nfs4_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
 static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
                              struct nfs_fsinfo *info)
 {
+       int minor_version = server->nfs_client->cl_minorversion;
        int status = nfs4_lookup_root(server, fhandle, info);
        if ((status == -NFS4ERR_WRONGSEC) && !(server->flags & NFS_MOUNT_SECFLAVOUR))
                /*
                 * A status of -NFS4ERR_WRONGSEC will be mapped to -EPERM
                 * by nfs4_map_errors() as this function exits.
                 */
-               status = nfs4_find_root_sec(server, fhandle, info);
+               status = nfs_v4_minor_ops[minor_version]->find_root_sec(server, fhandle, info);
        if (status == 0)
                status = nfs4_server_capabilities(server, fhandle);
        if (status == 0)
@@ -5935,6 +5936,85 @@ out:
        rpc_put_task(task);
        return status;
 }
+
+static int
+_nfs41_proc_secinfo_no_name(struct nfs_server *server, struct nfs_fh *fhandle,
+                   struct nfs_fsinfo *info, struct nfs4_secinfo_flavors *flavors)
+{
+       struct nfs41_secinfo_no_name_args args = {
+               .style = SECINFO_STYLE_CURRENT_FH,
+       };
+       struct nfs4_secinfo_res res = {
+               .flavors = flavors,
+       };
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SECINFO_NO_NAME],
+               .rpc_argp = &args,
+               .rpc_resp = &res,
+       };
+       return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
+}
+
+static int
+nfs41_proc_secinfo_no_name(struct nfs_server *server, struct nfs_fh *fhandle,
+                          struct nfs_fsinfo *info, struct nfs4_secinfo_flavors *flavors)
+{
+       struct nfs4_exception exception = { };
+       int err;
+       do {
+               err = _nfs41_proc_secinfo_no_name(server, fhandle, info, flavors);
+               switch (err) {
+               case 0:
+               case -NFS4ERR_WRONGSEC:
+               case -NFS4ERR_NOTSUPP:
+                       break;
+               default:
+                       err = nfs4_handle_exception(server, err, &exception);
+               }
+       } while (exception.retry);
+       return err;
+}
+
+static int
+nfs41_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
+                   struct nfs_fsinfo *info)
+{
+       int err;
+       struct page *page;
+       rpc_authflavor_t flavor;
+       struct nfs4_secinfo_flavors *flavors;
+
+       page = alloc_page(GFP_KERNEL);
+       if (!page) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       flavors = page_address(page);
+       err = nfs41_proc_secinfo_no_name(server, fhandle, info, flavors);
+
+       /*
+        * Fall back on "guess and check" method if
+        * the server doesn't support SECINFO_NO_NAME
+        */
+       if (err == -NFS4ERR_WRONGSEC || err == -NFS4ERR_NOTSUPP) {
+               err = nfs4_find_root_sec(server, fhandle, info);
+               goto out_freepage;
+       }
+       if (err)
+               goto out_freepage;
+
+       flavor = nfs_find_best_sec(flavors);
+       if (err == 0)
+               err = nfs4_lookup_root_sec(server, fhandle, info, flavor);
+
+out_freepage:
+       put_page(page);
+       if (err == -EACCES)
+               return -EPERM;
+out:
+       return err;
+}
 #endif /* CONFIG_NFS_V4_1 */
 
 struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = {
@@ -5996,6 +6076,7 @@ static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = {
        .minor_version = 0,
        .call_sync = _nfs4_call_sync,
        .validate_stateid = nfs4_validate_delegation_stateid,
+       .find_root_sec = nfs4_find_root_sec,
        .reboot_recovery_ops = &nfs40_reboot_recovery_ops,
        .nograce_recovery_ops = &nfs40_nograce_recovery_ops,
        .state_renewal_ops = &nfs40_state_renewal_ops,
@@ -6006,6 +6087,7 @@ static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
        .minor_version = 1,
        .call_sync = _nfs4_call_sync_session,
        .validate_stateid = nfs41_validate_delegation_stateid,
+       .find_root_sec = nfs41_find_root_sec,
        .reboot_recovery_ops = &nfs41_reboot_recovery_ops,
        .nograce_recovery_ops = &nfs41_nograce_recovery_ops,
        .state_renewal_ops = &nfs41_state_renewal_ops,
index 1555c74..c8c069a 100644 (file)
@@ -343,6 +343,8 @@ static int nfs4_stat_to_errno(int);
                                1 /* FIXME: opaque lrf_body always empty at the moment */)
 #define decode_layoutreturn_maxsz (op_decode_hdr_maxsz + \
                                1 + decode_stateid_maxsz)
+#define encode_secinfo_no_name_maxsz (op_encode_hdr_maxsz + 1)
+#define decode_secinfo_no_name_maxsz decode_secinfo_maxsz
 #else /* CONFIG_NFS_V4_1 */
 #define encode_sequence_maxsz  0
 #define decode_sequence_maxsz  0
@@ -772,6 +774,14 @@ static int nfs4_stat_to_errno(int);
                                decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
                                decode_layoutreturn_maxsz)
+#define NFS4_enc_secinfo_no_name_sz    (compound_encode_hdr_maxsz + \
+                                       encode_sequence_maxsz + \
+                                       encode_putrootfh_maxsz +\
+                                       encode_secinfo_no_name_maxsz)
+#define NFS4_dec_secinfo_no_name_sz    (compound_decode_hdr_maxsz + \
+                                       decode_sequence_maxsz + \
+                                       decode_putrootfh_maxsz + \
+                                       decode_secinfo_no_name_maxsz)
 
 const u32 nfs41_maxwrite_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
                                      compound_encode_hdr_maxsz +
@@ -1938,6 +1948,20 @@ encode_layoutreturn(struct xdr_stream *xdr,
        hdr->nops++;
        hdr->replen += decode_layoutreturn_maxsz;
 }
+
+static int
+encode_secinfo_no_name(struct xdr_stream *xdr,
+                      const struct nfs41_secinfo_no_name_args *args,
+                      struct compound_hdr *hdr)
+{
+       __be32 *p;
+       p = reserve_space(xdr, 8);
+       *p++ = cpu_to_be32(OP_SECINFO_NO_NAME);
+       *p++ = cpu_to_be32(args->style);
+       hdr->nops++;
+       hdr->replen += decode_secinfo_no_name_maxsz;
+       return 0;
+}
 #endif /* CONFIG_NFS_V4_1 */
 
 /*
@@ -2790,6 +2814,25 @@ static void nfs4_xdr_enc_layoutreturn(struct rpc_rqst *req,
        encode_layoutreturn(xdr, args, &hdr);
        encode_nops(&hdr);
 }
+
+/*
+ * Encode SECINFO_NO_NAME request
+ */
+static int nfs4_xdr_enc_secinfo_no_name(struct rpc_rqst *req,
+                                       struct xdr_stream *xdr,
+                                       struct nfs41_secinfo_no_name_args *args)
+{
+       struct compound_hdr hdr = {
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
+       };
+
+       encode_compound_hdr(xdr, req, &hdr);
+       encode_sequence(xdr, &args->seq_args, &hdr);
+       encode_putrootfh(xdr, &hdr);
+       encode_secinfo_no_name(xdr, args, &hdr);
+       encode_nops(&hdr);
+       return 0;
+}
 #endif /* CONFIG_NFS_V4_1 */
 
 static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
@@ -6467,6 +6510,30 @@ static int nfs4_xdr_dec_layoutcommit(struct rpc_rqst *rqstp,
 out:
        return status;
 }
+
+/*
+ * Decode SECINFO_NO_NAME response
+ */
+static int nfs4_xdr_dec_secinfo_no_name(struct rpc_rqst *rqstp,
+                                       struct xdr_stream *xdr,
+                                       struct nfs4_secinfo_res *res)
+{
+       struct compound_hdr hdr;
+       int status;
+
+       status = decode_compound_hdr(xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
+       if (status)
+               goto out;
+       status = decode_putrootfh(xdr);
+       if (status)
+               goto out;
+       status = decode_secinfo(xdr, res);
+out:
+       return status;
+}
 #endif /* CONFIG_NFS_V4_1 */
 
 /**
@@ -6669,6 +6736,7 @@ struct rpc_procinfo       nfs4_procedures[] = {
        PROC(LAYOUTGET,         enc_layoutget,          dec_layoutget),
        PROC(LAYOUTCOMMIT,      enc_layoutcommit,       dec_layoutcommit),
        PROC(LAYOUTRETURN,      enc_layoutreturn,       dec_layoutreturn),
+       PROC(SECINFO_NO_NAME,   enc_secinfo_no_name,    dec_secinfo_no_name),
 #endif /* CONFIG_NFS_V4_1 */
 };
 
index 504b289..7cc7d57 100644 (file)
@@ -563,6 +563,7 @@ enum {
        NFSPROC4_CLNT_GETDEVICEINFO,
        NFSPROC4_CLNT_LAYOUTCOMMIT,
        NFSPROC4_CLNT_LAYOUTRETURN,
+       NFSPROC4_CLNT_SECINFO_NO_NAME,
 };
 
 /* nfs41 types */
index c4d98df..7159e1d 100644 (file)
@@ -1084,6 +1084,14 @@ struct nfs41_reclaim_complete_args {
 struct nfs41_reclaim_complete_res {
        struct nfs4_sequence_res        seq_res;
 };
+
+#define SECINFO_STYLE_CURRENT_FH 0
+#define SECINFO_STYLE_PARENT 1
+struct nfs41_secinfo_no_name_args {
+       int                             style;
+       struct nfs4_sequence_args       seq_args;
+};
+
 #endif /* CONFIG_NFS_V4_1 */
 
 struct nfs_page;