Blame view

fs/nfsd/nfs4recover.c 32.1 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.
  *
  */
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
34
  #include <linux/file.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
35
  #include <linux/slab.h>
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
36
  #include <linux/namei.h>
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
37
  #include <linux/crypto.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 *);
12760c668   Stanislav Kinsbursky   nfsd: pass nfsd_n...
59
  	void (*grace_done)(struct nfsd_net *, time_t);
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
60
  };
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
61
  /* Globals */
48483bf23   J. Bruce Fields   nfsd4: simplify r...
62
  static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery";
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
63

d84f4f992   David Howells   CRED: Inaugurate ...
64
65
  static int
  nfs4_save_creds(const struct cred **original_creds)
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
66
  {
d84f4f992   David Howells   CRED: Inaugurate ...
67
68
69
70
71
  	struct cred *new;
  
  	new = prepare_creds();
  	if (!new)
  		return -ENOMEM;
6fab87790   Eric W. Biederman   nfsd: Properly co...
72
73
  	new->fsuid = GLOBAL_ROOT_UID;
  	new->fsgid = GLOBAL_ROOT_GID;
d84f4f992   David Howells   CRED: Inaugurate ...
74
75
76
  	*original_creds = override_creds(new);
  	put_cred(new);
  	return 0;
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
77
78
79
  }
  
  static void
d84f4f992   David Howells   CRED: Inaugurate ...
80
  nfs4_reset_creds(const struct cred *original)
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
81
  {
d84f4f992   David Howells   CRED: Inaugurate ...
82
  	revert_creds(original);
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
83
  }
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
84
85
86
87
88
89
90
91
92
93
94
95
96
  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 ...
97
98
  static int
  nfs4_make_rec_clidname(char *dname, const struct xdr_netobj *clname)
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
99
100
  {
  	struct xdr_netobj cksum;
350586879   Herbert Xu   [CRYPTO] users: U...
101
  	struct hash_desc desc;
60c74f819   Jens Axboe   Update fs/ to use...
102
  	struct scatterlist sg;
2216d449a   Jeff Layton   nfsd: get rid of ...
103
  	int status;
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
104
105
106
107
  
  	dprintk("NFSD: nfs4_make_rec_clidname for %.*s
  ",
  			clname->len, clname->data);
350586879   Herbert Xu   [CRYPTO] users: U...
108
109
  	desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
  	desc.tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC);
2216d449a   Jeff Layton   nfsd: get rid of ...
110
111
  	if (IS_ERR(desc.tfm)) {
  		status = PTR_ERR(desc.tfm);
350586879   Herbert Xu   [CRYPTO] users: U...
112
  		goto out_no_tfm;
2216d449a   Jeff Layton   nfsd: get rid of ...
113
  	}
350586879   Herbert Xu   [CRYPTO] users: U...
114
  	cksum.len = crypto_hash_digestsize(desc.tfm);
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
115
  	cksum.data = kmalloc(cksum.len, GFP_KERNEL);
2216d449a   Jeff Layton   nfsd: get rid of ...
116
117
  	if (cksum.data == NULL) {
  		status = -ENOMEM;
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
118
   		goto out;
2216d449a   Jeff Layton   nfsd: get rid of ...
119
  	}
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
120

60c74f819   Jens Axboe   Update fs/ to use...
121
  	sg_init_one(&sg, clname->data, clname->len);
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
122

2216d449a   Jeff Layton   nfsd: get rid of ...
123
124
  	status = crypto_hash_digest(&desc, &sg, sg.length, cksum.data);
  	if (status)
350586879   Herbert Xu   [CRYPTO] users: U...
125
  		goto out;
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
126
127
  
  	md5_to_hex(dname, cksum.data);
2216d449a   Jeff Layton   nfsd: get rid of ...
128
  	status = 0;
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
129
  out:
2bd9e7b62   Krishna Kumar   nfsd: Fix leaked ...
130
  	kfree(cksum.data);
350586879   Herbert Xu   [CRYPTO] users: U...
131
132
  	crypto_free_hash(desc.tfm);
  out_no_tfm:
a55370a3c   NeilBrown   [PATCH] knfsd: nf...
133
134
  	return status;
  }
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
135

2216d449a   Jeff Layton   nfsd: get rid of ...
136
137
138
139
140
141
  /*
   * 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...
142
  legacy_recdir_name_error(struct nfs4_client *clp, int error)
2216d449a   Jeff Layton   nfsd: get rid of ...
143
144
145
146
147
148
149
150
151
152
153
154
155
156
  {
  	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...
157
  		nfsd4_client_tracking_exit(clp->net);
2216d449a   Jeff Layton   nfsd: get rid of ...
158
159
  	}
  }
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
160
161
  static void
  nfsd4_create_clid_dir(struct nfs4_client *clp)
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
162
  {
d84f4f992   David Howells   CRED: Inaugurate ...
163
  	const struct cred *original_cred;
2216d449a   Jeff Layton   nfsd: get rid of ...
164
  	char dname[HEXDIR_LEN];
e970a573c   Christoph Hellwig   nfsd: open a file...
165
  	struct dentry *dir, *dentry;
0ce0c2b5d   Jeff Layton   nfsd: don't searc...
166
  	struct nfs4_client_reclaim *crp;
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
167
  	int status;
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
168
  	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
169

a52d726bb   Jeff Layton   nfsd: convert nfs...
170
  	if (test_and_set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
7a6ef8c72   J. Bruce Fields   nfsd4: nfsd4_crea...
171
  		return;
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
172
  	if (!nn->rec_file)
7a6ef8c72   J. Bruce Fields   nfsd4: nfsd4_crea...
173
  		return;
2216d449a   Jeff Layton   nfsd: get rid of ...
174
175
176
  
  	status = nfs4_make_rec_clidname(dname, &clp->cl_name);
  	if (status)
7255e716b   Jeff Layton   nfsd: fix oops wh...
177
  		return legacy_recdir_name_error(clp, status);
2216d449a   Jeff Layton   nfsd: get rid of ...
178

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

3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
183
  	status = mnt_want_write_file(nn->rec_file);
4a55c1017   Jan Kara   nfsd: Push mnt_wa...
184
185
  	if (status)
  		return;
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
186
  	dir = nn->rec_file->f_path.dentry;
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
187
  	/* lock the parent */
