xref: /openbmc/linux/fs/nfsd/nfs4recover.c (revision ecc23d0a422a3118fcf6e4f0a46e17a6c2047b02)
1a55370a3SNeilBrown /*
2a55370a3SNeilBrown *  Copyright (c) 2004 The Regents of the University of Michigan.
3f3f80148SJeff Layton *  Copyright (c) 2012 Jeff Layton <jlayton@redhat.com>
4a55370a3SNeilBrown *  All rights reserved.
5a55370a3SNeilBrown *
6a55370a3SNeilBrown *  Andy Adamson <andros@citi.umich.edu>
7a55370a3SNeilBrown *
8a55370a3SNeilBrown *  Redistribution and use in source and binary forms, with or without
9a55370a3SNeilBrown *  modification, are permitted provided that the following conditions
10a55370a3SNeilBrown *  are met:
11a55370a3SNeilBrown *
12a55370a3SNeilBrown *  1. Redistributions of source code must retain the above copyright
13a55370a3SNeilBrown *     notice, this list of conditions and the following disclaimer.
14a55370a3SNeilBrown *  2. Redistributions in binary form must reproduce the above copyright
15a55370a3SNeilBrown *     notice, this list of conditions and the following disclaimer in the
16a55370a3SNeilBrown *     documentation and/or other materials provided with the distribution.
17a55370a3SNeilBrown *  3. Neither the name of the University nor the names of its
18a55370a3SNeilBrown *     contributors may be used to endorse or promote products derived
19a55370a3SNeilBrown *     from this software without specific prior written permission.
20a55370a3SNeilBrown *
21a55370a3SNeilBrown *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
22a55370a3SNeilBrown *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23a55370a3SNeilBrown *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24a55370a3SNeilBrown *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25a55370a3SNeilBrown *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26a55370a3SNeilBrown *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27a55370a3SNeilBrown *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28a55370a3SNeilBrown *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29a55370a3SNeilBrown *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30a55370a3SNeilBrown *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31a55370a3SNeilBrown *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32a55370a3SNeilBrown *
33a55370a3SNeilBrown */
34a55370a3SNeilBrown 
351edb82d2SHerbert Xu #include <crypto/hash.h>
36190e4fbfSNeilBrown #include <linux/file.h>
375a0e3ad6STejun Heo #include <linux/slab.h>
38190e4fbfSNeilBrown #include <linux/namei.h>
39e8edc6e0SAlexey Dobriyan #include <linux/sched.h>
40f3f80148SJeff Layton #include <linux/fs.h>
41813fd320SJeff Layton #include <linux/module.h>
42f3f80148SJeff Layton #include <net/net_namespace.h>
43f3f80148SJeff Layton #include <linux/sunrpc/rpc_pipe_fs.h>
44f3f80148SJeff Layton #include <linux/sunrpc/clnt.h>
45f3f80148SJeff Layton #include <linux/nfsd/cld.h>
469a74af21SBoaz Harrosh 
479a74af21SBoaz Harrosh #include "nfsd.h"
489a74af21SBoaz Harrosh #include "state.h"
490a3adadeSJ. Bruce Fields #include "vfs.h"
50f3f80148SJeff Layton #include "netns.h"
51a55370a3SNeilBrown 
52a55370a3SNeilBrown #define NFSDDBG_FACILITY                NFSDDBG_PROC
53a55370a3SNeilBrown 
542a4317c5SJeff Layton /* Declarations */
552a4317c5SJeff Layton struct nfsd4_client_tracking_ops {
562a4317c5SJeff Layton 	int (*init)(struct net *);
572a4317c5SJeff Layton 	void (*exit)(struct net *);
582a4317c5SJeff Layton 	void (*create)(struct nfs4_client *);
592a4317c5SJeff Layton 	void (*remove)(struct nfs4_client *);
602a4317c5SJeff Layton 	int (*check)(struct nfs4_client *);
61919b8049SJeff Layton 	void (*grace_done)(struct nfsd_net *);
6211a60d15SScott Mayhew 	uint8_t version;
6311a60d15SScott Mayhew 	size_t msglen;
642a4317c5SJeff Layton };
652a4317c5SJeff Layton 
6611a60d15SScott Mayhew static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops;
676ee95d1cSScott Mayhew static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops_v2;
6811a60d15SScott Mayhew 
69190e4fbfSNeilBrown /* Globals */
7048483bf2SJ. Bruce Fields static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery";
71190e4fbfSNeilBrown 
72d84f4f99SDavid Howells static int
nfs4_save_creds(const struct cred ** original_creds)73d84f4f99SDavid Howells nfs4_save_creds(const struct cred **original_creds)
74190e4fbfSNeilBrown {
75d84f4f99SDavid Howells 	struct cred *new;
76d84f4f99SDavid Howells 
77d84f4f99SDavid Howells 	new = prepare_creds();
78d84f4f99SDavid Howells 	if (!new)
79d84f4f99SDavid Howells 		return -ENOMEM;
80d84f4f99SDavid Howells 
816fab8779SEric W. Biederman 	new->fsuid = GLOBAL_ROOT_UID;
826fab8779SEric W. Biederman 	new->fsgid = GLOBAL_ROOT_GID;
83d84f4f99SDavid Howells 	*original_creds = override_creds(new);
84d84f4f99SDavid Howells 	put_cred(new);
85d84f4f99SDavid Howells 	return 0;
86190e4fbfSNeilBrown }
87190e4fbfSNeilBrown 
88190e4fbfSNeilBrown static void
nfs4_reset_creds(const struct cred * original)89d84f4f99SDavid Howells nfs4_reset_creds(const struct cred *original)
90190e4fbfSNeilBrown {
91d84f4f99SDavid Howells 	revert_creds(original);
92190e4fbfSNeilBrown }
93190e4fbfSNeilBrown 
94a55370a3SNeilBrown static void
md5_to_hex(char * out,char * md5)95a55370a3SNeilBrown md5_to_hex(char *out, char *md5)
96a55370a3SNeilBrown {
97a55370a3SNeilBrown 	int i;
98a55370a3SNeilBrown 
99a55370a3SNeilBrown 	for (i=0; i<16; i++) {
100a55370a3SNeilBrown 		unsigned char c = md5[i];
101a55370a3SNeilBrown 
102a55370a3SNeilBrown 		*out++ = '0' + ((c&0xf0)>>4) + (c>=0xa0)*('a'-'9'-1);
103a55370a3SNeilBrown 		*out++ = '0' + (c&0x0f) + ((c&0x0f)>=0x0a)*('a'-'9'-1);
104a55370a3SNeilBrown 	}
105a55370a3SNeilBrown 	*out = '\0';
106a55370a3SNeilBrown }
107a55370a3SNeilBrown 
1082216d449SJeff Layton static int
nfs4_make_rec_clidname(char * dname,const struct xdr_netobj * clname)1092216d449SJeff Layton nfs4_make_rec_clidname(char *dname, const struct xdr_netobj *clname)
110a55370a3SNeilBrown {
111a55370a3SNeilBrown 	struct xdr_netobj cksum;
1121edb82d2SHerbert Xu 	struct crypto_shash *tfm;
1132216d449SJeff Layton 	int status;
114a55370a3SNeilBrown 
115a55370a3SNeilBrown 	dprintk("NFSD: nfs4_make_rec_clidname for %.*s\n",
116a55370a3SNeilBrown 			clname->len, clname->data);
1171edb82d2SHerbert Xu 	tfm = crypto_alloc_shash("md5", 0, 0);
1181edb82d2SHerbert Xu 	if (IS_ERR(tfm)) {
1191edb82d2SHerbert Xu 		status = PTR_ERR(tfm);
12035058687SHerbert Xu 		goto out_no_tfm;
1212216d449SJeff Layton 	}
1222216d449SJeff Layton 
1231edb82d2SHerbert Xu 	cksum.len = crypto_shash_digestsize(tfm);
124a55370a3SNeilBrown 	cksum.data = kmalloc(cksum.len, GFP_KERNEL);
1252216d449SJeff Layton 	if (cksum.data == NULL) {
1262216d449SJeff Layton 		status = -ENOMEM;
127a55370a3SNeilBrown  		goto out;
1282216d449SJeff Layton 	}
129a55370a3SNeilBrown 
130ea794db2SEric Biggers 	status = crypto_shash_tfm_digest(tfm, clname->data, clname->len,
1311edb82d2SHerbert Xu 					 cksum.data);
1322216d449SJeff Layton 	if (status)
13335058687SHerbert Xu 		goto out;
134a55370a3SNeilBrown 
135a55370a3SNeilBrown 	md5_to_hex(dname, cksum.data);
136a55370a3SNeilBrown 
1372216d449SJeff Layton 	status = 0;
138a55370a3SNeilBrown out:
1392bd9e7b6SKrishna Kumar 	kfree(cksum.data);
1401edb82d2SHerbert Xu 	crypto_free_shash(tfm);
14135058687SHerbert Xu out_no_tfm:
142a55370a3SNeilBrown 	return status;
143a55370a3SNeilBrown }
144190e4fbfSNeilBrown 
1452216d449SJeff Layton /*
1462216d449SJeff Layton  * If we had an error generating the recdir name for the legacy tracker
1472216d449SJeff Layton  * then warn the admin. If the error doesn't appear to be transient,
1482216d449SJeff Layton  * then disable recovery tracking.
1492216d449SJeff Layton  */
1502216d449SJeff Layton static void
legacy_recdir_name_error(struct nfs4_client * clp,int error)1517255e716SJeff Layton legacy_recdir_name_error(struct nfs4_client *clp, int error)
1522216d449SJeff Layton {
1532216d449SJeff Layton 	printk(KERN_ERR "NFSD: unable to generate recoverydir "
1542216d449SJeff Layton 			"name (%d).\n", error);
1552216d449SJeff Layton 
1562216d449SJeff Layton 	/*
1572216d449SJeff Layton 	 * if the algorithm just doesn't exist, then disable the recovery
1582216d449SJeff Layton 	 * tracker altogether. The crypto libs will generally return this if
1592216d449SJeff Layton 	 * FIPS is enabled as well.
1602216d449SJeff Layton 	 */
1612216d449SJeff Layton 	if (error == -ENOENT) {
1622216d449SJeff Layton 		printk(KERN_ERR "NFSD: disabling legacy clientid tracking. "
1632216d449SJeff Layton 			"Reboot recovery will not function correctly!\n");
1647255e716SJeff Layton 		nfsd4_client_tracking_exit(clp->net);
1652216d449SJeff Layton 	}
1662216d449SJeff Layton }
1672216d449SJeff Layton 
1682a4317c5SJeff Layton static void
__nfsd4_create_reclaim_record_grace(struct nfs4_client * clp,const char * dname,int len,struct nfsd_net * nn)1696b189105SScott Mayhew __nfsd4_create_reclaim_record_grace(struct nfs4_client *clp,
1706b189105SScott Mayhew 		const char *dname, int len, struct nfsd_net *nn)
1716b189105SScott Mayhew {
1726b189105SScott Mayhew 	struct xdr_netobj name;
1736ee95d1cSScott Mayhew 	struct xdr_netobj princhash = { .len = 0, .data = NULL };
1746b189105SScott Mayhew 	struct nfs4_client_reclaim *crp;
1756b189105SScott Mayhew 
1766b189105SScott Mayhew 	name.data = kmemdup(dname, len, GFP_KERNEL);
1776b189105SScott Mayhew 	if (!name.data) {
1786b189105SScott Mayhew 		dprintk("%s: failed to allocate memory for name.data!\n",
1796b189105SScott Mayhew 			__func__);
1806b189105SScott Mayhew 		return;
1816b189105SScott Mayhew 	}
1826b189105SScott Mayhew 	name.len = len;
1836ee95d1cSScott Mayhew 	crp = nfs4_client_to_reclaim(name, princhash, nn);
1846b189105SScott Mayhew 	if (!crp) {
1856b189105SScott Mayhew 		kfree(name.data);
1866b189105SScott Mayhew 		return;
1876b189105SScott Mayhew 	}
1886b189105SScott Mayhew 	crp->cr_clp = clp;
1896b189105SScott Mayhew }
1906b189105SScott Mayhew 
1916b189105SScott Mayhew static void
nfsd4_create_clid_dir(struct nfs4_client * clp)1922a4317c5SJeff Layton nfsd4_create_clid_dir(struct nfs4_client *clp)
193c7b9a459SNeilBrown {
194d84f4f99SDavid Howells 	const struct cred *original_cred;
1952216d449SJeff Layton 	char dname[HEXDIR_LEN];
196e970a573SChristoph Hellwig 	struct dentry *dir, *dentry;
197c7b9a459SNeilBrown 	int status;
19852e19c09SStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
199c7b9a459SNeilBrown 
200a52d726bSJeff Layton 	if (test_and_set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
2017a6ef8c7SJ. Bruce Fields 		return;
2023a073369SStanislav Kinsbursky 	if (!nn->rec_file)
2037a6ef8c7SJ. Bruce Fields 		return;
2042216d449SJeff Layton 
2052216d449SJeff Layton 	status = nfs4_make_rec_clidname(dname, &clp->cl_name);
2062216d449SJeff Layton 	if (status)
2077255e716SJeff Layton 		return legacy_recdir_name_error(clp, status);
2082216d449SJeff Layton 
209d84f4f99SDavid Howells 	status = nfs4_save_creds(&original_cred);
210d84f4f99SDavid Howells 	if (status < 0)
2117a6ef8c7SJ. Bruce Fields 		return;
212c7b9a459SNeilBrown 
2133a073369SStanislav Kinsbursky 	status = mnt_want_write_file(nn->rec_file);
2144a55c101SJan Kara 	if (status)
215c2236f14SKinglong Mee 		goto out_creds;
2164a55c101SJan Kara 
2173a073369SStanislav Kinsbursky 	dir = nn->rec_file->f_path.dentry;
218c7b9a459SNeilBrown 	/* lock the parent */
2195955102cSAl Viro 	inode_lock(d_inode(dir));
220c7b9a459SNeilBrown 
221e970a573SChristoph Hellwig 	dentry = lookup_one_len(dname, dir, HEXDIR_LEN-1);
222c7b9a459SNeilBrown 	if (IS_ERR(dentry)) {
223c7b9a459SNeilBrown 		status = PTR_ERR(dentry);
224c7b9a459SNeilBrown 		goto out_unlock;
225c7b9a459SNeilBrown 	}
2262b0143b5SDavid Howells 	if (d_really_is_positive(dentry))
227aec39680SJ. Bruce Fields 		/*
228aec39680SJ. Bruce Fields 		 * In the 4.1 case, where we're called from
229aec39680SJ. Bruce Fields 		 * reclaim_complete(), records from the previous reboot
230aec39680SJ. Bruce Fields 		 * may still be left, so this is OK.
231aec39680SJ. Bruce Fields 		 *
232aec39680SJ. Bruce Fields 		 * In the 4.0 case, we should never get here; but we may
233aec39680SJ. Bruce Fields 		 * as well be forgiving and just succeed silently.
234aec39680SJ. Bruce Fields 		 */
235c7b9a459SNeilBrown 		goto out_put;
236abf08576SChristian Brauner 	status = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), dentry, S_IRWXU);
237c7b9a459SNeilBrown out_put:
238c7b9a459SNeilBrown 	dput(dentry);
239c7b9a459SNeilBrown out_unlock:
2405955102cSAl Viro 	inode_unlock(d_inode(dir));
2410ce0c2b5SJeff Layton 	if (status == 0) {
2426b189105SScott Mayhew 		if (nn->in_grace)
2436b189105SScott Mayhew 			__nfsd4_create_reclaim_record_grace(clp, dname,
2446b189105SScott Mayhew 					HEXDIR_LEN, nn);
2453a073369SStanislav Kinsbursky 		vfs_fsync(nn->rec_file, 0);
2460ce0c2b5SJeff Layton 	} else {
2476577aac0SBoaz Harrosh 		printk(KERN_ERR "NFSD: failed to write recovery record"
2486577aac0SBoaz Harrosh 				" (err %d); please check that %s exists"
2496577aac0SBoaz Harrosh 				" and is writeable", status,
2506577aac0SBoaz Harrosh 				user_recovery_dirname);
2510ce0c2b5SJeff Layton 	}
2523a073369SStanislav Kinsbursky 	mnt_drop_write_file(nn->rec_file);
253c2236f14SKinglong Mee out_creds:
254d84f4f99SDavid Howells 	nfs4_reset_creds(original_cred);
255c7b9a459SNeilBrown }
256c7b9a459SNeilBrown 
25752e19c09SStanislav Kinsbursky typedef int (recdir_func)(struct dentry *, struct dentry *, struct nfsd_net *);
258190e4fbfSNeilBrown 
25905f4f678SJ. Bruce Fields struct name_list {
26005f4f678SJ. Bruce Fields 	char name[HEXDIR_LEN];
261190e4fbfSNeilBrown 	struct list_head list;
262190e4fbfSNeilBrown };
263190e4fbfSNeilBrown 
264bb6f619bSAl Viro struct nfs4_dir_ctx {
265bb6f619bSAl Viro 	struct dir_context ctx;
266bb6f619bSAl Viro 	struct list_head names;
267bb6f619bSAl Viro };
268bb6f619bSAl Viro 
26925885a35SAl Viro static bool
nfsd4_build_namelist(struct dir_context * __ctx,const char * name,int namlen,loff_t offset,u64 ino,unsigned int d_type)270ac7576f4SMiklos Szeredi nfsd4_build_namelist(struct dir_context *__ctx, const char *name, int namlen,
271afefdbb2SDavid Howells 		loff_t offset, u64 ino, unsigned int d_type)
272190e4fbfSNeilBrown {
273ac7576f4SMiklos Szeredi 	struct nfs4_dir_ctx *ctx =
274ac7576f4SMiklos Szeredi 		container_of(__ctx, struct nfs4_dir_ctx, ctx);
27505f4f678SJ. Bruce Fields 	struct name_list *entry;
276190e4fbfSNeilBrown 
27705f4f678SJ. Bruce Fields 	if (namlen != HEXDIR_LEN - 1)
27825885a35SAl Viro 		return true;
27905f4f678SJ. Bruce Fields 	entry = kmalloc(sizeof(struct name_list), GFP_KERNEL);
28005f4f678SJ. Bruce Fields 	if (entry == NULL)
28125885a35SAl Viro 		return false;
28205f4f678SJ. Bruce Fields 	memcpy(entry->name, name, HEXDIR_LEN - 1);
28305f4f678SJ. Bruce Fields 	entry->name[HEXDIR_LEN - 1] = '\0';
284bb6f619bSAl Viro 	list_add(&entry->list, &ctx->names);
28525885a35SAl Viro 	return true;
286190e4fbfSNeilBrown }
287190e4fbfSNeilBrown 
288190e4fbfSNeilBrown static int
nfsd4_list_rec_dir(recdir_func * f,struct nfsd_net * nn)28952e19c09SStanislav Kinsbursky nfsd4_list_rec_dir(recdir_func *f, struct nfsd_net *nn)
290190e4fbfSNeilBrown {
291d84f4f99SDavid Howells 	const struct cred *original_cred;
2923a073369SStanislav Kinsbursky 	struct dentry *dir = nn->rec_file->f_path.dentry;
293ac6614b7SAl Viro 	struct nfs4_dir_ctx ctx = {
294ac6614b7SAl Viro 		.ctx.actor = nfsd4_build_namelist,
295ac6614b7SAl Viro 		.names = LIST_HEAD_INIT(ctx.names)
296ac6614b7SAl Viro 	};
2974691b271SKinglong Mee 	struct name_list *entry, *tmp;
298190e4fbfSNeilBrown 	int status;
299190e4fbfSNeilBrown 
300d84f4f99SDavid Howells 	status = nfs4_save_creds(&original_cred);
301d84f4f99SDavid Howells 	if (status < 0)
302d84f4f99SDavid Howells 		return status;
303190e4fbfSNeilBrown 
3043a073369SStanislav Kinsbursky 	status = vfs_llseek(nn->rec_file, 0, SEEK_SET);
3055b4b299cSAl Viro 	if (status < 0) {
3065b4b299cSAl Viro 		nfs4_reset_creds(original_cred);
3075b4b299cSAl Viro 		return status;
3085b4b299cSAl Viro 	}
3095b4b299cSAl Viro 
3105c0ba4e0SAl Viro 	status = iterate_dir(nn->rec_file, &ctx.ctx);
3115955102cSAl Viro 	inode_lock_nested(d_inode(dir), I_MUTEX_PARENT);
3124691b271SKinglong Mee 
3134691b271SKinglong Mee 	list_for_each_entry_safe(entry, tmp, &ctx.names, list) {
3145b4b299cSAl Viro 		if (!status) {
3155b4b299cSAl Viro 			struct dentry *dentry;
31605f4f678SJ. Bruce Fields 			dentry = lookup_one_len(entry->name, dir, HEXDIR_LEN-1);
31705f4f678SJ. Bruce Fields 			if (IS_ERR(dentry)) {
31805f4f678SJ. Bruce Fields 				status = PTR_ERR(dentry);
3192f9092e1SDavid Woodhouse 				break;
32005f4f678SJ. Bruce Fields 			}
32152e19c09SStanislav Kinsbursky 			status = f(dir, dentry, nn);
32205f4f678SJ. Bruce Fields 			dput(dentry);
3235b4b299cSAl Viro 		}
32405f4f678SJ. Bruce Fields 		list_del(&entry->list);
32505f4f678SJ. Bruce Fields 		kfree(entry);
326190e4fbfSNeilBrown 	}
3275955102cSAl Viro 	inode_unlock(d_inode(dir));
328d84f4f99SDavid Howells 	nfs4_reset_creds(original_cred);
3294691b271SKinglong Mee 
3304691b271SKinglong Mee 	list_for_each_entry_safe(entry, tmp, &ctx.names, list) {
3314691b271SKinglong Mee 		dprintk("NFSD: %s. Left entry %s\n", __func__, entry->name);
3324691b271SKinglong Mee 		list_del(&entry->list);
3334691b271SKinglong Mee 		kfree(entry);
3344691b271SKinglong Mee 	}
335190e4fbfSNeilBrown 	return status;
336190e4fbfSNeilBrown }
337190e4fbfSNeilBrown 
338190e4fbfSNeilBrown static int
nfsd4_unlink_clid_dir(char * name,int namlen,struct nfsd_net * nn)3393a073369SStanislav Kinsbursky nfsd4_unlink_clid_dir(char *name, int namlen, struct nfsd_net *nn)
340c7b9a459SNeilBrown {
341e970a573SChristoph Hellwig 	struct dentry *dir, *dentry;
342c7b9a459SNeilBrown 	int status;
343c7b9a459SNeilBrown 
344c7b9a459SNeilBrown 	dprintk("NFSD: nfsd4_unlink_clid_dir. name %.*s\n", namlen, name);
345c7b9a459SNeilBrown 
3463a073369SStanislav Kinsbursky 	dir = nn->rec_file->f_path.dentry;
3475955102cSAl Viro 	inode_lock_nested(d_inode(dir), I_MUTEX_PARENT);
348e970a573SChristoph Hellwig 	dentry = lookup_one_len(name, dir, namlen);
349c7b9a459SNeilBrown 	if (IS_ERR(dentry)) {
350c7b9a459SNeilBrown 		status = PTR_ERR(dentry);
3512f9092e1SDavid Woodhouse 		goto out_unlock;
352c7b9a459SNeilBrown 	}
353c7b9a459SNeilBrown 	status = -ENOENT;
3542b0143b5SDavid Howells 	if (d_really_is_negative(dentry))
355c7b9a459SNeilBrown 		goto out;
356abf08576SChristian Brauner 	status = vfs_rmdir(&nop_mnt_idmap, d_inode(dir), dentry);
357c7b9a459SNeilBrown out:
358c7b9a459SNeilBrown 	dput(dentry);
3592f9092e1SDavid Woodhouse out_unlock:
3605955102cSAl Viro 	inode_unlock(d_inode(dir));
361c7b9a459SNeilBrown 	return status;
362c7b9a459SNeilBrown }
363c7b9a459SNeilBrown 
3642a4317c5SJeff Layton static void
__nfsd4_remove_reclaim_record_grace(const char * dname,int len,struct nfsd_net * nn)3656b189105SScott Mayhew __nfsd4_remove_reclaim_record_grace(const char *dname, int len,
3666b189105SScott Mayhew 		struct nfsd_net *nn)
3676b189105SScott Mayhew {
3686b189105SScott Mayhew 	struct xdr_netobj name;
3696b189105SScott Mayhew 	struct nfs4_client_reclaim *crp;
3706b189105SScott Mayhew 
3716b189105SScott Mayhew 	name.data = kmemdup(dname, len, GFP_KERNEL);
3726b189105SScott Mayhew 	if (!name.data) {
3736b189105SScott Mayhew 		dprintk("%s: failed to allocate memory for name.data!\n",
3746b189105SScott Mayhew 			__func__);
3756b189105SScott Mayhew 		return;
3766b189105SScott Mayhew 	}
3776b189105SScott Mayhew 	name.len = len;
3786b189105SScott Mayhew 	crp = nfsd4_find_reclaim_client(name, nn);
3796b189105SScott Mayhew 	kfree(name.data);
3806b189105SScott Mayhew 	if (crp)
3816b189105SScott Mayhew 		nfs4_remove_reclaim_record(crp, nn);
3826b189105SScott Mayhew }
3836b189105SScott Mayhew 
3846b189105SScott Mayhew static void
nfsd4_remove_clid_dir(struct nfs4_client * clp)385c7b9a459SNeilBrown nfsd4_remove_clid_dir(struct nfs4_client *clp)
386c7b9a459SNeilBrown {
387d84f4f99SDavid Howells 	const struct cred *original_cred;
3882216d449SJeff Layton 	char dname[HEXDIR_LEN];
389c7b9a459SNeilBrown 	int status;
39052e19c09SStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
391c7b9a459SNeilBrown 
3923a073369SStanislav Kinsbursky 	if (!nn->rec_file || !test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
393c7b9a459SNeilBrown 		return;
394c7b9a459SNeilBrown 
3952216d449SJeff Layton 	status = nfs4_make_rec_clidname(dname, &clp->cl_name);
3962216d449SJeff Layton 	if (status)
3977255e716SJeff Layton 		return legacy_recdir_name_error(clp, status);
3982216d449SJeff Layton 
3993a073369SStanislav Kinsbursky 	status = mnt_want_write_file(nn->rec_file);
4000622753bSDave Hansen 	if (status)
4010622753bSDave Hansen 		goto out;
402a52d726bSJeff Layton 	clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
403d84f4f99SDavid Howells 
404d84f4f99SDavid Howells 	status = nfs4_save_creds(&original_cred);
405d84f4f99SDavid Howells 	if (status < 0)
406698d8d87SJeff Layton 		goto out_drop_write;
407d84f4f99SDavid Howells 
4083a073369SStanislav Kinsbursky 	status = nfsd4_unlink_clid_dir(dname, HEXDIR_LEN-1, nn);
409d84f4f99SDavid Howells 	nfs4_reset_creds(original_cred);
4100ce0c2b5SJeff Layton 	if (status == 0) {
4113a073369SStanislav Kinsbursky 		vfs_fsync(nn->rec_file, 0);
4126b189105SScott Mayhew 		if (nn->in_grace)
4136b189105SScott Mayhew 			__nfsd4_remove_reclaim_record_grace(dname,
4146b189105SScott Mayhew 					HEXDIR_LEN, nn);
4150ce0c2b5SJeff Layton 	}
416698d8d87SJeff Layton out_drop_write:
4173a073369SStanislav Kinsbursky 	mnt_drop_write_file(nn->rec_file);
4180622753bSDave Hansen out:
419c7b9a459SNeilBrown 	if (status)
420c7b9a459SNeilBrown 		printk("NFSD: Failed to remove expired client state directory"
4212216d449SJeff Layton 				" %.*s\n", HEXDIR_LEN, dname);
422c7b9a459SNeilBrown }
423c7b9a459SNeilBrown 
424c7b9a459SNeilBrown static int
purge_old(struct dentry * parent,struct dentry * child,struct nfsd_net * nn)42552e19c09SStanislav Kinsbursky purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn)
426c7b9a459SNeilBrown {
427c7b9a459SNeilBrown 	int status;
4286b189105SScott Mayhew 	struct xdr_netobj name;
429c7b9a459SNeilBrown 
4306b189105SScott Mayhew 	if (child->d_name.len != HEXDIR_LEN - 1) {
4316b189105SScott Mayhew 		printk("%s: illegal name %pd in recovery directory\n",
4326b189105SScott Mayhew 				__func__, child);
4336b189105SScott Mayhew 		/* Keep trying; maybe the others are OK: */
434b37ad28bSAl Viro 		return 0;
4356b189105SScott Mayhew 	}
4366b189105SScott Mayhew 	name.data = kmemdup_nul(child->d_name.name, child->d_name.len, GFP_KERNEL);
4376b189105SScott Mayhew 	if (!name.data) {
4386b189105SScott Mayhew 		dprintk("%s: failed to allocate memory for name.data!\n",
4396b189105SScott Mayhew 			__func__);
4406b189105SScott Mayhew 		goto out;
4416b189105SScott Mayhew 	}
4426b189105SScott Mayhew 	name.len = HEXDIR_LEN;
4436b189105SScott Mayhew 	if (nfs4_has_reclaimed_state(name, nn))
4446b189105SScott Mayhew 		goto out_free;
445c7b9a459SNeilBrown 
446abf08576SChristian Brauner 	status = vfs_rmdir(&nop_mnt_idmap, d_inode(parent), child);
447c7b9a459SNeilBrown 	if (status)
448a6a9f18fSAl Viro 		printk("failed to remove client recovery directory %pd\n",
449a6a9f18fSAl Viro 				child);
4506b189105SScott Mayhew out_free:
4516b189105SScott Mayhew 	kfree(name.data);
4526b189105SScott Mayhew out:
453c7b9a459SNeilBrown 	/* Keep trying, success or failure: */
454b37ad28bSAl Viro 	return 0;
455c7b9a459SNeilBrown }
456c7b9a459SNeilBrown 
4572a4317c5SJeff Layton static void
nfsd4_recdir_purge_old(struct nfsd_net * nn)458919b8049SJeff Layton nfsd4_recdir_purge_old(struct nfsd_net *nn)
4592a4317c5SJeff Layton {
460c7b9a459SNeilBrown 	int status;
461c7b9a459SNeilBrown 
462f141f79dSStanislav Kinsbursky 	nn->in_grace = false;
4633a073369SStanislav Kinsbursky 	if (!nn->rec_file)
464c7b9a459SNeilBrown 		return;
4653a073369SStanislav Kinsbursky 	status = mnt_want_write_file(nn->rec_file);
4660622753bSDave Hansen 	if (status)
4670622753bSDave Hansen 		goto out;
46852e19c09SStanislav Kinsbursky 	status = nfsd4_list_rec_dir(purge_old, nn);
469c7b9a459SNeilBrown 	if (status == 0)
4703a073369SStanislav Kinsbursky 		vfs_fsync(nn->rec_file, 0);
4713a073369SStanislav Kinsbursky 	mnt_drop_write_file(nn->rec_file);
4720622753bSDave Hansen out:
47352e19c09SStanislav Kinsbursky 	nfs4_release_reclaim(nn);
474c7b9a459SNeilBrown 	if (status)
475c7b9a459SNeilBrown 		printk("nfsd4: failed to purge old clients from recovery"
476a6a9f18fSAl Viro 			" directory %pD\n", nn->rec_file);
477c7b9a459SNeilBrown }
478c7b9a459SNeilBrown 
479c7b9a459SNeilBrown static int
load_recdir(struct dentry * parent,struct dentry * child,struct nfsd_net * nn)48052e19c09SStanislav Kinsbursky load_recdir(struct dentry *parent, struct dentry *child, struct nfsd_net *nn)
481190e4fbfSNeilBrown {
4826b189105SScott Mayhew 	struct xdr_netobj name;
4836ee95d1cSScott Mayhew 	struct xdr_netobj princhash = { .len = 0, .data = NULL };
4846b189105SScott Mayhew 
485190e4fbfSNeilBrown 	if (child->d_name.len != HEXDIR_LEN - 1) {
4866b189105SScott Mayhew 		printk("%s: illegal name %pd in recovery directory\n",
4876b189105SScott Mayhew 				__func__, child);
488190e4fbfSNeilBrown 		/* Keep trying; maybe the others are OK: */
489b37ad28bSAl Viro 		return 0;
490190e4fbfSNeilBrown 	}
4916b189105SScott Mayhew 	name.data = kmemdup_nul(child->d_name.name, child->d_name.len, GFP_KERNEL);
4926b189105SScott Mayhew 	if (!name.data) {
4936b189105SScott Mayhew 		dprintk("%s: failed to allocate memory for name.data!\n",
4946b189105SScott Mayhew 			__func__);
4956b189105SScott Mayhew 		goto out;
4966b189105SScott Mayhew 	}
4976b189105SScott Mayhew 	name.len = HEXDIR_LEN;
4986ee95d1cSScott Mayhew 	if (!nfs4_client_to_reclaim(name, princhash, nn))
4996b189105SScott Mayhew 		kfree(name.data);
5006b189105SScott Mayhew out:
501b37ad28bSAl Viro 	return 0;
502190e4fbfSNeilBrown }
503190e4fbfSNeilBrown 
5042a4317c5SJeff Layton static int
nfsd4_recdir_load(struct net * net)50552e19c09SStanislav Kinsbursky nfsd4_recdir_load(struct net *net) {
506190e4fbfSNeilBrown 	int status;
50752e19c09SStanislav Kinsbursky 	struct nfsd_net *nn =  net_generic(net, nfsd_net_id);
508190e4fbfSNeilBrown 
5093a073369SStanislav Kinsbursky 	if (!nn->rec_file)
510e970a573SChristoph Hellwig 		return 0;
511e970a573SChristoph Hellwig 
51252e19c09SStanislav Kinsbursky 	status = nfsd4_list_rec_dir(load_recdir, nn);
513190e4fbfSNeilBrown 	if (status)
514190e4fbfSNeilBrown 		printk("nfsd4: failed loading clients from recovery"
515a6a9f18fSAl Viro 			" directory %pD\n", nn->rec_file);
516190e4fbfSNeilBrown 	return status;
517190e4fbfSNeilBrown }
518190e4fbfSNeilBrown 
519190e4fbfSNeilBrown /*
520190e4fbfSNeilBrown  * Hold reference to the recovery directory.
521190e4fbfSNeilBrown  */
522190e4fbfSNeilBrown 
5232a4317c5SJeff Layton static int
nfsd4_init_recdir(struct net * net)5243a073369SStanislav Kinsbursky nfsd4_init_recdir(struct net *net)
525190e4fbfSNeilBrown {
5263a073369SStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
527d84f4f99SDavid Howells 	const struct cred *original_cred;
528190e4fbfSNeilBrown 	int status;
529190e4fbfSNeilBrown 
530190e4fbfSNeilBrown 	printk("NFSD: Using %s as the NFSv4 state recovery directory\n",
53148483bf2SJ. Bruce Fields 			user_recovery_dirname);
532190e4fbfSNeilBrown 
5333a073369SStanislav Kinsbursky 	BUG_ON(nn->rec_file);
534190e4fbfSNeilBrown 
535d84f4f99SDavid Howells 	status = nfs4_save_creds(&original_cred);
536d84f4f99SDavid Howells 	if (status < 0) {
537d84f4f99SDavid Howells 		printk("NFSD: Unable to change credentials to find recovery"
538d84f4f99SDavid Howells 		       " directory: error %d\n",
539d84f4f99SDavid Howells 		       status);
5402a4317c5SJeff Layton 		return status;
541d84f4f99SDavid Howells 	}
542190e4fbfSNeilBrown 
5433a073369SStanislav Kinsbursky 	nn->rec_file = filp_open(user_recovery_dirname, O_RDONLY | O_DIRECTORY, 0);
5443a073369SStanislav Kinsbursky 	if (IS_ERR(nn->rec_file)) {
545c2642ab0SJ. Bruce Fields 		printk("NFSD: unable to find recovery directory %s\n",
54648483bf2SJ. Bruce Fields 				user_recovery_dirname);
5473a073369SStanislav Kinsbursky 		status = PTR_ERR(nn->rec_file);
5483a073369SStanislav Kinsbursky 		nn->rec_file = NULL;
549e970a573SChristoph Hellwig 	}
550190e4fbfSNeilBrown 
551d84f4f99SDavid Howells 	nfs4_reset_creds(original_cred);
5520ce0c2b5SJeff Layton 	if (!status)
553f141f79dSStanislav Kinsbursky 		nn->in_grace = true;
5542a4317c5SJeff Layton 	return status;
555190e4fbfSNeilBrown }
556190e4fbfSNeilBrown 
55715d176c1SKinglong Mee static void
nfsd4_shutdown_recdir(struct net * net)55815d176c1SKinglong Mee nfsd4_shutdown_recdir(struct net *net)
55915d176c1SKinglong Mee {
56015d176c1SKinglong Mee 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
56115d176c1SKinglong Mee 
56215d176c1SKinglong Mee 	if (!nn->rec_file)
56315d176c1SKinglong Mee 		return;
56415d176c1SKinglong Mee 	fput(nn->rec_file);
56515d176c1SKinglong Mee 	nn->rec_file = NULL;
56615d176c1SKinglong Mee }
56752e19c09SStanislav Kinsbursky 
56852e19c09SStanislav Kinsbursky static int
nfs4_legacy_state_init(struct net * net)56952e19c09SStanislav Kinsbursky nfs4_legacy_state_init(struct net *net)
57052e19c09SStanislav Kinsbursky {
57152e19c09SStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
57252e19c09SStanislav Kinsbursky 	int i;
57352e19c09SStanislav Kinsbursky 
5746da2ec56SKees Cook 	nn->reclaim_str_hashtbl = kmalloc_array(CLIENT_HASH_SIZE,
5756da2ec56SKees Cook 						sizeof(struct list_head),
5766da2ec56SKees Cook 						GFP_KERNEL);
57752e19c09SStanislav Kinsbursky 	if (!nn->reclaim_str_hashtbl)
57852e19c09SStanislav Kinsbursky 		return -ENOMEM;
57952e19c09SStanislav Kinsbursky 
58052e19c09SStanislav Kinsbursky 	for (i = 0; i < CLIENT_HASH_SIZE; i++)
58152e19c09SStanislav Kinsbursky 		INIT_LIST_HEAD(&nn->reclaim_str_hashtbl[i]);
58252e19c09SStanislav Kinsbursky 	nn->reclaim_str_hashtbl_size = 0;
58352e19c09SStanislav Kinsbursky 
58452e19c09SStanislav Kinsbursky 	return 0;
58552e19c09SStanislav Kinsbursky }
58652e19c09SStanislav Kinsbursky 
58752e19c09SStanislav Kinsbursky static void
nfs4_legacy_state_shutdown(struct net * net)58852e19c09SStanislav Kinsbursky nfs4_legacy_state_shutdown(struct net *net)
58952e19c09SStanislav Kinsbursky {
59052e19c09SStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
59152e19c09SStanislav Kinsbursky 
59252e19c09SStanislav Kinsbursky 	kfree(nn->reclaim_str_hashtbl);
59352e19c09SStanislav Kinsbursky }
59452e19c09SStanislav Kinsbursky 
5952a4317c5SJeff Layton static int
nfsd4_load_reboot_recovery_data(struct net * net)5962a4317c5SJeff Layton nfsd4_load_reboot_recovery_data(struct net *net)
5972a4317c5SJeff Layton {
5982a4317c5SJeff Layton 	int status;
5992a4317c5SJeff Layton 
6003a073369SStanislav Kinsbursky 	status = nfsd4_init_recdir(net);
60115d176c1SKinglong Mee 	if (status)
60215d176c1SKinglong Mee 		return status;
60315d176c1SKinglong Mee 
60452e19c09SStanislav Kinsbursky 	status = nfsd4_recdir_load(net);
60552e19c09SStanislav Kinsbursky 	if (status)
60615d176c1SKinglong Mee 		nfsd4_shutdown_recdir(net);
60715d176c1SKinglong Mee 
60852e19c09SStanislav Kinsbursky 	return status;
60952e19c09SStanislav Kinsbursky }
61052e19c09SStanislav Kinsbursky 
61152e19c09SStanislav Kinsbursky static int
nfsd4_legacy_tracking_init(struct net * net)61252e19c09SStanislav Kinsbursky nfsd4_legacy_tracking_init(struct net *net)
61352e19c09SStanislav Kinsbursky {
61452e19c09SStanislav Kinsbursky 	int status;
61552e19c09SStanislav Kinsbursky 
616cc27e0d4SJeff Layton 	/* XXX: The legacy code won't work in a container */
617cc27e0d4SJeff Layton 	if (net != &init_net) {
61846cc8ba3SPaul Gortmaker 		pr_warn("NFSD: attempt to initialize legacy client tracking in a container ignored.\n");
619cc27e0d4SJeff Layton 		return -EINVAL;
620cc27e0d4SJeff Layton 	}
621cc27e0d4SJeff Layton 
62252e19c09SStanislav Kinsbursky 	status = nfs4_legacy_state_init(net);
6232a4317c5SJeff Layton 	if (status)
62452e19c09SStanislav Kinsbursky 		return status;
62552e19c09SStanislav Kinsbursky 
62652e19c09SStanislav Kinsbursky 	status = nfsd4_load_reboot_recovery_data(net);
62752e19c09SStanislav Kinsbursky 	if (status)
62852e19c09SStanislav Kinsbursky 		goto err;
629f988a7b7SPaul Menzel 	pr_info("NFSD: Using legacy client tracking operations.\n");
63052e19c09SStanislav Kinsbursky 	return 0;
63152e19c09SStanislav Kinsbursky 
63252e19c09SStanislav Kinsbursky err:
63352e19c09SStanislav Kinsbursky 	nfs4_legacy_state_shutdown(net);
6342a4317c5SJeff Layton 	return status;
6352a4317c5SJeff Layton }
6362a4317c5SJeff Layton 
6372a4317c5SJeff Layton static void
nfsd4_legacy_tracking_exit(struct net * net)6382a4317c5SJeff Layton nfsd4_legacy_tracking_exit(struct net *net)
6392a4317c5SJeff Layton {
64052e19c09SStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
64152e19c09SStanislav Kinsbursky 
64252e19c09SStanislav Kinsbursky 	nfs4_release_reclaim(nn);
64315d176c1SKinglong Mee 	nfsd4_shutdown_recdir(net);
64452e19c09SStanislav Kinsbursky 	nfs4_legacy_state_shutdown(net);
6452a4317c5SJeff Layton }
6462a4317c5SJeff Layton 
64748483bf2SJ. Bruce Fields /*
64848483bf2SJ. Bruce Fields  * Change the NFSv4 recovery directory to recdir.
64948483bf2SJ. Bruce Fields  */
65048483bf2SJ. Bruce Fields int
nfs4_reset_recoverydir(char * recdir)65148483bf2SJ. Bruce Fields nfs4_reset_recoverydir(char *recdir)
65248483bf2SJ. Bruce Fields {
65348483bf2SJ. Bruce Fields 	int status;
65448483bf2SJ. Bruce Fields 	struct path path;
65548483bf2SJ. Bruce Fields 
65648483bf2SJ. Bruce Fields 	status = kern_path(recdir, LOOKUP_FOLLOW, &path);
65748483bf2SJ. Bruce Fields 	if (status)
65848483bf2SJ. Bruce Fields 		return status;
65948483bf2SJ. Bruce Fields 	status = -ENOTDIR;
660e36cb0b8SDavid Howells 	if (d_is_dir(path.dentry)) {
661*02841754SChuck Lever 		strscpy(user_recovery_dirname, recdir,
662*02841754SChuck Lever 			sizeof(user_recovery_dirname));
66348483bf2SJ. Bruce Fields 		status = 0;
66448483bf2SJ. Bruce Fields 	}
66548483bf2SJ. Bruce Fields 	path_put(&path);
66648483bf2SJ. Bruce Fields 	return status;
66748483bf2SJ. Bruce Fields }
66848483bf2SJ. Bruce Fields 
66948483bf2SJ. Bruce Fields char *
nfs4_recoverydir(void)67048483bf2SJ. Bruce Fields nfs4_recoverydir(void)
67148483bf2SJ. Bruce Fields {
67248483bf2SJ. Bruce Fields 	return user_recovery_dirname;
67348483bf2SJ. Bruce Fields }
6742a4317c5SJeff Layton 
6752a4317c5SJeff Layton static int
nfsd4_check_legacy_client(struct nfs4_client * clp)6762a4317c5SJeff Layton nfsd4_check_legacy_client(struct nfs4_client *clp)
6772a4317c5SJeff Layton {
6782216d449SJeff Layton 	int status;
6792216d449SJeff Layton 	char dname[HEXDIR_LEN];
6800ce0c2b5SJeff Layton 	struct nfs4_client_reclaim *crp;
68152e19c09SStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
6826b189105SScott Mayhew 	struct xdr_netobj name;
6830ce0c2b5SJeff Layton 
6842a4317c5SJeff Layton 	/* did we already find that this client is stable? */
6852a4317c5SJeff Layton 	if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
6862a4317c5SJeff Layton 		return 0;
6872a4317c5SJeff Layton 
6882216d449SJeff Layton 	status = nfs4_make_rec_clidname(dname, &clp->cl_name);
6892216d449SJeff Layton 	if (status) {
6907255e716SJeff Layton 		legacy_recdir_name_error(clp, status);
6912216d449SJeff Layton 		return status;
6922216d449SJeff Layton 	}
6932216d449SJeff Layton 
6942a4317c5SJeff Layton 	/* look for it in the reclaim hashtable otherwise */
6956b189105SScott Mayhew 	name.data = kmemdup(dname, HEXDIR_LEN, GFP_KERNEL);
6966b189105SScott Mayhew 	if (!name.data) {
6976b189105SScott Mayhew 		dprintk("%s: failed to allocate memory for name.data!\n",
6986b189105SScott Mayhew 			__func__);
6996b189105SScott Mayhew 		goto out_enoent;
7006b189105SScott Mayhew 	}
7016b189105SScott Mayhew 	name.len = HEXDIR_LEN;
7026b189105SScott Mayhew 	crp = nfsd4_find_reclaim_client(name, nn);
7036b189105SScott Mayhew 	kfree(name.data);
7040ce0c2b5SJeff Layton 	if (crp) {
7052a4317c5SJeff Layton 		set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
7060ce0c2b5SJeff Layton 		crp->cr_clp = clp;
7072a4317c5SJeff Layton 		return 0;
7082a4317c5SJeff Layton 	}
7092a4317c5SJeff Layton 
7106b189105SScott Mayhew out_enoent:
7112a4317c5SJeff Layton 	return -ENOENT;
7122a4317c5SJeff Layton }
7132a4317c5SJeff Layton 
7147c582e4fSJulia Lawall static const struct nfsd4_client_tracking_ops nfsd4_legacy_tracking_ops = {
71552e19c09SStanislav Kinsbursky 	.init		= nfsd4_legacy_tracking_init,
7162a4317c5SJeff Layton 	.exit		= nfsd4_legacy_tracking_exit,
7172a4317c5SJeff Layton 	.create		= nfsd4_create_clid_dir,
7182a4317c5SJeff Layton 	.remove		= nfsd4_remove_clid_dir,
7192a4317c5SJeff Layton 	.check		= nfsd4_check_legacy_client,
7202a4317c5SJeff Layton 	.grace_done	= nfsd4_recdir_purge_old,
72111a60d15SScott Mayhew 	.version	= 1,
72211a60d15SScott Mayhew 	.msglen		= 0,
7232a4317c5SJeff Layton };
7242a4317c5SJeff Layton 
725f3f80148SJeff Layton /* Globals */
726f3f80148SJeff Layton #define NFSD_PIPE_DIR		"nfsd"
727f3f80148SJeff Layton #define NFSD_CLD_PIPE		"cld"
728f3f80148SJeff Layton 
729f3f80148SJeff Layton /* per-net-ns structure for holding cld upcall info */
730f3f80148SJeff Layton struct cld_net {
731f3f80148SJeff Layton 	struct rpc_pipe		*cn_pipe;
732f3f80148SJeff Layton 	spinlock_t		 cn_lock;
733f3f80148SJeff Layton 	struct list_head	 cn_list;
734f3f80148SJeff Layton 	unsigned int		 cn_xid;
7358a9f4f41SScott Mayhew 	bool			 cn_has_legacy;
7366ee95d1cSScott Mayhew 	struct crypto_shash	*cn_tfm;
737f3f80148SJeff Layton };
738f3f80148SJeff Layton 
739f3f80148SJeff Layton struct cld_upcall {
740f3f80148SJeff Layton 	struct list_head	 cu_list;
741f3f80148SJeff Layton 	struct cld_net		*cu_net;
742b493fd31SScott Mayhew 	struct completion	 cu_done;
74311a60d15SScott Mayhew 	union {
74411a60d15SScott Mayhew 		struct cld_msg_hdr	 cu_hdr;
745f3f80148SJeff Layton 		struct cld_msg		 cu_msg;
7466ee95d1cSScott Mayhew 		struct cld_msg_v2	 cu_msg_v2;
74711a60d15SScott Mayhew 	} cu_u;
748f3f80148SJeff Layton };
749f3f80148SJeff Layton 
750f3f80148SJeff Layton static int
__cld_pipe_upcall(struct rpc_pipe * pipe,void * cmsg,struct nfsd_net * nn)751df60446cSScott Mayhew __cld_pipe_upcall(struct rpc_pipe *pipe, void *cmsg, struct nfsd_net *nn)
752f3f80148SJeff Layton {
753f3f80148SJeff Layton 	int ret;
754f3f80148SJeff Layton 	struct rpc_pipe_msg msg;
75511a60d15SScott Mayhew 	struct cld_upcall *cup = container_of(cmsg, struct cld_upcall, cu_u);
756f3f80148SJeff Layton 
757f3f80148SJeff Layton 	memset(&msg, 0, sizeof(msg));
758f3f80148SJeff Layton 	msg.data = cmsg;
75911a60d15SScott Mayhew 	msg.len = nn->client_tracking_ops->msglen;
760f3f80148SJeff Layton 
761f3f80148SJeff Layton 	ret = rpc_queue_upcall(pipe, &msg);
762f3f80148SJeff Layton 	if (ret < 0) {
763f3f80148SJeff Layton 		goto out;
764f3f80148SJeff Layton 	}
765f3f80148SJeff Layton 
766b493fd31SScott Mayhew 	wait_for_completion(&cup->cu_done);
767f3f80148SJeff Layton 
768f3f80148SJeff Layton 	if (msg.errno < 0)
769f3f80148SJeff Layton 		ret = msg.errno;
770f3f80148SJeff Layton out:
771f3f80148SJeff Layton 	return ret;
772f3f80148SJeff Layton }
773f3f80148SJeff Layton 
774f3f80148SJeff Layton static int
cld_pipe_upcall(struct rpc_pipe * pipe,void * cmsg,struct nfsd_net * nn)775df60446cSScott Mayhew cld_pipe_upcall(struct rpc_pipe *pipe, void *cmsg, struct nfsd_net *nn)
776f3f80148SJeff Layton {
777f3f80148SJeff Layton 	int ret;
778f3f80148SJeff Layton 
779f3f80148SJeff Layton 	/*
780f3f80148SJeff Layton 	 * -EAGAIN occurs when pipe is closed and reopened while there are
781f3f80148SJeff Layton 	 *  upcalls queued.
782f3f80148SJeff Layton 	 */
783f3f80148SJeff Layton 	do {
784df60446cSScott Mayhew 		ret = __cld_pipe_upcall(pipe, cmsg, nn);
785f3f80148SJeff Layton 	} while (ret == -EAGAIN);
786f3f80148SJeff Layton 
787f3f80148SJeff Layton 	return ret;
788f3f80148SJeff Layton }
789f3f80148SJeff Layton 
790f3f80148SJeff Layton static ssize_t
__cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user * cmsg,struct nfsd_net * nn)7916ee95d1cSScott Mayhew __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg,
79274725959SScott Mayhew 		struct nfsd_net *nn)
79374725959SScott Mayhew {
7946ee95d1cSScott Mayhew 	uint8_t cmd, princhashlen;
7956ee95d1cSScott Mayhew 	struct xdr_netobj name, princhash = { .len = 0, .data = NULL };
79674725959SScott Mayhew 	uint16_t namelen;
7978a9f4f41SScott Mayhew 	struct cld_net *cn = nn->cld_net;
79874725959SScott Mayhew 
79974725959SScott Mayhew 	if (get_user(cmd, &cmsg->cm_cmd)) {
80074725959SScott Mayhew 		dprintk("%s: error when copying cmd from userspace", __func__);
80174725959SScott Mayhew 		return -EFAULT;
80274725959SScott Mayhew 	}
80374725959SScott Mayhew 	if (cmd == Cld_GraceStart) {
8046ee95d1cSScott Mayhew 		if (nn->client_tracking_ops->version >= 2) {
8056ee95d1cSScott Mayhew 			const struct cld_clntinfo __user *ci;
8066ee95d1cSScott Mayhew 
8076ee95d1cSScott Mayhew 			ci = &cmsg->cm_u.cm_clntinfo;
8086ee95d1cSScott Mayhew 			if (get_user(namelen, &ci->cc_name.cn_len))
80974725959SScott Mayhew 				return -EFAULT;
810318f7085SLi Lingfeng 			if (!namelen) {
811318f7085SLi Lingfeng 				dprintk("%s: namelen should not be zero", __func__);
812318f7085SLi Lingfeng 				return -EINVAL;
813318f7085SLi Lingfeng 			}
8146ee95d1cSScott Mayhew 			name.data = memdup_user(&ci->cc_name.cn_id, namelen);
815d44899b8SChristophe JAILLET 			if (IS_ERR(name.data))
81630a30fccSChristophe JAILLET 				return PTR_ERR(name.data);
81774725959SScott Mayhew 			name.len = namelen;
8186ee95d1cSScott Mayhew 			get_user(princhashlen, &ci->cc_princhash.cp_len);
8196ee95d1cSScott Mayhew 			if (princhashlen > 0) {
8206ee95d1cSScott Mayhew 				princhash.data = memdup_user(
8216ee95d1cSScott Mayhew 						&ci->cc_princhash.cp_data,
8226ee95d1cSScott Mayhew 						princhashlen);
823d44899b8SChristophe JAILLET 				if (IS_ERR(princhash.data)) {
824fd1ef880SChristophe JAILLET 					kfree(name.data);
82530a30fccSChristophe JAILLET 					return PTR_ERR(princhash.data);
826fd1ef880SChristophe JAILLET 				}
8276ee95d1cSScott Mayhew 				princhash.len = princhashlen;
8286ee95d1cSScott Mayhew 			} else
8296ee95d1cSScott Mayhew 				princhash.len = 0;
8306ee95d1cSScott Mayhew 		} else {
8316ee95d1cSScott Mayhew 			const struct cld_name __user *cnm;
8326ee95d1cSScott Mayhew 
8336ee95d1cSScott Mayhew 			cnm = &cmsg->cm_u.cm_name;
8346ee95d1cSScott Mayhew 			if (get_user(namelen, &cnm->cn_len))
8356ee95d1cSScott Mayhew 				return -EFAULT;
836318f7085SLi Lingfeng 			if (!namelen) {
837318f7085SLi Lingfeng 				dprintk("%s: namelen should not be zero", __func__);
838318f7085SLi Lingfeng 				return -EINVAL;
839318f7085SLi Lingfeng 			}
8406ee95d1cSScott Mayhew 			name.data = memdup_user(&cnm->cn_id, namelen);
841d44899b8SChristophe JAILLET 			if (IS_ERR(name.data))
84230a30fccSChristophe JAILLET 				return PTR_ERR(name.data);
8436ee95d1cSScott Mayhew 			name.len = namelen;
8446ee95d1cSScott Mayhew 		}
8458a9f4f41SScott Mayhew 		if (name.len > 5 && memcmp(name.data, "hash:", 5) == 0) {
8468a9f4f41SScott Mayhew 			name.len = name.len - 5;
8478a9f4f41SScott Mayhew 			memmove(name.data, name.data + 5, name.len);
8488a9f4f41SScott Mayhew 			cn->cn_has_legacy = true;
8498a9f4f41SScott Mayhew 		}
8506ee95d1cSScott Mayhew 		if (!nfs4_client_to_reclaim(name, princhash, nn)) {
85174725959SScott Mayhew 			kfree(name.data);
8526ee95d1cSScott Mayhew 			kfree(princhash.data);
85374725959SScott Mayhew 			return -EFAULT;
85474725959SScott Mayhew 		}
85511a60d15SScott Mayhew 		return nn->client_tracking_ops->msglen;
85674725959SScott Mayhew 	}
85774725959SScott Mayhew 	return -EFAULT;
85874725959SScott Mayhew }
85974725959SScott Mayhew 
86074725959SScott Mayhew static ssize_t
cld_pipe_downcall(struct file * filp,const char __user * src,size_t mlen)861f3f80148SJeff Layton cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
862f3f80148SJeff Layton {
863f3f80148SJeff Layton 	struct cld_upcall *tmp, *cup;
86411a60d15SScott Mayhew 	struct cld_msg_hdr __user *hdr = (struct cld_msg_hdr __user *)src;
8656ee95d1cSScott Mayhew 	struct cld_msg_v2 __user *cmsg = (struct cld_msg_v2 __user *)src;
866f3f80148SJeff Layton 	uint32_t xid;
867ef8a1a10SAl Viro 	struct nfsd_net *nn = net_generic(file_inode(filp)->i_sb->s_fs_info,
868f3f80148SJeff Layton 						nfsd_net_id);
869f3f80148SJeff Layton 	struct cld_net *cn = nn->cld_net;
87074725959SScott Mayhew 	int16_t status;
871f3f80148SJeff Layton 
87211a60d15SScott Mayhew 	if (mlen != nn->client_tracking_ops->msglen) {
8738a7dc4b0SRandy Dunlap 		dprintk("%s: got %zu bytes, expected %zu\n", __func__, mlen,
87411a60d15SScott Mayhew 			nn->client_tracking_ops->msglen);
875f3f80148SJeff Layton 		return -EINVAL;
876f3f80148SJeff Layton 	}
877f3f80148SJeff Layton 
878f3f80148SJeff Layton 	/* copy just the xid so we can try to find that */
87911a60d15SScott Mayhew 	if (copy_from_user(&xid, &hdr->cm_xid, sizeof(xid)) != 0) {
880f3f80148SJeff Layton 		dprintk("%s: error when copying xid from userspace", __func__);
881f3f80148SJeff Layton 		return -EFAULT;
882f3f80148SJeff Layton 	}
883f3f80148SJeff Layton 
88474725959SScott Mayhew 	/*
88574725959SScott Mayhew 	 * copy the status so we know whether to remove the upcall from the
88674725959SScott Mayhew 	 * list (for -EINPROGRESS, we just want to make sure the xid is
88774725959SScott Mayhew 	 * valid, not remove the upcall from the list)
88874725959SScott Mayhew 	 */
88911a60d15SScott Mayhew 	if (get_user(status, &hdr->cm_status)) {
89074725959SScott Mayhew 		dprintk("%s: error when copying status from userspace", __func__);
89174725959SScott Mayhew 		return -EFAULT;
89274725959SScott Mayhew 	}
89374725959SScott Mayhew 
894f3f80148SJeff Layton 	/* walk the list and find corresponding xid */
895f3f80148SJeff Layton 	cup = NULL;
896f3f80148SJeff Layton 	spin_lock(&cn->cn_lock);
897f3f80148SJeff Layton 	list_for_each_entry(tmp, &cn->cn_list, cu_list) {
89811a60d15SScott Mayhew 		if (get_unaligned(&tmp->cu_u.cu_hdr.cm_xid) == xid) {
899f3f80148SJeff Layton 			cup = tmp;
90074725959SScott Mayhew 			if (status != -EINPROGRESS)
901f3f80148SJeff Layton 				list_del_init(&cup->cu_list);
902f3f80148SJeff Layton 			break;
903f3f80148SJeff Layton 		}
904f3f80148SJeff Layton 	}
905f3f80148SJeff Layton 	spin_unlock(&cn->cn_lock);
906f3f80148SJeff Layton 
907f3f80148SJeff Layton 	/* couldn't find upcall? */
908f3f80148SJeff Layton 	if (!cup) {
90921f72c9fSJeff Layton 		dprintk("%s: couldn't find upcall -- xid=%u\n", __func__, xid);
910f3f80148SJeff Layton 		return -EINVAL;
911f3f80148SJeff Layton 	}
912f3f80148SJeff Layton 
91374725959SScott Mayhew 	if (status == -EINPROGRESS)
91474725959SScott Mayhew 		return __cld_pipe_inprogress_downcall(cmsg, nn);
91574725959SScott Mayhew 
9166ee95d1cSScott Mayhew 	if (copy_from_user(&cup->cu_u.cu_msg_v2, src, mlen) != 0)
917f3f80148SJeff Layton 		return -EFAULT;
918f3f80148SJeff Layton 
919b493fd31SScott Mayhew 	complete(&cup->cu_done);
920f3f80148SJeff Layton 	return mlen;
921f3f80148SJeff Layton }
922f3f80148SJeff Layton 
923f3f80148SJeff Layton static void
cld_pipe_destroy_msg(struct rpc_pipe_msg * msg)924f3f80148SJeff Layton cld_pipe_destroy_msg(struct rpc_pipe_msg *msg)
925f3f80148SJeff Layton {
926f3f80148SJeff Layton 	struct cld_msg *cmsg = msg->data;
927f3f80148SJeff Layton 	struct cld_upcall *cup = container_of(cmsg, struct cld_upcall,
92811a60d15SScott Mayhew 						 cu_u.cu_msg);
929f3f80148SJeff Layton 
930f3f80148SJeff Layton 	/* errno >= 0 means we got a downcall */
931f3f80148SJeff Layton 	if (msg->errno >= 0)
932f3f80148SJeff Layton 		return;
933f3f80148SJeff Layton 
934b493fd31SScott Mayhew 	complete(&cup->cu_done);
935f3f80148SJeff Layton }
936f3f80148SJeff Layton 
937f3f80148SJeff Layton static const struct rpc_pipe_ops cld_upcall_ops = {
938f3f80148SJeff Layton 	.upcall		= rpc_pipe_generic_upcall,
939f3f80148SJeff Layton 	.downcall	= cld_pipe_downcall,
940f3f80148SJeff Layton 	.destroy_msg	= cld_pipe_destroy_msg,
941f3f80148SJeff Layton };
942f3f80148SJeff Layton 
943f3f80148SJeff Layton static struct dentry *
nfsd4_cld_register_sb(struct super_block * sb,struct rpc_pipe * pipe)944f3f80148SJeff Layton nfsd4_cld_register_sb(struct super_block *sb, struct rpc_pipe *pipe)
945f3f80148SJeff Layton {
946f3f80148SJeff Layton 	struct dentry *dir, *dentry;
947f3f80148SJeff Layton 
948f3f80148SJeff Layton 	dir = rpc_d_lookup_sb(sb, NFSD_PIPE_DIR);
949f3f80148SJeff Layton 	if (dir == NULL)
950f3f80148SJeff Layton 		return ERR_PTR(-ENOENT);
951f3f80148SJeff Layton 	dentry = rpc_mkpipe_dentry(dir, NFSD_CLD_PIPE, NULL, pipe);
952f3f80148SJeff Layton 	dput(dir);
953f3f80148SJeff Layton 	return dentry;
954f3f80148SJeff Layton }
955f3f80148SJeff Layton 
956f3f80148SJeff Layton static void
nfsd4_cld_unregister_sb(struct rpc_pipe * pipe)957f3f80148SJeff Layton nfsd4_cld_unregister_sb(struct rpc_pipe *pipe)
958f3f80148SJeff Layton {
959f3f80148SJeff Layton 	if (pipe->dentry)
960f3f80148SJeff Layton 		rpc_unlink(pipe->dentry);
961f3f80148SJeff Layton }
962f3f80148SJeff Layton 
963f3f80148SJeff Layton static struct dentry *
nfsd4_cld_register_net(struct net * net,struct rpc_pipe * pipe)964f3f80148SJeff Layton nfsd4_cld_register_net(struct net *net, struct rpc_pipe *pipe)
965f3f80148SJeff Layton {
966f3f80148SJeff Layton 	struct super_block *sb;
967f3f80148SJeff Layton 	struct dentry *dentry;
968f3f80148SJeff Layton 
969f3f80148SJeff Layton 	sb = rpc_get_sb_net(net);
970f3f80148SJeff Layton 	if (!sb)
971f3f80148SJeff Layton 		return NULL;
972f3f80148SJeff Layton 	dentry = nfsd4_cld_register_sb(sb, pipe);
973f3f80148SJeff Layton 	rpc_put_sb_net(net);
974f3f80148SJeff Layton 	return dentry;
975f3f80148SJeff Layton }
976f3f80148SJeff Layton 
977f3f80148SJeff Layton static void
nfsd4_cld_unregister_net(struct net * net,struct rpc_pipe * pipe)978f3f80148SJeff Layton nfsd4_cld_unregister_net(struct net *net, struct rpc_pipe *pipe)
979f3f80148SJeff Layton {
980f3f80148SJeff Layton 	struct super_block *sb;
981f3f80148SJeff Layton 
982f3f80148SJeff Layton 	sb = rpc_get_sb_net(net);
983f3f80148SJeff Layton 	if (sb) {
984f3f80148SJeff Layton 		nfsd4_cld_unregister_sb(pipe);
985f3f80148SJeff Layton 		rpc_put_sb_net(net);
986f3f80148SJeff Layton 	}
987f3f80148SJeff Layton }
988f3f80148SJeff Layton 
989f3f80148SJeff Layton /* Initialize rpc_pipefs pipe for communication with client tracking daemon */
990f3f80148SJeff Layton static int
__nfsd4_init_cld_pipe(struct net * net)99186921607SScott Mayhew __nfsd4_init_cld_pipe(struct net *net)
992f3f80148SJeff Layton {
993f3f80148SJeff Layton 	int ret;
994f3f80148SJeff Layton 	struct dentry *dentry;
995f3f80148SJeff Layton 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
996f3f80148SJeff Layton 	struct cld_net *cn;
997f3f80148SJeff Layton 
998f3f80148SJeff Layton 	if (nn->cld_net)
999f3f80148SJeff Layton 		return 0;
1000f3f80148SJeff Layton 
1001f3f80148SJeff Layton 	cn = kzalloc(sizeof(*cn), GFP_KERNEL);
1002f3f80148SJeff Layton 	if (!cn) {
1003f3f80148SJeff Layton 		ret = -ENOMEM;
1004f3f80148SJeff Layton 		goto err;
1005f3f80148SJeff Layton 	}
1006f3f80148SJeff Layton 
1007f3f80148SJeff Layton 	cn->cn_pipe = rpc_mkpipe_data(&cld_upcall_ops, RPC_PIPE_WAIT_FOR_OPEN);
1008f3f80148SJeff Layton 	if (IS_ERR(cn->cn_pipe)) {
1009f3f80148SJeff Layton 		ret = PTR_ERR(cn->cn_pipe);
1010f3f80148SJeff Layton 		goto err;
1011f3f80148SJeff Layton 	}
1012f3f80148SJeff Layton 	spin_lock_init(&cn->cn_lock);
1013f3f80148SJeff Layton 	INIT_LIST_HEAD(&cn->cn_list);
1014f3f80148SJeff Layton 
1015f3f80148SJeff Layton 	dentry = nfsd4_cld_register_net(net, cn->cn_pipe);
1016f3f80148SJeff Layton 	if (IS_ERR(dentry)) {
1017f3f80148SJeff Layton 		ret = PTR_ERR(dentry);
1018f3f80148SJeff Layton 		goto err_destroy_data;
1019f3f80148SJeff Layton 	}
1020f3f80148SJeff Layton 
1021f3f80148SJeff Layton 	cn->cn_pipe->dentry = dentry;
10228a9f4f41SScott Mayhew 	cn->cn_has_legacy = false;
1023f3f80148SJeff Layton 	nn->cld_net = cn;
1024f3f80148SJeff Layton 	return 0;
1025f3f80148SJeff Layton 
1026f3f80148SJeff Layton err_destroy_data:
1027f3f80148SJeff Layton 	rpc_destroy_pipe_data(cn->cn_pipe);
1028f3f80148SJeff Layton err:
1029f3f80148SJeff Layton 	kfree(cn);
1030f3f80148SJeff Layton 	printk(KERN_ERR "NFSD: unable to create nfsdcld upcall pipe (%d)\n",
1031f3f80148SJeff Layton 			ret);
1032f3f80148SJeff Layton 	return ret;
1033f3f80148SJeff Layton }
1034f3f80148SJeff Layton 
103586921607SScott Mayhew static int
nfsd4_init_cld_pipe(struct net * net)103686921607SScott Mayhew nfsd4_init_cld_pipe(struct net *net)
103786921607SScott Mayhew {
103886921607SScott Mayhew 	int status;
103986921607SScott Mayhew 
104086921607SScott Mayhew 	status = __nfsd4_init_cld_pipe(net);
104186921607SScott Mayhew 	if (!status)
1042f988a7b7SPaul Menzel 		pr_info("NFSD: Using old nfsdcld client tracking operations.\n");
104386921607SScott Mayhew 	return status;
104486921607SScott Mayhew }
104586921607SScott Mayhew 
1046f3f80148SJeff Layton static void
nfsd4_remove_cld_pipe(struct net * net)1047f3f80148SJeff Layton nfsd4_remove_cld_pipe(struct net *net)
1048f3f80148SJeff Layton {
1049f3f80148SJeff Layton 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
1050f3f80148SJeff Layton 	struct cld_net *cn = nn->cld_net;
1051f3f80148SJeff Layton 
1052f3f80148SJeff Layton 	nfsd4_cld_unregister_net(net, cn->cn_pipe);
1053f3f80148SJeff Layton 	rpc_destroy_pipe_data(cn->cn_pipe);
10546ee95d1cSScott Mayhew 	if (cn->cn_tfm)
10556ee95d1cSScott Mayhew 		crypto_free_shash(cn->cn_tfm);
1056f3f80148SJeff Layton 	kfree(nn->cld_net);
1057f3f80148SJeff Layton 	nn->cld_net = NULL;
1058f3f80148SJeff Layton }
1059f3f80148SJeff Layton 
1060f3f80148SJeff Layton static struct cld_upcall *
alloc_cld_upcall(struct nfsd_net * nn)106111a60d15SScott Mayhew alloc_cld_upcall(struct nfsd_net *nn)
1062f3f80148SJeff Layton {
1063f3f80148SJeff Layton 	struct cld_upcall *new, *tmp;
106411a60d15SScott Mayhew 	struct cld_net *cn = nn->cld_net;
1065f3f80148SJeff Layton 
1066f3f80148SJeff Layton 	new = kzalloc(sizeof(*new), GFP_KERNEL);
1067f3f80148SJeff Layton 	if (!new)
1068f3f80148SJeff Layton 		return new;
1069f3f80148SJeff Layton 
1070f3f80148SJeff Layton 	/* FIXME: hard cap on number in flight? */
1071f3f80148SJeff Layton restart_search:
1072f3f80148SJeff Layton 	spin_lock(&cn->cn_lock);
1073f3f80148SJeff Layton 	list_for_each_entry(tmp, &cn->cn_list, cu_list) {
107411a60d15SScott Mayhew 		if (tmp->cu_u.cu_msg.cm_xid == cn->cn_xid) {
1075f3f80148SJeff Layton 			cn->cn_xid++;
1076f3f80148SJeff Layton 			spin_unlock(&cn->cn_lock);
1077f3f80148SJeff Layton 			goto restart_search;
1078f3f80148SJeff Layton 		}
1079f3f80148SJeff Layton 	}
1080b493fd31SScott Mayhew 	init_completion(&new->cu_done);
108111a60d15SScott Mayhew 	new->cu_u.cu_msg.cm_vers = nn->client_tracking_ops->version;
108211a60d15SScott Mayhew 	put_unaligned(cn->cn_xid++, &new->cu_u.cu_msg.cm_xid);
1083f3f80148SJeff Layton 	new->cu_net = cn;
1084f3f80148SJeff Layton 	list_add(&new->cu_list, &cn->cn_list);
1085f3f80148SJeff Layton 	spin_unlock(&cn->cn_lock);
1086f3f80148SJeff Layton 
108711a60d15SScott Mayhew 	dprintk("%s: allocated xid %u\n", __func__, new->cu_u.cu_msg.cm_xid);
1088f3f80148SJeff Layton 
1089f3f80148SJeff Layton 	return new;
1090f3f80148SJeff Layton }
1091f3f80148SJeff Layton 
1092f3f80148SJeff Layton static void
free_cld_upcall(struct cld_upcall * victim)1093f3f80148SJeff Layton free_cld_upcall(struct cld_upcall *victim)
1094f3f80148SJeff Layton {
1095f3f80148SJeff Layton 	struct cld_net *cn = victim->cu_net;
1096f3f80148SJeff Layton 
1097f3f80148SJeff Layton 	spin_lock(&cn->cn_lock);
1098f3f80148SJeff Layton 	list_del(&victim->cu_list);
1099f3f80148SJeff Layton 	spin_unlock(&cn->cn_lock);
1100f3f80148SJeff Layton 	kfree(victim);
1101f3f80148SJeff Layton }
1102f3f80148SJeff Layton 
1103f3f80148SJeff Layton /* Ask daemon to create a new record */
1104f3f80148SJeff Layton static void
nfsd4_cld_create(struct nfs4_client * clp)1105f3f80148SJeff Layton nfsd4_cld_create(struct nfs4_client *clp)
1106f3f80148SJeff Layton {
1107f3f80148SJeff Layton 	int ret;
1108f3f80148SJeff Layton 	struct cld_upcall *cup;
1109c212cecfSStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
1110f3f80148SJeff Layton 	struct cld_net *cn = nn->cld_net;
1111f3f80148SJeff Layton 
1112f3f80148SJeff Layton 	/* Don't upcall if it's already stored */
1113f3f80148SJeff Layton 	if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
1114f3f80148SJeff Layton 		return;
1115f3f80148SJeff Layton 
111611a60d15SScott Mayhew 	cup = alloc_cld_upcall(nn);
1117f3f80148SJeff Layton 	if (!cup) {
1118f3f80148SJeff Layton 		ret = -ENOMEM;
1119f3f80148SJeff Layton 		goto out_err;
1120f3f80148SJeff Layton 	}
1121f3f80148SJeff Layton 
112211a60d15SScott Mayhew 	cup->cu_u.cu_msg.cm_cmd = Cld_Create;
112311a60d15SScott Mayhew 	cup->cu_u.cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len;
112411a60d15SScott Mayhew 	memcpy(cup->cu_u.cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data,
1125f3f80148SJeff Layton 			clp->cl_name.len);
1126f3f80148SJeff Layton 
1127df60446cSScott Mayhew 	ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
1128f3f80148SJeff Layton 	if (!ret) {
112911a60d15SScott Mayhew 		ret = cup->cu_u.cu_msg.cm_status;
1130f3f80148SJeff Layton 		set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
1131f3f80148SJeff Layton 	}
1132f3f80148SJeff Layton 
1133f3f80148SJeff Layton 	free_cld_upcall(cup);
1134f3f80148SJeff Layton out_err:
1135f3f80148SJeff Layton 	if (ret)
1136f3f80148SJeff Layton 		printk(KERN_ERR "NFSD: Unable to create client "
1137f3f80148SJeff Layton 				"record on stable storage: %d\n", ret);
1138f3f80148SJeff Layton }
1139f3f80148SJeff Layton 
1140f3f80148SJeff Layton /* Ask daemon to create a new record */
1141f3f80148SJeff Layton static void
nfsd4_cld_create_v2(struct nfs4_client * clp)11426ee95d1cSScott Mayhew nfsd4_cld_create_v2(struct nfs4_client *clp)
11436ee95d1cSScott Mayhew {
11446ee95d1cSScott Mayhew 	int ret;
11456ee95d1cSScott Mayhew 	struct cld_upcall *cup;
11466ee95d1cSScott Mayhew 	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
11476ee95d1cSScott Mayhew 	struct cld_net *cn = nn->cld_net;
11486ee95d1cSScott Mayhew 	struct cld_msg_v2 *cmsg;
11496ee95d1cSScott Mayhew 	struct crypto_shash *tfm = cn->cn_tfm;
11506ee95d1cSScott Mayhew 	struct xdr_netobj cksum;
11516ee95d1cSScott Mayhew 	char *principal = NULL;
11526ee95d1cSScott Mayhew 
11536ee95d1cSScott Mayhew 	/* Don't upcall if it's already stored */
11546ee95d1cSScott Mayhew 	if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
11556ee95d1cSScott Mayhew 		return;
11566ee95d1cSScott Mayhew 
11576ee95d1cSScott Mayhew 	cup = alloc_cld_upcall(nn);
11586ee95d1cSScott Mayhew 	if (!cup) {
11596ee95d1cSScott Mayhew 		ret = -ENOMEM;
11606ee95d1cSScott Mayhew 		goto out_err;
11616ee95d1cSScott Mayhew 	}
11626ee95d1cSScott Mayhew 
11636ee95d1cSScott Mayhew 	cmsg = &cup->cu_u.cu_msg_v2;
11646ee95d1cSScott Mayhew 	cmsg->cm_cmd = Cld_Create;
11656ee95d1cSScott Mayhew 	cmsg->cm_u.cm_clntinfo.cc_name.cn_len = clp->cl_name.len;
11666ee95d1cSScott Mayhew 	memcpy(cmsg->cm_u.cm_clntinfo.cc_name.cn_id, clp->cl_name.data,
11676ee95d1cSScott Mayhew 			clp->cl_name.len);
11686ee95d1cSScott Mayhew 	if (clp->cl_cred.cr_raw_principal)
11696ee95d1cSScott Mayhew 		principal = clp->cl_cred.cr_raw_principal;
11706ee95d1cSScott Mayhew 	else if (clp->cl_cred.cr_principal)
11716ee95d1cSScott Mayhew 		principal = clp->cl_cred.cr_principal;
11726ee95d1cSScott Mayhew 	if (principal) {
11736ee95d1cSScott Mayhew 		cksum.len = crypto_shash_digestsize(tfm);
11746ee95d1cSScott Mayhew 		cksum.data = kmalloc(cksum.len, GFP_KERNEL);
11756ee95d1cSScott Mayhew 		if (cksum.data == NULL) {
11766ee95d1cSScott Mayhew 			ret = -ENOMEM;
11776ee95d1cSScott Mayhew 			goto out;
11786ee95d1cSScott Mayhew 		}
1179ea794db2SEric Biggers 		ret = crypto_shash_tfm_digest(tfm, principal, strlen(principal),
11806ee95d1cSScott Mayhew 					      cksum.data);
11816ee95d1cSScott Mayhew 		if (ret) {
11826ee95d1cSScott Mayhew 			kfree(cksum.data);
11836ee95d1cSScott Mayhew 			goto out;
11846ee95d1cSScott Mayhew 		}
11856ee95d1cSScott Mayhew 		cmsg->cm_u.cm_clntinfo.cc_princhash.cp_len = cksum.len;
11866ee95d1cSScott Mayhew 		memcpy(cmsg->cm_u.cm_clntinfo.cc_princhash.cp_data,
11876ee95d1cSScott Mayhew 		       cksum.data, cksum.len);
11886ee95d1cSScott Mayhew 		kfree(cksum.data);
11896ee95d1cSScott Mayhew 	} else
11906ee95d1cSScott Mayhew 		cmsg->cm_u.cm_clntinfo.cc_princhash.cp_len = 0;
11916ee95d1cSScott Mayhew 
1192df60446cSScott Mayhew 	ret = cld_pipe_upcall(cn->cn_pipe, cmsg, nn);
11936ee95d1cSScott Mayhew 	if (!ret) {
11946ee95d1cSScott Mayhew 		ret = cmsg->cm_status;
11956ee95d1cSScott Mayhew 		set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
11966ee95d1cSScott Mayhew 	}
11976ee95d1cSScott Mayhew 
11986ee95d1cSScott Mayhew out:
11996ee95d1cSScott Mayhew 	free_cld_upcall(cup);
12006ee95d1cSScott Mayhew out_err:
12016ee95d1cSScott Mayhew 	if (ret)
12026ee95d1cSScott Mayhew 		pr_err("NFSD: Unable to create client record on stable storage: %d\n",
12036ee95d1cSScott Mayhew 				ret);
12046ee95d1cSScott Mayhew }
12056ee95d1cSScott Mayhew 
12066ee95d1cSScott Mayhew /* Ask daemon to create a new record */
12076ee95d1cSScott Mayhew static void
nfsd4_cld_remove(struct nfs4_client * clp)1208f3f80148SJeff Layton nfsd4_cld_remove(struct nfs4_client *clp)
1209f3f80148SJeff Layton {
1210f3f80148SJeff Layton 	int ret;
1211f3f80148SJeff Layton 	struct cld_upcall *cup;
1212c212cecfSStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
1213f3f80148SJeff Layton 	struct cld_net *cn = nn->cld_net;
1214f3f80148SJeff Layton 
1215f3f80148SJeff Layton 	/* Don't upcall if it's already removed */
1216f3f80148SJeff Layton 	if (!test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
1217f3f80148SJeff Layton 		return;
1218f3f80148SJeff Layton 
121911a60d15SScott Mayhew 	cup = alloc_cld_upcall(nn);
1220f3f80148SJeff Layton 	if (!cup) {
1221f3f80148SJeff Layton 		ret = -ENOMEM;
1222f3f80148SJeff Layton 		goto out_err;
1223f3f80148SJeff Layton 	}
1224f3f80148SJeff Layton 
122511a60d15SScott Mayhew 	cup->cu_u.cu_msg.cm_cmd = Cld_Remove;
122611a60d15SScott Mayhew 	cup->cu_u.cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len;
122711a60d15SScott Mayhew 	memcpy(cup->cu_u.cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data,
1228f3f80148SJeff Layton 			clp->cl_name.len);
1229f3f80148SJeff Layton 
1230df60446cSScott Mayhew 	ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
1231f3f80148SJeff Layton 	if (!ret) {
123211a60d15SScott Mayhew 		ret = cup->cu_u.cu_msg.cm_status;
1233f3f80148SJeff Layton 		clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
1234f3f80148SJeff Layton 	}
1235f3f80148SJeff Layton 
1236f3f80148SJeff Layton 	free_cld_upcall(cup);
1237f3f80148SJeff Layton out_err:
1238f3f80148SJeff Layton 	if (ret)
1239f3f80148SJeff Layton 		printk(KERN_ERR "NFSD: Unable to remove client "
1240f3f80148SJeff Layton 				"record from stable storage: %d\n", ret);
1241f3f80148SJeff Layton }
1242f3f80148SJeff Layton 
124374725959SScott Mayhew /*
124474725959SScott Mayhew  * For older nfsdcld's that do not allow us to "slurp" the clients
124574725959SScott Mayhew  * from the tracking database during startup.
124674725959SScott Mayhew  *
124774725959SScott Mayhew  * Check for presence of a record, and update its timestamp
124874725959SScott Mayhew  */
1249f3f80148SJeff Layton static int
nfsd4_cld_check_v0(struct nfs4_client * clp)125074725959SScott Mayhew nfsd4_cld_check_v0(struct nfs4_client *clp)
1251f3f80148SJeff Layton {
1252f3f80148SJeff Layton 	int ret;
1253f3f80148SJeff Layton 	struct cld_upcall *cup;
1254c212cecfSStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
1255f3f80148SJeff Layton 	struct cld_net *cn = nn->cld_net;
1256f3f80148SJeff Layton 
1257f3f80148SJeff Layton 	/* Don't upcall if one was already stored during this grace pd */
1258f3f80148SJeff Layton 	if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
1259f3f80148SJeff Layton 		return 0;
1260f3f80148SJeff Layton 
126111a60d15SScott Mayhew 	cup = alloc_cld_upcall(nn);
1262f3f80148SJeff Layton 	if (!cup) {
1263f3f80148SJeff Layton 		printk(KERN_ERR "NFSD: Unable to check client record on "
1264f3f80148SJeff Layton 				"stable storage: %d\n", -ENOMEM);
1265f3f80148SJeff Layton 		return -ENOMEM;
1266f3f80148SJeff Layton 	}
1267f3f80148SJeff Layton 
126811a60d15SScott Mayhew 	cup->cu_u.cu_msg.cm_cmd = Cld_Check;
126911a60d15SScott Mayhew 	cup->cu_u.cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len;
127011a60d15SScott Mayhew 	memcpy(cup->cu_u.cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data,
1271f3f80148SJeff Layton 			clp->cl_name.len);
1272f3f80148SJeff Layton 
1273df60446cSScott Mayhew 	ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
1274f3f80148SJeff Layton 	if (!ret) {
127511a60d15SScott Mayhew 		ret = cup->cu_u.cu_msg.cm_status;
1276f3f80148SJeff Layton 		set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
1277f3f80148SJeff Layton 	}
1278f3f80148SJeff Layton 
1279f3f80148SJeff Layton 	free_cld_upcall(cup);
1280f3f80148SJeff Layton 	return ret;
1281f3f80148SJeff Layton }
1282f3f80148SJeff Layton 
128374725959SScott Mayhew /*
128474725959SScott Mayhew  * For newer nfsdcld's that allow us to "slurp" the clients
128574725959SScott Mayhew  * from the tracking database during startup.
128674725959SScott Mayhew  *
128774725959SScott Mayhew  * Check for presence of a record in the reclaim_str_hashtbl
128874725959SScott Mayhew  */
128974725959SScott Mayhew static int
nfsd4_cld_check(struct nfs4_client * clp)129074725959SScott Mayhew nfsd4_cld_check(struct nfs4_client *clp)
129174725959SScott Mayhew {
129274725959SScott Mayhew 	struct nfs4_client_reclaim *crp;
129374725959SScott Mayhew 	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
12948a9f4f41SScott Mayhew 	struct cld_net *cn = nn->cld_net;
12958a9f4f41SScott Mayhew 	int status;
12968a9f4f41SScott Mayhew 	char dname[HEXDIR_LEN];
12978a9f4f41SScott Mayhew 	struct xdr_netobj name;
129874725959SScott Mayhew 
129974725959SScott Mayhew 	/* did we already find that this client is stable? */
130074725959SScott Mayhew 	if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
130174725959SScott Mayhew 		return 0;
130274725959SScott Mayhew 
130374725959SScott Mayhew 	/* look for it in the reclaim hashtable otherwise */
130474725959SScott Mayhew 	crp = nfsd4_find_reclaim_client(clp->cl_name, nn);
13058a9f4f41SScott Mayhew 	if (crp)
13068a9f4f41SScott Mayhew 		goto found;
13078a9f4f41SScott Mayhew 
13088a9f4f41SScott Mayhew 	if (cn->cn_has_legacy) {
13098a9f4f41SScott Mayhew 		status = nfs4_make_rec_clidname(dname, &clp->cl_name);
13108a9f4f41SScott Mayhew 		if (status)
13118a9f4f41SScott Mayhew 			return -ENOENT;
13128a9f4f41SScott Mayhew 
13138a9f4f41SScott Mayhew 		name.data = kmemdup(dname, HEXDIR_LEN, GFP_KERNEL);
13148a9f4f41SScott Mayhew 		if (!name.data) {
13158a9f4f41SScott Mayhew 			dprintk("%s: failed to allocate memory for name.data!\n",
13168a9f4f41SScott Mayhew 				__func__);
13178a9f4f41SScott Mayhew 			return -ENOENT;
13188a9f4f41SScott Mayhew 		}
13198a9f4f41SScott Mayhew 		name.len = HEXDIR_LEN;
13208a9f4f41SScott Mayhew 		crp = nfsd4_find_reclaim_client(name, nn);
13218a9f4f41SScott Mayhew 		kfree(name.data);
13228a9f4f41SScott Mayhew 		if (crp)
13238a9f4f41SScott Mayhew 			goto found;
13248a9f4f41SScott Mayhew 
13258a9f4f41SScott Mayhew 	}
13268a9f4f41SScott Mayhew 	return -ENOENT;
13278a9f4f41SScott Mayhew found:
132874725959SScott Mayhew 	crp->cr_clp = clp;
132974725959SScott Mayhew 	return 0;
133074725959SScott Mayhew }
133174725959SScott Mayhew 
133274725959SScott Mayhew static int
nfsd4_cld_check_v2(struct nfs4_client * clp)13336ee95d1cSScott Mayhew nfsd4_cld_check_v2(struct nfs4_client *clp)
13346ee95d1cSScott Mayhew {
13356ee95d1cSScott Mayhew 	struct nfs4_client_reclaim *crp;
13366ee95d1cSScott Mayhew 	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
13376ee95d1cSScott Mayhew 	struct cld_net *cn = nn->cld_net;
13386ee95d1cSScott Mayhew 	int status;
13396ee95d1cSScott Mayhew 	char dname[HEXDIR_LEN];
13406ee95d1cSScott Mayhew 	struct xdr_netobj name;
13416ee95d1cSScott Mayhew 	struct crypto_shash *tfm = cn->cn_tfm;
13426ee95d1cSScott Mayhew 	struct xdr_netobj cksum;
13436ee95d1cSScott Mayhew 	char *principal = NULL;
13446ee95d1cSScott Mayhew 
13456ee95d1cSScott Mayhew 	/* did we already find that this client is stable? */
13466ee95d1cSScott Mayhew 	if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
13476ee95d1cSScott Mayhew 		return 0;
13486ee95d1cSScott Mayhew 
13496ee95d1cSScott Mayhew 	/* look for it in the reclaim hashtable otherwise */
13506ee95d1cSScott Mayhew 	crp = nfsd4_find_reclaim_client(clp->cl_name, nn);
13516ee95d1cSScott Mayhew 	if (crp)
13526ee95d1cSScott Mayhew 		goto found;
13536ee95d1cSScott Mayhew 
13546ee95d1cSScott Mayhew 	if (cn->cn_has_legacy) {
13556ee95d1cSScott Mayhew 		status = nfs4_make_rec_clidname(dname, &clp->cl_name);
13566ee95d1cSScott Mayhew 		if (status)
13576ee95d1cSScott Mayhew 			return -ENOENT;
13586ee95d1cSScott Mayhew 
13596ee95d1cSScott Mayhew 		name.data = kmemdup(dname, HEXDIR_LEN, GFP_KERNEL);
13606ee95d1cSScott Mayhew 		if (!name.data) {
13616ee95d1cSScott Mayhew 			dprintk("%s: failed to allocate memory for name.data\n",
13626ee95d1cSScott Mayhew 					__func__);
13636ee95d1cSScott Mayhew 			return -ENOENT;
13646ee95d1cSScott Mayhew 		}
13656ee95d1cSScott Mayhew 		name.len = HEXDIR_LEN;
13666ee95d1cSScott Mayhew 		crp = nfsd4_find_reclaim_client(name, nn);
13676ee95d1cSScott Mayhew 		kfree(name.data);
13686ee95d1cSScott Mayhew 		if (crp)
13696ee95d1cSScott Mayhew 			goto found;
13706ee95d1cSScott Mayhew 
13716ee95d1cSScott Mayhew 	}
13726ee95d1cSScott Mayhew 	return -ENOENT;
13736ee95d1cSScott Mayhew found:
13746ee95d1cSScott Mayhew 	if (crp->cr_princhash.len) {
13756ee95d1cSScott Mayhew 		if (clp->cl_cred.cr_raw_principal)
13766ee95d1cSScott Mayhew 			principal = clp->cl_cred.cr_raw_principal;
13776ee95d1cSScott Mayhew 		else if (clp->cl_cred.cr_principal)
13786ee95d1cSScott Mayhew 			principal = clp->cl_cred.cr_principal;
13796ee95d1cSScott Mayhew 		if (principal == NULL)
13806ee95d1cSScott Mayhew 			return -ENOENT;
13816ee95d1cSScott Mayhew 		cksum.len = crypto_shash_digestsize(tfm);
13826ee95d1cSScott Mayhew 		cksum.data = kmalloc(cksum.len, GFP_KERNEL);
13836ee95d1cSScott Mayhew 		if (cksum.data == NULL)
13846ee95d1cSScott Mayhew 			return -ENOENT;
1385ea794db2SEric Biggers 		status = crypto_shash_tfm_digest(tfm, principal,
1386ea794db2SEric Biggers 						 strlen(principal), cksum.data);
13876ee95d1cSScott Mayhew 		if (status) {
13886ee95d1cSScott Mayhew 			kfree(cksum.data);
13896ee95d1cSScott Mayhew 			return -ENOENT;
13906ee95d1cSScott Mayhew 		}
13916ee95d1cSScott Mayhew 		if (memcmp(crp->cr_princhash.data, cksum.data,
13926ee95d1cSScott Mayhew 				crp->cr_princhash.len)) {
13936ee95d1cSScott Mayhew 			kfree(cksum.data);
13946ee95d1cSScott Mayhew 			return -ENOENT;
13956ee95d1cSScott Mayhew 		}
13966ee95d1cSScott Mayhew 		kfree(cksum.data);
13976ee95d1cSScott Mayhew 	}
13986ee95d1cSScott Mayhew 	crp->cr_clp = clp;
13996ee95d1cSScott Mayhew 	return 0;
14006ee95d1cSScott Mayhew }
14016ee95d1cSScott Mayhew 
14026ee95d1cSScott Mayhew static int
nfsd4_cld_grace_start(struct nfsd_net * nn)140374725959SScott Mayhew nfsd4_cld_grace_start(struct nfsd_net *nn)
140474725959SScott Mayhew {
140574725959SScott Mayhew 	int ret;
140674725959SScott Mayhew 	struct cld_upcall *cup;
140774725959SScott Mayhew 	struct cld_net *cn = nn->cld_net;
140874725959SScott Mayhew 
140911a60d15SScott Mayhew 	cup = alloc_cld_upcall(nn);
141074725959SScott Mayhew 	if (!cup) {
141174725959SScott Mayhew 		ret = -ENOMEM;
141274725959SScott Mayhew 		goto out_err;
141374725959SScott Mayhew 	}
141474725959SScott Mayhew 
141511a60d15SScott Mayhew 	cup->cu_u.cu_msg.cm_cmd = Cld_GraceStart;
1416df60446cSScott Mayhew 	ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
141774725959SScott Mayhew 	if (!ret)
141811a60d15SScott Mayhew 		ret = cup->cu_u.cu_msg.cm_status;
141974725959SScott Mayhew 
142074725959SScott Mayhew 	free_cld_upcall(cup);
142174725959SScott Mayhew out_err:
142274725959SScott Mayhew 	if (ret)
142374725959SScott Mayhew 		dprintk("%s: Unable to get clients from userspace: %d\n",
142474725959SScott Mayhew 			__func__, ret);
142574725959SScott Mayhew 	return ret;
142674725959SScott Mayhew }
142774725959SScott Mayhew 
142874725959SScott Mayhew /* For older nfsdcld's that need cm_gracetime */
1429f3f80148SJeff Layton static void
nfsd4_cld_grace_done_v0(struct nfsd_net * nn)143074725959SScott Mayhew nfsd4_cld_grace_done_v0(struct nfsd_net *nn)
1431f3f80148SJeff Layton {
1432f3f80148SJeff Layton 	int ret;
1433f3f80148SJeff Layton 	struct cld_upcall *cup;
1434f3f80148SJeff Layton 	struct cld_net *cn = nn->cld_net;
1435f3f80148SJeff Layton 
143611a60d15SScott Mayhew 	cup = alloc_cld_upcall(nn);
1437f3f80148SJeff Layton 	if (!cup) {
1438f3f80148SJeff Layton 		ret = -ENOMEM;
1439f3f80148SJeff Layton 		goto out_err;
1440f3f80148SJeff Layton 	}
1441f3f80148SJeff Layton 
144211a60d15SScott Mayhew 	cup->cu_u.cu_msg.cm_cmd = Cld_GraceDone;
14439cc76801SArnd Bergmann 	cup->cu_u.cu_msg.cm_u.cm_gracetime = nn->boot_time;
1444df60446cSScott Mayhew 	ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
1445f3f80148SJeff Layton 	if (!ret)
144611a60d15SScott Mayhew 		ret = cup->cu_u.cu_msg.cm_status;
1447f3f80148SJeff Layton 
1448f3f80148SJeff Layton 	free_cld_upcall(cup);
1449f3f80148SJeff Layton out_err:
1450f3f80148SJeff Layton 	if (ret)
1451f3f80148SJeff Layton 		printk(KERN_ERR "NFSD: Unable to end grace period: %d\n", ret);
1452f3f80148SJeff Layton }
1453f3f80148SJeff Layton 
145474725959SScott Mayhew /*
145574725959SScott Mayhew  * For newer nfsdcld's that do not need cm_gracetime.  We also need to call
145674725959SScott Mayhew  * nfs4_release_reclaim() to clear out the reclaim_str_hashtbl.
145774725959SScott Mayhew  */
145874725959SScott Mayhew static void
nfsd4_cld_grace_done(struct nfsd_net * nn)145974725959SScott Mayhew nfsd4_cld_grace_done(struct nfsd_net *nn)
146074725959SScott Mayhew {
146174725959SScott Mayhew 	int ret;
146274725959SScott Mayhew 	struct cld_upcall *cup;
146374725959SScott Mayhew 	struct cld_net *cn = nn->cld_net;
146474725959SScott Mayhew 
146511a60d15SScott Mayhew 	cup = alloc_cld_upcall(nn);
146674725959SScott Mayhew 	if (!cup) {
146774725959SScott Mayhew 		ret = -ENOMEM;
146874725959SScott Mayhew 		goto out_err;
146974725959SScott Mayhew 	}
147074725959SScott Mayhew 
147111a60d15SScott Mayhew 	cup->cu_u.cu_msg.cm_cmd = Cld_GraceDone;
1472df60446cSScott Mayhew 	ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
147374725959SScott Mayhew 	if (!ret)
147411a60d15SScott Mayhew 		ret = cup->cu_u.cu_msg.cm_status;
147574725959SScott Mayhew 
147674725959SScott Mayhew 	free_cld_upcall(cup);
147774725959SScott Mayhew out_err:
147874725959SScott Mayhew 	nfs4_release_reclaim(nn);
147974725959SScott Mayhew 	if (ret)
148074725959SScott Mayhew 		printk(KERN_ERR "NFSD: Unable to end grace period: %d\n", ret);
148174725959SScott Mayhew }
148274725959SScott Mayhew 
148374725959SScott Mayhew static int
nfs4_cld_state_init(struct net * net)148474725959SScott Mayhew nfs4_cld_state_init(struct net *net)
148574725959SScott Mayhew {
148674725959SScott Mayhew 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
148774725959SScott Mayhew 	int i;
148874725959SScott Mayhew 
148974725959SScott Mayhew 	nn->reclaim_str_hashtbl = kmalloc_array(CLIENT_HASH_SIZE,
149074725959SScott Mayhew 						sizeof(struct list_head),
149174725959SScott Mayhew 						GFP_KERNEL);
149274725959SScott Mayhew 	if (!nn->reclaim_str_hashtbl)
149374725959SScott Mayhew 		return -ENOMEM;
149474725959SScott Mayhew 
149574725959SScott Mayhew 	for (i = 0; i < CLIENT_HASH_SIZE; i++)
149674725959SScott Mayhew 		INIT_LIST_HEAD(&nn->reclaim_str_hashtbl[i]);
149774725959SScott Mayhew 	nn->reclaim_str_hashtbl_size = 0;
1498362063a5SScott Mayhew 	nn->track_reclaim_completes = true;
1499362063a5SScott Mayhew 	atomic_set(&nn->nr_reclaim_complete, 0);
150074725959SScott Mayhew 
150174725959SScott Mayhew 	return 0;
150274725959SScott Mayhew }
150374725959SScott Mayhew 
150474725959SScott Mayhew static void
nfs4_cld_state_shutdown(struct net * net)150574725959SScott Mayhew nfs4_cld_state_shutdown(struct net *net)
150674725959SScott Mayhew {
150774725959SScott Mayhew 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
150874725959SScott Mayhew 
1509362063a5SScott Mayhew 	nn->track_reclaim_completes = false;
151074725959SScott Mayhew 	kfree(nn->reclaim_str_hashtbl);
151174725959SScott Mayhew }
151274725959SScott Mayhew 
151386921607SScott Mayhew static bool
cld_running(struct nfsd_net * nn)151486921607SScott Mayhew cld_running(struct nfsd_net *nn)
151586921607SScott Mayhew {
151686921607SScott Mayhew 	struct cld_net *cn = nn->cld_net;
151786921607SScott Mayhew 	struct rpc_pipe *pipe = cn->cn_pipe;
151886921607SScott Mayhew 
151986921607SScott Mayhew 	return pipe->nreaders || pipe->nwriters;
152086921607SScott Mayhew }
152186921607SScott Mayhew 
152274725959SScott Mayhew static int
nfsd4_cld_get_version(struct nfsd_net * nn)152311a60d15SScott Mayhew nfsd4_cld_get_version(struct nfsd_net *nn)
152411a60d15SScott Mayhew {
152511a60d15SScott Mayhew 	int ret = 0;
152611a60d15SScott Mayhew 	struct cld_upcall *cup;
152711a60d15SScott Mayhew 	struct cld_net *cn = nn->cld_net;
152811a60d15SScott Mayhew 	uint8_t version;
152911a60d15SScott Mayhew 
153011a60d15SScott Mayhew 	cup = alloc_cld_upcall(nn);
153111a60d15SScott Mayhew 	if (!cup) {
153211a60d15SScott Mayhew 		ret = -ENOMEM;
153311a60d15SScott Mayhew 		goto out_err;
153411a60d15SScott Mayhew 	}
153511a60d15SScott Mayhew 	cup->cu_u.cu_msg.cm_cmd = Cld_GetVersion;
1536df60446cSScott Mayhew 	ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
153711a60d15SScott Mayhew 	if (!ret) {
153811a60d15SScott Mayhew 		ret = cup->cu_u.cu_msg.cm_status;
153911a60d15SScott Mayhew 		if (ret)
154011a60d15SScott Mayhew 			goto out_free;
154111a60d15SScott Mayhew 		version = cup->cu_u.cu_msg.cm_u.cm_version;
154211a60d15SScott Mayhew 		dprintk("%s: userspace returned version %u\n",
154311a60d15SScott Mayhew 				__func__, version);
154411a60d15SScott Mayhew 		if (version < 1)
154511a60d15SScott Mayhew 			version = 1;
154611a60d15SScott Mayhew 		else if (version > CLD_UPCALL_VERSION)
154711a60d15SScott Mayhew 			version = CLD_UPCALL_VERSION;
154811a60d15SScott Mayhew 
154911a60d15SScott Mayhew 		switch (version) {
155011a60d15SScott Mayhew 		case 1:
155111a60d15SScott Mayhew 			nn->client_tracking_ops = &nfsd4_cld_tracking_ops;
155211a60d15SScott Mayhew 			break;
15536ee95d1cSScott Mayhew 		case 2:
15546ee95d1cSScott Mayhew 			nn->client_tracking_ops = &nfsd4_cld_tracking_ops_v2;
15556ee95d1cSScott Mayhew 			break;
155611a60d15SScott Mayhew 		default:
155711a60d15SScott Mayhew 			break;
155811a60d15SScott Mayhew 		}
155911a60d15SScott Mayhew 	}
156011a60d15SScott Mayhew out_free:
156111a60d15SScott Mayhew 	free_cld_upcall(cup);
156211a60d15SScott Mayhew out_err:
156311a60d15SScott Mayhew 	if (ret)
156411a60d15SScott Mayhew 		dprintk("%s: Unable to get version from userspace: %d\n",
156511a60d15SScott Mayhew 			__func__, ret);
156611a60d15SScott Mayhew 	return ret;
156711a60d15SScott Mayhew }
156811a60d15SScott Mayhew 
156911a60d15SScott Mayhew static int
nfsd4_cld_tracking_init(struct net * net)157074725959SScott Mayhew nfsd4_cld_tracking_init(struct net *net)
157174725959SScott Mayhew {
157274725959SScott Mayhew 	int status;
157374725959SScott Mayhew 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
157486921607SScott Mayhew 	bool running;
157586921607SScott Mayhew 	int retries = 10;
157618b9a895SScott Mayhew 	struct crypto_shash *tfm;
157774725959SScott Mayhew 
157874725959SScott Mayhew 	status = nfs4_cld_state_init(net);
157974725959SScott Mayhew 	if (status)
158074725959SScott Mayhew 		return status;
158174725959SScott Mayhew 
158286921607SScott Mayhew 	status = __nfsd4_init_cld_pipe(net);
158374725959SScott Mayhew 	if (status)
158474725959SScott Mayhew 		goto err_shutdown;
158574725959SScott Mayhew 
158686921607SScott Mayhew 	/*
158786921607SScott Mayhew 	 * rpc pipe upcalls take 30 seconds to time out, so we don't want to
158886921607SScott Mayhew 	 * queue an upcall unless we know that nfsdcld is running (because we
158986921607SScott Mayhew 	 * want this to fail fast so that nfsd4_client_tracking_init() can try
159086921607SScott Mayhew 	 * the next client tracking method).  nfsdcld should already be running
159186921607SScott Mayhew 	 * before nfsd is started, so the wait here is for nfsdcld to open the
159286921607SScott Mayhew 	 * pipefs file we just created.
159386921607SScott Mayhew 	 */
159486921607SScott Mayhew 	while (!(running = cld_running(nn)) && retries--)
159586921607SScott Mayhew 		msleep(100);
159686921607SScott Mayhew 
159786921607SScott Mayhew 	if (!running) {
159886921607SScott Mayhew 		status = -ETIMEDOUT;
159986921607SScott Mayhew 		goto err_remove;
160086921607SScott Mayhew 	}
160118b9a895SScott Mayhew 	tfm = crypto_alloc_shash("sha256", 0, 0);
160218b9a895SScott Mayhew 	if (IS_ERR(tfm)) {
160318b9a895SScott Mayhew 		status = PTR_ERR(tfm);
160418b9a895SScott Mayhew 		goto err_remove;
160518b9a895SScott Mayhew 	}
160618b9a895SScott Mayhew 	nn->cld_net->cn_tfm = tfm;
160786921607SScott Mayhew 
160811a60d15SScott Mayhew 	status = nfsd4_cld_get_version(nn);
160911a60d15SScott Mayhew 	if (status == -EOPNOTSUPP)
161011a60d15SScott Mayhew 		pr_warn("NFSD: nfsdcld GetVersion upcall failed. Please upgrade nfsdcld.\n");
161111a60d15SScott Mayhew 
161274725959SScott Mayhew 	status = nfsd4_cld_grace_start(nn);
161374725959SScott Mayhew 	if (status) {
161474725959SScott Mayhew 		if (status == -EOPNOTSUPP)
161511a60d15SScott Mayhew 			pr_warn("NFSD: nfsdcld GraceStart upcall failed. Please upgrade nfsdcld.\n");
161674725959SScott Mayhew 		nfs4_release_reclaim(nn);
161774725959SScott Mayhew 		goto err_remove;
161886921607SScott Mayhew 	} else
1619f988a7b7SPaul Menzel 		pr_info("NFSD: Using nfsdcld client tracking operations.\n");
162074725959SScott Mayhew 	return 0;
162174725959SScott Mayhew 
162274725959SScott Mayhew err_remove:
162374725959SScott Mayhew 	nfsd4_remove_cld_pipe(net);
162474725959SScott Mayhew err_shutdown:
162574725959SScott Mayhew 	nfs4_cld_state_shutdown(net);
162674725959SScott Mayhew 	return status;
162774725959SScott Mayhew }
162874725959SScott Mayhew 
162974725959SScott Mayhew static void
nfsd4_cld_tracking_exit(struct net * net)163074725959SScott Mayhew nfsd4_cld_tracking_exit(struct net *net)
163174725959SScott Mayhew {
163274725959SScott Mayhew 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
163374725959SScott Mayhew 
163474725959SScott Mayhew 	nfs4_release_reclaim(nn);
163574725959SScott Mayhew 	nfsd4_remove_cld_pipe(net);
163674725959SScott Mayhew 	nfs4_cld_state_shutdown(net);
163774725959SScott Mayhew }
163874725959SScott Mayhew 
163974725959SScott Mayhew /* For older nfsdcld's */
164074725959SScott Mayhew static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops_v0 = {
1641f3f80148SJeff Layton 	.init		= nfsd4_init_cld_pipe,
1642f3f80148SJeff Layton 	.exit		= nfsd4_remove_cld_pipe,
1643f3f80148SJeff Layton 	.create		= nfsd4_cld_create,
1644f3f80148SJeff Layton 	.remove		= nfsd4_cld_remove,
164574725959SScott Mayhew 	.check		= nfsd4_cld_check_v0,
164674725959SScott Mayhew 	.grace_done	= nfsd4_cld_grace_done_v0,
164711a60d15SScott Mayhew 	.version	= 1,
164811a60d15SScott Mayhew 	.msglen		= sizeof(struct cld_msg),
164974725959SScott Mayhew };
165074725959SScott Mayhew 
165174725959SScott Mayhew /* For newer nfsdcld's */
165274725959SScott Mayhew static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops = {
165374725959SScott Mayhew 	.init		= nfsd4_cld_tracking_init,
165474725959SScott Mayhew 	.exit		= nfsd4_cld_tracking_exit,
165574725959SScott Mayhew 	.create		= nfsd4_cld_create,
165674725959SScott Mayhew 	.remove		= nfsd4_cld_remove,
1657f3f80148SJeff Layton 	.check		= nfsd4_cld_check,
1658f3f80148SJeff Layton 	.grace_done	= nfsd4_cld_grace_done,
165911a60d15SScott Mayhew 	.version	= 1,
166011a60d15SScott Mayhew 	.msglen		= sizeof(struct cld_msg),
1661f3f80148SJeff Layton };
1662f3f80148SJeff Layton 
16636ee95d1cSScott Mayhew /* v2 create/check ops include the principal, if available */
16646ee95d1cSScott Mayhew static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops_v2 = {
16656ee95d1cSScott Mayhew 	.init		= nfsd4_cld_tracking_init,
16666ee95d1cSScott Mayhew 	.exit		= nfsd4_cld_tracking_exit,
16676ee95d1cSScott Mayhew 	.create		= nfsd4_cld_create_v2,
16686ee95d1cSScott Mayhew 	.remove		= nfsd4_cld_remove,
16696ee95d1cSScott Mayhew 	.check		= nfsd4_cld_check_v2,
16706ee95d1cSScott Mayhew 	.grace_done	= nfsd4_cld_grace_done,
16716ee95d1cSScott Mayhew 	.version	= 2,
16726ee95d1cSScott Mayhew 	.msglen		= sizeof(struct cld_msg_v2),
16736ee95d1cSScott Mayhew };
16746ee95d1cSScott Mayhew 
16752873d214SJeff Layton /* upcall via usermodehelper */
16762873d214SJeff Layton static char cltrack_prog[PATH_MAX] = "/sbin/nfsdcltrack";
16772873d214SJeff Layton module_param_string(cltrack_prog, cltrack_prog, sizeof(cltrack_prog),
16782873d214SJeff Layton 			S_IRUGO|S_IWUSR);
16792873d214SJeff Layton MODULE_PARM_DESC(cltrack_prog, "Path to the nfsdcltrack upcall program");
16802873d214SJeff Layton 
1681f3aa7e24SJeff Layton static bool cltrack_legacy_disable;
1682f3aa7e24SJeff Layton module_param(cltrack_legacy_disable, bool, S_IRUGO|S_IWUSR);
1683f3aa7e24SJeff Layton MODULE_PARM_DESC(cltrack_legacy_disable,
1684f3aa7e24SJeff Layton 		"Disable legacy recoverydir conversion. Default: false");
1685f3aa7e24SJeff Layton 
1686f3aa7e24SJeff Layton #define LEGACY_TOPDIR_ENV_PREFIX "NFSDCLTRACK_LEGACY_TOPDIR="
1687f3aa7e24SJeff Layton #define LEGACY_RECDIR_ENV_PREFIX "NFSDCLTRACK_LEGACY_RECDIR="
1688d4318acdSJeff Layton #define HAS_SESSION_ENV_PREFIX "NFSDCLTRACK_CLIENT_HAS_SESSION="
1689d4318acdSJeff Layton #define GRACE_START_ENV_PREFIX "NFSDCLTRACK_GRACE_START="
1690f3aa7e24SJeff Layton 
1691f3aa7e24SJeff Layton static char *
nfsd4_cltrack_legacy_topdir(void)1692f3aa7e24SJeff Layton nfsd4_cltrack_legacy_topdir(void)
16932873d214SJeff Layton {
1694f3aa7e24SJeff Layton 	int copied;
1695f3aa7e24SJeff Layton 	size_t len;
1696f3aa7e24SJeff Layton 	char *result;
1697f3aa7e24SJeff Layton 
1698f3aa7e24SJeff Layton 	if (cltrack_legacy_disable)
1699f3aa7e24SJeff Layton 		return NULL;
1700f3aa7e24SJeff Layton 
1701f3aa7e24SJeff Layton 	len = strlen(LEGACY_TOPDIR_ENV_PREFIX) +
1702f3aa7e24SJeff Layton 		strlen(nfs4_recoverydir()) + 1;
1703f3aa7e24SJeff Layton 
1704f3aa7e24SJeff Layton 	result = kmalloc(len, GFP_KERNEL);
1705f3aa7e24SJeff Layton 	if (!result)
1706f3aa7e24SJeff Layton 		return result;
1707f3aa7e24SJeff Layton 
1708f3aa7e24SJeff Layton 	copied = snprintf(result, len, LEGACY_TOPDIR_ENV_PREFIX "%s",
1709f3aa7e24SJeff Layton 				nfs4_recoverydir());
1710f3aa7e24SJeff Layton 	if (copied >= len) {
1711f3aa7e24SJeff Layton 		/* just return nothing if output was truncated */
1712f3aa7e24SJeff Layton 		kfree(result);
1713f3aa7e24SJeff Layton 		return NULL;
1714f3aa7e24SJeff Layton 	}
1715f3aa7e24SJeff Layton 
1716f3aa7e24SJeff Layton 	return result;
1717f3aa7e24SJeff Layton }
1718f3aa7e24SJeff Layton 
1719f3aa7e24SJeff Layton static char *
nfsd4_cltrack_legacy_recdir(const struct xdr_netobj * name)17202216d449SJeff Layton nfsd4_cltrack_legacy_recdir(const struct xdr_netobj *name)
1721f3aa7e24SJeff Layton {
1722f3aa7e24SJeff Layton 	int copied;
1723f3aa7e24SJeff Layton 	size_t len;
1724f3aa7e24SJeff Layton 	char *result;
1725f3aa7e24SJeff Layton 
1726f3aa7e24SJeff Layton 	if (cltrack_legacy_disable)
1727f3aa7e24SJeff Layton 		return NULL;
1728f3aa7e24SJeff Layton 
1729f3aa7e24SJeff Layton 	/* +1 is for '/' between "topdir" and "recdir" */
1730f3aa7e24SJeff Layton 	len = strlen(LEGACY_RECDIR_ENV_PREFIX) +
1731f3aa7e24SJeff Layton 		strlen(nfs4_recoverydir()) + 1 + HEXDIR_LEN;
1732f3aa7e24SJeff Layton 
1733f3aa7e24SJeff Layton 	result = kmalloc(len, GFP_KERNEL);
1734f3aa7e24SJeff Layton 	if (!result)
1735f3aa7e24SJeff Layton 		return result;
1736f3aa7e24SJeff Layton 
17372216d449SJeff Layton 	copied = snprintf(result, len, LEGACY_RECDIR_ENV_PREFIX "%s/",
17382216d449SJeff Layton 				nfs4_recoverydir());
17392216d449SJeff Layton 	if (copied > (len - HEXDIR_LEN)) {
17402216d449SJeff Layton 		/* just return nothing if output will be truncated */
17412216d449SJeff Layton 		kfree(result);
17422216d449SJeff Layton 		return NULL;
17432216d449SJeff Layton 	}
17442216d449SJeff Layton 
17452216d449SJeff Layton 	copied = nfs4_make_rec_clidname(result + copied, name);
17462216d449SJeff Layton 	if (copied) {
1747f3aa7e24SJeff Layton 		kfree(result);
1748f3aa7e24SJeff Layton 		return NULL;
1749f3aa7e24SJeff Layton 	}
1750f3aa7e24SJeff Layton 
1751f3aa7e24SJeff Layton 	return result;
1752f3aa7e24SJeff Layton }
1753f3aa7e24SJeff Layton 
1754d4318acdSJeff Layton static char *
nfsd4_cltrack_client_has_session(struct nfs4_client * clp)1755d4318acdSJeff Layton nfsd4_cltrack_client_has_session(struct nfs4_client *clp)
1756f3aa7e24SJeff Layton {
1757d4318acdSJeff Layton 	int copied;
1758d4318acdSJeff Layton 	size_t len;
1759d4318acdSJeff Layton 	char *result;
1760d4318acdSJeff Layton 
1761d4318acdSJeff Layton 	/* prefix + Y/N character + terminating NULL */
1762d4318acdSJeff Layton 	len = strlen(HAS_SESSION_ENV_PREFIX) + 1 + 1;
1763d4318acdSJeff Layton 
1764d4318acdSJeff Layton 	result = kmalloc(len, GFP_KERNEL);
1765d4318acdSJeff Layton 	if (!result)
1766d4318acdSJeff Layton 		return result;
1767d4318acdSJeff Layton 
1768d4318acdSJeff Layton 	copied = snprintf(result, len, HAS_SESSION_ENV_PREFIX "%c",
1769d4318acdSJeff Layton 				clp->cl_minorversion ? 'Y' : 'N');
1770d4318acdSJeff Layton 	if (copied >= len) {
1771d4318acdSJeff Layton 		/* just return nothing if output was truncated */
1772d4318acdSJeff Layton 		kfree(result);
1773d4318acdSJeff Layton 		return NULL;
1774d4318acdSJeff Layton 	}
1775d4318acdSJeff Layton 
1776d4318acdSJeff Layton 	return result;
1777d4318acdSJeff Layton }
1778d4318acdSJeff Layton 
1779d4318acdSJeff Layton static char *
nfsd4_cltrack_grace_start(time64_t grace_start)17809cc76801SArnd Bergmann nfsd4_cltrack_grace_start(time64_t grace_start)
1781d4318acdSJeff Layton {
1782d4318acdSJeff Layton 	int copied;
1783d4318acdSJeff Layton 	size_t len;
1784d4318acdSJeff Layton 	char *result;
1785d4318acdSJeff Layton 
1786d4318acdSJeff Layton 	/* prefix + max width of int64_t string + terminating NULL */
1787d4318acdSJeff Layton 	len = strlen(GRACE_START_ENV_PREFIX) + 22 + 1;
1788d4318acdSJeff Layton 
1789d4318acdSJeff Layton 	result = kmalloc(len, GFP_KERNEL);
1790d4318acdSJeff Layton 	if (!result)
1791d4318acdSJeff Layton 		return result;
1792d4318acdSJeff Layton 
17939cc76801SArnd Bergmann 	copied = snprintf(result, len, GRACE_START_ENV_PREFIX "%lld",
1794d4318acdSJeff Layton 				grace_start);
1795d4318acdSJeff Layton 	if (copied >= len) {
1796d4318acdSJeff Layton 		/* just return nothing if output was truncated */
1797d4318acdSJeff Layton 		kfree(result);
1798d4318acdSJeff Layton 		return NULL;
1799d4318acdSJeff Layton 	}
1800d4318acdSJeff Layton 
1801d4318acdSJeff Layton 	return result;
1802d4318acdSJeff Layton }
1803d4318acdSJeff Layton 
1804d4318acdSJeff Layton static int
nfsd4_umh_cltrack_upcall(char * cmd,char * arg,char * env0,char * env1)1805d4318acdSJeff Layton nfsd4_umh_cltrack_upcall(char *cmd, char *arg, char *env0, char *env1)
1806d4318acdSJeff Layton {
1807d4318acdSJeff Layton 	char *envp[3];
18082873d214SJeff Layton 	char *argv[4];
18092873d214SJeff Layton 	int ret;
18102873d214SJeff Layton 
18112873d214SJeff Layton 	if (unlikely(!cltrack_prog[0])) {
18122873d214SJeff Layton 		dprintk("%s: cltrack_prog is disabled\n", __func__);
18132873d214SJeff Layton 		return -EACCES;
18142873d214SJeff Layton 	}
18152873d214SJeff Layton 
18162873d214SJeff Layton 	dprintk("%s: cmd: %s\n", __func__, cmd);
18172873d214SJeff Layton 	dprintk("%s: arg: %s\n", __func__, arg ? arg : "(null)");
1818d4318acdSJeff Layton 	dprintk("%s: env0: %s\n", __func__, env0 ? env0 : "(null)");
1819d4318acdSJeff Layton 	dprintk("%s: env1: %s\n", __func__, env1 ? env1 : "(null)");
1820f3aa7e24SJeff Layton 
1821d4318acdSJeff Layton 	envp[0] = env0;
1822d4318acdSJeff Layton 	envp[1] = env1;
1823d4318acdSJeff Layton 	envp[2] = NULL;
18242873d214SJeff Layton 
18252873d214SJeff Layton 	argv[0] = (char *)cltrack_prog;
18262873d214SJeff Layton 	argv[1] = cmd;
18272873d214SJeff Layton 	argv[2] = arg;
18282873d214SJeff Layton 	argv[3] = NULL;
18292873d214SJeff Layton 
18302873d214SJeff Layton 	ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
18312873d214SJeff Layton 	/*
18322873d214SJeff Layton 	 * Disable the upcall mechanism if we're getting an ENOENT or EACCES
18332873d214SJeff Layton 	 * error. The admin can re-enable it on the fly by using sysfs
18342873d214SJeff Layton 	 * once the problem has been fixed.
18352873d214SJeff Layton 	 */
18362873d214SJeff Layton 	if (ret == -ENOENT || ret == -EACCES) {
18372873d214SJeff Layton 		dprintk("NFSD: %s was not found or isn't executable (%d). "
18382873d214SJeff Layton 			"Setting cltrack_prog to blank string!",
18392873d214SJeff Layton 			cltrack_prog, ret);
18402873d214SJeff Layton 		cltrack_prog[0] = '\0';
18412873d214SJeff Layton 	}
18422873d214SJeff Layton 	dprintk("%s: %s return value: %d\n", __func__, cltrack_prog, ret);
18432873d214SJeff Layton 
18442873d214SJeff Layton 	return ret;
18452873d214SJeff Layton }
18462873d214SJeff Layton 
18472873d214SJeff Layton static char *
bin_to_hex_dup(const unsigned char * src,int srclen)18482873d214SJeff Layton bin_to_hex_dup(const unsigned char *src, int srclen)
18492873d214SJeff Layton {
185012b4157bSAndy Shevchenko 	char *buf;
18512873d214SJeff Layton 
18522873d214SJeff Layton 	/* +1 for terminating NULL */
185312b4157bSAndy Shevchenko 	buf = kzalloc((srclen * 2) + 1, GFP_KERNEL);
18542873d214SJeff Layton 	if (!buf)
18552873d214SJeff Layton 		return buf;
18562873d214SJeff Layton 
185712b4157bSAndy Shevchenko 	bin2hex(buf, src, srclen);
18582873d214SJeff Layton 	return buf;
18592873d214SJeff Layton }
18602873d214SJeff Layton 
18612873d214SJeff Layton static int
nfsd4_umh_cltrack_init(struct net * net)1862d4318acdSJeff Layton nfsd4_umh_cltrack_init(struct net *net)
18632873d214SJeff Layton {
1864d4318acdSJeff Layton 	int ret;
1865d4318acdSJeff Layton 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
1866d4318acdSJeff Layton 	char *grace_start = nfsd4_cltrack_grace_start(nn->boot_time);
1867d4318acdSJeff Layton 
186871a50306SStanislav Kinsbursky 	/* XXX: The usermode helper s not working in container yet. */
186971a50306SStanislav Kinsbursky 	if (net != &init_net) {
187046cc8ba3SPaul Gortmaker 		pr_warn("NFSD: attempt to initialize umh client tracking in a container ignored.\n");
1871956ccef3SSudip Mukherjee 		kfree(grace_start);
187271a50306SStanislav Kinsbursky 		return -EINVAL;
187371a50306SStanislav Kinsbursky 	}
1874d4318acdSJeff Layton 
1875d4318acdSJeff Layton 	ret = nfsd4_umh_cltrack_upcall("init", NULL, grace_start, NULL);
1876d4318acdSJeff Layton 	kfree(grace_start);
187786921607SScott Mayhew 	if (!ret)
1878f988a7b7SPaul Menzel 		pr_info("NFSD: Using UMH upcall client tracking operations.\n");
1879d4318acdSJeff Layton 	return ret;
18802873d214SJeff Layton }
18812873d214SJeff Layton 
18822873d214SJeff Layton static void
nfsd4_cltrack_upcall_lock(struct nfs4_client * clp)1883d682e750SJeff Layton nfsd4_cltrack_upcall_lock(struct nfs4_client *clp)
1884d682e750SJeff Layton {
1885d682e750SJeff Layton 	wait_on_bit_lock(&clp->cl_flags, NFSD4_CLIENT_UPCALL_LOCK,
1886d682e750SJeff Layton 			 TASK_UNINTERRUPTIBLE);
1887d682e750SJeff Layton }
1888d682e750SJeff Layton 
1889d682e750SJeff Layton static void
nfsd4_cltrack_upcall_unlock(struct nfs4_client * clp)1890d682e750SJeff Layton nfsd4_cltrack_upcall_unlock(struct nfs4_client *clp)
1891d682e750SJeff Layton {
1892d682e750SJeff Layton 	smp_mb__before_atomic();
1893d682e750SJeff Layton 	clear_bit(NFSD4_CLIENT_UPCALL_LOCK, &clp->cl_flags);
1894d682e750SJeff Layton 	smp_mb__after_atomic();
1895d682e750SJeff Layton 	wake_up_bit(&clp->cl_flags, NFSD4_CLIENT_UPCALL_LOCK);
1896d682e750SJeff Layton }
1897d682e750SJeff Layton 
1898d682e750SJeff Layton static void
nfsd4_umh_cltrack_create(struct nfs4_client * clp)18992873d214SJeff Layton nfsd4_umh_cltrack_create(struct nfs4_client *clp)
19002873d214SJeff Layton {
1901d4318acdSJeff Layton 	char *hexid, *has_session, *grace_start;
1902d4318acdSJeff Layton 	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
19032873d214SJeff Layton 
190465decb65SJeff Layton 	/*
190565decb65SJeff Layton 	 * With v4.0 clients, there's little difference in outcome between a
190665decb65SJeff Layton 	 * create and check operation, and we can end up calling into this
190765decb65SJeff Layton 	 * function multiple times per client (once for each openowner). So,
190865decb65SJeff Layton 	 * for v4.0 clients skip upcalling once the client has been recorded
190965decb65SJeff Layton 	 * on stable storage.
191065decb65SJeff Layton 	 *
191165decb65SJeff Layton 	 * For v4.1+ clients, the outcome of the two operations is different,
191265decb65SJeff Layton 	 * so we must ensure that we upcall for the create operation. v4.1+
191365decb65SJeff Layton 	 * clients call this on RECLAIM_COMPLETE though, so we should only end
191465decb65SJeff Layton 	 * up doing a single create upcall per client.
191565decb65SJeff Layton 	 */
191665decb65SJeff Layton 	if (clp->cl_minorversion == 0 &&
191765decb65SJeff Layton 	    test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
191865decb65SJeff Layton 		return;
191965decb65SJeff Layton 
19202873d214SJeff Layton 	hexid = bin_to_hex_dup(clp->cl_name.data, clp->cl_name.len);
19212873d214SJeff Layton 	if (!hexid) {
19222873d214SJeff Layton 		dprintk("%s: can't allocate memory for upcall!\n", __func__);
19232873d214SJeff Layton 		return;
19242873d214SJeff Layton 	}
1925d682e750SJeff Layton 
1926d4318acdSJeff Layton 	has_session = nfsd4_cltrack_client_has_session(clp);
1927d4318acdSJeff Layton 	grace_start = nfsd4_cltrack_grace_start(nn->boot_time);
1928d682e750SJeff Layton 
1929d682e750SJeff Layton 	nfsd4_cltrack_upcall_lock(clp);
1930788a7914SJeff Layton 	if (!nfsd4_umh_cltrack_upcall("create", hexid, has_session, grace_start))
1931788a7914SJeff Layton 		set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
1932d682e750SJeff Layton 	nfsd4_cltrack_upcall_unlock(clp);
1933d682e750SJeff Layton 
1934d4318acdSJeff Layton 	kfree(has_session);
1935d4318acdSJeff Layton 	kfree(grace_start);
19362873d214SJeff Layton 	kfree(hexid);
19372873d214SJeff Layton }
19382873d214SJeff Layton 
19392873d214SJeff Layton static void
nfsd4_umh_cltrack_remove(struct nfs4_client * clp)19402873d214SJeff Layton nfsd4_umh_cltrack_remove(struct nfs4_client *clp)
19412873d214SJeff Layton {
19422873d214SJeff Layton 	char *hexid;
19432873d214SJeff Layton 
1944788a7914SJeff Layton 	if (!test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
1945788a7914SJeff Layton 		return;
1946788a7914SJeff Layton 
19472873d214SJeff Layton 	hexid = bin_to_hex_dup(clp->cl_name.data, clp->cl_name.len);
19482873d214SJeff Layton 	if (!hexid) {
19492873d214SJeff Layton 		dprintk("%s: can't allocate memory for upcall!\n", __func__);
19502873d214SJeff Layton 		return;
19512873d214SJeff Layton 	}
1952d682e750SJeff Layton 
1953d682e750SJeff Layton 	nfsd4_cltrack_upcall_lock(clp);
1954788a7914SJeff Layton 	if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags) &&
1955788a7914SJeff Layton 	    nfsd4_umh_cltrack_upcall("remove", hexid, NULL, NULL) == 0)
1956788a7914SJeff Layton 		clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
1957d682e750SJeff Layton 	nfsd4_cltrack_upcall_unlock(clp);
1958d682e750SJeff Layton 
19592873d214SJeff Layton 	kfree(hexid);
19602873d214SJeff Layton }
19612873d214SJeff Layton 
19622873d214SJeff Layton static int
nfsd4_umh_cltrack_check(struct nfs4_client * clp)19632873d214SJeff Layton nfsd4_umh_cltrack_check(struct nfs4_client *clp)
19642873d214SJeff Layton {
19652873d214SJeff Layton 	int ret;
1966d4318acdSJeff Layton 	char *hexid, *has_session, *legacy;
19672873d214SJeff Layton 
1968788a7914SJeff Layton 	if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
1969788a7914SJeff Layton 		return 0;
1970788a7914SJeff Layton 
19712873d214SJeff Layton 	hexid = bin_to_hex_dup(clp->cl_name.data, clp->cl_name.len);
19722873d214SJeff Layton 	if (!hexid) {
19732873d214SJeff Layton 		dprintk("%s: can't allocate memory for upcall!\n", __func__);
19742873d214SJeff Layton 		return -ENOMEM;
19752873d214SJeff Layton 	}
1976d4318acdSJeff Layton 
1977d4318acdSJeff Layton 	has_session = nfsd4_cltrack_client_has_session(clp);
19782216d449SJeff Layton 	legacy = nfsd4_cltrack_legacy_recdir(&clp->cl_name);
1979d682e750SJeff Layton 
1980d682e750SJeff Layton 	nfsd4_cltrack_upcall_lock(clp);
1981788a7914SJeff Layton 	if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) {
1982788a7914SJeff Layton 		ret = 0;
1983788a7914SJeff Layton 	} else {
1984d4318acdSJeff Layton 		ret = nfsd4_umh_cltrack_upcall("check", hexid, has_session, legacy);
1985788a7914SJeff Layton 		if (ret == 0)
1986788a7914SJeff Layton 			set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
1987788a7914SJeff Layton 	}
1988d682e750SJeff Layton 	nfsd4_cltrack_upcall_unlock(clp);
1989d4318acdSJeff Layton 	kfree(has_session);
1990f3aa7e24SJeff Layton 	kfree(legacy);
19912873d214SJeff Layton 	kfree(hexid);
1992d4318acdSJeff Layton 
19932873d214SJeff Layton 	return ret;
19942873d214SJeff Layton }
19952873d214SJeff Layton 
19962873d214SJeff Layton static void
nfsd4_umh_cltrack_grace_done(struct nfsd_net * nn)1997919b8049SJeff Layton nfsd4_umh_cltrack_grace_done(struct nfsd_net *nn)
19982873d214SJeff Layton {
1999f3aa7e24SJeff Layton 	char *legacy;
20002873d214SJeff Layton 	char timestr[22]; /* FIXME: better way to determine max size? */
20012873d214SJeff Layton 
20029cc76801SArnd Bergmann 	sprintf(timestr, "%lld", nn->boot_time);
2003f3aa7e24SJeff Layton 	legacy = nfsd4_cltrack_legacy_topdir();
2004d4318acdSJeff Layton 	nfsd4_umh_cltrack_upcall("gracedone", timestr, legacy, NULL);
2005f3aa7e24SJeff Layton 	kfree(legacy);
20062873d214SJeff Layton }
20072873d214SJeff Layton 
20087c582e4fSJulia Lawall static const struct nfsd4_client_tracking_ops nfsd4_umh_tracking_ops = {
20092873d214SJeff Layton 	.init		= nfsd4_umh_cltrack_init,
20102873d214SJeff Layton 	.exit		= NULL,
20112873d214SJeff Layton 	.create		= nfsd4_umh_cltrack_create,
20122873d214SJeff Layton 	.remove		= nfsd4_umh_cltrack_remove,
20132873d214SJeff Layton 	.check		= nfsd4_umh_cltrack_check,
20142873d214SJeff Layton 	.grace_done	= nfsd4_umh_cltrack_grace_done,
201511a60d15SScott Mayhew 	.version	= 1,
201611a60d15SScott Mayhew 	.msglen		= 0,
20172873d214SJeff Layton };
20182873d214SJeff Layton 
20192a4317c5SJeff Layton int
nfsd4_client_tracking_init(struct net * net)20202a4317c5SJeff Layton nfsd4_client_tracking_init(struct net *net)
20212a4317c5SJeff Layton {
20222a4317c5SJeff Layton 	int status;
2023f3f80148SJeff Layton 	struct path path;
20249a9c6478SStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
20252a4317c5SJeff Layton 
20262d77bf0aSJeff Layton 	/* just run the init if it the method is already decided */
20279a9c6478SStanislav Kinsbursky 	if (nn->client_tracking_ops)
20282d77bf0aSJeff Layton 		goto do_init;
20292d77bf0aSJeff Layton 
203086921607SScott Mayhew 	/* First, try to use nfsdcld */
203186921607SScott Mayhew 	nn->client_tracking_ops = &nfsd4_cld_tracking_ops;
203286921607SScott Mayhew 	status = nn->client_tracking_ops->init(net);
203386921607SScott Mayhew 	if (!status)
203486921607SScott Mayhew 		return status;
203586921607SScott Mayhew 	if (status != -ETIMEDOUT) {
203686921607SScott Mayhew 		nn->client_tracking_ops = &nfsd4_cld_tracking_ops_v0;
203786921607SScott Mayhew 		status = nn->client_tracking_ops->init(net);
203886921607SScott Mayhew 		if (!status)
203986921607SScott Mayhew 			return status;
204086921607SScott Mayhew 	}
204186921607SScott Mayhew 
20422d77bf0aSJeff Layton 	/*
204386921607SScott Mayhew 	 * Next, try the UMH upcall.
20442d77bf0aSJeff Layton 	 */
20459a9c6478SStanislav Kinsbursky 	nn->client_tracking_ops = &nfsd4_umh_tracking_ops;
20469a9c6478SStanislav Kinsbursky 	status = nn->client_tracking_ops->init(net);
20472d77bf0aSJeff Layton 	if (!status)
20482d77bf0aSJeff Layton 		return status;
20492d77bf0aSJeff Layton 
20502d77bf0aSJeff Layton 	/*
205186921607SScott Mayhew 	 * Finally, See if the recoverydir exists and is a directory.
205286921607SScott Mayhew 	 * If it is, then use the legacy ops.
20532d77bf0aSJeff Layton 	 */
20549a9c6478SStanislav Kinsbursky 	nn->client_tracking_ops = &nfsd4_legacy_tracking_ops;
2055f3f80148SJeff Layton 	status = kern_path(nfs4_recoverydir(), LOOKUP_FOLLOW, &path);
2056f3f80148SJeff Layton 	if (!status) {
2057e36cb0b8SDavid Howells 		status = d_is_dir(path.dentry);
2058f3f80148SJeff Layton 		path_put(&path);
205986921607SScott Mayhew 		if (!status) {
206086921607SScott Mayhew 			status = -EINVAL;
206186921607SScott Mayhew 			goto out;
206286921607SScott Mayhew 		}
2063f3f80148SJeff Layton 	}
20642a4317c5SJeff Layton 
20652d77bf0aSJeff Layton do_init:
20669a9c6478SStanislav Kinsbursky 	status = nn->client_tracking_ops->init(net);
206786921607SScott Mayhew out:
20682a4317c5SJeff Layton 	if (status) {
20692a4317c5SJeff Layton 		printk(KERN_WARNING "NFSD: Unable to initialize client "
20702a4317c5SJeff Layton 				    "recovery tracking! (%d)\n", status);
20719a9c6478SStanislav Kinsbursky 		nn->client_tracking_ops = NULL;
20722a4317c5SJeff Layton 	}
20732a4317c5SJeff Layton 	return status;
20742a4317c5SJeff Layton }
20752a4317c5SJeff Layton 
20762a4317c5SJeff Layton void
nfsd4_client_tracking_exit(struct net * net)20772a4317c5SJeff Layton nfsd4_client_tracking_exit(struct net *net)
20782a4317c5SJeff Layton {
20799a9c6478SStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
20809a9c6478SStanislav Kinsbursky 
20819a9c6478SStanislav Kinsbursky 	if (nn->client_tracking_ops) {
20829a9c6478SStanislav Kinsbursky 		if (nn->client_tracking_ops->exit)
20839a9c6478SStanislav Kinsbursky 			nn->client_tracking_ops->exit(net);
20849a9c6478SStanislav Kinsbursky 		nn->client_tracking_ops = NULL;
20852a4317c5SJeff Layton 	}
20862a4317c5SJeff Layton }
20872a4317c5SJeff Layton 
20882a4317c5SJeff Layton void
nfsd4_client_record_create(struct nfs4_client * clp)20892a4317c5SJeff Layton nfsd4_client_record_create(struct nfs4_client *clp)
20902a4317c5SJeff Layton {
20919a9c6478SStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
20929a9c6478SStanislav Kinsbursky 
20939a9c6478SStanislav Kinsbursky 	if (nn->client_tracking_ops)
20949a9c6478SStanislav Kinsbursky 		nn->client_tracking_ops->create(clp);
20952a4317c5SJeff Layton }
20962a4317c5SJeff Layton 
20972a4317c5SJeff Layton void
nfsd4_client_record_remove(struct nfs4_client * clp)20982a4317c5SJeff Layton nfsd4_client_record_remove(struct nfs4_client *clp)
20992a4317c5SJeff Layton {
21009a9c6478SStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
21019a9c6478SStanislav Kinsbursky 
21029a9c6478SStanislav Kinsbursky 	if (nn->client_tracking_ops)
21039a9c6478SStanislav Kinsbursky 		nn->client_tracking_ops->remove(clp);
21042a4317c5SJeff Layton }
21052a4317c5SJeff Layton 
21062a4317c5SJeff Layton int
nfsd4_client_record_check(struct nfs4_client * clp)21072a4317c5SJeff Layton nfsd4_client_record_check(struct nfs4_client *clp)
21082a4317c5SJeff Layton {
21099a9c6478SStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
21109a9c6478SStanislav Kinsbursky 
21119a9c6478SStanislav Kinsbursky 	if (nn->client_tracking_ops)
21129a9c6478SStanislav Kinsbursky 		return nn->client_tracking_ops->check(clp);
21132a4317c5SJeff Layton 
21142a4317c5SJeff Layton 	return -EOPNOTSUPP;
21152a4317c5SJeff Layton }
21162a4317c5SJeff Layton 
21172a4317c5SJeff Layton void
nfsd4_record_grace_done(struct nfsd_net * nn)2118919b8049SJeff Layton nfsd4_record_grace_done(struct nfsd_net *nn)
21192a4317c5SJeff Layton {
21209a9c6478SStanislav Kinsbursky 	if (nn->client_tracking_ops)
2121919b8049SJeff Layton 		nn->client_tracking_ops->grace_done(nn);
21222a4317c5SJeff Layton }
2123813fd320SJeff Layton 
2124813fd320SJeff Layton static int
rpc_pipefs_event(struct notifier_block * nb,unsigned long event,void * ptr)2125813fd320SJeff Layton rpc_pipefs_event(struct notifier_block *nb, unsigned long event, void *ptr)
2126813fd320SJeff Layton {
2127813fd320SJeff Layton 	struct super_block *sb = ptr;
2128813fd320SJeff Layton 	struct net *net = sb->s_fs_info;
2129813fd320SJeff Layton 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
2130813fd320SJeff Layton 	struct cld_net *cn = nn->cld_net;
2131813fd320SJeff Layton 	struct dentry *dentry;
2132813fd320SJeff Layton 	int ret = 0;
2133813fd320SJeff Layton 
2134813fd320SJeff Layton 	if (!try_module_get(THIS_MODULE))
2135813fd320SJeff Layton 		return 0;
2136813fd320SJeff Layton 
2137813fd320SJeff Layton 	if (!cn) {
2138813fd320SJeff Layton 		module_put(THIS_MODULE);
2139813fd320SJeff Layton 		return 0;
2140813fd320SJeff Layton 	}
2141813fd320SJeff Layton 
2142813fd320SJeff Layton 	switch (event) {
2143813fd320SJeff Layton 	case RPC_PIPEFS_MOUNT:
2144813fd320SJeff Layton 		dentry = nfsd4_cld_register_sb(sb, cn->cn_pipe);
2145813fd320SJeff Layton 		if (IS_ERR(dentry)) {
2146813fd320SJeff Layton 			ret = PTR_ERR(dentry);
2147813fd320SJeff Layton 			break;
2148813fd320SJeff Layton 		}
2149813fd320SJeff Layton 		cn->cn_pipe->dentry = dentry;
2150813fd320SJeff Layton 		break;
2151813fd320SJeff Layton 	case RPC_PIPEFS_UMOUNT:
2152813fd320SJeff Layton 		if (cn->cn_pipe->dentry)
2153813fd320SJeff Layton 			nfsd4_cld_unregister_sb(cn->cn_pipe);
2154813fd320SJeff Layton 		break;
2155813fd320SJeff Layton 	default:
2156813fd320SJeff Layton 		ret = -ENOTSUPP;
2157813fd320SJeff Layton 		break;
2158813fd320SJeff Layton 	}
2159813fd320SJeff Layton 	module_put(THIS_MODULE);
2160813fd320SJeff Layton 	return ret;
2161813fd320SJeff Layton }
2162813fd320SJeff Layton 
21632355c596SJ. Bruce Fields static struct notifier_block nfsd4_cld_block = {
2164813fd320SJeff Layton 	.notifier_call = rpc_pipefs_event,
2165813fd320SJeff Layton };
2166797a9d79SJeff Layton 
2167797a9d79SJeff Layton int
register_cld_notifier(void)2168797a9d79SJeff Layton register_cld_notifier(void)
2169797a9d79SJeff Layton {
2170b10252c7SAlexander Sverdlin 	WARN_ON(!nfsd_net_id);
2171797a9d79SJeff Layton 	return rpc_pipefs_notifier_register(&nfsd4_cld_block);
2172797a9d79SJeff Layton }
2173797a9d79SJeff Layton 
2174797a9d79SJeff Layton void
unregister_cld_notifier(void)2175797a9d79SJeff Layton unregister_cld_notifier(void)
2176797a9d79SJeff Layton {
2177797a9d79SJeff Layton 	rpc_pipefs_notifier_unregister(&nfsd4_cld_block);
2178797a9d79SJeff Layton }
2179