diff options
Diffstat (limited to 'fs/aufs/i_op_ren.c')
-rw-r--r-- | fs/aufs/i_op_ren.c | 1017 |
1 files changed, 0 insertions, 1017 deletions
diff --git a/fs/aufs/i_op_ren.c b/fs/aufs/i_op_ren.c deleted file mode 100644 index 4db778057..000000000 --- a/fs/aufs/i_op_ren.c +++ /dev/null @@ -1,1017 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * inode operation (rename entry) - * todo: this is crazy monster - */ - -#include "aufs.h" - -enum { AuSRC, AuDST, AuSrcDst }; -enum { AuPARENT, AuCHILD, AuParentChild }; - -#define AuRen_ISDIR 1 -#define AuRen_ISSAMEDIR (1 << 1) -#define AuRen_WHSRC (1 << 2) -#define AuRen_WHDST (1 << 3) -#define AuRen_MNT_WRITE (1 << 4) -#define AuRen_DT_DSTDIR (1 << 5) -#define AuRen_DIROPQ (1 << 6) -#define au_ftest_ren(flags, name) ((flags) & AuRen_##name) -#define au_fset_ren(flags, name) \ - do { (flags) |= AuRen_##name; } while (0) -#define au_fclr_ren(flags, name) \ - do { (flags) &= ~AuRen_##name; } while (0) - -struct au_ren_args { - struct { - struct dentry *dentry, *h_dentry, *parent, *h_parent, - *wh_dentry; - struct inode *dir, *inode; - struct au_hinode *hdir; - struct au_dtime dt[AuParentChild]; - aufs_bindex_t bstart; - } sd[AuSrcDst]; - -#define src_dentry sd[AuSRC].dentry -#define src_dir sd[AuSRC].dir -#define src_inode sd[AuSRC].inode -#define src_h_dentry sd[AuSRC].h_dentry -#define src_parent sd[AuSRC].parent -#define src_h_parent sd[AuSRC].h_parent -#define src_wh_dentry sd[AuSRC].wh_dentry -#define src_hdir sd[AuSRC].hdir -#define src_h_dir sd[AuSRC].hdir->hi_inode -#define src_dt sd[AuSRC].dt -#define src_bstart sd[AuSRC].bstart - -#define dst_dentry sd[AuDST].dentry -#define dst_dir sd[AuDST].dir -#define dst_inode sd[AuDST].inode -#define dst_h_dentry sd[AuDST].h_dentry -#define dst_parent sd[AuDST].parent -#define dst_h_parent sd[AuDST].h_parent -#define dst_wh_dentry sd[AuDST].wh_dentry -#define dst_hdir sd[AuDST].hdir -#define dst_h_dir sd[AuDST].hdir->hi_inode -#define dst_dt sd[AuDST].dt -#define dst_bstart sd[AuDST].bstart - - struct dentry *h_trap; - struct au_branch *br; - struct au_hinode *src_hinode; - struct path h_path; - struct au_nhash whlist; - aufs_bindex_t btgt, src_bwh, src_bdiropq; - - unsigned int flags; - - struct au_whtmp_rmdir *thargs; - struct dentry *h_dst; -}; - -/* ---------------------------------------------------------------------- */ - -/* - * functions for reverting. - * when an error happened in a single rename systemcall, we should revert - * everything as if nothing happend. - * we don't need to revert the copied-up/down the parent dir since they are - * harmless. - */ - -#define RevertFailure(fmt, ...) do { \ - AuIOErr("revert failure: " fmt " (%d, %d)\n", \ - ##__VA_ARGS__, err, rerr); \ - err = -EIO; \ -} while (0) - -static void au_ren_rev_diropq(int err, struct au_ren_args *a) -{ - int rerr; - - au_hn_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD); - rerr = au_diropq_remove(a->src_dentry, a->btgt); - au_hn_imtx_unlock(a->src_hinode); - au_set_dbdiropq(a->src_dentry, a->src_bdiropq); - if (rerr) - RevertFailure("remove diropq %pd", a->src_dentry); -} - -static void au_ren_rev_rename(int err, struct au_ren_args *a) -{ - int rerr; - struct inode *delegated; - - a->h_path.dentry = vfsub_lkup_one(&a->src_dentry->d_name, - a->src_h_parent); - rerr = PTR_ERR(a->h_path.dentry); - if (IS_ERR(a->h_path.dentry)) { - RevertFailure("lkup one %pd", a->src_dentry); - return; - } - - delegated = NULL; - rerr = vfsub_rename(a->dst_h_dir, - au_h_dptr(a->src_dentry, a->btgt), - a->src_h_dir, &a->h_path, &delegated); - if (unlikely(rerr == -EWOULDBLOCK)) { - pr_warn("cannot retry for NFSv4 delegation" - " for an internal rename\n"); - iput(delegated); - } - d_drop(a->h_path.dentry); - dput(a->h_path.dentry); - /* au_set_h_dptr(a->src_dentry, a->btgt, NULL); */ - if (rerr) - RevertFailure("rename %pd", a->src_dentry); -} - -static void au_ren_rev_whtmp(int err, struct au_ren_args *a) -{ - int rerr; - struct inode *delegated; - - a->h_path.dentry = vfsub_lkup_one(&a->dst_dentry->d_name, - a->dst_h_parent); - rerr = PTR_ERR(a->h_path.dentry); - if (IS_ERR(a->h_path.dentry)) { - RevertFailure("lkup one %pd", a->dst_dentry); - return; - } - if (d_is_positive(a->h_path.dentry)) { - d_drop(a->h_path.dentry); - dput(a->h_path.dentry); - return; - } - - delegated = NULL; - rerr = vfsub_rename(a->dst_h_dir, a->h_dst, a->dst_h_dir, &a->h_path, - &delegated); - if (unlikely(rerr == -EWOULDBLOCK)) { - pr_warn("cannot retry for NFSv4 delegation" - " for an internal rename\n"); - iput(delegated); - } - d_drop(a->h_path.dentry); - dput(a->h_path.dentry); - if (!rerr) - au_set_h_dptr(a->dst_dentry, a->btgt, dget(a->h_dst)); - else - RevertFailure("rename %pd", a->h_dst); -} - -static void au_ren_rev_whsrc(int err, struct au_ren_args *a) -{ - int rerr; - - a->h_path.dentry = a->src_wh_dentry; - rerr = au_wh_unlink_dentry(a->src_h_dir, &a->h_path, a->src_dentry); - au_set_dbwh(a->src_dentry, a->src_bwh); - if (rerr) - RevertFailure("unlink %pd", a->src_wh_dentry); -} -#undef RevertFailure - -/* ---------------------------------------------------------------------- */ - -/* - * when we have to copyup the renaming entry, do it with the rename-target name - * in order to minimize the cost (the later actual rename is unnecessary). - * otherwise rename it on the target branch. - */ -static int au_ren_or_cpup(struct au_ren_args *a) -{ - int err; - struct dentry *d; - struct inode *delegated; - - d = a->src_dentry; - if (au_dbstart(d) == a->btgt) { - a->h_path.dentry = a->dst_h_dentry; - if (au_ftest_ren(a->flags, DIROPQ) - && au_dbdiropq(d) == a->btgt) - au_fclr_ren(a->flags, DIROPQ); - AuDebugOn(au_dbstart(d) != a->btgt); - delegated = NULL; - err = vfsub_rename(a->src_h_dir, au_h_dptr(d, a->btgt), - a->dst_h_dir, &a->h_path, &delegated); - if (unlikely(err == -EWOULDBLOCK)) { - pr_warn("cannot retry for NFSv4 delegation" - " for an internal rename\n"); - iput(delegated); - } - } else - BUG(); - - if (!err && a->h_dst) - /* it will be set to dinfo later */ - dget(a->h_dst); - - return err; -} - -/* cf. aufs_rmdir() */ -static int au_ren_del_whtmp(struct au_ren_args *a) -{ - int err; - struct inode *dir; - - dir = a->dst_dir; - SiMustAnyLock(dir->i_sb); - if (!au_nhash_test_longer_wh(&a->whlist, a->btgt, - au_sbi(dir->i_sb)->si_dirwh) - || au_test_fs_remote(a->h_dst->d_sb)) { - err = au_whtmp_rmdir(dir, a->btgt, a->h_dst, &a->whlist); - if (unlikely(err)) - pr_warn("failed removing whtmp dir %pd (%d), " - "ignored.\n", a->h_dst, err); - } else { - au_nhash_wh_free(&a->thargs->whlist); - a->thargs->whlist = a->whlist; - a->whlist.nh_num = 0; - au_whtmp_kick_rmdir(dir, a->btgt, a->h_dst, a->thargs); - dput(a->h_dst); - a->thargs = NULL; - } - - return 0; -} - -/* make it 'opaque' dir. */ -static int au_ren_diropq(struct au_ren_args *a) -{ - int err; - struct dentry *diropq; - - err = 0; - a->src_bdiropq = au_dbdiropq(a->src_dentry); - a->src_hinode = au_hi(a->src_inode, a->btgt); - au_hn_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD); - diropq = au_diropq_create(a->src_dentry, a->btgt); - au_hn_imtx_unlock(a->src_hinode); - if (IS_ERR(diropq)) - err = PTR_ERR(diropq); - else - dput(diropq); - - return err; -} - -static int do_rename(struct au_ren_args *a) -{ - int err; - struct dentry *d, *h_d; - - /* prepare workqueue args for asynchronous rmdir */ - h_d = a->dst_h_dentry; - if (au_ftest_ren(a->flags, ISDIR) && d_is_positive(h_d)) { - err = -ENOMEM; - a->thargs = au_whtmp_rmdir_alloc(a->src_dentry->d_sb, GFP_NOFS); - if (unlikely(!a->thargs)) - goto out; - a->h_dst = dget(h_d); - } - - /* create whiteout for src_dentry */ - if (au_ftest_ren(a->flags, WHSRC)) { - a->src_bwh = au_dbwh(a->src_dentry); - AuDebugOn(a->src_bwh >= 0); - a->src_wh_dentry - = au_wh_create(a->src_dentry, a->btgt, a->src_h_parent); - err = PTR_ERR(a->src_wh_dentry); - if (IS_ERR(a->src_wh_dentry)) - goto out_thargs; - } - - /* lookup whiteout for dentry */ - if (au_ftest_ren(a->flags, WHDST)) { - h_d = au_wh_lkup(a->dst_h_parent, &a->dst_dentry->d_name, - a->br); - err = PTR_ERR(h_d); - if (IS_ERR(h_d)) - goto out_whsrc; - if (d_is_negative(h_d)) - dput(h_d); - else - a->dst_wh_dentry = h_d; - } - - /* rename dentry to tmpwh */ - if (a->thargs) { - err = au_whtmp_ren(a->dst_h_dentry, a->br); - if (unlikely(err)) - goto out_whdst; - - d = a->dst_dentry; - au_set_h_dptr(d, a->btgt, NULL); - err = au_lkup_neg(d, a->btgt, /*wh*/0); - if (unlikely(err)) - goto out_whtmp; - a->dst_h_dentry = au_h_dptr(d, a->btgt); - } - - BUG_ON(d_is_positive(a->dst_h_dentry) && a->src_bstart != a->btgt); - - /* rename by vfs_rename or cpup */ - d = a->dst_dentry; - if (au_ftest_ren(a->flags, ISDIR) - && (a->dst_wh_dentry - || au_dbdiropq(d) == a->btgt - /* hide the lower to keep xino */ - || a->btgt < au_dbend(d) - || au_opt_test(au_mntflags(d->d_sb), ALWAYS_DIROPQ))) - au_fset_ren(a->flags, DIROPQ); - err = au_ren_or_cpup(a); - if (unlikely(err)) - /* leave the copied-up one */ - goto out_whtmp; - - /* make dir opaque */ - if (au_ftest_ren(a->flags, DIROPQ)) { - err = au_ren_diropq(a); - if (unlikely(err)) - goto out_rename; - } - - /* update target timestamps */ - AuDebugOn(au_dbstart(a->src_dentry) != a->btgt); - a->h_path.dentry = au_h_dptr(a->src_dentry, a->btgt); - vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/ - a->src_inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime; - - /* remove whiteout for dentry */ - if (a->dst_wh_dentry) { - a->h_path.dentry = a->dst_wh_dentry; - err = au_wh_unlink_dentry(a->dst_h_dir, &a->h_path, - a->dst_dentry); - if (unlikely(err)) - goto out_diropq; - } - - /* remove whtmp */ - if (a->thargs) - au_ren_del_whtmp(a); /* ignore this error */ - - au_fhsm_wrote(a->src_dentry->d_sb, a->btgt, /*force*/0); - err = 0; - goto out_success; - -out_diropq: - if (au_ftest_ren(a->flags, DIROPQ)) - au_ren_rev_diropq(err, a); -out_rename: - au_ren_rev_rename(err, a); - dput(a->h_dst); -out_whtmp: - if (a->thargs) - au_ren_rev_whtmp(err, a); -out_whdst: - dput(a->dst_wh_dentry); - a->dst_wh_dentry = NULL; -out_whsrc: - if (a->src_wh_dentry) - au_ren_rev_whsrc(err, a); -out_success: - dput(a->src_wh_dentry); - dput(a->dst_wh_dentry); -out_thargs: - if (a->thargs) { - dput(a->h_dst); - au_whtmp_rmdir_free(a->thargs); - a->thargs = NULL; - } -out: - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* - * test if @dentry dir can be rename destination or not. - * success means, it is a logically empty dir. - */ -static int may_rename_dstdir(struct dentry *dentry, struct au_nhash *whlist) -{ - return au_test_empty(dentry, whlist); -} - -/* - * test if @dentry dir can be rename source or not. - * if it can, return 0 and @children is filled. - * success means, - * - it is a logically empty dir. - * - or, it exists on writable branch and has no children including whiteouts - * on the lower branch. - */ -static int may_rename_srcdir(struct dentry *dentry, aufs_bindex_t btgt) -{ - int err; - unsigned int rdhash; - aufs_bindex_t bstart; - - bstart = au_dbstart(dentry); - if (bstart != btgt) { - struct au_nhash whlist; - - SiMustAnyLock(dentry->d_sb); - rdhash = au_sbi(dentry->d_sb)->si_rdhash; - if (!rdhash) - rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, - dentry)); - err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS); - if (unlikely(err)) - goto out; - err = au_test_empty(dentry, &whlist); - au_nhash_wh_free(&whlist); - goto out; - } - - if (bstart == au_dbtaildir(dentry)) - return 0; /* success */ - - err = au_test_empty_lower(dentry); - -out: - if (err == -ENOTEMPTY) { - AuWarn1("renaming dir who has child(ren) on multiple branches," - " is not supported\n"); - err = -EXDEV; - } - return err; -} - -/* side effect: sets whlist and h_dentry */ -static int au_ren_may_dir(struct au_ren_args *a) -{ - int err; - unsigned int rdhash; - struct dentry *d; - - d = a->dst_dentry; - SiMustAnyLock(d->d_sb); - - err = 0; - if (au_ftest_ren(a->flags, ISDIR) && a->dst_inode) { - rdhash = au_sbi(d->d_sb)->si_rdhash; - if (!rdhash) - rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, d)); - err = au_nhash_alloc(&a->whlist, rdhash, GFP_NOFS); - if (unlikely(err)) - goto out; - - au_set_dbstart(d, a->dst_bstart); - err = may_rename_dstdir(d, &a->whlist); - au_set_dbstart(d, a->btgt); - } - a->dst_h_dentry = au_h_dptr(d, au_dbstart(d)); - if (unlikely(err)) - goto out; - - d = a->src_dentry; - a->src_h_dentry = au_h_dptr(d, au_dbstart(d)); - if (au_ftest_ren(a->flags, ISDIR)) { - err = may_rename_srcdir(d, a->btgt); - if (unlikely(err)) { - au_nhash_wh_free(&a->whlist); - a->whlist.nh_num = 0; - } - } -out: - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* - * simple tests for rename. - * following the checks in vfs, plus the parent-child relationship. - */ -static int au_may_ren(struct au_ren_args *a) -{ - int err, isdir; - struct inode *h_inode; - - if (a->src_bstart == a->btgt) { - err = au_may_del(a->src_dentry, a->btgt, a->src_h_parent, - au_ftest_ren(a->flags, ISDIR)); - if (unlikely(err)) - goto out; - err = -EINVAL; - if (unlikely(a->src_h_dentry == a->h_trap)) - goto out; - } - - err = 0; - if (a->dst_bstart != a->btgt) - goto out; - - err = -ENOTEMPTY; - if (unlikely(a->dst_h_dentry == a->h_trap)) - goto out; - - err = -EIO; - isdir = !!au_ftest_ren(a->flags, ISDIR); - if (d_really_is_negative(a->dst_dentry)) { - if (d_is_negative(a->dst_h_dentry)) - err = au_may_add(a->dst_dentry, a->btgt, - a->dst_h_parent, isdir); - } else { - if (unlikely(d_is_negative(a->dst_h_dentry))) - goto out; - h_inode = d_inode(a->dst_h_dentry); - if (h_inode->i_nlink) - err = au_may_del(a->dst_dentry, a->btgt, - a->dst_h_parent, isdir); - } - -out: - if (unlikely(err == -ENOENT || err == -EEXIST)) - err = -EIO; - AuTraceErr(err); - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* - * locking order - * (VFS) - * - src_dir and dir by lock_rename() - * - inode if exitsts - * (aufs) - * - lock all - * + src_dentry and dentry by aufs_read_and_write_lock2() which calls, - * + si_read_lock - * + di_write_lock2_child() - * + di_write_lock_child() - * + ii_write_lock_child() - * + di_write_lock_child2() - * + ii_write_lock_child2() - * + src_parent and parent - * + di_write_lock_parent() - * + ii_write_lock_parent() - * + di_write_lock_parent2() - * + ii_write_lock_parent2() - * + lower src_dir and dir by vfsub_lock_rename() - * + verify the every relationships between child and parent. if any - * of them failed, unlock all and return -EBUSY. - */ -static void au_ren_unlock(struct au_ren_args *a) -{ - vfsub_unlock_rename(a->src_h_parent, a->src_hdir, - a->dst_h_parent, a->dst_hdir); - if (au_ftest_ren(a->flags, MNT_WRITE)) - vfsub_mnt_drop_write(au_br_mnt(a->br)); -} - -static int au_ren_lock(struct au_ren_args *a) -{ - int err; - unsigned int udba; - - err = 0; - a->src_h_parent = au_h_dptr(a->src_parent, a->btgt); - a->src_hdir = au_hi(a->src_dir, a->btgt); - a->dst_h_parent = au_h_dptr(a->dst_parent, a->btgt); - a->dst_hdir = au_hi(a->dst_dir, a->btgt); - - err = vfsub_mnt_want_write(au_br_mnt(a->br)); - if (unlikely(err)) - goto out; - au_fset_ren(a->flags, MNT_WRITE); - a->h_trap = vfsub_lock_rename(a->src_h_parent, a->src_hdir, - a->dst_h_parent, a->dst_hdir); - udba = au_opt_udba(a->src_dentry->d_sb); - if (unlikely(a->src_hdir->hi_inode != d_inode(a->src_h_parent) - || a->dst_hdir->hi_inode != d_inode(a->dst_h_parent))) - err = au_busy_or_stale(); - if (!err && au_dbstart(a->src_dentry) == a->btgt) - err = au_h_verify(a->src_h_dentry, udba, - d_inode(a->src_h_parent), a->src_h_parent, - a->br); - if (!err && au_dbstart(a->dst_dentry) == a->btgt) - err = au_h_verify(a->dst_h_dentry, udba, - d_inode(a->dst_h_parent), a->dst_h_parent, - a->br); - if (!err) - goto out; /* success */ - - err = au_busy_or_stale(); - au_ren_unlock(a); - -out: - return err; -} - -/* ---------------------------------------------------------------------- */ - -static void au_ren_refresh_dir(struct au_ren_args *a) -{ - struct inode *dir; - - dir = a->dst_dir; - dir->i_version++; - if (au_ftest_ren(a->flags, ISDIR)) { - /* is this updating defined in POSIX? */ - au_cpup_attr_timesizes(a->src_inode); - au_cpup_attr_nlink(dir, /*force*/1); - } - - au_dir_ts(dir, a->btgt); - - if (au_ftest_ren(a->flags, ISSAMEDIR)) - return; - - dir = a->src_dir; - dir->i_version++; - if (au_ftest_ren(a->flags, ISDIR)) - au_cpup_attr_nlink(dir, /*force*/1); - au_dir_ts(dir, a->btgt); -} - -static void au_ren_refresh(struct au_ren_args *a) -{ - aufs_bindex_t bend, bindex; - struct dentry *d, *h_d; - struct inode *i, *h_i; - struct super_block *sb; - - d = a->dst_dentry; - d_drop(d); - if (a->h_dst) - /* already dget-ed by au_ren_or_cpup() */ - au_set_h_dptr(d, a->btgt, a->h_dst); - - i = a->dst_inode; - if (i) { - if (!au_ftest_ren(a->flags, ISDIR)) - vfsub_drop_nlink(i); - else { - vfsub_dead_dir(i); - au_cpup_attr_timesizes(i); - } - au_update_dbrange(d, /*do_put_zero*/1); - } else { - bend = a->btgt; - for (bindex = au_dbstart(d); bindex < bend; bindex++) - au_set_h_dptr(d, bindex, NULL); - bend = au_dbend(d); - for (bindex = a->btgt + 1; bindex <= bend; bindex++) - au_set_h_dptr(d, bindex, NULL); - au_update_dbrange(d, /*do_put_zero*/0); - } - - d = a->src_dentry; - au_set_dbwh(d, -1); - bend = au_dbend(d); - for (bindex = a->btgt + 1; bindex <= bend; bindex++) { - h_d = au_h_dptr(d, bindex); - if (h_d) - au_set_h_dptr(d, bindex, NULL); - } - au_set_dbend(d, a->btgt); - - sb = d->d_sb; - i = a->src_inode; - if (au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(i)) - return; /* success */ - - bend = au_ibend(i); - for (bindex = a->btgt + 1; bindex <= bend; bindex++) { - h_i = au_h_iptr(i, bindex); - if (h_i) { - au_xino_write(sb, bindex, h_i->i_ino, /*ino*/0); - /* ignore this error */ - au_set_h_iptr(i, bindex, NULL, 0); - } - } - au_set_ibend(i, a->btgt); -} - -/* ---------------------------------------------------------------------- */ - -/* mainly for link(2) and rename(2) */ -int au_wbr(struct dentry *dentry, aufs_bindex_t btgt) -{ - aufs_bindex_t bdiropq, bwh; - struct dentry *parent; - struct au_branch *br; - - parent = dentry->d_parent; - IMustLock(d_inode(parent)); /* dir is locked */ - - bdiropq = au_dbdiropq(parent); - bwh = au_dbwh(dentry); - br = au_sbr(dentry->d_sb, btgt); - if (au_br_rdonly(br) - || (0 <= bdiropq && bdiropq < btgt) - || (0 <= bwh && bwh < btgt)) - btgt = -1; - - AuDbg("btgt %d\n", btgt); - return btgt; -} - -/* sets src_bstart, dst_bstart and btgt */ -static int au_ren_wbr(struct au_ren_args *a) -{ - int err; - struct au_wr_dir_args wr_dir_args = { - /* .force_btgt = -1, */ - .flags = AuWrDir_ADD_ENTRY - }; - - a->src_bstart = au_dbstart(a->src_dentry); - a->dst_bstart = au_dbstart(a->dst_dentry); - if (au_ftest_ren(a->flags, ISDIR)) - au_fset_wrdir(wr_dir_args.flags, ISDIR); - wr_dir_args.force_btgt = a->src_bstart; - if (a->dst_inode && a->dst_bstart < a->src_bstart) - wr_dir_args.force_btgt = a->dst_bstart; - wr_dir_args.force_btgt = au_wbr(a->dst_dentry, wr_dir_args.force_btgt); - err = au_wr_dir(a->dst_dentry, a->src_dentry, &wr_dir_args); - a->btgt = err; - - return err; -} - -static void au_ren_dt(struct au_ren_args *a) -{ - a->h_path.dentry = a->src_h_parent; - au_dtime_store(a->src_dt + AuPARENT, a->src_parent, &a->h_path); - if (!au_ftest_ren(a->flags, ISSAMEDIR)) { - a->h_path.dentry = a->dst_h_parent; - au_dtime_store(a->dst_dt + AuPARENT, a->dst_parent, &a->h_path); - } - - au_fclr_ren(a->flags, DT_DSTDIR); - if (!au_ftest_ren(a->flags, ISDIR)) - return; - - a->h_path.dentry = a->src_h_dentry; - au_dtime_store(a->src_dt + AuCHILD, a->src_dentry, &a->h_path); - if (d_is_positive(a->dst_h_dentry)) { - au_fset_ren(a->flags, DT_DSTDIR); - a->h_path.dentry = a->dst_h_dentry; - au_dtime_store(a->dst_dt + AuCHILD, a->dst_dentry, &a->h_path); - } -} - -static void au_ren_rev_dt(int err, struct au_ren_args *a) -{ - struct dentry *h_d; - struct mutex *h_mtx; - - au_dtime_revert(a->src_dt + AuPARENT); - if (!au_ftest_ren(a->flags, ISSAMEDIR)) - au_dtime_revert(a->dst_dt + AuPARENT); - - if (au_ftest_ren(a->flags, ISDIR) && err != -EIO) { - h_d = a->src_dt[AuCHILD].dt_h_path.dentry; - h_mtx = &d_inode(h_d)->i_mutex; - mutex_lock_nested(h_mtx, AuLsc_I_CHILD); - au_dtime_revert(a->src_dt + AuCHILD); - mutex_unlock(h_mtx); - - if (au_ftest_ren(a->flags, DT_DSTDIR)) { - h_d = a->dst_dt[AuCHILD].dt_h_path.dentry; - h_mtx = &d_inode(h_d)->i_mutex; - mutex_lock_nested(h_mtx, AuLsc_I_CHILD); - au_dtime_revert(a->dst_dt + AuCHILD); - mutex_unlock(h_mtx); - } - } -} - -/* ---------------------------------------------------------------------- */ - -int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry, - struct inode *_dst_dir, struct dentry *_dst_dentry) -{ - int err, flags; - /* reduce stack space */ - struct au_ren_args *a; - - AuDbg("%pd, %pd\n", _src_dentry, _dst_dentry); - IMustLock(_src_dir); - IMustLock(_dst_dir); - - err = -ENOMEM; - BUILD_BUG_ON(sizeof(*a) > PAGE_SIZE); - a = kzalloc(sizeof(*a), GFP_NOFS); - if (unlikely(!a)) - goto out; - - a->src_dir = _src_dir; - a->src_dentry = _src_dentry; - a->src_inode = NULL; - if (d_really_is_positive(a->src_dentry)) - a->src_inode = d_inode(a->src_dentry); - a->src_parent = a->src_dentry->d_parent; /* dir inode is locked */ - a->dst_dir = _dst_dir; - a->dst_dentry = _dst_dentry; - a->dst_inode = NULL; - if (d_really_is_positive(a->dst_dentry)) - a->dst_inode = d_inode(a->dst_dentry); - a->dst_parent = a->dst_dentry->d_parent; /* dir inode is locked */ - if (a->dst_inode) { - IMustLock(a->dst_inode); - au_igrab(a->dst_inode); - } - - err = -ENOTDIR; - flags = AuLock_FLUSH | AuLock_NOPLM | AuLock_GEN; - if (d_is_dir(a->src_dentry)) { - au_fset_ren(a->flags, ISDIR); - if (unlikely(d_really_is_positive(a->dst_dentry) - && !d_is_dir(a->dst_dentry))) - goto out_free; - err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, - AuLock_DIR | flags); - } else - err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, - flags); - if (unlikely(err)) - goto out_free; - - err = au_d_hashed_positive(a->src_dentry); - if (unlikely(err)) - goto out_unlock; - err = -ENOENT; - if (a->dst_inode) { - /* - * If it is a dir, VFS unhash dst_dentry before this - * function. It means we cannot rely upon d_unhashed(). - */ - if (unlikely(!a->dst_inode->i_nlink)) - goto out_unlock; - if (!S_ISDIR(a->dst_inode->i_mode)) { - err = au_d_hashed_positive(a->dst_dentry); - if (unlikely(err)) - goto out_unlock; - } else if (unlikely(IS_DEADDIR(a->dst_inode))) - goto out_unlock; - } else if (unlikely(d_unhashed(a->dst_dentry))) - goto out_unlock; - - /* - * is it possible? - * yes, it happend (in linux-3.3-rcN) but I don't know why. - * there may exist a problem somewhere else. - */ - err = -EINVAL; - if (unlikely(d_inode(a->dst_parent) == d_inode(a->src_dentry))) - goto out_unlock; - - au_fset_ren(a->flags, ISSAMEDIR); /* temporary */ - di_write_lock_parent(a->dst_parent); - - /* which branch we process */ - err = au_ren_wbr(a); - if (unlikely(err < 0)) - goto out_parent; - a->br = au_sbr(a->dst_dentry->d_sb, a->btgt); - a->h_path.mnt = au_br_mnt(a->br); - - /* are they available to be renamed */ - err = au_ren_may_dir(a); - if (unlikely(err)) - goto out_children; - - /* prepare the writable parent dir on the same branch */ - if (a->dst_bstart == a->btgt) { - au_fset_ren(a->flags, WHDST); - } else { - err = au_cpup_dirs(a->dst_dentry, a->btgt); - if (unlikely(err)) - goto out_children; - } - - if (a->src_dir != a->dst_dir) { - /* - * this temporary unlock is safe, - * because both dir->i_mutex are locked. - */ - di_write_unlock(a->dst_parent); - di_write_lock_parent(a->src_parent); - err = au_wr_dir_need_wh(a->src_dentry, - au_ftest_ren(a->flags, ISDIR), - &a->btgt); - di_write_unlock(a->src_parent); - di_write_lock2_parent(a->src_parent, a->dst_parent, /*isdir*/1); - au_fclr_ren(a->flags, ISSAMEDIR); - } else - err = au_wr_dir_need_wh(a->src_dentry, - au_ftest_ren(a->flags, ISDIR), - &a->btgt); - if (unlikely(err < 0)) - goto out_children; - if (err) - au_fset_ren(a->flags, WHSRC); - - /* cpup src */ - if (a->src_bstart != a->btgt) { - struct au_pin pin; - - err = au_pin(&pin, a->src_dentry, a->btgt, - au_opt_udba(a->src_dentry->d_sb), - AuPin_DI_LOCKED | AuPin_MNT_WRITE); - if (!err) { - struct au_cp_generic cpg = { - .dentry = a->src_dentry, - .bdst = a->btgt, - .bsrc = a->src_bstart, - .len = -1, - .pin = &pin, - .flags = AuCpup_DTIME | AuCpup_HOPEN - }; - AuDebugOn(au_dbstart(a->src_dentry) != a->src_bstart); - err = au_sio_cpup_simple(&cpg); - au_unpin(&pin); - } - if (unlikely(err)) - goto out_children; - a->src_bstart = a->btgt; - a->src_h_dentry = au_h_dptr(a->src_dentry, a->btgt); - au_fset_ren(a->flags, WHSRC); - } - - /* lock them all */ - err = au_ren_lock(a); - if (unlikely(err)) - /* leave the copied-up one */ - goto out_children; - - if (!au_opt_test(au_mntflags(a->dst_dir->i_sb), UDBA_NONE)) - err = au_may_ren(a); - else if (unlikely(a->dst_dentry->d_name.len > AUFS_MAX_NAMELEN)) - err = -ENAMETOOLONG; - if (unlikely(err)) - goto out_hdir; - - /* store timestamps to be revertible */ - au_ren_dt(a); - - /* here we go */ - err = do_rename(a); - if (unlikely(err)) - goto out_dt; - - /* update dir attributes */ - au_ren_refresh_dir(a); - - /* dput/iput all lower dentries */ - au_ren_refresh(a); - - goto out_hdir; /* success */ - -out_dt: - au_ren_rev_dt(err, a); -out_hdir: - au_ren_unlock(a); -out_children: - au_nhash_wh_free(&a->whlist); - if (err && a->dst_inode && a->dst_bstart != a->btgt) { - AuDbg("bstart %d, btgt %d\n", a->dst_bstart, a->btgt); - au_set_h_dptr(a->dst_dentry, a->btgt, NULL); - au_set_dbstart(a->dst_dentry, a->dst_bstart); - } -out_parent: - if (!err) - d_move(a->src_dentry, a->dst_dentry); - else { - au_update_dbstart(a->dst_dentry); - if (!a->dst_inode) - d_drop(a->dst_dentry); - } - if (au_ftest_ren(a->flags, ISSAMEDIR)) - di_write_unlock(a->dst_parent); - else - di_write_unlock2(a->src_parent, a->dst_parent); -out_unlock: - aufs_read_and_write_unlock2(a->dst_dentry, a->src_dentry); -out_free: - iput(a->dst_inode); - if (a->thargs) - au_whtmp_rmdir_free(a->thargs); - kfree(a); -out: - AuTraceErr(err); - return err; -} |