e970a573c   Christoph Hellwig   nfsd: open a file...
188
  	mutex_lock(&dir->d_inode->i_mutex);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
189

e970a573c   Christoph Hellwig   nfsd: open a file...
190
  	dentry = lookup_one_len(dname, dir, HEXDIR_LEN-1);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
191
192
193
194
  	if (IS_ERR(dentry)) {
  		status = PTR_ERR(dentry);
  		goto out_unlock;
  	}
6577aac01   Boaz Harrosh   nfsd4: fix failur...
195
  	if (dentry->d_inode)
aec39680b   J. Bruce Fields   nfsd4: fix spurio...
196
197
198
199
200
201
202
203
  		/*
  		 * 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...
204
  		goto out_put;
e970a573c   Christoph Hellwig   nfsd: open a file...
205
  	status = vfs_mkdir(dir->d_inode, dentry, S_IRWXU);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
206
207
208
  out_put:
  	dput(dentry);
  out_unlock:
e970a573c   Christoph Hellwig   nfsd: open a file...
209
  	mutex_unlock(&dir->d_inode->i_mutex);
0ce0c2b5d   Jeff Layton   nfsd: don't searc...
210
  	if (status == 0) {
f141f79d7   Stanislav Kinsbursky   nfsd: recovery - ...
211
  		if (nn->in_grace) {
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
212
  			crp = nfs4_client_to_reclaim(dname, nn);
0ce0c2b5d   Jeff Layton   nfsd: don't searc...
213
214
215
  			if (crp)
  				crp->cr_clp = clp;
  		}
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
216
  		vfs_fsync(nn->rec_file, 0);
0ce0c2b5d   Jeff Layton   nfsd: don't searc...
217
  	} else {
6577aac01   Boaz Harrosh   nfsd4: fix failur...
218
219
220
221
  		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...
222
  	}
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
223
  	mnt_drop_write_file(nn->rec_file);
d84f4f992   David Howells   CRED: Inaugurate ...
224
  	nfs4_reset_creds(original_cred);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
225
  }
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
226
  typedef int (recdir_func)(struct dentry *, struct dentry *, struct nfsd_net *);
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
227

05f4f678b   J. Bruce Fields   nfsd4: don't do l...
228
229
  struct name_list {
  	char name[HEXDIR_LEN];
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
230
231
  	struct list_head list;
  };
bb6f619b3   Al Viro   [readdir] introdu...
232
233
234
235
  struct nfs4_dir_ctx {
  	struct dir_context ctx;
  	struct list_head names;
  };
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
236
  static int
05f4f678b   J. Bruce Fields   nfsd4: don't do l...
237
  nfsd4_build_namelist(void *arg, const char *name, int namlen,
afefdbb28   David Howells   [PATCH] VFS: Make...
238
  		loff_t offset, u64 ino, unsigned int d_type)
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
239
  {
bb6f619b3   Al Viro   [readdir] introdu...
240
  	struct nfs4_dir_ctx *ctx = arg;
05f4f678b   J. Bruce Fields   nfsd4: don't do l...
241
  	struct name_list *entry;
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
242

05f4f678b   J. Bruce Fields   nfsd4: don't do l...
243
  	if (namlen != HEXDIR_LEN - 1)
b37ad28bc   Al Viro   [PATCH] nfsd: nfs...
244
  		return 0;
05f4f678b   J. Bruce Fields   nfsd4: don't do l...
245
246
  	entry = kmalloc(sizeof(struct name_list), GFP_KERNEL);
  	if (entry == NULL)
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
247
  		return -ENOMEM;
05f4f678b   J. Bruce Fields   nfsd4: don't do l...
248
249
  	memcpy(entry->name, name, HEXDIR_LEN - 1);
  	entry->name[HEXDIR_LEN - 1] = '\0';
bb6f619b3   Al Viro   [readdir] introdu...
250
  	list_add(&entry->list, &ctx->names);
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
251
252
253
254
  	return 0;
  }
  
  static int
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
255
  nfsd4_list_rec_dir(recdir_func *f, struct nfsd_net *nn)
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
256
  {
d84f4f992   David Howells   CRED: Inaugurate ...
257
  	const struct cred *original_cred;
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
258
  	struct dentry *dir = nn->rec_file->f_path.dentry;
ac6614b76   Al Viro   [readdir] constif...
259
260
261
262
  	struct nfs4_dir_ctx ctx = {
  		.ctx.actor = nfsd4_build_namelist,
  		.names = LIST_HEAD_INIT(ctx.names)
  	};
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
263
  	int status;
d84f4f992   David Howells   CRED: Inaugurate ...
264
265
266
  	status = nfs4_save_creds(&original_cred);
  	if (status < 0)
  		return status;
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
267

3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
268
  	status = vfs_llseek(nn->rec_file, 0, SEEK_SET);
5b4b299cc   Al Viro   nfsd4_list_rec_di...
269
270
271
272
  	if (status < 0) {
  		nfs4_reset_creds(original_cred);
  		return status;
  	}
5c0ba4e07   Al Viro   [readdir] introdu...
273
  	status = iterate_dir(nn->rec_file, &ctx.ctx);
8daed1e54   J. Bruce Fields   nfsd: silence loc...
274
  	mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT);
5c0ba4e07   Al Viro   [readdir] introdu...
275
  	while (!list_empty(&ctx.names)) {
5b4b299cc   Al Viro   nfsd4_list_rec_di...
276
  		struct name_list *entry;
5c0ba4e07   Al Viro   [readdir] introdu...
277
  		entry = list_entry(ctx.names.next, struct name_list, list);
5b4b299cc   Al Viro   nfsd4_list_rec_di...
278
279
280
281
282
283
284
  		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...
285
  			status = f(dir, dentry, nn);
5b4b299cc   Al Viro   nfsd4_list_rec_di...
286
  			dput(dentry);
05f4f678b   J. Bruce Fields   nfsd4: don't do l...
287
  		}
05f4f678b   J. Bruce Fields   nfsd4: don't do l...
288
289
  		list_del(&entry->list);
  		kfree(entry);
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
290
  	}
2f9092e10   David Woodhouse   Fix i_mutex vs. r...
291
  	mutex_unlock(&dir->d_inode->i_mutex);
d84f4f992   David Howells   CRED: Inaugurate ...
292
  	nfs4_reset_creds(original_cred);
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
293
294
295
296
  	return status;
  }
  
  static int
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
297
  nfsd4_unlink_clid_dir(char *name, int namlen, struct nfsd_net *nn)
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
298
  {
e970a573c   Christoph Hellwig   nfsd: open a file...
299
  	struct dentry *dir, *dentry;
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
300
301
302
303
  	int status;
  
  	dprintk("NFSD: nfsd4_unlink_clid_dir. name %.*s
  ", namlen, name);
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
304
  	dir = nn->rec_file->f_path.dentry;
e970a573c   Christoph Hellwig   nfsd: open a file...
305
306
  	mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT);
  	dentry = lookup_one_len(name, dir, namlen);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
307
308
  	if (IS_ERR(dentry)) {
  		status = PTR_ERR(dentry);
2f9092e10   David Woodhouse   Fix i_mutex vs. r...
309
  		goto out_unlock;
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
310
311
312
313
  	}
  	status = -ENOENT;
  	if (!dentry->d_inode)
  		goto out;
e970a573c   Christoph Hellwig   nfsd: open a file...
314
  	status = vfs_rmdir(dir->d_inode, dentry);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
315
316
  out:
  	dput(dentry);
2f9092e10   David Woodhouse   Fix i_mutex vs. r...
317
  out_unlock:
e970a573c   Christoph Hellwig   nfsd: open a file...
318
  	mutex_unlock(&dir->d_inode->i_mutex);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
319
320
  	return status;
  }
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
321
  static void
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
322
323
  nfsd4_remove_clid_dir(struct nfs4_client *clp)
  {
d84f4f992   David Howells   CRED: Inaugurate ...
324
  	const struct cred *original_cred;
0ce0c2b5d   Jeff Layton   nfsd: don't searc...
325
  	struct nfs4_client_reclaim *crp;
2216d449a   Jeff Layton   nfsd: get rid of ...
326
  	char dname[HEXDIR_LEN];
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
327
  	int status;
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
328
  	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
329

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

3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
336
  	status = mnt_want_write_file(nn->rec_file);
0622753b8   Dave Hansen   [PATCH] r/o bind ...
337
338
  	if (status)
  		goto out;
a52d726bb   Jeff Layton   nfsd: convert nfs...
339
  	clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
d84f4f992   David Howells   CRED: Inaugurate ...
340
341
342
  
  	status = nfs4_save_creds(&original_cred);
  	if (status < 0)
698d8d875   Jeff Layton   nfsd: fix error h...
343
  		goto out_drop_write;
d84f4f992   David Howells   CRED: Inaugurate ...
344

3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
345
  	status = nfsd4_unlink_clid_dir(dname, HEXDIR_LEN-1, nn);
d84f4f992   David Howells   CRED: Inaugurate ...
346
  	nfs4_reset_creds(original_cred);
0ce0c2b5d   Jeff Layton   nfsd: don't searc...
347
  	if (status == 0) {
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
348
  		vfs_fsync(nn->rec_file, 0);
f141f79d7   Stanislav Kinsbursky   nfsd: recovery - ...
349
  		if (nn->in_grace) {
0ce0c2b5d   Jeff Layton   nfsd: don't searc...
350
  			/* remove reclaim record */
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
351
  			crp = nfsd4_find_reclaim_client(dname, nn);
