Blame view

fs/nfsd/nfs4recover.c 50.6 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

1edb82d20   Herbert Xu   nfsd: Use shash
124
125
126
127
  	{
  		SHASH_DESC_ON_STACK(desc, tfm);
  
  		desc->tfm = tfm;
1edb82d20   Herbert Xu   nfsd: Use shash
128
129
130
131
132
  
  		status = crypto_shash_digest(desc, clname->data, clname->len,
  					     cksum.data);
  		shash_desc_zero(desc);
  	}
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
133

2216d449a   Jeff Layton   nfsd: get rid of ...
134
  	if (status)
350586879   Herbert Xu   [CRYPTO] users: U...
135
  		goto out;
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
136
137
  
  	md5_to_hex(dname, cksum.data);
2216d449a   Jeff Layton   nfsd: get rid of ...
138
  	status = 0;
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
139
  out:
2bd9e7b62   Krishna Kumar   nfsd: Fix leaked ...
140
  	kfree(cksum.data);
1edb82d20   Herbert Xu   nfsd: Use shash
141
  	crypto_free_shash(tfm);
350586879   Herbert Xu   [CRYPTO] users: U...
142
  out_no_tfm:
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
143
144
  	return status;
  }
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
145

2216d449a   Jeff Layton   nfsd: get rid of ...
146
147
148
149
150
151
  /*
   * 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...
152
  legacy_recdir_name_error(struct nfs4_client *clp, int error)
2216d449a   Jeff Layton   nfsd: get rid of ...
153
154
155
156
157
158
159
160
161
162
163
164
165
166
  {
  	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...
167
  		nfsd4_client_tracking_exit(clp->net);
2216d449a   Jeff Layton   nfsd: get rid of ...
168
169
  	}
  }
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
170
  static void
6b1891052   Scott Mayhew   nfsd: make nfs4_c...
171
172
173
174
  __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...
175
  	struct xdr_netobj princhash = { .len = 0, .data = NULL };
6b1891052   Scott Mayhew   nfsd: make nfs4_c...
176
177
178
179
180
181
182
183
184
185
  	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...
186
  	crp = nfs4_client_to_reclaim(name, princhash, nn);
6b1891052   Scott Mayhew   nfsd: make nfs4_c...
187
188
189
190
191
192
193
194
  	if (!crp) {
  		kfree(name.data);
  		return;
  	}
  	crp->cr_clp = clp;
  }
  
  static void
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
195
  nfsd4_create_clid_dir(struct nfs4_client *clp)
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
196
  {
d84f4f992   David Howells   CRED: Inaugurate ...
197
  	const struct cred *original_cred;
2216d449a   Jeff Layton   nfsd: get rid of ...
198
  	char dname[HEXDIR_LEN];
e970a573c   Christoph Hellwig   nfsd: open a file...
199
  	struct dentry *dir, *dentry;
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
200
  	int status;
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
201
  	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
202

a52d726bb   Jeff Layton   nfsd: convert nfs...
203
  	if (test_and_set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
7a6ef8c72   J. Bruce Fields   nfsd4: nfsd4_crea...
204
  		return;
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
205
  	if (!nn->rec_file)
7a6ef8c72   J. Bruce Fields   nfsd4: nfsd4_crea...
206
  		return;
2216d449a   Jeff Layton   nfsd: get rid of ...
207
208
209
  
  	status = nfs4_make_rec_clidname(dname, &clp->cl_name);
  	if (status)
7255e716b   Jeff Layton   nfsd: fix oops wh...
210
  		return legacy_recdir_name_error(clp, status);
2216d449a   Jeff Layton   nfsd: get rid of ...
211

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

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

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

e970a573c   Christoph Hellwig   nfsd: open a file...
224
  	dentry = lookup_one_len(dname, dir, HEXDIR_LEN-1);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
225
226
227
228
  	if (IS_ERR(dentry)) {
  		status = PTR_ERR(dentry);
  		goto out_unlock;
  	}
2b0143b5c   David Howells   VFS: normal files...
229
  	if (d_really_is_positive(dentry))
aec39680b   J. Bruce Fields   nfsd4: fix spurio...
230
231
232
233
234
235
236
237
  		/*
  		 * 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...
238
  		goto out_put;
2b0143b5c   David Howells   VFS: normal files...
239
  	status = vfs_mkdir(d_inode(dir), dentry, S_IRWXU);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
240
241
242
  out_put:
  	dput(dentry);
  out_unlock:
5955102c9   Al Viro   wrappers for ->i_...
243
  	inode_unlock(d_inode(dir));
0ce0c2b5d   Jeff Layton   nfsd: don't searc...
244
  	if (status == 0) {
6b1891052   Scott Mayhew   nfsd: make nfs4_c...
245
246
247
  		if (nn->in_grace)
  			__nfsd4_create_reclaim_record_grace(clp, dname,
  					HEXDIR_LEN, nn);
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
248
  		vfs_fsync(nn->rec_file, 0);
0ce0c2b5d   Jeff Layton   nfsd: don't searc...
249
  	} else {
6577aac01   Boaz Harrosh   nfsd4: fix failur...
250
251
252
253
  		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...
254
  	}
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
255
  	mnt_drop_write_file(nn->rec_file);
c2236f141   Kinglong Mee   NFSD: Reset creds...
256
  out_creds:
d84f4f992   David Howells   CRED: Inaugurate ...
257
  	nfs4_reset_creds(original_cred);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
258
  }
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
259
  typedef int (recdir_func)(struct dentry *, struct dentry *, struct nfsd_net *);
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
260

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

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

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

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

3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
397
  	status = mnt_want_write_file(nn->rec_file);
0622753b8   Dave Hansen   [PATCH] r/o bind ...
398
399
  	if (status)
  		goto out;
a52d726bb   Jeff Layton   nfsd: convert nfs...
400
  	clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
d84f4f992   David Howells   CRED: Inaugurate ...
401
402
403
  
  	status = nfs4_save_creds(&original_cred);
  	if (status < 0)
698d8d875   Jeff Layton   nfsd: fix error h...
404
  		goto out_drop_write;
d84f4f992   David Howells   CRED: Inaugurate ...
405

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

6b1891052   Scott Mayhew   nfsd: make nfs4_c...
429
430
431
432
433
  	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...
434
  		return 0;
6b1891052   Scott Mayhew   nfsd: make nfs4_c...
435
436
437
438
439
440
441
442
443
444
445
  	}
  	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...
446

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

190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
486
  	if (child->d_name.len != HEXDIR_LEN - 1) {
6b1891052   Scott Mayhew   nfsd: make nfs4_c...
487
488
489
  		printk("%s: illegal name %pd in recovery directory
  ",
  				__func__, child);
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
490
  		/* Keep trying; maybe the others are OK: */
