summaryrefslogtreecommitdiff
path: root/fs/nfsd/nfs4recover.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfsd/nfs4recover.c')
-rw-r--r--fs/nfsd/nfs4recover.c233
1 files changed, 70 insertions, 163 deletions
diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c
index e2b9472e5c78..441dfbfe2d2b 100644
--- a/fs/nfsd/nfs4recover.c
+++ b/fs/nfsd/nfs4recover.c
@@ -32,7 +32,7 @@
*
*/
-#include <crypto/hash.h>
+#include <crypto/md5.h>
#include <crypto/sha2.h>
#include <linux/file.h>
#include <linux/slab.h>
@@ -92,79 +92,29 @@ nfs4_reset_creds(const struct cred *original)
put_cred(revert_creds(original));
}
-static int
+static void
nfs4_make_rec_clidname(char dname[HEXDIR_LEN], const struct xdr_netobj *clname)
{
u8 digest[MD5_DIGEST_SIZE];
- struct crypto_shash *tfm;
- int status;
dprintk("NFSD: nfs4_make_rec_clidname for %.*s\n",
clname->len, clname->data);
- tfm = crypto_alloc_shash("md5", 0, 0);
- if (IS_ERR(tfm)) {
- status = PTR_ERR(tfm);
- goto out_no_tfm;
- }
- status = crypto_shash_tfm_digest(tfm, clname->data, clname->len,
- digest);
- if (status)
- goto out;
+ md5(clname->data, clname->len, digest);
static_assert(HEXDIR_LEN == 2 * MD5_DIGEST_SIZE + 1);
sprintf(dname, "%*phN", MD5_DIGEST_SIZE, digest);
-
- status = 0;
-out:
- crypto_free_shash(tfm);
-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(struct nfs4_client *clp, 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");
- nfsd4_client_tracking_exit(clp->net);
- }
}
static void
__nfsd4_create_reclaim_record_grace(struct nfs4_client *clp,
- const char *dname, int len, struct nfsd_net *nn)
+ char *dname, struct nfsd_net *nn)
{
- struct xdr_netobj name;
+ struct xdr_netobj name = { .len = strlen(dname), .data = dname };
struct xdr_netobj princhash = { .len = 0, .data = NULL };
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, princhash, nn);
- if (!crp) {
- kfree(name.data);
- return;
- }
crp->cr_clp = clp;
}
@@ -182,9 +132,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
if (!nn->rec_file)
return;
- status = nfs4_make_rec_clidname(dname, &clp->cl_name);
- if (status)
- return legacy_recdir_name_error(clp, status);
+ nfs4_make_rec_clidname(dname, &clp->cl_name);
status = nfs4_save_creds(&original_cred);
if (status < 0)
@@ -195,13 +143,11 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
goto out_creds;
dir = nn->rec_file->f_path.dentry;
- /* lock the parent */
- inode_lock(d_inode(dir));
- dentry = lookup_one(&nop_mnt_idmap, &QSTR(dname), dir);
+ dentry = start_creating(&nop_mnt_idmap, dir, &QSTR(dname));
if (IS_ERR(dentry)) {
status = PTR_ERR(dentry);
- goto out_unlock;
+ goto out;
}
if (d_really_is_positive(dentry))
/*
@@ -212,19 +158,16 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
* In the 4.0 case, we should never get here; but we may
* as well be forgiving and just succeed silently.
*/
- goto out_put;
- dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), dentry, S_IRWXU);
+ goto out_end;
+ dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), dentry, 0700, NULL);
if (IS_ERR(dentry))
status = PTR_ERR(dentry);
-out_put:
- if (!status)
- dput(dentry);
-out_unlock:
- inode_unlock(d_inode(dir));
+out_end:
+ end_creating(dentry);
+out:
if (status == 0) {
if (nn->in_grace)
- __nfsd4_create_reclaim_record_grace(clp, dname,
- HEXDIR_LEN, nn);
+ __nfsd4_create_reclaim_record_grace(clp, dname, nn);
vfs_fsync(nn->rec_file, 0);
} else {
printk(KERN_ERR "NFSD: failed to write recovery record"
@@ -237,7 +180,7 @@ out_creds:
nfs4_reset_creds(original_cred);
}
-typedef int (recdir_func)(struct dentry *, struct dentry *, struct nfsd_net *);
+typedef int (recdir_func)(struct dentry *, char *, struct nfsd_net *);
struct name_list {
char name[HEXDIR_LEN];
@@ -291,24 +234,14 @@ nfsd4_list_rec_dir(recdir_func *f, struct nfsd_net *nn)
}
status = iterate_dir(nn->rec_file, &ctx.ctx);
- inode_lock_nested(d_inode(dir), I_MUTEX_PARENT);
list_for_each_entry_safe(entry, tmp, &ctx.names, list) {
- if (!status) {
- struct dentry *dentry;
- dentry = lookup_one(&nop_mnt_idmap,
- &QSTR(entry->name), dir);
- if (IS_ERR(dentry)) {
- status = PTR_ERR(dentry);
- break;
- }
- status = f(dir, dentry, nn);
- dput(dentry);
- }
+ if (!status)
+ status = f(dir, entry->name, nn);
+
list_del(&entry->list);
kfree(entry);
}
- inode_unlock(d_inode(dir));
nfs4_reset_creds(original_cred);
list_for_each_entry_safe(entry, tmp, &ctx.names, list) {
@@ -328,20 +261,12 @@ nfsd4_unlink_clid_dir(char *name, struct nfsd_net *nn)
dprintk("NFSD: nfsd4_unlink_clid_dir. name %s\n", name);
dir = nn->rec_file->f_path.dentry;
- inode_lock_nested(d_inode(dir), I_MUTEX_PARENT);
- dentry = lookup_one(&nop_mnt_idmap, &QSTR(name), dir);
- if (IS_ERR(dentry)) {
- status = PTR_ERR(dentry);
- goto out_unlock;
- }
- status = -ENOENT;
- if (d_really_is_negative(dentry))
- goto out;
- status = vfs_rmdir(&nop_mnt_idmap, d_inode(dir), dentry);
-out:
- dput(dentry);
-out_unlock:
- inode_unlock(d_inode(dir));
+ dentry = start_removing(&nop_mnt_idmap, dir, &QSTR(name));
+ if (IS_ERR(dentry))
+ return PTR_ERR(dentry);
+
+ status = vfs_rmdir(&nop_mnt_idmap, d_inode(dir), dentry, NULL);
+ end_removing(dentry);
return status;
}
@@ -376,9 +301,7 @@ nfsd4_remove_clid_dir(struct nfs4_client *clp)
if (!nn->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(clp, status);
+ nfs4_make_rec_clidname(dname, &clp->cl_name);
status = mnt_want_write_file(nn->rec_file);
if (status)
@@ -406,18 +329,19 @@ out:
}
static int
-purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn)
+purge_old(struct dentry *parent, char *cname, struct nfsd_net *nn)
{
int status;
+ struct dentry *child;
struct xdr_netobj name;
- if (child->d_name.len != HEXDIR_LEN - 1) {
- printk("%s: illegal name %pd in recovery directory\n",
- __func__, child);
+ if (strlen(cname) != HEXDIR_LEN - 1) {
+ printk("%s: illegal name %s in recovery directory\n",
+ __func__, cname);
/* Keep trying; maybe the others are OK: */
return 0;
}
- name.data = kmemdup_nul(child->d_name.name, child->d_name.len, GFP_KERNEL);
+ name.data = kstrdup(cname, GFP_KERNEL);
if (!name.data) {
dprintk("%s: failed to allocate memory for name.data!\n",
__func__);
@@ -427,10 +351,17 @@ purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn)
if (nfs4_has_reclaimed_state(name, nn))
goto out_free;
- status = vfs_rmdir(&nop_mnt_idmap, d_inode(parent), child);
- if (status)
- printk("failed to remove client recovery directory %pd\n",
- child);
+ inode_lock_nested(d_inode(parent), I_MUTEX_PARENT);
+ child = lookup_one(&nop_mnt_idmap, &QSTR(cname), parent);
+ if (!IS_ERR(child)) {
+ status = vfs_rmdir(&nop_mnt_idmap, d_inode(parent), child, NULL);
+ if (status)
+ printk("failed to remove client recovery directory %pd\n",
+ child);
+ dput(child);
+ }
+ inode_unlock(d_inode(parent));
+
out_free:
kfree(name.data);
out:
@@ -461,27 +392,18 @@ out:
}
static int
-load_recdir(struct dentry *parent, struct dentry *child, struct nfsd_net *nn)
+load_recdir(struct dentry *parent, char *cname, struct nfsd_net *nn)
{
- struct xdr_netobj name;
+ struct xdr_netobj name = { .len = HEXDIR_LEN, .data = cname };
struct xdr_netobj princhash = { .len = 0, .data = NULL };
- if (child->d_name.len != HEXDIR_LEN - 1) {
- printk("%s: illegal name %pd in recovery directory\n",
- __func__, child);
+ if (strlen(cname) != HEXDIR_LEN - 1) {
+ printk("%s: illegal name %s in recovery directory\n",
+ __func__, cname);
/* 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_client_to_reclaim(name, princhash, nn))
- kfree(name.data);
-out:
+ nfs4_client_to_reclaim(name, princhash, nn);
return 0;
}
@@ -659,7 +581,6 @@ nfs4_recoverydir(void)
static int
nfsd4_check_legacy_client(struct nfs4_client *clp)
{
- int status;
char dname[HEXDIR_LEN];
struct nfs4_client_reclaim *crp;
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
@@ -669,11 +590,7 @@ nfsd4_check_legacy_client(struct nfs4_client *clp)
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(clp, status);
- return status;
- }
+ nfs4_make_rec_clidname(dname, &clp->cl_name);
/* look for it in the reclaim hashtable otherwise */
name.data = kmemdup(dname, HEXDIR_LEN, GFP_KERNEL);
@@ -779,6 +696,8 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg,
{
uint8_t cmd, princhashlen;
struct xdr_netobj name, princhash = { .len = 0, .data = NULL };
+ char *namecopy __free(kfree) = NULL;
+ char *princhashcopy __free(kfree) = NULL;
uint16_t namelen;
if (get_user(cmd, &cmsg->cm_cmd)) {
@@ -796,19 +715,19 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg,
dprintk("%s: invalid namelen (%u)", __func__, namelen);
return -EINVAL;
}
- name.data = memdup_user(&ci->cc_name.cn_id, namelen);
- if (IS_ERR(name.data))
- return PTR_ERR(name.data);
+ namecopy = memdup_user(&ci->cc_name.cn_id, namelen);
+ if (IS_ERR(namecopy))
+ return PTR_ERR(namecopy);
+ name.data = namecopy;
name.len = namelen;
get_user(princhashlen, &ci->cc_princhash.cp_len);
if (princhashlen > 0) {
- princhash.data = memdup_user(
- &ci->cc_princhash.cp_data,
- princhashlen);
- if (IS_ERR(princhash.data)) {
- kfree(name.data);
- return PTR_ERR(princhash.data);
- }
+ princhashcopy = memdup_user(
+ &ci->cc_princhash.cp_data,
+ princhashlen);
+ if (IS_ERR(princhashcopy))
+ return PTR_ERR(princhashcopy);
+ princhash.data = princhashcopy;
princhash.len = princhashlen;
} else
princhash.len = 0;
@@ -822,9 +741,10 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg,
dprintk("%s: invalid namelen (%u)", __func__, namelen);
return -EINVAL;
}
- name.data = memdup_user(&cnm->cn_id, namelen);
- if (IS_ERR(name.data))
- return PTR_ERR(name.data);
+ namecopy = memdup_user(&cnm->cn_id, namelen);
+ if (IS_ERR(namecopy))
+ return PTR_ERR(namecopy);
+ name.data = namecopy;
name.len = namelen;
}
#ifdef CONFIG_NFSD_LEGACY_CLIENT_TRACKING
@@ -832,15 +752,12 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg,
struct cld_net *cn = nn->cld_net;
name.len = name.len - 5;
- memmove(name.data, name.data + 5, name.len);
+ name.data = name.data + 5;
cn->cn_has_legacy = true;
}
#endif
- if (!nfs4_client_to_reclaim(name, princhash, nn)) {
- kfree(name.data);
- kfree(princhash.data);
+ if (!nfs4_client_to_reclaim(name, princhash, nn))
return -EFAULT;
- }
return nn->client_tracking_ops->msglen;
}
return -EFAULT;
@@ -1266,13 +1183,10 @@ nfsd4_cld_check(struct nfs4_client *clp)
#ifdef CONFIG_NFSD_LEGACY_CLIENT_TRACKING
if (nn->cld_net->cn_has_legacy) {
- int status;
char dname[HEXDIR_LEN];
struct xdr_netobj name;
- status = nfs4_make_rec_clidname(dname, &clp->cl_name);
- if (status)
- return -ENOENT;
+ nfs4_make_rec_clidname(dname, &clp->cl_name);
name.data = kmemdup(dname, HEXDIR_LEN, GFP_KERNEL);
if (!name.data) {
@@ -1317,11 +1231,8 @@ nfsd4_cld_check_v2(struct nfs4_client *clp)
if (cn->cn_has_legacy) {
struct xdr_netobj name;
char dname[HEXDIR_LEN];
- int status;
- status = nfs4_make_rec_clidname(dname, &clp->cl_name);
- if (status)
- return -ENOENT;
+ nfs4_make_rec_clidname(dname, &clp->cl_name);
name.data = kmemdup(dname, HEXDIR_LEN, GFP_KERNEL);
if (!name.data) {
@@ -1694,11 +1605,7 @@ nfsd4_cltrack_legacy_recdir(const struct xdr_netobj *name)
return NULL;
}
- copied = nfs4_make_rec_clidname(result + copied, name);
- if (copied) {
- kfree(result);
- return NULL;
- }
+ nfs4_make_rec_clidname(result + copied, name);
return result;
}