nfsd: make nfs4_client_reclaim use an xdr_netobj instead of a fixed char array
authorScott Mayhew <smayhew@redhat.com>
Tue, 26 Mar 2019 22:06:26 +0000 (18:06 -0400)
committerJ. Bruce Fields <bfields@redhat.com>
Wed, 24 Apr 2019 13:46:34 +0000 (09:46 -0400)
This will allow the reclaim_str_hashtbl to store either the recovery
directory names used by the legacy client tracking code or the full
client strings used by the nfsdcld client tracking code.

Signed-off-by: Scott Mayhew <smayhew@redhat.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
fs/nfsd/nfs4recover.c
fs/nfsd/nfs4state.c
fs/nfsd/state.h

index 5188f9f..2243b90 100644 (file)
@@ -170,12 +170,33 @@ legacy_recdir_name_error(struct nfs4_client *clp, int error)
 }
 
 static void
+__nfsd4_create_reclaim_record_grace(struct nfs4_client *clp,
+               const char *dname, int len, struct nfsd_net *nn)
+{
+       struct xdr_netobj name;
+       struct nfs4_client_reclaim *crp;
+
+       name.data = kmemdup(dname, len, GFP_KERNEL);
+       if (!name.data) {
+               dprintk("%s: failed to allocate memory for name.data!\n",
+                       __func__);
+               return;
+       }
+       name.len = len;
+       crp = nfs4_client_to_reclaim(name, nn);
+       if (!crp) {
+               kfree(name.data);
+               return;
+       }
+       crp->cr_clp = clp;
+}
+
+static void
 nfsd4_create_clid_dir(struct nfs4_client *clp)
 {
        const struct cred *original_cred;
        char dname[HEXDIR_LEN];
        struct dentry *dir, *dentry;
-       struct nfs4_client_reclaim *crp;
        int status;
        struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
 
@@ -221,11 +242,9 @@ out_put:
 out_unlock:
        inode_unlock(d_inode(dir));
        if (status == 0) {
-               if (nn->in_grace) {
-                       crp = nfs4_client_to_reclaim(dname, nn);
-                       if (crp)
-                               crp->cr_clp = clp;
-               }
+               if (nn->in_grace)
+                       __nfsd4_create_reclaim_record_grace(clp, dname,
+                                       HEXDIR_LEN, nn);
                vfs_fsync(nn->rec_file, 0);
        } else {
                printk(KERN_ERR "NFSD: failed to write recovery record"
@@ -346,10 +365,29 @@ out_unlock:
 }
 
 static void
+__nfsd4_remove_reclaim_record_grace(const char *dname, int len,
+               struct nfsd_net *nn)
+{
+       struct xdr_netobj name;
+       struct nfs4_client_reclaim *crp;
+
+       name.data = kmemdup(dname, len, GFP_KERNEL);
+       if (!name.data) {
+               dprintk("%s: failed to allocate memory for name.data!\n",
+                       __func__);
+               return;
+       }
+       name.len = len;
+       crp = nfsd4_find_reclaim_client(name, nn);
+       kfree(name.data);
+       if (crp)
+               nfs4_remove_reclaim_record(crp, nn);
+}
+
+static void
 nfsd4_remove_clid_dir(struct nfs4_client *clp)
 {
        const struct cred *original_cred;
-       struct nfs4_client_reclaim *crp;
        char dname[HEXDIR_LEN];
        int status;
        struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
@@ -374,12 +412,9 @@ nfsd4_remove_clid_dir(struct nfs4_client *clp)
        nfs4_reset_creds(original_cred);
        if (status == 0) {
                vfs_fsync(nn->rec_file, 0);
-               if (nn->in_grace) {
-                       /* remove reclaim record */
-                       crp = nfsd4_find_reclaim_client(dname, nn);
-                       if (crp)
-                               nfs4_remove_reclaim_record(crp, nn);
-               }
+               if (nn->in_grace)
+                       __nfsd4_remove_reclaim_record_grace(dname,
+                                       HEXDIR_LEN, nn);
        }
 out_drop_write:
        mnt_drop_write_file(nn->rec_file);
@@ -393,14 +428,31 @@ static int
 purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn)
 {
        int status;
+       struct xdr_netobj name;
 
-       if (nfs4_has_reclaimed_state(child->d_name.name, nn))
+       if (child->d_name.len != HEXDIR_LEN - 1) {
+               printk("%s: illegal name %pd in recovery directory\n",
+                               __func__, child);
+               /* Keep trying; maybe the others are OK: */
                return 0;
+       }
+       name.data = kmemdup_nul(child->d_name.name, child->d_name.len, GFP_KERNEL);
+       if (!name.data) {
+               dprintk("%s: failed to allocate memory for name.data!\n",
+                       __func__);
+               goto out;
+       }
+       name.len = HEXDIR_LEN;
+       if (nfs4_has_reclaimed_state(name, nn))
+               goto out_free;
 
        status = vfs_rmdir(d_inode(parent), child);
        if (status)
                printk("failed to remove client recovery directory %pd\n",
                                child);
+out_free:
+       kfree(name.data);
+out:
        /* Keep trying, success or failure: */
        return 0;
 }
@@ -430,13 +482,24 @@ out:
 static int
 load_recdir(struct dentry *parent, struct dentry *child, struct nfsd_net *nn)
 {
+       struct xdr_netobj name;
+
        if (child->d_name.len != HEXDIR_LEN - 1) {
-               printk("nfsd4: illegal name %pd in recovery directory\n",
-                               child);
+               printk("%s: illegal name %pd in recovery directory\n",
+                               __func__, child);
                /* Keep trying; maybe the others are OK: */
                return 0;
        }
-       nfs4_client_to_reclaim(child->d_name.name, nn);
+       name.data = kmemdup_nul(child->d_name.name, child->d_name.len, GFP_KERNEL);
+       if (!name.data) {
+               dprintk("%s: failed to allocate memory for name.data!\n",
+                       __func__);
+               goto out;
+       }
+       name.len = HEXDIR_LEN;
+       if (!nfs4_client_to_reclaim(name, nn))
+               kfree(name.data);
+out:
        return 0;
 }
 
@@ -616,6 +679,7 @@ nfsd4_check_legacy_client(struct nfs4_client *clp)
        char dname[HEXDIR_LEN];
        struct nfs4_client_reclaim *crp;
        struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+       struct xdr_netobj name;
 
        /* did we already find that this client is stable? */
        if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
@@ -628,13 +692,22 @@ nfsd4_check_legacy_client(struct nfs4_client *clp)
        }
 
        /* look for it in the reclaim hashtable otherwise */
-       crp = nfsd4_find_reclaim_client(dname, nn);
+       name.data = kmemdup(dname, HEXDIR_LEN, GFP_KERNEL);
+       if (!name.data) {
+               dprintk("%s: failed to allocate memory for name.data!\n",
+                       __func__);
+               goto out_enoent;
+       }
+       name.len = HEXDIR_LEN;
+       crp = nfsd4_find_reclaim_client(name, nn);
+       kfree(name.data);
        if (crp) {
                set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
                crp->cr_clp = clp;
                return 0;
        }
 
+out_enoent:
        return -ENOENT;
 }
 
