/* * decide if a new whiteout for @dentry is necessary or not. * when it is necessary, prepare the parent dir for the upper branch whose * branch index is @bcpup for creation. the actual creation of the whiteout will * be done by caller. * return value: * 0: wh is unnecessary * plus: wh is necessary * minus: error */ int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup) { int need_wh, err; aufs_bindex_t bstart; struct super_block *sb; sb = dentry->d_sb; bstart = au_dbstart(dentry); if (*bcpup < 0) { *bcpup = bstart; if (au_test_ro(sb, bstart, dentry->d_inode)) { err = AuWbrCopyup(au_sbi(sb), dentry); *bcpup = err; if (unlikely(err < 0)) goto out; } } else AuDebugOn(bstart < *bcpup || au_test_ro(sb, *bcpup, dentry->d_inode)); AuDbg("bcpup %d, bstart %d\n", *bcpup, bstart); if (*bcpup != bstart) { err = au_cpup_dirs(dentry, *bcpup); if (unlikely(err)) goto out; need_wh = 1; } else { struct au_dinfo *dinfo, *tmp; need_wh = -ENOMEM; dinfo = au_di(dentry); tmp = au_di_alloc(sb, AuLsc_DI_TMP); if (tmp) { au_di_cp(tmp, dinfo); au_di_swap(tmp, dinfo); /* returns the number of positive dentries */ need_wh = au_lkup_dentry(dentry, bstart + 1, /*type*/0, /*nd*/NULL); au_di_swap(tmp, dinfo); au_rw_write_unlock(&tmp->di_rwsem); au_di_free(tmp); } } AuDbg("need_wh %d\n", need_wh); err = need_wh; out: return err; }
static int au_mvd_args_intermediate(const unsigned char dmsg, struct au_mvd_args *a) { int err; struct au_dinfo *dinfo, *tmp; /* lookup the next lower positive entry */ err = -ENOMEM; tmp = au_di_alloc(a->sb, AuLsc_DI_TMP); if (unlikely(!tmp)) goto out; a->bfound = -1; a->bwh = -1; dinfo = au_di(a->dentry); au_di_cp(tmp, dinfo); au_di_swap(tmp, dinfo); /* returns the number of positive dentries */ err = au_lkup_dentry(a->dentry, a->mvd_bsrc + 1, /*type*/0); if (!err) a->bwh = au_dbwh(a->dentry); else if (err > 0) a->bfound = au_dbstart(a->dentry); au_di_swap(tmp, dinfo); au_rw_write_unlock(&tmp->di_rwsem); au_di_free(tmp); if (unlikely(err < 0)) AU_MVD_PR(dmsg, "failed look-up lower\n"); /* * here, we have these cases. * bfound == -1 * no positive dentry under bsrc. there are more sub-cases. * bwh < 0 * there no whiteout, we can safely move-down. * bwh <= bsrc * impossible * bsrc < bwh && bwh < bdst * there is a whiteout on RO branch. cannot proceed. * bwh == bdst * there is a whiteout on the RW target branch. it should * be removed. * bdst < bwh * there is a whiteout somewhere unrelated branch. * -1 < bfound && bfound <= bsrc * impossible. * bfound < bdst * found, but it is on RO branch between bsrc and bdst. cannot * proceed. * bfound == bdst * found, replace it if AUFS_MVDOWN_FORCE is set. otherwise return * error. * bdst < bfound * found, after we create the file on bdst, it will be hidden. */ AuDebugOn(a->bfound == -1 && a->bwh != -1 && a->bwh <= a->mvd_bsrc); AuDebugOn(-1 < a->bfound && a->bfound <= a->mvd_bsrc); err = -EINVAL; if (a->bfound == -1 && a->mvd_bsrc < a->bwh && a->bwh != -1 && a->bwh < a->mvd_bdst) { a->mvd_errno = EAU_MVDOWN_WHITEOUT; AU_MVD_PR(dmsg, "bsrc %d, bdst %d, bfound %d, bwh %d\n", a->mvd_bsrc, a->mvd_bdst, a->bfound, a->bwh); goto out; } else if (a->bfound != -1 && a->bfound < a->mvd_bdst) { a->mvd_errno = EAU_MVDOWN_UPPER; AU_MVD_PR(dmsg, "bdst %d, bfound %d\n", a->mvd_bdst, a->bfound); goto out; } err = 0; /* success */ out: 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; }