/* * if valid returns 1, otherwise 0. */ static int aufs_d_revalidate(struct dentry *dentry, struct nameidata *nd) { int valid, err; unsigned int sigen; unsigned char do_udba; struct super_block *sb; struct inode *inode; err = -EINVAL; sb = dentry->d_sb; inode = dentry->d_inode; aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW); sigen = au_sigen(sb); if (au_digen(dentry) != sigen) { AuDebugOn(IS_ROOT(dentry)); if (inode) err = au_reval_dpath(dentry, sigen); if (unlikely(err)) goto out_dgrade; AuDebugOn(au_digen(dentry) != sigen); } if (inode && au_iigen(inode) != sigen) { AuDebugOn(IS_ROOT(dentry)); err = au_refresh_hinode(inode, dentry); if (unlikely(err)) goto out_dgrade; AuDebugOn(au_iigen(inode) != sigen); } di_downgrade_lock(dentry, AuLock_IR); AuDebugOn(au_digen(dentry) != sigen); AuDebugOn(inode && au_iigen(inode) != sigen); err = -EINVAL; do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE); if (do_udba && inode) { aufs_bindex_t bstart = au_ibstart(inode); if (bstart >= 0 && au_test_higen(inode, au_h_iptr(inode, bstart))) goto out; } err = h_d_revalidate(dentry, inode, nd, do_udba); if (unlikely(!err && do_udba && au_dbstart(dentry) < 0)) /* both of real entry and whiteout found */ err = -EIO; goto out; out_dgrade: di_downgrade_lock(dentry, AuLock_IR); out: aufs_read_unlock(dentry, AuLock_IR); AuTraceErr(err); valid = !err; if (!valid) AuDbg("%.*s invalid\n", AuDLNPair(dentry)); return valid; }
/* common function to regular file and dir */ int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file), int wlock) { int err; unsigned int sigen, figen; aufs_bindex_t bstart; unsigned char pseudo_link; struct dentry *dentry; struct inode *inode; err = 0; dentry = file->f_dentry; inode = dentry->d_inode; AuDebugOn(au_special_file(inode->i_mode)); sigen = au_sigen(dentry->d_sb); fi_write_lock(file); figen = au_figen(file); di_write_lock_child(dentry); bstart = au_dbstart(dentry); pseudo_link = (bstart != au_ibstart(inode)); if (sigen == figen && !pseudo_link && au_fbstart(file) == bstart) { if (!wlock) { di_downgrade_lock(dentry, AuLock_IR); fi_downgrade_lock(file); } goto out; /* success */ } AuDbg("sigen %d, figen %d\n", sigen, figen); if (sigen != au_digen(dentry) || sigen != au_iigen(inode)) { err = au_reval_dpath(dentry, sigen); if (unlikely(err < 0)) goto out; AuDebugOn(au_digen(dentry) != sigen || au_iigen(inode) != sigen); } err = refresh_file(file, reopen); if (!err) { if (!wlock) { di_downgrade_lock(dentry, AuLock_IR); fi_downgrade_lock(file); } } else { di_write_unlock(dentry); fi_write_unlock(file); } out: return err; }
/* * if valid returns 1, otherwise 0. */ static int aufs_d_revalidate(struct dentry *dentry, struct nameidata *nd) { int valid, err; unsigned int sigen; unsigned char do_udba; struct super_block *sb; struct inode *inode; valid = 0; if (unlikely(!au_di(dentry))) goto out; valid = 1; sb = dentry->d_sb; inode = dentry->d_inode; /* * todo: very ugly * i_mutex of parent dir may be held, * but we should not return 'invalid' due to busy. */ err = aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW | AuLock_NOPLM); if (unlikely(err)) { valid = err; AuTraceErr(err); goto out; } if (unlikely(au_dbrange_test(dentry))) { err = -EINVAL; AuTraceErr(err); goto out_dgrade; } sigen = au_sigen(sb); if (au_digen_test(dentry, sigen)) { AuDebugOn(IS_ROOT(dentry)); err = au_reval_dpath(dentry, sigen); if (unlikely(err)) { AuTraceErr(err); goto out_dgrade; } } di_downgrade_lock(dentry, AuLock_IR); err = -EINVAL; if (inode && (IS_DEADDIR(inode) || !inode->i_nlink)) goto out_inval; do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE); if (do_udba && inode) { aufs_bindex_t bstart = au_ibstart(inode); struct inode *h_inode; if (bstart >= 0) { h_inode = au_h_iptr(inode, bstart); if (h_inode && au_test_higen(inode, h_inode)) goto out_inval; } } err = h_d_revalidate(dentry, inode, nd, do_udba); if (unlikely(!err && do_udba && au_dbstart(dentry) < 0)) { err = -EIO; AuDbg("both of real entry and whiteout found, %.*s, err %d\n", AuDLNPair(dentry), err); } goto out_inval; out_dgrade: di_downgrade_lock(dentry, AuLock_IR); out_inval: aufs_read_unlock(dentry, AuLock_IR); AuTraceErr(err); valid = !err; out: if (!valid) { AuDbg("%.*s invalid, %d\n", AuDLNPair(dentry), valid); d_drop(dentry); } return valid; }
/* * if valid returns 1, otherwise 0. */ static int aufs_d_revalidate(struct dentry *dentry, unsigned int flags) { int valid, err; unsigned int sigen; unsigned char do_udba; struct super_block *sb; struct inode *inode; /* todo: support rcu-walk? */ if (flags & LOOKUP_RCU) return -ECHILD; valid = 0; if (unlikely(!au_di(dentry))) goto out; valid = 1; sb = dentry->d_sb; /* * todo: very ugly * i_mutex of parent dir may be held, * but we should not return 'invalid' due to busy. */ err = aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW | AuLock_NOPLM); if (unlikely(err)) { valid = err; AuTraceErr(err); goto out; } inode = NULL; if (d_really_is_positive(dentry)) inode = d_inode(dentry); if (unlikely(inode && is_bad_inode(inode))) { err = -EINVAL; AuTraceErr(err); goto out_dgrade; } if (unlikely(au_dbrange_test(dentry))) { err = -EINVAL; AuTraceErr(err); goto out_dgrade; } sigen = au_sigen(sb); if (au_digen_test(dentry, sigen)) { AuDebugOn(IS_ROOT(dentry)); err = au_reval_dpath(dentry, sigen); if (unlikely(err)) { AuTraceErr(err); goto out_dgrade; } } di_downgrade_lock(dentry, AuLock_IR); err = -EINVAL; if (!(flags & (LOOKUP_OPEN | LOOKUP_EMPTY)) && inode && !(inode->i_state && I_LINKABLE) && (IS_DEADDIR(inode) || !inode->i_nlink)) goto out_inval; do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE); if (do_udba && inode) { aufs_bindex_t bstart = au_ibstart(inode); struct inode *h_inode; if (bstart >= 0) { h_inode = au_h_iptr(inode, bstart); if (h_inode && au_test_higen(inode, h_inode)) goto out_inval; } } err = h_d_revalidate(dentry, inode, flags, do_udba); if (unlikely(!err && do_udba && au_dbstart(dentry) < 0)) { err = -EIO; AuDbg("both of real entry and whiteout found, %p, err %d\n", dentry, err); } goto out_inval; out_dgrade: di_downgrade_lock(dentry, AuLock_IR); out_inval: aufs_read_unlock(dentry, AuLock_IR); AuTraceErr(err); valid = !err; out: if (!valid) { AuDbg("%pd invalid, %d\n", dentry, valid); d_drop(dentry); } return valid; }
/* * test if the branch is deletable or not. */ static int test_dentry_busy(struct dentry *root, aufs_bindex_t bindex, unsigned int sigen, const unsigned int verbose) { int err, i, j, ndentry; aufs_bindex_t bstart, bend; struct au_dcsub_pages dpages; struct au_dpage *dpage; struct dentry *d; err = au_dpages_init(&dpages, GFP_NOFS); if (unlikely(err)) goto out; err = au_dcsub_pages(&dpages, root, NULL, NULL); if (unlikely(err)) goto out_dpages; for (i = 0; !err && i < dpages.ndpage; i++) { dpage = dpages.dpages + i; ndentry = dpage->ndentry; for (j = 0; !err && j < ndentry; j++) { d = dpage->dentries[j]; AuDebugOn(!atomic_read(&d->d_count)); if (!au_digen_test(d, sigen)) { di_read_lock_child(d, AuLock_IR); if (unlikely(au_dbrange_test(d))) { di_read_unlock(d, AuLock_IR); continue; } } else { di_write_lock_child(d); if (unlikely(au_dbrange_test(d))) { di_write_unlock(d); continue; } err = au_reval_dpath(d, sigen); if (!err) di_downgrade_lock(d, AuLock_IR); else { di_write_unlock(d); break; } } /* AuDbgDentry(d); */ bstart = au_dbstart(d); bend = au_dbend(d); if (bstart <= bindex && bindex <= bend && au_h_dptr(d, bindex) && au_test_dbusy(d, bstart, bend)) { err = -EBUSY; AuVerbose(verbose, "busy %.*s\n", AuDLNPair(d)); AuDbgDentry(d); } di_read_unlock(d, AuLock_IR); } } out_dpages: au_dpages_free(&dpages); out: return err; }