void di_read_unlock(struct dentry *d, int flags) { if (d->d_inode) { if (au_ftest_lock(flags, IW)) { au_dbg_verify_dinode(d); ii_write_unlock(d->d_inode); } else if (au_ftest_lock(flags, IR)) { au_dbg_verify_dinode(d); ii_read_unlock(d->d_inode); } } au_rw_read_unlock(&au_di(d)->di_rwsem); }
void di_write_unlock(struct dentry *d) { au_dbg_verify_dinode(d); if (d->d_inode) ii_write_unlock(d->d_inode); au_rw_write_unlock(&au_di(d)->di_rwsem); }
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; }
/* * 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 */ static int au_refresh_by_dinfo(struct dentry *dentry, struct au_dinfo *dinfo, struct au_dinfo *tmp) { 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; }