static int test_inode_busy(struct super_block *sb, aufs_bindex_t bindex, unsigned int sigen, const unsigned int verbose) { int err; unsigned long long max, ull; struct inode *i, **array; aufs_bindex_t bstart, bend; array = au_iarray_alloc(sb, &max); err = PTR_ERR(array); if (IS_ERR(array)) goto out; err = 0; AuDbg("b%d\n", bindex); for (ull = 0; !err && ull < max; ull++) { i = array[ull]; if (i->i_ino == AUFS_ROOT_INO) continue; /* AuDbgInode(i); */ if (au_iigen(i, NULL) == sigen) ii_read_lock_child(i); else { ii_write_lock_child(i); err = au_refresh_hinode_self(i); au_iigen_dec(i); if (!err) ii_downgrade_lock(i); else { ii_write_unlock(i); break; } } bstart = au_ibstart(i); bend = au_ibend(i); if (bstart <= bindex && bindex <= bend && au_h_iptr(i, bindex) && au_test_ibusy(i, bstart, bend)) { err = -EBUSY; AuVerbose(verbose, "busy i%lu\n", i->i_ino); AuDbgInode(i); } ii_read_unlock(i); } au_iarray_free(array, max); out: return err; }
static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino, ino_t dir_ino) { struct dentry *dentry, *d; struct inode *inode; au_gen_t sigen; LKTRTrace("i%lu, diri%lu\n", (unsigned long)ino, (unsigned long)dir_ino); dentry = NULL; inode = ilookup(sb, ino); if (!inode) goto out; dentry = ERR_PTR(-ESTALE); sigen = au_sigen(sb); if (unlikely(is_bad_inode(inode) || IS_DEADDIR(inode) || sigen != au_iigen(inode))) goto out_iput; dentry = NULL; if (!dir_ino || S_ISDIR(inode->i_mode)) dentry = d_find_alias(inode); else { spin_lock(&dcache_lock); list_for_each_entry(d, &inode->i_dentry, d_alias) if (!au_test_anon(d) && d->d_parent->d_inode->i_ino == dir_ino) { dentry = dget_locked(d); break; } spin_unlock(&dcache_lock); } if (unlikely(dentry && sigen != au_digen(dentry))) { dput(dentry); dentry = ERR_PTR(-ESTALE); } out_iput: iput(inode); out: AuTraceErrPtr(dentry); return dentry; }
/* * returns the number of found lower positive dentries, * otherwise an error. */ int au_refresh_hdentry(struct dentry *dentry, mode_t type) { int npositive, err; unsigned int sigen; aufs_bindex_t bstart; struct au_dinfo *dinfo; struct super_block *sb; struct dentry *parent; DiMustWriteLock(dentry); sb = dentry->d_sb; AuDebugOn(IS_ROOT(dentry)); sigen = au_sigen(sb); parent = dget_parent(dentry); AuDebugOn(au_digen(parent) != sigen || au_iigen(parent->d_inode) != sigen); dinfo = au_di(dentry); err = au_di_realloc(dinfo, au_sbend(sb) + 1); npositive = err; if (unlikely(err)) goto out; au_do_refresh_hdentry(dinfo->di_hdentry + dinfo->di_bstart, dinfo, parent); npositive = 0; bstart = au_dbstart(parent); if (type != S_IFDIR && dinfo->di_bstart == bstart) goto out_dgen; /* success */ npositive = au_lkup_dentry(dentry, bstart, type, /*nd*/NULL); if (npositive < 0) goto out; if (dinfo->di_bwh >= 0 && dinfo->di_bwh <= dinfo->di_bstart) d_drop(dentry); out_dgen: au_update_digen(dentry); out: dput(parent); AuTraceErr(npositive); return npositive; }
/* * successful returns with iinfo write_locked * minus: errno * zero: success, matched * plus: no error, but unmatched */ static int reval_inode(struct inode *inode, struct dentry *dentry) { int err; unsigned int gen; struct au_iigen iigen; aufs_bindex_t bindex, bend; struct inode *h_inode, *h_dinode; /* * before this function, if aufs got any iinfo lock, it must be only * one, the parent dir. * it can happen by UDBA and the obsoleted inode number. */ err = -EIO; if (unlikely(inode->i_ino == parent_ino(dentry))) goto out; err = 1; ii_write_lock_new_child(inode); h_dinode = au_h_dptr(dentry, au_dbstart(dentry))->d_inode; bend = au_ibend(inode); for (bindex = au_ibstart(inode); bindex <= bend; bindex++) { h_inode = au_h_iptr(inode, bindex); if (!h_inode || h_inode != h_dinode) continue; err = 0; gen = au_iigen(inode, &iigen); if (gen == au_digen(dentry) && !au_ig_ftest(iigen.ig_flags, HALF_REFRESHED)) break; /* fully refresh inode using dentry */ err = au_refresh_hinode(inode, dentry); if (!err) au_update_iigen(inode, /*half*/0); break; } if (unlikely(err)) ii_write_unlock(inode); out: return err; }
void au_dpri_inode(struct inode *inode) { struct au_iinfo *iinfo; aufs_bindex_t bindex; int err; err = do_pri_inode(-1, inode, NULL); if (err || !au_test_aufs(inode->i_sb)) return; iinfo = au_ii(inode); if (!iinfo) return; dpri("i-1: bstart %d, bend %d, gen %d\n", iinfo->ii_bstart, iinfo->ii_bend, au_iigen(inode)); if (iinfo->ii_bstart < 0) return; for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; bindex++) do_pri_inode(bindex, iinfo->ii_hinode[0 + bindex].hi_inode, iinfo->ii_hinode[0 + bindex].hi_whdentry); }
/* * ->setattr() and ->getattr() are called in various cases. * chmod, stat: dentry is revalidated. * fchmod, fstat: file and dentry are not revalidated, additionally they may be * unhashed. * for ->setattr(), ia->ia_file is passed from ftruncate only. */ 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(dentry) != sigen || au_iigen(inode) != sigen) { parent = dget_parent(dentry); di_read_lock_parent(parent, AuLock_IR); /* returns a number of positive dentries */ err = au_refresh_hdentry(dentry, inode->i_mode & S_IFMT); if (err >= 0) err = au_refresh_hinode(inode, dentry); di_read_unlock(parent, AuLock_IR); dput(parent); } AuTraceErr(err); return err; }
/* successful returns with iinfo write_locked */ static int reval_inode(struct inode *inode, struct dentry *dentry, int *matched) { int err; aufs_bindex_t bindex, bend; struct inode *h_inode, *h_dinode; *matched = 0; /* * before this function, if aufs got any iinfo lock, it must be only * one, the parent dir. * it can happen by UDBA and the obsoleted inode number. */ err = -EIO; if (unlikely(inode->i_ino == parent_ino(dentry))) goto out; err = 0; ii_write_lock_new_child(inode); h_dinode = au_h_dptr(dentry, au_dbstart(dentry))->d_inode; bend = au_ibend(inode); for (bindex = au_ibstart(inode); bindex <= bend; bindex++) { h_inode = au_h_iptr(inode, bindex); if (h_inode && h_inode == h_dinode) { *matched = 1; err = 0; if (au_iigen(inode) != au_digen(dentry)) err = au_refresh_hinode(inode, dentry); break; } } if (unlikely(err)) ii_write_unlock(inode); 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 = 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; goto out; } 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; } if (inode && au_iigen(inode) != sigen) { AuDebugOn(IS_ROOT(dentry)); err = au_refresh_hinode(inode, dentry); if (unlikely(err)) goto out_dgrade; } di_downgrade_lock(dentry, AuLock_IR); 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_inval; } 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_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); return valid; }