diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-05-13 19:32:16 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-05-13 19:32:16 -0400 |
commit | 014049a1c7d5ab42ffbe3995f36372c1cdda16a7 (patch) | |
tree | 437be943ef7c5e76df84806c6ea555c242d6f706 /fs | |
parent | c98861f7de20fa982de0b5bbe01628414d7a7fbf (diff) | |
parent | 83aca8f480fcd2d9748301a5d060cf947dc75b94 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ryusuke/nilfs2
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ryusuke/nilfs2:
nilfs2: check size of array structured data exchanged via ioctls
nilfs2: fix lock order reversal in nilfs_clean_segments ioctl
nilfs2: fix possible circular locking for get information ioctls
nilfs2: ensure to clear dirty state when deleting metadata file block
nilfs2: fix circular locking dependency of writer mutex
nilfs2: fix possible recovery failure due to block creation without writer
Diffstat (limited to 'fs')
-rw-r--r-- | fs/nilfs2/ioctl.c | 281 | ||||
-rw-r--r-- | fs/nilfs2/mdt.c | 15 | ||||
-rw-r--r-- | fs/nilfs2/nilfs.h | 3 | ||||
-rw-r--r-- | fs/nilfs2/page.c | 3 | ||||
-rw-r--r-- | fs/nilfs2/recovery.c | 6 | ||||
-rw-r--r-- | fs/nilfs2/segment.c | 5 | ||||
-rw-r--r-- | fs/nilfs2/segment.h | 3 |
7 files changed, 167 insertions, 149 deletions
diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c index 108d281ebca5..50ff3f2cdf24 100644 --- a/fs/nilfs2/ioctl.c +++ b/fs/nilfs2/ioctl.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/smp_lock.h> /* lock_kernel(), unlock_kernel() */ | 25 | #include <linux/smp_lock.h> /* lock_kernel(), unlock_kernel() */ |
26 | #include <linux/capability.h> /* capable() */ | 26 | #include <linux/capability.h> /* capable() */ |
27 | #include <linux/uaccess.h> /* copy_from_user(), copy_to_user() */ | 27 | #include <linux/uaccess.h> /* copy_from_user(), copy_to_user() */ |
28 | #include <linux/vmalloc.h> | ||
28 | #include <linux/nilfs2_fs.h> | 29 | #include <linux/nilfs2_fs.h> |
29 | #include "nilfs.h" | 30 | #include "nilfs.h" |
30 | #include "segment.h" | 31 | #include "segment.h" |
@@ -147,29 +148,12 @@ static ssize_t | |||
147 | nilfs_ioctl_do_get_cpinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, | 148 | nilfs_ioctl_do_get_cpinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, |
148 | void *buf, size_t size, size_t nmembs) | 149 | void *buf, size_t size, size_t nmembs) |
149 | { | 150 | { |
150 | return nilfs_cpfile_get_cpinfo(nilfs->ns_cpfile, posp, flags, buf, | ||
151 | nmembs); | ||
152 | } | ||
153 | |||
154 | static int nilfs_ioctl_get_cpinfo(struct inode *inode, struct file *filp, | ||
155 | unsigned int cmd, void __user *argp) | ||
156 | { | ||
157 | struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs; | ||
158 | struct nilfs_argv argv; | ||
159 | int ret; | 151 | int ret; |
160 | 152 | ||
161 | if (copy_from_user(&argv, argp, sizeof(argv))) | ||
162 | return -EFAULT; | ||
163 | |||
164 | down_read(&nilfs->ns_segctor_sem); | 153 | down_read(&nilfs->ns_segctor_sem); |
165 | ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), | 154 | ret = nilfs_cpfile_get_cpinfo(nilfs->ns_cpfile, posp, flags, buf, |
166 | nilfs_ioctl_do_get_cpinfo); | 155 | nmembs); |
167 | up_read(&nilfs->ns_segctor_sem); | 156 | up_read(&nilfs->ns_segctor_sem); |
168 | if (ret < 0) | ||
169 | return ret; | ||
170 | |||
171 | if (copy_to_user(argp, &argv, sizeof(argv))) | ||
172 | ret = -EFAULT; | ||
173 | return ret; | 157 | return ret; |
174 | } | 158 | } |
175 | 159 | ||
@@ -195,28 +179,11 @@ static ssize_t | |||
195 | nilfs_ioctl_do_get_suinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, | 179 | nilfs_ioctl_do_get_suinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, |
196 | void *buf, size_t size, size_t nmembs) | 180 | void *buf, size_t size, size_t nmembs) |
197 | { | 181 | { |
198 | return nilfs_sufile_get_suinfo(nilfs->ns_sufile, *posp, buf, nmembs); | ||
199 | } | ||
200 | |||
201 | static int nilfs_ioctl_get_suinfo(struct inode *inode, struct file *filp, | ||
202 | unsigned int cmd, void __user *argp) | ||
203 | { | ||
204 | struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs; | ||
205 | struct nilfs_argv argv; | ||
206 | int ret; | 182 | int ret; |
207 | 183 | ||
208 | if (copy_from_user(&argv, argp, sizeof(argv))) | ||
209 | return -EFAULT; | ||
210 | |||
211 | down_read(&nilfs->ns_segctor_sem); | 184 | down_read(&nilfs->ns_segctor_sem); |
212 | ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), | 185 | ret = nilfs_sufile_get_suinfo(nilfs->ns_sufile, *posp, buf, nmembs); |
213 | nilfs_ioctl_do_get_suinfo); | ||
214 | up_read(&nilfs->ns_segctor_sem); | 186 | up_read(&nilfs->ns_segctor_sem); |
215 | if (ret < 0) | ||
216 | return ret; | ||
217 | |||
218 | if (copy_to_user(argp, &argv, sizeof(argv))) | ||
219 | ret = -EFAULT; | ||
220 | return ret; | 187 | return ret; |
221 | } | 188 | } |
222 | 189 | ||
@@ -242,28 +209,11 @@ static ssize_t | |||
242 | nilfs_ioctl_do_get_vinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, | 209 | nilfs_ioctl_do_get_vinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, |
243 | void *buf, size_t size, size_t nmembs) | 210 | void *buf, size_t size, size_t nmembs) |
244 | { | 211 | { |
245 | return nilfs_dat_get_vinfo(nilfs_dat_inode(nilfs), buf, nmembs); | ||
246 | } | ||
247 | |||
248 | static int nilfs_ioctl_get_vinfo(struct inode *inode, struct file *filp, | ||
249 | unsigned int cmd, void __user *argp) | ||
250 | { | ||
251 | struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs; | ||
252 | struct nilfs_argv argv; | ||
253 | int ret; | 212 | int ret; |
254 | 213 | ||
255 | if (copy_from_user(&argv, argp, sizeof(argv))) | ||
256 | return -EFAULT; | ||
257 | |||
258 | down_read(&nilfs->ns_segctor_sem); | 214 | down_read(&nilfs->ns_segctor_sem); |
259 | ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), | 215 | ret = nilfs_dat_get_vinfo(nilfs_dat_inode(nilfs), buf, nmembs); |
260 | nilfs_ioctl_do_get_vinfo); | ||
261 | up_read(&nilfs->ns_segctor_sem); | 216 | up_read(&nilfs->ns_segctor_sem); |
262 | if (ret < 0) | ||
263 | return ret; | ||
264 | |||
265 | if (copy_to_user(argp, &argv, sizeof(argv))) | ||
266 | ret = -EFAULT; | ||
267 | return ret; | 217 | return ret; |
268 | } | 218 | } |
269 | 219 | ||
@@ -276,17 +226,21 @@ nilfs_ioctl_do_get_bdescs(struct the_nilfs *nilfs, __u64 *posp, int flags, | |||
276 | struct nilfs_bdesc *bdescs = buf; | 226 | struct nilfs_bdesc *bdescs = buf; |
277 | int ret, i; | 227 | int ret, i; |
278 | 228 | ||
229 | down_read(&nilfs->ns_segctor_sem); | ||
279 | for (i = 0; i < nmembs; i++) { | 230 | for (i = 0; i < nmembs; i++) { |
280 | ret = nilfs_bmap_lookup_at_level(bmap, | 231 | ret = nilfs_bmap_lookup_at_level(bmap, |
281 | bdescs[i].bd_offset, | 232 | bdescs[i].bd_offset, |
282 | bdescs[i].bd_level + 1, | 233 | bdescs[i].bd_level + 1, |
283 | &bdescs[i].bd_blocknr); | 234 | &bdescs[i].bd_blocknr); |
284 | if (ret < 0) { | 235 | if (ret < 0) { |
285 | if (ret != -ENOENT) | 236 | if (ret != -ENOENT) { |
237 | up_read(&nilfs->ns_segctor_sem); | ||
286 | return ret; | 238 | return ret; |
239 | } | ||
287 | bdescs[i].bd_blocknr = 0; | 240 | bdescs[i].bd_blocknr = 0; |
288 | } | 241 | } |
289 | } | 242 | } |
243 | up_read(&nilfs->ns_segctor_sem); | ||
290 | return nmembs; | 244 | return nmembs; |
291 | } | 245 | } |
292 | 246 | ||
@@ -300,10 +254,11 @@ static int nilfs_ioctl_get_bdescs(struct inode *inode, struct file *filp, | |||
300 | if (copy_from_user(&argv, argp, sizeof(argv))) | 254 | if (copy_from_user(&argv, argp, sizeof(argv))) |
301 | return -EFAULT; | 255 | return -EFAULT; |
302 | 256 | ||
303 | down_read(&nilfs->ns_segctor_sem); | 257 | if (argv.v_size != sizeof(struct nilfs_bdesc)) |
258 | return -EINVAL; | ||
259 | |||
304 | ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), | 260 | ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), |
305 | nilfs_ioctl_do_get_bdescs); | 261 | nilfs_ioctl_do_get_bdescs); |
306 | up_read(&nilfs->ns_segctor_sem); | ||
307 | if (ret < 0) | 262 | if (ret < 0) |
308 | return ret; | 263 | return ret; |
309 | 264 | ||
@@ -346,10 +301,10 @@ static int nilfs_ioctl_move_inode_block(struct inode *inode, | |||
346 | return 0; | 301 | return 0; |
347 | } | 302 | } |
348 | 303 | ||
349 | static ssize_t | 304 | static int nilfs_ioctl_move_blocks(struct the_nilfs *nilfs, |
350 | nilfs_ioctl_do_move_blocks(struct the_nilfs *nilfs, __u64 *posp, int flags, | 305 | struct nilfs_argv *argv, void *buf) |
351 | void *buf, size_t size, size_t nmembs) | ||
352 | { | 306 | { |
307 | size_t nmembs = argv->v_nmembs; | ||
353 | struct inode *inode; | 308 | struct inode *inode; |
354 | struct nilfs_vdesc *vdesc; | 309 | struct nilfs_vdesc *vdesc; |
355 | struct buffer_head *bh, *n; | 310 | struct buffer_head *bh, *n; |
@@ -410,19 +365,10 @@ nilfs_ioctl_do_move_blocks(struct the_nilfs *nilfs, __u64 *posp, int flags, | |||
410 | return ret; | 365 | return ret; |
411 | } | 366 | } |
412 | 367 | ||
413 | static inline int nilfs_ioctl_move_blocks(struct the_nilfs *nilfs, | 368 | static int nilfs_ioctl_delete_checkpoints(struct the_nilfs *nilfs, |
414 | struct nilfs_argv *argv, | 369 | struct nilfs_argv *argv, void *buf) |
415 | int dir) | ||
416 | { | ||
417 | return nilfs_ioctl_wrap_copy(nilfs, argv, dir, | ||
418 | nilfs_ioctl_do_move_blocks); | ||
419 | } | ||
420 | |||
421 | static ssize_t | ||
422 | nilfs_ioctl_do_delete_checkpoints(struct the_nilfs *nilfs, __u64 *posp, | ||
423 | int flags, void *buf, size_t size, | ||
424 | size_t nmembs) | ||
425 | { | 370 | { |
371 | size_t nmembs = argv->v_nmembs; | ||
426 | struct inode *cpfile = nilfs->ns_cpfile; | 372 | struct inode *cpfile = nilfs->ns_cpfile; |
427 | struct nilfs_period *periods = buf; | 373 | struct nilfs_period *periods = buf; |
428 | int ret, i; | 374 | int ret, i; |
@@ -436,36 +382,21 @@ nilfs_ioctl_do_delete_checkpoints(struct the_nilfs *nilfs, __u64 *posp, | |||
436 | return nmembs; | 382 | return nmembs; |
437 | } | 383 | } |
438 | 384 | ||
439 | static inline int nilfs_ioctl_delete_checkpoints(struct the_nilfs *nilfs, | 385 | static int nilfs_ioctl_free_vblocknrs(struct the_nilfs *nilfs, |
440 | struct nilfs_argv *argv, | 386 | struct nilfs_argv *argv, void *buf) |
441 | int dir) | ||
442 | { | 387 | { |
443 | return nilfs_ioctl_wrap_copy(nilfs, argv, dir, | 388 | size_t nmembs = argv->v_nmembs; |
444 | nilfs_ioctl_do_delete_checkpoints); | 389 | int ret; |
445 | } | ||
446 | 390 | ||
447 | static ssize_t | 391 | ret = nilfs_dat_freev(nilfs_dat_inode(nilfs), buf, nmembs); |
448 | nilfs_ioctl_do_free_vblocknrs(struct the_nilfs *nilfs, __u64 *posp, int flags, | ||
449 | void *buf, size_t size, size_t nmembs) | ||
450 | { | ||
451 | int ret = nilfs_dat_freev(nilfs_dat_inode(nilfs), buf, nmembs); | ||
452 | 392 | ||
453 | return (ret < 0) ? ret : nmembs; | 393 | return (ret < 0) ? ret : nmembs; |
454 | } | 394 | } |
455 | 395 | ||
456 | static inline int nilfs_ioctl_free_vblocknrs(struct the_nilfs *nilfs, | 396 | static int nilfs_ioctl_mark_blocks_dirty(struct the_nilfs *nilfs, |
457 | struct nilfs_argv *argv, | 397 | struct nilfs_argv *argv, void *buf) |
458 | int dir) | ||
459 | { | ||
460 | return nilfs_ioctl_wrap_copy(nilfs, argv, dir, | ||
461 | nilfs_ioctl_do_free_vblocknrs); | ||
462 | } | ||
463 | |||
464 | static ssize_t | ||
465 | nilfs_ioctl_do_mark_blocks_dirty(struct the_nilfs *nilfs, __u64 *posp, | ||
466 | int flags, void *buf, size_t size, | ||
467 | size_t nmembs) | ||
468 | { | 398 | { |
399 | size_t nmembs = argv->v_nmembs; | ||
469 | struct inode *dat = nilfs_dat_inode(nilfs); | 400 | struct inode *dat = nilfs_dat_inode(nilfs); |
470 | struct nilfs_bmap *bmap = NILFS_I(dat)->i_bmap; | 401 | struct nilfs_bmap *bmap = NILFS_I(dat)->i_bmap; |
471 | struct nilfs_bdesc *bdescs = buf; | 402 | struct nilfs_bdesc *bdescs = buf; |
@@ -504,55 +435,37 @@ nilfs_ioctl_do_mark_blocks_dirty(struct the_nilfs *nilfs, __u64 *posp, | |||
504 | return nmembs; | 435 | return nmembs; |
505 | } | 436 | } |
506 | 437 | ||
507 | static inline int nilfs_ioctl_mark_blocks_dirty(struct the_nilfs *nilfs, | 438 | static int nilfs_ioctl_free_segments(struct the_nilfs *nilfs, |
508 | struct nilfs_argv *argv, | 439 | struct nilfs_argv *argv, void *buf) |
509 | int dir) | ||
510 | { | 440 | { |
511 | return nilfs_ioctl_wrap_copy(nilfs, argv, dir, | 441 | size_t nmembs = argv->v_nmembs; |
512 | nilfs_ioctl_do_mark_blocks_dirty); | 442 | struct nilfs_sb_info *sbi = nilfs->ns_writer; |
513 | } | ||
514 | |||
515 | static ssize_t | ||
516 | nilfs_ioctl_do_free_segments(struct the_nilfs *nilfs, __u64 *posp, int flags, | ||
517 | void *buf, size_t size, size_t nmembs) | ||
518 | { | ||
519 | struct nilfs_sb_info *sbi = nilfs_get_writer(nilfs); | ||
520 | int ret; | 443 | int ret; |
521 | 444 | ||
522 | if (unlikely(!sbi)) | 445 | if (unlikely(!sbi)) { |
446 | /* never happens because called for a writable mount */ | ||
447 | WARN_ON(1); | ||
523 | return -EROFS; | 448 | return -EROFS; |
449 | } | ||
524 | ret = nilfs_segctor_add_segments_to_be_freed( | 450 | ret = nilfs_segctor_add_segments_to_be_freed( |
525 | NILFS_SC(sbi), buf, nmembs); | 451 | NILFS_SC(sbi), buf, nmembs); |
526 | nilfs_put_writer(nilfs); | ||
527 | 452 | ||
528 | return (ret < 0) ? ret : nmembs; | 453 | return (ret < 0) ? ret : nmembs; |
529 | } | 454 | } |
530 | 455 | ||
531 | static inline int nilfs_ioctl_free_segments(struct the_nilfs *nilfs, | ||
532 | struct nilfs_argv *argv, | ||
533 | int dir) | ||
534 | { | ||
535 | return nilfs_ioctl_wrap_copy(nilfs, argv, dir, | ||
536 | nilfs_ioctl_do_free_segments); | ||
537 | } | ||
538 | |||
539 | int nilfs_ioctl_prepare_clean_segments(struct the_nilfs *nilfs, | 456 | int nilfs_ioctl_prepare_clean_segments(struct the_nilfs *nilfs, |
540 | void __user *argp) | 457 | struct nilfs_argv *argv, void **kbufs) |
541 | { | 458 | { |
542 | struct nilfs_argv argv[5]; | ||
543 | const char *msg; | 459 | const char *msg; |
544 | int dir, ret; | 460 | int ret; |
545 | |||
546 | if (copy_from_user(argv, argp, sizeof(argv))) | ||
547 | return -EFAULT; | ||
548 | 461 | ||
549 | dir = _IOC_WRITE; | 462 | ret = nilfs_ioctl_move_blocks(nilfs, &argv[0], kbufs[0]); |
550 | ret = nilfs_ioctl_move_blocks(nilfs, &argv[0], dir); | ||
551 | if (ret < 0) { | 463 | if (ret < 0) { |
552 | msg = "cannot read source blocks"; | 464 | msg = "cannot read source blocks"; |
553 | goto failed; | 465 | goto failed; |
554 | } | 466 | } |
555 | ret = nilfs_ioctl_delete_checkpoints(nilfs, &argv[1], dir); | 467 | |
468 | ret = nilfs_ioctl_delete_checkpoints(nilfs, &argv[1], kbufs[1]); | ||
556 | if (ret < 0) { | 469 | if (ret < 0) { |
557 | /* | 470 | /* |
558 | * can safely abort because checkpoints can be removed | 471 | * can safely abort because checkpoints can be removed |
@@ -561,7 +474,7 @@ int nilfs_ioctl_prepare_clean_segments(struct the_nilfs *nilfs, | |||
561 | msg = "cannot delete checkpoints"; | 474 | msg = "cannot delete checkpoints"; |
562 | goto failed; | 475 | goto failed; |
563 | } | 476 | } |
564 | ret = nilfs_ioctl_free_vblocknrs(nilfs, &argv[2], dir); | 477 | ret = nilfs_ioctl_free_vblocknrs(nilfs, &argv[2], kbufs[2]); |
565 | if (ret < 0) { | 478 | if (ret < 0) { |
566 | /* | 479 | /* |
567 | * can safely abort because DAT file is updated atomically | 480 | * can safely abort because DAT file is updated atomically |
@@ -570,7 +483,7 @@ int nilfs_ioctl_prepare_clean_segments(struct the_nilfs *nilfs, | |||
570 | msg = "cannot delete virtual blocks from DAT file"; | 483 | msg = "cannot delete virtual blocks from DAT file"; |
571 | goto failed; | 484 | goto failed; |
572 | } | 485 | } |
573 | ret = nilfs_ioctl_mark_blocks_dirty(nilfs, &argv[3], dir); | 486 | ret = nilfs_ioctl_mark_blocks_dirty(nilfs, &argv[3], kbufs[3]); |
574 | if (ret < 0) { | 487 | if (ret < 0) { |
575 | /* | 488 | /* |
576 | * can safely abort because the operation is nondestructive. | 489 | * can safely abort because the operation is nondestructive. |
@@ -578,7 +491,7 @@ int nilfs_ioctl_prepare_clean_segments(struct the_nilfs *nilfs, | |||
578 | msg = "cannot mark copying blocks dirty"; | 491 | msg = "cannot mark copying blocks dirty"; |
579 | goto failed; | 492 | goto failed; |
580 | } | 493 | } |
581 | ret = nilfs_ioctl_free_segments(nilfs, &argv[4], dir); | 494 | ret = nilfs_ioctl_free_segments(nilfs, &argv[4], kbufs[4]); |
582 | if (ret < 0) { | 495 | if (ret < 0) { |
583 | /* | 496 | /* |
584 | * can safely abort because this operation is atomic. | 497 | * can safely abort because this operation is atomic. |
@@ -598,9 +511,75 @@ int nilfs_ioctl_prepare_clean_segments(struct the_nilfs *nilfs, | |||
598 | static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp, | 511 | static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp, |
599 | unsigned int cmd, void __user *argp) | 512 | unsigned int cmd, void __user *argp) |
600 | { | 513 | { |
514 | struct nilfs_argv argv[5]; | ||
515 | const static size_t argsz[5] = { | ||
516 | sizeof(struct nilfs_vdesc), | ||
517 | sizeof(struct nilfs_period), | ||
518 | sizeof(__u64), | ||
519 | sizeof(struct nilfs_bdesc), | ||
520 | sizeof(__u64), | ||
521 | }; | ||
522 | void __user *base; | ||
523 | void *kbufs[5]; | ||
524 | struct the_nilfs *nilfs; | ||
525 | size_t len, nsegs; | ||
526 | int n, ret; | ||
527 | |||
601 | if (!capable(CAP_SYS_ADMIN)) | 528 | if (!capable(CAP_SYS_ADMIN)) |
602 | return -EPERM; | 529 | return -EPERM; |
603 | return nilfs_clean_segments(inode->i_sb, argp); | 530 | |
531 | if (copy_from_user(argv, argp, sizeof(argv))) | ||
532 | return -EFAULT; | ||
533 | |||
534 | nsegs = argv[4].v_nmembs; | ||
535 | if (argv[4].v_size != argsz[4]) | ||
536 | return -EINVAL; | ||
537 | /* | ||
538 | * argv[4] points to segment numbers this ioctl cleans. We | ||
539 | * use kmalloc() for its buffer because memory used for the | ||
540 | * segment numbers is enough small. | ||
541 | */ | ||
542 | kbufs[4] = memdup_user((void __user *)(unsigned long)argv[4].v_base, | ||
543 | nsegs * sizeof(__u64)); | ||
544 | if (IS_ERR(kbufs[4])) | ||
545 | return PTR_ERR(kbufs[4]); | ||
546 | |||
547 | nilfs = NILFS_SB(inode->i_sb)->s_nilfs; | ||
548 | |||
549 | for (n = 0; n < 4; n++) { | ||
550 | ret = -EINVAL; | ||
551 | if (argv[n].v_size != argsz[n]) | ||
552 | goto out_free; | ||
553 | |||
554 | if (argv[n].v_nmembs > nsegs * nilfs->ns_blocks_per_segment) | ||
555 | goto out_free; | ||
556 | |||
557 | len = argv[n].v_size * argv[n].v_nmembs; | ||
558 | base = (void __user *)(unsigned long)argv[n].v_base; | ||
559 | if (len == 0) { | ||
560 | kbufs[n] = NULL; | ||
561 | continue; | ||
562 | } | ||
563 | |||
564 | kbufs[n] = vmalloc(len); | ||
565 | if (!kbufs[n]) { | ||
566 | ret = -ENOMEM; | ||
567 | goto out_free; | ||
568 | } | ||
569 | if (copy_from_user(kbufs[n], base, len)) { | ||
570 | ret = -EFAULT; | ||
571 | vfree(kbufs[n]); | ||
572 | goto out_free; | ||
573 | } | ||
574 | } | ||
575 | |||
576 | ret = nilfs_clean_segments(inode->i_sb, argv, kbufs); | ||
577 | |||
578 | out_free: | ||
579 | while (--n > 0) | ||
580 | vfree(kbufs[n]); | ||
581 | kfree(kbufs[4]); | ||
582 | return ret; | ||
604 | } | 583 | } |
605 | 584 | ||
606 | static int nilfs_ioctl_sync(struct inode *inode, struct file *filp, | 585 | static int nilfs_ioctl_sync(struct inode *inode, struct file *filp, |
@@ -621,6 +600,33 @@ static int nilfs_ioctl_sync(struct inode *inode, struct file *filp, | |||
621 | return 0; | 600 | return 0; |
622 | } | 601 | } |
623 | 602 | ||
603 | static int nilfs_ioctl_get_info(struct inode *inode, struct file *filp, | ||
604 | unsigned int cmd, void __user *argp, | ||
605 | size_t membsz, | ||
606 | ssize_t (*dofunc)(struct the_nilfs *, | ||
607 | __u64 *, int, | ||
608 | void *, size_t, size_t)) | ||
609 | |||
610 | { | ||
611 | struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs; | ||
612 | struct nilfs_argv argv; | ||
613 | int ret; | ||
614 | |||
615 | if (copy_from_user(&argv, argp, sizeof(argv))) | ||
616 | return -EFAULT; | ||
617 | |||
618 | if (argv.v_size != membsz) | ||
619 | return -EINVAL; | ||
620 | |||
621 | ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), dofunc); | ||
622 | if (ret < 0) | ||
623 | return ret; | ||
624 | |||
625 | if (copy_to_user(argp, &argv, sizeof(argv))) | ||
626 | ret = -EFAULT; | ||
627 | return ret; | ||
628 | } | ||
629 | |||
624 | long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | 630 | long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) |
625 | { | 631 | { |
626 | struct inode *inode = filp->f_dentry->d_inode; | 632 | struct inode *inode = filp->f_dentry->d_inode; |
@@ -632,16 +638,21 @@ long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | |||
632 | case NILFS_IOCTL_DELETE_CHECKPOINT: | 638 | case NILFS_IOCTL_DELETE_CHECKPOINT: |
633 | return nilfs_ioctl_delete_checkpoint(inode, filp, cmd, argp); | 639 | return nilfs_ioctl_delete_checkpoint(inode, filp, cmd, argp); |
634 | case NILFS_IOCTL_GET_CPINFO: | 640 | case NILFS_IOCTL_GET_CPINFO: |
635 | return nilfs_ioctl_get_cpinfo(inode, filp, cmd, argp); | 641 | return nilfs_ioctl_get_info(inode, filp, cmd, argp, |
642 | sizeof(struct nilfs_cpinfo), | ||
643 | nilfs_ioctl_do_get_cpinfo); | ||
636 | case NILFS_IOCTL_GET_CPSTAT: | 644 | case NILFS_IOCTL_GET_CPSTAT: |
637 | return nilfs_ioctl_get_cpstat(inode, filp, cmd, argp); | 645 | return nilfs_ioctl_get_cpstat(inode, filp, cmd, argp); |
638 | case NILFS_IOCTL_GET_SUINFO: | 646 | case NILFS_IOCTL_GET_SUINFO: |
639 | return nilfs_ioctl_get_suinfo(inode, filp, cmd, argp); | 647 | return nilfs_ioctl_get_info(inode, filp, cmd, argp, |
648 | sizeof(struct nilfs_suinfo), | ||
649 | nilfs_ioctl_do_get_suinfo); | ||
640 | case NILFS_IOCTL_GET_SUSTAT: | 650 | case NILFS_IOCTL_GET_SUSTAT: |
641 | return nilfs_ioctl_get_sustat(inode, filp, cmd, argp); | 651 | return nilfs_ioctl_get_sustat(inode, filp, cmd, argp); |
642 | case NILFS_IOCTL_GET_VINFO: | 652 | case NILFS_IOCTL_GET_VINFO: |
643 | /* XXX: rename to ??? */ | 653 | return nilfs_ioctl_get_info(inode, filp, cmd, argp, |
644 | return nilfs_ioctl_get_vinfo(inode, filp, cmd, argp); | 654 | sizeof(struct nilfs_vinfo), |
655 | nilfs_ioctl_do_get_vinfo); | ||
645 | case NILFS_IOCTL_GET_BDESCS: | 656 | case NILFS_IOCTL_GET_BDESCS: |
646 | return nilfs_ioctl_get_bdescs(inode, filp, cmd, argp); | 657 | return nilfs_ioctl_get_bdescs(inode, filp, cmd, argp); |
647 | case NILFS_IOCTL_CLEAN_SEGMENTS: | 658 | case NILFS_IOCTL_CLEAN_SEGMENTS: |
diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c index 47dd815433fd..bb78745a0e30 100644 --- a/fs/nilfs2/mdt.c +++ b/fs/nilfs2/mdt.c | |||
@@ -77,19 +77,22 @@ static int nilfs_mdt_create_block(struct inode *inode, unsigned long block, | |||
77 | void *)) | 77 | void *)) |
78 | { | 78 | { |
79 | struct the_nilfs *nilfs = NILFS_MDT(inode)->mi_nilfs; | 79 | struct the_nilfs *nilfs = NILFS_MDT(inode)->mi_nilfs; |
80 | struct nilfs_sb_info *writer = NULL; | ||
81 | struct super_block *sb = inode->i_sb; | 80 | struct super_block *sb = inode->i_sb; |
82 | struct nilfs_transaction_info ti; | 81 | struct nilfs_transaction_info ti; |
83 | struct buffer_head *bh; | 82 | struct buffer_head *bh; |
84 | int err; | 83 | int err; |
85 | 84 | ||
86 | if (!sb) { | 85 | if (!sb) { |
87 | writer = nilfs_get_writer(nilfs); | 86 | /* |
88 | if (!writer) { | 87 | * Make sure this function is not called from any |
88 | * read-only context. | ||
89 | */ | ||
90 | if (!nilfs->ns_writer) { | ||
91 | WARN_ON(1); | ||
89 | err = -EROFS; | 92 | err = -EROFS; |
90 | goto out; | 93 | goto out; |
91 | } | 94 | } |
92 | sb = writer->s_super; | 95 | sb = nilfs->ns_writer->s_super; |
93 | } | 96 | } |
94 | 97 | ||
95 | nilfs_transaction_begin(sb, &ti, 0); | 98 | nilfs_transaction_begin(sb, &ti, 0); |
@@ -127,8 +130,6 @@ static int nilfs_mdt_create_block(struct inode *inode, unsigned long block, | |||
127 | err = nilfs_transaction_commit(sb); | 130 | err = nilfs_transaction_commit(sb); |
128 | else | 131 | else |
129 | nilfs_transaction_abort(sb); | 132 | nilfs_transaction_abort(sb); |
130 | if (writer) | ||
131 | nilfs_put_writer(nilfs); | ||
132 | out: | 133 | out: |
133 | return err; | 134 | return err; |
134 | } | 135 | } |
@@ -299,7 +300,7 @@ int nilfs_mdt_delete_block(struct inode *inode, unsigned long block) | |||
299 | int err; | 300 | int err; |
300 | 301 | ||
301 | err = nilfs_bmap_delete(ii->i_bmap, block); | 302 | err = nilfs_bmap_delete(ii->i_bmap, block); |
302 | if (likely(!err)) { | 303 | if (!err || err == -ENOENT) { |
303 | nilfs_mdt_mark_dirty(inode); | 304 | nilfs_mdt_mark_dirty(inode); |
304 | nilfs_mdt_forget_block(inode, block); | 305 | nilfs_mdt_forget_block(inode, block); |
305 | } | 306 | } |
diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h index 3d0c18a16db1..da6fc0bba2e5 100644 --- a/fs/nilfs2/nilfs.h +++ b/fs/nilfs2/nilfs.h | |||
@@ -236,7 +236,8 @@ extern int nilfs_sync_file(struct file *, struct dentry *, int); | |||
236 | 236 | ||
237 | /* ioctl.c */ | 237 | /* ioctl.c */ |
238 | long nilfs_ioctl(struct file *, unsigned int, unsigned long); | 238 | long nilfs_ioctl(struct file *, unsigned int, unsigned long); |
239 | int nilfs_ioctl_prepare_clean_segments(struct the_nilfs *, void __user *); | 239 | int nilfs_ioctl_prepare_clean_segments(struct the_nilfs *, struct nilfs_argv *, |
240 | void **); | ||
240 | 241 | ||
241 | /* inode.c */ | 242 | /* inode.c */ |
242 | extern struct inode *nilfs_new_inode(struct inode *, int); | 243 | extern struct inode *nilfs_new_inode(struct inode *, int); |
diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c index 1bfbba9c0e9a..a2692bbc7b50 100644 --- a/fs/nilfs2/page.c +++ b/fs/nilfs2/page.c | |||
@@ -128,7 +128,8 @@ void nilfs_forget_buffer(struct buffer_head *bh) | |||
128 | 128 | ||
129 | lock_buffer(bh); | 129 | lock_buffer(bh); |
130 | clear_buffer_nilfs_volatile(bh); | 130 | clear_buffer_nilfs_volatile(bh); |
131 | if (test_clear_buffer_dirty(bh) && nilfs_page_buffers_clean(page)) | 131 | clear_buffer_dirty(bh); |
132 | if (nilfs_page_buffers_clean(page)) | ||
132 | __nilfs_clear_page_dirty(page); | 133 | __nilfs_clear_page_dirty(page); |
133 | 134 | ||
134 | clear_buffer_uptodate(bh); | 135 | clear_buffer_uptodate(bh); |
diff --git a/fs/nilfs2/recovery.c b/fs/nilfs2/recovery.c index 4fc081e47d70..57afa9d24061 100644 --- a/fs/nilfs2/recovery.c +++ b/fs/nilfs2/recovery.c | |||
@@ -407,6 +407,7 @@ void nilfs_dispose_segment_list(struct list_head *head) | |||
407 | } | 407 | } |
408 | 408 | ||
409 | static int nilfs_prepare_segment_for_recovery(struct the_nilfs *nilfs, | 409 | static int nilfs_prepare_segment_for_recovery(struct the_nilfs *nilfs, |
410 | struct nilfs_sb_info *sbi, | ||
410 | struct nilfs_recovery_info *ri) | 411 | struct nilfs_recovery_info *ri) |
411 | { | 412 | { |
412 | struct list_head *head = &ri->ri_used_segments; | 413 | struct list_head *head = &ri->ri_used_segments; |
@@ -421,6 +422,7 @@ static int nilfs_prepare_segment_for_recovery(struct the_nilfs *nilfs, | |||
421 | segnum[2] = ri->ri_segnum; | 422 | segnum[2] = ri->ri_segnum; |
422 | segnum[3] = ri->ri_nextnum; | 423 | segnum[3] = ri->ri_nextnum; |
423 | 424 | ||
425 | nilfs_attach_writer(nilfs, sbi); | ||
424 | /* | 426 | /* |
425 | * Releasing the next segment of the latest super root. | 427 | * Releasing the next segment of the latest super root. |
426 | * The next segment is invalidated by this recovery. | 428 | * The next segment is invalidated by this recovery. |
@@ -459,10 +461,10 @@ static int nilfs_prepare_segment_for_recovery(struct the_nilfs *nilfs, | |||
459 | nilfs->ns_pseg_offset = 0; | 461 | nilfs->ns_pseg_offset = 0; |
460 | nilfs->ns_seg_seq = ri->ri_seq + 2; | 462 | nilfs->ns_seg_seq = ri->ri_seq + 2; |
461 | nilfs->ns_nextnum = nilfs->ns_segnum = segnum[0]; | 463 | nilfs->ns_nextnum = nilfs->ns_segnum = segnum[0]; |
462 | return 0; | ||
463 | 464 | ||
464 | failed: | 465 | failed: |
465 | /* No need to recover sufile because it will be destroyed on error */ | 466 | /* No need to recover sufile because it will be destroyed on error */ |
467 | nilfs_detach_writer(nilfs, sbi); | ||
466 | return err; | 468 | return err; |
467 | } | 469 | } |
468 | 470 | ||
@@ -728,7 +730,7 @@ int nilfs_recover_logical_segments(struct the_nilfs *nilfs, | |||
728 | goto failed; | 730 | goto failed; |
729 | 731 | ||
730 | if (ri->ri_need_recovery == NILFS_RECOVERY_ROLLFORWARD_DONE) { | 732 | if (ri->ri_need_recovery == NILFS_RECOVERY_ROLLFORWARD_DONE) { |
731 | err = nilfs_prepare_segment_for_recovery(nilfs, ri); | 733 | err = nilfs_prepare_segment_for_recovery(nilfs, sbi, ri); |
732 | if (unlikely(err)) { | 734 | if (unlikely(err)) { |
733 | printk(KERN_ERR "NILFS: Error preparing segments for " | 735 | printk(KERN_ERR "NILFS: Error preparing segments for " |
734 | "recovery.\n"); | 736 | "recovery.\n"); |
diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index fb70ec3be20e..22c7f65c2403 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c | |||
@@ -2589,7 +2589,8 @@ nilfs_remove_written_gcinodes(struct the_nilfs *nilfs, struct list_head *head) | |||
2589 | } | 2589 | } |
2590 | } | 2590 | } |
2591 | 2591 | ||
2592 | int nilfs_clean_segments(struct super_block *sb, void __user *argp) | 2592 | int nilfs_clean_segments(struct super_block *sb, struct nilfs_argv *argv, |
2593 | void **kbufs) | ||
2593 | { | 2594 | { |
2594 | struct nilfs_sb_info *sbi = NILFS_SB(sb); | 2595 | struct nilfs_sb_info *sbi = NILFS_SB(sb); |
2595 | struct nilfs_sc_info *sci = NILFS_SC(sbi); | 2596 | struct nilfs_sc_info *sci = NILFS_SC(sbi); |
@@ -2606,7 +2607,7 @@ int nilfs_clean_segments(struct super_block *sb, void __user *argp) | |||
2606 | err = nilfs_init_gcdat_inode(nilfs); | 2607 | err = nilfs_init_gcdat_inode(nilfs); |
2607 | if (unlikely(err)) | 2608 | if (unlikely(err)) |
2608 | goto out_unlock; | 2609 | goto out_unlock; |
2609 | err = nilfs_ioctl_prepare_clean_segments(nilfs, argp); | 2610 | err = nilfs_ioctl_prepare_clean_segments(nilfs, argv, kbufs); |
2610 | if (unlikely(err)) | 2611 | if (unlikely(err)) |
2611 | goto out_unlock; | 2612 | goto out_unlock; |
2612 | 2613 | ||
diff --git a/fs/nilfs2/segment.h b/fs/nilfs2/segment.h index a98fc1ed0bbb..476bdd5df5be 100644 --- a/fs/nilfs2/segment.h +++ b/fs/nilfs2/segment.h | |||
@@ -222,7 +222,8 @@ extern int nilfs_construct_segment(struct super_block *); | |||
222 | extern int nilfs_construct_dsync_segment(struct super_block *, struct inode *, | 222 | extern int nilfs_construct_dsync_segment(struct super_block *, struct inode *, |
223 | loff_t, loff_t); | 223 | loff_t, loff_t); |
224 | extern void nilfs_flush_segment(struct super_block *, ino_t); | 224 | extern void nilfs_flush_segment(struct super_block *, ino_t); |
225 | extern int nilfs_clean_segments(struct super_block *, void __user *); | 225 | extern int nilfs_clean_segments(struct super_block *, struct nilfs_argv *, |
226 | void **); | ||
226 | 227 | ||
227 | extern int nilfs_segctor_add_segments_to_be_freed(struct nilfs_sc_info *, | 228 | extern int nilfs_segctor_add_segments_to_be_freed(struct nilfs_sc_info *, |
228 | __u64 *, size_t); | 229 | __u64 *, size_t); |