/* * Copyright (c) 2004 The Regents of the University of Michigan. * Copyright (c) 2012 Jeff Layton <jlayton@redhat.com> * All rights reserved. * * Andy Adamson <andros@citi.umich.edu> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
*/
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;
}
cksum.len = crypto_shash_digestsize(tfm);
cksum.data = kmalloc(cksum.len, GFP_KERNEL); if (cksum.data == NULL) {
status = -ENOMEM; goto out;
}
status = crypto_shash_tfm_digest(tfm, clname->data, clname->len,
cksum.data); if (status) goto out;
md5_to_hex(dname, cksum.data);
status = 0;
out:
kfree(cksum.data);
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.
*/ staticvoid
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);
}
}
if (test_and_set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) return; if (!nn->rec_file) return;
status = nfs4_make_rec_clidname(dname, &clp->cl_name); if (status) return legacy_recdir_name_error(clp, status);
status = nfs4_save_creds(&original_cred); if (status < 0) return;
status = mnt_want_write_file(nn->rec_file); if (status) 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); if (IS_ERR(dentry)) {
status = PTR_ERR(dentry); goto out_unlock;
} if (d_really_is_positive(dentry)) /* * In the 4.1 case, where we're called from * reclaim_complete(), records from the previous reboot * may still be left, so this is OK. * * 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); if (IS_ERR(dentry))
status = PTR_ERR(dentry);
out_put: if (!status)
dput(dentry);
out_unlock:
inode_unlock(d_inode(dir)); if (status == 0) { 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" " (err %d); please check that %s exists" " and is writeable", status,
user_recovery_dirname);
}
mnt_drop_write_file(nn->rec_file);
out_creds:
nfs4_reset_creds(original_cred);
}
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(&nop_mnt_idmap, 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;
}
staticvoid
nfsd4_recdir_purge_old(struct nfsd_net *nn)
{ int status;
nn->in_grace = false; if (!nn->rec_file) return;
status = mnt_want_write_file(nn->rec_file); if (status) goto out;
status = nfsd4_list_rec_dir(purge_old, nn); if (status == 0)
vfs_fsync(nn->rec_file, 0);
mnt_drop_write_file(nn->rec_file);
out:
nfs4_release_reclaim(nn); if (status)
printk("nfsd4: failed to purge old clients from recovery" " directory %pD\n", nn->rec_file);
}
staticint
nfsd4_load_reboot_recovery_data(struct net *net)
{ int status;
status = nfsd4_init_recdir(net); if (status) return status;
status = nfsd4_recdir_load(net); if (status)
nfsd4_shutdown_recdir(net);
return status;
}
staticint
nfsd4_legacy_tracking_init(struct net *net)
{ int status;
/* XXX: The legacy code won't work in a container */ if (net != &init_net) {
pr_warn("NFSD: attempt to initialize legacy client tracking in a container ignored.\n"); return -EINVAL;
}
status = nfs4_legacy_state_init(net); if (status) return status;
status = nfsd4_load_reboot_recovery_data(net); if (status) goto err;
pr_info("NFSD: Using legacy client tracking operations.\n"); return 0;
/* * -EAGAIN occurs when pipe is closed and reopened while there are * upcalls queued.
*/ do {
ret = __cld_pipe_upcall(pipe, cmsg, nn);
} while (ret == -EAGAIN);
/* copy just the xid so we can try to find that */ if (copy_from_user(&xid, &hdr->cm_xid, sizeof(xid)) != 0) {
dprintk("%s: error when copying xid from userspace", __func__); return -EFAULT;
}
/* * copy the status so we know whether to remove the upcall from the * list (for -EINPROGRESS, we just want to make sure the xid is * valid, not remove the upcall from the list)
*/ if (get_user(status, &hdr->cm_status)) {
dprintk("%s: error when copying status from userspace", __func__); return -EFAULT;
}
/* walk the list and find corresponding xid */
cup = NULL;
spin_lock(&cn->cn_lock);
list_for_each_entry(tmp, &cn->cn_list, cu_list) { if (get_unaligned(&tmp->cu_u.cu_hdr.cm_xid) == xid) {
cup = tmp; if (status != -EINPROGRESS)
list_del_init(&cup->cu_list); break;
}
}
spin_unlock(&cn->cn_lock);
ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn); if (!ret) {
ret = cup->cu_u.cu_msg.cm_status;
set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
}
free_cld_upcall(cup);
out_err: if (ret)
printk(KERN_ERR "NFSD: Unable to create client " "record on stable storage: %d\n", ret);
}
/* Ask daemon to create a new record */ staticvoid
nfsd4_cld_create_v2(struct nfs4_client *clp)
{ int ret; struct cld_upcall *cup; struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); struct cld_net *cn = nn->cld_net; struct cld_msg_v2 *cmsg; char *principal = NULL;
/* Don't upcall if it's already stored */ if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) return;
cup = alloc_cld_upcall(nn); if (!cup) {
ret = -ENOMEM; goto out_err;
}
cmsg = &cup->cu_u.cu_msg_v2;
cmsg->cm_cmd = Cld_Create;
cmsg->cm_u.cm_clntinfo.cc_name.cn_len = clp->cl_name.len;
memcpy(cmsg->cm_u.cm_clntinfo.cc_name.cn_id, clp->cl_name.data,
clp->cl_name.len); if (clp->cl_cred.cr_raw_principal)
principal = clp->cl_cred.cr_raw_principal; elseif (clp->cl_cred.cr_principal)
principal = clp->cl_cred.cr_principal; if (principal) {
sha256(principal, strlen(principal),
cmsg->cm_u.cm_clntinfo.cc_princhash.cp_data);
cmsg->cm_u.cm_clntinfo.cc_princhash.cp_len = SHA256_DIGEST_SIZE;
} else
cmsg->cm_u.cm_clntinfo.cc_princhash.cp_len = 0;
ret = cld_pipe_upcall(cn->cn_pipe, cmsg, nn); if (!ret) {
ret = cmsg->cm_status;
set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
}
free_cld_upcall(cup);
out_err: if (ret)
pr_err("NFSD: Unable to create client record on stable storage: %d\n",
ret);
}
/* Ask daemon to create a new record */ staticvoid
nfsd4_cld_remove(struct nfs4_client *clp)
{ int ret; struct cld_upcall *cup; struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); struct cld_net *cn = nn->cld_net;
/* Don't upcall if it's already removed */ if (!test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) return;
cup = alloc_cld_upcall(nn); if (!cup) {
ret = -ENOMEM; goto out_err;
}
ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn); if (!ret) {
ret = cup->cu_u.cu_msg.cm_status;
clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
}
free_cld_upcall(cup);
out_err: if (ret)
printk(KERN_ERR "NFSD: Unable to remove client " "record from stable storage: %d\n", ret);
}
/* * For older nfsdcld's that do not allow us to "slurp" the clients * from the tracking database during startup. * * Check for presence of a record, and update its timestamp
*/ staticint
nfsd4_cld_check_v0(struct nfs4_client *clp)
{ int ret; struct cld_upcall *cup; struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); struct cld_net *cn = nn->cld_net;
/* Don't upcall if one was already stored during this grace pd */ if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) return 0;
cup = alloc_cld_upcall(nn); if (!cup) {
printk(KERN_ERR "NFSD: Unable to check client record on " "stable storage: %d\n", -ENOMEM); return -ENOMEM;
}
ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn); if (!ret) {
ret = cup->cu_u.cu_msg.cm_status;
set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
}
free_cld_upcall(cup); return ret;
}
/* * For newer nfsdcld's that allow us to "slurp" the clients * from the tracking database during startup. * * Check for presence of a record in the reclaim_str_hashtbl
*/ staticint
nfsd4_cld_check(struct nfs4_client *clp)
{ struct nfs4_client_reclaim *crp; struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
/* did we already find that this client is stable? */ if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) return 0;
/* look for it in the reclaim hashtable otherwise */
crp = nfsd4_find_reclaim_client(clp->cl_name, nn); if (crp) goto found;
#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;
name.data = kmemdup(dname, HEXDIR_LEN, GFP_KERNEL); if (!name.data) {
dprintk("%s: failed to allocate memory for name.data!\n",
__func__); return -ENOENT;
}
name.len = HEXDIR_LEN;
crp = nfsd4_find_reclaim_client(name, nn);
kfree(name.data); if (crp) goto found;
cup = alloc_cld_upcall(nn); if (!cup) {
ret = -ENOMEM; goto out_err;
}
cup->cu_u.cu_msg.cm_cmd = Cld_GraceStart;
ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn); if (!ret)
ret = cup->cu_u.cu_msg.cm_status;
free_cld_upcall(cup);
out_err: if (ret)
dprintk("%s: Unable to get clients from userspace: %d\n",
__func__, ret); return ret;
}
/* For older nfsdcld's that need cm_gracetime */ staticvoid
nfsd4_cld_grace_done_v0(struct nfsd_net *nn)
{ int ret; struct cld_upcall *cup; struct cld_net *cn = nn->cld_net;
cup = alloc_cld_upcall(nn); if (!cup) {
ret = -ENOMEM; goto out_err;
}
cup->cu_u.cu_msg.cm_cmd = Cld_GraceDone;
cup->cu_u.cu_msg.cm_u.cm_gracetime = nn->boot_time;
ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn); if (!ret)
ret = cup->cu_u.cu_msg.cm_status;
free_cld_upcall(cup);
out_err: if (ret)
printk(KERN_ERR "NFSD: Unable to end grace period: %d\n", ret);
}
/* * For newer nfsdcld's that do not need cm_gracetime. We also need to call * nfs4_release_reclaim() to clear out the reclaim_str_hashtbl.
*/ staticvoid
nfsd4_cld_grace_done(struct nfsd_net *nn)
{ int ret; struct cld_upcall *cup; struct cld_net *cn = nn->cld_net;
cup = alloc_cld_upcall(nn); if (!cup) {
ret = -ENOMEM; goto out_err;
}
cup->cu_u.cu_msg.cm_cmd = Cld_GraceDone;
ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn); if (!ret)
ret = cup->cu_u.cu_msg.cm_status;
free_cld_upcall(cup);
out_err:
nfs4_release_reclaim(nn); if (ret)
printk(KERN_ERR "NFSD: Unable to end grace period: %d\n", ret);
}
staticint
nfs4_cld_state_init(struct net *net)
{ struct nfsd_net *nn = net_generic(net, nfsd_net_id); int i;
nn->reclaim_str_hashtbl = kmalloc_array(CLIENT_HASH_SIZE, sizeof(struct list_head),
GFP_KERNEL); if (!nn->reclaim_str_hashtbl) return -ENOMEM;
for (i = 0; i < CLIENT_HASH_SIZE; i++)
INIT_LIST_HEAD(&nn->reclaim_str_hashtbl[i]);
nn->reclaim_str_hashtbl_size = 0;
nn->track_reclaim_completes = true;
atomic_set(&nn->nr_reclaim_complete, 0);
staticint
nfsd4_cld_get_version(struct nfsd_net *nn)
{ int ret = 0; struct cld_upcall *cup; struct cld_net *cn = nn->cld_net;
uint8_t version;
cup = alloc_cld_upcall(nn); if (!cup) {
ret = -ENOMEM; goto out_err;
}
cup->cu_u.cu_msg.cm_cmd = Cld_GetVersion;
ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn); if (!ret) {
ret = cup->cu_u.cu_msg.cm_status; if (ret) goto out_free;
version = cup->cu_u.cu_msg.cm_u.cm_version;
dprintk("%s: userspace returned version %u\n",
__func__, version); if (version < 1)
version = 1; elseif (version > CLD_UPCALL_VERSION)
version = CLD_UPCALL_VERSION;
switch (version) { case 1:
nn->client_tracking_ops = &nfsd4_cld_tracking_ops; break; case 2:
nn->client_tracking_ops = &nfsd4_cld_tracking_ops_v2; break; default: break;
}
}
out_free:
free_cld_upcall(cup);
out_err: if (ret)
dprintk("%s: Unable to get version from userspace: %d\n",
__func__, ret); return ret;
}
staticint
nfsd4_cld_tracking_init(struct net *net)
{ int status; struct nfsd_net *nn = net_generic(net, nfsd_net_id); bool running; int retries = 10;
status = nfs4_cld_state_init(net); if (status) return status;
status = __nfsd4_init_cld_pipe(net); if (status) goto err_shutdown;
/* * rpc pipe upcalls take 30 seconds to time out, so we don't want to * queue an upcall unless we know that nfsdcld is running (because we * want this to fail fast so that nfsd4_client_tracking_init() can try * the next client tracking method). nfsdcld should already be running * before nfsd is started, so the wait here is for nfsdcld to open the * pipefs file we just created.
*/ while (!(running = cld_running(nn)) && retries--)
msleep(100);
if (!running) {
status = -ETIMEDOUT; goto err_remove;
}
status = nfsd4_cld_get_version(nn); if (status == -EOPNOTSUPP)
pr_warn("NFSD: nfsdcld GetVersion upcall failed. Please upgrade nfsdcld.\n");
status = nfsd4_cld_grace_start(nn); if (status) { if (status == -EOPNOTSUPP)
pr_warn("NFSD: nfsdcld GraceStart upcall failed. Please upgrade nfsdcld.\n");
nfs4_release_reclaim(nn); goto err_remove;
} else
pr_info("NFSD: Using nfsdcld client tracking operations.\n"); return 0;
ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC); /* * Disable the upcall mechanism if we're getting an ENOENT or EACCES * error. The admin can re-enable it on the fly by using sysfs * once the problem has been fixed.
*/ if (ret == -ENOENT || ret == -EACCES) {
dprintk("NFSD: %s was not found or isn't executable (%d). " "Setting cltrack_prog to blank string!",
cltrack_prog, ret);
cltrack_prog[0] = '\0';
}
dprintk("%s: %s return value: %d\n", __func__, cltrack_prog, ret);
return ret;
}
staticchar *
bin_to_hex_dup(constunsignedchar *src, int srclen)
{ char *buf;
/* +1 for terminating NULL */
buf = kzalloc((srclen * 2) + 1, GFP_KERNEL); if (!buf) return buf;
bin2hex(buf, src, srclen); return buf;
}
staticint
nfsd4_umh_cltrack_init(struct net *net)
{ int ret; struct nfsd_net *nn = net_generic(net, nfsd_net_id); char *grace_start = nfsd4_cltrack_grace_start(nn->boot_time);
/* XXX: The usermode helper s not working in container yet. */ if (net != &init_net) {
pr_warn("NFSD: attempt to initialize umh client tracking in a container ignored.\n");
kfree(grace_start); return -EINVAL;
}
ret = nfsd4_umh_cltrack_upcall("init", NULL, grace_start, NULL);
kfree(grace_start); if (!ret)
pr_info("NFSD: Using UMH upcall client tracking operations.\n"); return ret;
}
/* * With v4.0 clients, there's little difference in outcome between a * create and check operation, and we can end up calling into this * function multiple times per client (once for each openowner). So, * for v4.0 clients skip upcalling once the client has been recorded * on stable storage. * * For v4.1+ clients, the outcome of the two operations is different, * so we must ensure that we upcall for the create operation. v4.1+ * clients call this on RECLAIM_COMPLETE though, so we should only end * up doing a single create upcall per client.
*/ if (clp->cl_minorversion == 0 &&
test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) return;
hexid = bin_to_hex_dup(clp->cl_name.data, clp->cl_name.len); if (!hexid) {
dprintk("%s: can't allocate memory for upcall!\n", __func__); return;
}
/* * Next, try the UMH upcall.
*/
nn->client_tracking_ops = &nfsd4_umh_tracking_ops;
status = nn->client_tracking_ops->init(net); if (!status) return status;
/* * Finally, See if the recoverydir exists and is a directory. * If it is, then use the legacy ops.
*/
nn->client_tracking_ops = &nfsd4_legacy_tracking_ops;
status = kern_path(nfs4_recoverydir(), LOOKUP_FOLLOW, &path); if (!status) {
status = !d_is_dir(path.dentry);
path_put(&path); if (status) return -ENOTDIR;
} return status;
} #else staticinlineint check_for_legacy_methods(int status, struct net *net)
{ return status;
} #endif/* CONFIG_LEGACY_NFSD_CLIENT_TRACKING */
int
nfsd4_client_tracking_init(struct net *net)
{ struct nfsd_net *nn = net_generic(net, nfsd_net_id); int status;
/* just run the init if it the method is already decided */ if (nn->client_tracking_ops) goto do_init;
/* First, try to use nfsdcld */
nn->client_tracking_ops = &nfsd4_cld_tracking_ops;
status = nn->client_tracking_ops->init(net); if (!status) return status; if (status != -ETIMEDOUT) {
nn->client_tracking_ops = &nfsd4_cld_tracking_ops_v0;
status = nn->client_tracking_ops->init(net); if (!status) return status;
}
status = check_for_legacy_methods(status, net); if (status) goto out;
do_init:
status = nn->client_tracking_ops->init(net);
out: if (status) {
pr_warn("NFSD: Unable to initialize client recovery tracking! (%d)\n", status);
pr_warn("NFSD: Is nfsdcld running? If not, enable CONFIG_NFSD_LEGACY_CLIENT_TRACKING.\n");
nn->client_tracking_ops = NULL;
} return status;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.