0ce0c2b5d   Jeff Layton   nfsd: don't searc...
352
  			if (crp)
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
353
  				nfs4_remove_reclaim_record(crp, nn);
0ce0c2b5d   Jeff Layton   nfsd: don't searc...
354
355
  		}
  	}
698d8d875   Jeff Layton   nfsd: fix error h...
356
  out_drop_write:
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
357
  	mnt_drop_write_file(nn->rec_file);
0622753b8   Dave Hansen   [PATCH] r/o bind ...
358
  out:
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
359
360
  	if (status)
  		printk("NFSD: Failed to remove expired client state directory"
2216d449a   Jeff Layton   nfsd: get rid of ...
361
362
  				" %.*s
  ", HEXDIR_LEN, dname);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
363
364
365
  }
  
  static int
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
366
  purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn)
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
367
368
  {
  	int status;
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
369
  	if (nfs4_has_reclaimed_state(child->d_name.name, nn))
b37ad28bc   Al Viro   [PATCH] nfsd: nfs...
370
  		return 0;
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
371

2f9092e10   David Woodhouse   Fix i_mutex vs. r...
372
  	status = vfs_rmdir(parent->d_inode, child);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
373
  	if (status)
a6a9f18f0   Al Viro   nfsd: switch to %...
374
375
376
  		printk("failed to remove client recovery directory %pd
  ",
  				child);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
377
  	/* Keep trying, success or failure: */
b37ad28bc   Al Viro   [PATCH] nfsd: nfs...
378
  	return 0;
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
379
  }
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
380
  static void
