aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfsd/nfs4recover.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-12-20 17:04:11 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2012-12-20 17:04:11 -0500
commit982197277c85018cc6eb77f1d3bef17933b0c5fd (patch)
tree805fcef9ec7c1e83867b89332fd37f751594fae3 /fs/nfsd/nfs4recover.c
parent40889e8d9fc6355980cf2bc94ef4356c10dec4ec (diff)
parent24ffb93872f7363a01ad639e3c8a9889b46c3f0a (diff)
Merge branch 'for-3.8' of git://linux-nfs.org/~bfields/linux
Pull nfsd update from Bruce Fields: "Included this time: - more nfsd containerization work from Stanislav Kinsbursky: we're not quite there yet, but should be by 3.9. - NFSv4.1 progress: implementation of basic backchannel security negotiation and the mandatory BACKCHANNEL_CTL operation. See http://wiki.linux-nfs.org/wiki/index.php/Server_4.0_and_4.1_issues for remaining TODO's - Fixes for some bugs that could be triggered by unusual compounds. Our xdr code wasn't designed with v4 compounds in mind, and it shows. A more thorough rewrite is still a todo. - If you've ever seen "RPC: multiple fragments per record not supported" logged while using some sort of odd userland NFS client, that should now be fixed. - Further work from Jeff Layton on our mechanism for storing information about NFSv4 clients across reboots. - Further work from Bryan Schumaker on his fault-injection mechanism (which allows us to discard selective NFSv4 state, to excercise rarely-taken recovery code paths in the client.) - The usual mix of miscellaneous bugs and cleanup. Thanks to everyone who tested or contributed this cycle." * 'for-3.8' of git://linux-nfs.org/~bfields/linux: (111 commits) nfsd4: don't leave freed stateid hashed nfsd4: free_stateid can use the current stateid nfsd4: cleanup: replace rq_resused count by rq_next_page pointer nfsd: warn on odd reply state in nfsd_vfs_read nfsd4: fix oops on unusual readlike compound nfsd4: disable zero-copy on non-final read ops svcrpc: fix some printks NFSD: Correct the size calculation in fault_inject_write NFSD: Pass correct buffer size to rpc_ntop nfsd: pass proper net to nfsd_destroy() from NFSd kthreads nfsd: simplify service shutdown nfsd: replace boolean nfsd_up flag by users counter nfsd: simplify NFSv4 state init and shutdown nfsd: introduce helpers for generic resources init and shutdown nfsd: make NFSd service structure allocated per net nfsd: make NFSd service boot time per-net nfsd: per-net NFSd up flag introduced nfsd: move per-net startup code to separated function nfsd: pass net to __write_ports() and down nfsd: pass net to nfsd_set_nrthreads() ...
Diffstat (limited to 'fs/nfsd/nfs4recover.c')
-rw-r--r--fs/nfsd/nfs4recover.c561
1 files changed, 461 insertions, 100 deletions
diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c
index 43295d45cc2b..ba6fdd4a0455 100644
--- a/fs/nfsd/nfs4recover.c
+++ b/fs/nfsd/nfs4recover.c
@@ -58,13 +58,11 @@ struct nfsd4_client_tracking_ops {
58 void (*create)(struct nfs4_client *); 58 void (*create)(struct nfs4_client *);
59 void (*remove)(struct nfs4_client *); 59 void (*remove)(struct nfs4_client *);
60 int (*check)(struct nfs4_client *); 60 int (*check)(struct nfs4_client *);
61 void (*grace_done)(struct net *, time_t); 61 void (*grace_done)(struct nfsd_net *, time_t);
62}; 62};
63 63
64/* Globals */ 64/* Globals */
65static struct file *rec_file;
66static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery"; 65static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery";
67static struct nfsd4_client_tracking_ops *client_tracking_ops;
68 66
69static int 67static int
70nfs4_save_creds(const struct cred **original_creds) 68nfs4_save_creds(const struct cred **original_creds)
@@ -102,33 +100,39 @@ md5_to_hex(char *out, char *md5)
102 *out = '\0'; 100 *out = '\0';
103} 101}
104 102
105__be32 103static int
106nfs4_make_rec_clidname(char *dname, struct xdr_netobj *clname) 104nfs4_make_rec_clidname(char *dname, const struct xdr_netobj *clname)
107{ 105{
108 struct xdr_netobj cksum; 106 struct xdr_netobj cksum;
109 struct hash_desc desc; 107 struct hash_desc desc;
110 struct scatterlist sg; 108 struct scatterlist sg;
111 __be32 status = nfserr_jukebox; 109 int status;
112 110
113 dprintk("NFSD: nfs4_make_rec_clidname for %.*s\n", 111 dprintk("NFSD: nfs4_make_rec_clidname for %.*s\n",
114 clname->len, clname->data); 112 clname->len, clname->data);
115 desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP; 113 desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
116 desc.tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC); 114 desc.tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC);
117 if (IS_ERR(desc.tfm)) 115 if (IS_ERR(desc.tfm)) {
116 status = PTR_ERR(desc.tfm);
118 goto out_no_tfm; 117 goto out_no_tfm;
118 }
119
119 cksum.len = crypto_hash_digestsize(desc.tfm); 120 cksum.len = crypto_hash_digestsize(desc.tfm);
120 cksum.data = kmalloc(cksum.len, GFP_KERNEL); 121 cksum.data = kmalloc(cksum.len, GFP_KERNEL);
121 if (cksum.data == NULL) 122 if (cksum.data == NULL) {
123 status = -ENOMEM;
122 goto out; 124 goto out;
125 }
123 126
124 sg_init_one(&sg, clname->data, clname->len); 127 sg_init_one(&sg, clname->data, clname->len);
125 128
126 if (crypto_hash_digest(&desc, &sg, sg.length, cksum.data)) 129 status = crypto_hash_digest(&desc, &sg, sg.length, cksum.data);
130 if (status)
127 goto out; 131 goto out;
128 132
129 md5_to_hex(dname, cksum.data); 133 md5_to_hex(dname, cksum.data);
130 134
131 status = nfs_ok; 135 status = 0;
132out: 136out:
133 kfree(cksum.data); 137 kfree(cksum.data);
134 crypto_free_hash(desc.tfm); 138 crypto_free_hash(desc.tfm);
@@ -136,29 +140,61 @@ out_no_tfm:
136 return status; 140 return status;
137} 141}
138 142
143/*
144 * If we had an error generating the recdir name for the legacy tracker
145 * then warn the admin. If the error doesn't appear to be transient,
146 * then disable recovery tracking.
147 */
148static void
149legacy_recdir_name_error(int error)
150{
151 printk(KERN_ERR "NFSD: unable to generate recoverydir "
152 "name (%d).\n", error);
153
154 /*
155 * if the algorithm just doesn't exist, then disable the recovery
156 * tracker altogether. The crypto libs will generally return this if
157 * FIPS is enabled as well.
158 */
159 if (error == -ENOENT) {
160 printk(KERN_ERR "NFSD: disabling legacy clientid tracking. "
161 "Reboot recovery will not function correctly!\n");
162
163 /* the argument is ignored by the legacy exit function */
164 nfsd4_client_tracking_exit(NULL);
165 }
166}
167
139static void 168static void
140nfsd4_create_clid_dir(struct nfs4_client *clp) 169nfsd4_create_clid_dir(struct nfs4_client *clp)
141{ 170{
142 const struct cred *original_cred; 171 const struct cred *original_cred;
143 char *dname = clp->cl_recdir; 172 char dname[HEXDIR_LEN];
144 struct dentry *dir, *dentry; 173 struct dentry *dir, *dentry;
174 struct nfs4_client_reclaim *crp;
145 int status; 175 int status;
176 struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
146 177
147 dprintk("NFSD: nfsd4_create_clid_dir for \"%s\"\n", dname); 178 dprintk("NFSD: nfsd4_create_clid_dir for \"%s\"\n", dname);
148 179
149 if (test_and_set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) 180 if (test_and_set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
150 return; 181 return;
151 if (!rec_file) 182 if (!nn->rec_file)
152 return; 183 return;
184
185 status = nfs4_make_rec_clidname(dname, &clp->cl_name);
186 if (status)
187 return legacy_recdir_name_error(status);
188
153 status = nfs4_save_creds(&original_cred); 189 status = nfs4_save_creds(&original_cred);
154 if (status < 0) 190 if (status < 0)
155 return; 191 return;
156 192
157 status = mnt_want_write_file(rec_file); 193 status = mnt_want_write_file(nn->rec_file);
158 if (status) 194 if (status)
159 return; 195 return;
160 196
161 dir = rec_file->f_path.dentry; 197 dir = nn->rec_file->f_path.dentry;
162 /* lock the parent */ 198 /* lock the parent */
163 mutex_lock(&dir->d_inode->i_mutex); 199 mutex_lock(&dir->d_inode->i_mutex);
164 200
@@ -182,18 +218,24 @@ out_put:
182 dput(dentry); 218 dput(dentry);
183out_unlock: 219out_unlock:
184 mutex_unlock(&dir->d_inode->i_mutex); 220 mutex_unlock(&dir->d_inode->i_mutex);
185 if (status == 0) 221 if (status == 0) {
186 vfs_fsync(rec_file, 0); 222 if (nn->in_grace) {
187 else 223 crp = nfs4_client_to_reclaim(dname, nn);
224 if (crp)
225 crp->cr_clp = clp;
226 }
227 vfs_fsync(nn->rec_file, 0);
228 } else {
188 printk(KERN_ERR "NFSD: failed to write recovery record" 229 printk(KERN_ERR "NFSD: failed to write recovery record"
189 " (err %d); please check that %s exists" 230 " (err %d); please check that %s exists"
190 " and is writeable", status, 231 " and is writeable", status,
191 user_recovery_dirname); 232 user_recovery_dirname);
192 mnt_drop_write_file(rec_file); 233 }
234 mnt_drop_write_file(nn->rec_file);
193 nfs4_reset_creds(original_cred); 235 nfs4_reset_creds(original_cred);
194} 236}
195 237
196typedef int (recdir_func)(struct dentry *, struct dentry *); 238typedef int (recdir_func)(struct dentry *, struct dentry *, struct nfsd_net *);
197 239
198struct name_list { 240struct name_list {
199 char name[HEXDIR_LEN]; 241 char name[HEXDIR_LEN];
@@ -219,10 +261,10 @@ nfsd4_build_namelist(void *arg, const char *name, int namlen,
219} 261}
220 262
221static int 263static int
222nfsd4_list_rec_dir(recdir_func *f) 264nfsd4_list_rec_dir(recdir_func *f, struct nfsd_net *nn)
223{ 265{
224 const struct cred *original_cred; 266 const struct cred *original_cred;
225 struct dentry *dir = rec_file->f_path.dentry; 267 struct dentry *dir = nn->rec_file->f_path.dentry;
226 LIST_HEAD(names); 268 LIST_HEAD(names);
227 int status; 269 int status;
228 270
@@ -230,13 +272,13 @@ nfsd4_list_rec_dir(recdir_func *f)
230 if (status < 0) 272 if (status < 0)
231 return status; 273 return status;
232 274
233 status = vfs_llseek(rec_file, 0, SEEK_SET); 275 status = vfs_llseek(nn->rec_file, 0, SEEK_SET);
234 if (status < 0) { 276 if (status < 0) {
235 nfs4_reset_creds(original_cred); 277 nfs4_reset_creds(original_cred);
236 return status; 278 return status;
237 } 279 }
238 280
239 status = vfs_readdir(rec_file, nfsd4_build_namelist, &names); 281 status = vfs_readdir(nn->rec_file, nfsd4_build_namelist, &names);
240 mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); 282 mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT);
241 while (!list_empty(&names)) { 283 while (!list_empty(&names)) {
242 struct name_list *entry; 284 struct name_list *entry;
@@ -248,7 +290,7 @@ nfsd4_list_rec_dir(recdir_func *f)
248 status = PTR_ERR(dentry); 290 status = PTR_ERR(dentry);
249 break; 291 break;
250 } 292 }
251 status = f(dir, dentry); 293 status = f(dir, dentry, nn);
252 dput(dentry); 294 dput(dentry);
253 } 295 }
254 list_del(&entry->list); 296 list_del(&entry->list);
@@ -260,14 +302,14 @@ nfsd4_list_rec_dir(recdir_func *f)
260} 302}
261 303
262static int 304static int
263nfsd4_unlink_clid_dir(char *name, int namlen) 305nfsd4_unlink_clid_dir(char *name, int namlen, struct nfsd_net *nn)
264{ 306{
265 struct dentry *dir, *dentry; 307 struct dentry *dir, *dentry;
266 int status; 308 int status;
267 309
268 dprintk("NFSD: nfsd4_unlink_clid_dir. name %.*s\n", namlen, name); 310 dprintk("NFSD: nfsd4_unlink_clid_dir. name %.*s\n", namlen, name);
269 311
270 dir = rec_file->f_path.dentry; 312 dir = nn->rec_file->f_path.dentry;
271 mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); 313 mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT);
272 dentry = lookup_one_len(name, dir, namlen); 314 dentry = lookup_one_len(name, dir, namlen);
273 if (IS_ERR(dentry)) { 315 if (IS_ERR(dentry)) {
@@ -289,37 +331,52 @@ static void
289nfsd4_remove_clid_dir(struct nfs4_client *clp) 331nfsd4_remove_clid_dir(struct nfs4_client *clp)
290{ 332{
291 const struct cred *original_cred; 333 const struct cred *original_cred;
334 struct nfs4_client_reclaim *crp;
335 char dname[HEXDIR_LEN];
292 int status; 336 int status;
337 struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
293 338
294 if (!rec_file || !test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) 339 if (!nn->rec_file || !test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
295 return; 340 return;
296 341
297 status = mnt_want_write_file(rec_file); 342 status = nfs4_make_rec_clidname(dname, &clp->cl_name);
343 if (status)
344 return legacy_recdir_name_error(status);
345
346 status = mnt_want_write_file(nn->rec_file);
298 if (status) 347 if (status)
299 goto out; 348 goto out;
300 clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); 349 clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
301 350
302 status = nfs4_save_creds(&original_cred); 351 status = nfs4_save_creds(&original_cred);
303 if (status < 0) 352 if (status < 0)
304 goto out; 353 goto out_drop_write;
305 354
306 status = nfsd4_unlink_clid_dir(clp->cl_recdir, HEXDIR_LEN-1); 355 status = nfsd4_unlink_clid_dir(dname, HEXDIR_LEN-1, nn);
307 nfs4_reset_creds(original_cred); 356 nfs4_reset_creds(original_cred);
308 if (status == 0) 357 if (status == 0) {
309 vfs_fsync(rec_file, 0); 358 vfs_fsync(nn->rec_file, 0);
310 mnt_drop_write_file(rec_file); 359 if (nn->in_grace) {
360 /* remove reclaim record */
361 crp = nfsd4_find_reclaim_client(dname, nn);
362 if (crp)
363 nfs4_remove_reclaim_record(crp, nn);
364 }
365 }
366out_drop_write:
367 mnt_drop_write_file(nn->rec_file);
311out: 368out:
312 if (status) 369 if (status)
313 printk("NFSD: Failed to remove expired client state directory" 370 printk("NFSD: Failed to remove expired client state directory"
314 " %.*s\n", HEXDIR_LEN, clp->cl_recdir); 371 " %.*s\n", HEXDIR_LEN, dname);
315} 372}
316 373
317static int 374static int
318purge_old(struct dentry *parent, struct dentry *child) 375purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn)
319{ 376{
320 int status; 377 int status;
321 378
322 if (nfs4_has_reclaimed_state(child->d_name.name, false)) 379 if (nfs4_has_reclaimed_state(child->d_name.name, nn))
323 return 0; 380 return 0;
324 381
325 status = vfs_rmdir(parent->d_inode, child); 382 status = vfs_rmdir(parent->d_inode, child);
@@ -331,27 +388,29 @@ purge_old(struct dentry *parent, struct dentry *child)
331} 388}
332 389
333static void 390static void
334nfsd4_recdir_purge_old(struct net *net, time_t boot_time) 391nfsd4_recdir_purge_old(struct nfsd_net *nn, time_t boot_time)
335{ 392{
336 int status; 393 int status;
337 394
338 if (!rec_file) 395 nn->in_grace = false;
396 if (!nn->rec_file)
339 return; 397 return;
340 status = mnt_want_write_file(rec_file); 398 status = mnt_want_write_file(nn->rec_file);
341 if (status) 399 if (status)
342 goto out; 400 goto out;
343 status = nfsd4_list_rec_dir(purge_old); 401 status = nfsd4_list_rec_dir(purge_old, nn);
344 if (status == 0) 402 if (status == 0)
345 vfs_fsync(rec_file, 0); 403 vfs_fsync(nn->rec_file, 0);
346 mnt_drop_write_file(rec_file); 404 mnt_drop_write_file(nn->rec_file);
347out: 405out:
406 nfs4_release_reclaim(nn);
348 if (status) 407 if (status)
349 printk("nfsd4: failed to purge old clients from recovery" 408 printk("nfsd4: failed to purge old clients from recovery"
350 " directory %s\n", rec_file->f_path.dentry->d_name.name); 409 " directory %s\n", nn->rec_file->f_path.dentry->d_name.name);
351} 410}
352 411
353static int 412static int
354load_recdir(struct dentry *parent, struct dentry *child) 413load_recdir(struct dentry *parent, struct dentry *child, struct nfsd_net *nn)
355{ 414{
356 if (child->d_name.len != HEXDIR_LEN - 1) { 415 if (child->d_name.len != HEXDIR_LEN - 1) {
357 printk("nfsd4: illegal name %s in recovery directory\n", 416 printk("nfsd4: illegal name %s in recovery directory\n",
@@ -359,21 +418,22 @@ load_recdir(struct dentry *parent, struct dentry *child)
359 /* Keep trying; maybe the others are OK: */ 418 /* Keep trying; maybe the others are OK: */
360 return 0; 419 return 0;
361 } 420 }
362 nfs4_client_to_reclaim(child->d_name.name); 421 nfs4_client_to_reclaim(child->d_name.name, nn);
363 return 0; 422 return 0;
364} 423}
365 424
366static int 425static int
367nfsd4_recdir_load(void) { 426nfsd4_recdir_load(struct net *net) {
368 int status; 427 int status;
428 struct nfsd_net *nn = net_generic(net, nfsd_net_id);
369 429
370 if (!rec_file) 430 if (!nn->rec_file)
371 return 0; 431 return 0;
372 432
373 status = nfsd4_list_rec_dir(load_recdir); 433 status = nfsd4_list_rec_dir(load_recdir, nn);
374 if (status) 434 if (status)
375 printk("nfsd4: failed loading clients from recovery" 435 printk("nfsd4: failed loading clients from recovery"
376 " directory %s\n", rec_file->f_path.dentry->d_name.name); 436 " directory %s\n", nn->rec_file->f_path.dentry->d_name.name);
377 return status; 437 return status;
378} 438}
379 439
@@ -382,15 +442,16 @@ nfsd4_recdir_load(void) {
382 */ 442 */
383 443
384static int 444static int
385nfsd4_init_recdir(void) 445nfsd4_init_recdir(struct net *net)
386{ 446{
447 struct nfsd_net *nn = net_generic(net, nfsd_net_id);
387 const struct cred *original_cred; 448 const struct cred *original_cred;
388 int status; 449 int status;
389 450
390 printk("NFSD: Using %s as the NFSv4 state recovery directory\n", 451 printk("NFSD: Using %s as the NFSv4 state recovery directory\n",
391 user_recovery_dirname); 452 user_recovery_dirname);
392 453
393 BUG_ON(rec_file); 454 BUG_ON(nn->rec_file);
394 455
395 status = nfs4_save_creds(&original_cred); 456 status = nfs4_save_creds(&original_cred);
396 if (status < 0) { 457 if (status < 0) {
@@ -400,23 +461,65 @@ nfsd4_init_recdir(void)
400 return status; 461 return status;
401 } 462 }
402 463
403 rec_file = filp_open(user_recovery_dirname, O_RDONLY | O_DIRECTORY, 0); 464 nn->rec_file = filp_open(user_recovery_dirname, O_RDONLY | O_DIRECTORY, 0);
404 if (IS_ERR(rec_file)) { 465 if (IS_ERR(nn->rec_file)) {
405 printk("NFSD: unable to find recovery directory %s\n", 466 printk("NFSD: unable to find recovery directory %s\n",
406 user_recovery_dirname); 467 user_recovery_dirname);
407 status = PTR_ERR(rec_file); 468 status = PTR_ERR(nn->rec_file);
408 rec_file = NULL; 469 nn->rec_file = NULL;
409 } 470 }
410 471
411 nfs4_reset_creds(original_cred); 472 nfs4_reset_creds(original_cred);
473 if (!status)
474 nn->in_grace = true;
412 return status; 475 return status;
413} 476}
414 477
478
479static int
480nfs4_legacy_state_init(struct net *net)
481{
482 struct nfsd_net *nn = net_generic(net, nfsd_net_id);
483 int i;
484
485 nn->reclaim_str_hashtbl = kmalloc(sizeof(struct list_head) *
486 CLIENT_HASH_SIZE, GFP_KERNEL);
487 if (!nn->reclaim_str_hashtbl)
488 return -ENOMEM;
489
490 for (i = 0; i < CLIENT_HASH_SIZE; i++)
491 INIT_LIST_HEAD(&nn->reclaim_str_hashtbl[i]);
492 nn->reclaim_str_hashtbl_size = 0;
493
494 return 0;
495}
496
497static void
498nfs4_legacy_state_shutdown(struct net *net)
499{
500 struct nfsd_net *nn = net_generic(net, nfsd_net_id);
501
502 kfree(nn->reclaim_str_hashtbl);
503}
504
415static int 505static int
416nfsd4_load_reboot_recovery_data(struct net *net) 506nfsd4_load_reboot_recovery_data(struct net *net)
417{ 507{
418 int status; 508 int status;
419 509
510 status = nfsd4_init_recdir(net);
511 if (!status)
512 status = nfsd4_recdir_load(net);
513 if (status)
514 printk(KERN_ERR "NFSD: Failure reading reboot recovery data\n");
515 return status;
516}
517
518static int
519nfsd4_legacy_tracking_init(struct net *net)
520{
521 int status;
522
420 /* XXX: The legacy code won't work in a container */ 523 /* XXX: The legacy code won't work in a container */
421 if (net != &init_net) { 524 if (net != &init_net) {
422 WARN(1, KERN_ERR "NFSD: attempt to initialize legacy client " 525 WARN(1, KERN_ERR "NFSD: attempt to initialize legacy client "
@@ -424,30 +527,37 @@ nfsd4_load_reboot_recovery_data(struct net *net)
424 return -EINVAL; 527 return -EINVAL;
425 } 528 }
426 529
427 nfs4_lock_state(); 530 status = nfs4_legacy_state_init(net);
428 status = nfsd4_init_recdir();
429 if (!status)
430 status = nfsd4_recdir_load();
431 nfs4_unlock_state();
432 if (status) 531 if (status)
433 printk(KERN_ERR "NFSD: Failure reading reboot recovery data\n"); 532 return status;
533
534 status = nfsd4_load_reboot_recovery_data(net);
535 if (status)
536 goto err;
537 return 0;
538
539err:
540 nfs4_legacy_state_shutdown(net);
434 return status; 541 return status;
435} 542}
436 543
437static void 544static void
438nfsd4_shutdown_recdir(void) 545nfsd4_shutdown_recdir(struct nfsd_net *nn)
439{ 546{
440 if (!rec_file) 547 if (!nn->rec_file)
441 return; 548 return;
442 fput(rec_file); 549 fput(nn->rec_file);
443 rec_file = NULL; 550 nn->rec_file = NULL;
444} 551}
445 552
446static void 553static void
447nfsd4_legacy_tracking_exit(struct net *net) 554nfsd4_legacy_tracking_exit(struct net *net)
448{ 555{
449 nfs4_release_reclaim(); 556 struct nfsd_net *nn = net_generic(net, nfsd_net_id);
450 nfsd4_shutdown_recdir(); 557
558 nfs4_release_reclaim(nn);
559 nfsd4_shutdown_recdir(nn);
560 nfs4_legacy_state_shutdown(net);
451} 561}
452 562
453/* 563/*
@@ -480,13 +590,26 @@ nfs4_recoverydir(void)
480static int 590static int
481nfsd4_check_legacy_client(struct nfs4_client *clp) 591nfsd4_check_legacy_client(struct nfs4_client *clp)
482{ 592{
593 int status;
594 char dname[HEXDIR_LEN];
595 struct nfs4_client_reclaim *crp;
596 struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
597
483 /* did we already find that this client is stable? */ 598 /* did we already find that this client is stable? */
484 if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) 599 if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
485 return 0; 600 return 0;
486 601
602 status = nfs4_make_rec_clidname(dname, &clp->cl_name);
603 if (status) {
604 legacy_recdir_name_error(status);
605 return status;
606 }
607
487 /* look for it in the reclaim hashtable otherwise */ 608 /* look for it in the reclaim hashtable otherwise */
488 if (nfsd4_find_reclaim_client(clp)) { 609 crp = nfsd4_find_reclaim_client(dname, nn);
610 if (crp) {
489 set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); 611 set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
612 crp->cr_clp = clp;
490 return 0; 613 return 0;
491 } 614 }
492 615
@@ -494,7 +617,7 @@ nfsd4_check_legacy_client(struct nfs4_client *clp)
494} 617}
495 618
496static struct nfsd4_client_tracking_ops nfsd4_legacy_tracking_ops = { 619static struct nfsd4_client_tracking_ops nfsd4_legacy_tracking_ops = {
497 .init = nfsd4_load_reboot_recovery_data, 620 .init = nfsd4_legacy_tracking_init,
498 .exit = nfsd4_legacy_tracking_exit, 621 .exit = nfsd4_legacy_tracking_exit,
499 .create = nfsd4_create_clid_dir, 622 .create = nfsd4_create_clid_dir,
500 .remove = nfsd4_remove_clid_dir, 623 .remove = nfsd4_remove_clid_dir,
@@ -785,8 +908,7 @@ nfsd4_cld_create(struct nfs4_client *clp)
785{ 908{
786 int ret; 909 int ret;
787 struct cld_upcall *cup; 910 struct cld_upcall *cup;
788 /* FIXME: determine net from clp */ 911 struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
789 struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
790 struct cld_net *cn = nn->cld_net; 912 struct cld_net *cn = nn->cld_net;
791 913
792 /* Don't upcall if it's already stored */ 914 /* Don't upcall if it's already stored */
@@ -823,8 +945,7 @@ nfsd4_cld_remove(struct nfs4_client *clp)
823{ 945{
824 int ret; 946 int ret;
825 struct cld_upcall *cup; 947 struct cld_upcall *cup;
826 /* FIXME: determine net from clp */ 948 struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
827 struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
828 struct cld_net *cn = nn->cld_net; 949 struct cld_net *cn = nn->cld_net;
829 950
830 /* Don't upcall if it's already removed */ 951 /* Don't upcall if it's already removed */
@@ -861,8 +982,7 @@ nfsd4_cld_check(struct nfs4_client *clp)
861{ 982{
862 int ret; 983 int ret;
863 struct cld_upcall *cup; 984 struct cld_upcall *cup;
864 /* FIXME: determine net from clp */ 985 struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
865 struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
866 struct cld_net *cn = nn->cld_net; 986 struct cld_net *cn = nn->cld_net;
867 987
868 /* Don't upcall if one was already stored during this grace pd */ 988 /* Don't upcall if one was already stored during this grace pd */
@@ -892,11 +1012,10 @@ nfsd4_cld_check(struct nfs4_client *clp)
892} 1012}
893 1013
894static void 1014static void
895nfsd4_cld_grace_done(struct net *net, time_t boot_time) 1015nfsd4_cld_grace_done(struct nfsd_net *nn, time_t boot_time)
896{ 1016{
897 int ret; 1017 int ret;
898 struct cld_upcall *cup; 1018 struct cld_upcall *cup;
899 struct nfsd_net *nn = net_generic(net, nfsd_net_id);
900 struct cld_net *cn = nn->cld_net; 1019 struct cld_net *cn = nn->cld_net;
901 1020
902 cup = alloc_cld_upcall(cn); 1021 cup = alloc_cld_upcall(cn);
@@ -926,28 +1045,261 @@ static struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops = {
926 .grace_done = nfsd4_cld_grace_done, 1045 .grace_done = nfsd4_cld_grace_done,
927}; 1046};
928 1047
1048/* upcall via usermodehelper */
1049static char cltrack_prog[PATH_MAX] = "/sbin/nfsdcltrack";
1050module_param_string(cltrack_prog, cltrack_prog, sizeof(cltrack_prog),
1051 S_IRUGO|S_IWUSR);
1052MODULE_PARM_DESC(cltrack_prog, "Path to the nfsdcltrack upcall program");
1053
1054static bool cltrack_legacy_disable;
1055module_param(cltrack_legacy_disable, bool, S_IRUGO|S_IWUSR);
1056MODULE_PARM_DESC(cltrack_legacy_disable,
1057 "Disable legacy recoverydir conversion. Default: false");
1058
1059#define LEGACY_TOPDIR_ENV_PREFIX "NFSDCLTRACK_LEGACY_TOPDIR="
1060#define LEGACY_RECDIR_ENV_PREFIX "NFSDCLTRACK_LEGACY_RECDIR="
1061
1062static char *
1063nfsd4_cltrack_legacy_topdir(void)
1064{
1065 int copied;
1066 size_t len;
1067 char *result;
1068
1069 if (cltrack_legacy_disable)
1070 return NULL;
1071
1072 len = strlen(LEGACY_TOPDIR_ENV_PREFIX) +
1073 strlen(nfs4_recoverydir()) + 1;
1074
1075 result = kmalloc(len, GFP_KERNEL);
1076 if (!result)
1077 return result;
1078
1079 copied = snprintf(result, len, LEGACY_TOPDIR_ENV_PREFIX "%s",
1080 nfs4_recoverydir());
1081 if (copied >= len) {
1082 /* just return nothing if output was truncated */
1083 kfree(result);
1084 return NULL;
1085 }
1086
1087 return result;
1088}
1089
1090static char *
1091nfsd4_cltrack_legacy_recdir(const struct xdr_netobj *name)
1092{
1093 int copied;
1094 size_t len;
1095 char *result;
1096
1097 if (cltrack_legacy_disable)
1098 return NULL;
1099
1100 /* +1 is for '/' between "topdir" and "recdir" */
1101 len = strlen(LEGACY_RECDIR_ENV_PREFIX) +
1102 strlen(nfs4_recoverydir()) + 1 + HEXDIR_LEN;
1103
1104 result = kmalloc(len, GFP_KERNEL);
1105 if (!result)
1106 return result;
1107
1108 copied = snprintf(result, len, LEGACY_RECDIR_ENV_PREFIX "%s/",
1109 nfs4_recoverydir());
1110 if (copied > (len - HEXDIR_LEN)) {
1111 /* just return nothing if output will be truncated */
1112 kfree(result);
1113 return NULL;
1114 }
1115
1116 copied = nfs4_make_rec_clidname(result + copied, name);
1117 if (copied) {
1118 kfree(result);
1119 return NULL;
1120 }
1121
1122 return result;
1123}
1124
1125static int
1126nfsd4_umh_cltrack_upcall(char *cmd, char *arg, char *legacy)
1127{
1128 char *envp[2];
1129 char *argv[4];
1130 int ret;
1131
1132 if (unlikely(!cltrack_prog[0])) {
1133 dprintk("%s: cltrack_prog is disabled\n", __func__);
1134 return -EACCES;
1135 }
1136
1137 dprintk("%s: cmd: %s\n", __func__, cmd);
1138 dprintk("%s: arg: %s\n", __func__, arg ? arg : "(null)");
1139 dprintk("%s: legacy: %s\n", __func__, legacy ? legacy : "(null)");
1140
1141 envp[0] = legacy;
1142 envp[1] = NULL;
1143
1144 argv[0] = (char *)cltrack_prog;
1145 argv[1] = cmd;
1146 argv[2] = arg;
1147 argv[3] = NULL;
1148
1149 ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
1150 /*
1151 * Disable the upcall mechanism if we're getting an ENOENT or EACCES
1152 * error. The admin can re-enable it on the fly by using sysfs
1153 * once the problem has been fixed.
1154 */
1155 if (ret == -ENOENT || ret == -EACCES) {
1156 dprintk("NFSD: %s was not found or isn't executable (%d). "
1157 "Setting cltrack_prog to blank string!",
1158 cltrack_prog, ret);
1159 cltrack_prog[0] = '\0';
1160 }
1161 dprintk("%s: %s return value: %d\n", __func__, cltrack_prog, ret);
1162
1163 return ret;
1164}
1165
1166static char *
1167bin_to_hex_dup(const unsigned char *src, int srclen)
1168{
1169 int i;
1170 char *buf, *hex;
1171
1172 /* +1 for terminating NULL */
1173 buf = kmalloc((srclen * 2) + 1, GFP_KERNEL);
1174 if (!buf)
1175 return buf;
1176
1177 hex = buf;
1178 for (i = 0; i < srclen; i++) {
1179 sprintf(hex, "%2.2x", *src++);
1180 hex += 2;
1181 }
1182 return buf;
1183}
1184
1185static int
1186nfsd4_umh_cltrack_init(struct net __attribute__((unused)) *net)
1187{
1188 return nfsd4_umh_cltrack_upcall("init", NULL, NULL);
1189}
1190
1191static void
1192nfsd4_umh_cltrack_create(struct nfs4_client *clp)
1193{
1194 char *hexid;
1195
1196 hexid = bin_to_hex_dup(clp->cl_name.data, clp->cl_name.len);
1197 if (!hexid) {
1198 dprintk("%s: can't allocate memory for upcall!\n", __func__);
1199 return;
1200 }
1201 nfsd4_umh_cltrack_upcall("create", hexid, NULL);
1202 kfree(hexid);
1203}
1204
1205static void
1206nfsd4_umh_cltrack_remove(struct nfs4_client *clp)
1207{
1208 char *hexid;
1209
1210 hexid = bin_to_hex_dup(clp->cl_name.data, clp->cl_name.len);
1211 if (!hexid) {
1212 dprintk("%s: can't allocate memory for upcall!\n", __func__);
1213 return;
1214 }
1215 nfsd4_umh_cltrack_upcall("remove", hexid, NULL);
1216 kfree(hexid);
1217}
1218
1219static int
1220nfsd4_umh_cltrack_check(struct nfs4_client *clp)
1221{
1222 int ret;
1223 char *hexid, *legacy;
1224
1225 hexid = bin_to_hex_dup(clp->cl_name.data, clp->cl_name.len);
1226 if (!hexid) {
1227 dprintk("%s: can't allocate memory for upcall!\n", __func__);
1228 return -ENOMEM;
1229 }
1230 legacy = nfsd4_cltrack_legacy_recdir(&clp->cl_name);
1231 ret = nfsd4_umh_cltrack_upcall("check", hexid, legacy);
1232 kfree(legacy);
1233 kfree(hexid);
1234 return ret;
1235}
1236
1237static void
1238nfsd4_umh_cltrack_grace_done(struct nfsd_net __attribute__((unused)) *nn,
1239 time_t boot_time)
1240{
1241 char *legacy;
1242 char timestr[22]; /* FIXME: better way to determine max size? */
1243
1244 sprintf(timestr, "%ld", boot_time);
1245 legacy = nfsd4_cltrack_legacy_topdir();
1246 nfsd4_umh_cltrack_upcall("gracedone", timestr, legacy);
1247 kfree(legacy);
1248}
1249
1250static struct nfsd4_client_tracking_ops nfsd4_umh_tracking_ops = {
1251 .init = nfsd4_umh_cltrack_init,
1252 .exit = NULL,
1253 .create = nfsd4_umh_cltrack_create,
1254 .remove = nfsd4_umh_cltrack_remove,
1255 .check = nfsd4_umh_cltrack_check,
1256 .grace_done = nfsd4_umh_cltrack_grace_done,
1257};
1258
929int 1259int
930nfsd4_client_tracking_init(struct net *net) 1260nfsd4_client_tracking_init(struct net *net)
931{ 1261{
932 int status; 1262 int status;
933 struct path path; 1263 struct path path;
1264 struct nfsd_net *nn = net_generic(net, nfsd_net_id);
934 1265
935 if (!client_tracking_ops) { 1266 /* just run the init if it the method is already decided */
936 client_tracking_ops = &nfsd4_cld_tracking_ops; 1267 if (nn->client_tracking_ops)
937 status = kern_path(nfs4_recoverydir(), LOOKUP_FOLLOW, &path); 1268 goto do_init;
938 if (!status) { 1269
939 if (S_ISDIR(path.dentry->d_inode->i_mode)) 1270 /*
940 client_tracking_ops = 1271 * First, try a UMH upcall. It should succeed or fail quickly, so
941 &nfsd4_legacy_tracking_ops; 1272 * there's little harm in trying that first.
942 path_put(&path); 1273 */
943 } 1274 nn->client_tracking_ops = &nfsd4_umh_tracking_ops;
1275 status = nn->client_tracking_ops->init(net);
1276 if (!status)
1277 return status;
1278
1279 /*
1280 * See if the recoverydir exists and is a directory. If it is,
1281 * then use the legacy ops.
1282 */
1283 nn->client_tracking_ops = &nfsd4_legacy_tracking_ops;
1284 status = kern_path(nfs4_recoverydir(), LOOKUP_FOLLOW, &path);
1285 if (!status) {
1286 status = S_ISDIR(path.dentry->d_inode->i_mode);
1287 path_put(&path);
1288 if (status)
1289 goto do_init;
944 } 1290 }
945 1291
946 status = client_tracking_ops->init(net); 1292 /* Finally, try to use nfsdcld */
1293 nn->client_tracking_ops = &nfsd4_cld_tracking_ops;
1294 printk(KERN_WARNING "NFSD: the nfsdcld client tracking upcall will be "
1295 "removed in 3.10. Please transition to using "
1296 "nfsdcltrack.\n");
1297do_init:
1298 status = nn->client_tracking_ops->init(net);
947 if (status) { 1299 if (status) {
948 printk(KERN_WARNING "NFSD: Unable to initialize client " 1300 printk(KERN_WARNING "NFSD: Unable to initialize client "
949 "recovery tracking! (%d)\n", status); 1301 "recovery tracking! (%d)\n", status);
950 client_tracking_ops = NULL; 1302 nn->client_tracking_ops = NULL;
951 } 1303 }
952 return status; 1304 return status;
953} 1305}
@@ -955,40 +1307,49 @@ nfsd4_client_tracking_init(struct net *net)
955void 1307void
956nfsd4_client_tracking_exit(struct net *net) 1308nfsd4_client_tracking_exit(struct net *net)
957{ 1309{
958 if (client_tracking_ops) { 1310 struct nfsd_net *nn = net_generic(net, nfsd_net_id);
959 client_tracking_ops->exit(net); 1311
960 client_tracking_ops = NULL; 1312 if (nn->client_tracking_ops) {
1313 if (nn->client_tracking_ops->exit)
1314 nn->client_tracking_ops->exit(net);
1315 nn->client_tracking_ops = NULL;
961 } 1316 }
962} 1317}
963 1318
964void 1319void
965nfsd4_client_record_create(struct nfs4_client *clp) 1320nfsd4_client_record_create(struct nfs4_client *clp)
966{ 1321{
967 if (client_tracking_ops) 1322 struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
968 client_tracking_ops->create(clp); 1323
1324 if (nn->client_tracking_ops)
1325 nn->client_tracking_ops->create(clp);
969} 1326}
970 1327
971void 1328void
972nfsd4_client_record_remove(struct nfs4_client *clp) 1329nfsd4_client_record_remove(struct nfs4_client *clp)
973{ 1330{
974 if (client_tracking_ops) 1331 struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
975 client_tracking_ops->remove(clp); 1332
1333 if (nn->client_tracking_ops)
1334 nn->client_tracking_ops->remove(clp);
976} 1335}
977 1336
978int 1337int
979nfsd4_client_record_check(struct nfs4_client *clp) 1338nfsd4_client_record_check(struct nfs4_client *clp)
980{ 1339{
981 if (client_tracking_ops) 1340 struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
982 return client_tracking_ops->check(clp); 1341
1342 if (nn->client_tracking_ops)
1343 return nn->client_tracking_ops->check(clp);
983 1344
984 return -EOPNOTSUPP; 1345 return -EOPNOTSUPP;
985} 1346}
986 1347
987void 1348void
988nfsd4_record_grace_done(struct net *net, time_t boot_time) 1349nfsd4_record_grace_done(struct nfsd_net *nn, time_t boot_time)
989{ 1350{
990 if (client_tracking_ops) 1351 if (nn->client_tracking_ops)
991 client_tracking_ops->grace_done(net, boot_time); 1352 nn->client_tracking_ops->grace_done(nn, boot_time);
992} 1353}
993 1354
994static int 1355static int