}
err = npositive;
if (unlikely(!au_opt_test(au_mntflags(dentry->d_sb), UDBA_NONE)
- && au_dbstart(dentry) < 0))
- /* both of real entry and whiteout found */
+ && au_dbstart(dentry) < 0)) {
err = -EIO;
+ AuIOErr("both of real entry and whiteout found, %.*s, err %d\n",
+ AuDLNPair(dentry), err);
+ }
out_parent:
dput(parent);
goto out;
if (unlikely(h_dentry->d_inode)) {
err = -EIO;
- AuIOErr("b%d %.*s should be negative.\n",
- bindex, AuDLNPair(h_dentry));
+ AuIOErr("%.*s should be negative on b%d.\n",
+ AuDLNPair(h_dentry), bindex);
dput(h_dentry);
goto out;
}
/* ---------------------------------------------------------------------- */
-static void au_do_refresh_hdentry(struct au_hdentry *p, struct au_dinfo *dinfo,
- struct dentry *parent)
+static int au_do_refresh_hdentry(struct dentry *dentry, struct dentry *parent)
{
- struct dentry *h_d, *h_dp;
- struct au_hdentry tmp, *q;
- struct super_block *sb;
+ int err;
aufs_bindex_t new_bindex, bindex, bend, bwh, bdiropq;
+ struct au_hdentry tmp, *p, *q;
+ struct au_dinfo *dinfo;
+ struct super_block *sb;
- AuRwMustWriteLock(&dinfo->di_rwsem);
+ DiMustWriteLock(dentry);
+ sb = dentry->d_sb;
+ dinfo = au_di(dentry);
bend = dinfo->di_bend;
bwh = dinfo->di_bwh;
bdiropq = dinfo->di_bdiropq;
+ p = dinfo->di_hdentry + dinfo->di_bstart;
for (bindex = dinfo->di_bstart; bindex <= bend; bindex++, p++) {
- h_d = p->hd_dentry;
- if (!h_d)
+ if (!p->hd_dentry)
continue;
- h_dp = dget_parent(h_d);
- if (h_dp == au_h_dptr(parent, bindex)) {
- dput(h_dp);
+ new_bindex = au_br_index(sb, p->hd_id);
+ if (new_bindex == bindex)
continue;
- }
- new_bindex = au_find_dbindex(parent, h_dp);
- dput(h_dp);
if (dinfo->di_bwh == bindex)
bwh = new_bindex;
if (dinfo->di_bdiropq == bindex)
}
}
- sb = parent->d_sb;
dinfo->di_bwh = -1;
if (bwh >= 0 && bwh <= au_sbend(sb) && au_sbr_whable(sb, bwh))
dinfo->di_bwh = bwh;
&& au_sbr_whable(sb, bdiropq))
dinfo->di_bdiropq = bdiropq;
+ err = -EIO;
+ dinfo->di_bstart = -1;
+ dinfo->di_bend = -1;
bend = au_dbend(parent);
p = dinfo->di_hdentry;
for (bindex = 0; bindex <= bend; bindex++, p++)
break;
}
- p = dinfo->di_hdentry + bend;
- for (bindex = bend; bindex >= 0; bindex--, p--)
- if (p->hd_dentry) {
- dinfo->di_bend = bindex;
- break;
+ if (dinfo->di_bstart >= 0) {
+ p = dinfo->di_hdentry + bend;
+ for (bindex = bend; bindex >= 0; bindex--, p--)
+ if (p->hd_dentry) {
+ dinfo->di_bend = bindex;
+ err = 0;
+ break;
+ }
+ }
+
+ return err;
+}
+
+static void au_do_hide(struct dentry *dentry)
+{
+ struct inode *inode;
+
+ inode = dentry->d_inode;
+ if (inode) {
+ if (!S_ISDIR(inode->i_mode)) {
+ if (inode->i_nlink && !d_unhashed(dentry))
+ drop_nlink(inode);
+ } else {
+ clear_nlink(inode);
+ /* stop next lookup */
+ inode->i_flags |= S_DEAD;
}
+ smp_mb(); /* necessary? */
+ }
+ d_drop(dentry);
+}
+
+static int au_hide_children(struct dentry *parent)
+{
+ int err, i, j, ndentry;
+ struct au_dcsub_pages dpages;
+ struct au_dpage *dpage;
+ struct dentry *dentry;
+
+ err = au_dpages_init(&dpages, GFP_NOFS);
+ if (unlikely(err))
+ goto out;
+ err = au_dcsub_pages(&dpages, parent, NULL, NULL);
+ if (unlikely(err))
+ goto out_dpages;
+
+ /* in reverse order */
+ for (i = dpages.ndpage - 1; i >= 0; i--) {
+ dpage = dpages.dpages + i;
+ ndentry = dpage->ndentry;
+ for (j = ndentry - 1; j >= 0; j--) {
+ dentry = dpage->dentries[j];
+ if (dentry != parent)
+ au_do_hide(dentry);
+ }
+ }
+
+out_dpages:
+ au_dpages_free(&dpages);
+out:
+ return err;
+}
+
+static void au_hide(struct dentry *dentry)
+{
+ int err;
+ struct inode *inode;
+
+ AuDbgDentry(dentry);
+ inode = dentry->d_inode;
+ if (inode && S_ISDIR(inode->i_mode)) {
+ /* shrink_dcache_parent(dentry); */
+ err = au_hide_children(dentry);
+ if (unlikely(err))
+ AuIOErr("%.*s, failed hiding children, ignored %d\n",
+ AuDLNPair(dentry), err);
+ }
+ au_do_hide(dentry);
}
/*
- * returns the number of found lower positive dentries,
- * otherwise an error.
+ * By adding a dirty branch, a cached dentry may be affected in various ways.
+ *
+ * a dirty branch is added
+ * - on the top of layers
+ * - in the middle of layers
+ * - to the bottom of layers
+ *
+ * on the added branch there exists
+ * - a whiteout
+ * - a diropq
+ * - a same named entry
+ * + exist
+ * * negative --> positive
+ * * positive --> positive
+ * - type is unchanged
+ * - type is changed
+ * + doesn't exist
+ * * negative --> negative
+ * * positive --> negative (rejected by au_br_del() for non-dir case)
+ * - none
*/
-int au_refresh_hdentry(struct dentry *dentry, mode_t type)
+static int au_refresh_by_dinfo(struct dentry *dentry, struct au_dinfo *dinfo,
+ struct au_dinfo *tmp)
{
- int npositive, err;
+ int err;
+ aufs_bindex_t bindex, bend;
+ struct {
+ struct dentry *dentry;
+ struct inode *inode;
+ mode_t mode;
+ } orig_h, tmp_h;
+ struct au_hdentry *hd;
+ struct inode *inode, *h_inode;
+ struct dentry *h_dentry;
+
+ err = 0;
+ AuDebugOn(dinfo->di_bstart < 0);
+ orig_h.dentry = dinfo->di_hdentry[dinfo->di_bstart].hd_dentry;
+ orig_h.inode = orig_h.dentry->d_inode;
+ orig_h.mode = 0;
+ if (orig_h.inode)
+ orig_h.mode = orig_h.inode->i_mode & S_IFMT;
+ memset(&tmp_h, 0, sizeof(tmp_h));
+ if (tmp->di_bstart >= 0) {
+ tmp_h.dentry = tmp->di_hdentry[tmp->di_bstart].hd_dentry;
+ tmp_h.inode = tmp_h.dentry->d_inode;
+ if (tmp_h.inode)
+ tmp_h.mode = tmp_h.inode->i_mode & S_IFMT;
+ }
+
+ inode = dentry->d_inode;
+ if (!orig_h.inode) {
+ AuDbg("nagative originally\n");
+ if (inode) {
+ au_hide(dentry);
+ goto out;
+ }
+ AuDebugOn(inode);
+ AuDebugOn(dinfo->di_bstart != dinfo->di_bend);
+ AuDebugOn(dinfo->di_bdiropq != -1);
+
+ if (!tmp_h.inode) {
+ AuDbg("negative --> negative\n");
+ /* should have only one negative lower */
+ if (tmp->di_bstart >= 0
+ && tmp->di_bstart < dinfo->di_bstart) {
+ AuDebugOn(tmp->di_bstart != tmp->di_bend);
+ AuDebugOn(dinfo->di_bstart != dinfo->di_bend);
+ au_set_h_dptr(dentry, dinfo->di_bstart, NULL);
+ au_di_cp(dinfo, tmp);
+ hd = tmp->di_hdentry + tmp->di_bstart;
+ au_set_h_dptr(dentry, tmp->di_bstart,
+ dget(hd->hd_dentry));
+ }
+ au_dbg_verify_dinode(dentry);
+ } else {
+ AuDbg("negative --> positive\n");
+ /*
+ * similar to the behaviour of creating with bypassing
+ * aufs.
+ * unhash it in order to force an error in the
+ * succeeding create operation.
+ * we should not set S_DEAD here.
+ */
+ d_drop(dentry);
+ /* au_di_swap(tmp, dinfo); */
+ au_dbg_verify_dinode(dentry);
+ }
+ } else {
+ AuDbg("positive originally\n");
+ /* inode may be NULL */
+ AuDebugOn(inode && (inode->i_mode & S_IFMT) != orig_h.mode);
+ if (!tmp_h.inode) {
+ AuDbg("positive --> negative\n");
+ /* or bypassing aufs */
+ au_hide(dentry);
+ if (tmp->di_bwh >= 0 && tmp->di_bwh <= dinfo->di_bstart)
+ dinfo->di_bwh = tmp->di_bwh;
+ if (inode)
+ err = au_refresh_hinode_self(inode);
+ au_dbg_verify_dinode(dentry);
+ } else if (orig_h.mode == tmp_h.mode) {
+ AuDbg("positive --> positive, same type\n");
+ if (!S_ISDIR(orig_h.mode)
+ && dinfo->di_bstart > tmp->di_bstart) {
+ /*
+ * similar to the behaviour of removing and
+ * creating.
+ */
+ au_hide(dentry);
+ if (inode)
+ err = au_refresh_hinode_self(inode);
+ au_dbg_verify_dinode(dentry);
+ } else {
+ /* fill empty slots */
+ if (dinfo->di_bstart > tmp->di_bstart)
+ dinfo->di_bstart = tmp->di_bstart;
+ if (dinfo->di_bend < tmp->di_bend)
+ dinfo->di_bend = tmp->di_bend;
+ dinfo->di_bwh = tmp->di_bwh;
+ dinfo->di_bdiropq = tmp->di_bdiropq;
+ hd = tmp->di_hdentry;
+ bend = dinfo->di_bend;
+ for (bindex = tmp->di_bstart; bindex <= bend;
+ bindex++) {
+ if (au_h_dptr(dentry, bindex))
+ continue;
+ h_dentry = hd[bindex].hd_dentry;
+ if (!h_dentry)
+ continue;
+ h_inode = h_dentry->d_inode;
+ AuDebugOn(!h_inode);
+ AuDebugOn(orig_h.mode
+ != (h_inode->i_mode
+ & S_IFMT));
+ au_set_h_dptr(dentry, bindex,
+ dget(h_dentry));
+ }
+ err = au_refresh_hinode(inode, dentry);
+ au_dbg_verify_dinode(dentry);
+ }
+ } else {
+ AuDbg("positive --> positive, different type\n");
+ /* similar to the behaviour of removing and creating */
+ au_hide(dentry);
+ if (inode)
+ err = au_refresh_hinode_self(inode);
+ au_dbg_verify_dinode(dentry);
+ }
+ }
+
+out:
+ return err;
+}
+
+int au_refresh_dentry(struct dentry *dentry, struct dentry *parent)
+{
+ int err, ebrange;
unsigned int sigen;
- aufs_bindex_t bstart;
- struct au_dinfo *dinfo;
+ struct au_dinfo *dinfo, *tmp;
struct super_block *sb;
- struct dentry *parent;
+ struct inode *inode;
DiMustWriteLock(dentry);
+ AuDebugOn(IS_ROOT(dentry));
+ AuDebugOn(!parent->d_inode);
sb = dentry->d_sb;
- AuDebugOn(IS_ROOT(dentry));
+ inode = dentry->d_inode;
sigen = au_sigen(sb);
- parent = dget_parent(dentry);
- AuDebugOn(au_digen(parent) != sigen
- || au_iigen(parent->d_inode) != sigen);
+ err = au_digen_test(parent, sigen);
+ if (unlikely(err))
+ goto out;
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);
+ ebrange = au_dbrange_test(dentry);
+ if (!ebrange)
+ ebrange = au_do_refresh_hdentry(dentry, parent);
- npositive = 0;
- bstart = au_dbstart(parent);
- if (type != S_IFDIR && dinfo->di_bstart == bstart)
- goto out_dgen; /* success */
+ 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;
+ }
- npositive = au_lkup_dentry(dentry, bstart, type, /*nd*/NULL);
- if (npositive < 0)
+ /* 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;
- 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;
+ if (unlikely(err && !(dentry->d_flags & DCACHE_NFSFS_RENAMED))) {
+ AuIOErr("failed refreshing %.*s, %d\n",
+ AuDLNPair(dentry), err);
+ AuDbgDentry(dentry);
+ }
+ AuTraceErr(err);
+ return err;
}
static noinline_for_stack
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;
- struct inode *inode;
- inode = dentry->d_inode;
- if (au_digen(dentry) == sigen && au_iigen(inode) == sigen)
+ if (!au_digen_test(dentry, 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);
+ AuDebugOn(au_digen_test(parent, 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);
-
+ err = au_refresh_dentry(dentry, parent);
di_read_unlock(parent, AuLock_IR);
dput(parent);
+ AuTraceErr(err);
return err;
}
struct dentry *d, *parent;
struct inode *inode;
- if (!au_ftest_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIRS))
+ 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(dentry) != sigen
- || au_iigen(dentry->d_inode) != sigen) {
+ while (au_digen_test(dentry, sigen)) {
d = dentry;
while (1) {
dput(parent);
parent = dget_parent(d);
- if (au_digen(parent) == sigen
- && au_iigen(parent->d_inode) == sigen)
+ if (!au_digen_test(parent, sigen))
break;
d = parent;
}
inode = d->d_inode;
if (d != dentry)
- di_write_lock_child(d);
+ di_write_lock_child2(d);
/* someone might update our dentry while we were sleeping */
- if (au_digen(d) != sigen || au_iigen(d->d_inode) != sigen) {
+ 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);
- /* 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);
+ err = au_refresh_dentry(d, parent);
di_read_unlock(parent, AuLock_IR);
}
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;
err = aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW | AuLock_NOPLM);
if (unlikely(err)) {
valid = err;
+ AuTraceErr(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 (unlikely(au_dbrange_test(dentry))) {
+ err = -EINVAL;
+ AuTraceErr(err);
+ goto out_dgrade;
}
- if (inode && au_iigen(inode) != sigen) {
+
+ sigen = au_sigen(sb);
+ if (au_digen_test(dentry, sigen)) {
AuDebugOn(IS_ROOT(dentry));
- err = au_refresh_hinode(inode, dentry);
- if (unlikely(err))
+ 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
- && au_test_higen(inode, au_h_iptr(inode, bstart)))
- goto out_inval;
+ 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))
- /* both of real entry and whiteout found */
+ 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:
AuTraceErr(err);
valid = !err;
out:
- if (!valid)
+ if (!valid) {
AuDbg("%.*s invalid, %d\n", AuDLNPair(dentry), valid);
+ d_drop(dentry);
+ }
return valid;
}
static void aufs_d_release(struct dentry *dentry)
{
- if (dentry->d_fsdata) {
+ if (au_di(dentry)) {
au_di_fin(dentry);
au_hn_di_reinit(dentry);
}