12760c668   Stanislav Kinsbursky   nfsd: pass nfsd_n...
381
  nfsd4_recdir_purge_old(struct nfsd_net *nn, time_t boot_time)
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
382
  {
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
383
  	int status;
f141f79d7   Stanislav Kinsbursky   nfsd: recovery - ...
384
  	nn->in_grace = false;
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
385
  	if (!nn->rec_file)
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
386
  		return;
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
387
  	status = mnt_want_write_file(nn->rec_file);
0622753b8   Dave Hansen   [PATCH] r/o bind ...
388
389
  	if (status)
  		goto out;
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
390
  	status = nfsd4_list_rec_dir(purge_old, nn);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
391
  	if (status == 0)
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
392
393
  		vfs_fsync(nn->rec_file, 0);
  	mnt_drop_write_file(nn->rec_file);
0622753b8   Dave Hansen   [PATCH] r/o bind ...
394
  out:
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
395
  	nfs4_release_reclaim(nn);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
396
397
  	if (status)
  		printk("nfsd4: failed to purge old clients from recovery"
a6a9f18f0   Al Viro   nfsd: switch to %...
398
399
  			" directory %pD
  ", nn->rec_file);
c7b9a4592   NeilBrown   [PATCH] knfsd: nf...
400
401
402
  }
  
  static int
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
403
  load_recdir(struct dentry *parent, struct dentry *child, struct nfsd_net *nn)
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
404
405
  {
  	if (child->d_name.len != HEXDIR_LEN - 1) {
a6a9f18f0   Al Viro   nfsd: switch to %...
406
407
408
  		printk("nfsd4: illegal name %pd in recovery directory
  ",
  				child);
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
409
  		/* Keep trying; maybe the others are OK: */
b37ad28bc   Al Viro   [PATCH] nfsd: nfs...
410
  		return 0;
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
411
  	}
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
412
  	nfs4_client_to_reclaim(child->d_name.name, nn);
b37ad28bc   Al Viro   [PATCH] nfsd: nfs...
413
  	return 0;
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
414
  }
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
415
  static int
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
416
  nfsd4_recdir_load(struct net *net) {
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
417
  	int status;
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
418
  	struct nfsd_net *nn =  net_generic(net, nfsd_net_id);
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
419

3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
420
  	if (!nn->rec_file)
e970a573c   Christoph Hellwig   nfsd: open a file...
421
  		return 0;
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
422
  	status = nfsd4_list_rec_dir(load_recdir, nn);
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
423
424
  	if (status)
  		printk("nfsd4: failed loading clients from recovery"
a6a9f18f0   Al Viro   nfsd: switch to %...
425
426
  			" directory %pD
  ", nn->rec_file);
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
427
428
429
430
431
432
  	return status;
  }
  
  /*
   * Hold reference to the recovery directory.
   */
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
433
  static int
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
434
  nfsd4_init_recdir(struct net *net)
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
435
  {
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
436
  	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
d84f4f992   David Howells   CRED: Inaugurate ...
437
438
  	const struct cred *original_cred;
  	int status;
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
439
440
441
  
  	printk("NFSD: Using %s as the NFSv4 state recovery directory
  ",
48483bf23   J. Bruce Fields   nfsd4: simplify r...
442
  			user_recovery_dirname);
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
443

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

d84f4f992   David Howells   CRED: Inaugurate ...
446
447
448
449
450
451
  	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...
452
  		return status;
d84f4f992   David Howells   CRED: Inaugurate ...
453
  	}
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
454

3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
455
456
  	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...
457
458
  		printk("NFSD: unable to find recovery directory %s
  ",
48483bf23   J. Bruce Fields   nfsd4: simplify r...
459
  				user_recovery_dirname);
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
460
461
  		status = PTR_ERR(nn->rec_file);
  		nn->rec_file = NULL;
e970a573c   Christoph Hellwig   nfsd: open a file...
462
  	}
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
463

d84f4f992   David Howells   CRED: Inaugurate ...
464
  	nfs4_reset_creds(original_cred);
0ce0c2b5d   Jeff Layton   nfsd: don't searc...
465
  	if (!status)
f141f79d7   Stanislav Kinsbursky   nfsd: recovery - ...
466
  		nn->in_grace = true;
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
467
  	return status;
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
468
  }
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
  
  static int
  nfs4_legacy_state_init(struct net *net)
  {
  	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
  	int i;
  
  	nn->reclaim_str_hashtbl = kmalloc(sizeof(struct list_head) *
  					  CLIENT_HASH_SIZE, 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;
  
  	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...
495
496
497
498
  static int
  nfsd4_load_reboot_recovery_data(struct net *net)
  {
  	int status;
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
499
  	status = nfsd4_init_recdir(net);
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
500
501
  	if (!status)
  		status = nfsd4_recdir_load(net);
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
502
503
504
505
506
507
508
509
510
511
  	if (status)
  		printk(KERN_ERR "NFSD: Failure reading reboot recovery data
  ");
  	return status;
  }
  
  static int
  nfsd4_legacy_tracking_init(struct net *net)
  {
  	int status;
cc27e0d40   Jeff Layton   nfsd: don't allow...
512
513
514
515
516
517
518
  	/* XXX: The legacy code won't work in a container */
  	if (net != &init_net) {
  		WARN(1, KERN_ERR "NFSD: attempt to initialize legacy client "
  			"tracking in a container!
  ");
  		return -EINVAL;
  	}
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
519
  	status = nfs4_legacy_state_init(net);
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
520
  	if (status)
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
521
522
523
524
525
526
527
528
529
  		return status;
  
  	status = nfsd4_load_reboot_recovery_data(net);
  	if (status)
  		goto err;
  	return 0;
  
  err:
  	nfs4_legacy_state_shutdown(net);
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
530
531
532
533
  	return status;
  }
  
  static void
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
534
  nfsd4_shutdown_recdir(struct nfsd_net *nn)
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
535
  {
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
536
  	if (!nn->rec_file)
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
537
  		return;
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
538
539
  	fput(nn->rec_file);
  	nn->rec_file = NULL;
190e4fbf9   NeilBrown   [PATCH] knfsd: nf...
540
  }
48483bf23   J. Bruce Fields   nfsd4: simplify r...
541

2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
542
543
544
  static void
  nfsd4_legacy_tracking_exit(struct net *net)
  {
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
545
546
547
  	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
  
  	nfs4_release_reclaim(nn);
3a0733692   Stanislav Kinsbursky   nfsd: recovery - ...
548
  	nfsd4_shutdown_recdir(nn);
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
549
  	nfs4_legacy_state_shutdown(net);
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
550
  }
48483bf23   J. Bruce Fields   nfsd4: simplify r...
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
  /*
   * 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;
  	if (S_ISDIR(path.dentry->d_inode->i_mode)) {
  		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...
577
578
579
580
  
  static int
  nfsd4_check_legacy_client(struct nfs4_client *clp)
  {
2216d449a   Jeff Layton   nfsd: get rid of ...
581
582
  	int status;
  	char dname[HEXDIR_LEN];
0ce0c2b5d   Jeff Layton   nfsd: don't searc...
583
  	struct nfs4_client_reclaim *crp;
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
584
  	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
0ce0c2b5d   Jeff Layton   nfsd: don't searc...
585

2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
586
587
588
  	/* 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 ...
589
590
  	status = nfs4_make_rec_clidname(dname, &clp->cl_name);
  	if (status) {
7255e716b   Jeff Layton   nfsd: fix oops wh...
591
  		legacy_recdir_name_error(clp, status);
2216d449a   Jeff Layton   nfsd: get rid of ...
592
593
  		return status;
  	}
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
594
  	/* look for it in the reclaim hashtable otherwise */
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
595
  	crp = nfsd4_find_reclaim_client(dname, nn);
0ce0c2b5d   Jeff Layton   nfsd: don't searc...
596
  	if (crp) {
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
597
  		set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
0ce0c2b5d   Jeff Layton   nfsd: don't searc...
598
  		crp->cr_clp = clp;
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
599
600
601
602
603
604
605
  		return 0;
  	}
  
  	return -ENOENT;
  }
  
  static struct nfsd4_client_tracking_ops nfsd4_legacy_tracking_ops = {
52e19c09a   Stanislav Kinsbursky   nfsd: make reclai...
606
  	.init		= nfsd4_legacy_tracking_init,
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
607
608
609
610
611
612
  	.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,
  };
f3f801486   Jeff Layton   nfsd: add the inf...
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
  /* 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;
  };
  
  struct cld_upcall {
  	struct list_head	 cu_list;
  	struct cld_net		*cu_net;
  	struct task_struct	*cu_task;
  	struct cld_msg		 cu_msg;
  };
  
  static int
  __cld_pipe_upcall(struct rpc_pipe *pipe, struct cld_msg *cmsg)
  {
  	int ret;
  	struct rpc_pipe_msg msg;
  
  	memset(&msg, 0, sizeof(msg));
  	msg.data = cmsg;
  	msg.len = sizeof(*cmsg);
  
  	/*
  	 * Set task state before we queue the upcall. That prevents
  	 * wake_up_process in the downcall from racing with schedule.
  	 */
  	set_current_state(TASK_UNINTERRUPTIBLE);
  	ret = rpc_queue_upcall(pipe, &msg);
  	if (ret < 0) {
  		set_current_state(TASK_RUNNING);
  		goto out;
  	}
  
  	schedule();
  	set_current_state(TASK_RUNNING);
  
  	if (msg.errno < 0)
  		ret = msg.errno;
  out:
  	return ret;
  }
  
  static int
  cld_pipe_upcall(struct rpc_pipe *pipe, struct cld_msg *cmsg)
  {
  	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
  cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
  {
  	struct cld_upcall *tmp, *cup;
bc1b542be   J. Bruce Fields   nfsd4: preserve _...
682
  	struct cld_msg __user *cmsg = (struct cld_msg __user *)src;
f3f801486   Jeff Layton   nfsd: add the inf...
683
684
685
686
687
688
  	uint32_t xid;
  	struct nfsd_net *nn = net_generic(filp->f_dentry->d_sb->s_fs_info,
  						nfsd_net_id);
  	struct cld_net *cn = nn->cld_net;
  
  	if (mlen != sizeof(*cmsg)) {
8a7dc4b04   Randy Dunlap   nfsd: fix nfs4rec...
689
690
  		dprintk("%s: got %zu bytes, expected %zu
  ", __func__, mlen,
f3f801486   Jeff Layton   nfsd: add the inf...
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
  			sizeof(*cmsg));
  		return -EINVAL;
  	}
  
  	/* copy just the xid so we can try to find that */
  	if (copy_from_user(&xid, &cmsg->cm_xid, sizeof(xid)) != 0) {
  		dprintk("%s: error when copying xid from userspace", __func__);
  		return -EFAULT;
  	}
  
  	/* walk the list and find corresponding xid */
  	cup = NULL;
  	spin_lock(&cn->cn_lock);
  	list_for_each_entry(tmp, &cn->cn_list, cu_list) {
  		if (get_unaligned(&tmp->cu_msg.cm_xid) == xid) {
  			cup = tmp;
  			list_del_init(&cup->cu_list);
  			break;
  		}
  	}
  	spin_unlock(&cn->cn_lock);
  
  	/* couldn't find upcall? */
  	if (!cup) {
21f72c9f0   Jeff Layton   nfsd: fix NULL po...
715
716
  		dprintk("%s: couldn't find upcall -- xid=%u
  ", __func__, xid);
f3f801486   Jeff Layton   nfsd: add the inf...
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
  		return -EINVAL;
  	}
  
  	if (copy_from_user(&cup->cu_msg, src, mlen) != 0)
  		return -EFAULT;
  
  	wake_up_process(cup->cu_task);
  	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,
  						 cu_msg);
  
  	/* errno >= 0 means we got a downcall */
  	if (msg->errno >= 0)
  		return;
  
  	wake_up_process(cup->cu_task);
  }
  
  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
  nfsd4_init_cld_pipe(struct net *net)
  {
  	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;
  	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;
  }
  
  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);
  	kfree(nn->cld_net);
  	nn->cld_net = NULL;
  }
  
  static struct cld_upcall *
  alloc_cld_upcall(struct cld_net *cn)
  {
  	struct cld_upcall *new, *tmp;
  
  	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) {
  		if (tmp->cu_msg.cm_xid == cn->cn_xid) {
  			cn->cn_xid++;
  			spin_unlock(&cn->cn_lock);
  			goto restart_search;
  		}
  	}
  	new->cu_task = current;
  	new->cu_msg.cm_vers = CLD_UPCALL_VERSION;
  	put_unaligned(cn->cn_xid++, &new->cu_msg.cm_xid);
  	new->cu_net = cn;
  	list_add(&new->cu_list, &cn->cn_list);
  	spin_unlock(&cn->cn_lock);
  
  	dprintk("%s: allocated xid %u
  ", __func__, new->cu_msg.cm_xid);
  
  	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...
900
  	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
f3f801486   Jeff Layton   nfsd: add the inf...
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
  	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;
  
  	cup = alloc_cld_upcall(cn);
  	if (!cup) {
  		ret = -ENOMEM;
  		goto out_err;
  	}
  
  	cup->cu_msg.cm_cmd = Cld_Create;
  	cup->cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len;
  	memcpy(cup->cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data,
  			clp->cl_name.len);
  
  	ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg);
  	if (!ret) {
  		ret = cup->cu_msg.cm_status;
  		set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
  	}
  
  	free_cld_upcall(cup);
  out_err:
  	if (ret)
  		printk(KERN_ERR "NFSD: Unable to create client "
  				"record on stable storage: %d
  ", ret);
  }
  
  /* Ask daemon to create a new record */
  static void
  nfsd4_cld_remove(struct nfs4_client *clp)
  {
  	int ret;
  	struct cld_upcall *cup;
c212cecfa   Stanislav Kinsbursky   nfsd: make nfs4_c...
938
  	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
f3f801486   Jeff Layton   nfsd: add the inf...
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
971
972
973
974
975
  	struct cld_net *cn = nn->cld_net;
  
  	/* Don't upcall if it's already removed */
  	if (!test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
  		return;
  
  	cup = alloc_cld_upcall(cn);
  	if (!cup) {
  		ret = -ENOMEM;
  		goto out_err;
  	}
  
  	cup->cu_msg.cm_cmd = Cld_Remove;
  	cup->cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len;
  	memcpy(cup->cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data,
  			clp->cl_name.len);
  
  	ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg);
  	if (!ret) {
  		ret = cup->cu_msg.cm_status;
  		clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
  	}
  
  	free_cld_upcall(cup);
  out_err:
  	if (ret)
  		printk(KERN_ERR "NFSD: Unable to remove client "
  				"record from stable storage: %d
  ", ret);
  }
  
  /* Check for presence of a record, and update its timestamp */
  static int
  nfsd4_cld_check(struct nfs4_client *clp)
  {
  	int ret;
  	struct cld_upcall *cup;
c212cecfa   Stanislav Kinsbursky   nfsd: make nfs4_c...
976
  	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
f3f801486   Jeff Layton   nfsd: add the inf...
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
1002
1003
1004
1005
1006
  	struct cld_net *cn = nn->cld_net;
  
  	/* Don't upcall if one was already stored during this grace pd */
  	if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
  		return 0;
  
  	cup = alloc_cld_upcall(cn);
  	if (!cup) {
  		printk(KERN_ERR "NFSD: Unable to check client record on "
  				"stable storage: %d
  ", -ENOMEM);
  		return -ENOMEM;
  	}
  
  	cup->cu_msg.cm_cmd = Cld_Check;
  	cup->cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len;
  	memcpy(cup->cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data,
  			clp->cl_name.len);
  
  	ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg);
  	if (!ret) {
  		ret = cup->cu_msg.cm_status;
  		set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
  	}
  
  	free_cld_upcall(cup);
  	return ret;
  }
  
  static void
12760c668   Stanislav Kinsbursky   nfsd: pass nfsd_n...
1007
  nfsd4_cld_grace_done(struct nfsd_net *nn, time_t boot_time)
f3f801486   Jeff Layton   nfsd: add the inf...
1008
1009
1010
  {
  	int ret;
  	struct cld_upcall *cup;
f3f801486   Jeff Layton   nfsd: add the inf...
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
  	struct cld_net *cn = nn->cld_net;
  
  	cup = alloc_cld_upcall(cn);
  	if (!cup) {
  		ret = -ENOMEM;
  		goto out_err;
  	}
  
  	cup->cu_msg.cm_cmd = Cld_GraceDone;
  	cup->cu_msg.cm_u.cm_gracetime = (int64_t)boot_time;
  	ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg);
  	if (!ret)
  		ret = cup->cu_msg.cm_status;
  
  	free_cld_upcall(cup);
  out_err:
  	if (ret)
  		printk(KERN_ERR "NFSD: Unable to end grace period: %d
  ", ret);
  }
  
  static struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops = {
  	.init		= nfsd4_init_cld_pipe,
  	.exit		= nfsd4_remove_cld_pipe,
  	.create		= nfsd4_cld_create,
  	.remove		= nfsd4_cld_remove,
  	.check		= nfsd4_cld_check,
  	.grace_done	= nfsd4_cld_grace_done,
  };
2873d2147   Jeff Layton   nfsd: add a userm...
1040
1041
1042
1043
1044
  /* 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...
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
  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="
  
  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 ...
1082
  nfsd4_cltrack_legacy_recdir(const struct xdr_netobj *name)
f3aa7e24c   Jeff Layton   nfsd: pass info a...
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
  {
  	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 ...
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
  	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...
1108
1109
1110
1111
1112
1113
  		kfree(result);
  		return NULL;
  	}
  
  	return result;
  }
2873d2147   Jeff Layton   nfsd: add a userm...
1114
  static int
f3aa7e24c   Jeff Layton   nfsd: pass info a...
1115
  nfsd4_umh_cltrack_upcall(char *cmd, char *arg, char *legacy)
2873d2147   Jeff Layton   nfsd: add a userm...
1116
  {
f3aa7e24c   Jeff Layton   nfsd: pass info a...
1117
  	char *envp[2];
2873d2147   Jeff Layton   nfsd: add a userm...
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
  	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)");
f3aa7e24c   Jeff Layton   nfsd: pass info a...
1131
1132
1133
1134
1135
  	dprintk("%s: legacy: %s
  ", __func__, legacy ? legacy : "(null)");
  
  	envp[0] = legacy;
  	envp[1] = NULL;
2873d2147   Jeff Layton   nfsd: add a userm...
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
  
  	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
  nfsd4_umh_cltrack_init(struct net __attribute__((unused)) *net)
  {
71a503069   Stanislav Kinsbursky   nfsd: disable use...
1182
1183
1184
1185
1186
1187
1188
  	/* XXX: The usermode helper s not working in container yet. */
  	if (net != &init_net) {
  		WARN(1, KERN_ERR "NFSD: attempt to initialize umh client "
  			"tracking in a container!
  ");
  		return -EINVAL;
  	}
f3aa7e24c   Jeff Layton   nfsd: pass info a...
1189
  	return nfsd4_umh_cltrack_upcall("init", NULL, NULL);
2873d2147   Jeff Layton   nfsd: add a userm...
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
  }
  
  static void
  nfsd4_umh_cltrack_create(struct nfs4_client *clp)
  {
  	char *hexid;
  
  	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;
  	}
f3aa7e24c   Jeff Layton   nfsd: pass info a...
1203
  	nfsd4_umh_cltrack_upcall("create", hexid, NULL);
2873d2147   Jeff Layton   nfsd: add a userm...
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
  	kfree(hexid);
  }
  
  static void
  nfsd4_umh_cltrack_remove(struct nfs4_client *clp)
  {
  	char *hexid;
  
  	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;
  	}
f3aa7e24c   Jeff Layton   nfsd: pass info a...
1218
  	nfsd4_umh_cltrack_upcall("remove", hexid, NULL);
2873d2147   Jeff Layton   nfsd: add a userm...
1219
1220
1221
1222
1223
1224
1225
  	kfree(hexid);
  }
  
  static int
  nfsd4_umh_cltrack_check(struct nfs4_client *clp)
  {
  	int ret;
f3aa7e24c   Jeff Layton   nfsd: pass info a...
1226
  	char *hexid, *legacy;
2873d2147   Jeff Layton   nfsd: add a userm...
1227
1228
1229
1230
1231
1232
1233
  
  	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;
  	}
