/* * when removing a dir, rename it to a unique temporary whiteout-ed name first * in order to be revertible and save time for removing many child whiteouts * under the dir. * returns 1 when there are too many child whiteout and caller should remove * them asynchronously. returns 0 when the number of children is enough small to * remove now or the branch fs is a remote fs. * otherwise return an error. */ static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex, struct au_nhash *whlist, struct inode *dir) { int rmdir_later, err, dirwh; struct dentry *h_dentry; struct super_block *sb; struct inode *inode; sb = dentry->d_sb; SiMustAnyLock(sb); h_dentry = au_h_dptr(dentry, bindex); err = au_whtmp_ren(h_dentry, au_sbr(sb, bindex)); if (unlikely(err)) goto out; /* stop monitoring */ inode = d_inode(dentry); au_hn_free(au_hi(inode, bindex)); if (!au_test_fs_remote(h_dentry->d_sb)) { dirwh = au_sbi(sb)->si_dirwh; rmdir_later = (dirwh <= 1); if (!rmdir_later) rmdir_later = au_nhash_test_longer_wh(whlist, bindex, dirwh); if (rmdir_later) return rmdir_later; } err = au_whtmp_rmdir(dir, bindex, h_dentry, whlist); if (unlikely(err)) { AuIOErr("rmdir %pd, b%d failed, %d. ignored\n", h_dentry, bindex, err); err = 0; } out: AuTraceErr(err); 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) && h_d->d_inode) { 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 (!h_d->d_inode) 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); if (unlikely(err)) goto out_whtmp; a->dst_h_dentry = au_h_dptr(d, a->btgt); } /* cpup src */ if (a->dst_h_dentry->d_inode && a->src_bstart != a->btgt) { struct mutex *h_mtx = &a->src_h_dentry->d_inode->i_mutex; struct file *h_file; mutex_lock_nested(h_mtx, AuLsc_I_CHILD); AuDebugOn(au_dbstart(a->src_dentry) != a->src_bstart); h_file = au_h_open_pre(a->src_dentry, a->src_bstart); if (IS_ERR(h_file)) { err = PTR_ERR(h_file); h_file = NULL; } else err = au_sio_cpup_simple(a->src_dentry, a->btgt, -1, !AuCpup_DTIME); mutex_unlock(h_mtx); au_h_open_post(a->src_dentry, a->src_bstart, h_file); if (unlikely(err)) goto out_whtmp; } /* 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 = a->h_path.dentry->d_inode->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 */ err = 0; goto out_success; out_diropq: if (au_ftest_ren(a->flags, DIROPQ)) au_ren_rev_diropq(err, a); out_rename: if (!au_ftest_ren(a->flags, CPUP)) au_ren_rev_rename(err, a); else au_ren_rev_cpup(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; }