index f056b1d..e601416 100644 (file)
@@ -1067,9 +1067,9 @@ static unsigned int clientid_hashval(u32 id)
        return id & CLIENT_HASH_MASK;
 }
 
-static unsigned int clientstr_hashval(const char *name)
+static unsigned int clientstr_hashval(struct xdr_netobj name)
 {
-       return opaque_hashval(name, 8) & CLIENT_HASH_MASK;
+       return opaque_hashval(name.data, 8) & CLIENT_HASH_MASK;
 }
 
 /*
@@ -2048,11 +2048,6 @@ compare_blob(const struct xdr_netobj *o1, const struct xdr_netobj *o2)
        return memcmp(o1->data, o2->data, o1->len);
 }
 
-static int same_name(const char *n1, const char *n2)
-{
-       return 0 == memcmp(n1, n2, HEXDIR_LEN);
-}
-
 static int
 same_verf(nfs4_verifier *v1, nfs4_verifier *v2)
 {
@@ -6457,7 +6452,7 @@ alloc_reclaim(void)
 }
 
 bool
-nfs4_has_reclaimed_state(const char *name, struct nfsd_net *nn)
+nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn)
 {
        struct nfs4_client_reclaim *crp;
 
@@ -6467,20 +6462,24 @@ nfs4_has_reclaimed_state(const char *name, struct nfsd_net *nn)
 
 /*
  * failure => all reset bets are off, nfserr_no_grace...
+ *
+ * The caller is responsible for freeing name.data if NULL is returned (it
+ * will be freed in nfs4_remove_reclaim_record in the normal case).
  */
 struct nfs4_client_reclaim *
