diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ext4/dir.c | 214 | ||||
-rw-r--r-- | fs/ext4/ext4.h | 6 | ||||
-rw-r--r-- | fs/ext4/hash.c | 4 | ||||
-rw-r--r-- | fs/lockd/svc.c | 2 | ||||
-rw-r--r-- | fs/nfsd/current_stateid.h | 28 | ||||
-rw-r--r-- | fs/nfsd/export.c | 2 | ||||
-rw-r--r-- | fs/nfsd/netns.h | 34 | ||||
-rw-r--r-- | fs/nfsd/nfs4callback.c | 19 | ||||
-rw-r--r-- | fs/nfsd/nfs4idmap.c | 53 | ||||
-rw-r--r-- | fs/nfsd/nfs4proc.c | 118 | ||||
-rw-r--r-- | fs/nfsd/nfs4recover.c | 647 | ||||
-rw-r--r-- | fs/nfsd/nfs4state.c | 365 | ||||
-rw-r--r-- | fs/nfsd/nfs4xdr.c | 132 | ||||
-rw-r--r-- | fs/nfsd/nfsctl.c | 22 | ||||
-rw-r--r-- | fs/nfsd/nfsd.h | 7 | ||||
-rw-r--r-- | fs/nfsd/nfssvc.c | 44 | ||||
-rw-r--r-- | fs/nfsd/state.h | 47 | ||||
-rw-r--r-- | fs/nfsd/vfs.c | 33 | ||||
-rw-r--r-- | fs/nfsd/vfs.h | 2 | ||||
-rw-r--r-- | fs/nfsd/xdr4.h | 34 |
20 files changed, 1488 insertions, 325 deletions
diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index ad56866d729a..b86786202643 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c | |||
@@ -32,24 +32,8 @@ static unsigned char ext4_filetype_table[] = { | |||
32 | DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK | 32 | DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK |
33 | }; | 33 | }; |
34 | 34 | ||
35 | static int ext4_readdir(struct file *, void *, filldir_t); | ||
36 | static int ext4_dx_readdir(struct file *filp, | 35 | static int ext4_dx_readdir(struct file *filp, |
37 | void *dirent, filldir_t filldir); | 36 | void *dirent, filldir_t filldir); |
38 | static int ext4_release_dir(struct inode *inode, | ||
39 | struct file *filp); | ||
40 | |||
41 | const struct file_operations ext4_dir_operations = { | ||
42 | .llseek = ext4_llseek, | ||
43 | .read = generic_read_dir, | ||
44 | .readdir = ext4_readdir, /* we take BKL. needed?*/ | ||
45 | .unlocked_ioctl = ext4_ioctl, | ||
46 | #ifdef CONFIG_COMPAT | ||
47 | .compat_ioctl = ext4_compat_ioctl, | ||
48 | #endif | ||
49 | .fsync = ext4_sync_file, | ||
50 | .release = ext4_release_dir, | ||
51 | }; | ||
52 | |||
53 | 37 | ||
54 | static unsigned char get_dtype(struct super_block *sb, int filetype) | 38 | static unsigned char get_dtype(struct super_block *sb, int filetype) |
55 | { | 39 | { |
@@ -60,6 +44,26 @@ static unsigned char get_dtype(struct super_block *sb, int filetype) | |||
60 | return (ext4_filetype_table[filetype]); | 44 | return (ext4_filetype_table[filetype]); |
61 | } | 45 | } |
62 | 46 | ||
47 | /** | ||
48 | * Check if the given dir-inode refers to an htree-indexed directory | ||
49 | * (or a directory which chould potentially get coverted to use htree | ||
50 | * indexing). | ||
51 | * | ||
52 | * Return 1 if it is a dx dir, 0 if not | ||
53 | */ | ||
54 | static int is_dx_dir(struct inode *inode) | ||
55 | { | ||
56 | struct super_block *sb = inode->i_sb; | ||
57 | |||
58 | if (EXT4_HAS_COMPAT_FEATURE(inode->i_sb, | ||
59 | EXT4_FEATURE_COMPAT_DIR_INDEX) && | ||
60 | ((ext4_test_inode_flag(inode, EXT4_INODE_INDEX)) || | ||
61 | ((inode->i_size >> sb->s_blocksize_bits) == 1))) | ||
62 | return 1; | ||
63 | |||
64 | return 0; | ||
65 | } | ||
66 | |||
63 | /* | 67 | /* |
64 | * Return 0 if the directory entry is OK, and 1 if there is a problem | 68 | * Return 0 if the directory entry is OK, and 1 if there is a problem |
65 | * | 69 | * |
@@ -115,18 +119,13 @@ static int ext4_readdir(struct file *filp, | |||
115 | unsigned int offset; | 119 | unsigned int offset; |
116 | int i, stored; | 120 | int i, stored; |
117 | struct ext4_dir_entry_2 *de; | 121 | struct ext4_dir_entry_2 *de; |
118 | struct super_block *sb; | ||
119 | int err; | 122 | int err; |
120 | struct inode *inode = filp->f_path.dentry->d_inode; | 123 | struct inode *inode = filp->f_path.dentry->d_inode; |
124 | struct super_block *sb = inode->i_sb; | ||
121 | int ret = 0; | 125 | int ret = 0; |
122 | int dir_has_error = 0; | 126 | int dir_has_error = 0; |
123 | 127 | ||
124 | sb = inode->i_sb; | 128 | if (is_dx_dir(inode)) { |
125 | |||
126 | if (EXT4_HAS_COMPAT_FEATURE(inode->i_sb, | ||
127 | EXT4_FEATURE_COMPAT_DIR_INDEX) && | ||
128 | ((ext4_test_inode_flag(inode, EXT4_INODE_INDEX)) || | ||
129 | ((inode->i_size >> sb->s_blocksize_bits) == 1))) { | ||
130 | err = ext4_dx_readdir(filp, dirent, filldir); | 129 | err = ext4_dx_readdir(filp, dirent, filldir); |
131 | if (err != ERR_BAD_DX_DIR) { | 130 | if (err != ERR_BAD_DX_DIR) { |
132 | ret = err; | 131 | ret = err; |
@@ -254,22 +253,134 @@ out: | |||
254 | return ret; | 253 | return ret; |
255 | } | 254 | } |
256 | 255 | ||
256 | static inline int is_32bit_api(void) | ||
257 | { | ||
258 | #ifdef CONFIG_COMPAT | ||
259 | return is_compat_task(); | ||
260 | #else | ||
261 | return (BITS_PER_LONG == 32); | ||
262 | #endif | ||
263 | } | ||
264 | |||
257 | /* | 265 | /* |
258 | * These functions convert from the major/minor hash to an f_pos | 266 | * These functions convert from the major/minor hash to an f_pos |
259 | * value. | 267 | * value for dx directories |
268 | * | ||
269 | * Upper layer (for example NFS) should specify FMODE_32BITHASH or | ||
270 | * FMODE_64BITHASH explicitly. On the other hand, we allow ext4 to be mounted | ||
271 | * directly on both 32-bit and 64-bit nodes, under such case, neither | ||
272 | * FMODE_32BITHASH nor FMODE_64BITHASH is specified. | ||
273 | */ | ||
274 | static inline loff_t hash2pos(struct file *filp, __u32 major, __u32 minor) | ||
275 | { | ||
276 | if ((filp->f_mode & FMODE_32BITHASH) || | ||
277 | (!(filp->f_mode & FMODE_64BITHASH) && is_32bit_api())) | ||
278 | return major >> 1; | ||
279 | else | ||
280 | return ((__u64)(major >> 1) << 32) | (__u64)minor; | ||
281 | } | ||
282 | |||
283 | static inline __u32 pos2maj_hash(struct file *filp, loff_t pos) | ||
284 | { | ||
285 | if ((filp->f_mode & FMODE_32BITHASH) || | ||
286 | (!(filp->f_mode & FMODE_64BITHASH) && is_32bit_api())) | ||
287 | return (pos << 1) & 0xffffffff; | ||
288 | else | ||
289 | return ((pos >> 32) << 1) & 0xffffffff; | ||
290 | } | ||
291 | |||
292 | static inline __u32 pos2min_hash(struct file *filp, loff_t pos) | ||
293 | { | ||
294 | if ((filp->f_mode & FMODE_32BITHASH) || | ||
295 | (!(filp->f_mode & FMODE_64BITHASH) && is_32bit_api())) | ||
296 | return 0; | ||
297 | else | ||
298 | return pos & 0xffffffff; | ||
299 | } | ||
300 | |||
301 | /* | ||
302 | * Return 32- or 64-bit end-of-file for dx directories | ||
303 | */ | ||
304 | static inline loff_t ext4_get_htree_eof(struct file *filp) | ||
305 | { | ||
306 | if ((filp->f_mode & FMODE_32BITHASH) || | ||
307 | (!(filp->f_mode & FMODE_64BITHASH) && is_32bit_api())) | ||
308 | return EXT4_HTREE_EOF_32BIT; | ||
309 | else | ||
310 | return EXT4_HTREE_EOF_64BIT; | ||
311 | } | ||
312 | |||
313 | |||
314 | /* | ||
315 | * ext4_dir_llseek() based on generic_file_llseek() to handle both | ||
316 | * non-htree and htree directories, where the "offset" is in terms | ||
317 | * of the filename hash value instead of the byte offset. | ||
260 | * | 318 | * |
261 | * Currently we only use major hash numer. This is unfortunate, but | 319 | * NOTE: offsets obtained *before* ext4_set_inode_flag(dir, EXT4_INODE_INDEX) |
262 | * on 32-bit machines, the same VFS interface is used for lseek and | 320 | * will be invalid once the directory was converted into a dx directory |
263 | * llseek, so if we use the 64 bit offset, then the 32-bit versions of | ||
264 | * lseek/telldir/seekdir will blow out spectacularly, and from within | ||
265 | * the ext2 low-level routine, we don't know if we're being called by | ||
266 | * a 64-bit version of the system call or the 32-bit version of the | ||
267 | * system call. Worse yet, NFSv2 only allows for a 32-bit readdir | ||
268 | * cookie. Sigh. | ||
269 | */ | 321 | */ |
270 | #define hash2pos(major, minor) (major >> 1) | 322 | loff_t ext4_dir_llseek(struct file *file, loff_t offset, int origin) |
271 | #define pos2maj_hash(pos) ((pos << 1) & 0xffffffff) | 323 | { |
272 | #define pos2min_hash(pos) (0) | 324 | struct inode *inode = file->f_mapping->host; |
325 | loff_t ret = -EINVAL; | ||
326 | int dx_dir = is_dx_dir(inode); | ||
327 | |||
328 | mutex_lock(&inode->i_mutex); | ||
329 | |||
330 | /* NOTE: relative offsets with dx directories might not work | ||
331 | * as expected, as it is difficult to figure out the | ||
332 | * correct offset between dx hashes */ | ||
333 | |||
334 | switch (origin) { | ||
335 | case SEEK_END: | ||
336 | if (unlikely(offset > 0)) | ||
337 | goto out_err; /* not supported for directories */ | ||
338 | |||
339 | /* so only negative offsets are left, does that have a | ||
340 | * meaning for directories at all? */ | ||
341 | if (dx_dir) | ||
342 | offset += ext4_get_htree_eof(file); | ||
343 | else | ||
344 | offset += inode->i_size; | ||
345 | break; | ||
346 | case SEEK_CUR: | ||
347 | /* | ||
348 | * Here we special-case the lseek(fd, 0, SEEK_CUR) | ||
349 | * position-querying operation. Avoid rewriting the "same" | ||
350 | * f_pos value back to the file because a concurrent read(), | ||
351 | * write() or lseek() might have altered it | ||
352 | */ | ||
353 | if (offset == 0) { | ||
354 | offset = file->f_pos; | ||
355 | goto out_ok; | ||
356 | } | ||
357 | |||
358 | offset += file->f_pos; | ||
359 | break; | ||
360 | } | ||
361 | |||
362 | if (unlikely(offset < 0)) | ||
363 | goto out_err; | ||
364 | |||
365 | if (!dx_dir) { | ||
366 | if (offset > inode->i_sb->s_maxbytes) | ||
367 | goto out_err; | ||
368 | } else if (offset > ext4_get_htree_eof(file)) | ||
369 | goto out_err; | ||
370 | |||
371 | /* Special lock needed here? */ | ||
372 | if (offset != file->f_pos) { | ||
373 | file->f_pos = offset; | ||
374 | file->f_version = 0; | ||
375 | } | ||
376 | |||
377 | out_ok: | ||
378 | ret = offset; | ||
379 | out_err: | ||
380 | mutex_unlock(&inode->i_mutex); | ||
381 | |||
382 | return ret; | ||
383 | } | ||
273 | 384 | ||
274 | /* | 385 | /* |
275 | * This structure holds the nodes of the red-black tree used to store | 386 | * This structure holds the nodes of the red-black tree used to store |
@@ -330,15 +441,16 @@ static void free_rb_tree_fname(struct rb_root *root) | |||
330 | } | 441 | } |
331 | 442 | ||
332 | 443 | ||
333 | static struct dir_private_info *ext4_htree_create_dir_info(loff_t pos) | 444 | static struct dir_private_info *ext4_htree_create_dir_info(struct file *filp, |
445 | loff_t pos) | ||
334 | { | 446 | { |
335 | struct dir_private_info *p; | 447 | struct dir_private_info *p; |
336 | 448 | ||
337 | p = kzalloc(sizeof(struct dir_private_info), GFP_KERNEL); | 449 | p = kzalloc(sizeof(struct dir_private_info), GFP_KERNEL); |
338 | if (!p) | 450 | if (!p) |
339 | return NULL; | 451 | return NULL; |
340 | p->curr_hash = pos2maj_hash(pos); | 452 | p->curr_hash = pos2maj_hash(filp, pos); |
341 | p->curr_minor_hash = pos2min_hash(pos); | 453 | p->curr_minor_hash = pos2min_hash(filp, pos); |
342 | return p; | 454 | return p; |
343 | } | 455 | } |
344 | 456 | ||
@@ -430,7 +542,7 @@ static int call_filldir(struct file *filp, void *dirent, | |||
430 | inode->i_ino, current->comm); | 542 | inode->i_ino, current->comm); |
431 | return 0; | 543 | return 0; |
432 | } | 544 | } |
433 | curr_pos = hash2pos(fname->hash, fname->minor_hash); | 545 | curr_pos = hash2pos(filp, fname->hash, fname->minor_hash); |
434 | while (fname) { | 546 | while (fname) { |
435 | error = filldir(dirent, fname->name, | 547 | error = filldir(dirent, fname->name, |
436 | fname->name_len, curr_pos, | 548 | fname->name_len, curr_pos, |
@@ -455,13 +567,13 @@ static int ext4_dx_readdir(struct file *filp, | |||
455 | int ret; | 567 | int ret; |
456 | 568 | ||
457 | if (!info) { | 569 | if (!info) { |
458 | info = ext4_htree_create_dir_info(filp->f_pos); | 570 | info = ext4_htree_create_dir_info(filp, filp->f_pos); |
459 | if (!info) | 571 | if (!info) |
460 | return -ENOMEM; | 572 | return -ENOMEM; |
461 | filp->private_data = info; | 573 | filp->private_data = info; |
462 | } | 574 | } |
463 | 575 | ||
464 | if (filp->f_pos == EXT4_HTREE_EOF) | 576 | if (filp->f_pos == ext4_get_htree_eof(filp)) |
465 | return 0; /* EOF */ | 577 | return 0; /* EOF */ |
466 | 578 | ||
467 | /* Some one has messed with f_pos; reset the world */ | 579 | /* Some one has messed with f_pos; reset the world */ |
@@ -469,8 +581,8 @@ static int ext4_dx_readdir(struct file *filp, | |||
469 | free_rb_tree_fname(&info->root); | 581 | free_rb_tree_fname(&info->root); |
470 | info->curr_node = NULL; | 582 | info->curr_node = NULL; |
471 | info->extra_fname = NULL; | 583 | info->extra_fname = NULL; |
472 | info->curr_hash = pos2maj_hash(filp->f_pos); | 584 | info->curr_hash = pos2maj_hash(filp, filp->f_pos); |
473 | info->curr_minor_hash = pos2min_hash(filp->f_pos); | 585 | info->curr_minor_hash = pos2min_hash(filp, filp->f_pos); |
474 | } | 586 | } |
475 | 587 | ||
476 | /* | 588 | /* |
@@ -502,7 +614,7 @@ static int ext4_dx_readdir(struct file *filp, | |||
502 | if (ret < 0) | 614 | if (ret < 0) |
503 | return ret; | 615 | return ret; |
504 | if (ret == 0) { | 616 | if (ret == 0) { |
505 | filp->f_pos = EXT4_HTREE_EOF; | 617 | filp->f_pos = ext4_get_htree_eof(filp); |
506 | break; | 618 | break; |
507 | } | 619 | } |
508 | info->curr_node = rb_first(&info->root); | 620 | info->curr_node = rb_first(&info->root); |
@@ -522,7 +634,7 @@ static int ext4_dx_readdir(struct file *filp, | |||
522 | info->curr_minor_hash = fname->minor_hash; | 634 | info->curr_minor_hash = fname->minor_hash; |
523 | } else { | 635 | } else { |
524 | if (info->next_hash == ~0) { | 636 | if (info->next_hash == ~0) { |
525 | filp->f_pos = EXT4_HTREE_EOF; | 637 | filp->f_pos = ext4_get_htree_eof(filp); |
526 | break; | 638 | break; |
527 | } | 639 | } |
528 | info->curr_hash = info->next_hash; | 640 | info->curr_hash = info->next_hash; |
@@ -541,3 +653,15 @@ static int ext4_release_dir(struct inode *inode, struct file *filp) | |||
541 | 653 | ||
542 | return 0; | 654 | return 0; |
543 | } | 655 | } |
656 | |||
657 | const struct file_operations ext4_dir_operations = { | ||
658 | .llseek = ext4_dir_llseek, | ||
659 | .read = generic_read_dir, | ||
660 | .readdir = ext4_readdir, | ||
661 | .unlocked_ioctl = ext4_ioctl, | ||
662 | #ifdef CONFIG_COMPAT | ||
663 | .compat_ioctl = ext4_compat_ioctl, | ||
664 | #endif | ||
665 | .fsync = ext4_sync_file, | ||
666 | .release = ext4_release_dir, | ||
667 | }; | ||
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index ded731ac8a32..ab2594a30f86 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -1623,7 +1623,11 @@ struct dx_hash_info | |||
1623 | u32 *seed; | 1623 | u32 *seed; |
1624 | }; | 1624 | }; |
1625 | 1625 | ||
1626 | #define EXT4_HTREE_EOF 0x7fffffff | 1626 | |
1627 | /* 32 and 64 bit signed EOF for dx directories */ | ||
1628 | #define EXT4_HTREE_EOF_32BIT ((1UL << (32 - 1)) - 1) | ||
1629 | #define EXT4_HTREE_EOF_64BIT ((1ULL << (64 - 1)) - 1) | ||
1630 | |||
1627 | 1631 | ||
1628 | /* | 1632 | /* |
1629 | * Control parameters used by ext4_htree_next_block | 1633 | * Control parameters used by ext4_htree_next_block |
diff --git a/fs/ext4/hash.c b/fs/ext4/hash.c index ac8f168c8ab4..fa8e4911d354 100644 --- a/fs/ext4/hash.c +++ b/fs/ext4/hash.c | |||
@@ -200,8 +200,8 @@ int ext4fs_dirhash(const char *name, int len, struct dx_hash_info *hinfo) | |||
200 | return -1; | 200 | return -1; |
201 | } | 201 | } |
202 | hash = hash & ~1; | 202 | hash = hash & ~1; |
203 | if (hash == (EXT4_HTREE_EOF << 1)) | 203 | if (hash == (EXT4_HTREE_EOF_32BIT << 1)) |
204 | hash = (EXT4_HTREE_EOF-1) << 1; | 204 | hash = (EXT4_HTREE_EOF_32BIT - 1) << 1; |
205 | hinfo->hash = hash; | 205 | hinfo->hash = hash; |
206 | hinfo->minor_hash = minor_hash; | 206 | hinfo->minor_hash = minor_hash; |
207 | return 0; | 207 | return 0; |
diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index 2774e1013b34..f49b9afc4436 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c | |||
@@ -496,7 +496,7 @@ static int param_set_##name(const char *val, struct kernel_param *kp) \ | |||
496 | __typeof__(type) num = which_strtol(val, &endp, 0); \ | 496 | __typeof__(type) num = which_strtol(val, &endp, 0); \ |
497 | if (endp == val || *endp || num < (min) || num > (max)) \ | 497 | if (endp == val || *endp || num < (min) || num > (max)) \ |
498 | return -EINVAL; \ | 498 | return -EINVAL; \ |
499 | *((int *) kp->arg) = num; \ | 499 | *((type *) kp->arg) = num; \ |
500 | return 0; \ | 500 | return 0; \ |
501 | } | 501 | } |
502 | 502 | ||
diff --git a/fs/nfsd/current_stateid.h b/fs/nfsd/current_stateid.h new file mode 100644 index 000000000000..4123551208d8 --- /dev/null +++ b/fs/nfsd/current_stateid.h | |||
@@ -0,0 +1,28 @@ | |||
1 | #ifndef _NFSD4_CURRENT_STATE_H | ||
2 | #define _NFSD4_CURRENT_STATE_H | ||
3 | |||
4 | #include "state.h" | ||
5 | #include "xdr4.h" | ||
6 | |||
7 | extern void clear_current_stateid(struct nfsd4_compound_state *cstate); | ||
8 | /* | ||
9 | * functions to set current state id | ||
10 | */ | ||
11 | extern void nfsd4_set_opendowngradestateid(struct nfsd4_compound_state *cstate, struct nfsd4_open_downgrade *); | ||
12 | extern void nfsd4_set_openstateid(struct nfsd4_compound_state *, struct nfsd4_open *); | ||
13 | extern void nfsd4_set_lockstateid(struct nfsd4_compound_state *, struct nfsd4_lock *); | ||
14 | extern void nfsd4_set_closestateid(struct nfsd4_compound_state *, struct nfsd4_close *); | ||
15 | |||
16 | /* | ||
17 | * functions to consume current state id | ||
18 | */ | ||
19 | extern void nfsd4_get_opendowngradestateid(struct nfsd4_compound_state *cstate, struct nfsd4_open_downgrade *); | ||
20 | extern void nfsd4_get_delegreturnstateid(struct nfsd4_compound_state *, struct nfsd4_delegreturn *); | ||
21 | extern void nfsd4_get_freestateid(struct nfsd4_compound_state *, struct nfsd4_free_stateid *); | ||
22 | extern void nfsd4_get_setattrstateid(struct nfsd4_compound_state *, struct nfsd4_setattr *); | ||
23 | extern void nfsd4_get_closestateid(struct nfsd4_compound_state *, struct nfsd4_close *); | ||
24 | extern void nfsd4_get_lockustateid(struct nfsd4_compound_state *, struct nfsd4_locku *); | ||
25 | extern void nfsd4_get_readstateid(struct nfsd4_compound_state *, struct nfsd4_read *); | ||
26 | extern void nfsd4_get_writestateid(struct nfsd4_compound_state *, struct nfsd4_write *); | ||
27 | |||
28 | #endif /* _NFSD4_CURRENT_STATE_H */ | ||
diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index cf8a6bd062fa..8e9689abbc0c 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c | |||
@@ -87,7 +87,7 @@ static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen) | |||
87 | struct svc_expkey key; | 87 | struct svc_expkey key; |
88 | struct svc_expkey *ek = NULL; | 88 | struct svc_expkey *ek = NULL; |
89 | 89 | ||
90 | if (mlen < 1 || mesg[mlen-1] != '\n') | 90 | if (mesg[mlen - 1] != '\n') |
91 | return -EINVAL; | 91 | return -EINVAL; |
92 | mesg[mlen-1] = 0; | 92 | mesg[mlen-1] = 0; |
93 | 93 | ||
diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h new file mode 100644 index 000000000000..12e0cff435b4 --- /dev/null +++ b/fs/nfsd/netns.h | |||
@@ -0,0 +1,34 @@ | |||
1 | /* | ||
2 | * per net namespace data structures for nfsd | ||
3 | * | ||
4 | * Copyright (C) 2012, Jeff Layton <jlayton@redhat.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms of the GNU General Public License as published by the Free | ||
8 | * Software Foundation; either version 2 of the License, or (at your option) | ||
9 | * any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
14 | * more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along with | ||
17 | * this program; if not, write to the Free Software Foundation, Inc., 51 | ||
18 | * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
19 | */ | ||
20 | |||
21 | #ifndef __NFSD_NETNS_H__ | ||
22 | #define __NFSD_NETNS_H__ | ||
23 | |||
24 | #include <net/net_namespace.h> | ||
25 | #include <net/netns/generic.h> | ||
26 | |||
27 | struct cld_net; | ||
28 | |||
29 | struct nfsd_net { | ||
30 | struct cld_net *cld_net; | ||
31 | }; | ||
32 | |||
33 | extern int nfsd_net_id; | ||
34 | #endif /* __NFSD_NETNS_H__ */ | ||
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 0e262f32ac41..c8e9f637153a 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c | |||
@@ -645,7 +645,6 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c | |||
645 | .timeout = &timeparms, | 645 | .timeout = &timeparms, |
646 | .program = &cb_program, | 646 | .program = &cb_program, |
647 | .version = 0, | 647 | .version = 0, |
648 | .authflavor = clp->cl_flavor, | ||
649 | .flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET), | 648 | .flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET), |
650 | }; | 649 | }; |
651 | struct rpc_clnt *client; | 650 | struct rpc_clnt *client; |
@@ -656,6 +655,7 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c | |||
656 | args.client_name = clp->cl_principal; | 655 | args.client_name = clp->cl_principal; |
657 | args.prognumber = conn->cb_prog, | 656 | args.prognumber = conn->cb_prog, |
658 | args.protocol = XPRT_TRANSPORT_TCP; | 657 | args.protocol = XPRT_TRANSPORT_TCP; |
658 | args.authflavor = clp->cl_flavor; | ||
659 | clp->cl_cb_ident = conn->cb_ident; | 659 | clp->cl_cb_ident = conn->cb_ident; |
660 | } else { | 660 | } else { |
661 | if (!conn->cb_xprt) | 661 | if (!conn->cb_xprt) |
@@ -665,6 +665,7 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c | |||
665 | args.bc_xprt = conn->cb_xprt; | 665 | args.bc_xprt = conn->cb_xprt; |
666 | args.prognumber = clp->cl_cb_session->se_cb_prog; | 666 | args.prognumber = clp->cl_cb_session->se_cb_prog; |
667 | args.protocol = XPRT_TRANSPORT_BC_TCP; | 667 | args.protocol = XPRT_TRANSPORT_BC_TCP; |
668 | args.authflavor = RPC_AUTH_UNIX; | ||
668 | } | 669 | } |
669 | /* Create RPC client */ | 670 | /* Create RPC client */ |
670 | client = rpc_create(&args); | 671 | client = rpc_create(&args); |
@@ -754,9 +755,9 @@ static void do_probe_callback(struct nfs4_client *clp) | |||
754 | */ | 755 | */ |
755 | void nfsd4_probe_callback(struct nfs4_client *clp) | 756 | void nfsd4_probe_callback(struct nfs4_client *clp) |
756 | { | 757 | { |
757 | /* XXX: atomicity? Also, should we be using cl_cb_flags? */ | 758 | /* XXX: atomicity? Also, should we be using cl_flags? */ |
758 | clp->cl_cb_state = NFSD4_CB_UNKNOWN; | 759 | clp->cl_cb_state = NFSD4_CB_UNKNOWN; |
759 | set_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_cb_flags); | 760 | set_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_flags); |
760 | do_probe_callback(clp); | 761 | do_probe_callback(clp); |
761 | } | 762 | } |
762 | 763 | ||
@@ -915,7 +916,7 @@ void nfsd4_destroy_callback_queue(void) | |||
915 | /* must be called under the state lock */ | 916 | /* must be called under the state lock */ |
916 | void nfsd4_shutdown_callback(struct nfs4_client *clp) | 917 | void nfsd4_shutdown_callback(struct nfs4_client *clp) |
917 | { | 918 | { |
918 | set_bit(NFSD4_CLIENT_KILL, &clp->cl_cb_flags); | 919 | set_bit(NFSD4_CLIENT_CB_KILL, &clp->cl_flags); |
919 | /* | 920 | /* |
920 | * Note this won't actually result in a null callback; | 921 | * Note this won't actually result in a null callback; |
921 | * instead, nfsd4_do_callback_rpc() will detect the killed | 922 | * instead, nfsd4_do_callback_rpc() will detect the killed |
@@ -966,15 +967,15 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb) | |||
966 | svc_xprt_put(clp->cl_cb_conn.cb_xprt); | 967 | svc_xprt_put(clp->cl_cb_conn.cb_xprt); |
967 | clp->cl_cb_conn.cb_xprt = NULL; | 968 | clp->cl_cb_conn.cb_xprt = NULL; |
968 | } | 969 | } |
969 | if (test_bit(NFSD4_CLIENT_KILL, &clp->cl_cb_flags)) | 970 | if (test_bit(NFSD4_CLIENT_CB_KILL, &clp->cl_flags)) |
970 | return; | 971 | return; |
971 | spin_lock(&clp->cl_lock); | 972 | spin_lock(&clp->cl_lock); |
972 | /* | 973 | /* |
973 | * Only serialized callback code is allowed to clear these | 974 | * Only serialized callback code is allowed to clear these |
974 | * flags; main nfsd code can only set them: | 975 | * flags; main nfsd code can only set them: |
975 | */ | 976 | */ |
976 | BUG_ON(!clp->cl_cb_flags); | 977 | BUG_ON(!(clp->cl_flags & NFSD4_CLIENT_CB_FLAG_MASK)); |
977 | clear_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_cb_flags); | 978 | clear_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_flags); |
978 | memcpy(&conn, &cb->cb_clp->cl_cb_conn, sizeof(struct nfs4_cb_conn)); | 979 | memcpy(&conn, &cb->cb_clp->cl_cb_conn, sizeof(struct nfs4_cb_conn)); |
979 | c = __nfsd4_find_backchannel(clp); | 980 | c = __nfsd4_find_backchannel(clp); |
980 | if (c) { | 981 | if (c) { |
@@ -986,7 +987,7 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb) | |||
986 | 987 | ||
987 | err = setup_callback_client(clp, &conn, ses); | 988 | err = setup_callback_client(clp, &conn, ses); |
988 | if (err) { | 989 | if (err) { |
989 | warn_no_callback_path(clp, err); | 990 | nfsd4_mark_cb_down(clp, err); |
990 | return; | 991 | return; |
991 | } | 992 | } |
992 | /* Yay, the callback channel's back! Restart any callbacks: */ | 993 | /* Yay, the callback channel's back! Restart any callbacks: */ |
@@ -1000,7 +1001,7 @@ void nfsd4_do_callback_rpc(struct work_struct *w) | |||
1000 | struct nfs4_client *clp = cb->cb_clp; | 1001 | struct nfs4_client *clp = cb->cb_clp; |
1001 | struct rpc_clnt *clnt; | 1002 | struct rpc_clnt *clnt; |
1002 | 1003 | ||
1003 | if (clp->cl_cb_flags) | 1004 | if (clp->cl_flags & NFSD4_CLIENT_CB_FLAG_MASK) |
1004 | nfsd4_process_cb_update(cb); | 1005 | nfsd4_process_cb_update(cb); |
1005 | 1006 | ||
1006 | clnt = clp->cl_cb_client; | 1007 | clnt = clp->cl_cb_client; |
diff --git a/fs/nfsd/nfs4idmap.c b/fs/nfsd/nfs4idmap.c index 94096273cd6c..322d11ce06a4 100644 --- a/fs/nfsd/nfs4idmap.c +++ b/fs/nfsd/nfs4idmap.c | |||
@@ -41,6 +41,14 @@ | |||
41 | #include "nfsd.h" | 41 | #include "nfsd.h" |
42 | 42 | ||
43 | /* | 43 | /* |
44 | * Turn off idmapping when using AUTH_SYS. | ||
45 | */ | ||
46 | static bool nfs4_disable_idmapping = true; | ||
47 | module_param(nfs4_disable_idmapping, bool, 0644); | ||
48 | MODULE_PARM_DESC(nfs4_disable_idmapping, | ||
49 | "Turn off server's NFSv4 idmapping when using 'sec=sys'"); | ||
50 | |||
51 | /* | ||
44 | * Cache entry | 52 | * Cache entry |
45 | */ | 53 | */ |
46 | 54 | ||
@@ -561,28 +569,65 @@ idmap_id_to_name(struct svc_rqst *rqstp, int type, uid_t id, char *name) | |||
561 | return ret; | 569 | return ret; |
562 | } | 570 | } |
563 | 571 | ||
572 | static bool | ||
573 | numeric_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, uid_t *id) | ||
574 | { | ||
575 | int ret; | ||
576 | char buf[11]; | ||
577 | |||
578 | if (namelen + 1 > sizeof(buf)) | ||
579 | /* too long to represent a 32-bit id: */ | ||
580 | return false; | ||
581 | /* Just to make sure it's null-terminated: */ | ||
582 | memcpy(buf, name, namelen); | ||
583 | buf[namelen] = '\0'; | ||
584 | ret = kstrtouint(name, 10, id); | ||
585 | return ret == 0; | ||
586 | } | ||
587 | |||
588 | static __be32 | ||
589 | do_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, uid_t *id) | ||
590 | { | ||
591 | if (nfs4_disable_idmapping && rqstp->rq_flavor < RPC_AUTH_GSS) | ||
592 | if (numeric_name_to_id(rqstp, type, name, namelen, id)) | ||
593 | return 0; | ||
594 | /* | ||
595 | * otherwise, fall through and try idmapping, for | ||
596 | * backwards compatibility with clients sending names: | ||
597 | */ | ||
598 | return idmap_name_to_id(rqstp, type, name, namelen, id); | ||
599 | } | ||
600 | |||
601 | static int | ||
602 | do_id_to_name(struct svc_rqst *rqstp, int type, uid_t id, char *name) | ||
603 | { | ||
604 | if (nfs4_disable_idmapping && rqstp->rq_flavor < RPC_AUTH_GSS) | ||
605 | return sprintf(name, "%u", id); | ||
606 | return idmap_id_to_name(rqstp, type, id, name); | ||
607 | } | ||
608 | |||
564 | __be32 | 609 | __be32 |
565 | nfsd_map_name_to_uid(struct svc_rqst *rqstp, const char *name, size_t namelen, | 610 | nfsd_map_name_to_uid(struct svc_rqst *rqstp, const char *name, size_t namelen, |
566 | __u32 *id) | 611 | __u32 *id) |
567 | { | 612 | { |
568 | return idmap_name_to_id(rqstp, IDMAP_TYPE_USER, name, namelen, id); | 613 | return do_name_to_id(rqstp, IDMAP_TYPE_USER, name, namelen, id); |
569 | } | 614 | } |
570 | 615 | ||
571 | __be32 | 616 | __be32 |
572 | nfsd_map_name_to_gid(struct svc_rqst *rqstp, const char *name, size_t namelen, | 617 | nfsd_map_name_to_gid(struct svc_rqst *rqstp, const char *name, size_t namelen, |
573 | __u32 *id) | 618 | __u32 *id) |
574 | { | 619 | { |
575 | return idmap_name_to_id(rqstp, IDMAP_TYPE_GROUP, name, namelen, id); | 620 | return do_name_to_id(rqstp, IDMAP_TYPE_GROUP, name, namelen, id); |
576 | } | 621 | } |
577 | 622 | ||
578 | int | 623 | int |
579 | nfsd_map_uid_to_name(struct svc_rqst *rqstp, __u32 id, char *name) | 624 | nfsd_map_uid_to_name(struct svc_rqst *rqstp, __u32 id, char *name) |
580 | { | 625 | { |
581 | return idmap_id_to_name(rqstp, IDMAP_TYPE_USER, id, name); | 626 | return do_id_to_name(rqstp, IDMAP_TYPE_USER, id, name); |
582 | } | 627 | } |
583 | 628 | ||
584 | int | 629 | int |
585 | nfsd_map_gid_to_name(struct svc_rqst *rqstp, __u32 id, char *name) | 630 | nfsd_map_gid_to_name(struct svc_rqst *rqstp, __u32 id, char *name) |
586 | { | 631 | { |
587 | return idmap_id_to_name(rqstp, IDMAP_TYPE_GROUP, id, name); | 632 | return do_id_to_name(rqstp, IDMAP_TYPE_GROUP, id, name); |
588 | } | 633 | } |
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 896da74ec563..2ed14dfd00a2 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c | |||
@@ -39,6 +39,7 @@ | |||
39 | #include "cache.h" | 39 | #include "cache.h" |
40 | #include "xdr4.h" | 40 | #include "xdr4.h" |
41 | #include "vfs.h" | 41 | #include "vfs.h" |
42 | #include "current_stateid.h" | ||
42 | 43 | ||
43 | #define NFSDDBG_FACILITY NFSDDBG_PROC | 44 | #define NFSDDBG_FACILITY NFSDDBG_PROC |
44 | 45 | ||
@@ -192,10 +193,13 @@ static __be32 nfsd_check_obj_isreg(struct svc_fh *fh) | |||
192 | static __be32 | 193 | static __be32 |
193 | do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open) | 194 | do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open) |
194 | { | 195 | { |
195 | struct svc_fh resfh; | 196 | struct svc_fh *resfh; |
196 | __be32 status; | 197 | __be32 status; |
197 | 198 | ||
198 | fh_init(&resfh, NFS4_FHSIZE); | 199 | resfh = kmalloc(sizeof(struct svc_fh), GFP_KERNEL); |
200 | if (!resfh) | ||
201 | return nfserr_jukebox; | ||
202 | fh_init(resfh, NFS4_FHSIZE); | ||
199 | open->op_truncate = 0; | 203 | open->op_truncate = 0; |
200 | 204 | ||
201 | if (open->op_create) { | 205 | if (open->op_create) { |
@@ -220,7 +224,7 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o | |||
220 | */ | 224 | */ |
221 | status = do_nfsd_create(rqstp, current_fh, open->op_fname.data, | 225 | status = do_nfsd_create(rqstp, current_fh, open->op_fname.data, |
222 | open->op_fname.len, &open->op_iattr, | 226 | open->op_fname.len, &open->op_iattr, |
223 | &resfh, open->op_createmode, | 227 | resfh, open->op_createmode, |
224 | (u32 *)open->op_verf.data, | 228 | (u32 *)open->op_verf.data, |
225 | &open->op_truncate, &open->op_created); | 229 | &open->op_truncate, &open->op_created); |
226 | 230 | ||
@@ -234,30 +238,29 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o | |||
234 | FATTR4_WORD1_TIME_MODIFY); | 238 | FATTR4_WORD1_TIME_MODIFY); |
235 | } else { | 239 | } else { |
236 | status = nfsd_lookup(rqstp, current_fh, | 240 | status = nfsd_lookup(rqstp, current_fh, |
237 | open->op_fname.data, open->op_fname.len, &resfh); | 241 | open->op_fname.data, open->op_fname.len, resfh); |
238 | fh_unlock(current_fh); | 242 | fh_unlock(current_fh); |
239 | if (status) | 243 | if (status) |
240 | goto out; | 244 | goto out; |
241 | status = nfsd_check_obj_isreg(&resfh); | 245 | status = nfsd_check_obj_isreg(resfh); |
242 | } | 246 | } |
243 | if (status) | 247 | if (status) |
244 | goto out; | 248 | goto out; |
245 | 249 | ||
246 | if (is_create_with_attrs(open) && open->op_acl != NULL) | 250 | if (is_create_with_attrs(open) && open->op_acl != NULL) |
247 | do_set_nfs4_acl(rqstp, &resfh, open->op_acl, open->op_bmval); | 251 | do_set_nfs4_acl(rqstp, resfh, open->op_acl, open->op_bmval); |
248 | |||
249 | set_change_info(&open->op_cinfo, current_fh); | ||
250 | fh_dup2(current_fh, &resfh); | ||
251 | 252 | ||
252 | /* set reply cache */ | 253 | /* set reply cache */ |
253 | fh_copy_shallow(&open->op_openowner->oo_owner.so_replay.rp_openfh, | 254 | fh_copy_shallow(&open->op_openowner->oo_owner.so_replay.rp_openfh, |
254 | &resfh.fh_handle); | 255 | &resfh->fh_handle); |
255 | if (!open->op_created) | 256 | if (!open->op_created) |
256 | status = do_open_permission(rqstp, current_fh, open, | 257 | status = do_open_permission(rqstp, resfh, open, |
257 | NFSD_MAY_NOP); | 258 | NFSD_MAY_NOP); |
258 | 259 | set_change_info(&open->op_cinfo, current_fh); | |
260 | fh_dup2(current_fh, resfh); | ||
259 | out: | 261 | out: |
260 | fh_put(&resfh); | 262 | fh_put(resfh); |
263 | kfree(resfh); | ||
261 | return status; | 264 | return status; |
262 | } | 265 | } |
263 | 266 | ||
@@ -310,16 +313,14 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
310 | if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL) | 313 | if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL) |
311 | return nfserr_inval; | 314 | return nfserr_inval; |
312 | 315 | ||
313 | /* We don't yet support WANT bits: */ | ||
314 | open->op_share_access &= NFS4_SHARE_ACCESS_MASK; | ||
315 | |||
316 | open->op_created = 0; | 316 | open->op_created = 0; |
317 | /* | 317 | /* |
318 | * RFC5661 18.51.3 | 318 | * RFC5661 18.51.3 |
319 | * Before RECLAIM_COMPLETE done, server should deny new lock | 319 | * Before RECLAIM_COMPLETE done, server should deny new lock |
320 | */ | 320 | */ |
321 | if (nfsd4_has_session(cstate) && | 321 | if (nfsd4_has_session(cstate) && |
322 | !cstate->session->se_client->cl_firststate && | 322 | !test_bit(NFSD4_CLIENT_RECLAIM_COMPLETE, |
323 | &cstate->session->se_client->cl_flags) && | ||
323 | open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) | 324 | open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) |
324 | return nfserr_grace; | 325 | return nfserr_grace; |
325 | 326 | ||
@@ -452,6 +453,10 @@ nfsd4_restorefh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
452 | return nfserr_restorefh; | 453 | return nfserr_restorefh; |
453 | 454 | ||
454 | fh_dup2(&cstate->current_fh, &cstate->save_fh); | 455 | fh_dup2(&cstate->current_fh, &cstate->save_fh); |
456 | if (HAS_STATE_ID(cstate, SAVED_STATE_ID_FLAG)) { | ||
457 | memcpy(&cstate->current_stateid, &cstate->save_stateid, sizeof(stateid_t)); | ||
458 | SET_STATE_ID(cstate, CURRENT_STATE_ID_FLAG); | ||
459 | } | ||
455 | return nfs_ok; | 460 | return nfs_ok; |
456 | } | 461 | } |
457 | 462 | ||
@@ -463,6 +468,10 @@ nfsd4_savefh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
463 | return nfserr_nofilehandle; | 468 | return nfserr_nofilehandle; |
464 | 469 | ||
465 | fh_dup2(&cstate->save_fh, &cstate->current_fh); | 470 | fh_dup2(&cstate->save_fh, &cstate->current_fh); |
471 | if (HAS_STATE_ID(cstate, CURRENT_STATE_ID_FLAG)) { | ||
472 | memcpy(&cstate->save_stateid, &cstate->current_stateid, sizeof(stateid_t)); | ||
473 | SET_STATE_ID(cstate, SAVED_STATE_ID_FLAG); | ||
474 | } | ||
466 | return nfs_ok; | 475 | return nfs_ok; |
467 | } | 476 | } |
468 | 477 | ||
@@ -481,14 +490,20 @@ nfsd4_access(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
481 | &access->ac_supported); | 490 | &access->ac_supported); |
482 | } | 491 | } |
483 | 492 | ||
493 | static void gen_boot_verifier(nfs4_verifier *verifier) | ||
494 | { | ||
495 | __be32 verf[2]; | ||
496 | |||
497 | verf[0] = (__be32)nfssvc_boot.tv_sec; | ||
498 | verf[1] = (__be32)nfssvc_boot.tv_usec; | ||
499 | memcpy(verifier->data, verf, sizeof(verifier->data)); | ||
500 | } | ||
501 | |||
484 | static __be32 | 502 | static __be32 |
485 | nfsd4_commit(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | 503 | nfsd4_commit(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, |
486 | struct nfsd4_commit *commit) | 504 | struct nfsd4_commit *commit) |
487 | { | 505 | { |
488 | u32 *p = (u32 *)commit->co_verf.data; | 506 | gen_boot_verifier(&commit->co_verf); |
489 | *p++ = nfssvc_boot.tv_sec; | ||
490 | *p++ = nfssvc_boot.tv_usec; | ||
491 | |||
492 | return nfsd_commit(rqstp, &cstate->current_fh, commit->co_offset, | 507 | return nfsd_commit(rqstp, &cstate->current_fh, commit->co_offset, |
493 | commit->co_count); | 508 | commit->co_count); |
494 | } | 509 | } |
@@ -865,7 +880,6 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
865 | { | 880 | { |
866 | stateid_t *stateid = &write->wr_stateid; | 881 | stateid_t *stateid = &write->wr_stateid; |
867 | struct file *filp = NULL; | 882 | struct file *filp = NULL; |
868 | u32 *p; | ||
869 | __be32 status = nfs_ok; | 883 | __be32 status = nfs_ok; |
870 | unsigned long cnt; | 884 | unsigned long cnt; |
871 | 885 | ||
@@ -887,9 +901,7 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
887 | 901 | ||
888 | cnt = write->wr_buflen; | 902 | cnt = write->wr_buflen; |
889 | write->wr_how_written = write->wr_stable_how; | 903 | write->wr_how_written = write->wr_stable_how; |
890 | p = (u32 *)write->wr_verifier.data; | 904 | gen_boot_verifier(&write->wr_verifier); |
891 | *p++ = nfssvc_boot.tv_sec; | ||
892 | *p++ = nfssvc_boot.tv_usec; | ||
893 | 905 | ||
894 | status = nfsd_write(rqstp, &cstate->current_fh, filp, | 906 | status = nfsd_write(rqstp, &cstate->current_fh, filp, |
895 | write->wr_offset, rqstp->rq_vec, write->wr_vlen, | 907 | write->wr_offset, rqstp->rq_vec, write->wr_vlen, |
@@ -1000,6 +1012,8 @@ static inline void nfsd4_increment_op_stats(u32 opnum) | |||
1000 | typedef __be32(*nfsd4op_func)(struct svc_rqst *, struct nfsd4_compound_state *, | 1012 | typedef __be32(*nfsd4op_func)(struct svc_rqst *, struct nfsd4_compound_state *, |
1001 | void *); | 1013 | void *); |
1002 | typedef u32(*nfsd4op_rsize)(struct svc_rqst *, struct nfsd4_op *op); | 1014 | typedef u32(*nfsd4op_rsize)(struct svc_rqst *, struct nfsd4_op *op); |
1015 | typedef void(*stateid_setter)(struct nfsd4_compound_state *, void *); | ||
1016 | typedef void(*stateid_getter)(struct nfsd4_compound_state *, void *); | ||
1003 | 1017 | ||
1004 | enum nfsd4_op_flags { | 1018 | enum nfsd4_op_flags { |
1005 | ALLOWED_WITHOUT_FH = 1 << 0, /* No current filehandle required */ | 1019 | ALLOWED_WITHOUT_FH = 1 << 0, /* No current filehandle required */ |
@@ -1025,6 +1039,10 @@ enum nfsd4_op_flags { | |||
1025 | * the v4.0 case). | 1039 | * the v4.0 case). |
1026 | */ | 1040 | */ |
1027 | OP_CACHEME = 1 << 6, | 1041 | OP_CACHEME = 1 << 6, |
1042 | /* | ||
1043 | * These are ops which clear current state id. | ||
1044 | */ | ||
1045 | OP_CLEAR_STATEID = 1 << 7, | ||
1028 | }; | 1046 | }; |
1029 | 1047 | ||
1030 | struct nfsd4_operation { | 1048 | struct nfsd4_operation { |
@@ -1033,11 +1051,15 @@ struct nfsd4_operation { | |||
1033 | char *op_name; | 1051 | char *op_name; |
1034 | /* Try to get response size before operation */ | 1052 | /* Try to get response size before operation */ |
1035 | nfsd4op_rsize op_rsize_bop; | 1053 | nfsd4op_rsize op_rsize_bop; |
1054 | stateid_setter op_get_currentstateid; | ||
1055 | stateid_getter op_set_currentstateid; | ||
1036 | }; | 1056 | }; |
1037 | 1057 | ||
1038 | static struct nfsd4_operation nfsd4_ops[]; | 1058 | static struct nfsd4_operation nfsd4_ops[]; |
1039 | 1059 | ||
1060 | #ifdef NFSD_DEBUG | ||
1040 | static const char *nfsd4_op_name(unsigned opnum); | 1061 | static const char *nfsd4_op_name(unsigned opnum); |
1062 | #endif | ||
1041 | 1063 | ||
1042 | /* | 1064 | /* |
1043 | * Enforce NFSv4.1 COMPOUND ordering rules: | 1065 | * Enforce NFSv4.1 COMPOUND ordering rules: |
@@ -1215,13 +1237,23 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, | |||
1215 | if (op->status) | 1237 | if (op->status) |
1216 | goto encode_op; | 1238 | goto encode_op; |
1217 | 1239 | ||
1218 | if (opdesc->op_func) | 1240 | if (opdesc->op_func) { |
1241 | if (opdesc->op_get_currentstateid) | ||
1242 | opdesc->op_get_currentstateid(cstate, &op->u); | ||
1219 | op->status = opdesc->op_func(rqstp, cstate, &op->u); | 1243 | op->status = opdesc->op_func(rqstp, cstate, &op->u); |
1220 | else | 1244 | } else |
1221 | BUG_ON(op->status == nfs_ok); | 1245 | BUG_ON(op->status == nfs_ok); |
1222 | 1246 | ||
1223 | if (!op->status && need_wrongsec_check(rqstp)) | 1247 | if (!op->status) { |
1224 | op->status = check_nfsd_access(cstate->current_fh.fh_export, rqstp); | 1248 | if (opdesc->op_set_currentstateid) |
1249 | opdesc->op_set_currentstateid(cstate, &op->u); | ||
1250 | |||
1251 | if (opdesc->op_flags & OP_CLEAR_STATEID) | ||
1252 | clear_current_stateid(cstate); | ||
1253 | |||
1254 | if (need_wrongsec_check(rqstp)) | ||
1255 | op->status = check_nfsd_access(cstate->current_fh.fh_export, rqstp); | ||
1256 | } | ||
1225 | 1257 | ||
1226 | encode_op: | 1258 | encode_op: |
1227 | /* Only from SEQUENCE */ | 1259 | /* Only from SEQUENCE */ |
@@ -1413,6 +1445,8 @@ static struct nfsd4_operation nfsd4_ops[] = { | |||
1413 | .op_flags = OP_MODIFIES_SOMETHING, | 1445 | .op_flags = OP_MODIFIES_SOMETHING, |
1414 | .op_name = "OP_CLOSE", | 1446 | .op_name = "OP_CLOSE", |
1415 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_status_stateid_rsize, | 1447 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_status_stateid_rsize, |
1448 | .op_get_currentstateid = (stateid_getter)nfsd4_get_closestateid, | ||
1449 | .op_set_currentstateid = (stateid_setter)nfsd4_set_closestateid, | ||
1416 | }, | 1450 | }, |
1417 | [OP_COMMIT] = { | 1451 | [OP_COMMIT] = { |
1418 | .op_func = (nfsd4op_func)nfsd4_commit, | 1452 | .op_func = (nfsd4op_func)nfsd4_commit, |
@@ -1422,7 +1456,7 @@ static struct nfsd4_operation nfsd4_ops[] = { | |||
1422 | }, | 1456 | }, |
1423 | [OP_CREATE] = { | 1457 | [OP_CREATE] = { |
1424 | .op_func = (nfsd4op_func)nfsd4_create, | 1458 | .op_func = (nfsd4op_func)nfsd4_create, |
1425 | .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME, | 1459 | .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME | OP_CLEAR_STATEID, |
1426 | .op_name = "OP_CREATE", | 1460 | .op_name = "OP_CREATE", |
1427 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_create_rsize, | 1461 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_create_rsize, |
1428 | }, | 1462 | }, |
@@ -1431,6 +1465,7 @@ static struct nfsd4_operation nfsd4_ops[] = { | |||
1431 | .op_flags = OP_MODIFIES_SOMETHING, | 1465 | .op_flags = OP_MODIFIES_SOMETHING, |
1432 | .op_name = "OP_DELEGRETURN", | 1466 | .op_name = "OP_DELEGRETURN", |
1433 | .op_rsize_bop = nfsd4_only_status_rsize, | 1467 | .op_rsize_bop = nfsd4_only_status_rsize, |
1468 | .op_get_currentstateid = (stateid_getter)nfsd4_get_delegreturnstateid, | ||
1434 | }, | 1469 | }, |
1435 | [OP_GETATTR] = { | 1470 | [OP_GETATTR] = { |
1436 | .op_func = (nfsd4op_func)nfsd4_getattr, | 1471 | .op_func = (nfsd4op_func)nfsd4_getattr, |
@@ -1453,6 +1488,7 @@ static struct nfsd4_operation nfsd4_ops[] = { | |||
1453 | .op_flags = OP_MODIFIES_SOMETHING, | 1488 | .op_flags = OP_MODIFIES_SOMETHING, |
1454 | .op_name = "OP_LOCK", | 1489 | .op_name = "OP_LOCK", |
1455 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_lock_rsize, | 1490 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_lock_rsize, |
1491 | .op_set_currentstateid = (stateid_setter)nfsd4_set_lockstateid, | ||
1456 | }, | 1492 | }, |
1457 | [OP_LOCKT] = { | 1493 | [OP_LOCKT] = { |
1458 | .op_func = (nfsd4op_func)nfsd4_lockt, | 1494 | .op_func = (nfsd4op_func)nfsd4_lockt, |
@@ -1463,15 +1499,16 @@ static struct nfsd4_operation nfsd4_ops[] = { | |||
1463 | .op_flags = OP_MODIFIES_SOMETHING, | 1499 | .op_flags = OP_MODIFIES_SOMETHING, |
1464 | .op_name = "OP_LOCKU", | 1500 | .op_name = "OP_LOCKU", |
1465 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_status_stateid_rsize, | 1501 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_status_stateid_rsize, |
1502 | .op_get_currentstateid = (stateid_getter)nfsd4_get_lockustateid, | ||
1466 | }, | 1503 | }, |
1467 | [OP_LOOKUP] = { | 1504 | [OP_LOOKUP] = { |
1468 | .op_func = (nfsd4op_func)nfsd4_lookup, | 1505 | .op_func = (nfsd4op_func)nfsd4_lookup, |
1469 | .op_flags = OP_HANDLES_WRONGSEC, | 1506 | .op_flags = OP_HANDLES_WRONGSEC | OP_CLEAR_STATEID, |
1470 | .op_name = "OP_LOOKUP", | 1507 | .op_name = "OP_LOOKUP", |
1471 | }, | 1508 | }, |
1472 | [OP_LOOKUPP] = { | 1509 | [OP_LOOKUPP] = { |
1473 | .op_func = (nfsd4op_func)nfsd4_lookupp, | 1510 | .op_func = (nfsd4op_func)nfsd4_lookupp, |
1474 | .op_flags = OP_HANDLES_WRONGSEC, | 1511 | .op_flags = OP_HANDLES_WRONGSEC | OP_CLEAR_STATEID, |
1475 | .op_name = "OP_LOOKUPP", | 1512 | .op_name = "OP_LOOKUPP", |
1476 | }, | 1513 | }, |
1477 | [OP_NVERIFY] = { | 1514 | [OP_NVERIFY] = { |
@@ -1483,6 +1520,7 @@ static struct nfsd4_operation nfsd4_ops[] = { | |||
1483 | .op_flags = OP_HANDLES_WRONGSEC | OP_MODIFIES_SOMETHING, | 1520 | .op_flags = OP_HANDLES_WRONGSEC | OP_MODIFIES_SOMETHING, |
1484 | .op_name = "OP_OPEN", | 1521 | .op_name = "OP_OPEN", |
1485 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_open_rsize, | 1522 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_open_rsize, |
1523 | .op_set_currentstateid = (stateid_setter)nfsd4_set_openstateid, | ||
1486 | }, | 1524 | }, |
1487 | [OP_OPEN_CONFIRM] = { | 1525 | [OP_OPEN_CONFIRM] = { |
1488 | .op_func = (nfsd4op_func)nfsd4_open_confirm, | 1526 | .op_func = (nfsd4op_func)nfsd4_open_confirm, |
@@ -1495,25 +1533,30 @@ static struct nfsd4_operation nfsd4_ops[] = { | |||
1495 | .op_flags = OP_MODIFIES_SOMETHING, | 1533 | .op_flags = OP_MODIFIES_SOMETHING, |
1496 | .op_name = "OP_OPEN_DOWNGRADE", | 1534 | .op_name = "OP_OPEN_DOWNGRADE", |
1497 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_status_stateid_rsize, | 1535 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_status_stateid_rsize, |
1536 | .op_get_currentstateid = (stateid_getter)nfsd4_get_opendowngradestateid, | ||
1537 | .op_set_currentstateid = (stateid_setter)nfsd4_set_opendowngradestateid, | ||
1498 | }, | 1538 | }, |
1499 | [OP_PUTFH] = { | 1539 | [OP_PUTFH] = { |
1500 | .op_func = (nfsd4op_func)nfsd4_putfh, | 1540 | .op_func = (nfsd4op_func)nfsd4_putfh, |
1501 | .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS | 1541 | .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS |
1502 | | OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING, | 1542 | | OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING |
1543 | | OP_CLEAR_STATEID, | ||
1503 | .op_name = "OP_PUTFH", | 1544 | .op_name = "OP_PUTFH", |
1504 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, | 1545 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, |
1505 | }, | 1546 | }, |
1506 | [OP_PUTPUBFH] = { | 1547 | [OP_PUTPUBFH] = { |
1507 | .op_func = (nfsd4op_func)nfsd4_putrootfh, | 1548 | .op_func = (nfsd4op_func)nfsd4_putrootfh, |
1508 | .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS | 1549 | .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS |
1509 | | OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING, | 1550 | | OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING |
1551 | | OP_CLEAR_STATEID, | ||
1510 | .op_name = "OP_PUTPUBFH", | 1552 | .op_name = "OP_PUTPUBFH", |
1511 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, | 1553 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, |
1512 | }, | 1554 | }, |
1513 | [OP_PUTROOTFH] = { | 1555 | [OP_PUTROOTFH] = { |
1514 | .op_func = (nfsd4op_func)nfsd4_putrootfh, | 1556 | .op_func = (nfsd4op_func)nfsd4_putrootfh, |
1515 | .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS | 1557 | .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS |
1516 | | OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING, | 1558 | | OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING |
1559 | | OP_CLEAR_STATEID, | ||
1517 | .op_name = "OP_PUTROOTFH", | 1560 | .op_name = "OP_PUTROOTFH", |
1518 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, | 1561 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, |
1519 | }, | 1562 | }, |
@@ -1522,6 +1565,7 @@ static struct nfsd4_operation nfsd4_ops[] = { | |||
1522 | .op_flags = OP_MODIFIES_SOMETHING, | 1565 | .op_flags = OP_MODIFIES_SOMETHING, |
1523 | .op_name = "OP_READ", | 1566 | .op_name = "OP_READ", |
1524 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_read_rsize, | 1567 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_read_rsize, |
1568 | .op_get_currentstateid = (stateid_getter)nfsd4_get_readstateid, | ||
1525 | }, | 1569 | }, |
1526 | [OP_READDIR] = { | 1570 | [OP_READDIR] = { |
1527 | .op_func = (nfsd4op_func)nfsd4_readdir, | 1571 | .op_func = (nfsd4op_func)nfsd4_readdir, |
@@ -1576,6 +1620,7 @@ static struct nfsd4_operation nfsd4_ops[] = { | |||
1576 | .op_name = "OP_SETATTR", | 1620 | .op_name = "OP_SETATTR", |
1577 | .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME, | 1621 | .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME, |
1578 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_setattr_rsize, | 1622 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_setattr_rsize, |
1623 | .op_get_currentstateid = (stateid_getter)nfsd4_get_setattrstateid, | ||
1579 | }, | 1624 | }, |
1580 | [OP_SETCLIENTID] = { | 1625 | [OP_SETCLIENTID] = { |
1581 | .op_func = (nfsd4op_func)nfsd4_setclientid, | 1626 | .op_func = (nfsd4op_func)nfsd4_setclientid, |
@@ -1600,6 +1645,7 @@ static struct nfsd4_operation nfsd4_ops[] = { | |||
1600 | .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME, | 1645 | .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME, |
1601 | .op_name = "OP_WRITE", | 1646 | .op_name = "OP_WRITE", |
1602 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_write_rsize, | 1647 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_write_rsize, |
1648 | .op_get_currentstateid = (stateid_getter)nfsd4_get_writestateid, | ||
1603 | }, | 1649 | }, |
1604 | [OP_RELEASE_LOCKOWNER] = { | 1650 | [OP_RELEASE_LOCKOWNER] = { |
1605 | .op_func = (nfsd4op_func)nfsd4_release_lockowner, | 1651 | .op_func = (nfsd4op_func)nfsd4_release_lockowner, |
@@ -1674,12 +1720,14 @@ static struct nfsd4_operation nfsd4_ops[] = { | |||
1674 | }, | 1720 | }, |
1675 | }; | 1721 | }; |
1676 | 1722 | ||
1723 | #ifdef NFSD_DEBUG | ||
1677 | static const char *nfsd4_op_name(unsigned opnum) | 1724 | static const char *nfsd4_op_name(unsigned opnum) |
1678 | { | 1725 | { |
1679 | if (opnum < ARRAY_SIZE(nfsd4_ops)) | 1726 | if (opnum < ARRAY_SIZE(nfsd4_ops)) |
1680 | return nfsd4_ops[opnum].op_name; | 1727 | return nfsd4_ops[opnum].op_name; |
1681 | return "unknown_operation"; | 1728 | return "unknown_operation"; |
1682 | } | 1729 | } |
1730 | #endif | ||
1683 | 1731 | ||
1684 | #define nfsd4_voidres nfsd4_voidargs | 1732 | #define nfsd4_voidres nfsd4_voidargs |
1685 | struct nfsd4_voidargs { int dummy; }; | 1733 | struct nfsd4_voidargs { int dummy; }; |
diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index 0b3e875d1abd..4767429264a2 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c | |||
@@ -1,5 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (c) 2004 The Regents of the University of Michigan. | 2 | * Copyright (c) 2004 The Regents of the University of Michigan. |
3 | * Copyright (c) 2012 Jeff Layton <jlayton@redhat.com> | ||
3 | * All rights reserved. | 4 | * All rights reserved. |
4 | * | 5 | * |
5 | * Andy Adamson <andros@citi.umich.edu> | 6 | * Andy Adamson <andros@citi.umich.edu> |
@@ -36,16 +37,34 @@ | |||
36 | #include <linux/namei.h> | 37 | #include <linux/namei.h> |
37 | #include <linux/crypto.h> | 38 | #include <linux/crypto.h> |
38 | #include <linux/sched.h> | 39 | #include <linux/sched.h> |
40 | #include <linux/fs.h> | ||
41 | #include <linux/module.h> | ||
42 | #include <net/net_namespace.h> | ||
43 | #include <linux/sunrpc/rpc_pipe_fs.h> | ||
44 | #include <linux/sunrpc/clnt.h> | ||
45 | #include <linux/nfsd/cld.h> | ||
39 | 46 | ||
40 | #include "nfsd.h" | 47 | #include "nfsd.h" |
41 | #include "state.h" | 48 | #include "state.h" |
42 | #include "vfs.h" | 49 | #include "vfs.h" |
50 | #include "netns.h" | ||
43 | 51 | ||
44 | #define NFSDDBG_FACILITY NFSDDBG_PROC | 52 | #define NFSDDBG_FACILITY NFSDDBG_PROC |
45 | 53 | ||
54 | /* Declarations */ | ||
55 | struct nfsd4_client_tracking_ops { | ||
56 | int (*init)(struct net *); | ||
57 | void (*exit)(struct net *); | ||
58 | void (*create)(struct nfs4_client *); | ||
59 | void (*remove)(struct nfs4_client *); | ||
60 | int (*check)(struct nfs4_client *); | ||
61 | void (*grace_done)(struct net *, time_t); | ||
62 | }; | ||
63 | |||
46 | /* Globals */ | 64 | /* Globals */ |
47 | static struct file *rec_file; | 65 | static struct file *rec_file; |
48 | static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery"; | 66 | static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery"; |
67 | static struct nfsd4_client_tracking_ops *client_tracking_ops; | ||
49 | 68 | ||
50 | static int | 69 | static int |
51 | nfs4_save_creds(const struct cred **original_creds) | 70 | nfs4_save_creds(const struct cred **original_creds) |
@@ -117,7 +136,8 @@ out_no_tfm: | |||
117 | return status; | 136 | return status; |
118 | } | 137 | } |
119 | 138 | ||
120 | void nfsd4_create_clid_dir(struct nfs4_client *clp) | 139 | static void |
140 | nfsd4_create_clid_dir(struct nfs4_client *clp) | ||
121 | { | 141 | { |
122 | const struct cred *original_cred; | 142 | const struct cred *original_cred; |
123 | char *dname = clp->cl_recdir; | 143 | char *dname = clp->cl_recdir; |
@@ -126,9 +146,8 @@ void nfsd4_create_clid_dir(struct nfs4_client *clp) | |||
126 | 146 | ||
127 | dprintk("NFSD: nfsd4_create_clid_dir for \"%s\"\n", dname); | 147 | dprintk("NFSD: nfsd4_create_clid_dir for \"%s\"\n", dname); |
128 | 148 | ||
129 | if (clp->cl_firststate) | 149 | if (test_and_set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) |
130 | return; | 150 | return; |
131 | clp->cl_firststate = 1; | ||
132 | if (!rec_file) | 151 | if (!rec_file) |
133 | return; | 152 | return; |
134 | status = nfs4_save_creds(&original_cred); | 153 | status = nfs4_save_creds(&original_cred); |
@@ -265,19 +284,19 @@ out_unlock: | |||
265 | return status; | 284 | return status; |
266 | } | 285 | } |
267 | 286 | ||
268 | void | 287 | static void |
269 | nfsd4_remove_clid_dir(struct nfs4_client *clp) | 288 | nfsd4_remove_clid_dir(struct nfs4_client *clp) |
270 | { | 289 | { |
271 | const struct cred *original_cred; | 290 | const struct cred *original_cred; |
272 | int status; | 291 | int status; |
273 | 292 | ||
274 | if (!rec_file || !clp->cl_firststate) | 293 | if (!rec_file || !test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) |
275 | return; | 294 | return; |
276 | 295 | ||
277 | status = mnt_want_write_file(rec_file); | 296 | status = mnt_want_write_file(rec_file); |
278 | if (status) | 297 | if (status) |
279 | goto out; | 298 | goto out; |
280 | clp->cl_firststate = 0; | 299 | clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); |
281 | 300 | ||
282 | status = nfs4_save_creds(&original_cred); | 301 | status = nfs4_save_creds(&original_cred); |
283 | if (status < 0) | 302 | if (status < 0) |
@@ -292,7 +311,6 @@ out: | |||
292 | if (status) | 311 | if (status) |
293 | printk("NFSD: Failed to remove expired client state directory" | 312 | printk("NFSD: Failed to remove expired client state directory" |
294 | " %.*s\n", HEXDIR_LEN, clp->cl_recdir); | 313 | " %.*s\n", HEXDIR_LEN, clp->cl_recdir); |
295 | return; | ||
296 | } | 314 | } |
297 | 315 | ||
298 | static int | 316 | static int |
@@ -311,8 +329,9 @@ purge_old(struct dentry *parent, struct dentry *child) | |||
311 | return 0; | 329 | return 0; |
312 | } | 330 | } |
313 | 331 | ||
314 | void | 332 | static void |
315 | nfsd4_recdir_purge_old(void) { | 333 | nfsd4_recdir_purge_old(struct net *net, time_t boot_time) |
334 | { | ||
316 | int status; | 335 | int status; |
317 | 336 | ||
318 | if (!rec_file) | 337 | if (!rec_file) |
@@ -343,7 +362,7 @@ load_recdir(struct dentry *parent, struct dentry *child) | |||
343 | return 0; | 362 | return 0; |
344 | } | 363 | } |
345 | 364 | ||
346 | int | 365 | static int |
347 | nfsd4_recdir_load(void) { | 366 | nfsd4_recdir_load(void) { |
348 | int status; | 367 | int status; |
349 | 368 | ||
@@ -361,8 +380,8 @@ nfsd4_recdir_load(void) { | |||
361 | * Hold reference to the recovery directory. | 380 | * Hold reference to the recovery directory. |
362 | */ | 381 | */ |
363 | 382 | ||
364 | void | 383 | static int |
365 | nfsd4_init_recdir() | 384 | nfsd4_init_recdir(void) |
366 | { | 385 | { |
367 | const struct cred *original_cred; | 386 | const struct cred *original_cred; |
368 | int status; | 387 | int status; |
@@ -377,20 +396,44 @@ nfsd4_init_recdir() | |||
377 | printk("NFSD: Unable to change credentials to find recovery" | 396 | printk("NFSD: Unable to change credentials to find recovery" |
378 | " directory: error %d\n", | 397 | " directory: error %d\n", |
379 | status); | 398 | status); |
380 | return; | 399 | return status; |
381 | } | 400 | } |
382 | 401 | ||
383 | rec_file = filp_open(user_recovery_dirname, O_RDONLY | O_DIRECTORY, 0); | 402 | rec_file = filp_open(user_recovery_dirname, O_RDONLY | O_DIRECTORY, 0); |
384 | if (IS_ERR(rec_file)) { | 403 | if (IS_ERR(rec_file)) { |
385 | printk("NFSD: unable to find recovery directory %s\n", | 404 | printk("NFSD: unable to find recovery directory %s\n", |
386 | user_recovery_dirname); | 405 | user_recovery_dirname); |
406 | status = PTR_ERR(rec_file); | ||
387 | rec_file = NULL; | 407 | rec_file = NULL; |
388 | } | 408 | } |
389 | 409 | ||
390 | nfs4_reset_creds(original_cred); | 410 | nfs4_reset_creds(original_cred); |
411 | return status; | ||
391 | } | 412 | } |
392 | 413 | ||
393 | void | 414 | static int |
415 | nfsd4_load_reboot_recovery_data(struct net *net) | ||
416 | { | ||
417 | int status; | ||
418 | |||
419 | /* XXX: The legacy code won't work in a container */ | ||
420 | if (net != &init_net) { | ||
421 | WARN(1, KERN_ERR "NFSD: attempt to initialize legacy client " | ||
422 | "tracking in a container!\n"); | ||
423 | return -EINVAL; | ||
424 | } | ||
425 | |||
426 | nfs4_lock_state(); | ||
427 | status = nfsd4_init_recdir(); | ||
428 | if (!status) | ||
429 | status = nfsd4_recdir_load(); | ||
430 | nfs4_unlock_state(); | ||
431 | if (status) | ||
432 | printk(KERN_ERR "NFSD: Failure reading reboot recovery data\n"); | ||
433 | return status; | ||
434 | } | ||
435 | |||
436 | static void | ||
394 | nfsd4_shutdown_recdir(void) | 437 | nfsd4_shutdown_recdir(void) |
395 | { | 438 | { |
396 | if (!rec_file) | 439 | if (!rec_file) |
@@ -399,6 +442,13 @@ nfsd4_shutdown_recdir(void) | |||
399 | rec_file = NULL; | 442 | rec_file = NULL; |
400 | } | 443 | } |
401 | 444 | ||
445 | static void | ||
446 | nfsd4_legacy_tracking_exit(struct net *net) | ||
447 | { | ||
448 | nfs4_release_reclaim(); | ||
449 | nfsd4_shutdown_recdir(); | ||
450 | } | ||
451 | |||
402 | /* | 452 | /* |
403 | * Change the NFSv4 recovery directory to recdir. | 453 | * Change the NFSv4 recovery directory to recdir. |
404 | */ | 454 | */ |
@@ -425,3 +475,572 @@ nfs4_recoverydir(void) | |||
425 | { | 475 | { |
426 | return user_recovery_dirname; | 476 | return user_recovery_dirname; |
427 | } | 477 | } |
478 | |||
479 | static int | ||
480 | nfsd4_check_legacy_client(struct nfs4_client *clp) | ||
481 | { | ||
482 | /* did we already find that this client is stable? */ | ||
483 | if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) | ||
484 | return 0; | ||
485 | |||
486 | /* look for it in the reclaim hashtable otherwise */ | ||
487 | if (nfsd4_find_reclaim_client(clp)) { | ||
488 | set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); | ||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | return -ENOENT; | ||
493 | } | ||
494 | |||
495 | static struct nfsd4_client_tracking_ops nfsd4_legacy_tracking_ops = { | ||
496 | .init = nfsd4_load_reboot_recovery_data, | ||
497 | .exit = nfsd4_legacy_tracking_exit, | ||
498 | .create = nfsd4_create_clid_dir, | ||
499 | .remove = nfsd4_remove_clid_dir, | ||
500 | .check = nfsd4_check_legacy_client, | ||
501 | .grace_done = nfsd4_recdir_purge_old, | ||
502 | }; | ||
503 | |||
504 | /* Globals */ | ||
505 | #define NFSD_PIPE_DIR "nfsd" | ||
506 | #define NFSD_CLD_PIPE "cld" | ||
507 | |||
508 | /* per-net-ns structure for holding cld upcall info */ | ||
509 | struct cld_net { | ||
510 | struct rpc_pipe *cn_pipe; | ||
511 | spinlock_t cn_lock; | ||
512 | struct list_head cn_list; | ||
513 | unsigned int cn_xid; | ||
514 | }; | ||
515 | |||
516 | struct cld_upcall { | ||
517 | struct list_head cu_list; | ||
518 | struct cld_net *cu_net; | ||
519 | struct task_struct *cu_task; | ||
520 | struct cld_msg cu_msg; | ||
521 | }; | ||
522 | |||
523 | static int | ||
524 | __cld_pipe_upcall(struct rpc_pipe *pipe, struct cld_msg *cmsg) | ||
525 | { | ||
526 | int ret; | ||
527 | struct rpc_pipe_msg msg; | ||
528 | |||
529 | memset(&msg, 0, sizeof(msg)); | ||
530 | msg.data = cmsg; | ||
531 | msg.len = sizeof(*cmsg); | ||
532 | |||
533 | /* | ||
534 | * Set task state before we queue the upcall. That prevents | ||
535 | * wake_up_process in the downcall from racing with schedule. | ||
536 | */ | ||
537 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
538 | ret = rpc_queue_upcall(pipe, &msg); | ||
539 | if (ret < 0) { | ||
540 | set_current_state(TASK_RUNNING); | ||
541 | goto out; | ||
542 | } | ||
543 | |||
544 | schedule(); | ||
545 | set_current_state(TASK_RUNNING); | ||
546 | |||
547 | if (msg.errno < 0) | ||
548 | ret = msg.errno; | ||
549 | out: | ||
550 | return ret; | ||
551 | } | ||
552 | |||
553 | static int | ||
554 | cld_pipe_upcall(struct rpc_pipe *pipe, struct cld_msg *cmsg) | ||
555 | { | ||
556 | int ret; | ||
557 | |||
558 | /* | ||
559 | * -EAGAIN occurs when pipe is closed and reopened while there are | ||
560 | * upcalls queued. | ||
561 | */ | ||
562 | do { | ||
563 | ret = __cld_pipe_upcall(pipe, cmsg); | ||
564 | } while (ret == -EAGAIN); | ||
565 | |||
566 | return ret; | ||
567 | } | ||
568 | |||
569 | static ssize_t | ||
570 | cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) | ||
571 | { | ||
572 | struct cld_upcall *tmp, *cup; | ||
573 | struct cld_msg *cmsg = (struct cld_msg *)src; | ||
574 | uint32_t xid; | ||
575 | struct nfsd_net *nn = net_generic(filp->f_dentry->d_sb->s_fs_info, | ||
576 | nfsd_net_id); | ||
577 | struct cld_net *cn = nn->cld_net; | ||
578 | |||
579 | if (mlen != sizeof(*cmsg)) { | ||
580 | dprintk("%s: got %lu bytes, expected %lu\n", __func__, mlen, | ||
581 | sizeof(*cmsg)); | ||
582 | return -EINVAL; | ||
583 | } | ||
584 | |||
585 | /* copy just the xid so we can try to find that */ | ||
586 | if (copy_from_user(&xid, &cmsg->cm_xid, sizeof(xid)) != 0) { | ||
587 | dprintk("%s: error when copying xid from userspace", __func__); | ||
588 | return -EFAULT; | ||
589 | } | ||
590 | |||
591 | /* walk the list and find corresponding xid */ | ||
592 | cup = NULL; | ||
593 | spin_lock(&cn->cn_lock); | ||
594 | list_for_each_entry(tmp, &cn->cn_list, cu_list) { | ||
595 | if (get_unaligned(&tmp->cu_msg.cm_xid) == xid) { | ||
596 | cup = tmp; | ||
597 | list_del_init(&cup->cu_list); | ||
598 | break; | ||
599 | } | ||
600 | } | ||
601 | spin_unlock(&cn->cn_lock); | ||
602 | |||
603 | /* couldn't find upcall? */ | ||
604 | if (!cup) { | ||
605 | dprintk("%s: couldn't find upcall -- xid=%u\n", __func__, xid); | ||
606 | return -EINVAL; | ||
607 | } | ||
608 | |||
609 | if (copy_from_user(&cup->cu_msg, src, mlen) != 0) | ||
610 | return -EFAULT; | ||
611 | |||
612 | wake_up_process(cup->cu_task); | ||
613 | return mlen; | ||
614 | } | ||
615 | |||
616 | static void | ||
617 | cld_pipe_destroy_msg(struct rpc_pipe_msg *msg) | ||
618 | { | ||
619 | struct cld_msg *cmsg = msg->data; | ||
620 | struct cld_upcall *cup = container_of(cmsg, struct cld_upcall, | ||
621 | cu_msg); | ||
622 | |||
623 | /* errno >= 0 means we got a downcall */ | ||
624 | if (msg->errno >= 0) | ||
625 | return; | ||
626 | |||
627 | wake_up_process(cup->cu_task); | ||
628 | } | ||
629 | |||
630 | static const struct rpc_pipe_ops cld_upcall_ops = { | ||
631 | .upcall = rpc_pipe_generic_upcall, | ||
632 | .downcall = cld_pipe_downcall, | ||
633 | .destroy_msg = cld_pipe_destroy_msg, | ||
634 | }; | ||
635 | |||
636 | static struct dentry * | ||
637 | nfsd4_cld_register_sb(struct super_block *sb, struct rpc_pipe *pipe) | ||
638 | { | ||
639 | struct dentry *dir, *dentry; | ||
640 | |||
641 | dir = rpc_d_lookup_sb(sb, NFSD_PIPE_DIR); | ||
642 | if (dir == NULL) | ||
643 | return ERR_PTR(-ENOENT); | ||
644 | dentry = rpc_mkpipe_dentry(dir, NFSD_CLD_PIPE, NULL, pipe); | ||
645 | dput(dir); | ||
646 | return dentry; | ||
647 | } | ||
648 | |||
649 | static void | ||
650 | nfsd4_cld_unregister_sb(struct rpc_pipe *pipe) | ||
651 | { | ||
652 | if (pipe->dentry) | ||
653 | rpc_unlink(pipe->dentry); | ||
654 | } | ||
655 | |||
656 | static struct dentry * | ||
657 | nfsd4_cld_register_net(struct net *net, struct rpc_pipe *pipe) | ||
658 | { | ||
659 | struct super_block *sb; | ||
660 | struct dentry *dentry; | ||
661 | |||
662 | sb = rpc_get_sb_net(net); | ||
663 | if (!sb) | ||
664 | return NULL; | ||
665 | dentry = nfsd4_cld_register_sb(sb, pipe); | ||
666 | rpc_put_sb_net(net); | ||
667 | return dentry; | ||
668 | } | ||
669 | |||
670 | static void | ||
671 | nfsd4_cld_unregister_net(struct net *net, struct rpc_pipe *pipe) | ||
672 | { | ||
673 | struct super_block *sb; | ||
674 | |||
675 | sb = rpc_get_sb_net(net); | ||
676 | if (sb) { | ||
677 | nfsd4_cld_unregister_sb(pipe); | ||
678 | rpc_put_sb_net(net); | ||
679 | } | ||
680 | } | ||
681 | |||
682 | /* Initialize rpc_pipefs pipe for communication with client tracking daemon */ | ||
683 | static int | ||
684 | nfsd4_init_cld_pipe(struct net *net) | ||
685 | { | ||
686 | int ret; | ||
687 | struct dentry *dentry; | ||
688 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
689 | struct cld_net *cn; | ||
690 | |||
691 | if (nn->cld_net) | ||
692 | return 0; | ||
693 | |||
694 | cn = kzalloc(sizeof(*cn), GFP_KERNEL); | ||
695 | if (!cn) { | ||
696 | ret = -ENOMEM; | ||
697 | goto err; | ||
698 | } | ||
699 | |||
700 | cn->cn_pipe = rpc_mkpipe_data(&cld_upcall_ops, RPC_PIPE_WAIT_FOR_OPEN); | ||
701 | if (IS_ERR(cn->cn_pipe)) { | ||
702 | ret = PTR_ERR(cn->cn_pipe); | ||
703 | goto err; | ||
704 | } | ||
705 | spin_lock_init(&cn->cn_lock); | ||
706 | INIT_LIST_HEAD(&cn->cn_list); | ||
707 | |||
708 | dentry = nfsd4_cld_register_net(net, cn->cn_pipe); | ||
709 | if (IS_ERR(dentry)) { | ||
710 | ret = PTR_ERR(dentry); | ||
711 | goto err_destroy_data; | ||
712 | } | ||
713 | |||
714 | cn->cn_pipe->dentry = dentry; | ||
715 | nn->cld_net = cn; | ||
716 | return 0; | ||
717 | |||
718 | err_destroy_data: | ||
719 | rpc_destroy_pipe_data(cn->cn_pipe); | ||
720 | err: | ||
721 | kfree(cn); | ||
722 | printk(KERN_ERR "NFSD: unable to create nfsdcld upcall pipe (%d)\n", | ||
723 | ret); | ||
724 | return ret; | ||
725 | } | ||
726 | |||
727 | static void | ||
728 | nfsd4_remove_cld_pipe(struct net *net) | ||
729 | { | ||
730 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
731 | struct cld_net *cn = nn->cld_net; | ||
732 | |||
733 | nfsd4_cld_unregister_net(net, cn->cn_pipe); | ||
734 | rpc_destroy_pipe_data(cn->cn_pipe); | ||
735 | kfree(nn->cld_net); | ||
736 | nn->cld_net = NULL; | ||
737 | } | ||
738 | |||
739 | static struct cld_upcall * | ||
740 | alloc_cld_upcall(struct cld_net *cn) | ||
741 | { | ||
742 | struct cld_upcall *new, *tmp; | ||
743 | |||
744 | new = kzalloc(sizeof(*new), GFP_KERNEL); | ||
745 | if (!new) | ||
746 | return new; | ||
747 | |||
748 | /* FIXME: hard cap on number in flight? */ | ||
749 | restart_search: | ||
750 | spin_lock(&cn->cn_lock); | ||
751 | list_for_each_entry(tmp, &cn->cn_list, cu_list) { | ||
752 | if (tmp->cu_msg.cm_xid == cn->cn_xid) { | ||
753 | cn->cn_xid++; | ||
754 | spin_unlock(&cn->cn_lock); | ||
755 | goto restart_search; | ||
756 | } | ||
757 | } | ||
758 | new->cu_task = current; | ||
759 | new->cu_msg.cm_vers = CLD_UPCALL_VERSION; | ||
760 | put_unaligned(cn->cn_xid++, &new->cu_msg.cm_xid); | ||
761 | new->cu_net = cn; | ||
762 | list_add(&new->cu_list, &cn->cn_list); | ||
763 | spin_unlock(&cn->cn_lock); | ||
764 | |||
765 | dprintk("%s: allocated xid %u\n", __func__, new->cu_msg.cm_xid); | ||
766 | |||
767 | return new; | ||
768 | } | ||
769 | |||
770 | static void | ||
771 | free_cld_upcall(struct cld_upcall *victim) | ||
772 | { | ||
773 | struct cld_net *cn = victim->cu_net; | ||
774 | |||
775 | spin_lock(&cn->cn_lock); | ||
776 | list_del(&victim->cu_list); | ||
777 | spin_unlock(&cn->cn_lock); | ||
778 | kfree(victim); | ||
779 | } | ||
780 | |||
781 | /* Ask daemon to create a new record */ | ||
782 | static void | ||
783 | nfsd4_cld_create(struct nfs4_client *clp) | ||
784 | { | ||
785 | int ret; | ||
786 | struct cld_upcall *cup; | ||
787 | /* FIXME: determine net from clp */ | ||
788 | struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); | ||
789 | struct cld_net *cn = nn->cld_net; | ||
790 | |||
791 | /* Don't upcall if it's already stored */ | ||
792 | if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) | ||
793 | return; | ||
794 | |||
795 | cup = alloc_cld_upcall(cn); | ||
796 | if (!cup) { | ||
797 | ret = -ENOMEM; | ||
798 | goto out_err; | ||
799 | } | ||
800 | |||
801 | cup->cu_msg.cm_cmd = Cld_Create; | ||
802 | cup->cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len; | ||
803 | memcpy(cup->cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data, | ||
804 | clp->cl_name.len); | ||
805 | |||
806 | ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg); | ||
807 | if (!ret) { | ||
808 | ret = cup->cu_msg.cm_status; | ||
809 | set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); | ||
810 | } | ||
811 | |||
812 | free_cld_upcall(cup); | ||
813 | out_err: | ||
814 | if (ret) | ||
815 | printk(KERN_ERR "NFSD: Unable to create client " | ||
816 | "record on stable storage: %d\n", ret); | ||
817 | } | ||
818 | |||
819 | /* Ask daemon to create a new record */ | ||
820 | static void | ||
821 | nfsd4_cld_remove(struct nfs4_client *clp) | ||
822 | { | ||
823 | int ret; | ||
824 | struct cld_upcall *cup; | ||
825 | /* FIXME: determine net from clp */ | ||
826 | struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); | ||
827 | struct cld_net *cn = nn->cld_net; | ||
828 | |||
829 | /* Don't upcall if it's already removed */ | ||
830 | if (!test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) | ||
831 | return; | ||
832 | |||
833 | cup = alloc_cld_upcall(cn); | ||
834 | if (!cup) { | ||
835 | ret = -ENOMEM; | ||
836 | goto out_err; | ||
837 | } | ||
838 | |||
839 | cup->cu_msg.cm_cmd = Cld_Remove; | ||
840 | cup->cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len; | ||
841 | memcpy(cup->cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data, | ||
842 | clp->cl_name.len); | ||
843 | |||
844 | ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg); | ||
845 | if (!ret) { | ||
846 | ret = cup->cu_msg.cm_status; | ||
847 | clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); | ||
848 | } | ||
849 | |||
850 | free_cld_upcall(cup); | ||
851 | out_err: | ||
852 | if (ret) | ||
853 | printk(KERN_ERR "NFSD: Unable to remove client " | ||
854 | "record from stable storage: %d\n", ret); | ||
855 | } | ||
856 | |||
857 | /* Check for presence of a record, and update its timestamp */ | ||
858 | static int | ||
859 | nfsd4_cld_check(struct nfs4_client *clp) | ||
860 | { | ||
861 | int ret; | ||
862 | struct cld_upcall *cup; | ||
863 | /* FIXME: determine net from clp */ | ||
864 | struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); | ||
865 | struct cld_net *cn = nn->cld_net; | ||
866 | |||
867 | /* Don't upcall if one was already stored during this grace pd */ | ||
868 | if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) | ||
869 | return 0; | ||
870 | |||
871 | cup = alloc_cld_upcall(cn); | ||
872 | if (!cup) { | ||
873 | printk(KERN_ERR "NFSD: Unable to check client record on " | ||
874 | "stable storage: %d\n", -ENOMEM); | ||
875 | return -ENOMEM; | ||
876 | } | ||
877 | |||
878 | cup->cu_msg.cm_cmd = Cld_Check; | ||
879 | cup->cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len; | ||
880 | memcpy(cup->cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data, | ||
881 | clp->cl_name.len); | ||
882 | |||
883 | ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg); | ||
884 | if (!ret) { | ||
885 | ret = cup->cu_msg.cm_status; | ||
886 | set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); | ||
887 | } | ||
888 | |||
889 | free_cld_upcall(cup); | ||
890 | return ret; | ||
891 | } | ||
892 | |||
893 | static void | ||
894 | nfsd4_cld_grace_done(struct net *net, time_t boot_time) | ||
895 | { | ||
896 | int ret; | ||
897 | struct cld_upcall *cup; | ||
898 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
899 | struct cld_net *cn = nn->cld_net; | ||
900 | |||
901 | cup = alloc_cld_upcall(cn); | ||
902 | if (!cup) { | ||
903 | ret = -ENOMEM; | ||
904 | goto out_err; | ||
905 | } | ||
906 | |||
907 | cup->cu_msg.cm_cmd = Cld_GraceDone; | ||
908 | cup->cu_msg.cm_u.cm_gracetime = (int64_t)boot_time; | ||
909 | ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg); | ||
910 | if (!ret) | ||
911 | ret = cup->cu_msg.cm_status; | ||
912 | |||
913 | free_cld_upcall(cup); | ||
914 | out_err: | ||
915 | if (ret) | ||
916 | printk(KERN_ERR "NFSD: Unable to end grace period: %d\n", ret); | ||
917 | } | ||
918 | |||
919 | static struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops = { | ||
920 | .init = nfsd4_init_cld_pipe, | ||
921 | .exit = nfsd4_remove_cld_pipe, | ||
922 | .create = nfsd4_cld_create, | ||
923 | .remove = nfsd4_cld_remove, | ||
924 | .check = nfsd4_cld_check, | ||
925 | .grace_done = nfsd4_cld_grace_done, | ||
926 | }; | ||
927 | |||
928 | int | ||
929 | nfsd4_client_tracking_init(struct net *net) | ||
930 | { | ||
931 | int status; | ||
932 | struct path path; | ||
933 | |||
934 | if (!client_tracking_ops) { | ||
935 | client_tracking_ops = &nfsd4_cld_tracking_ops; | ||
936 | status = kern_path(nfs4_recoverydir(), LOOKUP_FOLLOW, &path); | ||
937 | if (!status) { | ||
938 | if (S_ISDIR(path.dentry->d_inode->i_mode)) | ||
939 | client_tracking_ops = | ||
940 | &nfsd4_legacy_tracking_ops; | ||
941 | path_put(&path); | ||
942 | } | ||
943 | } | ||
944 | |||
945 | status = client_tracking_ops->init(net); | ||
946 | if (status) { | ||
947 | printk(KERN_WARNING "NFSD: Unable to initialize client " | ||
948 | "recovery tracking! (%d)\n", status); | ||
949 | client_tracking_ops = NULL; | ||
950 | } | ||
951 | return status; | ||
952 | } | ||
953 | |||
954 | void | ||
955 | nfsd4_client_tracking_exit(struct net *net) | ||
956 | { | ||
957 | if (client_tracking_ops) { | ||
958 | client_tracking_ops->exit(net); | ||
959 | client_tracking_ops = NULL; | ||
960 | } | ||
961 | } | ||
962 | |||
963 | void | ||
964 | nfsd4_client_record_create(struct nfs4_client *clp) | ||
965 | { | ||
966 | if (client_tracking_ops) | ||
967 | client_tracking_ops->create(clp); | ||
968 | } | ||
969 | |||
970 | void | ||
971 | nfsd4_client_record_remove(struct nfs4_client *clp) | ||
972 | { | ||
973 | if (client_tracking_ops) | ||
974 | client_tracking_ops->remove(clp); | ||
975 | } | ||
976 | |||
977 | int | ||
978 | nfsd4_client_record_check(struct nfs4_client *clp) | ||
979 | { | ||
980 | if (client_tracking_ops) | ||
981 | return client_tracking_ops->check(clp); | ||
982 | |||
983 | return -EOPNOTSUPP; | ||
984 | } | ||
985 | |||
986 | void | ||
987 | nfsd4_record_grace_done(struct net *net, time_t boot_time) | ||
988 | { | ||
989 | if (client_tracking_ops) | ||
990 | client_tracking_ops->grace_done(net, boot_time); | ||
991 | } | ||
992 | |||
993 | static int | ||
994 | rpc_pipefs_event(struct notifier_block *nb, unsigned long event, void *ptr) | ||
995 | { | ||
996 | struct super_block *sb = ptr; | ||
997 | struct net *net = sb->s_fs_info; | ||
998 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
999 | struct cld_net *cn = nn->cld_net; | ||
1000 | struct dentry *dentry; | ||
1001 | int ret = 0; | ||
1002 | |||
1003 | if (!try_module_get(THIS_MODULE)) | ||
1004 | return 0; | ||
1005 | |||
1006 | if (!cn) { | ||
1007 | module_put(THIS_MODULE); | ||
1008 | return 0; | ||
1009 | } | ||
1010 | |||
1011 | switch (event) { | ||
1012 | case RPC_PIPEFS_MOUNT: | ||
1013 | dentry = nfsd4_cld_register_sb(sb, cn->cn_pipe); | ||
1014 | if (IS_ERR(dentry)) { | ||
1015 | ret = PTR_ERR(dentry); | ||
1016 | break; | ||
1017 | } | ||
1018 | cn->cn_pipe->dentry = dentry; | ||
1019 | break; | ||
1020 | case RPC_PIPEFS_UMOUNT: | ||
1021 | if (cn->cn_pipe->dentry) | ||
1022 | nfsd4_cld_unregister_sb(cn->cn_pipe); | ||
1023 | break; | ||
1024 | default: | ||
1025 | ret = -ENOTSUPP; | ||
1026 | break; | ||
1027 | } | ||
1028 | module_put(THIS_MODULE); | ||
1029 | return ret; | ||
1030 | } | ||
1031 | |||
1032 | struct notifier_block nfsd4_cld_block = { | ||
1033 | .notifier_call = rpc_pipefs_event, | ||
1034 | }; | ||
1035 | |||
1036 | int | ||
1037 | register_cld_notifier(void) | ||
1038 | { | ||
1039 | return rpc_pipefs_notifier_register(&nfsd4_cld_block); | ||
1040 | } | ||
1041 | |||
1042 | void | ||
1043 | unregister_cld_notifier(void) | ||
1044 | { | ||
1045 | rpc_pipefs_notifier_unregister(&nfsd4_cld_block); | ||
1046 | } | ||
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index c5cddd659429..1841f8bf845e 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -58,11 +58,15 @@ static const stateid_t one_stateid = { | |||
58 | static const stateid_t zero_stateid = { | 58 | static const stateid_t zero_stateid = { |
59 | /* all fields zero */ | 59 | /* all fields zero */ |
60 | }; | 60 | }; |
61 | static const stateid_t currentstateid = { | ||
62 | .si_generation = 1, | ||
63 | }; | ||
61 | 64 | ||
62 | static u64 current_sessionid = 1; | 65 | static u64 current_sessionid = 1; |
63 | 66 | ||
64 | #define ZERO_STATEID(stateid) (!memcmp((stateid), &zero_stateid, sizeof(stateid_t))) | 67 | #define ZERO_STATEID(stateid) (!memcmp((stateid), &zero_stateid, sizeof(stateid_t))) |
65 | #define ONE_STATEID(stateid) (!memcmp((stateid), &one_stateid, sizeof(stateid_t))) | 68 | #define ONE_STATEID(stateid) (!memcmp((stateid), &one_stateid, sizeof(stateid_t))) |
69 | #define CURRENT_STATEID(stateid) (!memcmp((stateid), ¤tstateid, sizeof(stateid_t))) | ||
66 | 70 | ||
67 | /* forward declarations */ | 71 | /* forward declarations */ |
68 | static int check_for_locks(struct nfs4_file *filp, struct nfs4_lockowner *lowner); | 72 | static int check_for_locks(struct nfs4_file *filp, struct nfs4_lockowner *lowner); |
@@ -91,6 +95,19 @@ nfs4_lock_state(void) | |||
91 | mutex_lock(&client_mutex); | 95 | mutex_lock(&client_mutex); |
92 | } | 96 | } |
93 | 97 | ||
98 | static void free_session(struct kref *); | ||
99 | |||
100 | /* Must be called under the client_lock */ | ||
101 | static void nfsd4_put_session_locked(struct nfsd4_session *ses) | ||
102 | { | ||
103 | kref_put(&ses->se_ref, free_session); | ||
104 | } | ||
105 | |||
106 | static void nfsd4_get_session(struct nfsd4_session *ses) | ||
107 | { | ||
108 | kref_get(&ses->se_ref); | ||
109 | } | ||
110 | |||
94 | void | 111 | void |
95 | nfs4_unlock_state(void) | 112 | nfs4_unlock_state(void) |
96 | { | 113 | { |
@@ -605,12 +622,20 @@ hash_sessionid(struct nfs4_sessionid *sessionid) | |||
605 | return sid->sequence % SESSION_HASH_SIZE; | 622 | return sid->sequence % SESSION_HASH_SIZE; |
606 | } | 623 | } |
607 | 624 | ||
625 | #ifdef NFSD_DEBUG | ||
608 | static inline void | 626 | static inline void |
609 | dump_sessionid(const char *fn, struct nfs4_sessionid *sessionid) | 627 | dump_sessionid(const char *fn, struct nfs4_sessionid *sessionid) |
610 | { | 628 | { |
611 | u32 *ptr = (u32 *)(&sessionid->data[0]); | 629 | u32 *ptr = (u32 *)(&sessionid->data[0]); |
612 | dprintk("%s: %u:%u:%u:%u\n", fn, ptr[0], ptr[1], ptr[2], ptr[3]); | 630 | dprintk("%s: %u:%u:%u:%u\n", fn, ptr[0], ptr[1], ptr[2], ptr[3]); |
613 | } | 631 | } |
632 | #else | ||
633 | static inline void | ||
634 | dump_sessionid(const char *fn, struct nfs4_sessionid *sessionid) | ||
635 | { | ||
636 | } | ||
637 | #endif | ||
638 | |||
614 | 639 | ||
615 | static void | 640 | static void |
616 | gen_sessionid(struct nfsd4_session *ses) | 641 | gen_sessionid(struct nfsd4_session *ses) |
@@ -832,11 +857,12 @@ static void nfsd4_del_conns(struct nfsd4_session *s) | |||
832 | spin_unlock(&clp->cl_lock); | 857 | spin_unlock(&clp->cl_lock); |
833 | } | 858 | } |
834 | 859 | ||
835 | void free_session(struct kref *kref) | 860 | static void free_session(struct kref *kref) |
836 | { | 861 | { |
837 | struct nfsd4_session *ses; | 862 | struct nfsd4_session *ses; |
838 | int mem; | 863 | int mem; |
839 | 864 | ||
865 | BUG_ON(!spin_is_locked(&client_lock)); | ||
840 | ses = container_of(kref, struct nfsd4_session, se_ref); | 866 | ses = container_of(kref, struct nfsd4_session, se_ref); |
841 | nfsd4_del_conns(ses); | 867 | nfsd4_del_conns(ses); |
842 | spin_lock(&nfsd_drc_lock); | 868 | spin_lock(&nfsd_drc_lock); |
@@ -847,6 +873,13 @@ void free_session(struct kref *kref) | |||
847 | kfree(ses); | 873 | kfree(ses); |
848 | } | 874 | } |
849 | 875 | ||
876 | void nfsd4_put_session(struct nfsd4_session *ses) | ||
877 | { | ||
878 | spin_lock(&client_lock); | ||
879 | nfsd4_put_session_locked(ses); | ||
880 | spin_unlock(&client_lock); | ||
881 | } | ||
882 | |||
850 | static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, struct nfsd4_create_session *cses) | 883 | static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, struct nfsd4_create_session *cses) |
851 | { | 884 | { |
852 | struct nfsd4_session *new; | 885 | struct nfsd4_session *new; |
@@ -894,7 +927,9 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n | |||
894 | status = nfsd4_new_conn_from_crses(rqstp, new); | 927 | status = nfsd4_new_conn_from_crses(rqstp, new); |
895 | /* whoops: benny points out, status is ignored! (err, or bogus) */ | 928 | /* whoops: benny points out, status is ignored! (err, or bogus) */ |
896 | if (status) { | 929 | if (status) { |
930 | spin_lock(&client_lock); | ||
897 | free_session(&new->se_ref); | 931 | free_session(&new->se_ref); |
932 | spin_unlock(&client_lock); | ||
898 | return NULL; | 933 | return NULL; |
899 | } | 934 | } |
900 | if (cses->flags & SESSION4_BACK_CHAN) { | 935 | if (cses->flags & SESSION4_BACK_CHAN) { |
@@ -1006,12 +1041,13 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name) | |||
1006 | static inline void | 1041 | static inline void |
1007 | free_client(struct nfs4_client *clp) | 1042 | free_client(struct nfs4_client *clp) |
1008 | { | 1043 | { |
1044 | BUG_ON(!spin_is_locked(&client_lock)); | ||
1009 | while (!list_empty(&clp->cl_sessions)) { | 1045 | while (!list_empty(&clp->cl_sessions)) { |
1010 | struct nfsd4_session *ses; | 1046 | struct nfsd4_session *ses; |
1011 | ses = list_entry(clp->cl_sessions.next, struct nfsd4_session, | 1047 | ses = list_entry(clp->cl_sessions.next, struct nfsd4_session, |
1012 | se_perclnt); | 1048 | se_perclnt); |
1013 | list_del(&ses->se_perclnt); | 1049 | list_del(&ses->se_perclnt); |
1014 | nfsd4_put_session(ses); | 1050 | nfsd4_put_session_locked(ses); |
1015 | } | 1051 | } |
1016 | if (clp->cl_cred.cr_group_info) | 1052 | if (clp->cl_cred.cr_group_info) |
1017 | put_group_info(clp->cl_cred.cr_group_info); | 1053 | put_group_info(clp->cl_cred.cr_group_info); |
@@ -1138,12 +1174,12 @@ static void gen_clid(struct nfs4_client *clp) | |||
1138 | 1174 | ||
1139 | static void gen_confirm(struct nfs4_client *clp) | 1175 | static void gen_confirm(struct nfs4_client *clp) |
1140 | { | 1176 | { |
1177 | __be32 verf[2]; | ||
1141 | static u32 i; | 1178 | static u32 i; |
1142 | u32 *p; | ||
1143 | 1179 | ||
1144 | p = (u32 *)clp->cl_confirm.data; | 1180 | verf[0] = (__be32)get_seconds(); |
1145 | *p++ = get_seconds(); | 1181 | verf[1] = (__be32)i++; |
1146 | *p++ = i++; | 1182 | memcpy(clp->cl_confirm.data, verf, sizeof(clp->cl_confirm.data)); |
1147 | } | 1183 | } |
1148 | 1184 | ||
1149 | static struct nfs4_stid *find_stateid(struct nfs4_client *cl, stateid_t *t) | 1185 | static struct nfs4_stid *find_stateid(struct nfs4_client *cl, stateid_t *t) |
@@ -1180,7 +1216,9 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir, | |||
1180 | if (princ) { | 1216 | if (princ) { |
1181 | clp->cl_principal = kstrdup(princ, GFP_KERNEL); | 1217 | clp->cl_principal = kstrdup(princ, GFP_KERNEL); |
1182 | if (clp->cl_principal == NULL) { | 1218 | if (clp->cl_principal == NULL) { |
1219 | spin_lock(&client_lock); | ||
1183 | free_client(clp); | 1220 | free_client(clp); |
1221 | spin_unlock(&client_lock); | ||
1184 | return NULL; | 1222 | return NULL; |
1185 | } | 1223 | } |
1186 | } | 1224 | } |
@@ -1347,6 +1385,7 @@ nfsd4_store_cache_entry(struct nfsd4_compoundres *resp) | |||
1347 | slot->sl_opcnt = resp->opcnt; | 1385 | slot->sl_opcnt = resp->opcnt; |
1348 | slot->sl_status = resp->cstate.status; | 1386 | slot->sl_status = resp->cstate.status; |
1349 | 1387 | ||
1388 | slot->sl_flags |= NFSD4_SLOT_INITIALIZED; | ||
1350 | if (nfsd4_not_cached(resp)) { | 1389 | if (nfsd4_not_cached(resp)) { |
1351 | slot->sl_datalen = 0; | 1390 | slot->sl_datalen = 0; |
1352 | return; | 1391 | return; |
@@ -1374,15 +1413,12 @@ nfsd4_enc_sequence_replay(struct nfsd4_compoundargs *args, | |||
1374 | struct nfsd4_op *op; | 1413 | struct nfsd4_op *op; |
1375 | struct nfsd4_slot *slot = resp->cstate.slot; | 1414 | struct nfsd4_slot *slot = resp->cstate.slot; |
1376 | 1415 | ||
1377 | dprintk("--> %s resp->opcnt %d cachethis %u \n", __func__, | ||
1378 | resp->opcnt, resp->cstate.slot->sl_cachethis); | ||
1379 | |||
1380 | /* Encode the replayed sequence operation */ | 1416 | /* Encode the replayed sequence operation */ |
1381 | op = &args->ops[resp->opcnt - 1]; | 1417 | op = &args->ops[resp->opcnt - 1]; |
1382 | nfsd4_encode_operation(resp, op); | 1418 | nfsd4_encode_operation(resp, op); |
1383 | 1419 | ||
1384 | /* Return nfserr_retry_uncached_rep in next operation. */ | 1420 | /* Return nfserr_retry_uncached_rep in next operation. */ |
1385 | if (args->opcnt > 1 && slot->sl_cachethis == 0) { | 1421 | if (args->opcnt > 1 && !(slot->sl_flags & NFSD4_SLOT_CACHETHIS)) { |
1386 | op = &args->ops[resp->opcnt++]; | 1422 | op = &args->ops[resp->opcnt++]; |
1387 | op->status = nfserr_retry_uncached_rep; | 1423 | op->status = nfserr_retry_uncached_rep; |
1388 | nfsd4_encode_operation(resp, op); | 1424 | nfsd4_encode_operation(resp, op); |
@@ -1575,16 +1611,11 @@ check_slot_seqid(u32 seqid, u32 slot_seqid, int slot_inuse) | |||
1575 | else | 1611 | else |
1576 | return nfserr_seq_misordered; | 1612 | return nfserr_seq_misordered; |
1577 | } | 1613 | } |
1578 | /* Normal */ | 1614 | /* Note unsigned 32-bit arithmetic handles wraparound: */ |
1579 | if (likely(seqid == slot_seqid + 1)) | 1615 | if (likely(seqid == slot_seqid + 1)) |
1580 | return nfs_ok; | 1616 | return nfs_ok; |
1581 | /* Replay */ | ||
1582 | if (seqid == slot_seqid) | 1617 | if (seqid == slot_seqid) |
1583 | return nfserr_replay_cache; | 1618 | return nfserr_replay_cache; |
1584 | /* Wraparound */ | ||
1585 | if (seqid == 1 && (slot_seqid + 1) == 0) | ||
1586 | return nfs_ok; | ||
1587 | /* Misordered replay or misordered new request */ | ||
1588 | return nfserr_seq_misordered; | 1619 | return nfserr_seq_misordered; |
1589 | } | 1620 | } |
1590 | 1621 | ||
@@ -1815,9 +1846,10 @@ nfsd4_destroy_session(struct svc_rqst *r, | |||
1815 | nfsd4_probe_callback_sync(ses->se_client); | 1846 | nfsd4_probe_callback_sync(ses->se_client); |
1816 | nfs4_unlock_state(); | 1847 | nfs4_unlock_state(); |
1817 | 1848 | ||
1849 | spin_lock(&client_lock); | ||
1818 | nfsd4_del_conns(ses); | 1850 | nfsd4_del_conns(ses); |
1819 | 1851 | nfsd4_put_session_locked(ses); | |
1820 | nfsd4_put_session(ses); | 1852 | spin_unlock(&client_lock); |
1821 | status = nfs_ok; | 1853 | status = nfs_ok; |
1822 | out: | 1854 | out: |
1823 | dprintk("%s returns %d\n", __func__, ntohl(status)); | 1855 | dprintk("%s returns %d\n", __func__, ntohl(status)); |
@@ -1921,8 +1953,12 @@ nfsd4_sequence(struct svc_rqst *rqstp, | |||
1921 | * sr_highest_slotid and the sr_target_slot id to maxslots */ | 1953 | * sr_highest_slotid and the sr_target_slot id to maxslots */ |
1922 | seq->maxslots = session->se_fchannel.maxreqs; | 1954 | seq->maxslots = session->se_fchannel.maxreqs; |
1923 | 1955 | ||
1924 | status = check_slot_seqid(seq->seqid, slot->sl_seqid, slot->sl_inuse); | 1956 | status = check_slot_seqid(seq->seqid, slot->sl_seqid, |
1957 | slot->sl_flags & NFSD4_SLOT_INUSE); | ||
1925 | if (status == nfserr_replay_cache) { | 1958 | if (status == nfserr_replay_cache) { |
1959 | status = nfserr_seq_misordered; | ||
1960 | if (!(slot->sl_flags & NFSD4_SLOT_INITIALIZED)) | ||
1961 | goto out; | ||
1926 | cstate->slot = slot; | 1962 | cstate->slot = slot; |
1927 | cstate->session = session; | 1963 | cstate->session = session; |
1928 | /* Return the cached reply status and set cstate->status | 1964 | /* Return the cached reply status and set cstate->status |
@@ -1938,9 +1974,12 @@ nfsd4_sequence(struct svc_rqst *rqstp, | |||
1938 | conn = NULL; | 1974 | conn = NULL; |
1939 | 1975 | ||
1940 | /* Success! bump slot seqid */ | 1976 | /* Success! bump slot seqid */ |
1941 | slot->sl_inuse = true; | ||
1942 | slot->sl_seqid = seq->seqid; | 1977 | slot->sl_seqid = seq->seqid; |
1943 | slot->sl_cachethis = seq->cachethis; | 1978 | slot->sl_flags |= NFSD4_SLOT_INUSE; |
1979 | if (seq->cachethis) | ||
1980 | slot->sl_flags |= NFSD4_SLOT_CACHETHIS; | ||
1981 | else | ||
1982 | slot->sl_flags &= ~NFSD4_SLOT_CACHETHIS; | ||
1944 | 1983 | ||
1945 | cstate->slot = slot; | 1984 | cstate->slot = slot; |
1946 | cstate->session = session; | 1985 | cstate->session = session; |
@@ -2030,7 +2069,8 @@ nfsd4_reclaim_complete(struct svc_rqst *rqstp, struct nfsd4_compound_state *csta | |||
2030 | 2069 | ||
2031 | nfs4_lock_state(); | 2070 | nfs4_lock_state(); |
2032 | status = nfserr_complete_already; | 2071 | status = nfserr_complete_already; |
2033 | if (cstate->session->se_client->cl_firststate) | 2072 | if (test_and_set_bit(NFSD4_CLIENT_RECLAIM_COMPLETE, |
2073 | &cstate->session->se_client->cl_flags)) | ||
2034 | goto out; | 2074 | goto out; |
2035 | 2075 | ||
2036 | status = nfserr_stale_clientid; | 2076 | status = nfserr_stale_clientid; |
@@ -2045,7 +2085,7 @@ nfsd4_reclaim_complete(struct svc_rqst *rqstp, struct nfsd4_compound_state *csta | |||
2045 | goto out; | 2085 | goto out; |
2046 | 2086 | ||
2047 | status = nfs_ok; | 2087 | status = nfs_ok; |
2048 | nfsd4_create_clid_dir(cstate->session->se_client); | 2088 | nfsd4_client_record_create(cstate->session->se_client); |
2049 | out: | 2089 | out: |
2050 | nfs4_unlock_state(); | 2090 | nfs4_unlock_state(); |
2051 | return status; | 2091 | return status; |
@@ -2240,7 +2280,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, | |||
2240 | conf = find_confirmed_client_by_str(unconf->cl_recdir, | 2280 | conf = find_confirmed_client_by_str(unconf->cl_recdir, |
2241 | hash); | 2281 | hash); |
2242 | if (conf) { | 2282 | if (conf) { |
2243 | nfsd4_remove_clid_dir(conf); | 2283 | nfsd4_client_record_remove(conf); |
2244 | expire_client(conf); | 2284 | expire_client(conf); |
2245 | } | 2285 | } |
2246 | move_to_confirmed(unconf); | 2286 | move_to_confirmed(unconf); |
@@ -2633,8 +2673,6 @@ nfs4_check_delegmode(struct nfs4_delegation *dp, int flags) | |||
2633 | 2673 | ||
2634 | static int share_access_to_flags(u32 share_access) | 2674 | static int share_access_to_flags(u32 share_access) |
2635 | { | 2675 | { |
2636 | share_access &= ~NFS4_SHARE_WANT_MASK; | ||
2637 | |||
2638 | return share_access == NFS4_SHARE_ACCESS_READ ? RD_STATE : WR_STATE; | 2676 | return share_access == NFS4_SHARE_ACCESS_READ ? RD_STATE : WR_STATE; |
2639 | } | 2677 | } |
2640 | 2678 | ||
@@ -2776,10 +2814,9 @@ nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp, struct svc_fh *c | |||
2776 | 2814 | ||
2777 | 2815 | ||
2778 | static void | 2816 | static void |
2779 | nfs4_set_claim_prev(struct nfsd4_open *open) | 2817 | nfs4_set_claim_prev(struct nfsd4_open *open, bool has_session) |
2780 | { | 2818 | { |
2781 | open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED; | 2819 | open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED; |
2782 | open->op_openowner->oo_owner.so_client->cl_firststate = 1; | ||
2783 | } | 2820 | } |
2784 | 2821 | ||
2785 | /* Should we give out recallable state?: */ | 2822 | /* Should we give out recallable state?: */ |
@@ -2855,6 +2892,27 @@ static int nfs4_set_delegation(struct nfs4_delegation *dp, int flag) | |||
2855 | return 0; | 2892 | return 0; |
2856 | } | 2893 | } |
2857 | 2894 | ||
2895 | static void nfsd4_open_deleg_none_ext(struct nfsd4_open *open, int status) | ||
2896 | { | ||
2897 | open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE_EXT; | ||
2898 | if (status == -EAGAIN) | ||
2899 | open->op_why_no_deleg = WND4_CONTENTION; | ||
2900 | else { | ||
2901 | open->op_why_no_deleg = WND4_RESOURCE; | ||
2902 | switch (open->op_deleg_want) { | ||
2903 | case NFS4_SHARE_WANT_READ_DELEG: | ||
2904 | case NFS4_SHARE_WANT_WRITE_DELEG: | ||
2905 | case NFS4_SHARE_WANT_ANY_DELEG: | ||
2906 | break; | ||
2907 | case NFS4_SHARE_WANT_CANCEL: | ||
2908 | open->op_why_no_deleg = WND4_CANCELLED; | ||
2909 | break; | ||
2910 | case NFS4_SHARE_WANT_NO_DELEG: | ||
2911 | BUG(); /* not supposed to get here */ | ||
2912 | } | ||
2913 | } | ||
2914 | } | ||
2915 | |||
2858 | /* | 2916 | /* |
2859 | * Attempt to hand out a delegation. | 2917 | * Attempt to hand out a delegation. |
2860 | */ | 2918 | */ |
@@ -2864,7 +2922,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_ol_ | |||
2864 | struct nfs4_delegation *dp; | 2922 | struct nfs4_delegation *dp; |
2865 | struct nfs4_openowner *oo = container_of(stp->st_stateowner, struct nfs4_openowner, oo_owner); | 2923 | struct nfs4_openowner *oo = container_of(stp->st_stateowner, struct nfs4_openowner, oo_owner); |
2866 | int cb_up; | 2924 | int cb_up; |
2867 | int status, flag = 0; | 2925 | int status = 0, flag = 0; |
2868 | 2926 | ||
2869 | cb_up = nfsd4_cb_channel_good(oo->oo_owner.so_client); | 2927 | cb_up = nfsd4_cb_channel_good(oo->oo_owner.so_client); |
2870 | flag = NFS4_OPEN_DELEGATE_NONE; | 2928 | flag = NFS4_OPEN_DELEGATE_NONE; |
@@ -2905,11 +2963,16 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_ol_ | |||
2905 | dprintk("NFSD: delegation stateid=" STATEID_FMT "\n", | 2963 | dprintk("NFSD: delegation stateid=" STATEID_FMT "\n", |
2906 | STATEID_VAL(&dp->dl_stid.sc_stateid)); | 2964 | STATEID_VAL(&dp->dl_stid.sc_stateid)); |
2907 | out: | 2965 | out: |
2908 | if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS | ||
2909 | && flag == NFS4_OPEN_DELEGATE_NONE | ||
2910 | && open->op_delegate_type != NFS4_OPEN_DELEGATE_NONE) | ||
2911 | dprintk("NFSD: WARNING: refusing delegation reclaim\n"); | ||
2912 | open->op_delegate_type = flag; | 2966 | open->op_delegate_type = flag; |
2967 | if (flag == NFS4_OPEN_DELEGATE_NONE) { | ||
2968 | if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS && | ||
2969 | open->op_delegate_type != NFS4_OPEN_DELEGATE_NONE) | ||
2970 | dprintk("NFSD: WARNING: refusing delegation reclaim\n"); | ||
2971 | |||
2972 | /* 4.1 client asking for a delegation? */ | ||
2973 | if (open->op_deleg_want) | ||
2974 | nfsd4_open_deleg_none_ext(open, status); | ||
2975 | } | ||
2913 | return; | 2976 | return; |
2914 | out_free: | 2977 | out_free: |
2915 | nfs4_put_delegation(dp); | 2978 | nfs4_put_delegation(dp); |
@@ -2918,6 +2981,24 @@ out_no_deleg: | |||
2918 | goto out; | 2981 | goto out; |
2919 | } | 2982 | } |
2920 | 2983 | ||
2984 | static void nfsd4_deleg_xgrade_none_ext(struct nfsd4_open *open, | ||
2985 | struct nfs4_delegation *dp) | ||
2986 | { | ||
2987 | if (open->op_deleg_want == NFS4_SHARE_WANT_READ_DELEG && | ||
2988 | dp->dl_type == NFS4_OPEN_DELEGATE_WRITE) { | ||
2989 | open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE_EXT; | ||
2990 | open->op_why_no_deleg = WND4_NOT_SUPP_DOWNGRADE; | ||
2991 | } else if (open->op_deleg_want == NFS4_SHARE_WANT_WRITE_DELEG && | ||
2992 | dp->dl_type == NFS4_OPEN_DELEGATE_WRITE) { | ||
2993 | open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE_EXT; | ||
2994 | open->op_why_no_deleg = WND4_NOT_SUPP_UPGRADE; | ||
2995 | } | ||
2996 | /* Otherwise the client must be confused wanting a delegation | ||
2997 | * it already has, therefore we don't return | ||
2998 | * NFS4_OPEN_DELEGATE_NONE_EXT and reason. | ||
2999 | */ | ||
3000 | } | ||
3001 | |||
2921 | /* | 3002 | /* |
2922 | * called with nfs4_lock_state() held. | 3003 | * called with nfs4_lock_state() held. |
2923 | */ | 3004 | */ |
@@ -2979,24 +3060,36 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf | |||
2979 | update_stateid(&stp->st_stid.sc_stateid); | 3060 | update_stateid(&stp->st_stid.sc_stateid); |
2980 | memcpy(&open->op_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t)); | 3061 | memcpy(&open->op_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t)); |
2981 | 3062 | ||
2982 | if (nfsd4_has_session(&resp->cstate)) | 3063 | if (nfsd4_has_session(&resp->cstate)) { |
2983 | open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED; | 3064 | open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED; |
2984 | 3065 | ||
3066 | if (open->op_deleg_want & NFS4_SHARE_WANT_NO_DELEG) { | ||
3067 | open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE_EXT; | ||
3068 | open->op_why_no_deleg = WND4_NOT_WANTED; | ||
3069 | goto nodeleg; | ||
3070 | } | ||
3071 | } | ||
3072 | |||
2985 | /* | 3073 | /* |
2986 | * Attempt to hand out a delegation. No error return, because the | 3074 | * Attempt to hand out a delegation. No error return, because the |
2987 | * OPEN succeeds even if we fail. | 3075 | * OPEN succeeds even if we fail. |
2988 | */ | 3076 | */ |
2989 | nfs4_open_delegation(current_fh, open, stp); | 3077 | nfs4_open_delegation(current_fh, open, stp); |
2990 | 3078 | nodeleg: | |
2991 | status = nfs_ok; | 3079 | status = nfs_ok; |
2992 | 3080 | ||
2993 | dprintk("%s: stateid=" STATEID_FMT "\n", __func__, | 3081 | dprintk("%s: stateid=" STATEID_FMT "\n", __func__, |
2994 | STATEID_VAL(&stp->st_stid.sc_stateid)); | 3082 | STATEID_VAL(&stp->st_stid.sc_stateid)); |
2995 | out: | 3083 | out: |
3084 | /* 4.1 client trying to upgrade/downgrade delegation? */ | ||
3085 | if (open->op_delegate_type == NFS4_OPEN_DELEGATE_NONE && dp && | ||
3086 | open->op_deleg_want) | ||
3087 | nfsd4_deleg_xgrade_none_ext(open, dp); | ||
3088 | |||
2996 | if (fp) | 3089 | if (fp) |
2997 | put_nfs4_file(fp); | 3090 | put_nfs4_file(fp); |
2998 | if (status == 0 && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) | 3091 | if (status == 0 && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) |
2999 | nfs4_set_claim_prev(open); | 3092 | nfs4_set_claim_prev(open, nfsd4_has_session(&resp->cstate)); |
3000 | /* | 3093 | /* |
3001 | * To finish the open response, we just need to set the rflags. | 3094 | * To finish the open response, we just need to set the rflags. |
3002 | */ | 3095 | */ |
@@ -3066,7 +3159,7 @@ static void | |||
3066 | nfsd4_end_grace(void) | 3159 | nfsd4_end_grace(void) |
3067 | { | 3160 | { |
3068 | dprintk("NFSD: end of grace period\n"); | 3161 | dprintk("NFSD: end of grace period\n"); |
3069 | nfsd4_recdir_purge_old(); | 3162 | nfsd4_record_grace_done(&init_net, boot_time); |
3070 | locks_end_grace(&nfsd4_manager); | 3163 | locks_end_grace(&nfsd4_manager); |
3071 | /* | 3164 | /* |
3072 | * Now that every NFSv4 client has had the chance to recover and | 3165 | * Now that every NFSv4 client has had the chance to recover and |
@@ -3115,7 +3208,7 @@ nfs4_laundromat(void) | |||
3115 | clp = list_entry(pos, struct nfs4_client, cl_lru); | 3208 | clp = list_entry(pos, struct nfs4_client, cl_lru); |
3116 | dprintk("NFSD: purging unused client (clientid %08x)\n", | 3209 | dprintk("NFSD: purging unused client (clientid %08x)\n", |
3117 | clp->cl_clientid.cl_id); | 3210 | clp->cl_clientid.cl_id); |
3118 | nfsd4_remove_clid_dir(clp); | 3211 | nfsd4_client_record_remove(clp); |
3119 | expire_client(clp); | 3212 | expire_client(clp); |
3120 | } | 3213 | } |
3121 | spin_lock(&recall_lock); | 3214 | spin_lock(&recall_lock); |
@@ -3400,7 +3493,14 @@ __be32 | |||
3400 | nfsd4_test_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | 3493 | nfsd4_test_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, |
3401 | struct nfsd4_test_stateid *test_stateid) | 3494 | struct nfsd4_test_stateid *test_stateid) |
3402 | { | 3495 | { |
3403 | /* real work is done during encoding */ | 3496 | struct nfsd4_test_stateid_id *stateid; |
3497 | struct nfs4_client *cl = cstate->session->se_client; | ||
3498 | |||
3499 | nfs4_lock_state(); | ||
3500 | list_for_each_entry(stateid, &test_stateid->ts_stateid_list, ts_id_list) | ||
3501 | stateid->ts_id_status = nfs4_validate_stateid(cl, &stateid->ts_id_stateid); | ||
3502 | nfs4_unlock_state(); | ||
3503 | |||
3404 | return nfs_ok; | 3504 | return nfs_ok; |
3405 | } | 3505 | } |
3406 | 3506 | ||
@@ -3539,7 +3639,7 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
3539 | dprintk("NFSD: %s: success, seqid=%d stateid=" STATEID_FMT "\n", | 3639 | dprintk("NFSD: %s: success, seqid=%d stateid=" STATEID_FMT "\n", |
3540 | __func__, oc->oc_seqid, STATEID_VAL(&stp->st_stid.sc_stateid)); | 3640 | __func__, oc->oc_seqid, STATEID_VAL(&stp->st_stid.sc_stateid)); |
3541 | 3641 | ||
3542 | nfsd4_create_clid_dir(oo->oo_owner.so_client); | 3642 | nfsd4_client_record_create(oo->oo_owner.so_client); |
3543 | status = nfs_ok; | 3643 | status = nfs_ok; |
3544 | out: | 3644 | out: |
3545 | if (!cstate->replay_owner) | 3645 | if (!cstate->replay_owner) |
@@ -3596,7 +3696,9 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, | |||
3596 | cstate->current_fh.fh_dentry->d_name.name); | 3696 | cstate->current_fh.fh_dentry->d_name.name); |
3597 | 3697 | ||
3598 | /* We don't yet support WANT bits: */ | 3698 | /* We don't yet support WANT bits: */ |
3599 | od->od_share_access &= NFS4_SHARE_ACCESS_MASK; | 3699 | if (od->od_deleg_want) |
3700 | dprintk("NFSD: %s: od_deleg_want=0x%x ignored\n", __func__, | ||
3701 | od->od_deleg_want); | ||
3600 | 3702 | ||
3601 | nfs4_lock_state(); | 3703 | nfs4_lock_state(); |
3602 | status = nfs4_preprocess_confirmed_seqid_op(cstate, od->od_seqid, | 3704 | status = nfs4_preprocess_confirmed_seqid_op(cstate, od->od_seqid, |
@@ -4353,7 +4455,9 @@ nfs4_has_reclaimed_state(const char *name, bool use_exchange_id) | |||
4353 | struct nfs4_client *clp; | 4455 | struct nfs4_client *clp; |
4354 | 4456 | ||
4355 | clp = find_confirmed_client_by_str(name, strhashval); | 4457 | clp = find_confirmed_client_by_str(name, strhashval); |
4356 | return clp ? 1 : 0; | 4458 | if (!clp) |
4459 | return 0; | ||
4460 | return test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); | ||
4357 | } | 4461 | } |
4358 | 4462 | ||
4359 | /* | 4463 | /* |
@@ -4377,7 +4481,7 @@ nfs4_client_to_reclaim(const char *name) | |||
4377 | return 1; | 4481 | return 1; |
4378 | } | 4482 | } |
4379 | 4483 | ||
4380 | static void | 4484 | void |
4381 | nfs4_release_reclaim(void) | 4485 | nfs4_release_reclaim(void) |
4382 | { | 4486 | { |
4383 | struct nfs4_client_reclaim *crp = NULL; | 4487 | struct nfs4_client_reclaim *crp = NULL; |
@@ -4397,19 +4501,12 @@ nfs4_release_reclaim(void) | |||
4397 | 4501 | ||
4398 | /* | 4502 | /* |
4399 | * called from OPEN, CLAIM_PREVIOUS with a new clientid. */ | 4503 | * called from OPEN, CLAIM_PREVIOUS with a new clientid. */ |
4400 | static struct nfs4_client_reclaim * | 4504 | struct nfs4_client_reclaim * |
4401 | nfs4_find_reclaim_client(clientid_t *clid) | 4505 | nfsd4_find_reclaim_client(struct nfs4_client *clp) |
4402 | { | 4506 | { |
4403 | unsigned int strhashval; | 4507 | unsigned int strhashval; |
4404 | struct nfs4_client *clp; | ||
4405 | struct nfs4_client_reclaim *crp = NULL; | 4508 | struct nfs4_client_reclaim *crp = NULL; |
4406 | 4509 | ||
4407 | |||
4408 | /* find clientid in conf_id_hashtbl */ | ||
4409 | clp = find_confirmed_client(clid); | ||
4410 | if (clp == NULL) | ||
4411 | return NULL; | ||
4412 | |||
4413 | dprintk("NFSD: nfs4_find_reclaim_client for %.*s with recdir %s\n", | 4510 | dprintk("NFSD: nfs4_find_reclaim_client for %.*s with recdir %s\n", |
4414 | clp->cl_name.len, clp->cl_name.data, | 4511 | clp->cl_name.len, clp->cl_name.data, |
4415 | clp->cl_recdir); | 4512 | clp->cl_recdir); |
@@ -4430,7 +4527,14 @@ nfs4_find_reclaim_client(clientid_t *clid) | |||
4430 | __be32 | 4527 | __be32 |
4431 | nfs4_check_open_reclaim(clientid_t *clid) | 4528 | nfs4_check_open_reclaim(clientid_t *clid) |
4432 | { | 4529 | { |
4433 | return nfs4_find_reclaim_client(clid) ? nfs_ok : nfserr_reclaim_bad; | 4530 | struct nfs4_client *clp; |
4531 | |||
4532 | /* find clientid in conf_id_hashtbl */ | ||
4533 | clp = find_confirmed_client(clid); | ||
4534 | if (clp == NULL) | ||
4535 | return nfserr_reclaim_bad; | ||
4536 | |||
4537 | return nfsd4_client_record_check(clp) ? nfserr_reclaim_bad : nfs_ok; | ||
4434 | } | 4538 | } |
4435 | 4539 | ||
4436 | #ifdef CONFIG_NFSD_FAULT_INJECTION | 4540 | #ifdef CONFIG_NFSD_FAULT_INJECTION |
@@ -4442,7 +4546,7 @@ void nfsd_forget_clients(u64 num) | |||
4442 | 4546 | ||
4443 | nfs4_lock_state(); | 4547 | nfs4_lock_state(); |
4444 | list_for_each_entry_safe(clp, next, &client_lru, cl_lru) { | 4548 | list_for_each_entry_safe(clp, next, &client_lru, cl_lru) { |
4445 | nfsd4_remove_clid_dir(clp); | 4549 | nfsd4_client_record_remove(clp); |
4446 | expire_client(clp); | 4550 | expire_client(clp); |
4447 | if (++count == num) | 4551 | if (++count == num) |
4448 | break; | 4552 | break; |
@@ -4577,19 +4681,6 @@ nfs4_state_init(void) | |||
4577 | reclaim_str_hashtbl_size = 0; | 4681 | reclaim_str_hashtbl_size = 0; |
4578 | } | 4682 | } |
4579 | 4683 | ||
4580 | static void | ||
4581 | nfsd4_load_reboot_recovery_data(void) | ||
4582 | { | ||
4583 | int status; | ||
4584 | |||
4585 | nfs4_lock_state(); | ||
4586 | nfsd4_init_recdir(); | ||
4587 | status = nfsd4_recdir_load(); | ||
4588 | nfs4_unlock_state(); | ||
4589 | if (status) | ||
4590 | printk("NFSD: Failure reading reboot recovery data\n"); | ||
4591 | } | ||
4592 | |||
4593 | /* | 4684 | /* |
4594 | * Since the lifetime of a delegation isn't limited to that of an open, a | 4685 | * Since the lifetime of a delegation isn't limited to that of an open, a |
4595 | * client may quite reasonably hang on to a delegation as long as it has | 4686 | * client may quite reasonably hang on to a delegation as long as it has |
@@ -4613,21 +4704,34 @@ set_max_delegations(void) | |||
4613 | 4704 | ||
4614 | /* initialization to perform when the nfsd service is started: */ | 4705 | /* initialization to perform when the nfsd service is started: */ |
4615 | 4706 | ||
4616 | static int | 4707 | int |
4617 | __nfs4_state_start(void) | 4708 | nfs4_state_start(void) |
4618 | { | 4709 | { |
4619 | int ret; | 4710 | int ret; |
4620 | 4711 | ||
4712 | /* | ||
4713 | * FIXME: For now, we hang most of the pernet global stuff off of | ||
4714 | * init_net until nfsd is fully containerized. Eventually, we'll | ||
4715 | * need to pass a net pointer into this function, take a reference | ||
4716 | * to that instead and then do most of the rest of this on a per-net | ||
4717 | * basis. | ||
4718 | */ | ||
4719 | get_net(&init_net); | ||
4720 | nfsd4_client_tracking_init(&init_net); | ||
4621 | boot_time = get_seconds(); | 4721 | boot_time = get_seconds(); |
4622 | locks_start_grace(&nfsd4_manager); | 4722 | locks_start_grace(&nfsd4_manager); |
4623 | printk(KERN_INFO "NFSD: starting %ld-second grace period\n", | 4723 | printk(KERN_INFO "NFSD: starting %ld-second grace period\n", |
4624 | nfsd4_grace); | 4724 | nfsd4_grace); |
4625 | ret = set_callback_cred(); | 4725 | ret = set_callback_cred(); |
4626 | if (ret) | 4726 | if (ret) { |
4627 | return -ENOMEM; | 4727 | ret = -ENOMEM; |
4728 | goto out_recovery; | ||
4729 | } | ||
4628 | laundry_wq = create_singlethread_workqueue("nfsd4"); | 4730 | laundry_wq = create_singlethread_workqueue("nfsd4"); |
4629 | if (laundry_wq == NULL) | 4731 | if (laundry_wq == NULL) { |
4630 | return -ENOMEM; | 4732 | ret = -ENOMEM; |
4733 | goto out_recovery; | ||
4734 | } | ||
4631 | ret = nfsd4_create_callback_queue(); | 4735 | ret = nfsd4_create_callback_queue(); |
4632 | if (ret) | 4736 | if (ret) |
4633 | goto out_free_laundry; | 4737 | goto out_free_laundry; |
@@ -4636,16 +4740,12 @@ __nfs4_state_start(void) | |||
4636 | return 0; | 4740 | return 0; |
4637 | out_free_laundry: | 4741 | out_free_laundry: |
4638 | destroy_workqueue(laundry_wq); | 4742 | destroy_workqueue(laundry_wq); |
4743 | out_recovery: | ||
4744 | nfsd4_client_tracking_exit(&init_net); | ||
4745 | put_net(&init_net); | ||
4639 | return ret; | 4746 | return ret; |
4640 | } | 4747 | } |
4641 | 4748 | ||
4642 | int | ||
4643 | nfs4_state_start(void) | ||
4644 | { | ||
4645 | nfsd4_load_reboot_recovery_data(); | ||
4646 | return __nfs4_state_start(); | ||
4647 | } | ||
4648 | |||
4649 | static void | 4749 | static void |
4650 | __nfs4_state_shutdown(void) | 4750 | __nfs4_state_shutdown(void) |
4651 | { | 4751 | { |
@@ -4676,7 +4776,8 @@ __nfs4_state_shutdown(void) | |||
4676 | unhash_delegation(dp); | 4776 | unhash_delegation(dp); |
4677 | } | 4777 | } |
4678 | 4778 | ||
4679 | nfsd4_shutdown_recdir(); | 4779 | nfsd4_client_tracking_exit(&init_net); |
4780 | put_net(&init_net); | ||
4680 | } | 4781 | } |
4681 | 4782 | ||
4682 | void | 4783 | void |
@@ -4686,8 +4787,108 @@ nfs4_state_shutdown(void) | |||
4686 | destroy_workqueue(laundry_wq); | 4787 | destroy_workqueue(laundry_wq); |
4687 | locks_end_grace(&nfsd4_manager); | 4788 | locks_end_grace(&nfsd4_manager); |
4688 | nfs4_lock_state(); | 4789 | nfs4_lock_state(); |
4689 | nfs4_release_reclaim(); | ||
4690 | __nfs4_state_shutdown(); | 4790 | __nfs4_state_shutdown(); |
4691 | nfs4_unlock_state(); | 4791 | nfs4_unlock_state(); |
4692 | nfsd4_destroy_callback_queue(); | 4792 | nfsd4_destroy_callback_queue(); |
4693 | } | 4793 | } |
4794 | |||
4795 | static void | ||
4796 | get_stateid(struct nfsd4_compound_state *cstate, stateid_t *stateid) | ||
4797 | { | ||
4798 | if (HAS_STATE_ID(cstate, CURRENT_STATE_ID_FLAG) && CURRENT_STATEID(stateid)) | ||
4799 | memcpy(stateid, &cstate->current_stateid, sizeof(stateid_t)); | ||
4800 | } | ||
4801 | |||
4802 | static void | ||
4803 | put_stateid(struct nfsd4_compound_state *cstate, stateid_t *stateid) | ||
4804 | { | ||
4805 | if (cstate->minorversion) { | ||
4806 | memcpy(&cstate->current_stateid, stateid, sizeof(stateid_t)); | ||
4807 | SET_STATE_ID(cstate, CURRENT_STATE_ID_FLAG); | ||
4808 | } | ||
4809 | } | ||
4810 | |||
4811 | void | ||
4812 | clear_current_stateid(struct nfsd4_compound_state *cstate) | ||
4813 | { | ||
4814 | CLEAR_STATE_ID(cstate, CURRENT_STATE_ID_FLAG); | ||
4815 | } | ||
4816 | |||
4817 | /* | ||
4818 | * functions to set current state id | ||
4819 | */ | ||
4820 | void | ||
4821 | nfsd4_set_opendowngradestateid(struct nfsd4_compound_state *cstate, struct nfsd4_open_downgrade *odp) | ||
4822 | { | ||
4823 | put_stateid(cstate, &odp->od_stateid); | ||
4824 | } | ||
4825 | |||
4826 | void | ||
4827 | nfsd4_set_openstateid(struct nfsd4_compound_state *cstate, struct nfsd4_open *open) | ||
4828 | { | ||
4829 | put_stateid(cstate, &open->op_stateid); | ||
4830 | } | ||
4831 | |||
4832 | void | ||
4833 | nfsd4_set_closestateid(struct nfsd4_compound_state *cstate, struct nfsd4_close *close) | ||
4834 | { | ||
4835 | put_stateid(cstate, &close->cl_stateid); | ||
4836 | } | ||
4837 | |||
4838 | void | ||
4839 | nfsd4_set_lockstateid(struct nfsd4_compound_state *cstate, struct nfsd4_lock *lock) | ||
4840 | { | ||
4841 | put_stateid(cstate, &lock->lk_resp_stateid); | ||
4842 | } | ||
4843 | |||
4844 | /* | ||
4845 | * functions to consume current state id | ||
4846 | */ | ||
4847 | |||
4848 | void | ||
4849 | nfsd4_get_opendowngradestateid(struct nfsd4_compound_state *cstate, struct nfsd4_open_downgrade *odp) | ||
4850 | { | ||
4851 | get_stateid(cstate, &odp->od_stateid); | ||
4852 | } | ||
4853 | |||
4854 | void | ||
4855 | nfsd4_get_delegreturnstateid(struct nfsd4_compound_state *cstate, struct nfsd4_delegreturn *drp) | ||
4856 | { | ||
4857 | get_stateid(cstate, &drp->dr_stateid); | ||
4858 | } | ||
4859 | |||
4860 | void | ||
4861 | nfsd4_get_freestateid(struct nfsd4_compound_state *cstate, struct nfsd4_free_stateid *fsp) | ||
4862 | { | ||
4863 | get_stateid(cstate, &fsp->fr_stateid); | ||
4864 | } | ||
4865 | |||
4866 | void | ||
4867 | nfsd4_get_setattrstateid(struct nfsd4_compound_state *cstate, struct nfsd4_setattr *setattr) | ||
4868 | { | ||
4869 | get_stateid(cstate, &setattr->sa_stateid); | ||
4870 | } | ||
4871 | |||
4872 | void | ||
4873 | nfsd4_get_closestateid(struct nfsd4_compound_state *cstate, struct nfsd4_close *close) | ||
4874 | { | ||
4875 | get_stateid(cstate, &close->cl_stateid); | ||
4876 | } | ||
4877 | |||
4878 | void | ||
4879 | nfsd4_get_lockustateid(struct nfsd4_compound_state *cstate, struct nfsd4_locku *locku) | ||
4880 | { | ||
4881 | get_stateid(cstate, &locku->lu_stateid); | ||
4882 | } | ||
4883 | |||
4884 | void | ||
4885 | nfsd4_get_readstateid(struct nfsd4_compound_state *cstate, struct nfsd4_read *read) | ||
4886 | { | ||
4887 | get_stateid(cstate, &read->rd_stateid); | ||
4888 | } | ||
4889 | |||
4890 | void | ||
4891 | nfsd4_get_writestateid(struct nfsd4_compound_state *cstate, struct nfsd4_write *write) | ||
4892 | { | ||
4893 | get_stateid(cstate, &write->wr_stateid); | ||
4894 | } | ||
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 0ec5a1b9700e..bcd8904ab1e3 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c | |||
@@ -133,22 +133,6 @@ xdr_error: \ | |||
133 | } \ | 133 | } \ |
134 | } while (0) | 134 | } while (0) |
135 | 135 | ||
136 | static void save_buf(struct nfsd4_compoundargs *argp, struct nfsd4_saved_compoundargs *savep) | ||
137 | { | ||
138 | savep->p = argp->p; | ||
139 | savep->end = argp->end; | ||
140 | savep->pagelen = argp->pagelen; | ||
141 | savep->pagelist = argp->pagelist; | ||
142 | } | ||
143 | |||
144 | static void restore_buf(struct nfsd4_compoundargs *argp, struct nfsd4_saved_compoundargs *savep) | ||
145 | { | ||
146 | argp->p = savep->p; | ||
147 | argp->end = savep->end; | ||
148 | argp->pagelen = savep->pagelen; | ||
149 | argp->pagelist = savep->pagelist; | ||
150 | } | ||
151 | |||
152 | static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes) | 136 | static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes) |
153 | { | 137 | { |
154 | /* We want more bytes than seem to be available. | 138 | /* We want more bytes than seem to be available. |
@@ -638,14 +622,18 @@ nfsd4_decode_lookup(struct nfsd4_compoundargs *argp, struct nfsd4_lookup *lookup | |||
638 | DECODE_TAIL; | 622 | DECODE_TAIL; |
639 | } | 623 | } |
640 | 624 | ||
641 | static __be32 nfsd4_decode_share_access(struct nfsd4_compoundargs *argp, u32 *x) | 625 | static __be32 nfsd4_decode_share_access(struct nfsd4_compoundargs *argp, u32 *share_access, u32 *deleg_want, u32 *deleg_when) |
642 | { | 626 | { |
643 | __be32 *p; | 627 | __be32 *p; |
644 | u32 w; | 628 | u32 w; |
645 | 629 | ||
646 | READ_BUF(4); | 630 | READ_BUF(4); |
647 | READ32(w); | 631 | READ32(w); |
648 | *x = w; | 632 | *share_access = w & NFS4_SHARE_ACCESS_MASK; |
633 | *deleg_want = w & NFS4_SHARE_WANT_MASK; | ||
634 | if (deleg_when) | ||
635 | *deleg_when = w & NFS4_SHARE_WHEN_MASK; | ||
636 | |||
649 | switch (w & NFS4_SHARE_ACCESS_MASK) { | 637 | switch (w & NFS4_SHARE_ACCESS_MASK) { |
650 | case NFS4_SHARE_ACCESS_READ: | 638 | case NFS4_SHARE_ACCESS_READ: |
651 | case NFS4_SHARE_ACCESS_WRITE: | 639 | case NFS4_SHARE_ACCESS_WRITE: |
@@ -673,6 +661,9 @@ static __be32 nfsd4_decode_share_access(struct nfsd4_compoundargs *argp, u32 *x) | |||
673 | w &= ~NFS4_SHARE_WANT_MASK; | 661 | w &= ~NFS4_SHARE_WANT_MASK; |
674 | if (!w) | 662 | if (!w) |
675 | return nfs_ok; | 663 | return nfs_ok; |
664 | |||
665 | if (!deleg_when) /* open_downgrade */ | ||
666 | return nfserr_inval; | ||
676 | switch (w) { | 667 | switch (w) { |
677 | case NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL: | 668 | case NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL: |
678 | case NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED: | 669 | case NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED: |
@@ -719,6 +710,7 @@ static __be32 | |||
719 | nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) | 710 | nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) |
720 | { | 711 | { |
721 | DECODE_HEAD; | 712 | DECODE_HEAD; |
713 | u32 dummy; | ||
722 | 714 | ||
723 | memset(open->op_bmval, 0, sizeof(open->op_bmval)); | 715 | memset(open->op_bmval, 0, sizeof(open->op_bmval)); |
724 | open->op_iattr.ia_valid = 0; | 716 | open->op_iattr.ia_valid = 0; |
@@ -727,7 +719,9 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) | |||
727 | /* seqid, share_access, share_deny, clientid, ownerlen */ | 719 | /* seqid, share_access, share_deny, clientid, ownerlen */ |
728 | READ_BUF(4); | 720 | READ_BUF(4); |
729 | READ32(open->op_seqid); | 721 | READ32(open->op_seqid); |
730 | status = nfsd4_decode_share_access(argp, &open->op_share_access); | 722 | /* decode, yet ignore deleg_when until supported */ |
723 | status = nfsd4_decode_share_access(argp, &open->op_share_access, | ||
724 | &open->op_deleg_want, &dummy); | ||
731 | if (status) | 725 | if (status) |
732 | goto xdr_error; | 726 | goto xdr_error; |
733 | status = nfsd4_decode_share_deny(argp, &open->op_share_deny); | 727 | status = nfsd4_decode_share_deny(argp, &open->op_share_deny); |
@@ -755,14 +749,14 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) | |||
755 | goto out; | 749 | goto out; |
756 | break; | 750 | break; |
757 | case NFS4_CREATE_EXCLUSIVE: | 751 | case NFS4_CREATE_EXCLUSIVE: |
758 | READ_BUF(8); | 752 | READ_BUF(NFS4_VERIFIER_SIZE); |
759 | COPYMEM(open->op_verf.data, 8); | 753 | COPYMEM(open->op_verf.data, NFS4_VERIFIER_SIZE); |
760 | break; | 754 | break; |
761 | case NFS4_CREATE_EXCLUSIVE4_1: | 755 | case NFS4_CREATE_EXCLUSIVE4_1: |
762 | if (argp->minorversion < 1) | 756 | if (argp->minorversion < 1) |
763 | goto xdr_error; | 757 | goto xdr_error; |
764 | READ_BUF(8); | 758 | READ_BUF(NFS4_VERIFIER_SIZE); |
765 | COPYMEM(open->op_verf.data, 8); | 759 | COPYMEM(open->op_verf.data, NFS4_VERIFIER_SIZE); |
766 | status = nfsd4_decode_fattr(argp, open->op_bmval, | 760 | status = nfsd4_decode_fattr(argp, open->op_bmval, |
767 | &open->op_iattr, &open->op_acl); | 761 | &open->op_iattr, &open->op_acl); |
768 | if (status) | 762 | if (status) |
@@ -848,7 +842,8 @@ nfsd4_decode_open_downgrade(struct nfsd4_compoundargs *argp, struct nfsd4_open_d | |||
848 | return status; | 842 | return status; |
849 | READ_BUF(4); | 843 | READ_BUF(4); |
850 | READ32(open_down->od_seqid); | 844 | READ32(open_down->od_seqid); |
851 | status = nfsd4_decode_share_access(argp, &open_down->od_share_access); | 845 | status = nfsd4_decode_share_access(argp, &open_down->od_share_access, |
846 | &open_down->od_deleg_want, NULL); | ||
852 | if (status) | 847 | if (status) |
853 | return status; | 848 | return status; |
854 | status = nfsd4_decode_share_deny(argp, &open_down->od_share_deny); | 849 | status = nfsd4_decode_share_deny(argp, &open_down->od_share_deny); |
@@ -994,8 +989,8 @@ nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, struct nfsd4_setclient | |||
994 | { | 989 | { |
995 | DECODE_HEAD; | 990 | DECODE_HEAD; |
996 | 991 | ||
997 | READ_BUF(8); | 992 | READ_BUF(NFS4_VERIFIER_SIZE); |
998 | COPYMEM(setclientid->se_verf.data, 8); | 993 | COPYMEM(setclientid->se_verf.data, NFS4_VERIFIER_SIZE); |
999 | 994 | ||
1000 | status = nfsd4_decode_opaque(argp, &setclientid->se_name); | 995 | status = nfsd4_decode_opaque(argp, &setclientid->se_name); |
1001 | if (status) | 996 | if (status) |
@@ -1020,9 +1015,9 @@ nfsd4_decode_setclientid_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_s | |||
1020 | { | 1015 | { |
1021 | DECODE_HEAD; | 1016 | DECODE_HEAD; |
1022 | 1017 | ||
1023 | READ_BUF(8 + sizeof(nfs4_verifier)); | 1018 | READ_BUF(8 + NFS4_VERIFIER_SIZE); |
1024 | COPYMEM(&scd_c->sc_clientid, 8); | 1019 | COPYMEM(&scd_c->sc_clientid, 8); |
1025 | COPYMEM(&scd_c->sc_confirm, sizeof(nfs4_verifier)); | 1020 | COPYMEM(&scd_c->sc_confirm, NFS4_VERIFIER_SIZE); |
1026 | 1021 | ||
1027 | DECODE_TAIL; | 1022 | DECODE_TAIL; |
1028 | } | 1023 | } |
@@ -1385,26 +1380,29 @@ nfsd4_decode_sequence(struct nfsd4_compoundargs *argp, | |||
1385 | static __be32 | 1380 | static __be32 |
1386 | nfsd4_decode_test_stateid(struct nfsd4_compoundargs *argp, struct nfsd4_test_stateid *test_stateid) | 1381 | nfsd4_decode_test_stateid(struct nfsd4_compoundargs *argp, struct nfsd4_test_stateid *test_stateid) |
1387 | { | 1382 | { |
1388 | unsigned int nbytes; | ||
1389 | stateid_t si; | ||
1390 | int i; | 1383 | int i; |
1391 | __be32 *p; | 1384 | __be32 *p, status; |
1392 | __be32 status; | 1385 | struct nfsd4_test_stateid_id *stateid; |
1393 | 1386 | ||
1394 | READ_BUF(4); | 1387 | READ_BUF(4); |
1395 | test_stateid->ts_num_ids = ntohl(*p++); | 1388 | test_stateid->ts_num_ids = ntohl(*p++); |
1396 | 1389 | ||
1397 | nbytes = test_stateid->ts_num_ids * sizeof(stateid_t); | 1390 | INIT_LIST_HEAD(&test_stateid->ts_stateid_list); |
1398 | if (nbytes > (u32)((char *)argp->end - (char *)argp->p)) | ||
1399 | goto xdr_error; | ||
1400 | |||
1401 | test_stateid->ts_saved_args = argp; | ||
1402 | save_buf(argp, &test_stateid->ts_savedp); | ||
1403 | 1391 | ||
1404 | for (i = 0; i < test_stateid->ts_num_ids; i++) { | 1392 | for (i = 0; i < test_stateid->ts_num_ids; i++) { |
1405 | status = nfsd4_decode_stateid(argp, &si); | 1393 | stateid = kmalloc(sizeof(struct nfsd4_test_stateid_id), GFP_KERNEL); |
1394 | if (!stateid) { | ||
1395 | status = PTR_ERR(stateid); | ||
1396 | goto out; | ||
1397 | } | ||
1398 | |||
1399 | defer_free(argp, kfree, stateid); | ||
1400 | INIT_LIST_HEAD(&stateid->ts_id_list); | ||
1401 | list_add_tail(&stateid->ts_id_list, &test_stateid->ts_stateid_list); | ||
1402 | |||
1403 | status = nfsd4_decode_stateid(argp, &stateid->ts_id_stateid); | ||
1406 | if (status) | 1404 | if (status) |
1407 | return status; | 1405 | goto out; |
1408 | } | 1406 | } |
1409 | 1407 | ||
1410 | status = 0; | 1408 | status = 0; |
@@ -2661,8 +2659,8 @@ nfsd4_encode_commit(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_ | |||
2661 | __be32 *p; | 2659 | __be32 *p; |
2662 | 2660 | ||
2663 | if (!nfserr) { | 2661 | if (!nfserr) { |
2664 | RESERVE_SPACE(8); | 2662 | RESERVE_SPACE(NFS4_VERIFIER_SIZE); |
2665 | WRITEMEM(commit->co_verf.data, 8); | 2663 | WRITEMEM(commit->co_verf.data, NFS4_VERIFIER_SIZE); |
2666 | ADJUST_ARGS(); | 2664 | ADJUST_ARGS(); |
2667 | } | 2665 | } |
2668 | return nfserr; | 2666 | return nfserr; |
@@ -2851,6 +2849,20 @@ nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_op | |||
2851 | WRITE32(0); /* XXX: is NULL principal ok? */ | 2849 | WRITE32(0); /* XXX: is NULL principal ok? */ |
2852 | ADJUST_ARGS(); | 2850 | ADJUST_ARGS(); |
2853 | break; | 2851 | break; |
2852 | case NFS4_OPEN_DELEGATE_NONE_EXT: /* 4.1 */ | ||
2853 | switch (open->op_why_no_deleg) { | ||
2854 | case WND4_CONTENTION: | ||
2855 | case WND4_RESOURCE: | ||
2856 | RESERVE_SPACE(8); | ||
2857 | WRITE32(open->op_why_no_deleg); | ||
2858 | WRITE32(0); /* deleg signaling not supported yet */ | ||
2859 | break; | ||
2860 | default: | ||
2861 | RESERVE_SPACE(4); | ||
2862 | WRITE32(open->op_why_no_deleg); | ||
2863 | } | ||
2864 | ADJUST_ARGS(); | ||
2865 | break; | ||
2854 | default: | 2866 | default: |
2855 | BUG(); | 2867 | BUG(); |
2856 | } | 2868 | } |
@@ -3008,7 +3020,7 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4 | |||
3008 | if (resp->xbuf->page_len) | 3020 | if (resp->xbuf->page_len) |
3009 | return nfserr_resource; | 3021 | return nfserr_resource; |
3010 | 3022 | ||
3011 | RESERVE_SPACE(8); /* verifier */ | 3023 | RESERVE_SPACE(NFS4_VERIFIER_SIZE); |
3012 | savep = p; | 3024 | savep = p; |
3013 | 3025 | ||
3014 | /* XXX: Following NFSv3, we ignore the READDIR verifier for now. */ | 3026 | /* XXX: Following NFSv3, we ignore the READDIR verifier for now. */ |
@@ -3209,9 +3221,9 @@ nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, __be32 nfserr, struct n | |||
3209 | __be32 *p; | 3221 | __be32 *p; |
3210 | 3222 | ||
3211 | if (!nfserr) { | 3223 | if (!nfserr) { |
3212 | RESERVE_SPACE(8 + sizeof(nfs4_verifier)); | 3224 | RESERVE_SPACE(8 + NFS4_VERIFIER_SIZE); |
3213 | WRITEMEM(&scd->se_clientid, 8); | 3225 | WRITEMEM(&scd->se_clientid, 8); |
3214 | WRITEMEM(&scd->se_confirm, sizeof(nfs4_verifier)); | 3226 | WRITEMEM(&scd->se_confirm, NFS4_VERIFIER_SIZE); |
3215 | ADJUST_ARGS(); | 3227 | ADJUST_ARGS(); |
3216 | } | 3228 | } |
3217 | else if (nfserr == nfserr_clid_inuse) { | 3229 | else if (nfserr == nfserr_clid_inuse) { |
@@ -3232,7 +3244,7 @@ nfsd4_encode_write(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_w | |||
3232 | RESERVE_SPACE(16); | 3244 | RESERVE_SPACE(16); |
3233 | WRITE32(write->wr_bytes_written); | 3245 | WRITE32(write->wr_bytes_written); |
3234 | WRITE32(write->wr_how_written); | 3246 | WRITE32(write->wr_how_written); |
3235 | WRITEMEM(write->wr_verifier.data, 8); | 3247 | WRITEMEM(write->wr_verifier.data, NFS4_VERIFIER_SIZE); |
3236 | ADJUST_ARGS(); | 3248 | ADJUST_ARGS(); |
3237 | } | 3249 | } |
3238 | return nfserr; | 3250 | return nfserr; |
@@ -3391,30 +3403,17 @@ __be32 | |||
3391 | nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, int nfserr, | 3403 | nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, int nfserr, |
3392 | struct nfsd4_test_stateid *test_stateid) | 3404 | struct nfsd4_test_stateid *test_stateid) |
3393 | { | 3405 | { |
3394 | struct nfsd4_compoundargs *argp; | 3406 | struct nfsd4_test_stateid_id *stateid, *next; |
3395 | struct nfs4_client *cl = resp->cstate.session->se_client; | ||
3396 | stateid_t si; | ||
3397 | __be32 *p; | 3407 | __be32 *p; |
3398 | int i; | ||
3399 | int valid; | ||
3400 | |||
3401 | restore_buf(test_stateid->ts_saved_args, &test_stateid->ts_savedp); | ||
3402 | argp = test_stateid->ts_saved_args; | ||
3403 | 3408 | ||
3404 | RESERVE_SPACE(4); | 3409 | RESERVE_SPACE(4 + (4 * test_stateid->ts_num_ids)); |
3405 | *p++ = htonl(test_stateid->ts_num_ids); | 3410 | *p++ = htonl(test_stateid->ts_num_ids); |
3406 | resp->p = p; | ||
3407 | 3411 | ||
3408 | nfs4_lock_state(); | 3412 | list_for_each_entry_safe(stateid, next, &test_stateid->ts_stateid_list, ts_id_list) { |
3409 | for (i = 0; i < test_stateid->ts_num_ids; i++) { | 3413 | *p++ = htonl(stateid->ts_id_status); |
3410 | nfsd4_decode_stateid(argp, &si); | ||
3411 | valid = nfs4_validate_stateid(cl, &si); | ||
3412 | RESERVE_SPACE(4); | ||
3413 | *p++ = htonl(valid); | ||
3414 | resp->p = p; | ||
3415 | } | 3414 | } |
3416 | nfs4_unlock_state(); | ||
3417 | 3415 | ||
3416 | ADJUST_ARGS(); | ||
3418 | return nfserr; | 3417 | return nfserr; |
3419 | } | 3418 | } |
3420 | 3419 | ||
@@ -3532,7 +3531,7 @@ int nfsd4_check_resp_size(struct nfsd4_compoundres *resp, u32 pad) | |||
3532 | if (length > session->se_fchannel.maxresp_sz) | 3531 | if (length > session->se_fchannel.maxresp_sz) |
3533 | return nfserr_rep_too_big; | 3532 | return nfserr_rep_too_big; |
3534 | 3533 | ||
3535 | if (slot->sl_cachethis == 1 && | 3534 | if ((slot->sl_flags & NFSD4_SLOT_CACHETHIS) && |
3536 | length > session->se_fchannel.maxresp_cached) | 3535 | length > session->se_fchannel.maxresp_cached) |
3537 | return nfserr_rep_too_big_to_cache; | 3536 | return nfserr_rep_too_big_to_cache; |
3538 | 3537 | ||
@@ -3656,8 +3655,7 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo | |||
3656 | if (nfsd4_has_session(cs)) { | 3655 | if (nfsd4_has_session(cs)) { |
3657 | if (cs->status != nfserr_replay_cache) { | 3656 | if (cs->status != nfserr_replay_cache) { |
3658 | nfsd4_store_cache_entry(resp); | 3657 | nfsd4_store_cache_entry(resp); |
3659 | dprintk("%s: SET SLOT STATE TO AVAILABLE\n", __func__); | 3658 | cs->slot->sl_flags &= ~NFSD4_SLOT_INUSE; |
3660 | cs->slot->sl_inuse = false; | ||
3661 | } | 3659 | } |
3662 | /* Renew the clientid on success and on replay */ | 3660 | /* Renew the clientid on success and on replay */ |
3663 | release_session_client(cs->session); | 3661 | release_session_client(cs->session); |
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 64c24af8d7ea..2c53be6d3579 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c | |||
@@ -13,12 +13,14 @@ | |||
13 | #include <linux/sunrpc/clnt.h> | 13 | #include <linux/sunrpc/clnt.h> |
14 | #include <linux/sunrpc/gss_api.h> | 14 | #include <linux/sunrpc/gss_api.h> |
15 | #include <linux/sunrpc/gss_krb5_enctypes.h> | 15 | #include <linux/sunrpc/gss_krb5_enctypes.h> |
16 | #include <linux/sunrpc/rpc_pipe_fs.h> | ||
16 | #include <linux/module.h> | 17 | #include <linux/module.h> |
17 | 18 | ||
18 | #include "idmap.h" | 19 | #include "idmap.h" |
19 | #include "nfsd.h" | 20 | #include "nfsd.h" |
20 | #include "cache.h" | 21 | #include "cache.h" |
21 | #include "fault_inject.h" | 22 | #include "fault_inject.h" |
23 | #include "netns.h" | ||
22 | 24 | ||
23 | /* | 25 | /* |
24 | * We have a single directory with several nodes in it. | 26 | * We have a single directory with several nodes in it. |
@@ -1124,14 +1126,26 @@ static int create_proc_exports_entry(void) | |||
1124 | } | 1126 | } |
1125 | #endif | 1127 | #endif |
1126 | 1128 | ||
1129 | int nfsd_net_id; | ||
1130 | static struct pernet_operations nfsd_net_ops = { | ||
1131 | .id = &nfsd_net_id, | ||
1132 | .size = sizeof(struct nfsd_net), | ||
1133 | }; | ||
1134 | |||
1127 | static int __init init_nfsd(void) | 1135 | static int __init init_nfsd(void) |
1128 | { | 1136 | { |
1129 | int retval; | 1137 | int retval; |
1130 | printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n"); | 1138 | printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n"); |
1131 | 1139 | ||
1132 | retval = nfsd4_init_slabs(); | 1140 | retval = register_cld_notifier(); |
1133 | if (retval) | 1141 | if (retval) |
1134 | return retval; | 1142 | return retval; |
1143 | retval = register_pernet_subsys(&nfsd_net_ops); | ||
1144 | if (retval < 0) | ||
1145 | goto out_unregister_notifier; | ||
1146 | retval = nfsd4_init_slabs(); | ||
1147 | if (retval) | ||
1148 | goto out_unregister_pernet; | ||
1135 | nfs4_state_init(); | 1149 | nfs4_state_init(); |
1136 | retval = nfsd_fault_inject_init(); /* nfsd fault injection controls */ | 1150 | retval = nfsd_fault_inject_init(); /* nfsd fault injection controls */ |
1137 | if (retval) | 1151 | if (retval) |
@@ -1169,6 +1183,10 @@ out_free_stat: | |||
1169 | nfsd_fault_inject_cleanup(); | 1183 | nfsd_fault_inject_cleanup(); |
1170 | out_free_slabs: | 1184 | out_free_slabs: |
1171 | nfsd4_free_slabs(); | 1185 | nfsd4_free_slabs(); |
1186 | out_unregister_pernet: | ||
1187 | unregister_pernet_subsys(&nfsd_net_ops); | ||
1188 | out_unregister_notifier: | ||
1189 | unregister_cld_notifier(); | ||
1172 | return retval; | 1190 | return retval; |
1173 | } | 1191 | } |
1174 | 1192 | ||
@@ -1184,6 +1202,8 @@ static void __exit exit_nfsd(void) | |||
1184 | nfsd4_free_slabs(); | 1202 | nfsd4_free_slabs(); |
1185 | nfsd_fault_inject_cleanup(); | 1203 | nfsd_fault_inject_cleanup(); |
1186 | unregister_filesystem(&nfsd_fs_type); | 1204 | unregister_filesystem(&nfsd_fs_type); |
1205 | unregister_pernet_subsys(&nfsd_net_ops); | ||
1206 | unregister_cld_notifier(); | ||
1187 | } | 1207 | } |
1188 | 1208 | ||
1189 | MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>"); | 1209 | MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>"); |
diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index 1d1e8589b4ce..1671429ffa66 100644 --- a/fs/nfsd/nfsd.h +++ b/fs/nfsd/nfsd.h | |||
@@ -364,12 +364,17 @@ static inline u32 nfsd_suppattrs2(u32 minorversion) | |||
364 | NFSD_WRITEABLE_ATTRS_WORD2 | 364 | NFSD_WRITEABLE_ATTRS_WORD2 |
365 | 365 | ||
366 | extern int nfsd4_is_junction(struct dentry *dentry); | 366 | extern int nfsd4_is_junction(struct dentry *dentry); |
367 | #else | 367 | extern int register_cld_notifier(void); |
368 | extern void unregister_cld_notifier(void); | ||
369 | #else /* CONFIG_NFSD_V4 */ | ||
368 | static inline int nfsd4_is_junction(struct dentry *dentry) | 370 | static inline int nfsd4_is_junction(struct dentry *dentry) |
369 | { | 371 | { |
370 | return 0; | 372 | return 0; |
371 | } | 373 | } |
372 | 374 | ||
375 | #define register_cld_notifier() 0 | ||
376 | #define unregister_cld_notifier() do { } while(0) | ||
377 | |||
373 | #endif /* CONFIG_NFSD_V4 */ | 378 | #endif /* CONFIG_NFSD_V4 */ |
374 | 379 | ||
375 | #endif /* LINUX_NFSD_NFSD_H */ | 380 | #endif /* LINUX_NFSD_NFSD_H */ |
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index fce472f5f39e..28dfad39f0c5 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c | |||
@@ -307,33 +307,37 @@ static void set_max_drc(void) | |||
307 | dprintk("%s nfsd_drc_max_mem %u \n", __func__, nfsd_drc_max_mem); | 307 | dprintk("%s nfsd_drc_max_mem %u \n", __func__, nfsd_drc_max_mem); |
308 | } | 308 | } |
309 | 309 | ||
310 | int nfsd_create_serv(void) | 310 | static int nfsd_get_default_max_blksize(void) |
311 | { | 311 | { |
312 | int err = 0; | 312 | struct sysinfo i; |
313 | unsigned long long target; | ||
314 | unsigned long ret; | ||
315 | |||
316 | si_meminfo(&i); | ||
317 | target = (i.totalram - i.totalhigh) << PAGE_SHIFT; | ||
318 | /* | ||
319 | * Aim for 1/4096 of memory per thread This gives 1MB on 4Gig | ||
320 | * machines, but only uses 32K on 128M machines. Bottom out at | ||
321 | * 8K on 32M and smaller. Of course, this is only a default. | ||
322 | */ | ||
323 | target >>= 12; | ||
324 | |||
325 | ret = NFSSVC_MAXBLKSIZE; | ||
326 | while (ret > target && ret >= 8*1024*2) | ||
327 | ret /= 2; | ||
328 | return ret; | ||
329 | } | ||
313 | 330 | ||
331 | int nfsd_create_serv(void) | ||
332 | { | ||
314 | WARN_ON(!mutex_is_locked(&nfsd_mutex)); | 333 | WARN_ON(!mutex_is_locked(&nfsd_mutex)); |
315 | if (nfsd_serv) { | 334 | if (nfsd_serv) { |
316 | svc_get(nfsd_serv); | 335 | svc_get(nfsd_serv); |
317 | return 0; | 336 | return 0; |
318 | } | 337 | } |
319 | if (nfsd_max_blksize == 0) { | 338 | if (nfsd_max_blksize == 0) |
320 | /* choose a suitable default */ | 339 | nfsd_max_blksize = nfsd_get_default_max_blksize(); |
321 | struct sysinfo i; | ||
322 | si_meminfo(&i); | ||
323 | /* Aim for 1/4096 of memory per thread | ||
324 | * This gives 1MB on 4Gig machines | ||
325 | * But only uses 32K on 128M machines. | ||
326 | * Bottom out at 8K on 32M and smaller. | ||
327 | * Of course, this is only a default. | ||
328 | */ | ||
329 | nfsd_max_blksize = NFSSVC_MAXBLKSIZE; | ||
330 | i.totalram <<= PAGE_SHIFT - 12; | ||
331 | while (nfsd_max_blksize > i.totalram && | ||
332 | nfsd_max_blksize >= 8*1024*2) | ||
333 | nfsd_max_blksize /= 2; | ||
334 | } | ||
335 | nfsd_reset_versions(); | 340 | nfsd_reset_versions(); |
336 | |||
337 | nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize, | 341 | nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize, |
338 | nfsd_last_thread, nfsd, THIS_MODULE); | 342 | nfsd_last_thread, nfsd, THIS_MODULE); |
339 | if (nfsd_serv == NULL) | 343 | if (nfsd_serv == NULL) |
@@ -341,7 +345,7 @@ int nfsd_create_serv(void) | |||
341 | 345 | ||
342 | set_max_drc(); | 346 | set_max_drc(); |
343 | do_gettimeofday(&nfssvc_boot); /* record boot time */ | 347 | do_gettimeofday(&nfssvc_boot); /* record boot time */ |
344 | return err; | 348 | return 0; |
345 | } | 349 | } |
346 | 350 | ||
347 | int nfsd_nrpools(void) | 351 | int nfsd_nrpools(void) |
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index ffb5df1db94f..89ab137d379a 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h | |||
@@ -128,12 +128,14 @@ static inline struct nfs4_delegation *delegstateid(struct nfs4_stid *s) | |||
128 | (NFSD_CACHE_SIZE_SLOTS_PER_SESSION * NFSD_SLOT_CACHE_SIZE) | 128 | (NFSD_CACHE_SIZE_SLOTS_PER_SESSION * NFSD_SLOT_CACHE_SIZE) |
129 | 129 | ||
130 | struct nfsd4_slot { | 130 | struct nfsd4_slot { |
131 | bool sl_inuse; | ||
132 | bool sl_cachethis; | ||
133 | u16 sl_opcnt; | ||
134 | u32 sl_seqid; | 131 | u32 sl_seqid; |
135 | __be32 sl_status; | 132 | __be32 sl_status; |
136 | u32 sl_datalen; | 133 | u32 sl_datalen; |
134 | u16 sl_opcnt; | ||
135 | #define NFSD4_SLOT_INUSE (1 << 0) | ||
136 | #define NFSD4_SLOT_CACHETHIS (1 << 1) | ||
137 | #define NFSD4_SLOT_INITIALIZED (1 << 2) | ||
138 | u8 sl_flags; | ||
137 | char sl_data[]; | 139 | char sl_data[]; |
138 | }; | 140 | }; |
139 | 141 | ||
@@ -196,18 +198,7 @@ struct nfsd4_session { | |||
196 | struct nfsd4_slot *se_slots[]; /* forward channel slots */ | 198 | struct nfsd4_slot *se_slots[]; /* forward channel slots */ |
197 | }; | 199 | }; |
198 | 200 | ||
199 | static inline void | 201 | extern void nfsd4_put_session(struct nfsd4_session *ses); |
200 | nfsd4_put_session(struct nfsd4_session *ses) | ||
201 | { | ||
202 | extern void free_session(struct kref *kref); | ||
203 | kref_put(&ses->se_ref, free_session); | ||
204 | } | ||
205 | |||
206 | static inline void | ||
207 | nfsd4_get_session(struct nfsd4_session *ses) | ||
208 | { | ||
209 | kref_get(&ses->se_ref); | ||
210 | } | ||
211 | 202 | ||
212 | /* formatted contents of nfs4_sessionid */ | 203 | /* formatted contents of nfs4_sessionid */ |
213 | struct nfsd4_sessionid { | 204 | struct nfsd4_sessionid { |
@@ -245,14 +236,17 @@ struct nfs4_client { | |||
245 | struct svc_cred cl_cred; /* setclientid principal */ | 236 | struct svc_cred cl_cred; /* setclientid principal */ |
246 | clientid_t cl_clientid; /* generated by server */ | 237 | clientid_t cl_clientid; /* generated by server */ |
247 | nfs4_verifier cl_confirm; /* generated by server */ | 238 | nfs4_verifier cl_confirm; /* generated by server */ |
248 | u32 cl_firststate; /* recovery dir creation */ | ||
249 | u32 cl_minorversion; | 239 | u32 cl_minorversion; |
250 | 240 | ||
251 | /* for v4.0 and v4.1 callbacks: */ | 241 | /* for v4.0 and v4.1 callbacks: */ |
252 | struct nfs4_cb_conn cl_cb_conn; | 242 | struct nfs4_cb_conn cl_cb_conn; |
253 | #define NFSD4_CLIENT_CB_UPDATE 1 | 243 | #define NFSD4_CLIENT_CB_UPDATE (0) |
254 | #define NFSD4_CLIENT_KILL 2 | 244 | #define NFSD4_CLIENT_CB_KILL (1) |
255 | unsigned long cl_cb_flags; | 245 | #define NFSD4_CLIENT_STABLE (2) /* client on stable storage */ |
246 | #define NFSD4_CLIENT_RECLAIM_COMPLETE (3) /* reclaim_complete done */ | ||
247 | #define NFSD4_CLIENT_CB_FLAG_MASK (1 << NFSD4_CLIENT_CB_UPDATE | \ | ||
248 | 1 << NFSD4_CLIENT_CB_KILL) | ||
249 | unsigned long cl_flags; | ||
256 | struct rpc_clnt *cl_cb_client; | 250 | struct rpc_clnt *cl_cb_client; |
257 | u32 cl_cb_ident; | 251 | u32 cl_cb_ident; |
258 | #define NFSD4_CB_UP 0 | 252 | #define NFSD4_CB_UP 0 |
@@ -463,6 +457,8 @@ extern __be32 nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, | |||
463 | extern void nfs4_lock_state(void); | 457 | extern void nfs4_lock_state(void); |
464 | extern void nfs4_unlock_state(void); | 458 | extern void nfs4_unlock_state(void); |
465 | extern int nfs4_in_grace(void); | 459 | extern int nfs4_in_grace(void); |
460 | extern void nfs4_release_reclaim(void); | ||
461 | extern struct nfs4_client_reclaim *nfsd4_find_reclaim_client(struct nfs4_client *crp); | ||
466 | extern __be32 nfs4_check_open_reclaim(clientid_t *clid); | 462 | extern __be32 nfs4_check_open_reclaim(clientid_t *clid); |
467 | extern void nfs4_free_openowner(struct nfs4_openowner *); | 463 | extern void nfs4_free_openowner(struct nfs4_openowner *); |
468 | extern void nfs4_free_lockowner(struct nfs4_lockowner *); | 464 | extern void nfs4_free_lockowner(struct nfs4_lockowner *); |
@@ -477,16 +473,17 @@ extern void nfsd4_destroy_callback_queue(void); | |||
477 | extern void nfsd4_shutdown_callback(struct nfs4_client *); | 473 | extern void nfsd4_shutdown_callback(struct nfs4_client *); |
478 | extern void nfs4_put_delegation(struct nfs4_delegation *dp); | 474 | extern void nfs4_put_delegation(struct nfs4_delegation *dp); |
479 | extern __be32 nfs4_make_rec_clidname(char *clidname, struct xdr_netobj *clname); | 475 | extern __be32 nfs4_make_rec_clidname(char *clidname, struct xdr_netobj *clname); |
480 | extern void nfsd4_init_recdir(void); | ||
481 | extern int nfsd4_recdir_load(void); | ||
482 | extern void nfsd4_shutdown_recdir(void); | ||
483 | extern int nfs4_client_to_reclaim(const char *name); | 476 | extern int nfs4_client_to_reclaim(const char *name); |
484 | extern int nfs4_has_reclaimed_state(const char *name, bool use_exchange_id); | 477 | extern int nfs4_has_reclaimed_state(const char *name, bool use_exchange_id); |
485 | extern void nfsd4_recdir_purge_old(void); | ||
486 | extern void nfsd4_create_clid_dir(struct nfs4_client *clp); | ||
487 | extern void nfsd4_remove_clid_dir(struct nfs4_client *clp); | ||
488 | extern void release_session_client(struct nfsd4_session *); | 478 | extern void release_session_client(struct nfsd4_session *); |
489 | extern __be32 nfs4_validate_stateid(struct nfs4_client *, stateid_t *); | 479 | extern __be32 nfs4_validate_stateid(struct nfs4_client *, stateid_t *); |
490 | extern void nfsd4_purge_closed_stateid(struct nfs4_stateowner *); | 480 | extern void nfsd4_purge_closed_stateid(struct nfs4_stateowner *); |
491 | 481 | ||
482 | /* nfs4recover operations */ | ||
483 | extern int nfsd4_client_tracking_init(struct net *net); | ||
484 | extern void nfsd4_client_tracking_exit(struct net *net); | ||
485 | extern void nfsd4_client_record_create(struct nfs4_client *clp); | ||
486 | extern void nfsd4_client_record_remove(struct nfs4_client *clp); | ||
487 | extern int nfsd4_client_record_check(struct nfs4_client *clp); | ||
488 | extern void nfsd4_record_grace_done(struct net *net, time_t boot_time); | ||
492 | #endif /* NFSD4_STATE_H */ | 489 | #endif /* NFSD4_STATE_H */ |
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index e59f71d0cf73..296d671654d6 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c | |||
@@ -737,12 +737,13 @@ static int nfsd_open_break_lease(struct inode *inode, int access) | |||
737 | 737 | ||
738 | /* | 738 | /* |
739 | * Open an existing file or directory. | 739 | * Open an existing file or directory. |
740 | * The access argument indicates the type of open (read/write/lock) | 740 | * The may_flags argument indicates the type of open (read/write/lock) |
741 | * and additional flags. | ||
741 | * N.B. After this call fhp needs an fh_put | 742 | * N.B. After this call fhp needs an fh_put |
742 | */ | 743 | */ |
743 | __be32 | 744 | __be32 |
744 | nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, | 745 | nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, |
745 | int access, struct file **filp) | 746 | int may_flags, struct file **filp) |
746 | { | 747 | { |
747 | struct dentry *dentry; | 748 | struct dentry *dentry; |
748 | struct inode *inode; | 749 | struct inode *inode; |
@@ -757,7 +758,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, | |||
757 | * and (hopefully) checked permission - so allow OWNER_OVERRIDE | 758 | * and (hopefully) checked permission - so allow OWNER_OVERRIDE |
758 | * in case a chmod has now revoked permission. | 759 | * in case a chmod has now revoked permission. |
759 | */ | 760 | */ |
760 | err = fh_verify(rqstp, fhp, type, access | NFSD_MAY_OWNER_OVERRIDE); | 761 | err = fh_verify(rqstp, fhp, type, may_flags | NFSD_MAY_OWNER_OVERRIDE); |
761 | if (err) | 762 | if (err) |
762 | goto out; | 763 | goto out; |
763 | 764 | ||
@@ -768,7 +769,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, | |||
768 | * or any access when mandatory locking enabled | 769 | * or any access when mandatory locking enabled |
769 | */ | 770 | */ |
770 | err = nfserr_perm; | 771 | err = nfserr_perm; |
771 | if (IS_APPEND(inode) && (access & NFSD_MAY_WRITE)) | 772 | if (IS_APPEND(inode) && (may_flags & NFSD_MAY_WRITE)) |
772 | goto out; | 773 | goto out; |
773 | /* | 774 | /* |
774 | * We must ignore files (but only files) which might have mandatory | 775 | * We must ignore files (but only files) which might have mandatory |
@@ -781,12 +782,12 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, | |||
781 | if (!inode->i_fop) | 782 | if (!inode->i_fop) |
782 | goto out; | 783 | goto out; |
783 | 784 | ||
784 | host_err = nfsd_open_break_lease(inode, access); | 785 | host_err = nfsd_open_break_lease(inode, may_flags); |
785 | if (host_err) /* NOMEM or WOULDBLOCK */ | 786 | if (host_err) /* NOMEM or WOULDBLOCK */ |
786 | goto out_nfserr; | 787 | goto out_nfserr; |
787 | 788 | ||
788 | if (access & NFSD_MAY_WRITE) { | 789 | if (may_flags & NFSD_MAY_WRITE) { |
789 | if (access & NFSD_MAY_READ) | 790 | if (may_flags & NFSD_MAY_READ) |
790 | flags = O_RDWR|O_LARGEFILE; | 791 | flags = O_RDWR|O_LARGEFILE; |
791 | else | 792 | else |
792 | flags = O_WRONLY|O_LARGEFILE; | 793 | flags = O_WRONLY|O_LARGEFILE; |
@@ -795,8 +796,15 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, | |||
795 | flags, current_cred()); | 796 | flags, current_cred()); |
796 | if (IS_ERR(*filp)) | 797 | if (IS_ERR(*filp)) |
797 | host_err = PTR_ERR(*filp); | 798 | host_err = PTR_ERR(*filp); |
798 | else | 799 | else { |
799 | host_err = ima_file_check(*filp, access); | 800 | host_err = ima_file_check(*filp, may_flags); |
801 | |||
802 | if (may_flags & NFSD_MAY_64BIT_COOKIE) | ||
803 | (*filp)->f_mode |= FMODE_64BITHASH; | ||
804 | else | ||
805 | (*filp)->f_mode |= FMODE_32BITHASH; | ||
806 | } | ||
807 | |||
800 | out_nfserr: | 808 | out_nfserr: |
801 | err = nfserrno(host_err); | 809 | err = nfserrno(host_err); |
802 | out: | 810 | out: |
@@ -2021,8 +2029,13 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp, | |||
2021 | __be32 err; | 2029 | __be32 err; |
2022 | struct file *file; | 2030 | struct file *file; |
2023 | loff_t offset = *offsetp; | 2031 | loff_t offset = *offsetp; |
2032 | int may_flags = NFSD_MAY_READ; | ||
2033 | |||
2034 | /* NFSv2 only supports 32 bit cookies */ | ||
2035 | if (rqstp->rq_vers > 2) | ||
2036 | may_flags |= NFSD_MAY_64BIT_COOKIE; | ||
2024 | 2037 | ||
2025 | err = nfsd_open(rqstp, fhp, S_IFDIR, NFSD_MAY_READ, &file); | 2038 | err = nfsd_open(rqstp, fhp, S_IFDIR, may_flags, &file); |
2026 | if (err) | 2039 | if (err) |
2027 | goto out; | 2040 | goto out; |
2028 | 2041 | ||
diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h index 1dcd238e11a0..ec0611b2b738 100644 --- a/fs/nfsd/vfs.h +++ b/fs/nfsd/vfs.h | |||
@@ -27,6 +27,8 @@ | |||
27 | #define NFSD_MAY_BYPASS_GSS 0x400 | 27 | #define NFSD_MAY_BYPASS_GSS 0x400 |
28 | #define NFSD_MAY_READ_IF_EXEC 0x800 | 28 | #define NFSD_MAY_READ_IF_EXEC 0x800 |
29 | 29 | ||
30 | #define NFSD_MAY_64BIT_COOKIE 0x1000 /* 64 bit readdir cookies for >= NFSv3 */ | ||
31 | |||
30 | #define NFSD_MAY_CREATE (NFSD_MAY_EXEC|NFSD_MAY_WRITE) | 32 | #define NFSD_MAY_CREATE (NFSD_MAY_EXEC|NFSD_MAY_WRITE) |
31 | #define NFSD_MAY_REMOVE (NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC) | 33 | #define NFSD_MAY_REMOVE (NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC) |
32 | 34 | ||
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index 2364747ee97d..1b3501598ab5 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h | |||
@@ -43,6 +43,13 @@ | |||
43 | #define NFSD4_MAX_TAGLEN 128 | 43 | #define NFSD4_MAX_TAGLEN 128 |
44 | #define XDR_LEN(n) (((n) + 3) & ~3) | 44 | #define XDR_LEN(n) (((n) + 3) & ~3) |
45 | 45 | ||
46 | #define CURRENT_STATE_ID_FLAG (1<<0) | ||
47 | #define SAVED_STATE_ID_FLAG (1<<1) | ||
48 | |||
49 | #define SET_STATE_ID(c, f) ((c)->sid_flags |= (f)) | ||
50 | #define HAS_STATE_ID(c, f) ((c)->sid_flags & (f)) | ||
51 | #define CLEAR_STATE_ID(c, f) ((c)->sid_flags &= ~(f)) | ||
52 | |||
46 | struct nfsd4_compound_state { | 53 | struct nfsd4_compound_state { |
47 | struct svc_fh current_fh; | 54 | struct svc_fh current_fh; |
48 | struct svc_fh save_fh; | 55 | struct svc_fh save_fh; |
@@ -54,6 +61,10 @@ struct nfsd4_compound_state { | |||
54 | size_t iovlen; | 61 | size_t iovlen; |
55 | u32 minorversion; | 62 | u32 minorversion; |
56 | u32 status; | 63 | u32 status; |
64 | stateid_t current_stateid; | ||
65 | stateid_t save_stateid; | ||
66 | /* to indicate current and saved state id presents */ | ||
67 | u32 sid_flags; | ||
57 | }; | 68 | }; |
58 | 69 | ||
59 | static inline bool nfsd4_has_session(struct nfsd4_compound_state *cs) | 70 | static inline bool nfsd4_has_session(struct nfsd4_compound_state *cs) |
@@ -212,16 +223,19 @@ struct nfsd4_open { | |||
212 | struct xdr_netobj op_fname; /* request - everything but CLAIM_PREV */ | 223 | struct xdr_netobj op_fname; /* request - everything but CLAIM_PREV */ |
213 | u32 op_delegate_type; /* request - CLAIM_PREV only */ | 224 | u32 op_delegate_type; /* request - CLAIM_PREV only */ |
214 | stateid_t op_delegate_stateid; /* request - response */ | 225 | stateid_t op_delegate_stateid; /* request - response */ |
226 | u32 op_why_no_deleg; /* response - DELEG_NONE_EXT only */ | ||
215 | u32 op_create; /* request */ | 227 | u32 op_create; /* request */ |
216 | u32 op_createmode; /* request */ | 228 | u32 op_createmode; /* request */ |
217 | u32 op_bmval[3]; /* request */ | 229 | u32 op_bmval[3]; /* request */ |
218 | struct iattr iattr; /* UNCHECKED4, GUARDED4, EXCLUSIVE4_1 */ | 230 | struct iattr iattr; /* UNCHECKED4, GUARDED4, EXCLUSIVE4_1 */ |
219 | nfs4_verifier verf; /* EXCLUSIVE4 */ | 231 | nfs4_verifier op_verf __attribute__((aligned(32))); |
232 | /* EXCLUSIVE4 */ | ||
220 | clientid_t op_clientid; /* request */ | 233 | clientid_t op_clientid; /* request */ |
221 | struct xdr_netobj op_owner; /* request */ | 234 | struct xdr_netobj op_owner; /* request */ |
222 | u32 op_seqid; /* request */ | 235 | u32 op_seqid; /* request */ |
223 | u32 op_share_access; /* request */ | 236 | u32 op_share_access; /* request */ |
224 | u32 op_share_deny; /* request */ | 237 | u32 op_share_deny; /* request */ |
238 | u32 op_deleg_want; /* request */ | ||
225 | stateid_t op_stateid; /* response */ | 239 | stateid_t op_stateid; /* response */ |
226 | u32 op_recall; /* recall */ | 240 | u32 op_recall; /* recall */ |
227 | struct nfsd4_change_info op_cinfo; /* response */ | 241 | struct nfsd4_change_info op_cinfo; /* response */ |
@@ -234,7 +248,6 @@ struct nfsd4_open { | |||
234 | struct nfs4_acl *op_acl; | 248 | struct nfs4_acl *op_acl; |
235 | }; | 249 | }; |
236 | #define op_iattr iattr | 250 | #define op_iattr iattr |
237 | #define op_verf verf | ||
238 | 251 | ||
239 | struct nfsd4_open_confirm { | 252 | struct nfsd4_open_confirm { |
240 | stateid_t oc_req_stateid /* request */; | 253 | stateid_t oc_req_stateid /* request */; |
@@ -245,8 +258,9 @@ struct nfsd4_open_confirm { | |||
245 | struct nfsd4_open_downgrade { | 258 | struct nfsd4_open_downgrade { |
246 | stateid_t od_stateid; | 259 | stateid_t od_stateid; |
247 | u32 od_seqid; | 260 | u32 od_seqid; |
248 | u32 od_share_access; | 261 | u32 od_share_access; /* request */ |
249 | u32 od_share_deny; | 262 | u32 od_deleg_want; /* request */ |
263 | u32 od_share_deny; /* request */ | ||
250 | }; | 264 | }; |
251 | 265 | ||
252 | 266 | ||
@@ -343,10 +357,15 @@ struct nfsd4_saved_compoundargs { | |||
343 | struct page **pagelist; | 357 | struct page **pagelist; |
344 | }; | 358 | }; |
345 | 359 | ||
360 | struct nfsd4_test_stateid_id { | ||
361 | __be32 ts_id_status; | ||
362 | stateid_t ts_id_stateid; | ||
363 | struct list_head ts_id_list; | ||
364 | }; | ||
365 | |||
346 | struct nfsd4_test_stateid { | 366 | struct nfsd4_test_stateid { |
347 | __be32 ts_num_ids; | 367 | __be32 ts_num_ids; |
348 | struct nfsd4_compoundargs *ts_saved_args; | 368 | struct list_head ts_stateid_list; |
349 | struct nfsd4_saved_compoundargs ts_savedp; | ||
350 | }; | 369 | }; |
351 | 370 | ||
352 | struct nfsd4_free_stateid { | 371 | struct nfsd4_free_stateid { |
@@ -503,7 +522,8 @@ static inline bool nfsd4_is_solo_sequence(struct nfsd4_compoundres *resp) | |||
503 | 522 | ||
504 | static inline bool nfsd4_not_cached(struct nfsd4_compoundres *resp) | 523 | static inline bool nfsd4_not_cached(struct nfsd4_compoundres *resp) |
505 | { | 524 | { |
506 | return !resp->cstate.slot->sl_cachethis || nfsd4_is_solo_sequence(resp); | 525 | return !(resp->cstate.slot->sl_flags & NFSD4_SLOT_CACHETHIS) |
526 | || nfsd4_is_solo_sequence(resp); | ||
507 | } | 527 | } |
508 | 528 | ||
509 | #define NFS4_SVC_XDRSIZE sizeof(struct nfsd4_compoundargs) | 529 | #define NFS4_SVC_XDRSIZE sizeof(struct nfsd4_compoundargs) |