b37ad28bc   Al Viro   [PATCH] nfsd: nfs...
491
  		return 0;
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
492
  	}
6b1891052   Scott Mayhew   nfsd: make nfs4_c...
493
494
495
496
497
498
499
500
  	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...
501
  	if (!nfs4_client_to_reclaim(name, princhash, nn))
6b1891052   Scott Mayhew   nfsd: make nfs4_c...
502
503
  		kfree(name.data);
  out:
b37ad28bc   Al Viro   [PATCH] nfsd: nfs...
504
  	return 0;
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
505
  }
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
506
  static int
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
507
  nfsd4_recdir_load(struct net *net) {
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
508
  	int status;
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
509
  	struct nfsd_net *nn =  net_generic(net, nfsd_net_id);
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
510

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

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

d84f4f992   David Howells   CRED: Inaugurate ...
537
538
539
540
541
542
  	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...
543
  		return status;
d84f4f992   David Howells   CRED: Inaugurate ...
544
  	}
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
545

3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
546
547
  	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...
548
549
  		printk("NFSD: unable to find recovery directory %s
  ",
48483bf23   J. Bruce Fields   nfsd4: simplify r...
550
  				user_recovery_dirname);
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
551
552
  		status = PTR_ERR(nn->rec_file);
  		nn->rec_file = NULL;
e970a573c   Christoph Hellwig   nfsd: open a file...
553
  	}
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
554

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

2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
681
682
683
  	/* 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 ...
684
685
  	status = nfs4_make_rec_clidname(dname, &clp->cl_name);
  	if (status) {
7255e716b   Jeff Layton   nfsd: fix oops wh...
686
  		legacy_recdir_name_error(clp, status);
2216d449a   Jeff Layton   nfsd: get rid of ...
687
688
  		return status;
  	}
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
689
  	/* look for it in the reclaim hashtable otherwise */
6b1891052   Scott Mayhew   nfsd: make nfs4_c...
690
691
692
693
694
695
696
697
698
699
  	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...
700
  	if (crp) {
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
701
  		set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
0ce0c2b5d   Jeff Layton   nfsd: don't searc...
702
  		crp->cr_clp = clp;
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
703
704
  		return 0;
  	}
6b1891052   Scott Mayhew   nfsd: make nfs4_c...
705
  out_enoent:
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
706
707
  	return -ENOENT;
  }
7c582e4fa   Julia Lawall   nfsd: recover: co...
708
  static const struct nfsd4_client_tracking_ops nfsd4_legacy_tracking_ops = {
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
709
  	.init		= nfsd4_legacy_tracking_init,
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
710
711
712
713
714
  	.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...
715
716
  	.version	= 1,
  	.msglen		= 0,
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
717
  };
f3f801486   Jeff Layton   nfsd: add the inf...
718
719
720
721
722
723
724
725
726
727
  /* 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...
728
  	bool			 cn_has_legacy;
6ee95d1c8   Scott Mayhew   nfsd: add support...
729
  	struct crypto_shash	*cn_tfm;
f3f801486   Jeff Layton   nfsd: add the inf...
730
731
732
733
734
  };
  
  struct cld_upcall {
  	struct list_head	 cu_list;
  	struct cld_net		*cu_net;
b493fd31c   Scott Mayhew   nfsd: fix a warni...
735
  	struct completion	 cu_done;
11a60d159   Scott Mayhew   nfsd: add a "GetV...
736
737
738
  	union {
  		struct cld_msg_hdr	 cu_hdr;
  		struct cld_msg		 cu_msg;
6ee95d1c8   Scott Mayhew   nfsd: add support...
739
  		struct cld_msg_v2	 cu_msg_v2;
11a60d159   Scott Mayhew   nfsd: add a "GetV...
740
  	} cu_u;
f3f801486   Jeff Layton   nfsd: add the inf...
741
742
743
  };
  
  static int
11a60d159   Scott Mayhew   nfsd: add a "GetV...
744
  __cld_pipe_upcall(struct rpc_pipe *pipe, void *cmsg)
f3f801486   Jeff Layton   nfsd: add the inf...
745
746
747
  {
  	int ret;
  	struct rpc_pipe_msg msg;
11a60d159   Scott Mayhew   nfsd: add a "GetV...
748
749
750
  	struct cld_upcall *cup = container_of(cmsg, struct cld_upcall, cu_u);
  	struct nfsd_net *nn = net_generic(pipe->dentry->d_sb->s_fs_info,
  					  nfsd_net_id);
f3f801486   Jeff Layton   nfsd: add the inf...
751
752
753
  
  	memset(&msg, 0, sizeof(msg));
  	msg.data = cmsg;
11a60d159   Scott Mayhew   nfsd: add a "GetV...
754
  	msg.len = nn->client_tracking_ops->msglen;
f3f801486   Jeff Layton   nfsd: add the inf...
755

f3f801486   Jeff Layton   nfsd: add the inf...
756
757
  	ret = rpc_queue_upcall(pipe, &msg);
  	if (ret < 0) {
f3f801486   Jeff Layton   nfsd: add the inf...
758
759
  		goto out;
  	}
b493fd31c   Scott Mayhew   nfsd: fix a warni...
760
  	wait_for_completion(&cup->cu_done);
f3f801486   Jeff Layton   nfsd: add the inf...
761
762
763
764
765
766
767
768
  
  	if (msg.errno < 0)
  		ret = msg.errno;
  out:
  	return ret;
  }
  
  static int
11a60d159   Scott Mayhew   nfsd: add a "GetV...
769
  cld_pipe_upcall(struct rpc_pipe *pipe, void *cmsg)
f3f801486   Jeff Layton   nfsd: add the inf...
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
  {
  	int ret;
  
  	/*
  	 * -EAGAIN occurs when pipe is closed and reopened while there are
  	 *  upcalls queued.
  	 */
  	do {
  		ret = __cld_pipe_upcall(pipe, cmsg);
  	} while (ret == -EAGAIN);
  
  	return ret;
  }
  
  static ssize_t
