void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, struct dentry *h_dentry) { struct au_hdentry *hd = au_di(dentry)->di_hdentry + bindex; DiMustWriteLock(dentry); if (hd->hd_dentry) au_hdput(hd); hd->hd_dentry = h_dentry; }
void au_set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex) { DiMustWriteLock(dentry); AuDebugOn(au_sbend(dentry->d_sb) < bindex); AuDebugOn((bindex >= 0 && (bindex < au_dbstart(dentry) || au_dbend(dentry) < bindex)) || (dentry->d_inode && dentry->d_inode->i_mode && !S_ISDIR(dentry->d_inode->i_mode))); au_di(dentry)->di_bdiropq = bindex; }
void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, struct dentry *h_dentry) { struct au_hdentry *hd = au_di(dentry)->di_hdentry + bindex; DiMustWriteLock(dentry); AuDebugOn(bindex < au_di(dentry)->di_bstart || bindex > au_di(dentry)->di_bend || (h_dentry && atomic_read(&h_dentry->d_count) <= 0) || (h_dentry && hd->hd_dentry) ); if (hd->hd_dentry) au_hdput(hd, /*do_free*/0); hd->hd_dentry = h_dentry; }
void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, struct dentry *h_dentry) { struct au_hdentry *hd = au_di(dentry)->di_hdentry + bindex; struct au_branch *br; DiMustWriteLock(dentry); au_hdput(hd); hd->hd_dentry = h_dentry; if (h_dentry) { br = au_sbr(dentry->d_sb, bindex); hd->hd_id = br->br_id; } }
static int do_coo(struct dentry *dentry, aufs_bindex_t bstart) { int err; struct dentry *parent, *h_parent, *h_dentry; aufs_bindex_t bcpup; struct inode *h_dir, *h_inode, *dir; LKTRTrace("%.*s\n", DLNPair(dentry)); DEBUG_ON(IS_ROOT(dentry)); DiMustWriteLock(dentry); parent = dentry->d_parent; // dget_parent() di_write_lock_parent(parent); bcpup = err = find_rw_parent_br(dentry, bstart); //bcpup = err = find_rw_br(sb, bstart); if (unlikely(err < 0)) { err = 0; // stop copyup, it is not an error goto out; } err = 0; h_parent = au_h_dptr_i(parent, bcpup); if (!h_parent) { err = cpup_dirs(dentry, bcpup, NULL); if (unlikely(err)) goto out; h_parent = au_h_dptr_i(parent, bcpup); } h_dir = h_parent->d_inode; h_dentry = au_h_dptr_i(dentry, bstart); h_inode = h_dentry->d_inode; dir = parent->d_inode; hdir_lock(h_dir, dir, bcpup); hi_lock_child(h_inode); DEBUG_ON(au_h_dptr_i(dentry, bcpup)); err = sio_cpup_simple(dentry, bcpup, -1, au_flags_cpup(CPUP_DTIME, parent)); TraceErr(err); i_unlock(h_inode); hdir_unlock(h_dir, dir, bcpup); out: di_write_unlock(parent); TraceErr(err); 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_update_dbend(struct dentry *dentry) { aufs_bindex_t bindex, bstart = au_dbstart(dentry), bend = au_dbend(dentry); struct dentry *h_dentry; DiMustWriteLock(dentry); for (bindex = bend; bindex <= bstart; bindex--) { h_dentry = au_h_dptr(dentry, bindex); if (!h_dentry) continue; if (h_dentry->d_inode) { au_set_dbend(dentry, bindex); return; } au_set_h_dptr(dentry, bindex, NULL); } }
void au_update_dbrange(struct dentry *dentry, int do_put_zero) { struct au_dinfo *dinfo; struct dentry *h_d; struct au_hdentry *hdp; DiMustWriteLock(dentry); dinfo = au_di(dentry); if (!dinfo || dinfo->di_bstart < 0) return; hdp = dinfo->di_hdentry; if (do_put_zero) { aufs_bindex_t bindex, bend; bend = dinfo->di_bend; for (bindex = dinfo->di_bstart; bindex <= bend; bindex++) { h_d = hdp[0 + bindex].hd_dentry; if (h_d && !h_d->d_inode) au_set_h_dptr(dentry, bindex, NULL); } } dinfo->di_bstart = -1; while (++dinfo->di_bstart <= dinfo->di_bend) if (hdp[0 + dinfo->di_bstart].hd_dentry) break; if (dinfo->di_bstart > dinfo->di_bend) { dinfo->di_bstart = -1; dinfo->di_bend = -1; return; } dinfo->di_bend++; while (0 <= --dinfo->di_bend) if (hdp[0 + dinfo->di_bend].hd_dentry) break; AuDebugOn(dinfo->di_bstart > dinfo->di_bend || dinfo->di_bend < 0); }
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; }
static int au_do_refresh_hdentry(struct dentry *dentry, struct dentry *parent) { 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; 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++) { if (!p->hd_dentry) continue; new_bindex = au_br_index(sb, p->hd_id); if (new_bindex == bindex) continue; if (dinfo->di_bwh == bindex) bwh = new_bindex; if (dinfo->di_bdiropq == bindex) bdiropq = new_bindex; if (new_bindex < 0) { au_hdput(p); p->hd_dentry = NULL; continue; } /* swap two lower dentries, and loop again */ q = dinfo->di_hdentry + new_bindex; tmp = *q; *q = *p; *p = tmp; if (tmp.hd_dentry) { bindex--; p--; } } dinfo->di_bwh = -1; if (bwh >= 0 && bwh <= au_sbend(sb) && au_sbr_whable(sb, bwh)) dinfo->di_bwh = bwh; dinfo->di_bdiropq = -1; if (bdiropq >= 0 && bdiropq <= au_sbend(sb) && 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++) if (p->hd_dentry) { dinfo->di_bstart = 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; }
/* * copyup the deleted file for writing. */ static int cpup_wh_file(struct file *file, aufs_bindex_t bdst, loff_t len) { int err; struct dentry *dentry, *parent, *hidden_parent, *tmp_dentry; struct dentry *hidden_dentry_bstart, *hidden_dentry_bdst; struct inode *hidden_dir; aufs_bindex_t bstart; struct aufs_dinfo *dinfo; struct dtime dt; struct lkup_args lkup; struct super_block *sb; dentry = file->f_dentry; LKTRTrace("%.*s, bdst %d, len %Lu\n", DLNPair(dentry), bdst, len); DEBUG_ON(S_ISDIR(dentry->d_inode->i_mode) || !(file->f_mode & FMODE_WRITE)); DiMustWriteLock(dentry); parent = dentry->d_parent; IiMustAnyLock(parent->d_inode); hidden_parent = au_h_dptr_i(parent, bdst); DEBUG_ON(!hidden_parent); hidden_dir = hidden_parent->d_inode; DEBUG_ON(!hidden_dir); IMustLock(hidden_dir); sb = parent->d_sb; lkup.nfsmnt = au_nfsmnt(sb, bdst); lkup.dlgt = need_dlgt(sb); tmp_dentry = lkup_whtmp(hidden_parent, &dentry->d_name, &lkup); //if (LktrCond) {dput(tmp_dentry); tmp_dentry = ERR_PTR(-1);} err = PTR_ERR(tmp_dentry); if (IS_ERR(tmp_dentry)) goto out; dtime_store(&dt, parent, hidden_parent); dinfo = dtodi(dentry); bstart = dinfo->di_bstart; hidden_dentry_bdst = dinfo->di_hdentry[0 + bdst].hd_dentry; hidden_dentry_bstart = dinfo->di_hdentry[0 + bstart].hd_dentry; dinfo->di_bstart = bdst; dinfo->di_hdentry[0 + bdst].hd_dentry = tmp_dentry; dinfo->di_hdentry[0 + bstart].hd_dentry = au_h_fptr(file)->f_dentry; err = cpup_single(dentry, bdst, bstart, len, au_flags_cpup(!CPUP_DTIME, parent)); //if (LktrCond) err = -1; if (!err) err = au_reopen_nondir(file); //err = -1; if (unlikely(err)) { dinfo->di_hdentry[0 + bstart].hd_dentry = hidden_dentry_bstart; dinfo->di_hdentry[0 + bdst].hd_dentry = hidden_dentry_bdst; dinfo->di_bstart = bstart; goto out_tmp; } DEBUG_ON(!d_unhashed(dentry)); err = vfsub_unlink(hidden_dir, tmp_dentry, lkup.dlgt); //if (LktrCond) err = -1; if (unlikely(err)) { IOErr("failed remove copied-up tmp file %.*s(%d)\n", DLNPair(tmp_dentry), err); err = -EIO; } dtime_revert(&dt, !CPUP_LOCKED_GHDIR); out_tmp: dput(tmp_dentry); out: TraceErr(err); return err; }
static int au_cmoo(struct dentry *dentry) { int err, cmoo; unsigned int udba; struct path h_path; struct au_pin pin; struct au_cp_generic cpg = { .dentry = dentry, .bdst = -1, .bsrc = -1, .len = -1, .pin = &pin, .flags = AuCpup_DTIME | AuCpup_HOPEN }; struct inode *inode, *delegated; struct super_block *sb; struct au_sbinfo *sbinfo; struct au_fhsm *fhsm; pid_t pid; struct au_branch *br; struct dentry *parent; struct au_hinode *hdir; DiMustWriteLock(dentry); inode = dentry->d_inode; IiMustWriteLock(inode); err = 0; if (IS_ROOT(dentry)) goto out; cpg.bsrc = au_dbstart(dentry); if (!cpg.bsrc) goto out; sb = dentry->d_sb; sbinfo = au_sbi(sb); fhsm = &sbinfo->si_fhsm; pid = au_fhsm_pid(fhsm); if (pid && (current->pid == pid || current->real_parent->pid == pid)) goto out; br = au_sbr(sb, cpg.bsrc); cmoo = au_br_cmoo(br->br_perm); if (!cmoo) goto out; if (!S_ISREG(inode->i_mode)) cmoo &= AuBrAttr_COO_ALL; if (!cmoo) goto out; parent = dget_parent(dentry); di_write_lock_parent(parent); err = au_wbr_do_copyup_bu(dentry, cpg.bsrc - 1); cpg.bdst = err; if (unlikely(err < 0)) { err = 0; /* there is no upper writable branch */ goto out_dgrade; } AuDbg("bsrc %d, bdst %d\n", cpg.bsrc, cpg.bdst); /* do not respect the coo attrib for the target branch */ err = au_cpup_dirs(dentry, cpg.bdst); if (unlikely(err)) goto out_dgrade; di_downgrade_lock(parent, AuLock_IR); udba = au_opt_udba(sb); err = au_pin(&pin, dentry, cpg.bdst, udba, AuPin_DI_LOCKED | AuPin_MNT_WRITE); if (unlikely(err)) goto out_parent; err = au_sio_cpup_simple(&cpg); au_unpin(&pin); if (unlikely(err)) goto out_parent; if (!(cmoo & AuBrWAttr_MOO)) goto out_parent; /* success */ err = au_pin(&pin, dentry, cpg.bsrc, udba, AuPin_DI_LOCKED | AuPin_MNT_WRITE); if (unlikely(err)) goto out_parent; h_path.mnt = au_br_mnt(br); h_path.dentry = au_h_dptr(dentry, cpg.bsrc); hdir = au_hi(parent->d_inode, cpg.bsrc); delegated = NULL; err = vfsub_unlink(hdir->hi_inode, &h_path, &delegated, /*force*/1); au_unpin(&pin); /* todo: keep h_dentry or not? */ if (unlikely(err == -EWOULDBLOCK)) { pr_warn("cannot retry for NFSv4 delegation" " for an internal unlink\n"); iput(delegated); } if (unlikely(err)) { pr_err("unlink %pd after coo failed (%d), ignored\n", dentry, err); err = 0; } goto out_parent; /* success */ out_dgrade: di_downgrade_lock(parent, AuLock_IR); out_parent: di_read_unlock(parent, AuLock_IR); dput(parent); out: AuTraceErr(err); return err; } int au_do_open(struct file *file, int (*open)(struct file *file, int flags), struct au_fidir *fidir) { int err; struct dentry *dentry; struct au_finfo *finfo; err = au_finfo_init(file, fidir); if (unlikely(err)) goto out; dentry = file->f_path.dentry; di_write_lock_child(dentry); err = au_cmoo(dentry); di_downgrade_lock(dentry, AuLock_IR); if (!err) err = open(file, vfsub_file_flags(file)); di_read_unlock(dentry, AuLock_IR); finfo = au_fi(file); if (!err) { finfo->fi_file = file; au_sphl_add(&finfo->fi_hlist, &au_sbi(file->f_path.dentry->d_sb)->si_files); } fi_write_unlock(file); if (unlikely(err)) { finfo->fi_hdir = NULL; au_finfo_fin(file); } out: return err; }