summaryrefslogtreecommitdiffstats
path: root/fs/nfsd/nfs4recover.c
diff options
context:
space:
mode:
authorJeff Layton <jlayton@redhat.com>2012-11-12 21:00:57 +0100
committerJ. Bruce Fields <bfields@redhat.com>2012-11-13 00:55:11 +0100
commit2216d449a97927cc105912e337d169cd4d4db548 (patch)
tree66e350123f674f0fbcdd558421d4bd29eea4e69c /fs/nfsd/nfs4recover.c
parentnfsd: move the confirmed and unconfirmed hlists to a rbtree (diff)
downloadlinux-2216d449a97927cc105912e337d169cd4d4db548.tar.xz
linux-2216d449a97927cc105912e337d169cd4d4db548.zip
nfsd: get rid of cl_recdir field
Remove the cl_recdir field from the nfs4_client struct. Instead, just compute it on the fly when and if it's needed, which is now only when the legacy client tracking code is in effect. The error handling in the legacy client tracker is also changed to handle the case where md5 is unavailable. In that case, we'll warn the admin with a KERN_ERR message and disable the client tracking. Signed-off-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs/nfsd/nfs4recover.c')
-rw-r--r--fs/nfsd/nfs4recover.c93
1 files changed, 74 insertions, 19 deletions
diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c
index 3048c012d4bc..80e77cc14250 100644
--- a/fs/nfsd/nfs4recover.c
+++ b/fs/nfsd/nfs4recover.c
@@ -103,33 +103,39 @@ md5_to_hex(char *out, char *md5)
*out = '\0';
}
-__be32
-nfs4_make_rec_clidname(char *dname, struct xdr_netobj *clname)
+static int
+nfs4_make_rec_clidname(char *dname, const struct xdr_netobj *clname)
{
struct xdr_netobj cksum;
struct hash_desc desc;
struct scatterlist sg;
- __be32 status = nfserr_jukebox;
+ int status;
dprintk("NFSD: nfs4_make_rec_clidname for %.*s\n",
clname->len, clname->data);
desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
desc.tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC);
- if (IS_ERR(desc.tfm))
+ if (IS_ERR(desc.tfm)) {
+ status = PTR_ERR(desc.tfm);
goto out_no_tfm;
+ }
+
cksum.len = crypto_hash_digestsize(desc.tfm);
cksum.data = kmalloc(cksum.len, GFP_KERNEL);
- if (cksum.data == NULL)
+ if (cksum.data == NULL) {
+ status = -ENOMEM;
goto out;
+ }
sg_init_one(&sg, clname->data, clname->len);
- if (crypto_hash_digest(&desc, &sg, sg.length, cksum.data))
+ status = crypto_hash_digest(&desc, &sg, sg.length, cksum.data);
+ if (status)
goto out;
md5_to_hex(dname, cksum.data);
- status = nfs_ok;
+ status = 0;
out:
kfree(cksum.data);
crypto_free_hash(desc.tfm);
@@ -137,11 +143,36 @@ out_no_tfm:
return status;
}
+/*
+ * If we had an error generating the recdir name for the legacy tracker
+ * then warn the admin. If the error doesn't appear to be transient,
+ * then disable recovery tracking.
+ */
+static void
+legacy_recdir_name_error(int error)
+{
+ printk(KERN_ERR "NFSD: unable to generate recoverydir "
+ "name (%d).\n", error);
+
+ /*
+ * if the algorithm just doesn't exist, then disable the recovery
+ * tracker altogether. The crypto libs will generally return this if
+ * FIPS is enabled as well.
+ */
+ if (error == -ENOENT) {
+ printk(KERN_ERR "NFSD: disabling legacy clientid tracking. "
+ "Reboot recovery will not function correctly!\n");
+
+ /* the argument is ignored by the legacy exit function */
+ nfsd4_client_tracking_exit(NULL);
+ }
+}
+
static void
nfsd4_create_clid_dir(struct nfs4_client *clp)
{
const struct cred *original_cred;
- char *dname = clp->cl_recdir;
+ char dname[HEXDIR_LEN];
struct dentry *dir, *dentry;
struct nfs4_client_reclaim *crp;
int status;
@@ -152,6 +183,11 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
return;
if (!rec_file)
return;
+
+ status = nfs4_make_rec_clidname(dname, &clp->cl_name);
+ if (status)
+ return legacy_recdir_name_error(status);
+
status = nfs4_save_creds(&original_cred);
if (status < 0)
return;
@@ -186,7 +222,7 @@ out_unlock:
mutex_unlock(&dir->d_inode->i_mutex);
if (status == 0) {
if (in_grace) {
- crp = nfs4_client_to_reclaim(clp->cl_recdir);
+ crp = nfs4_client_to_reclaim(dname);
if (crp)
crp->cr_clp = clp;
}
@@ -298,11 +334,16 @@ nfsd4_remove_clid_dir(struct nfs4_client *clp)
{
const struct cred *original_cred;
struct nfs4_client_reclaim *crp;
+ char dname[HEXDIR_LEN];
int status;
if (!rec_file || !test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
return;
+ status = nfs4_make_rec_clidname(dname, &clp->cl_name);
+ if (status)
+ return legacy_recdir_name_error(status);
+
status = mnt_want_write_file(rec_file);
if (status)
goto out;
@@ -312,13 +353,13 @@ nfsd4_remove_clid_dir(struct nfs4_client *clp)
if (status < 0)
goto out_drop_write;
- status = nfsd4_unlink_clid_dir(clp->cl_recdir, HEXDIR_LEN-1);
+ status = nfsd4_unlink_clid_dir(dname, HEXDIR_LEN-1);
nfs4_reset_creds(original_cred);
if (status == 0) {
vfs_fsync(rec_file, 0);
if (in_grace) {
/* remove reclaim record */
- crp = nfsd4_find_reclaim_client(clp->cl_recdir);
+ crp = nfsd4_find_reclaim_client(dname);
if (crp)
nfs4_remove_reclaim_record(crp);
}
@@ -328,7 +369,7 @@ out_drop_write:
out:
if (status)
printk("NFSD: Failed to remove expired client state directory"
- " %.*s\n", HEXDIR_LEN, clp->cl_recdir);
+ " %.*s\n", HEXDIR_LEN, dname);
}
static int
@@ -500,14 +541,22 @@ nfs4_recoverydir(void)
static int
nfsd4_check_legacy_client(struct nfs4_client *clp)
{
+ int status;
+ char dname[HEXDIR_LEN];
struct nfs4_client_reclaim *crp;
/* did we already find that this client is stable? */
if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
return 0;
+ status = nfs4_make_rec_clidname(dname, &clp->cl_name);
+ if (status) {
+ legacy_recdir_name_error(status);
+ return status;
+ }
+
/* look for it in the reclaim hashtable otherwise */
- crp = nfsd4_find_reclaim_client(clp->cl_recdir);
+ crp = nfsd4_find_reclaim_client(dname);
if (crp) {
set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
crp->cr_clp = clp;
@@ -993,7 +1042,7 @@ nfsd4_cltrack_legacy_topdir(void)
}
static char *
-nfsd4_cltrack_legacy_recdir(const char *recdir)
+nfsd4_cltrack_legacy_recdir(const struct xdr_netobj *name)
{
int copied;
size_t len;
@@ -1010,10 +1059,16 @@ nfsd4_cltrack_legacy_recdir(const char *recdir)
if (!result)
return result;
- copied = snprintf(result, len, LEGACY_RECDIR_ENV_PREFIX "%s/%s",
- nfs4_recoverydir(), recdir);
- if (copied >= len) {
- /* just return nothing if output was truncated */
+ copied = snprintf(result, len, LEGACY_RECDIR_ENV_PREFIX "%s/",
+ nfs4_recoverydir());
+ if (copied > (len - HEXDIR_LEN)) {
+ /* just return nothing if output will be truncated */
+ kfree(result);
+ return NULL;
+ }
+
+ copied = nfs4_make_rec_clidname(result + copied, name);
+ if (copied) {
kfree(result);
return NULL;
}
@@ -1126,7 +1181,7 @@ nfsd4_umh_cltrack_check(struct nfs4_client *clp)
dprintk("%s: can't allocate memory for upcall!\n", __func__);
return -ENOMEM;
}
- legacy = nfsd4_cltrack_legacy_recdir(clp->cl_recdir);
+ legacy = nfsd4_cltrack_legacy_recdir(&clp->cl_name);
ret = nfsd4_umh_cltrack_upcall("check", hexid, legacy);
kfree(legacy);
kfree(hexid);