6ee95d1c8   Scott Mayhew   nfsd: add support...
785
  __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg,
74725959c   Scott Mayhew   nfsd: un-deprecat...
786
787
  		struct nfsd_net *nn)
  {
6ee95d1c8   Scott Mayhew   nfsd: add support...
788
789
  	uint8_t cmd, princhashlen;
  	struct xdr_netobj name, princhash = { .len = 0, .data = NULL };
74725959c   Scott Mayhew   nfsd: un-deprecat...
790
  	uint16_t namelen;
8a9f4f412   Scott Mayhew   nfsd: handle lega...
791
  	struct cld_net *cn = nn->cld_net;
74725959c   Scott Mayhew   nfsd: un-deprecat...
792
793
794
795
796
797
  
  	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...
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
  		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...
829
830
831
832
833
  		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...
834
  		if (!nfs4_client_to_reclaim(name, princhash, nn)) {
74725959c   Scott Mayhew   nfsd: un-deprecat...
835
  			kfree(name.data);
6ee95d1c8   Scott Mayhew   nfsd: add support...
836
  			kfree(princhash.data);
74725959c   Scott Mayhew   nfsd: un-deprecat...
837
838
  			return -EFAULT;
  		}
11a60d159   Scott Mayhew   nfsd: add a "GetV...
839
  		return nn->client_tracking_ops->msglen;
74725959c   Scott Mayhew   nfsd: un-deprecat...
840
841
842
843
844
  	}
  	return -EFAULT;
  }
  
  static ssize_t
f3f801486   Jeff Layton   nfsd: add the inf...
845
846
847
  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...
848
  	struct cld_msg_hdr __user *hdr = (struct cld_msg_hdr __user *)src;
6ee95d1c8   Scott Mayhew   nfsd: add support...
849
  	struct cld_msg_v2 __user *cmsg = (struct cld_msg_v2 __user *)src;
f3f801486   Jeff Layton   nfsd: add the inf...
850
  	uint32_t xid;
ef8a1a10e   Al Viro   nfsd: get rid of ...
851
  	struct nfsd_net *nn = net_generic(file_inode(filp)->i_sb->s_fs_info,
f3f801486   Jeff Layton   nfsd: add the inf...
852
853
  						nfsd_net_id);
  	struct cld_net *cn = nn->cld_net;
74725959c   Scott Mayhew   nfsd: un-deprecat...
854
  	int16_t status;
f3f801486   Jeff Layton   nfsd: add the inf...
855

11a60d159   Scott Mayhew   nfsd: add a "GetV...
856
  	if (mlen != nn->client_tracking_ops->msglen) {
8a7dc4b04   Randy Dunlap   nfsd: fix nfs4rec...
857
858
  		dprintk("%s: got %zu bytes, expected %zu
  ", __func__, mlen,
11a60d159   Scott Mayhew   nfsd: add a "GetV...
859
  			nn->client_tracking_ops->msglen);
f3f801486   Jeff Layton   nfsd: add the inf...
860
861
862
863
  		return -EINVAL;
  	}
  
  	/* copy just the xid so we can try to find that */
11a60d159   Scott Mayhew   nfsd: add a "GetV...
864
  	if (copy_from_user(&xid, &hdr->cm_xid, sizeof(xid)) != 0) {
f3f801486   Jeff Layton   nfsd: add the inf...
865
866
867
  		dprintk("%s: error when copying xid from userspace", __func__);
  		return -EFAULT;
  	}
