Blame view

fs/nfsd/nfs4recover.c 50.3 KB
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
1
  /*
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
2
  *  Copyright (c) 2004 The Regents of the University of Michigan.
f3f801486   Jeff Layton   nfsd: add the inf...
3
  *  Copyright (c) 2012 Jeff Layton <jlayton@redhat.com>
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
  *  All rights reserved.
  *
  *  Andy Adamson <andros@citi.umich.edu>
  *
  *  Redistribution and use in source and binary forms, with or without
  *  modification, are permitted provided that the following conditions
  *  are met:
  *
  *  1. Redistributions of source code must retain the above copyright
  *     notice, this list of conditions and the following disclaimer.
  *  2. Redistributions in binary form must reproduce the above copyright
  *     notice, this list of conditions and the following disclaimer in the
  *     documentation and/or other materials provided with the distribution.
  *  3. Neither the name of the University nor the names of its
  *     contributors may be used to endorse or promote products derived
  *     from this software without specific prior written permission.
  *
  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
  *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
  *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  */
1edb82d20   Herbert Xu   nfsd: Use shash
34
  #include <crypto/hash.h>
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
35
  #include <linux/file.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
36
  #include <linux/slab.h>
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
37
  #include <linux/namei.h>
e8edc6e03   Alexey Dobriyan   Detach sched.h fr...
38
  #include <linux/sched.h>
f3f801486   Jeff Layton   nfsd: add the inf...
39
  #include <linux/fs.h>
813fd320c   Jeff Layton   nfsd: add notifie...
40
  #include <linux/module.h>
f3f801486   Jeff Layton   nfsd: add the inf...
41
42
43
44
  #include <net/net_namespace.h>
  #include <linux/sunrpc/rpc_pipe_fs.h>
  #include <linux/sunrpc/clnt.h>
  #include <linux/nfsd/cld.h>
9a74af213   Boaz Harrosh   nfsd: Move privat...
45
46
47
  
  #include "nfsd.h"
  #include "state.h"
0a3adadee   J. Bruce Fields   nfsd: make fs/nfs...
48
  #include "vfs.h"
f3f801486   Jeff Layton   nfsd: add the inf...
49
  #include "netns.h"
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
50
51
  
  #define NFSDDBG_FACILITY                NFSDDBG_PROC
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
52
53
54
55
56
57
58
  /* Declarations */
  struct nfsd4_client_tracking_ops {
  	int (*init)(struct net *);
  	void (*exit)(struct net *);
  	void (*create)(struct nfs4_client *);
  	void (*remove)(struct nfs4_client *);
  	int (*check)(struct nfs4_client *);
919b8049f   Jeff Layton   nfsd: remove redu...
59
  	void (*grace_done)(struct nfsd_net *);
11a60d159   Scott Mayhew   nfsd: add a "GetV...
60
61
  	uint8_t version;
  	size_t msglen;
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
62
  };
11a60d159   Scott Mayhew   nfsd: add a "GetV...
63
  static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops;
6ee95d1c8   Scott Mayhew   nfsd: add support...
64
  static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops_v2;
11a60d159   Scott Mayhew   nfsd: add a "GetV...
65

190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
66
  /* Globals */
48483bf23   J. Bruce Fields   nfsd4: simplify r...
67
  static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery";
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
68

d84f4f992   David Howells   CRED: Inaugurate ...
69
70
  static int
  nfs4_save_creds(const struct cred **original_creds)
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
71
  {
d84f4f992   David Howells   CRED: Inaugurate ...
72
73
74
75
76
  	struct cred *new;
  
  	new = prepare_creds();
  	if (!new)
  		return -ENOMEM;
6fab87790   Eric W. Biederman   nfsd: Properly co...
77
78
  	new->fsuid = GLOBAL_ROOT_UID;
  	new->fsgid = GLOBAL_ROOT_GID;
d84f4f992   David Howells   CRED: Inaugurate ...
79
80
81
  	*original_creds = override_creds(new);
  	put_cred(new);
  	return 0;
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
82
83
84
  }
  
  static void
d84f4f992   David Howells   CRED: Inaugurate ...
85
  nfs4_reset_creds(const struct cred *original)
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
86
  {
d84f4f992   David Howells   CRED: Inaugurate ...
87
  	revert_creds(original);
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
88
  }
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
89
90
91
92
93
94
95
96
97
98
99
100
101
  static void
  md5_to_hex(char *out, char *md5)
  {
  	int i;
  
  	for (i=0; i<16; i++) {
  		unsigned char c = md5[i];
  
  		*out++ = '0' + ((c&0xf0)>>4) + (c>=0xa0)*('a'-'9'-1);
  		*out++ = '0' + (c&0x0f) + ((c&0x0f)>=0x0a)*('a'-'9'-1);
  	}
  	*out = '\0';
  }
2216d449a   Jeff Layton   nfsd: get rid of ...
102
103
  static int
  nfs4_make_rec_clidname(char *dname, const struct xdr_netobj *clname)
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
104
105
  {
  	struct xdr_netobj cksum;
1edb82d20   Herbert Xu   nfsd: Use shash
106
  	struct crypto_shash *tfm;
2216d449a   Jeff Layton   nfsd: get rid of ...
107
  	int status;
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
108
109
110
111
  
  	dprintk("NFSD: nfs4_make_rec_clidname for %.*s
  ",
  			clname->len, clname->data);
1edb82d20   Herbert Xu   nfsd: Use shash
112
113
114
  	tfm = crypto_alloc_shash("md5", 0, 0);
  	if (IS_ERR(tfm)) {
  		status = PTR_ERR(tfm);
350586879   Herbert Xu   [CRYPTO] users: U...
115
  		goto out_no_tfm;
2216d449a   Jeff Layton   nfsd: get rid of ...
116
  	}
1edb82d20   Herbert Xu   nfsd: Use shash
117
  	cksum.len = crypto_shash_digestsize(tfm);
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
118
  	cksum.data = kmalloc(cksum.len, GFP_KERNEL);
2216d449a   Jeff Layton   nfsd: get rid of ...
119
120
  	if (cksum.data == NULL) {
  		status = -ENOMEM;
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
121
   		goto out;
2216d449a   Jeff Layton   nfsd: get rid of ...
122
  	}
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
123

ea794db26   Eric Biggers   nfsd: use crypto_...
124
125
  	status = crypto_shash_tfm_digest(tfm, clname->data, clname->len,
  					 cksum.data);
2216d449a   Jeff Layton   nfsd: get rid of ...
126
  	if (status)
350586879   Herbert Xu   [CRYPTO] users: U...
127
  		goto out;
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
128
129
  
  	md5_to_hex(dname, cksum.data);
2216d449a   Jeff Layton   nfsd: get rid of ...
130
  	status = 0;
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
131
  out:
2bd9e7b62   Krishna Kumar   nfsd: Fix leaked ...
132
  	kfree(cksum.data);
1edb82d20   Herbert Xu   nfsd: Use shash
133
  	crypto_free_shash(tfm);
350586879   Herbert Xu   [CRYPTO] users: U...
134
  out_no_tfm:
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
135
136
  	return status;
  }
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
137

2216d449a   Jeff Layton   nfsd: get rid of ...
138
139
140
141
142
143
  /*
   * If we had an error generating the recdir name for the legacy tracker
   * then warn the admin. If the error doesn't appear to be transient,
   * then disable recovery tracking.
   */
  static void
7255e716b   Jeff Layton   nfsd: fix oops wh...
144
  legacy_recdir_name_error(struct nfs4_client *clp, int error)
