/* 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_warning("failed removing whtmp dir %.*s (%d), " "ignored.\n", AuDLNPair(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; }
/* * 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; }