74725959c   Scott Mayhew   nfsd: un-deprecat...
868
869
870
871
872
  	/*
  	 * 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...
873
  	if (get_user(status, &hdr->cm_status)) {
74725959c   Scott Mayhew   nfsd: un-deprecat...
874
875
876
  		dprintk("%s: error when copying status from userspace", __func__);
  		return -EFAULT;
  	}
f3f801486   Jeff Layton   nfsd: add the inf...
877
878
879
880
  	/* 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...
881
  		if (get_unaligned(&tmp->cu_u.cu_hdr.cm_xid) == xid) {
f3f801486   Jeff Layton   nfsd: add the inf...
882
  			cup = tmp;
74725959c   Scott Mayhew   nfsd: un-deprecat...
883
884
  			if (status != -EINPROGRESS)
  				list_del_init(&cup->cu_list);
f3f801486   Jeff Layton   nfsd: add the inf...
885
886
887
888
889
890
891
  			break;
  		}
  	}
  	spin_unlock(&cn->cn_lock);
  
  	/* couldn't find upcall? */
  	if (!cup) {
21f72c9f0   Jeff Layton   nfsd: fix NULL po...
892
893
  		dprintk("%s: couldn't find upcall -- xid=%u
  ", __func__, xid);
f3f801486   Jeff Layton   nfsd: add the inf...
894
895
  		return -EINVAL;
  	}
74725959c   Scott Mayhew   nfsd: un-deprecat...
896
897
  	if (status == -EINPROGRESS)
  		return __cld_pipe_inprogress_downcall(cmsg, nn);
6ee95d1c8   Scott Mayhew   nfsd: add support...
898
  	if (copy_from_user(&cup->cu_u.cu_msg_v2, src, mlen) != 0)
f3f801486   Jeff Layton   nfsd: add the inf...
899
  		return -EFAULT;
b493fd31c   Scott Mayhew   nfsd: fix a warni...
900
  	complete(&cup->cu_done);
f3f801486   Jeff Layton   nfsd: add the inf...
901
902
903
904
905
906
907
908
  	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...
909
  						 cu_u.cu_msg);
f3f801486   Jeff Layton   nfsd: add the inf...
910
911
912
913
  
  	/* errno >= 0 means we got a downcall */
  	if (msg->errno >= 0)
  		return;
b493fd31c   Scott Mayhew   nfsd: fix a warni...
914
  	complete(&cup->cu_done);
f3f801486   Jeff Layton   nfsd: add the inf...
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
961
962
963
964
965
966
967
968
969
970
  }
  
  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...
971
  __nfsd4_init_cld_pipe(struct net *net)
f3f801486   Jeff Layton   nfsd: add the inf...
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
  {
  	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...
1002
  	cn->cn_has_legacy = false;
f3f801486   Jeff Layton   nfsd: add the inf...
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
  	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...
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
  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...
1026
1027
1028
1029
1030
1031
1032
1033
  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...
1034
1035
  	if (cn->cn_tfm)
  		crypto_free_shash(cn->cn_tfm);
f3f801486   Jeff Layton   nfsd: add the inf...
1036
1037
1038
1039
1040
  	kfree(nn->cld_net);
  	nn->cld_net = NULL;
  }
  
  static struct cld_upcall *
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1041
  alloc_cld_upcall(struct nfsd_net *nn)
f3f801486   Jeff Layton   nfsd: add the inf...
1042
1043
  {
  	struct cld_upcall *new, *tmp;
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1044
  	struct cld_net *cn = nn->cld_net;
f3f801486   Jeff Layton   nfsd: add the inf...
1045
1046
1047
1048
1049
1050
1051
1052
1053
  
  	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...
1054
  		if (tmp->cu_u.cu_msg.cm_xid == cn->cn_xid) {
f3f801486   Jeff Layton   nfsd: add the inf...
1055
1056
1057
1058
1059
  			cn->cn_xid++;
  			spin_unlock(&cn->cn_lock);
  			goto restart_search;
  		}
  	}
b493fd31c   Scott Mayhew   nfsd: fix a warni...
1060
  	init_completion(&new->cu_done);
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1061
1062
  	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...
1063
1064
1065
  	new->cu_net = cn;
  	list_add(&new->cu_list, &cn->cn_list);
  	spin_unlock(&cn->cn_lock);
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1066
1067
  	dprintk("%s: allocated xid %u
  ", __func__, new->cu_u.cu_msg.cm_xid);
f3f801486   Jeff Layton   nfsd: add the inf...
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
  
  	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...
1089
  	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
f3f801486   Jeff Layton   nfsd: add the inf...
1090
1091
1092
1093
1094
  	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...
1095
  	cup = alloc_cld_upcall(nn);
f3f801486   Jeff Layton   nfsd: add the inf...
1096
1097
1098
1099
  	if (!cup) {
  		ret = -ENOMEM;
  		goto out_err;
  	}
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1100
1101
1102
  	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...
1103
  			clp->cl_name.len);
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1104
  	ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg);
