diff options
Diffstat (limited to 'fs/gfs2/ops_inode.c')
-rw-r--r-- | fs/gfs2/ops_inode.c | 71 |
1 files changed, 60 insertions, 11 deletions
diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c index 35f6f032a026..534e1e2c65ca 100644 --- a/fs/gfs2/ops_inode.c +++ b/fs/gfs2/ops_inode.c | |||
@@ -589,6 +589,54 @@ static int gfs2_mknod(struct inode *dir, struct dentry *dentry, int mode, | |||
589 | return 0; | 589 | return 0; |
590 | } | 590 | } |
591 | 591 | ||
592 | /* | ||
593 | * gfs2_ok_to_move - check if it's ok to move a directory to another directory | ||
594 | * @this: move this | ||
595 | * @to: to here | ||
596 | * | ||
597 | * Follow @to back to the root and make sure we don't encounter @this | ||
598 | * Assumes we already hold the rename lock. | ||
599 | * | ||
600 | * Returns: errno | ||
601 | */ | ||
602 | |||
603 | static int gfs2_ok_to_move(struct gfs2_inode *this, struct gfs2_inode *to) | ||
604 | { | ||
605 | struct inode *dir = &to->i_inode; | ||
606 | struct super_block *sb = dir->i_sb; | ||
607 | struct inode *tmp; | ||
608 | struct qstr dotdot; | ||
609 | int error = 0; | ||
610 | |||
611 | gfs2_str2qstr(&dotdot, ".."); | ||
612 | |||
613 | igrab(dir); | ||
614 | |||
615 | for (;;) { | ||
616 | if (dir == &this->i_inode) { | ||
617 | error = -EINVAL; | ||
618 | break; | ||
619 | } | ||
620 | if (dir == sb->s_root->d_inode) { | ||
621 | error = 0; | ||
622 | break; | ||
623 | } | ||
624 | |||
625 | tmp = gfs2_lookupi(dir, &dotdot, 1); | ||
626 | if (IS_ERR(tmp)) { | ||
627 | error = PTR_ERR(tmp); | ||
628 | break; | ||
629 | } | ||
630 | |||
631 | iput(dir); | ||
632 | dir = tmp; | ||
633 | } | ||
634 | |||
635 | iput(dir); | ||
636 | |||
637 | return error; | ||
638 | } | ||
639 | |||
592 | /** | 640 | /** |
593 | * gfs2_rename - Rename a file | 641 | * gfs2_rename - Rename a file |
594 | * @odir: Parent directory of old file name | 642 | * @odir: Parent directory of old file name |
@@ -607,7 +655,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, | |||
607 | struct gfs2_inode *ip = GFS2_I(odentry->d_inode); | 655 | struct gfs2_inode *ip = GFS2_I(odentry->d_inode); |
608 | struct gfs2_inode *nip = NULL; | 656 | struct gfs2_inode *nip = NULL; |
609 | struct gfs2_sbd *sdp = GFS2_SB(odir); | 657 | struct gfs2_sbd *sdp = GFS2_SB(odir); |
610 | struct gfs2_holder ghs[5], r_gh; | 658 | struct gfs2_holder ghs[5], r_gh = { .gh_gl = NULL, }; |
611 | struct gfs2_rgrpd *nrgd; | 659 | struct gfs2_rgrpd *nrgd; |
612 | unsigned int num_gh; | 660 | unsigned int num_gh; |
613 | int dir_rename = 0; | 661 | int dir_rename = 0; |
@@ -621,19 +669,20 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, | |||
621 | return 0; | 669 | return 0; |
622 | } | 670 | } |
623 | 671 | ||
624 | /* Make sure we aren't trying to move a dirctory into it's subdir */ | ||
625 | 672 | ||
626 | if (S_ISDIR(ip->i_inode.i_mode) && odip != ndip) { | 673 | if (odip != ndip) { |
627 | dir_rename = 1; | 674 | error = gfs2_glock_nq_init(sdp->sd_rename_gl, LM_ST_EXCLUSIVE, |
628 | 675 | 0, &r_gh); | |
629 | error = gfs2_glock_nq_init(sdp->sd_rename_gl, LM_ST_EXCLUSIVE, 0, | ||
630 | &r_gh); | ||
631 | if (error) | 676 | if (error) |
632 | goto out; | 677 | goto out; |
633 | 678 | ||
634 | error = gfs2_ok_to_move(ip, ndip); | 679 | if (S_ISDIR(ip->i_inode.i_mode)) { |
635 | if (error) | 680 | dir_rename = 1; |
636 | goto out_gunlock_r; | 681 | /* don't move a dirctory into it's subdir */ |
682 | error = gfs2_ok_to_move(ip, ndip); | ||
683 | if (error) | ||
684 | goto out_gunlock_r; | ||
685 | } | ||
637 | } | 686 | } |
638 | 687 | ||
639 | num_gh = 1; | 688 | num_gh = 1; |
@@ -829,7 +878,7 @@ out_gunlock: | |||
829 | gfs2_holder_uninit(ghs + x); | 878 | gfs2_holder_uninit(ghs + x); |
830 | } | 879 | } |
831 | out_gunlock_r: | 880 | out_gunlock_r: |
832 | if (dir_rename) | 881 | if (r_gh.gh_gl) |
833 | gfs2_glock_dq_uninit(&r_gh); | 882 | gfs2_glock_dq_uninit(&r_gh); |
834 | out: | 883 | out: |
835 | return error; | 884 | return error; |