static int simple_reval_dpath(struct dentry *dentry, unsigned int sigen) { int err; struct dentry *parent; struct inode *inode; inode = dentry->d_inode; if (au_digen(dentry) == sigen && au_iigen(inode) == sigen) return 0; parent = dget_parent(dentry); di_read_lock_parent(parent, AuLock_IR); AuDebugOn(au_digen(parent) != sigen || au_iigen(parent->d_inode) != sigen); au_dbg_verify_gen(parent, sigen); /* 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); 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; 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; }
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_DIRS)) return simple_reval_dpath(dentry, sigen); /* slow loop, keep it simple and stupid */ /* cf: au_cpup_dirs() */ err = 0; parent = NULL; while (au_digen(dentry) != sigen || au_iigen(dentry->d_inode) != sigen) { d = dentry; while (1) { dput(parent); parent = dget_parent(d); if (au_digen(parent) == sigen && au_iigen(parent->d_inode) == sigen) break; d = parent; } inode = d->d_inode; if (d != dentry) di_write_lock_child(d); /* someone might update our dentry while we were sleeping */ if (au_digen(d) != sigen || au_iigen(d->d_inode) != sigen) { di_read_lock_parent(parent, AuLock_IR); /* returns a number of positive dentries */ err = au_refresh_hdentry(d, inode->i_mode & S_IFMT); if (err >= 0) err = au_refresh_hinode(inode, d); di_read_unlock(parent, AuLock_IR); } if (d != dentry) di_write_unlock(d); dput(parent); if (unlikely(err)) break; } return err; }
int au_finfo_init(struct file *file, struct au_fidir *fidir) { int err; unsigned long ul; struct au_finfo *finfo; struct dentry *dentry; err = -ENOMEM; dentry = file->f_dentry; finfo = au_cache_alloc_finfo(); if (unlikely(!finfo)) goto out; err = 0; au_rw_write_lock(&finfo->fi_rwsem); finfo->fi_btop = -1; finfo->fi_hdir = fidir; atomic_set(&finfo->fi_generation, au_digen(dentry)); /* smp_mb(); */ /* atomic_set */ /* cf. au_store_oflag() */ ul = (unsigned long)file->private_data; file->f_mode |= (ul & FMODE_EXEC); file->private_data = finfo; out: return err; }
int au_finfo_init(struct file *file, struct au_fidir *fidir) { int err, lc_idx; struct au_finfo *finfo; struct dentry *dentry; err = -ENOMEM; dentry = file->f_dentry; finfo = au_cache_alloc_finfo(); if (unlikely(!finfo)) goto out; err = 0; au_nfiles_inc(dentry->d_sb); lc_idx = AuLcNonDir_FIINFO; if (fidir) lc_idx = AuLcDir_FIINFO; au_rw_class(&finfo->fi_rwsem, au_lc_key + lc_idx); au_rw_write_lock(&finfo->fi_rwsem); finfo->fi_btop = -1; finfo->fi_hdir = fidir; atomic_set(&finfo->fi_generation, au_digen(dentry)); /* smp_mb(); */ /* atomic_set */ file->private_data = finfo; out: return err; }
int au_finfo_init(struct file *file) { struct au_finfo *finfo; struct dentry *dentry; unsigned long ul; dentry = file->f_dentry; finfo = au_cache_alloc_finfo(); if (unlikely(!finfo)) goto out; finfo->fi_hfile = kcalloc(au_sbend(dentry->d_sb) + 1, sizeof(*finfo->fi_hfile), GFP_NOFS); if (unlikely(!finfo->fi_hfile)) goto out_finfo; au_rw_init_wlock(&finfo->fi_rwsem); finfo->fi_bstart = -1; finfo->fi_bend = -1; atomic_set(&finfo->fi_generation, au_digen(dentry)); /* smp_mb(); */ /* atomic_set */ /* cf. au_store_oflag() */ /* suppress a warning in lp64 */ ul = (unsigned long)file->private_data; file->f_mode |= (vfsub_uint_to_fmode(ul) & FMODE_EXEC); file->private_data = finfo; return 0; /* success */ out_finfo: au_cache_free_finfo(finfo); out: return -ENOMEM; }
void au_dbg_verify_nondir_parent(struct dentry *dentry, unsigned int sigen) { struct dentry *parent; parent = dget_parent(dentry); AuDebugOn(S_ISDIR(dentry->d_inode->i_mode) || au_digen(parent) != sigen); dput(parent); }
int au_digen_test(struct dentry *dentry, unsigned int sigen) { int err; err = 0; if (unlikely(au_digen(dentry) != sigen || au_iigen_test(dentry->d_inode, sigen))) err = -EIO; 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; }
/* * 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; }
/* * 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; }
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(&dpages, parent, /*do_include*/1, NULL, NULL); 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(dentries[j]) != sigen); } au_dpages_free(&dpages); }
void au_dpri_dentry(struct dentry *dentry) { struct au_dinfo *dinfo; aufs_bindex_t bindex; int err; err = do_pri_dentry(-1, dentry); if (err || !au_test_aufs(dentry->d_sb)) return; dinfo = au_di(dentry); if (!dinfo) return; dpri("d-1: bstart %d, bend %d, bwh %d, bdiropq %d, gen %d\n", dinfo->di_bstart, dinfo->di_bend, dinfo->di_bwh, dinfo->di_bdiropq, au_digen(dentry)); if (dinfo->di_bstart < 0) return; for (bindex = dinfo->di_bstart; bindex <= dinfo->di_bend; bindex++) do_pri_dentry(bindex, dinfo->di_hdentry[0 + bindex].hd_dentry); }
/* * ->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; }
void au_update_figen(struct file *file) { atomic_set(&au_fi(file)->fi_generation, au_digen(file->f_dentry)); /* smp_mb(); */ /* atomic_set */ }