f3f801486   Jeff Layton   nfsd: add the inf...
1105
  	if (!ret) {
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1106
  		ret = cup->cu_u.cu_msg.cm_status;
f3f801486   Jeff Layton   nfsd: add the inf...
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
  		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...
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
  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;
  	SHASH_DESC_ON_STACK(desc, tfm);
  
  	/* 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) {
  		desc->tfm = tfm;
  		cksum.len = crypto_shash_digestsize(tfm);
  		cksum.data = kmalloc(cksum.len, GFP_KERNEL);
  		if (cksum.data == NULL) {
  			ret = -ENOMEM;
  			goto out;
  		}
  		ret = crypto_shash_digest(desc, principal, strlen(principal),
  					  cksum.data);
  		shash_desc_zero(desc);
  		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;
  
  	ret = cld_pipe_upcall(cn->cn_pipe, cmsg);
  	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...
1190
1191
1192
1193
  nfsd4_cld_remove(struct nfs4_client *clp)
  {
  	int ret;
  	struct cld_upcall *cup;
c212cecfa   Stanislav Kinsbursky   nfsd: make nfs4_c...
1194
  	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
f3f801486   Jeff Layton   nfsd: add the inf...
1195
1196
1197
1198
1199
  	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...
1200
  	cup = alloc_cld_upcall(nn);
f3f801486   Jeff Layton   nfsd: add the inf...
1201
1202
1203
1204
  	if (!cup) {
  		ret = -ENOMEM;
  		goto out_err;
  	}
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1205
1206
1207
  	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...
1208
  			clp->cl_name.len);
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1209
  	ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg);
f3f801486   Jeff Layton   nfsd: add the inf...
1210
  	if (!ret) {
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1211
  		ret = cup->cu_u.cu_msg.cm_status;
f3f801486   Jeff Layton   nfsd: add the inf...
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
  		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...
1222
1223
1224
1225
1226
1227
  /*
   * 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...
1228
  static int
74725959c   Scott Mayhew   nfsd: un-deprecat...
1229
  nfsd4_cld_check_v0(struct nfs4_client *clp)
f3f801486   Jeff Layton   nfsd: add the inf...
1230
1231
1232
  {
  	int ret;
  	struct cld_upcall *cup;
c212cecfa   Stanislav Kinsbursky   nfsd: make nfs4_c...
1233
  	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
f3f801486   Jeff Layton   nfsd: add the inf...
1234
1235
1236
1237
1238
  	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...
1239
  	cup = alloc_cld_upcall(nn);
f3f801486   Jeff Layton   nfsd: add the inf...
1240
1241
1242
1243
1244
1245
  	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...
1246
1247
1248
  	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...
1249
  			clp->cl_name.len);
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1250
  	ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg);
f3f801486   Jeff Layton   nfsd: add the inf...
1251
  	if (!ret) {
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1252
  		ret = cup->cu_u.cu_msg.cm_status;
f3f801486   Jeff Layton   nfsd: add the inf...
1253
1254
1255
1256
1257
1258
  		set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
  	}
  
  	free_cld_upcall(cup);
  	return ret;
  }
74725959c   Scott Mayhew   nfsd: un-deprecat...
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
  /*
   * 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...
1270
1271
1272
1273
  	struct cld_net *cn = nn->cld_net;
  	int status;
  	char dname[HEXDIR_LEN];
  	struct xdr_netobj name;
74725959c   Scott Mayhew   nfsd: un-deprecat...
1274
1275
1276
1277
1278
1279
1280
  
  	/* 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...
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
  	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...
1301

8a9f4f412   Scott Mayhew   nfsd: handle lega...
1302
  	}
74725959c   Scott Mayhew   nfsd: un-deprecat...
1303
  	return -ENOENT;
8a9f4f412   Scott Mayhew   nfsd: handle lega...
1304
1305
1306
  found:
  	crp->cr_clp = clp;
  	return 0;
74725959c   Scott Mayhew   nfsd: un-deprecat...
1307
1308
1309
  }
  
  static int
6ee95d1c8   Scott Mayhew   nfsd: add support...
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
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
  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;
  	SHASH_DESC_ON_STACK(desc, tfm);
  
  	/* 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;
  		desc->tfm = tfm;
  		cksum.len = crypto_shash_digestsize(tfm);
  		cksum.data = kmalloc(cksum.len, GFP_KERNEL);
  		if (cksum.data == NULL)
  			return -ENOENT;
  		status = crypto_shash_digest(desc, principal, strlen(principal),
  					     cksum.data);
  		shash_desc_zero(desc);
  		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...
1384
1385
1386
1387
1388
  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...
1389
  	cup = alloc_cld_upcall(nn);
74725959c   Scott Mayhew   nfsd: un-deprecat...
1390
1391
1392
1393
  	if (!cup) {
  		ret = -ENOMEM;
  		goto out_err;
  	}
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1394
1395
  	cup->cu_u.cu_msg.cm_cmd = Cld_GraceStart;
  	ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg);
74725959c   Scott Mayhew   nfsd: un-deprecat...
1396
  	if (!ret)
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1397
  		ret = cup->cu_u.cu_msg.cm_status;
74725959c   Scott Mayhew   nfsd: un-deprecat...
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
  
  	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...
1409
  static void
74725959c   Scott Mayhew   nfsd: un-deprecat...
1410
  nfsd4_cld_grace_done_v0(struct nfsd_net *nn)
f3f801486   Jeff Layton   nfsd: add the inf...
1411
1412
1413
  {
  	int ret;
  	struct cld_upcall *cup;
f3f801486   Jeff Layton   nfsd: add the inf...
1414
  	struct cld_net *cn = nn->cld_net;
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1415
  	cup = alloc_cld_upcall(nn);
f3f801486   Jeff Layton   nfsd: add the inf...
1416
1417
1418
1419
  	if (!cup) {
  		ret = -ENOMEM;
  		goto out_err;
  	}
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1420
1421
1422
  	cup->cu_u.cu_msg.cm_cmd = Cld_GraceDone;
  	cup->cu_u.cu_msg.cm_u.cm_gracetime = (int64_t)nn->boot_time;
  	ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg);
f3f801486   Jeff Layton   nfsd: add the inf...
1423
  	if (!ret)
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1424
  		ret = cup->cu_u.cu_msg.cm_status;
f3f801486   Jeff Layton   nfsd: add the inf...
1425
1426
1427
1428
1429
1430
1431
  
  	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...
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
  /*
   * 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...
1442
  	cup = alloc_cld_upcall(nn);
74725959c   Scott Mayhew   nfsd: un-deprecat...
1443
1444
1445
1446
  	if (!cup) {
  		ret = -ENOMEM;
  		goto out_err;
  	}
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1447
1448
  	cup->cu_u.cu_msg.cm_cmd = Cld_GraceDone;
  	ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg);
74725959c   Scott Mayhew   nfsd: un-deprecat...
1449
  	if (!ret)
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1450
  		ret = cup->cu_u.cu_msg.cm_status;
74725959c   Scott Mayhew   nfsd: un-deprecat...
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
  
  	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...
1475
1476
  	nn->track_reclaim_completes = true;
  	atomic_set(&nn->nr_reclaim_complete, 0);
74725959c   Scott Mayhew   nfsd: un-deprecat...
1477
1478
1479
1480
1481
1482
1483
1484
  
  	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...
1485
  	nn->track_reclaim_completes = false;
74725959c   Scott Mayhew   nfsd: un-deprecat...
1486
1487
  	kfree(nn->reclaim_str_hashtbl);
  }
869216075   Scott Mayhew   nfsd: re-order cl...
1488
1489
1490
1491
1492
1493
1494
1495
  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...
1496
  static int
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
  nfsd4_cld_get_version(struct nfsd_net *nn)
  {
  	int ret = 0;
  	struct cld_upcall *cup;
  	struct cld_net *cn = nn->cld_net;
  	uint8_t version;
  
  	cup = alloc_cld_upcall(nn);
  	if (!cup) {
  		ret = -ENOMEM;
  		goto out_err;
  	}
  	cup->cu_u.cu_msg.cm_cmd = Cld_GetVersion;
  	ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg);
  	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...
1528
1529
1530
  		case 2:
  			nn->client_tracking_ops = &nfsd4_cld_tracking_ops_v2;
  			break;
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
  		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...
1546
1547
1548
1549
  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...
1550
1551
  	bool running;
  	int retries = 10;
0efb7388f   Scott Mayhew   nfsd: Fix cld_net...
1552
  	struct crypto_shash *tfm;
74725959c   Scott Mayhew   nfsd: un-deprecat...
1553
1554
1555
1556
  
  	status = nfs4_cld_state_init(net);
  	if (status)
  		return status;
869216075   Scott Mayhew   nfsd: re-order cl...
1557
  	status = __nfsd4_init_cld_pipe(net);
74725959c   Scott Mayhew   nfsd: un-deprecat...
1558
1559
  	if (status)
  		goto err_shutdown;
869216075   Scott Mayhew   nfsd: re-order cl...
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
  	/*
  	 * 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;
  	}
0efb7388f   Scott Mayhew   nfsd: Fix cld_net...
1575
1576
1577
1578
1579
1580
  	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...
1581

11a60d159   Scott Mayhew   nfsd: add a "GetV...
1582
1583
1584
1585
  	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...
1586
1587
1588
  	status = nfsd4_cld_grace_start(nn);
  	if (status) {
  		if (status == -EOPNOTSUPP)
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1589
1590
  			pr_warn("NFSD: nfsdcld GraceStart upcall failed. Please upgrade nfsdcld.
  ");
74725959c   Scott Mayhew   nfsd: un-deprecat...
1591
1592
  		nfs4_release_reclaim(nn);
  		goto err_remove;
869216075   Scott Mayhew   nfsd: re-order cl...
1593
1594
1595
  	} else
  		printk("NFSD: Using nfsdcld client tracking operations.
  ");
74725959c   Scott Mayhew   nfsd: un-deprecat...
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
  	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...
1617
1618
1619
1620
  	.init		= nfsd4_init_cld_pipe,
  	.exit		= nfsd4_remove_cld_pipe,
  	.create		= nfsd4_cld_create,
  	.remove		= nfsd4_cld_remove,
74725959c   Scott Mayhew   nfsd: un-deprecat...
1621
1622
  	.check		= nfsd4_cld_check_v0,
  	.grace_done	= nfsd4_cld_grace_done_v0,
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1623
1624
  	.version	= 1,
  	.msglen		= sizeof(struct cld_msg),
74725959c   Scott Mayhew   nfsd: un-deprecat...
1625
1626
1627
1628
1629
1630
1631
1632
  };
  
  /* 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...
1633
1634
  	.check		= nfsd4_cld_check,
  	.grace_done	= nfsd4_cld_grace_done,
11a60d159   Scott Mayhew   nfsd: add a "GetV...
1635
1636
  	.version	= 1,
  	.msglen		= sizeof(struct cld_msg),
f3f801486   Jeff Layton   nfsd: add the inf...
1637
  };
6ee95d1c8   Scott Mayhew   nfsd: add support...
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
  /* 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...
1649
1650
1651
1652
1653
  /* 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...
1654
1655
1656
1657
1658
1659
1660
  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 ...
1661
1662
  #define HAS_SESSION_ENV_PREFIX "NFSDCLTRACK_CLIENT_HAS_SESSION="
  #define GRACE_START_ENV_PREFIX "NFSDCLTRACK_GRACE_START="
f3aa7e24c   Jeff Layton   nfsd: pass info a...
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
  
  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 ...
1693
  nfsd4_cltrack_legacy_recdir(const struct xdr_netobj *name)
f3aa7e24c   Jeff Layton   nfsd: pass info a...
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
  {
  	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 ...
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
  	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...
1719
1720
1721
1722
1723
1724
  		kfree(result);
  		return NULL;
  	}
  
  	return result;
  }
d4318acd5   Jeff Layton   nfsd: pass extra ...
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
  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 *
  nfsd4_cltrack_grace_start(time_t grace_start)
  {
  	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;
  
  	copied = snprintf(result, len, GRACE_START_ENV_PREFIX "%ld",
  				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...
1774
  static int
d4318acd5   Jeff Layton   nfsd: pass extra ...
1775
  nfsd4_umh_cltrack_upcall(char *cmd, char *arg, char *env0, char *env1)
2873d2147   Jeff Layton   nfsd: add a userm...
1776
  {
d4318acd5   Jeff Layton   nfsd: pass extra ...
1777
  	char *envp[3];
2873d2147   Jeff Layton   nfsd: add a userm...
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
  	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 ...
1791
1792
1793
1794
  	dprintk("%s: env0: %s
  ", __func__, env0 ? env0 : "(null)");
  	dprintk("%s: env1: %s
  ", __func__, env1 ? env1 : "(null)");
f3aa7e24c   Jeff Layton   nfsd: pass info a...
1795

d4318acd5   Jeff Layton   nfsd: pass extra ...
1796
1797
1798
  	envp[0] = env0;
  	envp[1] = env1;
  	envp[2] = NULL;
2873d2147   Jeff Layton   nfsd: add a userm...
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
  
  	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)
  {
  	int i;
  	char *buf, *hex;
  
  	/* +1 for terminating NULL */
  	buf = kmalloc((srclen * 2) + 1, GFP_KERNEL);
  	if (!buf)
  		return buf;
  
  	hex = buf;
  	for (i = 0; i < srclen; i++) {
  		sprintf(hex, "%2.2x", *src++);
  		hex += 2;
  	}
  	return buf;
  }
  
  static int