2216d449a   Jeff Layton   nfsd: get rid of ...
145
146
147
148
149
150
151
152
153
154
155
156
157
158
  {
  	printk(KERN_ERR "NFSD: unable to generate recoverydir "
  			"name (%d).
  ", error);
  
  	/*
  	 * if the algorithm just doesn't exist, then disable the recovery
  	 * tracker altogether. The crypto libs will generally return this if
  	 * FIPS is enabled as well.
  	 */
  	if (error == -ENOENT) {
  		printk(KERN_ERR "NFSD: disabling legacy clientid tracking. "
  			"Reboot recovery will not function correctly!
  ");
7255e716b   Jeff Layton   nfsd: fix oops wh...
159
  		nfsd4_client_tracking_exit(clp->net);
2216d449a   Jeff Layton   nfsd: get rid of ...
160
161
  	}
  }
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
162
  static void
6b1891052   Scott Mayhew   nfsd: make nfs4_c...
163
164
165
166
  __nfsd4_create_reclaim_record_grace(struct nfs4_client *clp,
  		const char *dname, int len, struct nfsd_net *nn)
  {
  	struct xdr_netobj name;
6ee95d1c8   Scott Mayhew   nfsd: add support...
167
  	struct xdr_netobj princhash = { .len = 0, .data = NULL };
6b1891052   Scott Mayhew   nfsd: make nfs4_c...
168
169
170
171
172
173
174
175
176
177
  	struct nfs4_client_reclaim *crp;
  
  	name.data = kmemdup(dname, len, GFP_KERNEL);
  	if (!name.data) {
  		dprintk("%s: failed to allocate memory for name.data!
  ",
  			__func__);
  		return;
  	}
  	name.len = len;
6ee95d1c8   Scott Mayhew   nfsd: add support...
178
  	crp = nfs4_client_to_reclaim(name, princhash, nn);
6b1891052   Scott Mayhew   nfsd: make nfs4_c...
179
180
181
182
183
184
185
186
  	if (!crp) {
  		kfree(name.data);
  		return;
  	}
  	crp->cr_clp = clp;
  }
  
  static void
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
187
  nfsd4_create_clid_dir(struct nfs4_client *clp)
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
188
  {
d84f4f992   David Howells   CRED: Inaugurate ...
189
  	const struct cred *original_cred;
2216d449a   Jeff Layton   nfsd: get rid of ...
190
  	char dname[HEXDIR_LEN];
e970a573c   Christoph Hellwig   nfsd: open a file...
191
  	struct dentry *dir, *dentry;
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
192
  	int status;
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
193
  	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
194

a52d726bb   Jeff Layton   nfsd: convert nfs...
195
  	if (test_and_set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
7a6ef8c72   J. Bruce Fields   nfsd4: nfsd4_crea...
196
  		return;
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
197
  	if (!nn->rec_file)
7a6ef8c72   J. Bruce Fields   nfsd4: nfsd4_crea...
198
  		return;
2216d449a   Jeff Layton   nfsd: get rid of ...
199
200
201
  
  	status = nfs4_make_rec_clidname(dname, &clp->cl_name);
  	if (status)
7255e716b   Jeff Layton   nfsd: fix oops wh...
202
  		return legacy_recdir_name_error(clp, status);
2216d449a   Jeff Layton   nfsd: get rid of ...
203

d84f4f992   David Howells   CRED: Inaugurate ...
204
205
  	status = nfs4_save_creds(&original_cred);
  	if (status < 0)
7a6ef8c72   J. Bruce Fields   nfsd4: nfsd4_crea...
206
  		return;
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
207

3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
208
  	status = mnt_want_write_file(nn->rec_file);
4a55c1017   Jan Kara   nfsd: Push mnt_wa...
209
  	if (status)
c2236f141   Kinglong Mee   NFSD: Reset creds...
210
  		goto out_creds;
4a55c1017   Jan Kara   nfsd: Push mnt_wa...
211

3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
212
  	dir = nn->rec_file->f_path.dentry;
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
213
  	/* lock the parent */
5955102c9   Al Viro   wrappers for ->i_...
214
  	inode_lock(d_inode(dir));
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
215

e970a573c   Christoph Hellwig   nfsd: open a file...
216
  	dentry = lookup_one_len(dname, dir, HEXDIR_LEN-1);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
217
218
219
220
  	if (IS_ERR(dentry)) {
  		status = PTR_ERR(dentry);
  		goto out_unlock;
  	}
2b0143b5c   David Howells   VFS: normal files...
221
  	if (d_really_is_positive(dentry))
aec39680b   J. Bruce Fields   nfsd4: fix spurio...
222
223
224
225
226
227
228
229
  		/*
  		 * In the 4.1 case, where we're called from
  		 * reclaim_complete(), records from the previous reboot
  		 * may still be left, so this is OK.
  		 *
  		 * In the 4.0 case, we should never get here; but we may
  		 * as well be forgiving and just succeed silently.
  		 */
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
230
  		goto out_put;
2b0143b5c   David Howells   VFS: normal files...
231
  	status = vfs_mkdir(d_inode(dir), dentry, S_IRWXU);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
232
233
234
  out_put:
  	dput(dentry);
  out_unlock:
5955102c9   Al Viro   wrappers for ->i_...
235
  	inode_unlock(d_inode(dir));
0ce0c2b5d   Jeff Layton   nfsd: don't searc...
236
  	if (status == 0) {
6b1891052   Scott Mayhew   nfsd: make nfs4_c...
237
238
239
  		if (nn->in_grace)
  			__nfsd4_create_reclaim_record_grace(clp, dname,
  					HEXDIR_LEN, nn);
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
240
  		vfs_fsync(nn->rec_file, 0);
0ce0c2b5d   Jeff Layton   nfsd: don't searc...
241
  	} else {
6577aac01   Boaz Harrosh   nfsd4: fix failur...
242
243
244
245
  		printk(KERN_ERR "NFSD: failed to write recovery record"
  				" (err %d); please check that %s exists"
  				" and is writeable", status,
  				user_recovery_dirname);
0ce0c2b5d   Jeff Layton   nfsd: don't searc...
246
  	}
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
247
  	mnt_drop_write_file(nn->rec_file);
c2236f141   Kinglong Mee   NFSD: Reset creds...
248
  out_creds:
d84f4f992   David Howells   CRED: Inaugurate ...
249
  	nfs4_reset_creds(original_cred);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
250
  }
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
251
  typedef int (recdir_func)(struct dentry *, struct dentry *, struct nfsd_net *);
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
252

05f4f678b   J. Bruce Fields   nfsd4: don't do l...
253
254
  struct name_list {
  	char name[HEXDIR_LEN];
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
255
256
  	struct list_head list;
  };
bb6f619b3   Al Viro   [readdir] introdu...
257
258
259
260
  struct nfs4_dir_ctx {
  	struct dir_context ctx;
  	struct list_head names;
  };
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
261
  static int
ac7576f4b   Miklos Szeredi   vfs: make first a...
262
  nfsd4_build_namelist(struct dir_context *__ctx, const char *name, int namlen,
afefdbb28   David Howells   [PATCH] VFS: Make...
263
  		loff_t offset, u64 ino, unsigned int d_type)
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
264
  {
ac7576f4b   Miklos Szeredi   vfs: make first a...
265
266
  	struct nfs4_dir_ctx *ctx =
  		container_of(__ctx, struct nfs4_dir_ctx, ctx);
05f4f678b   J. Bruce Fields   nfsd4: don't do l...
267
  	struct name_list *entry;
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
268

05f4f678b   J. Bruce Fields   nfsd4: don't do l...
269
  	if (namlen != HEXDIR_LEN - 1)
b37ad28bc   Al Viro   [PATCH] nfsd: nfs...
270
  		return 0;
05f4f678b   J. Bruce Fields   nfsd4: don't do l...
271
272
  	entry = kmalloc(sizeof(struct name_list), GFP_KERNEL);
  	if (entry == NULL)
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
273
  		return -ENOMEM;
05f4f678b   J. Bruce Fields   nfsd4: don't do l...
274
275
  	memcpy(entry->name, name, HEXDIR_LEN - 1);
  	entry->name[HEXDIR_LEN - 1] = '\0';
bb6f619b3   Al Viro   [readdir] introdu...
276
  	list_add(&entry->list, &ctx->names);
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
277
278
279
280
  	return 0;
  }
  
  static int
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
281
  nfsd4_list_rec_dir(recdir_func *f, struct nfsd_net *nn)
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
282
  {
d84f4f992   David Howells   CRED: Inaugurate ...
283
  	const struct cred *original_cred;
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
284
  	struct dentry *dir = nn->rec_file->f_path.dentry;
ac6614b76   Al Viro   [readdir] constif...
285
286
287
288
  	struct nfs4_dir_ctx ctx = {
  		.ctx.actor = nfsd4_build_namelist,
  		.names = LIST_HEAD_INIT(ctx.names)
  	};
4691b271a   Kinglong Mee   nfsd: Fix a memor...
289
  	struct name_list *entry, *tmp;
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
290
  	int status;
d84f4f992   David Howells   CRED: Inaugurate ...
291
292
293
  	status = nfs4_save_creds(&original_cred);
  	if (status < 0)
  		return status;
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
294

3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
295
  	status = vfs_llseek(nn->rec_file, 0, SEEK_SET);
5b4b299cc   Al Viro   nfsd4_list_rec_di...
296
297
298
299
  	if (status < 0) {
  		nfs4_reset_creds(original_cred);
  		return status;
  	}
5c0ba4e07   Al Viro   [readdir] introdu...
300
  	status = iterate_dir(nn->rec_file, &ctx.ctx);
5955102c9   Al Viro   wrappers for ->i_...
301
  	inode_lock_nested(d_inode(dir), I_MUTEX_PARENT);
4691b271a   Kinglong Mee   nfsd: Fix a memor...
302
303
  
  	list_for_each_entry_safe(entry, tmp, &ctx.names, list) {
5b4b299cc   Al Viro   nfsd4_list_rec_di...
304
305
306
307
308
309
310
  		if (!status) {
  			struct dentry *dentry;
  			dentry = lookup_one_len(entry->name, dir, HEXDIR_LEN-1);
  			if (IS_ERR(dentry)) {
  				status = PTR_ERR(dentry);
  				break;
  			}
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
311
  			status = f(dir, dentry, nn);
5b4b299cc   Al Viro   nfsd4_list_rec_di...
312
  			dput(dentry);
05f4f678b   J. Bruce Fields   nfsd4: don't do l...
313
  		}
05f4f678b   J. Bruce Fields   nfsd4: don't do l...
314
315
  		list_del(&entry->list);
  		kfree(entry);
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
316
  	}
5955102c9   Al Viro   wrappers for ->i_...
317
  	inode_unlock(d_inode(dir));
d84f4f992   David Howells   CRED: Inaugurate ...
318
  	nfs4_reset_creds(original_cred);
4691b271a   Kinglong Mee   nfsd: Fix a memor...
319
320
321
322
323
324
325
  
  	list_for_each_entry_safe(entry, tmp, &ctx.names, list) {
  		dprintk("NFSD: %s. Left entry %s
  ", __func__, entry->name);
  		list_del(&entry->list);
  		kfree(entry);
  	}
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
326
327
328
329
  	return status;
  }
  
  static int
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
330
  nfsd4_unlink_clid_dir(char *name, int namlen, struct nfsd_net *nn)
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
331
  {
e970a573c   Christoph Hellwig   nfsd: open a file...
332
  	struct dentry *dir, *dentry;
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
333
334
335
336
  	int status;
  
  	dprintk("NFSD: nfsd4_unlink_clid_dir. name %.*s
  ", namlen, name);
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
337
  	dir = nn->rec_file->f_path.dentry;
5955102c9   Al Viro   wrappers for ->i_...
338
  	inode_lock_nested(d_inode(dir), I_MUTEX_PARENT);
e970a573c   Christoph Hellwig   nfsd: open a file...
339
  	dentry = lookup_one_len(name, dir, namlen);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
340
341
  	if (IS_ERR(dentry)) {
  		status = PTR_ERR(dentry);
2f9092e10   David Woodhouse   Fix i_mutex vs. r...
342
  		goto out_unlock;
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
343
344
  	}
  	status = -ENOENT;
2b0143b5c   David Howells   VFS: normal files...
345
  	if (d_really_is_negative(dentry))
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
346
  		goto out;
2b0143b5c   David Howells   VFS: normal files...
347
  	status = vfs_rmdir(d_inode(dir), dentry);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
348
349
  out:
  	dput(dentry);
2f9092e10   David Woodhouse   Fix i_mutex vs. r...
350
  out_unlock:
5955102c9   Al Viro   wrappers for ->i_...
351
  	inode_unlock(d_inode(dir));
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
352
353
  	return status;
  }
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
354
  static void
6b1891052   Scott Mayhew   nfsd: make nfs4_c...
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
  __nfsd4_remove_reclaim_record_grace(const char *dname, int len,
  		struct nfsd_net *nn)
  {
  	struct xdr_netobj name;
  	struct nfs4_client_reclaim *crp;
  
  	name.data = kmemdup(dname, len, GFP_KERNEL);
  	if (!name.data) {
  		dprintk("%s: failed to allocate memory for name.data!
  ",
  			__func__);
  		return;
  	}
  	name.len = len;
  	crp = nfsd4_find_reclaim_client(name, nn);
  	kfree(name.data);
  	if (crp)
  		nfs4_remove_reclaim_record(crp, nn);
  }
  
  static void
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
376
377
  nfsd4_remove_clid_dir(struct nfs4_client *clp)
  {
d84f4f992   David Howells   CRED: Inaugurate ...
378
  	const struct cred *original_cred;
2216d449a   Jeff Layton   nfsd: get rid of ...
379
  	char dname[HEXDIR_LEN];
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
380
  	int status;
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
381
  	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
382

3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
383
  	if (!nn->rec_file || !test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
384
  		return;
2216d449a   Jeff Layton   nfsd: get rid of ...
385
386
  	status = nfs4_make_rec_clidname(dname, &clp->cl_name);
  	if (status)
7255e716b   Jeff Layton   nfsd: fix oops wh...
387
  		return legacy_recdir_name_error(clp, status);
2216d449a   Jeff Layton   nfsd: get rid of ...
388

3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
389
  	status = mnt_want_write_file(nn->rec_file);
0622753b8   Dave Hansen   [PATCH] r/o bind ...
390
391
  	if (status)
  		goto out;
a52d726bb   Jeff Layton   nfsd: convert nfs...
392
  	clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
d84f4f992   David Howells   CRED: Inaugurate ...
393
394
395
  
  	status = nfs4_save_creds(&original_cred);
  	if (status < 0)
698d8d875   Jeff Layton   nfsd: fix error h...
396
  		goto out_drop_write;
d84f4f992   David Howells   CRED: Inaugurate ...
397

3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
398
  	status = nfsd4_unlink_clid_dir(dname, HEXDIR_LEN-1, nn);
d84f4f992   David Howells   CRED: Inaugurate ...
399
  	nfs4_reset_creds(original_cred);
0ce0c2b5d   Jeff Layton   nfsd: don't searc...
400
  	if (status == 0) {
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
401
  		vfs_fsync(nn->rec_file, 0);
6b1891052   Scott Mayhew   nfsd: make nfs4_c...
402
403
404
  		if (nn->in_grace)
  			__nfsd4_remove_reclaim_record_grace(dname,
  					HEXDIR_LEN, nn);
0ce0c2b5d   Jeff Layton   nfsd: don't searc...
405
  	}
698d8d875   Jeff Layton   nfsd: fix error h...
406
  out_drop_write:
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
407
  	mnt_drop_write_file(nn->rec_file);
0622753b8   Dave Hansen   [PATCH] r/o bind ...
408
  out:
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
409
410
  	if (status)
  		printk("NFSD: Failed to remove expired client state directory"
2216d449a   Jeff Layton   nfsd: get rid of ...
411
412
  				" %.*s
  ", HEXDIR_LEN, dname);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
413
414
415
  }
  
  static int
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
416
  purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn)
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
417
418
  {
  	int status;
6b1891052   Scott Mayhew   nfsd: make nfs4_c...
419
  	struct xdr_netobj name;
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
420

6b1891052   Scott Mayhew   nfsd: make nfs4_c...
421
422
423
424
425
  	if (child->d_name.len != HEXDIR_LEN - 1) {
  		printk("%s: illegal name %pd in recovery directory
  ",
  				__func__, child);
  		/* Keep trying; maybe the others are OK: */
b37ad28bc   Al Viro   [PATCH] nfsd: nfs...
426
  		return 0;
6b1891052   Scott Mayhew   nfsd: make nfs4_c...
427
428
429
430
431
432
433
434
435
436
437
  	}
  	name.data = kmemdup_nul(child->d_name.name, child->d_name.len, GFP_KERNEL);
  	if (!name.data) {
  		dprintk("%s: failed to allocate memory for name.data!
  ",
  			__func__);
  		goto out;
  	}
  	name.len = HEXDIR_LEN;
  	if (nfs4_has_reclaimed_state(name, nn))
  		goto out_free;
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
438

2b0143b5c   David Howells   VFS: normal files...
439
  	status = vfs_rmdir(d_inode(parent), child);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
440
  	if (status)
a6a9f18f0   Al Viro   nfsd: switch to %...
441
442
443
  		printk("failed to remove client recovery directory %pd
  ",
  				child);
6b1891052   Scott Mayhew   nfsd: make nfs4_c...
444
445
446
  out_free:
  	kfree(name.data);
  out:
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
447
  	/* Keep trying, success or failure: */
b37ad28bc   Al Viro   [PATCH] nfsd: nfs...
448
  	return 0;
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
449
  }
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
450
  static void
919b8049f   Jeff Layton   nfsd: remove redu...
451
  nfsd4_recdir_purge_old(struct nfsd_net *nn)
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
452
  {
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
453
  	int status;
f141f79d7   Stanislav Kinsbursky   nfsd: recovery - ...
454
  	nn->in_grace = false;
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
455
  	if (!nn->rec_file)
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
456
  		return;
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
457
  	status = mnt_want_write_file(nn->rec_file);
0622753b8   Dave Hansen   [PATCH] r/o bind ...
458
459
  	if (status)
  		goto out;
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
460
  	status = nfsd4_list_rec_dir(purge_old, nn);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
461
  	if (status == 0)
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
462
463
  		vfs_fsync(nn->rec_file, 0);
  	mnt_drop_write_file(nn->rec_file);
0622753b8   Dave Hansen   [PATCH] r/o bind ...
464
  out:
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
465
  	nfs4_release_reclaim(nn);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
466
467
  	if (status)
  		printk("nfsd4: failed to purge old clients from recovery"
a6a9f18f0   Al Viro   nfsd: switch to %...
468
469
  			" directory %pD
  ", nn->rec_file);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
470
471
472
  }
  
  static int
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
473
  load_recdir(struct dentry *parent, struct dentry *child, struct nfsd_net *nn)
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
474
  {
6b1891052   Scott Mayhew   nfsd: make nfs4_c...
475
  	struct xdr_netobj name;
6ee95d1c8   Scott Mayhew   nfsd: add support...
476
  	struct xdr_netobj princhash = { .len = 0, .data = NULL };
6b1891052   Scott Mayhew   nfsd: make nfs4_c...
477

190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
478
  	if (child->d_name.len != HEXDIR_LEN - 1) {
6b1891052   Scott Mayhew   nfsd: make nfs4_c...
479
480
481
  		printk("%s: illegal name %pd in recovery directory
  ",
  				__func__, child);
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
482
  		/* Keep trying; maybe the others are OK: */
b37ad28bc   Al Viro   [PATCH] nfsd: nfs...
483
  		return 0;
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
484
  	}
6b1891052   Scott Mayhew   nfsd: make nfs4_c...
485
486
487
488
489
490
491
492
  	name.data = kmemdup_nul(child->d_name.name, child->d_name.len, GFP_KERNEL);
  	if (!name.data) {
  		dprintk("%s: failed to allocate memory for name.data!
  ",
  			__func__);
  		goto out;
  	}
  	name.len = HEXDIR_LEN;
6ee95d1c8   Scott Mayhew   nfsd: add support...
493
  	if (!nfs4_client_to_reclaim(name, princhash, nn))
6b1891052   Scott Mayhew   nfsd: make nfs4_c...
494
495
  		kfree(name.data);
  out:
b37ad28bc   Al Viro   [PATCH] nfsd: nfs...
496
  	return 0;
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
497
  }
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
498
  static int
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
499
  nfsd4_recdir_load(struct net *net) {
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
500
  	int status;
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
501
  	struct nfsd_net *nn =  net_generic(net, nfsd_net_id);
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
502

3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
503
  	if (!nn->rec_file)
e970a573c   Christoph Hellwig   nfsd: open a file...
504
  		return 0;
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
505
  	status = nfsd4_list_rec_dir(load_recdir, nn);
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
506
507
  	if (status)
  		printk("nfsd4: failed loading clients from recovery"
a6a9f18f0   Al Viro   nfsd: switch to %...
508
509
  			" directory %pD
  ", nn->rec_file);
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
510
511
512
513
514
515
  	return status;
  }
  
  /*
   * Hold reference to the recovery directory.
   */
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
516
  static int
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
517
  nfsd4_init_recdir(struct net *net)
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
518
  {
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
519
  	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
d84f4f992   David Howells   CRED: Inaugurate ...
520
521
  	const struct cred *original_cred;
  	int status;
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
522
523
524
  
  	printk("NFSD: Using %s as the NFSv4 state recovery directory
  ",
48483bf23   J. Bruce Fields   nfsd4: simplify r...
525
  			user_recovery_dirname);
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
526

3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
527
  	BUG_ON(nn->rec_file);
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
528

d84f4f992   David Howells   CRED: Inaugurate ...
529
530
531
532
533
534
  	status = nfs4_save_creds(&original_cred);
  	if (status < 0) {
  		printk("NFSD: Unable to change credentials to find recovery"
  		       " directory: error %d
  ",
  		       status);
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
535
  		return status;
d84f4f992   David Howells   CRED: Inaugurate ...
536
  	}
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
537

3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
538
539
  	nn->rec_file = filp_open(user_recovery_dirname, O_RDONLY | O_DIRECTORY, 0);
  	if (IS_ERR(nn->rec_file)) {
c2642ab05   J. Bruce Fields   [PATCH] nfsd4: re...
540
541
  		printk("NFSD: unable to find recovery directory %s
  ",
48483bf23   J. Bruce Fields   nfsd4: simplify r...
542
  				user_recovery_dirname);
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
543
544
  		status = PTR_ERR(nn->rec_file);
  		nn->rec_file = NULL;
e970a573c   Christoph Hellwig   nfsd: open a file...
545
  	}
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
546

d84f4f992   David Howells   CRED: Inaugurate ...
547
  	nfs4_reset_creds(original_cred);
0ce0c2b5d   Jeff Layton   nfsd: don't searc...
548
  	if (!status)
f141f79d7   Stanislav Kinsbursky   nfsd: recovery - ...
549
  		nn->in_grace = true;
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
550
  	return status;
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
551
  }
15d176c19   Kinglong Mee   NFSD: Fix a memor...
552
553
554
555
556
557
558
559
560
561
  static void
  nfsd4_shutdown_recdir(struct net *net)
  {
  	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
  
  	if (!nn->rec_file)
  		return;
  	fput(nn->rec_file);
  	nn->rec_file = NULL;
  }
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
562
563
564
565
566
567
  
  static int
  nfs4_legacy_state_init(struct net *net)
  {
  	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
  	int i;
6da2ec560   Kees Cook   treewide: kmalloc...
568
569
570
  	nn->reclaim_str_hashtbl = kmalloc_array(CLIENT_HASH_SIZE,
  						sizeof(struct list_head),
  						GFP_KERNEL);
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
  	if (!nn->reclaim_str_hashtbl)
  		return -ENOMEM;
  
  	for (i = 0; i < CLIENT_HASH_SIZE; i++)
  		INIT_LIST_HEAD(&nn->reclaim_str_hashtbl[i]);
  	nn->reclaim_str_hashtbl_size = 0;
  
  	return 0;
  }
  
  static void
  nfs4_legacy_state_shutdown(struct net *net)
  {
  	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
  
  	kfree(nn->reclaim_str_hashtbl);
  }
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
588
589
590
591
  static int
  nfsd4_load_reboot_recovery_data(struct net *net)
  {
  	int status;
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
592
  	status = nfsd4_init_recdir(net);
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
593
  	if (status)
15d176c19   Kinglong Mee   NFSD: Fix a memor...
594
595
596
597
598
  		return status;
  
  	status = nfsd4_recdir_load(net);
  	if (status)
  		nfsd4_shutdown_recdir(net);
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
599
600
601
602
603
604
605
  	return status;
  }
  
  static int
  nfsd4_legacy_tracking_init(struct net *net)
  {
  	int status;
cc27e0d40   Jeff Layton   nfsd: don't allow...
606
607
  	/* XXX: The legacy code won't work in a container */
  	if (net != &init_net) {
46cc8ba30   Paul Gortmaker   nfsd: don't WARN/...
608
609
  		pr_warn("NFSD: attempt to initialize legacy client tracking in a container ignored.
  ");
cc27e0d40   Jeff Layton   nfsd: don't allow...
610
611
  		return -EINVAL;
  	}
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
612
  	status = nfs4_legacy_state_init(net);
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
613
  	if (status)
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
614
615
616
617
618
  		return status;
  
  	status = nfsd4_load_reboot_recovery_data(net);
  	if (status)
  		goto err;
869216075   Scott Mayhew   nfsd: re-order cl...
619
620
  	printk("NFSD: Using legacy client tracking operations.
  ");
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
621
622
623
624
  	return 0;
  
  err:
  	nfs4_legacy_state_shutdown(net);
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
625
626
627
628
  	return status;
  }
  
  static void
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
629
630
  nfsd4_legacy_tracking_exit(struct net *net)
  {
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
631
632
633
  	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
  
  	nfs4_release_reclaim(nn);
15d176c19   Kinglong Mee   NFSD: Fix a memor...
634
  	nfsd4_shutdown_recdir(net);
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
635
  	nfs4_legacy_state_shutdown(net);
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
636
  }
48483bf23   J. Bruce Fields   nfsd4: simplify r...
637
638
639
640
641
642
643
644
645
646
647
648
649
  /*
   * Change the NFSv4 recovery directory to recdir.
   */
  int
  nfs4_reset_recoverydir(char *recdir)
  {
  	int status;
  	struct path path;
  
  	status = kern_path(recdir, LOOKUP_FOLLOW, &path);
  	if (status)
  		return status;
  	status = -ENOTDIR;
e36cb0b89   David Howells   VFS: (Scripted) C...
650
  	if (d_is_dir(path.dentry)) {
48483bf23   J. Bruce Fields   nfsd4: simplify r...
651
652
653
654
655
656
657
658
659
660
661
662
  		strcpy(user_recovery_dirname, recdir);
  		status = 0;
  	}
  	path_put(&path);
  	return status;
  }
  
  char *
  nfs4_recoverydir(void)
  {
  	return user_recovery_dirname;
  }
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
663
664
665
666
  
  static int
  nfsd4_check_legacy_client(struct nfs4_client *clp)
  {
2216d449a   Jeff Layton   nfsd: get rid of ...
667
668
  	int status;
  	char dname[HEXDIR_LEN];
0ce0c2b5d   Jeff Layton   nfsd: don't searc...
669
  	struct nfs4_client_reclaim *crp;
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
670
  	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
6b1891052   Scott Mayhew   nfsd: make nfs4_c...
671
  	struct xdr_netobj name;
0ce0c2b5d   Jeff Layton   nfsd: don't searc...
672

2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
673
674
675
  	/* did we already find that this client is stable? */
  	if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
  		return 0;
2216d449a   Jeff Layton   nfsd: get rid of ...
676
677
  	status = nfs4_make_rec_clidname(dname, &clp->cl_name);
  	if (status) {
7255e716b   Jeff Layton   nfsd: fix oops wh...
678
  		legacy_recdir_name_error(clp, status);
2216d449a   Jeff Layton   nfsd: get rid of ...
679
680
  		return status;
  	}
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
681
  	/* look for it in the reclaim hashtable otherwise */
6b1891052   Scott Mayhew   nfsd: make nfs4_c...
682
683
684
685
686
687
688
689
690
691
  	name.data = kmemdup(dname, HEXDIR_LEN, GFP_KERNEL);
  	if (!name.data) {
  		dprintk("%s: failed to allocate memory for name.data!
  ",
  			__func__);
  		goto out_enoent;
  	}
  	name.len = HEXDIR_LEN;
  	crp = nfsd4_find_reclaim_client(name, nn);
  	kfree(name.data);
0ce0c2b5d   Jeff Layton   nfsd: don't searc...
692
  	if (crp) {
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
693
  		set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
0ce0c2b5d   Jeff Layton   nfsd: don't searc...
694
  		crp->cr_clp = clp;
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
695
696
  		return 0;
  	}
6b1891052   Scott Mayhew   nfsd: make nfs4_c...
697
  out_enoent:
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
698
699
  	return -ENOENT;
  }
7c582e4fa   Julia Lawall   nfsd: recover: co...
700
  static const struct nfsd4_client_tracking_ops nfsd4_legacy_tracking_ops = {
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
701
  	.init		= nfsd4_legacy_tracking_init,
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
702
703
704
705
706
  	.exit		= nfsd4_legacy_tracking_exit,
  	.create		= nfsd4_create_clid_dir,
  	.remove		= nfsd4_remove_clid_dir,
  	.check		= nfsd4_check_legacy_client,
  	.grace_done	= nfsd4_recdir_purge_old,
11a60d159   Scott Mayhew   nfsd: add a "GetV...
707
708
  	.version	= 1,
  	.msglen		= 0,
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
709
  };
f3f801486   Jeff Layton   nfsd: add the inf...
710
711
712
713
714
715
716
717
718
719
  /* Globals */
  #define NFSD_PIPE_DIR		"nfsd"
  #define NFSD_CLD_PIPE		"cld"
  
  /* per-net-ns structure for holding cld upcall info */
  struct cld_net {
  	struct rpc_pipe		*cn_pipe;
  	spinlock_t		 cn_lock;
  	struct list_head	 cn_list;
  	unsigned int		 cn_xid;
8a9f4f412   Scott Mayhew   nfsd: handle lega...
720
  	bool			 cn_has_legacy;
6ee95d1c8   Scott Mayhew   nfsd: add support...
721
  	struct crypto_shash	*cn_tfm;
f3f801486   Jeff Layton   nfsd: add the inf...
722
723
724
725
726
  };
  
  struct cld_upcall {
  	struct list_head	 cu_list;
  	struct cld_net		*cu_net;
b493fd31c   Scott Mayhew   nfsd: fix a warni...
727
  	struct completion	 cu_done;
11a60d159   Scott Mayhew   nfsd: add a "GetV...
728
729
730
  	union {
  		struct cld_msg_hdr	 cu_hdr;
  		struct cld_msg		 cu_msg;
6ee95d1c8   Scott Mayhew   nfsd: add support...
731
  		struct cld_msg_v2	 cu_msg_v2;
11a60d159   Scott Mayhew   nfsd: add a "GetV...
732
  	} cu_u;
f3f801486   Jeff Layton   nfsd: add the inf...
733
734
735
  };
  
  static int
df60446cd   Scott Mayhew   nfsd: avoid a NUL...
736
  __cld_pipe_upcall(struct rpc_pipe *pipe, void *cmsg, struct nfsd_net *nn)
f3f801486   Jeff Layton   nfsd: add the inf...
737
738
739
  {
  	int ret;
  	struct rpc_pipe_msg msg;
11a60d159   Scott Mayhew   nfsd: add a "GetV...
740
  	struct cld_upcall *cup = container_of(cmsg, struct cld_upcall, cu_u);
f3f801486   Jeff Layton   nfsd: add the inf...
741
742
743
  
  	memset(&msg, 0, sizeof(msg));
  	msg.data = cmsg;
11a60d159   Scott Mayhew   nfsd: add a "GetV...
744
  	msg.len = nn->client_tracking_ops->msglen;
f3f801486   Jeff Layton   nfsd: add the inf...
745

f3f801486   Jeff Layton   nfsd: add the inf...
746
747
  	ret = rpc_queue_upcall(pipe, &msg);
  	if (ret < 0) {
f3f801486   Jeff Layton   nfsd: add the inf...
748
749
  		goto out;
  	}
b493fd31c   Scott Mayhew   nfsd: fix a warni...
750
  	wait_for_completion(&cup->cu_done);
f3f801486   Jeff Layton   nfsd: add the inf...
751
752
753
754
755
756
757
758
  
  	if (msg.errno < 0)
  		ret = msg.errno;
  out:
  	return ret;
  }
  
  static int
df60446cd   Scott Mayhew   nfsd: avoid a NUL...
759
  cld_pipe_upcall(struct rpc_pipe *pipe, void *cmsg, struct nfsd_net *nn)
f3f801486   Jeff Layton   nfsd: add the inf...
760
761
762
763
764
765
766
767
  {
  	int ret;
  
  	/*
  	 * -EAGAIN occurs when pipe is closed and reopened while there are
  	 *  upcalls queued.
  	 */
  	do {
df60446cd   Scott Mayhew   nfsd: avoid a NUL...
768
  		ret = __cld_pipe_upcall(pipe, cmsg, nn);
f3f801486   Jeff Layton   nfsd: add the inf...
769
770
771
772
773
774
  	} while (ret == -EAGAIN);
  
  	return ret;
  }
  
  static ssize_t
6ee95d1c8   Scott Mayhew   nfsd: add support...
775
  __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg,
74725959c   Scott Mayhew   nfsd: un-deprecat...
776
777
  		struct nfsd_net *nn)
  {
6ee95d1c8   Scott Mayhew   nfsd: add support...
778
779
  	uint8_t cmd, princhashlen;
  	struct xdr_netobj name, princhash = { .len = 0, .data = NULL };
74725959c   Scott Mayhew   nfsd: un-deprecat...
780
  	uint16_t namelen;
8a9f4f412   Scott Mayhew   nfsd: handle lega...
781
  	struct cld_net *cn = nn->cld_net;
74725959c   Scott Mayhew   nfsd: un-deprecat...
782
783
784
785
786
787
  
  	if (get_user(cmd, &cmsg->cm_cmd)) {
  		dprintk("%s: error when copying cmd from userspace", __func__);
  		return -EFAULT;
  	}
  	if (cmd == Cld_GraceStart) {
6ee95d1c8   Scott Mayhew   nfsd: add support...
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
  		if (nn->client_tracking_ops->version >= 2) {
  			const struct cld_clntinfo __user *ci;
  
  			ci = &cmsg->cm_u.cm_clntinfo;
  			if (get_user(namelen, &ci->cc_name.cn_len))
  				return -EFAULT;
  			name.data = memdup_user(&ci->cc_name.cn_id, namelen);
  			if (IS_ERR_OR_NULL(name.data))
  				return -EFAULT;
  			name.len = namelen;
  			get_user(princhashlen, &ci->cc_princhash.cp_len);
  			if (princhashlen > 0) {
  				princhash.data = memdup_user(
  						&ci->cc_princhash.cp_data,
  						princhashlen);
  				if (IS_ERR_OR_NULL(princhash.data))
  					return -EFAULT;
  				princhash.len = princhashlen;
  			} else
  				princhash.len = 0;
  		} else {
  			const struct cld_name __user *cnm;
  
  			cnm = &cmsg->cm_u.cm_name;
  			if (get_user(namelen, &cnm->cn_len))
  				return -EFAULT;
  			name.data = memdup_user(&cnm->cn_id, namelen);
  			if (IS_ERR_OR_NULL(name.data))
  				return -EFAULT;
  			name.len = namelen;
  		}
8a9f4f412   Scott Mayhew   nfsd: handle lega...
819
820
821
822
823
  		if (name.len > 5 && memcmp(name.data, "hash:", 5) == 0) {
  			name.len = name.len - 5;
  			memmove(name.data, name.data + 5, name.len);
  			cn->cn_has_legacy = true;
  		}
6ee95d1c8   Scott Mayhew   nfsd: add support...
824
  		if (!nfs4_client_to_reclaim(name, princhash, nn)) {
74725959c   Scott Mayhew   nfsd: un-deprecat...
825
  			kfree(name.data);
6ee95d1c8   Scott Mayhew   nfsd: add support...
826
  			kfree(princhash.data);
74725959c   Scott Mayhew   nfsd: un-deprecat...
827
828
  			return -EFAULT;
  		}
11a60d159   Scott Mayhew   nfsd: add a "GetV...
829
  		return nn->client_tracking_ops->msglen;
74725959c   Scott Mayhew   nfsd: un-deprecat...
830
831
832
833
834
  	}
  	return -EFAULT;
  }
  
  static ssize_t
f3f801486   Jeff Layton   nfsd: add the inf...
835
836
837
  cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
  {
  	struct cld_upcall *tmp, *cup;
11a60d159   Scott Mayhew   nfsd: add a "GetV...
838
  	struct cld_msg_hdr __user *hdr = (struct cld_msg_hdr __user *)src;
6ee95d1c8   Scott Mayhew   nfsd: add support...
839
  	struct cld_msg_v2 __user *cmsg = (struct cld_msg_v2 __user *)src;
f3f801486   Jeff Layton   nfsd: add the inf...
840
  	uint32_t xid;
ef8a1a10e   Al Viro   nfsd: get rid of ...
841
  	struct nfsd_net *nn = net_generic(file_inode(filp)->i_sb->s_fs_info,
f3f801486   Jeff Layton   nfsd: add the inf...
842
843
  						nfsd_net_id);
  	struct cld_net *cn = nn->cld_net;
74725959c   Scott Mayhew   nfsd: un-deprecat...
844
  	int16_t status;
f3f801486   Jeff Layton   nfsd: add the inf...
845

11a60d159   Scott Mayhew   nfsd: add a "GetV...
846
  	if (mlen != nn->client_tracking_ops->msglen) {
8a7dc4b04   Randy Dunlap   nfsd: fix nfs4rec...
847
848
  		dprintk("%s: got %zu bytes, expected %zu
  ", __func__, mlen,
11a60d159   Scott Mayhew   nfsd: add a "GetV...
849
  			nn->client_tracking_ops->msglen);
f3f801486   Jeff Layton   nfsd: add the inf...
850
851
852
853
  		return -EINVAL;
  	}
  
  	/* copy just the xid so we can try to find that */
11a60d159   Scott Mayhew   nfsd: add a "GetV...
854
  	if (copy_from_user(&xid, &hdr->cm_xid, sizeof(xid)) != 0) {
f3f801486   Jeff Layton   nfsd: add the inf...
855
856
857
  		dprintk("%s: error when copying xid from userspace", __func__);
  		return -EFAULT;
  	}
74725959c   Scott Mayhew   nfsd: un-deprecat...
858
859
860
861
862
  	/*
  	 * copy the status so we know whether to remove the upcall from the
  	 * list (for -EINPROGRESS, we just want to make sure the xid is
  	 * valid, not remove the upcall from the list)
  	 */
11a60d159   Scott Mayhew   nfsd: add a "GetV...
863
  	if (get_user(status, &hdr->cm_status)) {
74725959c   Scott Mayhew   nfsd: un-deprecat...
864
865
866
  		dprintk("%s: error when copying status from userspace", __func__);
  		return -EFAULT;
  	}
f3f801486   Jeff Layton   nfsd: add the inf...
867
868
869
870
  	/* walk the list and find corresponding xid */
  	cup = NULL;
  	spin_lock(&cn->cn_lock);
  	list_for_each_entry(tmp, &cn->cn_list, cu_list) {
11a60d159   Scott Mayhew   nfsd: add a "GetV...
871
  		if (get_unaligned(&tmp->cu_u.cu_hdr.cm_xid) == xid) {
f3f801486   Jeff Layton   nfsd: add the inf...
872
  			cup = tmp;
74725959c   Scott Mayhew   nfsd: un-deprecat...
873
874
  			if (status != -EINPROGRESS)
  				list_del_init(&cup->cu_list);
f3f801486   Jeff Layton   nfsd: add the inf...
875
876
877
878
879
880
881
  			break;
  		}
  	}
  	spin_unlock(&cn->cn_lock);
  
  	/* couldn't find upcall? */
  	if (!cup) {
21f72c9f0   Jeff Layton   nfsd: fix NULL po...
882
883
  		dprintk("%s: couldn't find upcall -- xid=%u
  ", __func__, xid);
f3f801486   Jeff Layton   nfsd: add the inf...
884
885
  		return -EINVAL;
  	}
74725959c   Scott Mayhew   nfsd: un-deprecat...
886
887
  	if (status == -EINPROGRESS)
  		return __cld_pipe_inprogress_downcall(cmsg, nn);
6ee95d1c8   Scott Mayhew   nfsd: add support...
888
  	if (copy_from_user(&cup->cu_u.cu_msg_v2, src, mlen) != 0)
f3f801486   Jeff Layton   nfsd: add the inf...
889
  		return -EFAULT;
b493fd31c   Scott Mayhew   nfsd: fix a warni...
890
  	complete(&cup->cu_done);
f3f801486   Jeff Layton   nfsd: add the inf...
891
892
893
894
895
896
897
898
  	return mlen;
  }
  
  static void
  cld_pipe_destroy_msg(struct rpc_pipe_msg *msg)
  {
  	struct cld_msg *cmsg = msg->data;
  	struct cld_upcall *cup = container_of(cmsg, struct cld_upcall,
11a60d159   Scott Mayhew   nfsd: add a "GetV...
899
  						 cu_u.cu_msg);
f3f801486   Jeff Layton   nfsd: add the inf...
900
901
902
903
  
  	/* errno >= 0 means we got a downcall */
  	if (msg->errno >= 0)
  		return;
b493fd31c   Scott Mayhew   nfsd: fix a warni...
904
  	complete(&cup->cu_done);
f3f801486   Jeff Layton   nfsd: add the inf...
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
  }
  
  static const struct rpc_pipe_ops cld_upcall_ops = {
  	.upcall		= rpc_pipe_generic_upcall,
  	.downcall	= cld_pipe_downcall,
  	.destroy_msg	= cld_pipe_destroy_msg,
  };
  
  static struct dentry *
  nfsd4_cld_register_sb(struct super_block *sb, struct rpc_pipe *pipe)
  {
  	struct dentry *dir, *dentry;
  
  	dir = rpc_d_lookup_sb(sb, NFSD_PIPE_DIR);
  	if (dir == NULL)
  		return ERR_PTR(-ENOENT);
  	dentry = rpc_mkpipe_dentry(dir, NFSD_CLD_PIPE, NULL, pipe);
  	dput(dir);
  	return dentry;
  }
  
  static void
  nfsd4_cld_unregister_sb(struct rpc_pipe *pipe)
  {
  	if (pipe->dentry)
  		rpc_unlink(pipe->dentry);
  }
  
  static struct dentry *
  nfsd4_cld_register_net(struct net *net, struct rpc_pipe *pipe)
  {
  	struct super_block *sb;
  	struct dentry *dentry;
  
  	sb = rpc_get_sb_net(net);
  	if (!sb)
  		return NULL;
  	dentry = nfsd4_cld_register_sb(sb, pipe);
  	rpc_put_sb_net(net);
  	return dentry;
  }
  
  static void
  nfsd4_cld_unregister_net(struct net *net, struct rpc_pipe *pipe)
  {
  	struct super_block *sb;
  
  	sb = rpc_get_sb_net(net);
  	if (sb) {
  		nfsd4_cld_unregister_sb(pipe);
  		rpc_put_sb_net(net);
  	}
  }
  
  /* Initialize rpc_pipefs pipe for communication with client tracking daemon */
  static int
869216075   Scott Mayhew   nfsd: re-order cl...
961
  __nfsd4_init_cld_pipe(struct net *net)
f3f801486   Jeff Layton   nfsd: add the inf...
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
  {
  	int ret;
  	struct dentry *dentry;
  	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
  	struct cld_net *cn;
  
  	if (nn->cld_net)
  		return 0;
  
  	cn = kzalloc(sizeof(*cn), GFP_KERNEL);
  	if (!cn) {
  		ret = -ENOMEM;
  		goto err;
  	}
  
  	cn->cn_pipe = rpc_mkpipe_data(&cld_upcall_ops, RPC_PIPE_WAIT_FOR_OPEN);
  	if (IS_ERR(cn->cn_pipe)) {
  		ret = PTR_ERR(cn->cn_pipe);
  		goto err;
  	}
  	spin_lock_init(&cn->cn_lock);
  	INIT_LIST_HEAD(&cn->cn_list);
  
  	dentry = nfsd4_cld_register_net(net, cn->cn_pipe);
  	if (IS_ERR(dentry)) {
  		ret = PTR_ERR(dentry);
  		goto err_destroy_data;
  	}
  
  	cn->cn_pipe->dentry = dentry;
8a9f4f412   Scott Mayhew   nfsd: handle lega...
992
  	cn->cn_has_legacy = false;
f3f801486   Jeff Layton   nfsd: add the inf...
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
  	nn->cld_net = cn;
  	return 0;
  
  err_destroy_data:
  	rpc_destroy_pipe_data(cn->cn_pipe);
  err:
  	kfree(cn);
  	printk(KERN_ERR "NFSD: unable to create nfsdcld upcall pipe (%d)
  ",
  			ret);
  	return ret;
  }
869216075   Scott Mayhew   nfsd: re-order cl...
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
  static int
  nfsd4_init_cld_pipe(struct net *net)
  {
  	int status;
  
  	status = __nfsd4_init_cld_pipe(net);
  	if (!status)
  		printk("NFSD: Using old nfsdcld client tracking operations.
  ");
  	return status;
  }
f3f801486   Jeff Layton   nfsd: add the inf...
1016
1017
1018
1019
1020
1021
1022
1023
  static void
  nfsd4_remove_cld_pipe(struct net *net)
  {
  	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
  	struct cld_net *cn = nn->cld_net;
  
  	nfsd4_cld_unregister_net(net, cn->cn_pipe);
  	rpc_destroy_pipe_data(cn->cn_pipe);
6ee95d1c8   Scott Mayhew   nfsd: add support...
1024
1025
  	if (cn->cn_tfm)
  		crypto_free_shash(cn->cn_tfm);
f3f801486   Jeff Layton   nfsd: add the inf...
1026
1027
1028
1029
1030
  	kfree(nn->cld_net);
  	nn->cld_net = NULL;
  }
  
  static struct cld_upcall *
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1031
  alloc_cld_upcall(struct nfsd_net *nn)
f3f801486   Jeff Layton   nfsd: add the inf...
1032
1033
  {
  	struct cld_upcall *new, *tmp;
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1034
  	struct cld_net *cn = nn->cld_net;
f3f801486   Jeff Layton   nfsd: add the inf...
1035
1036
1037
1038
1039
1040
1041
1042
1043
  
  	new = kzalloc(sizeof(*new), GFP_KERNEL);
  	if (!new)
  		return new;
  
  	/* FIXME: hard cap on number in flight? */
  restart_search:
  	spin_lock(&cn->cn_lock);
  	list_for_each_entry(tmp, &cn->cn_list, cu_list) {
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1044
  		if (tmp->cu_u.cu_msg.cm_xid == cn->cn_xid) {
f3f801486   Jeff Layton   nfsd: add the inf...
1045
1046
1047
1048
1049
  			cn->cn_xid++;
  			spin_unlock(&cn->cn_lock);
  			goto restart_search;
  		}
  	}
b493fd31c   Scott Mayhew   nfsd: fix a warni...
1050
  	init_completion(&new->cu_done);
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1051
1052
  	new->cu_u.cu_msg.cm_vers = nn->client_tracking_ops->version;
  	put_unaligned(cn->cn_xid++, &new->cu_u.cu_msg.cm_xid);
f3f801486   Jeff Layton   nfsd: add the inf...
1053
1054
1055
  	new->cu_net = cn;
  	list_add(&new->cu_list, &cn->cn_list);
  	spin_unlock(&cn->cn_lock);
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1056
1057
  	dprintk("%s: allocated xid %u
  ", __func__, new->cu_u.cu_msg.cm_xid);
f3f801486   Jeff Layton   nfsd: add the inf...
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
  
  	return new;
  }
  
  static void
  free_cld_upcall(struct cld_upcall *victim)
  {
  	struct cld_net *cn = victim->cu_net;
  
  	spin_lock(&cn->cn_lock);
  	list_del(&victim->cu_list);
  	spin_unlock(&cn->cn_lock);
  	kfree(victim);
  }
  
  /* Ask daemon to create a new record */
  static void
  nfsd4_cld_create(struct nfs4_client *clp)
  {
  	int ret;
  	struct cld_upcall *cup;
c212cecfa   Stanislav Kinsbursky   nfsd: make nfs4_c...
1079
  	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
f3f801486   Jeff Layton   nfsd: add the inf...
1080
1081
1082
1083
1084
  	struct cld_net *cn = nn->cld_net;
  
  	/* Don't upcall if it's already stored */
  	if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
  		return;
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1085
  	cup = alloc_cld_upcall(nn);
f3f801486   Jeff Layton   nfsd: add the inf...
1086
1087
1088
1089
  	if (!cup) {
  		ret = -ENOMEM;
  		goto out_err;
  	}
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1090
1091
1092
  	cup->cu_u.cu_msg.cm_cmd = Cld_Create;
  	cup->cu_u.cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len;
  	memcpy(cup->cu_u.cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data,
f3f801486   Jeff Layton   nfsd: add the inf...
1093
  			clp->cl_name.len);
df60446cd   Scott Mayhew   nfsd: avoid a NUL...
1094
  	ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
f3f801486   Jeff Layton   nfsd: add the inf...
1095
  	if (!ret) {
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1096
  		ret = cup->cu_u.cu_msg.cm_status;
f3f801486   Jeff Layton   nfsd: add the inf...
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
  		set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
  	}
  
  	free_cld_upcall(cup);
  out_err:
  	if (ret)
  		printk(KERN_ERR "NFSD: Unable to create client "
  				"record on stable storage: %d
  ", ret);
  }
  
  /* Ask daemon to create a new record */
  static void
6ee95d1c8   Scott Mayhew   nfsd: add support...
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
  nfsd4_cld_create_v2(struct nfs4_client *clp)
  {
  	int ret;
  	struct cld_upcall *cup;
  	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
  	struct cld_net *cn = nn->cld_net;
  	struct cld_msg_v2 *cmsg;
  	struct crypto_shash *tfm = cn->cn_tfm;
  	struct xdr_netobj cksum;
  	char *principal = NULL;
6ee95d1c8   Scott Mayhew   nfsd: add support...
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
  
  	/* Don't upcall if it's already stored */
  	if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
  		return;
  
  	cup = alloc_cld_upcall(nn);
  	if (!cup) {
  		ret = -ENOMEM;
  		goto out_err;
  	}
  
  	cmsg = &cup->cu_u.cu_msg_v2;
  	cmsg->cm_cmd = Cld_Create;
  	cmsg->cm_u.cm_clntinfo.cc_name.cn_len = clp->cl_name.len;
  	memcpy(cmsg->cm_u.cm_clntinfo.cc_name.cn_id, clp->cl_name.data,
  			clp->cl_name.len);
  	if (clp->cl_cred.cr_raw_principal)
  		principal = clp->cl_cred.cr_raw_principal;
  	else if (clp->cl_cred.cr_principal)
  		principal = clp->cl_cred.cr_principal;
  	if (principal) {
6ee95d1c8   Scott Mayhew   nfsd: add support...
1141
1142
1143
1144
1145
1146
  		cksum.len = crypto_shash_digestsize(tfm);
  		cksum.data = kmalloc(cksum.len, GFP_KERNEL);
  		if (cksum.data == NULL) {
  			ret = -ENOMEM;
  			goto out;
  		}
ea794db26   Eric Biggers   nfsd: use crypto_...
1147
1148
  		ret = crypto_shash_tfm_digest(tfm, principal, strlen(principal),
  					      cksum.data);
6ee95d1c8   Scott Mayhew   nfsd: add support...
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
  		if (ret) {
  			kfree(cksum.data);
  			goto out;
  		}
  		cmsg->cm_u.cm_clntinfo.cc_princhash.cp_len = cksum.len;
  		memcpy(cmsg->cm_u.cm_clntinfo.cc_princhash.cp_data,
  		       cksum.data, cksum.len);
  		kfree(cksum.data);
  	} else
  		cmsg->cm_u.cm_clntinfo.cc_princhash.cp_len = 0;
df60446cd   Scott Mayhew   nfsd: avoid a NUL...
1159
  	ret = cld_pipe_upcall(cn->cn_pipe, cmsg, nn);
6ee95d1c8   Scott Mayhew   nfsd: add support...
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
  	if (!ret) {
  		ret = cmsg->cm_status;
  		set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
  	}
  
  out:
  	free_cld_upcall(cup);
  out_err:
  	if (ret)
  		pr_err("NFSD: Unable to create client record on stable storage: %d
  ",
  				ret);
  }
  
  /* Ask daemon to create a new record */
  static void
f3f801486   Jeff Layton   nfsd: add the inf...
1176
1177
1178
1179
  nfsd4_cld_remove(struct nfs4_client *clp)
  {
  	int ret;
  	struct cld_upcall *cup;
c212cecfa   Stanislav Kinsbursky   nfsd: make nfs4_c...
1180
  	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
f3f801486   Jeff Layton   nfsd: add the inf...
1181
1182
1183
1184
1185
  	struct cld_net *cn = nn->cld_net;
  
  	/* Don't upcall if it's already removed */
  	if (!test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
  		return;
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1186
  	cup = alloc_cld_upcall(nn);
f3f801486   Jeff Layton   nfsd: add the inf...
1187
1188
1189
1190
  	if (!cup) {
  		ret = -ENOMEM;
  		goto out_err;
  	}
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1191
1192
1193
  	cup->cu_u.cu_msg.cm_cmd = Cld_Remove;
  	cup->cu_u.cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len;
  	memcpy(cup->cu_u.cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data,
f3f801486   Jeff Layton   nfsd: add the inf...
1194
  			clp->cl_name.len);
df60446cd   Scott Mayhew   nfsd: avoid a NUL...
1195
  	ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
f3f801486   Jeff Layton   nfsd: add the inf...
1196
  	if (!ret) {
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1197
  		ret = cup->cu_u.cu_msg.cm_status;
f3f801486   Jeff Layton   nfsd: add the inf...
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
  		clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
  	}
  
  	free_cld_upcall(cup);
  out_err:
  	if (ret)
  		printk(KERN_ERR "NFSD: Unable to remove client "
  				"record from stable storage: %d
  ", ret);
  }
74725959c   Scott Mayhew   nfsd: un-deprecat...
1208
1209
1210
1211
1212
1213
  /*
   * For older nfsdcld's that do not allow us to "slurp" the clients
   * from the tracking database during startup.
   *
   * Check for presence of a record, and update its timestamp
   */
f3f801486   Jeff Layton   nfsd: add the inf...
1214
  static int
74725959c   Scott Mayhew   nfsd: un-deprecat...
1215
  nfsd4_cld_check_v0(struct nfs4_client *clp)
f3f801486   Jeff Layton   nfsd: add the inf...
1216
1217
1218
  {
  	int ret;
  	struct cld_upcall *cup;
c212cecfa   Stanislav Kinsbursky   nfsd: make nfs4_c...
1219
  	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
f3f801486   Jeff Layton   nfsd: add the inf...
1220
1221
1222
1223
1224
  	struct cld_net *cn = nn->cld_net;
  
  	/* Don't upcall if one was already stored during this grace pd */
  	if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
  		return 0;
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1225
  	cup = alloc_cld_upcall(nn);
f3f801486   Jeff Layton   nfsd: add the inf...
1226
1227
1228
1229
1230
1231
  	if (!cup) {
  		printk(KERN_ERR "NFSD: Unable to check client record on "
  				"stable storage: %d
  ", -ENOMEM);
  		return -ENOMEM;
  	}
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1232
1233
1234
  	cup->cu_u.cu_msg.cm_cmd = Cld_Check;
  	cup->cu_u.cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len;
  	memcpy(cup->cu_u.cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data,
f3f801486   Jeff Layton   nfsd: add the inf...
1235
  			clp->cl_name.len);
df60446cd   Scott Mayhew   nfsd: avoid a NUL...
1236
  	ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
f3f801486   Jeff Layton   nfsd: add the inf...
1237
  	if (!ret) {
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1238
  		ret = cup->cu_u.cu_msg.cm_status;
f3f801486   Jeff Layton   nfsd: add the inf...
1239
1240
1241
1242
1243
1244
  		set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
  	}
  
  	free_cld_upcall(cup);
  	return ret;
  }
74725959c   Scott Mayhew   nfsd: un-deprecat...
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
  /*
   * For newer nfsdcld's that allow us to "slurp" the clients
   * from the tracking database during startup.
   *
   * Check for presence of a record in the reclaim_str_hashtbl
   */
  static int
  nfsd4_cld_check(struct nfs4_client *clp)
  {
  	struct nfs4_client_reclaim *crp;
  	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
8a9f4f412   Scott Mayhew   nfsd: handle lega...
1256
1257
1258
1259
  	struct cld_net *cn = nn->cld_net;
  	int status;
  	char dname[HEXDIR_LEN];
  	struct xdr_netobj name;
74725959c   Scott Mayhew   nfsd: un-deprecat...
1260
1261
1262
1263
1264
1265
1266
  
  	/* did we already find that this client is stable? */
  	if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
  		return 0;
  
  	/* look for it in the reclaim hashtable otherwise */
  	crp = nfsd4_find_reclaim_client(clp->cl_name, nn);
8a9f4f412   Scott Mayhew   nfsd: handle lega...
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
  	if (crp)
  		goto found;
  
  	if (cn->cn_has_legacy) {
  		status = nfs4_make_rec_clidname(dname, &clp->cl_name);
  		if (status)
  			return -ENOENT;
  
  		name.data = kmemdup(dname, HEXDIR_LEN, GFP_KERNEL);
  		if (!name.data) {
  			dprintk("%s: failed to allocate memory for name.data!
  ",
  				__func__);
  			return -ENOENT;
  		}
  		name.len = HEXDIR_LEN;
  		crp = nfsd4_find_reclaim_client(name, nn);
  		kfree(name.data);
  		if (crp)
  			goto found;
74725959c   Scott Mayhew   nfsd: un-deprecat...
1287

8a9f4f412   Scott Mayhew   nfsd: handle lega...
1288
  	}
74725959c   Scott Mayhew   nfsd: un-deprecat...
1289
  	return -ENOENT;
8a9f4f412   Scott Mayhew   nfsd: handle lega...
1290
1291
1292
  found:
  	crp->cr_clp = clp;
  	return 0;
74725959c   Scott Mayhew   nfsd: un-deprecat...
1293
1294
1295
  }
  
  static int
6ee95d1c8   Scott Mayhew   nfsd: add support...
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
  nfsd4_cld_check_v2(struct nfs4_client *clp)
  {
  	struct nfs4_client_reclaim *crp;
  	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
  	struct cld_net *cn = nn->cld_net;
  	int status;
  	char dname[HEXDIR_LEN];
  	struct xdr_netobj name;
  	struct crypto_shash *tfm = cn->cn_tfm;
  	struct xdr_netobj cksum;
  	char *principal = NULL;
6ee95d1c8   Scott Mayhew   nfsd: add support...
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
  
  	/* did we already find that this client is stable? */
  	if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
  		return 0;
  
  	/* look for it in the reclaim hashtable otherwise */
  	crp = nfsd4_find_reclaim_client(clp->cl_name, nn);
  	if (crp)
  		goto found;
  
  	if (cn->cn_has_legacy) {
  		status = nfs4_make_rec_clidname(dname, &clp->cl_name);
  		if (status)
  			return -ENOENT;
  
  		name.data = kmemdup(dname, HEXDIR_LEN, GFP_KERNEL);
  		if (!name.data) {
  			dprintk("%s: failed to allocate memory for name.data
  ",
  					__func__);
  			return -ENOENT;
  		}
  		name.len = HEXDIR_LEN;
  		crp = nfsd4_find_reclaim_client(name, nn);
  		kfree(name.data);
  		if (crp)
  			goto found;
  
  	}
  	return -ENOENT;
  found:
  	if (crp->cr_princhash.len) {
  		if (clp->cl_cred.cr_raw_principal)
  			principal = clp->cl_cred.cr_raw_principal;
  		else if (clp->cl_cred.cr_principal)
  			principal = clp->cl_cred.cr_principal;
  		if (principal == NULL)
  			return -ENOENT;
6ee95d1c8   Scott Mayhew   nfsd: add support...
1345
1346
1347
1348
  		cksum.len = crypto_shash_digestsize(tfm);
  		cksum.data = kmalloc(cksum.len, GFP_KERNEL);
  		if (cksum.data == NULL)
  			return -ENOENT;
ea794db26   Eric Biggers   nfsd: use crypto_...
1349
1350
  		status = crypto_shash_tfm_digest(tfm, principal,
  						 strlen(principal), cksum.data);
6ee95d1c8   Scott Mayhew   nfsd: add support...
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
  		if (status) {
  			kfree(cksum.data);
  			return -ENOENT;
  		}
  		if (memcmp(crp->cr_princhash.data, cksum.data,
  				crp->cr_princhash.len)) {
  			kfree(cksum.data);
  			return -ENOENT;
  		}
  		kfree(cksum.data);
  	}
  	crp->cr_clp = clp;
  	return 0;
  }
  
  static int
74725959c   Scott Mayhew   nfsd: un-deprecat...
1367
1368
1369
1370
1371
  nfsd4_cld_grace_start(struct nfsd_net *nn)
  {
  	int ret;
  	struct cld_upcall *cup;
  	struct cld_net *cn = nn->cld_net;
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1372
  	cup = alloc_cld_upcall(nn);
74725959c   Scott Mayhew   nfsd: un-deprecat...
1373
1374
1375
1376
  	if (!cup) {
  		ret = -ENOMEM;
  		goto out_err;
  	}
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1377
  	cup->cu_u.cu_msg.cm_cmd = Cld_GraceStart;
df60446cd   Scott Mayhew   nfsd: avoid a NUL...
1378
  	ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
74725959c   Scott Mayhew   nfsd: un-deprecat...
1379
  	if (!ret)
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1380
  		ret = cup->cu_u.cu_msg.cm_status;
74725959c   Scott Mayhew   nfsd: un-deprecat...
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
  
  	free_cld_upcall(cup);
  out_err:
  	if (ret)
  		dprintk("%s: Unable to get clients from userspace: %d
  ",
  			__func__, ret);
  	return ret;
  }
  
  /* For older nfsdcld's that need cm_gracetime */
f3f801486   Jeff Layton   nfsd: add the inf...
1392
  static void
74725959c   Scott Mayhew   nfsd: un-deprecat...
1393
  nfsd4_cld_grace_done_v0(struct nfsd_net *nn)
f3f801486   Jeff Layton   nfsd: add the inf...
1394
1395
1396
  {
  	int ret;
  	struct cld_upcall *cup;
f3f801486   Jeff Layton   nfsd: add the inf...
1397
  	struct cld_net *cn = nn->cld_net;
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1398
  	cup = alloc_cld_upcall(nn);
f3f801486   Jeff Layton   nfsd: add the inf...
1399
1400
1401
1402
  	if (!cup) {
  		ret = -ENOMEM;
  		goto out_err;
  	}
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1403
  	cup->cu_u.cu_msg.cm_cmd = Cld_GraceDone;
9cc768014   Arnd Bergmann   nfsd: make 'boot_...
1404
  	cup->cu_u.cu_msg.cm_u.cm_gracetime = nn->boot_time;
df60446cd   Scott Mayhew   nfsd: avoid a NUL...
1405
  	ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
f3f801486   Jeff Layton   nfsd: add the inf...
1406
  	if (!ret)
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1407
  		ret = cup->cu_u.cu_msg.cm_status;
f3f801486   Jeff Layton   nfsd: add the inf...
1408
1409
1410
1411
1412
1413
1414
  
  	free_cld_upcall(cup);
  out_err:
  	if (ret)
  		printk(KERN_ERR "NFSD: Unable to end grace period: %d
  ", ret);
  }
74725959c   Scott Mayhew   nfsd: un-deprecat...
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
  /*
   * For newer nfsdcld's that do not need cm_gracetime.  We also need to call
   * nfs4_release_reclaim() to clear out the reclaim_str_hashtbl.
   */
  static void
  nfsd4_cld_grace_done(struct nfsd_net *nn)
  {
  	int ret;
  	struct cld_upcall *cup;
  	struct cld_net *cn = nn->cld_net;
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1425
  	cup = alloc_cld_upcall(nn);
74725959c   Scott Mayhew   nfsd: un-deprecat...
1426
1427
1428
1429
  	if (!cup) {
  		ret = -ENOMEM;
  		goto out_err;
  	}
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1430
  	cup->cu_u.cu_msg.cm_cmd = Cld_GraceDone;
df60446cd   Scott Mayhew   nfsd: avoid a NUL...
1431
  	ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
74725959c   Scott Mayhew   nfsd: un-deprecat...
1432
  	if (!ret)
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1433
  		ret = cup->cu_u.cu_msg.cm_status;
74725959c   Scott Mayhew   nfsd: un-deprecat...
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
  
  	free_cld_upcall(cup);
  out_err:
  	nfs4_release_reclaim(nn);
  	if (ret)
  		printk(KERN_ERR "NFSD: Unable to end grace period: %d
  ", ret);
  }
  
  static int
  nfs4_cld_state_init(struct net *net)
  {
  	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
  	int i;
  
  	nn->reclaim_str_hashtbl = kmalloc_array(CLIENT_HASH_SIZE,
  						sizeof(struct list_head),
  						GFP_KERNEL);
  	if (!nn->reclaim_str_hashtbl)
  		return -ENOMEM;
  
  	for (i = 0; i < CLIENT_HASH_SIZE; i++)
  		INIT_LIST_HEAD(&nn->reclaim_str_hashtbl[i]);
  	nn->reclaim_str_hashtbl_size = 0;
362063a59   Scott Mayhew   nfsd: keep a tall...
1458
1459
  	nn->track_reclaim_completes = true;
  	atomic_set(&nn->nr_reclaim_complete, 0);
74725959c   Scott Mayhew   nfsd: un-deprecat...
1460
1461
1462
1463
1464
1465
1466
1467
  
  	return 0;
  }
  
  static void
  nfs4_cld_state_shutdown(struct net *net)
  {
  	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
362063a59   Scott Mayhew   nfsd: keep a tall...
1468
  	nn->track_reclaim_completes = false;
74725959c   Scott Mayhew   nfsd: un-deprecat...
1469
1470
  	kfree(nn->reclaim_str_hashtbl);
  }
869216075   Scott Mayhew   nfsd: re-order cl...
1471
1472
1473
1474
1475
1476
1477
1478
  static bool
  cld_running(struct nfsd_net *nn)
  {
  	struct cld_net *cn = nn->cld_net;
  	struct rpc_pipe *pipe = cn->cn_pipe;
  
  	return pipe->nreaders || pipe->nwriters;
  }
74725959c   Scott Mayhew   nfsd: un-deprecat...
1479
  static int
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
  nfsd4_cld_get_version(struct nfsd_net *nn)
  {
  	int ret = 0;
  	struct cld_upcall *cup;
  	struct cld_net *cn = nn->cld_net;
  	uint8_t version;
  
  	cup = alloc_cld_upcall(nn);
  	if (!cup) {
  		ret = -ENOMEM;
  		goto out_err;
  	}
  	cup->cu_u.cu_msg.cm_cmd = Cld_GetVersion;
df60446cd   Scott Mayhew   nfsd: avoid a NUL...
1493
  	ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
  	if (!ret) {
  		ret = cup->cu_u.cu_msg.cm_status;
  		if (ret)
  			goto out_free;
  		version = cup->cu_u.cu_msg.cm_u.cm_version;
  		dprintk("%s: userspace returned version %u
  ",
  				__func__, version);
  		if (version < 1)
  			version = 1;
  		else if (version > CLD_UPCALL_VERSION)
  			version = CLD_UPCALL_VERSION;
  
  		switch (version) {
  		case 1:
  			nn->client_tracking_ops = &nfsd4_cld_tracking_ops;
  			break;
6ee95d1c8   Scott Mayhew   nfsd: add support...
1511
1512
1513
  		case 2:
  			nn->client_tracking_ops = &nfsd4_cld_tracking_ops_v2;
  			break;
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
  		default:
  			break;
  		}
  	}
  out_free:
  	free_cld_upcall(cup);
  out_err:
  	if (ret)
  		dprintk("%s: Unable to get version from userspace: %d
  ",
  			__func__, ret);
  	return ret;
  }
  
  static int
74725959c   Scott Mayhew   nfsd: un-deprecat...
1529
1530
1531
1532
  nfsd4_cld_tracking_init(struct net *net)
  {
  	int status;
  	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
869216075   Scott Mayhew   nfsd: re-order cl...
1533
1534
  	bool running;
  	int retries = 10;
18b9a895e   Scott Mayhew   nfsd: Fix cld_net...
1535
  	struct crypto_shash *tfm;
74725959c   Scott Mayhew   nfsd: un-deprecat...
1536
1537
1538
1539
  
  	status = nfs4_cld_state_init(net);
  	if (status)
  		return status;
869216075   Scott Mayhew   nfsd: re-order cl...
1540
  	status = __nfsd4_init_cld_pipe(net);
74725959c   Scott Mayhew   nfsd: un-deprecat...
1541
1542
  	if (status)
  		goto err_shutdown;
869216075   Scott Mayhew   nfsd: re-order cl...
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
  	/*
  	 * rpc pipe upcalls take 30 seconds to time out, so we don't want to
  	 * queue an upcall unless we know that nfsdcld is running (because we
  	 * want this to fail fast so that nfsd4_client_tracking_init() can try
  	 * the next client tracking method).  nfsdcld should already be running
  	 * before nfsd is started, so the wait here is for nfsdcld to open the
  	 * pipefs file we just created.
  	 */
  	while (!(running = cld_running(nn)) && retries--)
  		msleep(100);
  
  	if (!running) {
  		status = -ETIMEDOUT;
  		goto err_remove;
  	}
18b9a895e   Scott Mayhew   nfsd: Fix cld_net...
1558
1559
1560
1561
1562
1563
  	tfm = crypto_alloc_shash("sha256", 0, 0);
  	if (IS_ERR(tfm)) {
  		status = PTR_ERR(tfm);
  		goto err_remove;
  	}
  	nn->cld_net->cn_tfm = tfm;
869216075   Scott Mayhew   nfsd: re-order cl...
1564

11a60d159   Scott Mayhew   nfsd: add a "GetV...
1565
1566
1567
1568
  	status = nfsd4_cld_get_version(nn);
  	if (status == -EOPNOTSUPP)
  		pr_warn("NFSD: nfsdcld GetVersion upcall failed. Please upgrade nfsdcld.
  ");
74725959c   Scott Mayhew   nfsd: un-deprecat...
1569
1570
1571
  	status = nfsd4_cld_grace_start(nn);
  	if (status) {
  		if (status == -EOPNOTSUPP)
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1572
1573
  			pr_warn("NFSD: nfsdcld GraceStart upcall failed. Please upgrade nfsdcld.
  ");
74725959c   Scott Mayhew   nfsd: un-deprecat...
1574
1575
  		nfs4_release_reclaim(nn);
  		goto err_remove;
869216075   Scott Mayhew   nfsd: re-order cl...
1576
1577
1578
  	} else
  		printk("NFSD: Using nfsdcld client tracking operations.
  ");
74725959c   Scott Mayhew   nfsd: un-deprecat...
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
  	return 0;
  
  err_remove:
  	nfsd4_remove_cld_pipe(net);
  err_shutdown:
  	nfs4_cld_state_shutdown(net);
  	return status;
  }
  
  static void
  nfsd4_cld_tracking_exit(struct net *net)
  {
  	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
  
  	nfs4_release_reclaim(nn);
  	nfsd4_remove_cld_pipe(net);
  	nfs4_cld_state_shutdown(net);
  }
  
  /* For older nfsdcld's */
  static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops_v0 = {
f3f801486   Jeff Layton   nfsd: add the inf...
1600
1601
1602
1603
  	.init		= nfsd4_init_cld_pipe,
  	.exit		= nfsd4_remove_cld_pipe,
  	.create		= nfsd4_cld_create,
  	.remove		= nfsd4_cld_remove,
74725959c   Scott Mayhew   nfsd: un-deprecat...
1604
1605
  	.check		= nfsd4_cld_check_v0,
  	.grace_done	= nfsd4_cld_grace_done_v0,
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1606
1607
  	.version	= 1,
  	.msglen		= sizeof(struct cld_msg),
74725959c   Scott Mayhew   nfsd: un-deprecat...
1608
1609
1610
1611
1612
1613
1614
1615
  };
  
  /* For newer nfsdcld's */
  static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops = {
  	.init		= nfsd4_cld_tracking_init,
  	.exit		= nfsd4_cld_tracking_exit,
  	.create		= nfsd4_cld_create,
  	.remove		= nfsd4_cld_remove,
f3f801486   Jeff Layton   nfsd: add the inf...
1616
1617
  	.check		= nfsd4_cld_check,
  	.grace_done	= nfsd4_cld_grace_done,
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1618
1619
  	.version	= 1,
  	.msglen		= sizeof(struct cld_msg),
f3f801486   Jeff Layton   nfsd: add the inf...
1620
  };
6ee95d1c8   Scott Mayhew   nfsd: add support...
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
  /* v2 create/check ops include the principal, if available */
  static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops_v2 = {
  	.init		= nfsd4_cld_tracking_init,
  	.exit		= nfsd4_cld_tracking_exit,
  	.create		= nfsd4_cld_create_v2,
  	.remove		= nfsd4_cld_remove,
  	.check		= nfsd4_cld_check_v2,
  	.grace_done	= nfsd4_cld_grace_done,
  	.version	= 2,
  	.msglen		= sizeof(struct cld_msg_v2),
  };
2873d2147   Jeff Layton   nfsd: add a userm...
1632
1633
1634
1635
1636
  /* upcall via usermodehelper */
  static char cltrack_prog[PATH_MAX] = "/sbin/nfsdcltrack";
  module_param_string(cltrack_prog, cltrack_prog, sizeof(cltrack_prog),
  			S_IRUGO|S_IWUSR);
  MODULE_PARM_DESC(cltrack_prog, "Path to the nfsdcltrack upcall program");
f3aa7e24c   Jeff Layton   nfsd: pass info a...
1637
1638
1639
1640
1641
1642
1643
  static bool cltrack_legacy_disable;
  module_param(cltrack_legacy_disable, bool, S_IRUGO|S_IWUSR);
  MODULE_PARM_DESC(cltrack_legacy_disable,
  		"Disable legacy recoverydir conversion. Default: false");
  
  #define LEGACY_TOPDIR_ENV_PREFIX "NFSDCLTRACK_LEGACY_TOPDIR="
  #define LEGACY_RECDIR_ENV_PREFIX "NFSDCLTRACK_LEGACY_RECDIR="
d4318acd5   Jeff Layton   nfsd: pass extra ...
1644
1645
  #define HAS_SESSION_ENV_PREFIX "NFSDCLTRACK_CLIENT_HAS_SESSION="
  #define GRACE_START_ENV_PREFIX "NFSDCLTRACK_GRACE_START="
f3aa7e24c   Jeff Layton   nfsd: pass info a...
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
  
  static char *
  nfsd4_cltrack_legacy_topdir(void)
  {
  	int copied;
  	size_t len;
  	char *result;
  
  	if (cltrack_legacy_disable)
  		return NULL;
  
  	len = strlen(LEGACY_TOPDIR_ENV_PREFIX) +
  		strlen(nfs4_recoverydir()) + 1;
  
  	result = kmalloc(len, GFP_KERNEL);
  	if (!result)
  		return result;
  
  	copied = snprintf(result, len, LEGACY_TOPDIR_ENV_PREFIX "%s",
  				nfs4_recoverydir());
  	if (copied >= len) {
  		/* just return nothing if output was truncated */
  		kfree(result);
  		return NULL;
  	}
  
  	return result;
  }
  
  static char *
2216d449a   Jeff Layton   nfsd: get rid of ...
1676
  nfsd4_cltrack_legacy_recdir(const struct xdr_netobj *name)
f3aa7e24c   Jeff Layton   nfsd: pass info a...
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
  {
  	int copied;
  	size_t len;
  	char *result;
  
  	if (cltrack_legacy_disable)
  		return NULL;
  
  	/* +1 is for '/' between "topdir" and "recdir" */
  	len = strlen(LEGACY_RECDIR_ENV_PREFIX) +
  		strlen(nfs4_recoverydir()) + 1 + HEXDIR_LEN;
  
  	result = kmalloc(len, GFP_KERNEL);
  	if (!result)
  		return result;
2216d449a   Jeff Layton   nfsd: get rid of ...
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
  	copied = snprintf(result, len, LEGACY_RECDIR_ENV_PREFIX "%s/",
  				nfs4_recoverydir());
  	if (copied > (len - HEXDIR_LEN)) {
  		/* just return nothing if output will be truncated */
  		kfree(result);
  		return NULL;
  	}
  
  	copied = nfs4_make_rec_clidname(result + copied, name);
  	if (copied) {
f3aa7e24c   Jeff Layton   nfsd: pass info a...
1702
1703
1704
1705
1706
1707
  		kfree(result);
  		return NULL;
  	}
  
  	return result;
  }
d4318acd5   Jeff Layton   nfsd: pass extra ...
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
  static char *
  nfsd4_cltrack_client_has_session(struct nfs4_client *clp)
  {
  	int copied;
  	size_t len;
  	char *result;
  
  	/* prefix + Y/N character + terminating NULL */
  	len = strlen(HAS_SESSION_ENV_PREFIX) + 1 + 1;
  
  	result = kmalloc(len, GFP_KERNEL);
  	if (!result)
  		return result;
  
  	copied = snprintf(result, len, HAS_SESSION_ENV_PREFIX "%c",
  				clp->cl_minorversion ? 'Y' : 'N');
  	if (copied >= len) {
  		/* just return nothing if output was truncated */
  		kfree(result);
  		return NULL;
  	}
  
  	return result;
  }
  
  static char *
9cc768014   Arnd Bergmann   nfsd: make 'boot_...
1734
  nfsd4_cltrack_grace_start(time64_t grace_start)
d4318acd5   Jeff Layton   nfsd: pass extra ...
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
  {
  	int copied;
  	size_t len;
  	char *result;
  
  	/* prefix + max width of int64_t string + terminating NULL */
  	len = strlen(GRACE_START_ENV_PREFIX) + 22 + 1;
  
  	result = kmalloc(len, GFP_KERNEL);
  	if (!result)
  		return result;
9cc768014   Arnd Bergmann   nfsd: make 'boot_...
1746
  	copied = snprintf(result, len, GRACE_START_ENV_PREFIX "%lld",
d4318acd5   Jeff Layton   nfsd: pass extra ...
1747
1748
1749
1750
1751
1752
1753
1754
1755
  				grace_start);
  	if (copied >= len) {
  		/* just return nothing if output was truncated */
  		kfree(result);
  		return NULL;
  	}
  
  	return result;
  }
2873d2147   Jeff Layton   nfsd: add a userm...
1756
  static int
d4318acd5   Jeff Layton   nfsd: pass extra ...
1757
  nfsd4_umh_cltrack_upcall(char *cmd, char *arg, char *env0, char *env1)
2873d2147   Jeff Layton   nfsd: add a userm...
1758
  {
d4318acd5   Jeff Layton   nfsd: pass extra ...
1759
  	char *envp[3];
2873d2147   Jeff Layton   nfsd: add a userm...
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
  	char *argv[4];
  	int ret;
  
  	if (unlikely(!cltrack_prog[0])) {
  		dprintk("%s: cltrack_prog is disabled
  ", __func__);
  		return -EACCES;
  	}
  
  	dprintk("%s: cmd: %s
  ", __func__, cmd);
  	dprintk("%s: arg: %s
  ", __func__, arg ? arg : "(null)");
d4318acd5   Jeff Layton   nfsd: pass extra ...
1773
1774
1775
1776
  	dprintk("%s: env0: %s
  ", __func__, env0 ? env0 : "(null)");
  	dprintk("%s: env1: %s
  ", __func__, env1 ? env1 : "(null)");
f3aa7e24c   Jeff Layton   nfsd: pass info a...
1777

d4318acd5   Jeff Layton   nfsd: pass extra ...
1778
1779
1780
  	envp[0] = env0;
  	envp[1] = env1;
  	envp[2] = NULL;
2873d2147   Jeff Layton   nfsd: add a userm...
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
  
  	argv[0] = (char *)cltrack_prog;
  	argv[1] = cmd;
  	argv[2] = arg;
  	argv[3] = NULL;
  
  	ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
  	/*
  	 * Disable the upcall mechanism if we're getting an ENOENT or EACCES
  	 * error. The admin can re-enable it on the fly by using sysfs
  	 * once the problem has been fixed.
  	 */
  	if (ret == -ENOENT || ret == -EACCES) {
  		dprintk("NFSD: %s was not found or isn't executable (%d). "
  			"Setting cltrack_prog to blank string!",
  			cltrack_prog, ret);
  		cltrack_prog[0] = '\0';
  	}
  	dprintk("%s: %s return value: %d
  ", __func__, cltrack_prog, ret);
  
  	return ret;
  }
  
  static char *
  bin_to_hex_dup(const unsigned char *src, int srclen)
  {
12b4157b7   Andy Shevchenko   nfsd: remove priv...
1808
  	char *buf;
2873d2147   Jeff Layton   nfsd: add a userm...
1809
1810
  
  	/* +1 for terminating NULL */
12b4157b7   Andy Shevchenko   nfsd: remove priv...
1811
  	buf = kzalloc((srclen * 2) + 1, GFP_KERNEL);
2873d2147   Jeff Layton   nfsd: add a userm...
1812
1813
  	if (!buf)
  		return buf;
12b4157b7   Andy Shevchenko   nfsd: remove priv...
1814
  	bin2hex(buf, src, srclen);
2873d2147   Jeff Layton   nfsd: add a userm...
1815
1816
1817
1818
  	return buf;
  }
  
  static int
d4318acd5   Jeff Layton   nfsd: pass extra ...
1819
  nfsd4_umh_cltrack_init(struct net *net)
2873d2147   Jeff Layton   nfsd: add a userm...
1820
  {
d4318acd5   Jeff Layton   nfsd: pass extra ...
1821
1822
1823
  	int ret;
  	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
  	char *grace_start = nfsd4_cltrack_grace_start(nn->boot_time);
71a503069   Stanislav Kinsbursky   nfsd: disable use...
1824
1825
  	/* XXX: The usermode helper s not working in container yet. */
  	if (net != &init_net) {
46cc8ba30   Paul Gortmaker   nfsd: don't WARN/...
1826
1827
  		pr_warn("NFSD: attempt to initialize umh client tracking in a container ignored.
  ");
956ccef3c   Sudip Mukherjee   nfsd: recover: fi...
1828
  		kfree(grace_start);
71a503069   Stanislav Kinsbursky   nfsd: disable use...
1829
1830
  		return -EINVAL;
  	}
d4318acd5   Jeff Layton   nfsd: pass extra ...
1831
1832
1833
  
  	ret = nfsd4_umh_cltrack_upcall("init", NULL, grace_start, NULL);
  	kfree(grace_start);
869216075   Scott Mayhew   nfsd: re-order cl...
1834
1835
1836
  	if (!ret)
  		printk("NFSD: Using UMH upcall client tracking operations.
  ");
d4318acd5   Jeff Layton   nfsd: pass extra ...
1837
  	return ret;
2873d2147   Jeff Layton   nfsd: add a userm...
1838
1839
1840
  }
  
  static void
d682e750c   Jeff Layton   nfsd: serialize n...
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
  nfsd4_cltrack_upcall_lock(struct nfs4_client *clp)
  {
  	wait_on_bit_lock(&clp->cl_flags, NFSD4_CLIENT_UPCALL_LOCK,
  			 TASK_UNINTERRUPTIBLE);
  }
  
  static void
  nfsd4_cltrack_upcall_unlock(struct nfs4_client *clp)
  {
  	smp_mb__before_atomic();
  	clear_bit(NFSD4_CLIENT_UPCALL_LOCK, &clp->cl_flags);
  	smp_mb__after_atomic();
  	wake_up_bit(&clp->cl_flags, NFSD4_CLIENT_UPCALL_LOCK);
  }
  
  static void
2873d2147   Jeff Layton   nfsd: add a userm...
1857
1858
  nfsd4_umh_cltrack_create(struct nfs4_client *clp)
  {
d4318acd5   Jeff Layton   nfsd: pass extra ...
1859
1860
  	char *hexid, *has_session, *grace_start;
  	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
2873d2147   Jeff Layton   nfsd: add a userm...
1861

65decb650   Jeff Layton   nfsd: skip subseq...
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
  	/*
  	 * With v4.0 clients, there's little difference in outcome between a
  	 * create and check operation, and we can end up calling into this
  	 * function multiple times per client (once for each openowner). So,
  	 * for v4.0 clients skip upcalling once the client has been recorded
  	 * on stable storage.
  	 *
  	 * For v4.1+ clients, the outcome of the two operations is different,
  	 * so we must ensure that we upcall for the create operation. v4.1+
  	 * clients call this on RECLAIM_COMPLETE though, so we should only end
  	 * up doing a single create upcall per client.
  	 */
  	if (clp->cl_minorversion == 0 &&
  	    test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
  		return;
2873d2147   Jeff Layton   nfsd: add a userm...
1877
1878
1879
1880
1881
1882
  	hexid = bin_to_hex_dup(clp->cl_name.data, clp->cl_name.len);
  	if (!hexid) {
  		dprintk("%s: can't allocate memory for upcall!
  ", __func__);
  		return;
  	}
d682e750c   Jeff Layton   nfsd: serialize n...
1883

d4318acd5   Jeff Layton   nfsd: pass extra ...
1884
1885
  	has_session = nfsd4_cltrack_client_has_session(clp);
  	grace_start = nfsd4_cltrack_grace_start(nn->boot_time);
d682e750c   Jeff Layton   nfsd: serialize n...
1886
1887
  
  	nfsd4_cltrack_upcall_lock(clp);
788a7914a   Jeff Layton   nfsd: set and tes...
1888
1889
  	if (!nfsd4_umh_cltrack_upcall("create", hexid, has_session, grace_start))
  		set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
d682e750c   Jeff Layton   nfsd: serialize n...
1890
  	nfsd4_cltrack_upcall_unlock(clp);
d4318acd5   Jeff Layton   nfsd: pass extra ...
1891
1892
  	kfree(has_session);
  	kfree(grace_start);
2873d2147   Jeff Layton   nfsd: add a userm...
1893
1894
1895
1896
1897
1898
1899
  	kfree(hexid);
  }
  
  static void
  nfsd4_umh_cltrack_remove(struct nfs4_client *clp)
  {
  	char *hexid;
788a7914a   Jeff Layton   nfsd: set and tes...
1900
1901
  	if (!test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
  		return;
2873d2147   Jeff Layton   nfsd: add a userm...
1902
1903
1904
1905
1906
1907
  	hexid = bin_to_hex_dup(clp->cl_name.data, clp->cl_name.len);
  	if (!hexid) {
  		dprintk("%s: can't allocate memory for upcall!
  ", __func__);
  		return;
  	}
d682e750c   Jeff Layton   nfsd: serialize n...
1908
1909
  
  	nfsd4_cltrack_upcall_lock(clp);
788a7914a   Jeff Layton   nfsd: set and tes...
1910
1911
1912
  	if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags) &&
  	    nfsd4_umh_cltrack_upcall("remove", hexid, NULL, NULL) == 0)
  		clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
d682e750c   Jeff Layton   nfsd: serialize n...
1913
  	nfsd4_cltrack_upcall_unlock(clp);
2873d2147   Jeff Layton   nfsd: add a userm...
1914
1915
1916
1917
1918
1919
1920
  	kfree(hexid);
  }
  
  static int
  nfsd4_umh_cltrack_check(struct nfs4_client *clp)
  {
  	int ret;
d4318acd5   Jeff Layton   nfsd: pass extra ...
1921
  	char *hexid, *has_session, *legacy;
2873d2147   Jeff Layton   nfsd: add a userm...
1922

788a7914a   Jeff Layton   nfsd: set and tes...
1923
1924
  	if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
  		return 0;
2873d2147   Jeff Layton   nfsd: add a userm...
1925
1926
1927
1928
1929
1930
  	hexid = bin_to_hex_dup(clp->cl_name.data, clp->cl_name.len);
  	if (!hexid) {
  		dprintk("%s: can't allocate memory for upcall!
  ", __func__);
  		return -ENOMEM;
  	}
d4318acd5   Jeff Layton   nfsd: pass extra ...
1931
1932
  
  	has_session = nfsd4_cltrack_client_has_session(clp);
2216d449a   Jeff Layton   nfsd: get rid of ...
1933
  	legacy = nfsd4_cltrack_legacy_recdir(&clp->cl_name);
d682e750c   Jeff Layton   nfsd: serialize n...
1934
1935
  
  	nfsd4_cltrack_upcall_lock(clp);
788a7914a   Jeff Layton   nfsd: set and tes...
1936
1937
1938
1939
1940
1941
1942
  	if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) {
  		ret = 0;
  	} else {
  		ret = nfsd4_umh_cltrack_upcall("check", hexid, has_session, legacy);
  		if (ret == 0)
  			set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
  	}
d682e750c   Jeff Layton   nfsd: serialize n...
1943
  	nfsd4_cltrack_upcall_unlock(clp);
d4318acd5   Jeff Layton   nfsd: pass extra ...
1944
  	kfree(has_session);
f3aa7e24c   Jeff Layton   nfsd: pass info a...
1945
  	kfree(legacy);
2873d2147   Jeff Layton   nfsd: add a userm...
1946
  	kfree(hexid);
d4318acd5   Jeff Layton   nfsd: pass extra ...
1947

2873d2147   Jeff Layton   nfsd: add a userm...
1948
1949
1950
1951
  	return ret;
  }
  
  static void
919b8049f   Jeff Layton   nfsd: remove redu...
1952
  nfsd4_umh_cltrack_grace_done(struct nfsd_net *nn)
2873d2147   Jeff Layton   nfsd: add a userm...
1953
  {
f3aa7e24c   Jeff Layton   nfsd: pass info a...
1954
  	char *legacy;
2873d2147   Jeff Layton   nfsd: add a userm...
1955
  	char timestr[22]; /* FIXME: better way to determine max size? */
9cc768014   Arnd Bergmann   nfsd: make 'boot_...
1956
  	sprintf(timestr, "%lld", nn->boot_time);
f3aa7e24c   Jeff Layton   nfsd: pass info a...
1957
  	legacy = nfsd4_cltrack_legacy_topdir();
d4318acd5   Jeff Layton   nfsd: pass extra ...
1958
  	nfsd4_umh_cltrack_upcall("gracedone", timestr, legacy, NULL);
f3aa7e24c   Jeff Layton   nfsd: pass info a...
1959
  	kfree(legacy);
2873d2147   Jeff Layton   nfsd: add a userm...
1960
  }
7c582e4fa   Julia Lawall   nfsd: recover: co...
1961
  static const struct nfsd4_client_tracking_ops nfsd4_umh_tracking_ops = {
2873d2147   Jeff Layton   nfsd: add a userm...
1962
1963
1964
1965
1966
1967
  	.init		= nfsd4_umh_cltrack_init,
  	.exit		= NULL,
  	.create		= nfsd4_umh_cltrack_create,
  	.remove		= nfsd4_umh_cltrack_remove,
  	.check		= nfsd4_umh_cltrack_check,
  	.grace_done	= nfsd4_umh_cltrack_grace_done,
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1968
1969
  	.version	= 1,
  	.msglen		= 0,
2873d2147   Jeff Layton   nfsd: add a userm...
1970
  };
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
1971
1972
1973
1974
  int
  nfsd4_client_tracking_init(struct net *net)
  {
  	int status;
f3f801486   Jeff Layton   nfsd: add the inf...
1975
  	struct path path;
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
1976
  	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
1977

2d77bf0a5   Jeff Layton   nfsd: change heur...
1978
  	/* just run the init if it the method is already decided */
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
1979
  	if (nn->client_tracking_ops)
2d77bf0a5   Jeff Layton   nfsd: change heur...
1980
  		goto do_init;
869216075   Scott Mayhew   nfsd: re-order cl...
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
  	/* First, try to use nfsdcld */
  	nn->client_tracking_ops = &nfsd4_cld_tracking_ops;
  	status = nn->client_tracking_ops->init(net);
  	if (!status)
  		return status;
  	if (status != -ETIMEDOUT) {
  		nn->client_tracking_ops = &nfsd4_cld_tracking_ops_v0;
  		status = nn->client_tracking_ops->init(net);
  		if (!status)
  			return status;
  	}
2d77bf0a5   Jeff Layton   nfsd: change heur...
1992
  	/*
869216075   Scott Mayhew   nfsd: re-order cl...
1993
  	 * Next, try the UMH upcall.
2d77bf0a5   Jeff Layton   nfsd: change heur...
1994
  	 */
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
1995
1996
  	nn->client_tracking_ops = &nfsd4_umh_tracking_ops;
  	status = nn->client_tracking_ops->init(net);
2d77bf0a5   Jeff Layton   nfsd: change heur...
1997
1998
1999
2000
  	if (!status)
  		return status;
  
  	/*
869216075   Scott Mayhew   nfsd: re-order cl...
2001
2002
  	 * Finally, See if the recoverydir exists and is a directory.
  	 * If it is, then use the legacy ops.
2d77bf0a5   Jeff Layton   nfsd: change heur...
2003
  	 */
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
2004
  	nn->client_tracking_ops = &nfsd4_legacy_tracking_ops;
2d77bf0a5   Jeff Layton   nfsd: change heur...
2005
2006
  	status = kern_path(nfs4_recoverydir(), LOOKUP_FOLLOW, &path);
  	if (!status) {
e36cb0b89   David Howells   VFS: (Scripted) C...
2007
  		status = d_is_dir(path.dentry);
2d77bf0a5   Jeff Layton   nfsd: change heur...
2008
  		path_put(&path);
869216075   Scott Mayhew   nfsd: re-order cl...
2009
2010
2011
2012
  		if (!status) {
  			status = -EINVAL;
  			goto out;
  		}
f3f801486   Jeff Layton   nfsd: add the inf...
2013
  	}
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
2014

2d77bf0a5   Jeff Layton   nfsd: change heur...
2015
  do_init:
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
2016
  	status = nn->client_tracking_ops->init(net);
869216075   Scott Mayhew   nfsd: re-order cl...
2017
  out:
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
2018
2019
2020
2021
  	if (status) {
  		printk(KERN_WARNING "NFSD: Unable to initialize client "
  				    "recovery tracking! (%d)
  ", status);
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
2022
  		nn->client_tracking_ops = NULL;
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
2023
2024
2025
2026
2027
2028
2029
  	}
  	return status;
  }
  
  void
  nfsd4_client_tracking_exit(struct net *net)
  {
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
2030
2031
2032
2033
2034
2035
  	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
  
  	if (nn->client_tracking_ops) {
  		if (nn->client_tracking_ops->exit)
  			nn->client_tracking_ops->exit(net);
  		nn->client_tracking_ops = NULL;
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
2036
2037
2038
2039
2040
2041
  	}
  }
  
  void
  nfsd4_client_record_create(struct nfs4_client *clp)
  {
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
2042
2043
2044
2045
  	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
  
  	if (nn->client_tracking_ops)
  		nn->client_tracking_ops->create(clp);
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
2046
2047
2048
2049
2050
  }
  
  void
  nfsd4_client_record_remove(struct nfs4_client *clp)
  {
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
2051
2052
2053
2054
  	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
  
  	if (nn->client_tracking_ops)
  		nn->client_tracking_ops->remove(clp);
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
2055
2056
2057
2058
2059
  }
  
  int
  nfsd4_client_record_check(struct nfs4_client *clp)
  {
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
2060
2061
2062
2063
  	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
  
  	if (nn->client_tracking_ops)
  		return nn->client_tracking_ops->check(clp);
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
2064
2065
2066
2067
2068
  
  	return -EOPNOTSUPP;
  }
  
  void
919b8049f   Jeff Layton   nfsd: remove redu...
2069
  nfsd4_record_grace_done(struct nfsd_net *nn)
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
2070
  {
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
2071
  	if (nn->client_tracking_ops)
919b8049f   Jeff Layton   nfsd: remove redu...
2072
  		nn->client_tracking_ops->grace_done(nn);
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
2073
  }
813fd320c   Jeff Layton   nfsd: add notifie...
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
  
  static int
  rpc_pipefs_event(struct notifier_block *nb, unsigned long event, void *ptr)
  {
  	struct super_block *sb = ptr;
  	struct net *net = sb->s_fs_info;
  	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
  	struct cld_net *cn = nn->cld_net;
  	struct dentry *dentry;
  	int ret = 0;
  
  	if (!try_module_get(THIS_MODULE))
  		return 0;
  
  	if (!cn) {
  		module_put(THIS_MODULE);
  		return 0;
  	}
  
  	switch (event) {
  	case RPC_PIPEFS_MOUNT:
  		dentry = nfsd4_cld_register_sb(sb, cn->cn_pipe);
  		if (IS_ERR(dentry)) {
  			ret = PTR_ERR(dentry);
  			break;
  		}
  		cn->cn_pipe->dentry = dentry;
  		break;
  	case RPC_PIPEFS_UMOUNT:
  		if (cn->cn_pipe->dentry)
  			nfsd4_cld_unregister_sb(cn->cn_pipe);
  		break;
  	default:
  		ret = -ENOTSUPP;
  		break;
  	}
  	module_put(THIS_MODULE);
  	return ret;
  }
2355c5964   J. Bruce Fields   nfsd4: fix missin...
2113
  static struct notifier_block nfsd4_cld_block = {
813fd320c   Jeff Layton   nfsd: add notifie...
2114
2115
  	.notifier_call = rpc_pipefs_event,
  };
797a9d797   Jeff Layton   nfsd: only regist...
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
  
  int
  register_cld_notifier(void)
  {
  	return rpc_pipefs_notifier_register(&nfsd4_cld_block);
  }
  
  void
  unregister_cld_notifier(void)
  {
  	rpc_pipefs_notifier_unregister(&nfsd4_cld_block);
  }