/* * decide the branch and the parent dir where we will create a new entry. * returns new bindex or an error. * copyup the parent dir if needed. */ int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, struct au_wr_dir_args *args) { int err; aufs_bindex_t bcpup, bstart, src_bstart; const unsigned char add_entry = !!au_ftest_wrdir(args->flags, ADD_ENTRY); struct super_block *sb; struct dentry *parent; struct au_sbinfo *sbinfo; sb = dentry->d_sb; sbinfo = au_sbi(sb); parent = dget_parent(dentry); bstart = au_dbstart(dentry); bcpup = bstart; if (args->force_btgt < 0) { if (src_dentry) { src_bstart = au_dbstart(src_dentry); if (src_bstart < bstart) bcpup = src_bstart; } else if (add_entry) { err = AuWbrCreate(sbinfo, dentry, au_ftest_wrdir(args->flags, ISDIR)); bcpup = err; } if (bcpup < 0 || au_test_ro(sb, bcpup, dentry->d_inode)) { if (add_entry) err = AuWbrCopyup(sbinfo, dentry); else { if (!IS_ROOT(dentry)) { di_read_lock_parent(parent, !AuLock_IR); err = AuWbrCopyup(sbinfo, dentry); di_read_unlock(parent, !AuLock_IR); } else err = AuWbrCopyup(sbinfo, dentry); } bcpup = err; if (unlikely(err < 0)) goto out; } } else { bcpup = args->force_btgt; AuDebugOn(au_test_ro(sb, bcpup, dentry->d_inode)); } AuDbg("bstart %d, bcpup %d\n", bstart, bcpup); err = bcpup; if (bcpup == bstart) goto out; /* success */ else if (bstart < bcpup) au_update_dbrange(dentry, /*do_put_zero*/1); /* copyup the new parent into the branch we process */ err = au_wr_dir_cpup(dentry, parent, add_entry, bcpup, bstart); out: dput(parent); return err; }
/* make sure the file is idle */ static int au_mvd_args_busy(const unsigned char dmsg, struct au_mvd_args *a) { int err, plinked; err = 0; plinked = !!au_opt_test(au_mntflags(a->sb), PLINK); if (au_dbstart(a->dentry) == a->mvd_bsrc && au_dcount(a->dentry) == 1 && atomic_read(&a->inode->i_count) == 1 /* && a->mvd_h_src_inode->i_nlink == 1 */ && (!plinked || !au_plink_test(a->inode)) && a->inode->i_nlink == 1) goto out; err = -EBUSY; AU_MVD_PR(dmsg, "b%d, d{b%d, c%d?}, i{c%d?, l%u}, hi{l%u}, p{%d, %d}\n", a->mvd_bsrc, au_dbstart(a->dentry), au_dcount(a->dentry), atomic_read(&a->inode->i_count), a->inode->i_nlink, a->mvd_h_src_inode->i_nlink, plinked, plinked ? au_plink_test(a->inode) : 0); out: AuTraceErr(err); return err; }
struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex) { struct dentry *d; DiMustAnyLock(dentry); if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) return NULL; AuDebugOn(bindex < 0); d = au_di(dentry)->di_hdentry[0 + bindex].hd_dentry; AuDebugOn(d && au_dcount(d) <= 0); return d; }
struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex) { struct dentry *d; DiMustAnyLock(dentry); if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) return NULL; AuDebugOn(bindex < 0 /* || bindex > au_sbend(dentry->d_sb) */); d = au_di(dentry)->di_hdentry[0 + bindex].hd_dentry; AuDebugOn(d && (atomic_read(&d->d_count) <= 0)); return d; }
int au_do_open_nondir(struct file *file, int flags) { int err; aufs_bindex_t bindex; struct file *h_file; struct dentry *dentry; struct au_finfo *finfo; FiMustWriteLock(file); dentry = file->f_dentry; err = au_d_alive(dentry); if (unlikely(err)) goto out; finfo = au_fi(file); memset(&finfo->fi_htop, 0, sizeof(finfo->fi_htop)); finfo->fi_hvmop = NULL; bindex = au_dbstart(dentry); h_file = au_h_open(dentry, bindex, flags, file); if (IS_ERR(h_file)) err = PTR_ERR(h_file); else { au_set_fbstart(file, bindex); au_set_h_fptr(file, bindex, h_file); au_update_figen(file); /* todo: necessary? */ /* file->f_ra = h_file->f_ra; */ } out: return err; }
/* * initial procedure of adding a new entry. * prepare writable branch and the parent dir, lock it, * and lookup whiteout for the new entry. */ static struct dentry* lock_hdir_lkup_wh(struct dentry *dentry, struct au_dtime *dt, struct dentry *src_dentry, struct au_pin *pin, struct au_wr_dir_args *wr_dir_args) { struct dentry *wh_dentry, *h_parent; struct super_block *sb; struct au_branch *br; int err; unsigned int udba; aufs_bindex_t bcpup; AuDbg("%.*s\n", AuDLNPair(dentry)); err = au_wr_dir(dentry, src_dentry, wr_dir_args); bcpup = err; wh_dentry = ERR_PTR(err); if (unlikely(err < 0)) goto out; sb = dentry->d_sb; udba = au_opt_udba(sb); err = au_pin(pin, dentry, bcpup, udba, AuPin_DI_LOCKED | AuPin_MNT_WRITE); wh_dentry = ERR_PTR(err); if (unlikely(err)) goto out; h_parent = au_pinned_h_parent(pin); if (udba != AuOpt_UDBA_NONE && au_dbstart(dentry) == bcpup) err = au_may_add(dentry, bcpup, h_parent, au_ftest_wrdir(wr_dir_args->flags, ISDIR)); else if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) err = -ENAMETOOLONG; wh_dentry = ERR_PTR(err); if (unlikely(err)) goto out_unpin; br = au_sbr(sb, bcpup); if (dt) { struct path tmp = { .dentry = h_parent, .mnt = au_br_mnt(br) }; au_dtime_store(dt, au_pinned_parent(pin), &tmp); } wh_dentry = NULL; if (bcpup != au_dbwh(dentry)) goto out; /* success */ wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, br); out_unpin: if (IS_ERR(wh_dentry)) au_unpin(pin); out: return wh_dentry; }
void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line) { struct inode *h_inode, *inode = dentry->d_inode; struct dentry *h_dentry; aufs_bindex_t bindex, bend, bi; if (!inode /* || au_di(dentry)->di_lsc == AuLsc_DI_TMP */) return; bend = au_dbend(dentry); bi = au_ibend(inode); if (bi < bend) bend = bi; bindex = au_dbstart(dentry); bi = au_ibstart(inode); if (bi > bindex) bindex = bi; for (; bindex <= bend; bindex++) { h_dentry = au_h_dptr(dentry, bindex); if (!h_dentry) continue; h_inode = au_h_iptr(inode, bindex); if (unlikely(h_inode != h_dentry->d_inode)) { au_debug_on(); AuDbg("b%d, %s:%d\n", bindex, func, line); AuDbgDentry(dentry); AuDbgInode(inode); au_debug_off(); BUG(); } } }
int au_do_open_nondir(struct file *file, int flags) { int err; aufs_bindex_t bindex; struct file *h_file; struct dentry *dentry; struct au_finfo *finfo; FiMustWriteLock(file); err = 0; dentry = file->f_dentry; finfo = au_fi(file); memset(&finfo->fi_htop, 0, sizeof(finfo->fi_htop)); atomic_set(&finfo->fi_mmapped, 0); bindex = au_dbstart(dentry); h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); if (IS_ERR(h_file)) err = PTR_ERR(h_file); else { au_set_fbstart(file, bindex); au_set_h_fptr(file, bindex, h_file); au_update_figen(file); finfo->fi_file = file; au_sphl_add(&finfo->fi_hlist, &au_sbi(dentry->d_sb)->si_files); /* todo: necessary? */ /* file->f_ra = h_file->f_ra; */ } return err; }
int au_do_open_nondir(struct file *file, int flags) { int err; aufs_bindex_t bindex; struct file *h_file; struct dentry *dentry; struct au_finfo *finfo; FiMustWriteLock(file); err = 0; dentry = file->f_dentry; finfo = au_fi(file); memset(&finfo->fi_htop, 0, sizeof(finfo->fi_htop)); finfo->fi_hvmop = NULL; bindex = au_dbstart(dentry); /* O_TRUNC is processed already */ BUG_ON(au_test_ro(dentry->d_sb, bindex, dentry->d_inode) && (flags & O_TRUNC)); h_file = au_h_open(dentry, bindex, flags, file); if (IS_ERR(h_file)) err = PTR_ERR(h_file); else { au_set_fbstart(file, bindex); au_set_h_fptr(file, bindex, h_file); au_update_figen(file); /* todo: necessary? */ /* file->f_ra = h_file->f_ra; */ } return err; }
static int au_ready_to_write_wh(struct file *file, loff_t len, aufs_bindex_t bcpup) { int err; struct inode *inode, *h_inode; struct dentry *dentry, *h_dentry, *hi_wh; dentry = file->f_dentry; au_update_dbstart(dentry); inode = dentry->d_inode; h_inode = NULL; if (au_dbstart(dentry) <= bcpup && au_dbend(dentry) >= bcpup) { h_dentry = au_h_dptr(dentry, bcpup); if (h_dentry) h_inode = h_dentry->d_inode; } hi_wh = au_hi_wh(inode, bcpup); if (!hi_wh && !h_inode) err = au_sio_cpup_wh(dentry, bcpup, len, file); else /* already copied-up after unlink */ err = au_reopen_wh(file, bcpup, hi_wh); if (!err && inode->i_nlink > 1 && au_opt_test(au_mntflags(dentry->d_sb), PLINK)) au_plink_append(inode, bcpup, au_h_dptr(dentry, bcpup)); return err; }
/* * when we have to copyup the renaming entry, do it with the rename-target name * in order to minimize the cost (the later actual rename is unnecessary). * otherwise rename it on the target branch. */ static int au_ren_or_cpup(struct au_ren_args *a) { int err; struct dentry *d; d = a->src_dentry; if (au_dbstart(d) == a->btgt) { a->h_path.dentry = a->dst_h_dentry; if (au_ftest_ren(a->flags, DIROPQ) && au_dbdiropq(d) == a->btgt) au_fclr_ren(a->flags, DIROPQ); AuDebugOn(au_dbstart(d) != a->btgt); err = vfsub_rename(a->src_h_dir, au_h_dptr(d, a->btgt), a->dst_h_dir, &a->h_path); } else { struct mutex *h_mtx = &a->src_h_dentry->d_inode->i_mutex; struct file *h_file; au_fset_ren(a->flags, CPUP); mutex_lock_nested(h_mtx, AuLsc_I_CHILD); au_set_dbstart(d, a->btgt); au_set_h_dptr(d, a->btgt, dget(a->dst_h_dentry)); h_file = au_h_open_pre(d, a->src_bstart); if (IS_ERR(h_file)) { err = PTR_ERR(h_file); h_file = NULL; } else err = au_sio_cpup_single(d, a->btgt, a->src_bstart, -1, !AuCpup_DTIME, a->dst_parent); mutex_unlock(h_mtx); au_h_open_post(d, a->src_bstart, h_file); if (!err) { d = a->dst_dentry; au_set_h_dptr(d, a->btgt, NULL); au_update_dbstart(d); } else { au_set_h_dptr(d, a->btgt, NULL); au_set_dbstart(d, a->src_bstart); } } if (!err && a->h_dst) /* it will be set to dinfo later */ dget(a->h_dst); return err; }
static void au_ren_refresh(struct au_ren_args *a) { aufs_bindex_t bend, bindex; struct dentry *d, *h_d; struct inode *i, *h_i; struct super_block *sb; d = a->dst_dentry; d_drop(d); if (a->h_dst) /* already dget-ed by au_ren_or_cpup() */ au_set_h_dptr(d, a->btgt, a->h_dst); i = a->dst_inode; if (i) { if (!au_ftest_ren(a->flags, ISDIR)) vfsub_drop_nlink(i); else { vfsub_dead_dir(i); au_cpup_attr_timesizes(i); } au_update_dbrange(d, /*do_put_zero*/1); } else { bend = a->btgt; for (bindex = au_dbstart(d); bindex < bend; bindex++) au_set_h_dptr(d, bindex, NULL); bend = au_dbend(d); for (bindex = a->btgt + 1; bindex <= bend; bindex++) au_set_h_dptr(d, bindex, NULL); au_update_dbrange(d, /*do_put_zero*/0); } d = a->src_dentry; au_set_dbwh(d, -1); bend = au_dbend(d); for (bindex = a->btgt + 1; bindex <= bend; bindex++) { h_d = au_h_dptr(d, bindex); if (h_d) au_set_h_dptr(d, bindex, NULL); } au_set_dbend(d, a->btgt); sb = d->d_sb; i = a->src_inode; if (au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(i)) return; /* success */ bend = au_ibend(i); for (bindex = a->btgt + 1; bindex <= bend; bindex++) { h_i = au_h_iptr(i, bindex); if (h_i) { au_xino_write(sb, bindex, h_i->i_ino, /*ino*/0); /* ignore this error */ au_set_h_iptr(i, bindex, NULL, 0); } } au_set_ibend(i, a->btgt); }
/* * 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; }
int au_refresh_hinode(struct inode *inode, struct dentry *dentry) { int err, e, update; unsigned int flags; umode_t mode; aufs_bindex_t bindex, bend; unsigned char isdir; struct au_hinode *p; struct au_iinfo *iinfo; err = au_ii_refresh(inode, &update); if (unlikely(err)) goto out; update = 0; iinfo = au_ii(inode); p = iinfo->ii_hinode + iinfo->ii_bstart; mode = (inode->i_mode & S_IFMT); isdir = S_ISDIR(mode); flags = au_hi_flags(inode, isdir); bend = au_dbend(dentry); for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { struct inode *h_i; struct dentry *h_d; h_d = au_h_dptr(dentry, bindex); if (!h_d || !h_d->d_inode) continue; AuDebugOn(mode != (h_d->d_inode->i_mode & S_IFMT)); if (iinfo->ii_bstart <= bindex && bindex <= iinfo->ii_bend) { h_i = au_h_iptr(inode, bindex); if (h_i) { if (h_i == h_d->d_inode) continue; err = -EIO; break; } } if (bindex < iinfo->ii_bstart) iinfo->ii_bstart = bindex; if (iinfo->ii_bend < bindex) iinfo->ii_bend = bindex; au_set_h_iptr(inode, bindex, au_igrab(h_d->d_inode), flags); update = 1; } au_update_ibrange(inode, /*do_put_zero*/0); e = au_dy_irefresh(inode); if (unlikely(e && !err)) err = e; if (!err) au_refresh_hinode_attr(inode, update && isdir); out: AuTraceErr(err); return err; }
int au_reopen_nondir(struct file *file) { int err; aufs_bindex_t bstart; struct dentry *dentry; struct file *h_file, *h_file_tmp; dentry = file->f_dentry; AuDebugOn(au_special_file(dentry->d_inode->i_mode)); bstart = au_dbstart(dentry); h_file_tmp = NULL; if (au_fbstart(file) == bstart) { h_file = au_hf_top(file); if (file->f_mode == h_file->f_mode) return 0; /* success */ h_file_tmp = h_file; get_file(h_file_tmp); au_set_h_fptr(file, bstart, NULL); } AuDebugOn(au_fi(file)->fi_hdir); /* * it can happen * file exists on both of rw and ro * open --> dbstart and fbstart are both 0 * prepend a branch as rw, "rw" become ro * remove rw/file * delete the top branch, "rw" becomes rw again * --> dbstart is 1, fbstart is still 0 * write --> fbstart is 0 but dbstart is 1 */ /* AuDebugOn(au_fbstart(file) < bstart); */ h_file = au_h_open(dentry, bstart, vfsub_file_flags(file) & ~O_TRUNC, file, /*force_wr*/0); err = PTR_ERR(h_file); if (IS_ERR(h_file)) { if (h_file_tmp) { atomic_inc(&au_sbr(dentry->d_sb, bstart)->br_count); au_set_h_fptr(file, bstart, h_file_tmp); h_file_tmp = NULL; } goto out; /* todo: close all? */ } err = 0; au_set_fbstart(file, bstart); au_set_h_fptr(file, bstart, h_file); au_update_figen(file); /* todo: necessary? */ /* file->f_ra = h_file->f_ra; */ out: if (h_file_tmp) fput(h_file_tmp); return err; }
int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry) { aufs_bindex_t bindex, bend; bend = au_dbend(dentry); for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) if (au_h_dptr(dentry, bindex) == h_dentry) return bindex; return -1; }
int au_refresh_hinode(struct inode *inode, struct dentry *dentry) { int err, update; unsigned int flags; aufs_bindex_t bindex, bend; unsigned char isdir; struct inode *first; struct au_hinode *p; struct au_iinfo *iinfo; err = au_refresh_hinode_self(inode, /*do_attr*/0); if (unlikely(err)) goto out; update = 0; iinfo = au_ii(inode); p = iinfo->ii_hinode + iinfo->ii_bstart; first = p->hi_inode; isdir = S_ISDIR(inode->i_mode); flags = au_hi_flags(inode, isdir); bend = au_dbend(dentry); for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { struct inode *h_i; struct dentry *h_d; h_d = au_h_dptr(dentry, bindex); if (!h_d || !h_d->d_inode) continue; if (iinfo->ii_bstart <= bindex && bindex <= iinfo->ii_bend) { h_i = au_h_iptr(inode, bindex); if (h_i) { if (h_i == h_d->d_inode) continue; err = -EIO; break; } } if (bindex < iinfo->ii_bstart) iinfo->ii_bstart = bindex; if (iinfo->ii_bend < bindex) iinfo->ii_bend = bindex; au_set_h_iptr(inode, bindex, au_igrab(h_d->d_inode), flags); update = 1; } au_update_brange(inode, /*do_put_zero*/0); if (unlikely(err)) goto out; au_refresh_hinode_attr(inode, update && isdir); out: return err; }
/* * 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 { aufs_bindex_t old_bend, new_bend, bdiropq = -1; old_bend = au_dbend(dentry); if (isdir) { bdiropq = au_dbdiropq(dentry); au_set_dbdiropq(dentry, -1); } need_wh = au_lkup_dentry(dentry, bstart + 1, /*type*/0, /*nd*/NULL); err = need_wh; if (isdir) au_set_dbdiropq(dentry, bdiropq); if (unlikely(err < 0)) goto out; new_bend = au_dbend(dentry); if (!need_wh && old_bend != new_bend) { au_set_h_dptr(dentry, new_bend, NULL); au_set_dbend(dentry, old_bend); } } AuDbg("need_wh %d\n", need_wh); err = need_wh; out: return err; }
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; }
/* sets src_bstart, dst_bstart and btgt */ static int au_ren_wbr(struct au_ren_args *a) { int err; struct au_wr_dir_args wr_dir_args = { /* .force_btgt = -1, */ .flags = AuWrDir_ADD_ENTRY }; a->src_bstart = au_dbstart(a->src_dentry); a->dst_bstart = au_dbstart(a->dst_dentry); if (au_ftest_ren(a->flags, ISDIR)) au_fset_wrdir(wr_dir_args.flags, ISDIR); wr_dir_args.force_btgt = a->src_bstart; if (a->dst_inode && a->dst_bstart < a->src_bstart) wr_dir_args.force_btgt = a->dst_bstart; wr_dir_args.force_btgt = au_wbr(a->dst_dentry, wr_dir_args.force_btgt); err = au_wr_dir(a->dst_dentry, a->src_dentry, &wr_dir_args); a->btgt = err; return err; }
/* * final procedure of adding a new entry, except link(2). * remove whiteout, instantiate, copyup the parent dir's times and size * and update version. * if it failed, re-create the removed whiteout. */ static int epilog(struct inode *dir, aufs_bindex_t bindex, struct dentry *wh_dentry, struct dentry *dentry) { int err, rerr; aufs_bindex_t bwh; struct path h_path; struct inode *inode, *h_dir; struct dentry *wh; bwh = -1; if (wh_dentry) { h_dir = wh_dentry->d_parent->d_inode; /* dir inode is locked */ IMustLock(h_dir); AuDebugOn(au_h_iptr(dir, bindex) != h_dir); bwh = au_dbwh(dentry); h_path.dentry = wh_dentry; h_path.mnt = au_sbr_mnt(dir->i_sb, bindex); err = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, dentry); if (unlikely(err)) goto out; } inode = au_new_inode(dentry, /*must_new*/1); if (!IS_ERR(inode)) { d_instantiate(dentry, inode); dir = dentry->d_parent->d_inode; /* dir inode is locked */ IMustLock(dir); if (au_ibstart(dir) == au_dbstart(dentry)) au_cpup_attr_timesizes(dir); dir->i_version++; return 0; /* success */ } err = PTR_ERR(inode); if (!wh_dentry) goto out; /* revert */ /* dir inode is locked */ wh = au_wh_create(dentry, bwh, wh_dentry->d_parent); rerr = PTR_ERR(wh); if (IS_ERR(wh)) { AuIOErr("%.*s reverting whiteout failed(%d, %d)\n", AuDLNPair(dentry), err, rerr); err = -EIO; } else dput(wh); out: return err; }
/* 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; }
/* * decide the branch where we operate for @dentry. the branch index will be set * @rbcpup. after diciding it, 'pin' it and store the timestamps of the parent * dir for reverting. * when a new whiteout is necessary, create it. */ static struct dentry* lock_hdir_create_wh(struct dentry *dentry, int isdir, aufs_bindex_t *rbcpup, struct au_dtime *dt, struct au_pin *pin) { struct dentry *wh_dentry; struct super_block *sb; struct path h_path; int err, need_wh; unsigned int udba; aufs_bindex_t bcpup; need_wh = au_wr_dir_need_wh(dentry, isdir, rbcpup); wh_dentry = ERR_PTR(need_wh); if (unlikely(need_wh < 0)) goto out; sb = dentry->d_sb; udba = au_opt_udba(sb); bcpup = *rbcpup; err = au_pin(pin, dentry, bcpup, udba, AuPin_DI_LOCKED | AuPin_MNT_WRITE); wh_dentry = ERR_PTR(err); if (unlikely(err)) goto out; h_path.dentry = au_pinned_h_parent(pin); if (udba != AuOpt_UDBA_NONE && au_dbstart(dentry) == bcpup) { err = au_may_del(dentry, bcpup, h_path.dentry, isdir); wh_dentry = ERR_PTR(err); if (unlikely(err)) goto out_unpin; } h_path.mnt = au_sbr_mnt(sb, bcpup); au_dtime_store(dt, au_pinned_parent(pin), &h_path); wh_dentry = NULL; if (!need_wh) goto out; /* success, no need to create whiteout */ wh_dentry = au_wh_create(dentry, bcpup, h_path.dentry); if (IS_ERR(wh_dentry)) goto out_unpin; /* returns with the parent is locked and wh_dentry is dget-ed */ goto out; /* success */ out_unpin: au_unpin(pin); out: return wh_dentry; }
static int au_ren_lock(struct au_ren_args *a) { int err; unsigned int udba; err = 0; a->src_h_parent = au_h_dptr(a->src_parent, a->btgt); a->src_hdir = au_hi(a->src_dir, a->btgt); a->dst_h_parent = au_h_dptr(a->dst_parent, a->btgt); a->dst_hdir = au_hi(a->dst_dir, a->btgt); a->h_trap = vfsub_lock_rename(a->src_h_parent, a->src_hdir, a->dst_h_parent, a->dst_hdir); udba = au_opt_udba(a->src_dentry->d_sb); if (unlikely(a->src_hdir->hi_inode != a->src_h_parent->d_inode || a->dst_hdir->hi_inode != a->dst_h_parent->d_inode)) err = au_busy_or_stale(); if (!err && au_dbstart(a->src_dentry) == a->btgt) err = au_h_verify(a->src_h_dentry, udba, a->src_h_parent->d_inode, a->src_h_parent, a->br); if (!err && au_dbstart(a->dst_dentry) == a->btgt) err = au_h_verify(a->dst_h_dentry, udba, a->dst_h_parent->d_inode, a->dst_h_parent, a->br); if (!err) { err = mnt_want_write(a->br->br_mnt); if (unlikely(err)) goto out_unlock; au_fset_ren(a->flags, MNT_WRITE); goto out; /* success */ } err = au_busy_or_stale(); out_unlock: au_ren_unlock(a); out: return err; }
/* top down parent */ static int au_wbr_create_tdp(struct dentry *dentry, unsigned int flags __maybe_unused) { int err; aufs_bindex_t bstart, bindex; struct super_block *sb; struct dentry *parent, *h_parent; sb = dentry->d_sb; bstart = au_dbstart(dentry); err = bstart; if (!au_br_rdonly(au_sbr(sb, bstart))) goto out; err = -EROFS; parent = dget_parent(dentry); for (bindex = au_dbstart(parent); bindex < bstart; bindex++) { h_parent = au_h_dptr(parent, bindex); if (!h_parent || !h_parent->d_inode) continue; if (!au_br_rdonly(au_sbr(sb, bindex))) { err = bindex; break; } } dput(parent); /* bottom up here */ if (unlikely(err < 0)) { err = au_wbr_bu(sb, bstart - 1); if (err >= 0) err = au_wbr_nonopq(dentry, err); } out: AuDbg("b%d\n", err); return err; }
/* side effect: sets whlist and h_dentry */ static int au_ren_may_dir(struct au_ren_args *a) { int err; unsigned int rdhash; struct dentry *d; d = a->dst_dentry; SiMustAnyLock(d->d_sb); err = 0; if (au_ftest_ren(a->flags, ISDIR) && a->dst_inode) { rdhash = au_sbi(d->d_sb)->si_rdhash; if (!rdhash) rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, d)); err = au_nhash_alloc(&a->whlist, rdhash, GFP_NOFS); if (unlikely(err)) goto out; au_set_dbstart(d, a->dst_bstart); err = may_rename_dstdir(d, &a->whlist); au_set_dbstart(d, a->btgt); } a->dst_h_dentry = au_h_dptr(d, au_dbstart(d)); if (unlikely(err)) goto out; d = a->src_dentry; a->src_h_dentry = au_h_dptr(d, au_dbstart(d)); if (au_ftest_ren(a->flags, ISDIR)) { err = may_rename_srcdir(d, a->btgt); if (unlikely(err)) { au_nhash_wh_free(&a->whlist); a->whlist.nh_num = 0; } } out: return err; }
/* * 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 struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { struct dentry *ret, *parent; struct inode *inode; struct super_block *sb; int err, npositive; IMustLock(dir); sb = dir->i_sb; si_read_lock(sb, AuLock_FLUSH); ret = ERR_PTR(-ENAMETOOLONG); if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) goto out; err = au_di_init(dentry); ret = ERR_PTR(err); if (unlikely(err)) goto out; parent = dentry->d_parent; /* dir inode is locked */ di_read_lock_parent(parent, AuLock_IR); npositive = au_lkup_dentry(dentry, au_dbstart(parent), /*type*/0, nd); di_read_unlock(parent, AuLock_IR); err = npositive; ret = ERR_PTR(err); if (unlikely(err < 0)) goto out_unlock; inode = NULL; if (npositive) { inode = au_new_inode(dentry, /*must_new*/0); ret = (void *)inode; } if (IS_ERR(inode)) goto out_unlock; ret = d_splice_alias(inode, dentry); if (unlikely(IS_ERR(ret) && inode)) ii_write_unlock(inode); out_unlock: di_write_unlock(dentry); out: si_read_unlock(sb); return ret; }
loff_t au_dir_size(struct file *file, struct dentry *dentry) { loff_t sz; aufs_bindex_t bindex, bend; struct file *h_file; struct dentry *h_dentry; sz = 0; if (file) { AuDebugOn(!file->f_dentry); AuDebugOn(!file->f_dentry->d_inode); AuDebugOn(!S_ISDIR(file->f_dentry->d_inode->i_mode)); bend = au_fbend_dir(file); for (bindex = au_fbstart(file); bindex <= bend && sz < KMALLOC_MAX_SIZE; bindex++) { h_file = au_hf_dir(file, bindex); if (h_file && h_file->f_dentry && h_file->f_dentry->d_inode) sz += i_size_read(h_file->f_dentry->d_inode); } } else { AuDebugOn(!dentry); AuDebugOn(!dentry->d_inode); AuDebugOn(!S_ISDIR(dentry->d_inode->i_mode)); bend = au_dbtaildir(dentry); for (bindex = au_dbstart(dentry); bindex <= bend && sz < KMALLOC_MAX_SIZE; bindex++) { h_dentry = au_h_dptr(dentry, bindex); if (h_dentry && h_dentry->d_inode) sz += i_size_read(h_dentry->d_inode); } } if (sz < KMALLOC_MAX_SIZE) sz = roundup_pow_of_two(sz); if (sz > KMALLOC_MAX_SIZE) sz = KMALLOC_MAX_SIZE; else if (sz < NAME_MAX) { BUILD_BUG_ON(AUFS_RDBLK_DEF < NAME_MAX); sz = AUFS_RDBLK_DEF; } return sz; }
void au_update_dbend(struct dentry *dentry) { aufs_bindex_t bindex, bstart; struct dentry *h_dentry; bstart = au_dbstart(dentry); for (bindex = au_dbend(dentry); 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); } }