d4318acd5   Jeff Layton   nfsd: pass extra ...
1843
  nfsd4_umh_cltrack_init(struct net *net)
2873d2147   Jeff Layton   nfsd: add a userm...
1844
  {
d4318acd5   Jeff Layton   nfsd: pass extra ...
1845
1846
1847
  	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...
1848
1849
  	/* XXX: The usermode helper s not working in container yet. */
  	if (net != &init_net) {
46cc8ba30   Paul Gortmaker   nfsd: don't WARN/...
1850
1851
  		pr_warn("NFSD: attempt to initialize umh client tracking in a container ignored.
  ");
956ccef3c   Sudip Mukherjee   nfsd: recover: fi...
1852
  		kfree(grace_start);
71a503069   Stanislav Kinsbursky   nfsd: disable use...
1853
1854
  		return -EINVAL;
  	}
d4318acd5   Jeff Layton   nfsd: pass extra ...
1855
1856
1857
  
  	ret = nfsd4_umh_cltrack_upcall("init", NULL, grace_start, NULL);
  	kfree(grace_start);
869216075   Scott Mayhew   nfsd: re-order cl...
1858
1859
1860
  	if (!ret)
  		printk("NFSD: Using UMH upcall client tracking operations.
  ");
d4318acd5   Jeff Layton   nfsd: pass extra ...
1861
  	return ret;
2873d2147   Jeff Layton   nfsd: add a userm...
1862
1863
1864
  }
  
  static void
d682e750c   Jeff Layton   nfsd: serialize n...
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
  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...
1881
1882
  nfsd4_umh_cltrack_create(struct nfs4_client *clp)
  {
d4318acd5   Jeff Layton   nfsd: pass extra ...
1883
1884
  	char *hexid, *has_session, *grace_start;
  	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
2873d2147   Jeff Layton   nfsd: add a userm...
1885

65decb650   Jeff Layton   nfsd: skip subseq...
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
  	/*
  	 * 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...
1901
1902
1903
1904
1905
1906
  	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...
1907

d4318acd5   Jeff Layton   nfsd: pass extra ...
1908
1909
  	has_session = nfsd4_cltrack_client_has_session(clp);
  	grace_start = nfsd4_cltrack_grace_start(nn->boot_time);
d682e750c   Jeff Layton   nfsd: serialize n...
1910
1911
  
  	nfsd4_cltrack_upcall_lock(clp);
788a7914a   Jeff Layton   nfsd: set and tes...
1912
1913
  	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...
1914
  	nfsd4_cltrack_upcall_unlock(clp);
d4318acd5   Jeff Layton   nfsd: pass extra ...
1915
1916
  	kfree(has_session);
  	kfree(grace_start);
2873d2147   Jeff Layton   nfsd: add a userm...
1917
1918
1919
1920
1921
1922
1923
  	kfree(hexid);
  }
  
  static void
  nfsd4_umh_cltrack_remove(struct nfs4_client *clp)
  {
  	char *hexid;
788a7914a   Jeff Layton   nfsd: set and tes...
1924
1925
  	if (!test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
  		return;
2873d2147   Jeff Layton   nfsd: add a userm...
1926
1927
1928
1929
1930
1931
  	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...
1932
1933
  
  	nfsd4_cltrack_upcall_lock(clp);
788a7914a   Jeff Layton   nfsd: set and tes...
1934
1935
1936
  	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...
1937
  	nfsd4_cltrack_upcall_unlock(clp);
2873d2147   Jeff Layton   nfsd: add a userm...
1938
1939
1940
1941
1942
1943
1944
  	kfree(hexid);
  }
  
  static int
  nfsd4_umh_cltrack_check(struct nfs4_client *clp)
  {
  	int ret;
d4318acd5   Jeff Layton   nfsd: pass extra ...
1945
  	char *hexid, *has_session, *legacy;
2873d2147   Jeff Layton   nfsd: add a userm...
1946

788a7914a   Jeff Layton   nfsd: set and tes...
1947
1948
  	if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
  		return 0;
2873d2147   Jeff Layton   nfsd: add a userm...
1949
1950
1951
1952
1953
1954
  	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 ...
1955
1956
  
  	has_session = nfsd4_cltrack_client_has_session(clp);
2216d449a   Jeff Layton   nfsd: get rid of ...
1957
  	legacy = nfsd4_cltrack_legacy_recdir(&clp->cl_name);
d682e750c   Jeff Layton   nfsd: serialize n...
1958
1959
  
  	nfsd4_cltrack_upcall_lock(clp);
788a7914a   Jeff Layton   nfsd: set and tes...
1960
1961
1962
1963
1964
1965
1966
  	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...
1967
  	nfsd4_cltrack_upcall_unlock(clp);
d4318acd5   Jeff Layton   nfsd: pass extra ...
1968
  	kfree(has_session);
f3aa7e24c   Jeff Layton   nfsd: pass info a...
1969
  	kfree(legacy);
2873d2147   Jeff Layton   nfsd: add a userm...
1970
  	kfree(hexid);
d4318acd5   Jeff Layton   nfsd: pass extra ...
1971

2873d2147   Jeff Layton   nfsd: add a userm...
1972
1973
1974
1975
  	return ret;
  }
  
  static void
919b8049f   Jeff Layton   nfsd: remove redu...
1976
  nfsd4_umh_cltrack_grace_done(struct nfsd_net *nn)
2873d2147   Jeff Layton   nfsd: add a userm...
1977
  {
f3aa7e24c   Jeff Layton   nfsd: pass info a...
1978
  	char *legacy;
2873d2147   Jeff Layton   nfsd: add a userm...
1979
  	char timestr[22]; /* FIXME: better way to determine max size? */
919b8049f   Jeff Layton   nfsd: remove redu...
1980
  	sprintf(timestr, "%ld", nn->boot_time);
f3aa7e24c   Jeff Layton   nfsd: pass info a...
1981
  	legacy = nfsd4_cltrack_legacy_topdir();
d4318acd5   Jeff Layton   nfsd: pass extra ...
1982
  	nfsd4_umh_cltrack_upcall("gracedone", timestr, legacy, NULL);
f3aa7e24c   Jeff Layton   nfsd: pass info a...
1983
  	kfree(legacy);
2873d2147   Jeff Layton   nfsd: add a userm...
1984
  }
7c582e4fa   Julia Lawall   nfsd: recover: co...
1985
  static const struct nfsd4_client_tracking_ops nfsd4_umh_tracking_ops = {
2873d2147   Jeff Layton   nfsd: add a userm...
1986
1987
1988
1989
1990
1991
  	.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...
1992
1993
  	.version	= 1,
  	.msglen		= 0,
2873d2147   Jeff Layton   nfsd: add a userm...
1994
  };
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
1995
1996
1997
1998
  int
  nfsd4_client_tracking_init(struct net *net)
  {
  	int status;
f3f801486   Jeff Layton   nfsd: add the inf...
1999
  	struct path path;
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
2000
  	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
2001

2d77bf0a5   Jeff Layton   nfsd: change heur...
2002
  	/* just run the init if it the method is already decided */
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
2003
  	if (nn->client_tracking_ops)
2d77bf0a5   Jeff Layton   nfsd: change heur...
2004
  		goto do_init;
869216075   Scott Mayhew   nfsd: re-order cl...
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
  	/* 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...
2016
  	/*
869216075   Scott Mayhew   nfsd: re-order cl...
2017
  	 * Next, try the UMH upcall.
2d77bf0a5   Jeff Layton   nfsd: change heur...
2018
  	 */
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
2019
2020
  	nn->client_tracking_ops = &nfsd4_umh_tracking_ops;
  	status = nn->client_tracking_ops->init(net);
2d77bf0a5   Jeff Layton   nfsd: change heur...
2021
2022
2023
2024
  	if (!status)
  		return status;
  
  	/*
869216075   Scott Mayhew   nfsd: re-order cl...
2025
2026
  	 * Finally, See if the recoverydir exists and is a directory.
  	 * If it is, then use the legacy ops.
2d77bf0a5   Jeff Layton   nfsd: change heur...
2027
  	 */
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
2028
  	nn->client_tracking_ops = &nfsd4_legacy_tracking_ops;
2d77bf0a5   Jeff Layton   nfsd: change heur...
2029
2030
  	status = kern_path(nfs4_recoverydir(), LOOKUP_FOLLOW, &path);
  	if (!status) {
e36cb0b89   David Howells   VFS: (Scripted) C...
2031
  		status = d_is_dir(path.dentry);
2d77bf0a5   Jeff Layton   nfsd: change heur...
2032
  		path_put(&path);
869216075   Scott Mayhew   nfsd: re-order cl...
2033
2034
2035
2036
  		if (!status) {
  			status = -EINVAL;
  			goto out;
  		}
f3f801486   Jeff Layton   nfsd: add the inf...
2037
  	}
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
2038

2d77bf0a5   Jeff Layton   nfsd: change heur...
2039
  do_init:
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
2040
  	status = nn->client_tracking_ops->init(net);
869216075   Scott Mayhew   nfsd: re-order cl...
2041
  out:
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
2042
2043
2044
2045
  	if (status) {
  		printk(KERN_WARNING "NFSD: Unable to initialize client "
  				    "recovery tracking! (%d)
  ", status);
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
2046
  		nn->client_tracking_ops = NULL;
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
2047
2048
2049
2050
2051
2052
2053
  	}
  	return status;
  }
  
  void
  nfsd4_client_tracking_exit(struct net *net)
  {
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
2054
2055
2056
2057
2058
2059
  	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...
2060
2061
2062
2063
2064
2065
  	}
  }
  
  void
  nfsd4_client_record_create(struct nfs4_client *clp)
  {
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
2066
2067
2068
2069
  	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...
2070
2071
2072
2073
2074
  }
  
  void
  nfsd4_client_record_remove(struct nfs4_client *clp)
  {
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
2075
2076
2077
2078
  	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...
2079
2080
2081
2082
2083
  }
  
  int
  nfsd4_client_record_check(struct nfs4_client *clp)
  {
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
2084
2085
2086
2087
  	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...
2088
2089
2090
2091
2092
  
  	return -EOPNOTSUPP;
  }
  
  void
919b8049f   Jeff Layton   nfsd: remove redu...
2093
  nfsd4_record_grace_done(struct nfsd_net *nn)
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
2094
  {
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
2095
  	if (nn->client_tracking_ops)
919b8049f   Jeff Layton   nfsd: remove redu...
2096
  		nn->client_tracking_ops->grace_done(nn);
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
2097
  }
813fd320c   Jeff Layton   nfsd: add notifie...
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
  
  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...
2137
  static struct notifier_block nfsd4_cld_block = {
813fd320c   Jeff Layton   nfsd: add notifie...
2138
2139
  	.notifier_call = rpc_pipefs_event,
  };
797a9d797   Jeff Layton   nfsd: only regist...
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
  
  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);
  }