diff options
| author | Chao Yu <chao2.yu@samsung.com> | 2014-07-12 07:13:54 -0400 |
|---|---|---|
| committer | Jaegeuk Kim <jaegeuk@kernel.org> | 2014-07-25 11:14:08 -0400 |
| commit | 32f9bc25cbda00410e2379c58ae027e88bf24770 (patch) | |
| tree | 3f8ae051e3383f1842ef60bf0126ca13d17193f4 | |
| parent | 79e35dc3c23dd2ac9f8681361026c82b71a0b006 (diff) | |
f2fs: support ->rename2()
Now new interface ->rename2() is added to VFS, here are related description:
https://lkml.org/lkml/2014/2/7/873
https://lkml.org/lkml/2014/2/7/758
This patch adds function f2fs_rename2() to support ->rename2() including
handling both RENAME_EXCHANGE and RENAME_NOREPLACE flag.
Signed-off-by: Chao Yu <chao2.yu@samsung.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
| -rw-r--r-- | fs/f2fs/namei.c | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 1b3cae03c24f..27b03776ffd2 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c | |||
| @@ -485,6 +485,169 @@ out: | |||
| 485 | return err; | 485 | return err; |
| 486 | } | 486 | } |
| 487 | 487 | ||
| 488 | static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, | ||
| 489 | struct inode *new_dir, struct dentry *new_dentry) | ||
| 490 | { | ||
| 491 | struct super_block *sb = old_dir->i_sb; | ||
| 492 | struct f2fs_sb_info *sbi = F2FS_SB(sb); | ||
| 493 | struct inode *old_inode = old_dentry->d_inode; | ||
| 494 | struct inode *new_inode = new_dentry->d_inode; | ||
| 495 | struct page *old_dir_page, *new_dir_page; | ||
| 496 | struct page *old_page, *new_page; | ||
| 497 | struct f2fs_dir_entry *old_dir_entry = NULL, *new_dir_entry = NULL; | ||
| 498 | struct f2fs_dir_entry *old_entry, *new_entry; | ||
| 499 | int old_nlink = 0, new_nlink = 0; | ||
| 500 | int err = -ENOENT; | ||
| 501 | |||
| 502 | f2fs_balance_fs(sbi); | ||
| 503 | |||
| 504 | old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page); | ||
| 505 | if (!old_entry) | ||
| 506 | goto out; | ||
| 507 | |||
| 508 | new_entry = f2fs_find_entry(new_dir, &new_dentry->d_name, &new_page); | ||
| 509 | if (!new_entry) | ||
| 510 | goto out_old; | ||
| 511 | |||
| 512 | /* prepare for updating ".." directory entry info later */ | ||
| 513 | if (old_dir != new_dir) { | ||
| 514 | if (S_ISDIR(old_inode->i_mode)) { | ||
| 515 | err = -EIO; | ||
| 516 | old_dir_entry = f2fs_parent_dir(old_inode, | ||
| 517 | &old_dir_page); | ||
| 518 | if (!old_dir_entry) | ||
| 519 | goto out_new; | ||
| 520 | } | ||
| 521 | |||
| 522 | if (S_ISDIR(new_inode->i_mode)) { | ||
| 523 | err = -EIO; | ||
| 524 | new_dir_entry = f2fs_parent_dir(new_inode, | ||
| 525 | &new_dir_page); | ||
| 526 | if (!new_dir_entry) | ||
| 527 | goto out_old_dir; | ||
| 528 | } | ||
| 529 | } | ||
| 530 | |||
| 531 | /* | ||
| 532 | * If cross rename between file and directory those are not | ||
| 533 | * in the same directory, we will inc nlink of file's parent | ||
| 534 | * later, so we should check upper boundary of its nlink. | ||
| 535 | */ | ||
| 536 | if ((!old_dir_entry || !new_dir_entry) && | ||
| 537 | old_dir_entry != new_dir_entry) { | ||
| 538 | old_nlink = old_dir_entry ? -1 : 1; | ||
| 539 | new_nlink = -old_nlink; | ||
| 540 | err = -EMLINK; | ||
| 541 | if ((old_nlink > 0 && old_inode->i_nlink >= F2FS_LINK_MAX) || | ||
| 542 | (new_nlink > 0 && new_inode->i_nlink >= F2FS_LINK_MAX)) | ||
| 543 | goto out_new_dir; | ||
| 544 | } | ||
| 545 | |||
| 546 | f2fs_lock_op(sbi); | ||
| 547 | |||
| 548 | err = update_dent_inode(old_inode, &new_dentry->d_name); | ||
| 549 | if (err) | ||
| 550 | goto out_unlock; | ||
| 551 | |||
| 552 | err = update_dent_inode(new_inode, &old_dentry->d_name); | ||
| 553 | if (err) | ||
| 554 | goto out_undo; | ||
| 555 | |||
| 556 | /* update ".." directory entry info of old dentry */ | ||
| 557 | if (old_dir_entry) | ||
| 558 | f2fs_set_link(old_inode, old_dir_entry, old_dir_page, new_dir); | ||
| 559 | |||
| 560 | /* update ".." directory entry info of new dentry */ | ||
| 561 | if (new_dir_entry) | ||
| 562 | f2fs_set_link(new_inode, new_dir_entry, new_dir_page, old_dir); | ||
| 563 | |||
| 564 | /* update directory entry info of old dir inode */ | ||
| 565 | f2fs_set_link(old_dir, old_entry, old_page, new_inode); | ||
| 566 | |||
| 567 | down_write(&F2FS_I(old_inode)->i_sem); | ||
| 568 | file_lost_pino(old_inode); | ||
| 569 | up_write(&F2FS_I(old_inode)->i_sem); | ||
| 570 | |||
| 571 | update_inode_page(old_inode); | ||
| 572 | |||
| 573 | old_dir->i_ctime = CURRENT_TIME; | ||
| 574 | if (old_nlink) { | ||
| 575 | down_write(&F2FS_I(old_dir)->i_sem); | ||
| 576 | if (old_nlink < 0) | ||
| 577 | drop_nlink(old_dir); | ||
| 578 | else | ||
| 579 | inc_nlink(old_dir); | ||
| 580 | up_write(&F2FS_I(old_dir)->i_sem); | ||
| 581 | } | ||
| 582 | mark_inode_dirty(old_dir); | ||
| 583 | update_inode_page(old_dir); | ||
| 584 | |||
| 585 | /* update directory entry info of new dir inode */ | ||
| 586 | f2fs_set_link(new_dir, new_entry, new_page, old_inode); | ||
| 587 | |||
| 588 | down_write(&F2FS_I(new_inode)->i_sem); | ||
| 589 | file_lost_pino(new_inode); | ||
| 590 | up_write(&F2FS_I(new_inode)->i_sem); | ||
| 591 | |||
| 592 | update_inode_page(new_inode); | ||
| 593 | |||
| 594 | new_dir->i_ctime = CURRENT_TIME; | ||
| 595 | if (new_nlink) { | ||
| 596 | down_write(&F2FS_I(new_dir)->i_sem); | ||
| 597 | if (new_nlink < 0) | ||
| 598 | drop_nlink(new_dir); | ||
| 599 | else | ||
| 600 | inc_nlink(new_dir); | ||
| 601 | up_write(&F2FS_I(new_dir)->i_sem); | ||
| 602 | } | ||
| 603 | mark_inode_dirty(new_dir); | ||
| 604 | update_inode_page(new_dir); | ||
| 605 | |||
| 606 | f2fs_unlock_op(sbi); | ||
| 607 | return 0; | ||
| 608 | out_undo: | ||
| 609 | /* Still we may fail to recover name info of f2fs_inode here */ | ||
| 610 | update_dent_inode(old_inode, &old_dentry->d_name); | ||
| 611 | out_unlock: | ||
| 612 | f2fs_unlock_op(sbi); | ||
| 613 | out_new_dir: | ||
| 614 | if (new_dir_entry) { | ||
| 615 | kunmap(new_dir_page); | ||
| 616 | f2fs_put_page(new_dir_page, 0); | ||
| 617 | } | ||
| 618 | out_old_dir: | ||
| 619 | if (old_dir_entry) { | ||
| 620 | kunmap(old_dir_page); | ||
| 621 | f2fs_put_page(old_dir_page, 0); | ||
| 622 | } | ||
| 623 | out_new: | ||
| 624 | kunmap(new_page); | ||
| 625 | f2fs_put_page(new_page, 0); | ||
| 626 | out_old: | ||
| 627 | kunmap(old_page); | ||
| 628 | f2fs_put_page(old_page, 0); | ||
| 629 | out: | ||
| 630 | return err; | ||
| 631 | } | ||
| 632 | |||
| 633 | static int f2fs_rename2(struct inode *old_dir, struct dentry *old_dentry, | ||
| 634 | struct inode *new_dir, struct dentry *new_dentry, | ||
| 635 | unsigned int flags) | ||
| 636 | { | ||
| 637 | if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE)) | ||
| 638 | return -EINVAL; | ||
| 639 | |||
| 640 | if (flags & RENAME_EXCHANGE) { | ||
| 641 | return f2fs_cross_rename(old_dir, old_dentry, | ||
| 642 | new_dir, new_dentry); | ||
| 643 | } | ||
| 644 | /* | ||
| 645 | * VFS has already handled the new dentry existence case, | ||
| 646 | * here, we just deal with "RENAME_NOREPLACE" as regular rename. | ||
| 647 | */ | ||
| 648 | return f2fs_rename(old_dir, old_dentry, new_dir, new_dentry); | ||
| 649 | } | ||
| 650 | |||
| 488 | static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) | 651 | static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) |
| 489 | { | 652 | { |
| 490 | struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb); | 653 | struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb); |
| @@ -542,6 +705,7 @@ const struct inode_operations f2fs_dir_inode_operations = { | |||
| 542 | .rmdir = f2fs_rmdir, | 705 | .rmdir = f2fs_rmdir, |
| 543 | .mknod = f2fs_mknod, | 706 | .mknod = f2fs_mknod, |
| 544 | .rename = f2fs_rename, | 707 | .rename = f2fs_rename, |
| 708 | .rename2 = f2fs_rename2, | ||
| 545 | .tmpfile = f2fs_tmpfile, | 709 | .tmpfile = f2fs_tmpfile, |
| 546 | .getattr = f2fs_getattr, | 710 | .getattr = f2fs_getattr, |
| 547 | .setattr = f2fs_setattr, | 711 | .setattr = f2fs_setattr, |
