int aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags) { int err; unsigned int sigen; struct super_block *sb; sb = d1->d_sb; err = si_read_lock(sb, flags); if (unlikely(err)) goto out; di_write_lock2_child(d1, d2, au_ftest_lock(flags, DIR)); if (au_ftest_lock(flags, GEN)) { sigen = au_sigen(sb); err = au_digen_test(d1, sigen); AuDebugOn(!err && au_dbrange_test(d1)); if (!err) { err = au_digen_test(d2, sigen); AuDebugOn(!err && au_dbrange_test(d2)); } if (unlikely(err)) aufs_read_and_write_unlock2(d1, d2); } out: return err; }
/* 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 (au_digen_test(dentry, sigen)) { err = au_reval_dpath(dentry, sigen); AuDebugOn(!err && au_digen_test(dentry, sigen)); } if (!err) 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; }
/* dentry and super_block lock. call at entry point */ int aufs_read_lock(struct dentry *dentry, int flags) { int err; struct super_block *sb; sb = dentry->d_sb; err = si_read_lock(sb, flags); if (unlikely(err)) goto out; if (au_ftest_lock(flags, DW)) di_write_lock_child(dentry); else di_read_lock_child(dentry, flags); if (au_ftest_lock(flags, GEN)) { err = au_digen_test(dentry, au_sigen(sb)); AuDebugOn(!err && au_dbrange_test(dentry)); if (unlikely(err)) aufs_read_unlock(dentry, flags); } out: return err; }
int au_reval_dpath(struct dentry *dentry, unsigned int sigen) { int err; struct dentry *d, *parent; struct inode *inode; if (!au_ftest_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIR)) return simple_reval_dpath(dentry, sigen); /* slow loop, keep it simple and stupid */ /* cf: au_cpup_dirs() */ err = 0; parent = NULL; while (au_digen_test(dentry, sigen)) { d = dentry; while (1) { dput(parent); parent = dget_parent(d); if (!au_digen_test(parent, sigen)) break; d = parent; } inode = d->d_inode; if (d != dentry) di_write_lock_child2(d); /* someone might update our dentry while we were sleeping */ if (au_digen_test(d, sigen)) { /* * todo: consolidate with simple_reval_dpath(), * do_refresh() and au_reval_for_attr(). */ di_read_lock_parent(parent, AuLock_IR); err = au_refresh_dentry(d, parent); di_read_unlock(parent, AuLock_IR); } if (d != dentry) di_write_unlock(d); dput(parent); if (unlikely(err)) break; } return err; }
/* todo: consolidate with do_refresh() and au_reval_for_attr() */ static int simple_reval_dpath(struct dentry *dentry, unsigned int sigen) { int err; struct dentry *parent; if (!au_digen_test(dentry, sigen)) return 0; parent = dget_parent(dentry); di_read_lock_parent(parent, AuLock_IR); AuDebugOn(au_digen_test(parent, sigen)); au_dbg_verify_gen(parent, sigen); err = au_refresh_dentry(dentry, parent); di_read_unlock(parent, AuLock_IR); dput(parent); AuTraceErr(err); return err; }
void au_dbg_verify_dir_parent(struct dentry *dentry, unsigned int sigen) { struct dentry *parent; parent = dget_parent(dentry); AuDebugOn(!S_ISDIR(dentry->d_inode->i_mode)); AuDebugOn(IS_ROOT(dentry)); AuDebugOn(au_digen_test(parent, sigen)); dput(parent); }
void au_dbg_verify_nondir_parent(struct dentry *dentry, unsigned int sigen) { struct dentry *parent; struct inode *inode; parent = dget_parent(dentry); inode = dentry->d_inode; AuDebugOn(inode && S_ISDIR(dentry->d_inode->i_mode)); AuDebugOn(au_digen_test(parent, sigen)); dput(parent); }
void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen) { int err, i, j; struct au_dcsub_pages dpages; struct au_dpage *dpage; struct dentry **dentries; err = au_dpages_init(&dpages, GFP_NOFS); AuDebugOn(err); err = au_dcsub_pages_rev_aufs(&dpages, parent, /*do_include*/1); AuDebugOn(err); for (i = dpages.ndpage - 1; !err && i >= 0; i--) { dpage = dpages.dpages + i; dentries = dpage->dentries; for (j = dpage->ndentry - 1; !err && j >= 0; j--) AuDebugOn(au_digen_test(dentries[j], sigen)); } au_dpages_free(&dpages); }
/* todo: consolidate with do_refresh() and simple_reval_dpath() */ static int au_reval_for_attr(struct dentry *dentry, unsigned int sigen) { int err; struct inode *inode; struct dentry *parent; err = 0; inode = dentry->d_inode; if (au_digen_test(dentry, sigen)) { parent = dget_parent(dentry); di_read_lock_parent(parent, AuLock_IR); err = au_refresh_dentry(dentry, parent); di_read_unlock(parent, AuLock_IR); dput(parent); } AuTraceErr(err); return err; }
static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { struct dentry *ret, *parent; struct inode *inode; struct super_block *sb; int err, npositive, lc_idx; IMustLock(dir); sb = dir->i_sb; err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ret = ERR_PTR(err); if (unlikely(err)) goto out; ret = ERR_PTR(-ENAMETOOLONG); if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) goto out_si; err = au_di_init(dentry); ret = ERR_PTR(err); if (unlikely(err)) goto out_si; inode = NULL; npositive = 0; /* suppress a warning */ parent = dentry->d_parent; /* dir inode is locked */ di_read_lock_parent(parent, AuLock_IR); err = au_alive_dir(parent); if (!err) err = au_digen_test(parent, au_sigen(sb)); if (!err) { npositive = au_lkup_dentry(dentry, au_dbstart(parent), /*type*/0, nd); err = npositive; } di_read_unlock(parent, AuLock_IR); ret = ERR_PTR(err); if (unlikely(err < 0)) goto out_unlock; if (npositive) { inode = au_new_inode(dentry, /*must_new*/0); ret = (void *)inode; } if (IS_ERR(inode)) { inode = NULL; goto out_unlock; } ret = d_splice_alias(inode, dentry); if (unlikely(IS_ERR(ret) && inode)) { ii_write_unlock(inode); lc_idx = AuLcNonDir_IIINFO; if (S_ISLNK(inode->i_mode)) lc_idx = AuLcSymlink_IIINFO; else if (S_ISDIR(inode->i_mode)) lc_idx = AuLcDir_IIINFO; au_rw_class(&au_ii(inode)->ii_rwsem, au_lc_key + lc_idx); iput(inode); } out_unlock: di_write_unlock(dentry); if (unlikely(IS_ERR(ret) && inode)) { lc_idx = AuLcNonDir_DIINFO; if (S_ISLNK(inode->i_mode)) lc_idx = AuLcSymlink_DIINFO; else if (S_ISDIR(inode->i_mode)) lc_idx = AuLcDir_DIINFO; au_rw_class(&au_di(dentry)->di_rwsem, au_lc_key + lc_idx); } out_si: si_read_unlock(sb); out: return ret; }
int au_refresh_dentry(struct dentry *dentry, struct dentry *parent) { int err, ebrange; unsigned int sigen; struct au_dinfo *dinfo, *tmp; struct super_block *sb; struct inode *inode; DiMustWriteLock(dentry); AuDebugOn(IS_ROOT(dentry)); AuDebugOn(!parent->d_inode); sb = dentry->d_sb; inode = dentry->d_inode; sigen = au_sigen(sb); err = au_digen_test(parent, sigen); if (unlikely(err)) goto out; dinfo = au_di(dentry); err = au_di_realloc(dinfo, au_sbend(sb) + 1); if (unlikely(err)) goto out; ebrange = au_dbrange_test(dentry); if (!ebrange) ebrange = au_do_refresh_hdentry(dentry, parent); if (d_unhashed(dentry) || ebrange) { AuDebugOn(au_dbstart(dentry) < 0 && au_dbend(dentry) >= 0); if (inode) err = au_refresh_hinode_self(inode); au_dbg_verify_dinode(dentry); if (!err) goto out_dgen; /* success */ goto out; } /* temporary dinfo */ AuDbgDentry(dentry); err = -ENOMEM; tmp = au_di_alloc(sb, AuLsc_DI_TMP); if (unlikely(!tmp)) goto out; au_di_swap(tmp, dinfo); /* returns the number of positive dentries */ /* * if current working dir is removed, it returns an error. * but the dentry is legal. */ err = au_lkup_dentry(dentry, /*bstart*/0, /*type*/0, /*nd*/NULL); AuDbgDentry(dentry); au_di_swap(tmp, dinfo); if (err == -ENOENT) err = 0; if (err >= 0) { /* compare/refresh by dinfo */ AuDbgDentry(dentry); err = au_refresh_by_dinfo(dentry, dinfo, tmp); au_dbg_verify_dinode(dentry); AuTraceErr(err); } au_rw_write_unlock(&tmp->di_rwsem); au_di_free(tmp); if (unlikely(err)) goto out; out_dgen: au_update_digen(dentry); out: if (unlikely(err && !(dentry->d_flags & DCACHE_NFSFS_RENAMED))) { AuIOErr("failed refreshing %.*s, %d\n", AuDLNPair(dentry), err); AuDbgDentry(dentry); } AuTraceErr(err); 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; }
static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { struct dentry *ret, *parent; struct inode *inode; struct super_block *sb; int err, npositive; IMustLock(dir); /* todo: support rcu-walk? */ ret = ERR_PTR(-ECHILD); if (flags & LOOKUP_RCU) goto out; ret = ERR_PTR(-ENAMETOOLONG); if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) goto out; sb = dir->i_sb; err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ret = ERR_PTR(err); if (unlikely(err)) goto out; err = au_di_init(dentry); ret = ERR_PTR(err); if (unlikely(err)) goto out_si; inode = NULL; npositive = 0; /* suppress a warning */ parent = dentry->d_parent; /* dir inode is locked */ di_read_lock_parent(parent, AuLock_IR); err = au_alive_dir(parent); if (!err) err = au_digen_test(parent, au_sigen(sb)); if (!err) { /* regardless LOOKUP_CREATE, always ALLOW_NEG */ npositive = au_lkup_dentry(dentry, au_dbtop(parent), AuLkup_ALLOW_NEG); err = npositive; } di_read_unlock(parent, AuLock_IR); ret = ERR_PTR(err); if (unlikely(err < 0)) goto out_unlock; if (npositive) { inode = au_new_inode(dentry, /*must_new*/0); if (IS_ERR(inode)) { ret = (void *)inode; inode = NULL; goto out_unlock; } } if (inode) atomic_inc(&inode->i_count); ret = d_splice_alias(inode, dentry); #if 0 if (unlikely(d_need_lookup(dentry))) { spin_lock(&dentry->d_lock); dentry->d_flags &= ~DCACHE_NEED_LOOKUP; spin_unlock(&dentry->d_lock); } else #endif if (inode) { if (!IS_ERR(ret)) { iput(inode); if (ret && ret != dentry) ii_write_unlock(inode); } else { ii_write_unlock(inode); iput(inode); inode = NULL; } } out_unlock: di_write_unlock(dentry); out_si: si_read_unlock(sb); out: return ret; }
static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { struct dentry *ret, *parent; struct inode *inode; struct super_block *sb; int err, npositive; IMustLock(dir); /* todo: support rcu-walk? */ ret = ERR_PTR(-ECHILD); if (flags & LOOKUP_RCU) goto out; ret = ERR_PTR(-ENAMETOOLONG); if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) goto out; sb = dir->i_sb; err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ret = ERR_PTR(err); if (unlikely(err)) goto out; err = au_di_init(dentry); ret = ERR_PTR(err); if (unlikely(err)) goto out_si; inode = NULL; npositive = 0; /* suppress a warning */ parent = dentry->d_parent; /* dir inode is locked */ di_read_lock_parent(parent, AuLock_IR); err = au_alive_dir(parent); if (!err) err = au_digen_test(parent, au_sigen(sb)); if (!err) { npositive = au_lkup_dentry(dentry, au_dbstart(parent), /*type*/0); err = npositive; } di_read_unlock(parent, AuLock_IR); ret = ERR_PTR(err); if (unlikely(err < 0)) goto out_unlock; if (npositive) { inode = au_new_inode(dentry, /*must_new*/0); if (IS_ERR(inode)) { ret = (void *)inode; inode = NULL; goto out_unlock; } } if (inode) atomic_inc(&inode->i_count); ret = d_splice_alias(inode, dentry); #if 0 if (unlikely(d_need_lookup(dentry))) { spin_lock(&dentry->d_lock); dentry->d_flags &= ~DCACHE_NEED_LOOKUP; spin_unlock(&dentry->d_lock); } else #endif if (inode) { if (!IS_ERR(ret)) { iput(inode); if (ret && ret != dentry) ii_write_unlock(inode); } else { ii_write_unlock(inode); iput(inode); inode = NULL; } } out_unlock: di_write_unlock(dentry); if (inode) { /* verbose coding for lock class name */ if (unlikely(S_ISLNK(inode->i_mode))) au_rw_class(&au_di(dentry)->di_rwsem, au_lc_key + AuLcSymlink_DIINFO); else if (unlikely(S_ISDIR(inode->i_mode))) au_rw_class(&au_di(dentry)->di_rwsem, au_lc_key + AuLcDir_DIINFO); else /* likely */ au_rw_class(&au_di(dentry)->di_rwsem, au_lc_key + AuLcNonDir_DIINFO); } out_si: si_read_unlock(sb); out: return ret; }