2216d449a   Jeff Layton   nfsd: get rid of ...
1234
  	legacy = nfsd4_cltrack_legacy_recdir(&clp->cl_name);
f3aa7e24c   Jeff Layton   nfsd: pass info a...
1235
1236
  	ret = nfsd4_umh_cltrack_upcall("check", hexid, legacy);
  	kfree(legacy);
2873d2147   Jeff Layton   nfsd: add a userm...
1237
1238
1239
1240
1241
  	kfree(hexid);
  	return ret;
  }
  
  static void
12760c668   Stanislav Kinsbursky   nfsd: pass nfsd_n...
1242
  nfsd4_umh_cltrack_grace_done(struct nfsd_net __attribute__((unused)) *nn,
2873d2147   Jeff Layton   nfsd: add a userm...
1243
1244
  				time_t boot_time)
  {
f3aa7e24c   Jeff Layton   nfsd: pass info a...
1245
  	char *legacy;
2873d2147   Jeff Layton   nfsd: add a userm...
1246
1247
1248
  	char timestr[22]; /* FIXME: better way to determine max size? */
  
  	sprintf(timestr, "%ld", boot_time);
f3aa7e24c   Jeff Layton   nfsd: pass info a...
1249
1250
1251
  	legacy = nfsd4_cltrack_legacy_topdir();
  	nfsd4_umh_cltrack_upcall("gracedone", timestr, legacy);
  	kfree(legacy);
2873d2147   Jeff Layton   nfsd: add a userm...
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
  }
  
  static struct nfsd4_client_tracking_ops nfsd4_umh_tracking_ops = {
  	.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,
  };
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
1262
1263
1264
1265
  int
  nfsd4_client_tracking_init(struct net *net)
  {
  	int status;
f3f801486   Jeff Layton   nfsd: add the inf...
1266
  	struct path path;
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
1267
  	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
1268

2d77bf0a5   Jeff Layton   nfsd: change heur...
1269
  	/* just run the init if it the method is already decided */
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
1270
  	if (nn->client_tracking_ops)
2d77bf0a5   Jeff Layton   nfsd: change heur...
1271
1272
1273
1274
1275
1276
  		goto do_init;
  
  	/*
  	 * First, try a UMH upcall. It should succeed or fail quickly, so
  	 * there's little harm in trying that first.
  	 */
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
1277
1278
  	nn->client_tracking_ops = &nfsd4_umh_tracking_ops;
  	status = nn->client_tracking_ops->init(net);
2d77bf0a5   Jeff Layton   nfsd: change heur...
1279
1280
1281
1282
1283
1284
1285
  	if (!status)
  		return status;
  
  	/*
  	 * See if the recoverydir exists and is a directory. If it is,
  	 * then use the legacy ops.
  	 */
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
1286
  	nn->client_tracking_ops = &nfsd4_legacy_tracking_ops;
2d77bf0a5   Jeff Layton   nfsd: change heur...
1287
1288
1289
1290
1291
1292
  	status = kern_path(nfs4_recoverydir(), LOOKUP_FOLLOW, &path);
  	if (!status) {
  		status = S_ISDIR(path.dentry->d_inode->i_mode);
  		path_put(&path);
  		if (status)
  			goto do_init;
f3f801486   Jeff Layton   nfsd: add the inf...
1293
  	}
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
1294

2d77bf0a5   Jeff Layton   nfsd: change heur...
1295
  	/* Finally, try to use nfsdcld */
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
1296
  	nn->client_tracking_ops = &nfsd4_cld_tracking_ops;
8b0554e9a   Jeff Layton   nfsd: warn about ...
1297
1298
1299
1300
  	printk(KERN_WARNING "NFSD: the nfsdcld client tracking upcall will be "
  			"removed in 3.10. Please transition to using "
  			"nfsdcltrack.
  ");
2d77bf0a5   Jeff Layton   nfsd: change heur...
1301
  do_init:
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
1302
  	status = nn->client_tracking_ops->init(net);
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
1303
1304
1305
1306
  	if (status) {
  		printk(KERN_WARNING "NFSD: Unable to initialize client "
  				    "recovery tracking! (%d)
  ", status);
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
1307
  		nn->client_tracking_ops = NULL;
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
1308
1309
1310
1311
1312
1313
1314
  	}
  	return status;
  }
  
  void
  nfsd4_client_tracking_exit(struct net *net)
  {
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
1315
1316
1317
1318
1319
1320
  	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...
1321
1322
1323
1324
1325
1326
  	}
  }
  
  void
  nfsd4_client_record_create(struct nfs4_client *clp)
  {
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
1327
1328
1329
1330
  	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...
1331
1332
1333
1334
1335
  }
  
  void
  nfsd4_client_record_remove(struct nfs4_client *clp)
  {
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
1336
1337
1338
1339
  	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...
1340
1341
1342
1343
1344
  }
  
  int
  nfsd4_client_record_check(struct nfs4_client *clp)
  {
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
1345
1346
1347
1348
  	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...
1349
1350
1351
1352
1353
  
  	return -EOPNOTSUPP;
  }
  
  void
12760c668   Stanislav Kinsbursky   nfsd: pass nfsd_n...
1354
  nfsd4_record_grace_done(struct nfsd_net *nn, time_t boot_time)
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
1355
  {
9a9c6478a   Stanislav Kinsbursky   nfsd: make NFSv4 ...
1356
1357
  	if (nn->client_tracking_ops)
  		nn->client_tracking_ops->grace_done(nn, boot_time);
2a4317c55   Jeff Layton   nfsd: add nfsd4_c...
1358
  }
813fd320c   Jeff Layton   nfsd: add notifie...
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
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
  
  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...
1398
  static struct notifier_block nfsd4_cld_block = {
813fd320c   Jeff Layton   nfsd: add notifie...
1399
1400
  	.notifier_call = rpc_pipefs_event,
  };
797a9d797   Jeff Layton   nfsd: only regist...
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
  
  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);
  }