-nfs4_client_to_reclaim(const char *name, struct nfsd_net *nn)
+nfs4_client_to_reclaim(struct xdr_netobj name, struct nfsd_net *nn)
 {
        unsigned int strhashval;
        struct nfs4_client_reclaim *crp;
 
-       dprintk("NFSD nfs4_client_to_reclaim NAME: %.*s\n", HEXDIR_LEN, name);
+       dprintk("NFSD nfs4_client_to_reclaim NAME: %.*s\n", name.len, name.data);
        crp = alloc_reclaim();
        if (crp) {
                strhashval = clientstr_hashval(name);
                INIT_LIST_HEAD(&crp->cr_strhash);
                list_add(&crp->cr_strhash, &nn->reclaim_str_hashtbl[strhashval]);
-               memcpy(crp->cr_recdir, name, HEXDIR_LEN);
+               crp->cr_name.data = name.data;
+               crp->cr_name.len = name.len;
                crp->cr_clp = NULL;
                nn->reclaim_str_hashtbl_size++;
        }
@@ -6491,6 +6490,7 @@ void
 nfs4_remove_reclaim_record(struct nfs4_client_reclaim *crp, struct nfsd_net *nn)
 {
        list_del(&crp->cr_strhash);
+       kfree(crp->cr_name.data);
        kfree(crp);
        nn->reclaim_str_hashtbl_size--;
 }
@@ -6514,16 +6514,16 @@ nfs4_release_reclaim(struct nfsd_net *nn)
 /*
  * called from OPEN, CLAIM_PREVIOUS with a new clientid. */
 struct nfs4_client_reclaim *
-nfsd4_find_reclaim_client(const char *recdir, struct nfsd_net *nn)
+nfsd4_find_reclaim_client(struct xdr_netobj name, struct nfsd_net *nn)
 {
        unsigned int strhashval;
        struct nfs4_client_reclaim *crp = NULL;
 
-       dprintk("NFSD: nfs4_find_reclaim_client for recdir %s\n", recdir);
+       dprintk("NFSD: nfs4_find_reclaim_client for name %.*s\n", name.len, name.data);
 
-       strhashval = clientstr_hashval(recdir);
+       strhashval = clientstr_hashval(name);
        list_for_each_entry(crp, &nn->reclaim_str_hashtbl[strhashval], cr_strhash) {
-               if (same_name(crp->cr_recdir, recdir)) {
+               if (compare_blob(&crp->cr_name, &name) == 0) {
                        return crp;
                }
        }
index 9d6cb24..0b74d37 100644 (file)
@@ -368,7 +368,7 @@ struct nfs4_client {
 struct nfs4_client_reclaim {
        struct list_head        cr_strhash;     /* hash by cr_name */
        struct nfs4_client      *cr_clp;        /* pointer to associated clp */
-       char                    cr_recdir[HEXDIR_LEN]; /* recover dir */
+       struct xdr_netobj       cr_name;        /* recovery dir name */
 };
 
 /* A reasonable value for REPLAY_ISIZE was estimated as follows:  
@@ -620,7 +620,7 @@ void nfs4_put_stid(struct nfs4_stid *s);
 void nfs4_inc_and_copy_stateid(stateid_t *dst, struct nfs4_stid *stid);
 void nfs4_remove_reclaim_record(struct nfs4_client_reclaim *, struct nfsd_net *);
 extern void nfs4_release_reclaim(struct nfsd_net *);
-extern struct nfs4_client_reclaim *nfsd4_find_reclaim_client(const char *recdir,
+extern struct nfs4_client_reclaim *nfsd4_find_reclaim_client(struct xdr_netobj name,
                                                        struct nfsd_net *nn);
 extern __be32 nfs4_check_open_reclaim(clientid_t *clid,
                struct nfsd4_compound_state *cstate, struct nfsd_net *nn);
@@ -635,9 +635,9 @@ extern void nfsd4_destroy_callback_queue(void);
 extern void nfsd4_shutdown_callback(struct nfs4_client *);
 extern void nfsd4_shutdown_copy(struct nfs4_client *clp);
 extern void nfsd4_prepare_cb_recall(struct nfs4_delegation *dp);
-extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(const char *name,
+extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(struct xdr_netobj name,
                                                        struct nfsd_net *nn);
-extern bool nfs4_has_reclaimed_state(const char *name, struct nfsd_net *nn);
+extern bool nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn);
 
 struct nfs4_file *find_file(struct knfsd_fh *fh);
 void put_nfs4_file(struct nfs4_file *fi);