static void au_do_unlock(const unsigned char dmsg, struct au_mvd_args *a) { if (!a->rename_lock) au_unpin(&a->mvd_pin_src); else { vfsub_unlock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, a->mvd_h_dst_parent, a->mvd_hdir_dst); au_pin_hdir_lock(&a->mvd_pin_src); au_unpin(&a->mvd_pin_src); au_pin_hdir_lock(&a->mvd_pin_dst); } au_unpin(&a->mvd_pin_dst); }
/* * 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; }
static void au_do_unlock(const unsigned char dmsg, struct au_mvd_args *a) { if (!a->rename_lock) { mutex_unlock(&a->mvd_h_src_dir->i_mutex); au_unpin(&a->pin); } else vfsub_unlock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, a->mvd_h_dst_parent, a->mvd_hdir_dst); }
/* * 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; }
/* * initial procedure of adding a new entry. * prepare writable branch and the parent dir, lock it, * 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; int err; unsigned int mnt_flags; unsigned char pin_flags; aufs_bindex_t bstart, bcpup; struct au_ndx ndx; LKTRTrace("%.*s, src %p\n", AuDLNPair(dentry), src_dentry); bstart = au_dbstart(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; mnt_flags = au_mntflags(sb); pin_flags = AuPin_DI_LOCKED | AuPin_MNT_WRITE; if (dt && au_opt_test(mnt_flags, UDBA_INOTIFY)) au_fset_pin(pin_flags, DO_GPARENT); err = au_pin(pin, dentry, bcpup, pin_flags); wh_dentry = ERR_PTR(err); if (unlikely(err)) goto out; ndx.nfsmnt = au_nfsmnt(sb, bcpup); ndx.flags = 0; if (au_test_dlgt(mnt_flags)) au_fset_ndx(ndx.flags, DLGT); ndx.nd = NULL; /* ndx.br = NULL; */ /* ndx.nd_file = NULL; */ h_parent = au_pinned_h_parent(pin); if (!au_opt_test(mnt_flags, UDBA_NONE) && au_dbstart(dentry) == bcpup) { struct nameidata nd; if (ndx.nfsmnt) { /* todo: dirty? */ ndx.nd = &nd; ndx.br = au_sbr(sb, bcpup); memset(&nd, 0, sizeof(nd)); nd.flags = LOOKUP_CREATE; nd.intent.open.flags = O_EXCL; } err = au_may_add(dentry, bcpup, h_parent, au_ftest_wrdir(wr_dir_args->flags, ISDIR), &ndx); wh_dentry = ERR_PTR(err); if (unlikely(err)) goto out_unpin; ndx.nd = NULL; ndx.br = NULL; } if (dt) au_dtime_store(dt, au_pinned_parent(pin), h_parent, au_pinned_hdir(pin), au_pinned_hgdir(pin)); wh_dentry = NULL; if (/* bcpup != bstart || */ bcpup != au_dbwh(dentry)) goto out; /* success */ wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, &ndx); out_unpin: if (IS_ERR(wh_dentry)) au_unpin(pin); out: AuTraceErrPtr(wh_dentry); return wh_dentry; }
int au_do_pin(struct au_pin *p) { int err; struct super_block *sb; struct dentry *h_dentry, *h_parent; struct au_branch *br; struct inode *h_dir; err = 0; sb = p->dentry->d_sb; br = au_sbr(sb, p->bindex); if (IS_ROOT(p->dentry)) { if (au_ftest_pin(p->flags, MNT_WRITE)) { p->h_mnt = br->br_mnt; err = mnt_want_write(p->h_mnt); if (unlikely(err)) { au_fclr_pin(p->flags, MNT_WRITE); goto out_err; } } goto out; } h_dentry = NULL; if (p->bindex <= au_dbend(p->dentry)) h_dentry = au_h_dptr(p->dentry, p->bindex); p->parent = dget_parent(p->dentry); if (!au_ftest_pin(p->flags, DI_LOCKED)) di_read_lock(p->parent, AuLock_IR, p->lsc_di); h_dir = NULL; h_parent = au_h_dptr(p->parent, p->bindex); p->hdir = au_hi(p->parent->d_inode, p->bindex); if (p->hdir) h_dir = p->hdir->hi_inode; /* * udba case, or * if DI_LOCKED is not set, then p->parent may be different * and h_parent can be NULL. */ if (unlikely(!p->hdir || !h_dir || !h_parent)) { err = -EBUSY; if (!au_ftest_pin(p->flags, DI_LOCKED)) di_read_unlock(p->parent, AuLock_IR); dput(p->parent); p->parent = NULL; goto out_err; } au_igrab(h_dir); au_hn_imtx_lock_nested(p->hdir, p->lsc_hi); if (unlikely(p->hdir->hi_inode != h_parent->d_inode)) { err = -EBUSY; goto out_unpin; } if (h_dentry) { err = au_h_verify(h_dentry, p->udba, h_dir, h_parent, br); if (unlikely(err)) { au_fclr_pin(p->flags, MNT_WRITE); goto out_unpin; } } if (au_ftest_pin(p->flags, MNT_WRITE)) { p->h_mnt = br->br_mnt; err = mnt_want_write(p->h_mnt); if (unlikely(err)) { au_fclr_pin(p->flags, MNT_WRITE); goto out_unpin; } } goto out; /* success */ out_unpin: au_unpin(p); out_err: pr_err("err %d\n", err); err = au_busy_or_stale(); out: return err; }
/* lock them all */ static int au_do_lock(const unsigned char dmsg, struct au_mvd_args *a) { int err; struct dentry *h_trap; a->mvd_h_src_sb = au_sbr_sb(a->sb, a->mvd_bsrc); a->mvd_h_dst_sb = au_sbr_sb(a->sb, a->mvd_bdst); err = au_pin(&a->mvd_pin_dst, a->dentry, a->mvd_bdst, au_opt_udba(a->sb), AuPin_MNT_WRITE | AuPin_DI_LOCKED); AuTraceErr(err); if (unlikely(err)) { AU_MVD_PR(dmsg, "pin_dst failed\n"); goto out; } if (a->mvd_h_src_sb != a->mvd_h_dst_sb) { a->rename_lock = 0; au_pin_init(&a->mvd_pin_src, a->dentry, a->mvd_bsrc, AuLsc_DI_PARENT, AuLsc_I_PARENT3, au_opt_udba(a->sb), AuPin_MNT_WRITE | AuPin_DI_LOCKED); err = au_do_pin(&a->mvd_pin_src); AuTraceErr(err); a->mvd_h_src_dir = d_inode(a->mvd_h_src_parent); if (unlikely(err)) { AU_MVD_PR(dmsg, "pin_src failed\n"); goto out_dst; } goto out; /* success */ } a->rename_lock = 1; au_pin_hdir_unlock(&a->mvd_pin_dst); err = au_pin(&a->mvd_pin_src, a->dentry, a->mvd_bsrc, au_opt_udba(a->sb), AuPin_MNT_WRITE | AuPin_DI_LOCKED); AuTraceErr(err); a->mvd_h_src_dir = d_inode(a->mvd_h_src_parent); if (unlikely(err)) { AU_MVD_PR(dmsg, "pin_src failed\n"); au_pin_hdir_lock(&a->mvd_pin_dst); goto out_dst; } au_pin_hdir_unlock(&a->mvd_pin_src); h_trap = vfsub_lock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, a->mvd_h_dst_parent, a->mvd_hdir_dst); if (h_trap) { err = (h_trap != a->mvd_h_src_parent); if (err) err = (h_trap != a->mvd_h_dst_parent); } BUG_ON(err); /* it should never happen */ if (unlikely(a->mvd_h_src_dir != au_pinned_h_dir(&a->mvd_pin_src))) { err = -EBUSY; AuTraceErr(err); vfsub_unlock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, a->mvd_h_dst_parent, a->mvd_hdir_dst); au_pin_hdir_lock(&a->mvd_pin_src); au_unpin(&a->mvd_pin_src); au_pin_hdir_lock(&a->mvd_pin_dst); goto out_dst; } goto out; /* success */ out_dst: au_unpin(&a->mvd_pin_dst); out: AuTraceErr(err); return err; }
static int au_ready_to_write_wh(struct file *file, loff_t len, aufs_bindex_t bcpup, struct au_pin *pin) { int err; struct inode *inode, *h_inode; struct dentry *h_dentry, *hi_wh; struct au_cp_generic cpg = { .dentry = file->f_dentry, .bdst = bcpup, .bsrc = -1, .len = len, .pin = pin }; au_update_dbstart(cpg.dentry); inode = cpg.dentry->d_inode; h_inode = NULL; if (au_dbstart(cpg.dentry) <= bcpup && au_dbend(cpg.dentry) >= bcpup) { h_dentry = au_h_dptr(cpg.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(&cpg, 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(cpg.dentry->d_sb), PLINK)) au_plink_append(inode, bcpup, au_h_dptr(cpg.dentry, bcpup)); return err; } /* * prepare the @file for writing. */ int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin) { int err; aufs_bindex_t dbstart; struct dentry *parent, *h_dentry; struct inode *inode; struct super_block *sb; struct file *h_file; struct au_cp_generic cpg = { .dentry = file->f_dentry, .bdst = -1, .bsrc = -1, .len = len, .pin = pin, .flags = AuCpup_DTIME }; sb = cpg.dentry->d_sb; inode = cpg.dentry->d_inode; AuDebugOn(au_special_file(inode->i_mode)); cpg.bsrc = au_fbstart(file); err = au_test_ro(sb, cpg.bsrc, inode); if (!err && (au_hf_top(file)->f_mode & FMODE_WRITE)) { err = au_pin(pin, cpg.dentry, cpg.bsrc, AuOpt_UDBA_NONE, /*flags*/0); goto out; } /* need to cpup or reopen */ parent = dget_parent(cpg.dentry); di_write_lock_parent(parent); err = AuWbrCopyup(au_sbi(sb), cpg.dentry); cpg.bdst = err; if (unlikely(err < 0)) goto out_dgrade; err = 0; if (!d_unhashed(cpg.dentry) && !au_h_dptr(parent, cpg.bdst)) { err = au_cpup_dirs(cpg.dentry, cpg.bdst); if (unlikely(err)) goto out_dgrade; } err = au_pin(pin, cpg.dentry, cpg.bdst, AuOpt_UDBA_NONE, AuPin_DI_LOCKED | AuPin_MNT_WRITE); if (unlikely(err)) goto out_dgrade; h_dentry = au_hf_top(file)->f_dentry; dbstart = au_dbstart(cpg.dentry); if (dbstart <= cpg.bdst) { h_dentry = au_h_dptr(cpg.dentry, cpg.bdst); AuDebugOn(!h_dentry); cpg.bsrc = cpg.bdst; } if (dbstart <= cpg.bdst /* just reopen */ || !d_unhashed(cpg.dentry) /* copyup and reopen */ ) { h_file = au_h_open_pre(cpg.dentry, cpg.bsrc, /*force_wr*/0); if (IS_ERR(h_file)) err = PTR_ERR(h_file); else { di_downgrade_lock(parent, AuLock_IR); if (dbstart > cpg.bdst) err = au_sio_cpup_simple(&cpg); if (!err) err = au_reopen_nondir(file); au_h_open_post(cpg.dentry, cpg.bsrc, h_file); } } else { /* copyup as wh and reopen */ /* * since writable hfsplus branch is not supported, * h_open_pre/post() are unnecessary. */ err = au_ready_to_write_wh(file, len, cpg.bdst, pin); di_downgrade_lock(parent, AuLock_IR); } if (!err) { au_pin_set_parent_lflag(pin, /*lflag*/0); goto out_dput; /* success */ } au_unpin(pin); goto out_unlock; out_dgrade: di_downgrade_lock(parent, AuLock_IR); out_unlock: di_read_unlock(parent, AuLock_IR); out_dput: dput(parent); out: return err; } /* ---------------------------------------------------------------------- */ int au_do_flush(struct file *file, fl_owner_t id, int (*flush)(struct file *file, fl_owner_t id)) { int err; struct super_block *sb; struct inode *inode; inode = file_inode(file); sb = inode->i_sb; si_noflush_read_lock(sb); fi_read_lock(file); ii_read_lock_child(inode); err = flush(file, id); au_cpup_attr_timesizes(inode); ii_read_unlock(inode); fi_read_unlock(file); si_read_unlock(sb); return err; }
/* * prepare the @file for writing. */ int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin) { int err; aufs_bindex_t bstart, bcpup, dbstart; struct dentry *dentry, *parent, *h_dentry; struct inode *h_inode, *inode; struct super_block *sb; struct file *h_file; dentry = file->f_dentry; sb = dentry->d_sb; inode = dentry->d_inode; AuDebugOn(au_special_file(inode->i_mode)); bstart = au_fbstart(file); err = au_test_ro(sb, bstart, inode); if (!err && (au_hf_top(file)->f_mode & FMODE_WRITE)) { err = au_pin(pin, dentry, bstart, AuOpt_UDBA_NONE, /*flags*/0); goto out; } /* need to cpup or reopen */ parent = dget_parent(dentry); di_write_lock_parent(parent); err = AuWbrCopyup(au_sbi(sb), dentry); bcpup = err; if (unlikely(err < 0)) goto out_dgrade; err = 0; if (!d_unhashed(dentry) && !au_h_dptr(parent, bcpup)) { err = au_cpup_dirs(dentry, bcpup); if (unlikely(err)) goto out_dgrade; } err = au_pin(pin, dentry, bcpup, AuOpt_UDBA_NONE, AuPin_DI_LOCKED | AuPin_MNT_WRITE); if (unlikely(err)) goto out_dgrade; h_dentry = au_hf_top(file)->f_dentry; h_inode = h_dentry->d_inode; dbstart = au_dbstart(dentry); if (dbstart <= bcpup) { h_dentry = au_h_dptr(dentry, bcpup); AuDebugOn(!h_dentry); h_inode = h_dentry->d_inode; AuDebugOn(!h_inode); bstart = bcpup; } if (dbstart <= bcpup /* just reopen */ || !d_unhashed(dentry) /* copyup and reopen */ ) { mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); h_file = au_h_open_pre(dentry, bstart); if (IS_ERR(h_file)) { err = PTR_ERR(h_file); h_file = NULL; } else { di_downgrade_lock(parent, AuLock_IR); if (dbstart > bcpup) err = au_sio_cpup_simple(dentry, bcpup, len, AuCpup_DTIME); if (!err) err = au_reopen_nondir(file); } mutex_unlock(&h_inode->i_mutex); au_h_open_post(dentry, bstart, h_file); } else { /* copyup as wh and reopen */ /* * since writable hfsplus branch is not supported, * h_open_pre/post() are unnecessary. */ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); err = au_ready_to_write_wh(file, len, bcpup); di_downgrade_lock(parent, AuLock_IR); mutex_unlock(&h_inode->i_mutex); } if (!err) { au_pin_set_parent_lflag(pin, /*lflag*/0); goto out_dput; /* success */ } au_unpin(pin); goto out_unlock; out_dgrade: di_downgrade_lock(parent, AuLock_IR); out_unlock: di_read_unlock(parent, AuLock_IR); out_dput: dput(parent); out: return err; }
int aufs_link(struct dentry *src_dentry, struct inode *dir, struct dentry *dentry) { int err, rerr; struct au_dtime dt; struct au_link_args *a; struct dentry *wh_dentry, *h_src_dentry; struct inode *inode; struct super_block *sb; struct au_wr_dir_args wr_dir_args = { /* .force_btgt = -1, */ .flags = AuWrDir_ADD_ENTRY }; IMustLock(dir); inode = src_dentry->d_inode; IMustLock(inode); err = -ENOMEM; a = kzalloc(sizeof(*a), GFP_NOFS); if (unlikely(!a)) goto out; a->parent = dentry->d_parent; /* dir inode is locked */ err = aufs_read_and_write_lock2(dentry, src_dentry, AuLock_NOPLM | AuLock_GEN); if (unlikely(err)) goto out_kfree; err = au_d_hashed_positive(src_dentry); if (unlikely(err)) goto out_unlock; err = au_d_may_add(dentry); if (unlikely(err)) goto out_unlock; a->src_parent = dget_parent(src_dentry); wr_dir_args.force_btgt = au_ibstart(inode); di_write_lock_parent(a->parent); wr_dir_args.force_btgt = au_wbr(dentry, wr_dir_args.force_btgt); wh_dentry = lock_hdir_lkup_wh(dentry, &dt, src_dentry, &a->pin, &wr_dir_args); err = PTR_ERR(wh_dentry); if (IS_ERR(wh_dentry)) goto out_parent; err = 0; sb = dentry->d_sb; a->bdst = au_dbstart(dentry); a->h_path.dentry = au_h_dptr(dentry, a->bdst); a->h_path.mnt = au_sbr_mnt(sb, a->bdst); a->bsrc = au_ibstart(inode); h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); if (!h_src_dentry) { a->bsrc = au_dbstart(src_dentry); h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); AuDebugOn(!h_src_dentry); } else if (IS_ERR(h_src_dentry)) goto out_parent; if (au_opt_test(au_mntflags(sb), PLINK)) { if (a->bdst < a->bsrc /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) err = au_cpup_or_link(src_dentry, a); else err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), &a->h_path); dput(h_src_dentry); } else { /* * copyup src_dentry to the branch we process, * and then link(2) to it. */ dput(h_src_dentry); if (a->bdst < a->bsrc /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) { au_unpin(&a->pin); di_write_unlock(a->parent); err = au_cpup_before_link(src_dentry, a); di_write_lock_parent(a->parent); if (!err) err = au_pin(&a->pin, dentry, a->bdst, au_opt_udba(sb), AuPin_DI_LOCKED | AuPin_MNT_WRITE); if (unlikely(err)) goto out_wh; } if (!err) { h_src_dentry = au_h_dptr(src_dentry, a->bdst); err = -ENOENT; if (h_src_dentry && h_src_dentry->d_inode) err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), &a->h_path); } } if (unlikely(err)) goto out_unpin; if (wh_dentry) { a->h_path.dentry = wh_dentry; err = au_wh_unlink_dentry(au_pinned_h_dir(&a->pin), &a->h_path, dentry); if (unlikely(err)) goto out_revert; } dir->i_version++; if (au_ibstart(dir) == au_dbstart(dentry)) au_cpup_attr_timesizes(dir); inc_nlink(inode); inode->i_ctime = dir->i_ctime; d_instantiate(dentry, au_igrab(inode)); if (d_unhashed(a->h_path.dentry)) /* some filesystem calls d_drop() */ d_drop(dentry); goto out_unpin; /* success */ out_revert: rerr = vfsub_unlink(au_pinned_h_dir(&a->pin), &a->h_path, /*force*/0); if (unlikely(rerr)) { AuIOErr("%.*s reverting failed(%d, %d)\n", AuDLNPair(dentry), err, rerr); err = -EIO; } au_dtime_revert(&dt); out_unpin: au_unpin(&a->pin); out_wh: dput(wh_dentry); out_parent: di_write_unlock(a->parent); dput(a->src_parent); out_unlock: if (unlikely(err)) { au_update_dbstart(dentry); d_drop(dentry); } aufs_read_and_write_unlock2(dentry, src_dentry); out_kfree: kfree(a); 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 = br->br_mnt }; 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; } /* ---------------------------------------------------------------------- */ enum { Mknod, Symlink, Creat }; struct simple_arg { int type; union { struct { int mode; struct nameidata *nd; } c; struct { const char *symname; } s; struct { int mode; dev_t dev; } m; } u; }; static int add_simple(struct inode *dir, struct dentry *dentry, struct simple_arg *arg) { int err; aufs_bindex_t bstart; unsigned char created; struct au_dtime dt; struct au_pin pin; struct path h_path; struct dentry *wh_dentry, *parent; struct inode *h_dir; struct au_wr_dir_args wr_dir_args = { .force_btgt = -1, .flags = AuWrDir_ADD_ENTRY }; AuDbg("%.*s\n", AuDLNPair(dentry)); IMustLock(dir); parent = dentry->d_parent; /* dir inode is locked */ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); if (unlikely(err)) goto out; err = au_d_may_add(dentry); if (unlikely(err)) goto out_unlock; di_write_lock_parent(parent); wh_dentry = lock_hdir_lkup_wh(dentry, &dt, /*src_dentry*/NULL, &pin, &wr_dir_args); err = PTR_ERR(wh_dentry); if (IS_ERR(wh_dentry)) goto out_parent; bstart = au_dbstart(dentry); h_path.dentry = au_h_dptr(dentry, bstart); h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart); h_dir = au_pinned_h_dir(&pin); switch (arg->type) { case Creat: err = vfsub_create(h_dir, &h_path, arg->u.c.mode); break; case Symlink: err = vfsub_symlink(h_dir, &h_path, arg->u.s.symname); break; case Mknod: err = vfsub_mknod(h_dir, &h_path, arg->u.m.mode, arg->u.m.dev); break; default: BUG(); } created = !err; if (!err) err = epilog(dir, bstart, wh_dentry, dentry); /* revert */ if (unlikely(created && err && h_path.dentry->d_inode)) { int rerr; rerr = vfsub_unlink(h_dir, &h_path, /*force*/0); if (rerr) { AuIOErr("%.*s revert failure(%d, %d)\n", AuDLNPair(dentry), err, rerr); err = -EIO; } au_dtime_revert(&dt); } au_unpin(&pin); dput(wh_dentry); out_parent: di_write_unlock(parent); out_unlock: if (unlikely(err)) { au_update_dbstart(dentry); d_drop(dentry); } aufs_read_unlock(dentry, AuLock_DW); out: return err; } int aufs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) { struct simple_arg arg = { .type = Mknod, .u.m = { .mode = mode, .dev = dev } }; return add_simple(dir, dentry, &arg); } int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { struct simple_arg arg = { .type = Symlink, .u.s.symname = symname }; return add_simple(dir, dentry, &arg); } int aufs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) { struct simple_arg arg = { .type = Creat, .u.c = { .mode = mode, .nd = nd } }; return add_simple(dir, dentry, &arg); } /* ---------------------------------------------------------------------- */ struct au_link_args { aufs_bindex_t bdst, bsrc; struct au_pin pin; struct path h_path; struct dentry *src_parent, *parent; }; static int au_cpup_before_link(struct dentry *src_dentry, struct au_link_args *a) { int err; struct dentry *h_src_dentry; struct mutex *h_mtx; struct file *h_file; di_read_lock_parent(a->src_parent, AuLock_IR); err = au_test_and_cpup_dirs(src_dentry, a->bdst); if (unlikely(err)) goto out; h_src_dentry = au_h_dptr(src_dentry, a->bsrc); h_mtx = &h_src_dentry->d_inode->i_mutex; err = au_pin(&a->pin, src_dentry, a->bdst, au_opt_udba(src_dentry->d_sb), AuPin_DI_LOCKED | AuPin_MNT_WRITE); if (unlikely(err)) goto out; mutex_lock_nested(h_mtx, AuLsc_I_CHILD); h_file = au_h_open_pre(src_dentry, a->bsrc); if (IS_ERR(h_file)) { err = PTR_ERR(h_file); h_file = NULL; } else err = au_sio_cpup_simple(src_dentry, a->bdst, a->bsrc, AuCpup_DTIME /* | AuCpup_KEEPLINO */); mutex_unlock(h_mtx); au_h_open_post(src_dentry, a->bsrc, h_file); au_unpin(&a->pin); out: di_read_unlock(a->src_parent, AuLock_IR); return err; } static int au_cpup_or_link(struct dentry *src_dentry, struct au_link_args *a) { int err; unsigned char plink; struct inode *h_inode, *inode; struct dentry *h_src_dentry; struct super_block *sb; struct file *h_file; plink = 0; h_inode = NULL; sb = src_dentry->d_sb; inode = src_dentry->d_inode; if (au_ibstart(inode) <= a->bdst) h_inode = au_h_iptr(inode, a->bdst); if (!h_inode || !h_inode->i_nlink) { /* copyup src_dentry as the name of dentry. */ au_set_dbstart(src_dentry, a->bdst); au_set_h_dptr(src_dentry, a->bdst, dget(a->h_path.dentry)); h_inode = au_h_dptr(src_dentry, a->bsrc)->d_inode; mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); h_file = au_h_open_pre(src_dentry, a->bsrc); if (IS_ERR(h_file)) { err = PTR_ERR(h_file); h_file = NULL; } else err = au_sio_cpup_single(src_dentry, a->bdst, a->bsrc, -1, AuCpup_KEEPLINO, a->parent); mutex_unlock(&h_inode->i_mutex); au_h_open_post(src_dentry, a->bsrc, h_file); au_set_h_dptr(src_dentry, a->bdst, NULL); au_set_dbstart(src_dentry, a->bsrc); } else { /* the inode of src_dentry already exists on a.bdst branch */ h_src_dentry = d_find_alias(h_inode); if (!h_src_dentry && au_plink_test(inode)) { plink = 1; h_src_dentry = au_plink_lkup(inode, a->bdst); err = PTR_ERR(h_src_dentry); if (IS_ERR(h_src_dentry)) goto out; if (unlikely(!h_src_dentry->d_inode)) { dput(h_src_dentry); h_src_dentry = NULL; } } if (h_src_dentry) { err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), &a->h_path); dput(h_src_dentry); } else { AuIOErr("no dentry found for hi%lu on b%d\n", h_inode->i_ino, a->bdst); err = -EIO; } } if (!err && !plink) au_plink_append(inode, a->bdst, a->h_path.dentry); out: AuTraceErr(err); return err; }
/* * when an error happened, remove the created whiteout and revert everything. */ static int do_revert(int err, struct inode *dir, aufs_bindex_t bindex, aufs_bindex_t bwh, struct dentry *wh_dentry, struct dentry *dentry, struct au_dtime *dt) { int rerr; struct path h_path = { .dentry = wh_dentry, .mnt = au_sbr_mnt(dir->i_sb, bindex) }; rerr = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, dentry); if (!rerr) { au_set_dbwh(dentry, bwh); au_dtime_revert(dt); return 0; } AuIOErr("%pd reverting whiteout failed(%d, %d)\n", dentry, err, rerr); return -EIO; } /* ---------------------------------------------------------------------- */ int aufs_unlink(struct inode *dir, struct dentry *dentry) { int err; aufs_bindex_t bwh, bindex, bstart; struct inode *inode, *h_dir, *delegated; struct dentry *parent, *wh_dentry; /* to reuduce stack size */ struct { struct au_dtime dt; struct au_pin pin; struct path h_path; } *a; IMustLock(dir); err = -ENOMEM; a = kmalloc(sizeof(*a), GFP_NOFS); if (unlikely(!a)) goto out; err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); if (unlikely(err)) goto out_free; err = au_d_hashed_positive(dentry); if (unlikely(err)) goto out_unlock; inode = d_inode(dentry); IMustLock(inode); err = -EISDIR; if (unlikely(d_is_dir(dentry))) goto out_unlock; /* possible? */ bstart = au_dbstart(dentry); bwh = au_dbwh(dentry); bindex = -1; parent = dentry->d_parent; /* dir inode is locked */ di_write_lock_parent(parent); wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &a->dt, &a->pin); err = PTR_ERR(wh_dentry); if (IS_ERR(wh_dentry)) goto out_parent; a->h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart); a->h_path.dentry = au_h_dptr(dentry, bstart); dget(a->h_path.dentry); if (bindex == bstart) { h_dir = au_pinned_h_dir(&a->pin); delegated = NULL; err = vfsub_unlink(h_dir, &a->h_path, &delegated, /*force*/0); if (unlikely(err == -EWOULDBLOCK)) { pr_warn("cannot retry for NFSv4 delegation" " for an internal unlink\n"); iput(delegated); } } else { /* dir inode is locked */ h_dir = d_inode(wh_dentry->d_parent); IMustLock(h_dir); err = 0; } if (!err) { vfsub_drop_nlink(inode); epilog(dir, dentry, bindex); /* update target timestamps */ if (bindex == bstart) { vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/ inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime; } else /* todo: this timestamp may be reverted later */ inode->i_ctime = h_dir->i_ctime; goto out_unpin; /* success */ } /* revert */ if (wh_dentry) { int rerr; rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, &a->dt); if (rerr) err = rerr; } out_unpin: au_unpin(&a->pin); dput(wh_dentry); dput(a->h_path.dentry); out_parent: di_write_unlock(parent); out_unlock: aufs_read_unlock(dentry, AuLock_DW); out_free: kfree(a); out: return err; }
/* * prepare the @file for writing. */ int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin) { int err; aufs_bindex_t bstart, bcpup; struct dentry *dentry, *parent, *h_dentry; struct inode *h_inode, *inode; struct super_block *sb; dentry = file->f_dentry; sb = dentry->d_sb; inode = dentry->d_inode; AuDebugOn(au_special_file(inode->i_mode)); bstart = au_fbstart(file); err = au_test_ro(sb, bstart, inode); if (!err && (au_h_fptr(file, bstart)->f_mode & FMODE_WRITE)) { err = au_pin(pin, dentry, bstart, AuOpt_UDBA_NONE, /*flags*/0); goto out; } /* need to cpup */ parent = dget_parent(dentry); di_write_lock_parent(parent); err = AuWbrCopyup(au_sbi(sb), dentry); bcpup = err; if (unlikely(err < 0)) goto out_dgrade; err = 0; if (!au_h_dptr(parent, bcpup)) { err = au_cpup_dirs(dentry, bcpup); if (unlikely(err)) goto out_dgrade; } err = au_pin(pin, dentry, bcpup, AuOpt_UDBA_NONE, AuPin_DI_LOCKED | AuPin_MNT_WRITE); if (unlikely(err)) goto out_dgrade; h_dentry = au_h_fptr(file, bstart)->f_dentry; h_inode = h_dentry->d_inode; mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); if (d_unhashed(dentry) /* || d_unhashed(h_dentry) */ /* || !h_inode->i_nlink */) { err = au_ready_to_write_wh(file, len, bcpup); di_downgrade_lock(parent, AuLock_IR); } else { di_downgrade_lock(parent, AuLock_IR); if (!au_h_dptr(dentry, bcpup)) err = au_sio_cpup_simple(dentry, bcpup, len, AuCpup_DTIME); if (!err) err = au_reopen_nondir(file); } mutex_unlock(&h_inode->i_mutex); if (!err) { au_pin_set_parent_lflag(pin, /*lflag*/0); goto out_dput; /* success */ } au_unpin(pin); goto out_unlock; out_dgrade: di_downgrade_lock(parent, AuLock_IR); out_unlock: di_read_unlock(parent, AuLock_IR); out_dput: dput(parent); out: return err; }
static int au_file_refresh_by_inode(struct file *file, int *need_reopen) { int err; struct au_pin pin; struct au_finfo *finfo; struct dentry *parent, *hi_wh; struct inode *inode; struct super_block *sb; struct au_cp_generic cpg = { .dentry = file->f_dentry, .bdst = -1, .bsrc = -1, .len = -1, .pin = &pin, .flags = AuCpup_DTIME }; FiMustWriteLock(file); err = 0; finfo = au_fi(file); sb = cpg.dentry->d_sb; inode = cpg.dentry->d_inode; cpg.bdst = au_ibstart(inode); if (cpg.bdst == finfo->fi_btop || IS_ROOT(cpg.dentry)) goto out; parent = dget_parent(cpg.dentry); if (au_test_ro(sb, cpg.bdst, inode)) { di_read_lock_parent(parent, !AuLock_IR); err = AuWbrCopyup(au_sbi(sb), cpg.dentry); cpg.bdst = err; di_read_unlock(parent, !AuLock_IR); if (unlikely(err < 0)) goto out_parent; err = 0; } di_read_lock_parent(parent, AuLock_IR); hi_wh = au_hi_wh(inode, cpg.bdst); if (!S_ISDIR(inode->i_mode) && au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(inode) && !d_unhashed(cpg.dentry) && cpg.bdst < au_dbstart(cpg.dentry)) { err = au_test_and_cpup_dirs(cpg.dentry, cpg.bdst); if (unlikely(err)) goto out_unlock; /* always superio. */ err = au_pin(&pin, cpg.dentry, cpg.bdst, AuOpt_UDBA_NONE, AuPin_DI_LOCKED | AuPin_MNT_WRITE); if (!err) { err = au_sio_cpup_simple(&cpg); au_unpin(&pin); } } else if (hi_wh) { /* already copied-up after unlink */ err = au_reopen_wh(file, cpg.bdst, hi_wh); *need_reopen = 0; } out_unlock: di_read_unlock(parent, AuLock_IR); out_parent: dput(parent); out: return err; } static void au_do_refresh_dir(struct file *file) { aufs_bindex_t bindex, bend, new_bindex, brid; struct au_hfile *p, tmp, *q; struct au_finfo *finfo; struct super_block *sb; struct au_fidir *fidir; FiMustWriteLock(file); sb = file->f_dentry->d_sb; finfo = au_fi(file); fidir = finfo->fi_hdir; AuDebugOn(!fidir); p = fidir->fd_hfile + finfo->fi_btop; brid = p->hf_br->br_id; bend = fidir->fd_bbot; for (bindex = finfo->fi_btop; bindex <= bend; bindex++, p++) { if (!p->hf_file) continue; new_bindex = au_br_index(sb, p->hf_br->br_id); if (new_bindex == bindex) continue; if (new_bindex < 0) { au_set_h_fptr(file, bindex, NULL); continue; } /* swap two lower inode, and loop again */ q = fidir->fd_hfile + new_bindex; tmp = *q; *q = *p; *p = tmp; if (tmp.hf_file) { bindex--; p--; } } p = fidir->fd_hfile; if (!au_test_mmapped(file) && !d_unlinked(file->f_dentry)) { bend = au_sbend(sb); for (finfo->fi_btop = 0; finfo->fi_btop <= bend; finfo->fi_btop++, p++) if (p->hf_file) { if (file_inode(p->hf_file)) break; else au_hfput(p, file); } } else { bend = au_br_index(sb, brid); for (finfo->fi_btop = 0; finfo->fi_btop < bend; finfo->fi_btop++, p++) if (p->hf_file) au_hfput(p, file); bend = au_sbend(sb); } p = fidir->fd_hfile + bend; for (fidir->fd_bbot = bend; fidir->fd_bbot >= finfo->fi_btop; fidir->fd_bbot--, p--) if (p->hf_file) { if (file_inode(p->hf_file)) break; else au_hfput(p, file); } AuDebugOn(fidir->fd_bbot < finfo->fi_btop); }
/* * when an error happened, remove the created whiteout and revert everything. */ static int do_revert(int err, struct inode *dir, aufs_bindex_t bindex, aufs_bindex_t bwh, struct dentry *wh_dentry, struct dentry *dentry, struct au_dtime *dt) { int rerr; struct path h_path = { .dentry = wh_dentry, .mnt = au_sbr_mnt(dir->i_sb, bindex) }; rerr = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, dentry); if (!rerr) { au_set_dbwh(dentry, bwh); au_dtime_revert(dt); return 0; } AuIOErr("%.*s reverting whiteout failed(%d, %d)\n", AuDLNPair(dentry), err, rerr); return -EIO; } /* ---------------------------------------------------------------------- */ int aufs_unlink(struct inode *dir, struct dentry *dentry) { int err; aufs_bindex_t bwh, bindex, bstart; struct au_dtime dt; struct au_pin pin; struct path h_path; struct inode *inode, *h_dir; struct dentry *parent, *wh_dentry; IMustLock(dir); err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); if (unlikely(err)) goto out; err = au_d_hashed_positive(dentry); if (unlikely(err)) goto out_unlock; inode = dentry->d_inode; IMustLock(inode); err = -EISDIR; if (unlikely(S_ISDIR(inode->i_mode))) goto out_unlock; /* possible? */ bstart = au_dbstart(dentry); bwh = au_dbwh(dentry); bindex = -1; parent = dentry->d_parent; /* dir inode is locked */ di_write_lock_parent(parent); wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &dt, &pin); err = PTR_ERR(wh_dentry); if (IS_ERR(wh_dentry)) goto out_parent; h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart); h_path.dentry = au_h_dptr(dentry, bstart); dget(h_path.dentry); if (bindex == bstart) { h_dir = au_pinned_h_dir(&pin); err = vfsub_unlink(h_dir, &h_path, /*force*/0); } else { /* dir inode is locked */ h_dir = wh_dentry->d_parent->d_inode; IMustLock(h_dir); err = 0; } if (!err) { vfsub_drop_nlink(inode); epilog(dir, dentry, bindex); /* update target timestamps */ if (bindex == bstart) { vfsub_update_h_iattr(&h_path, /*did*/NULL); /*ignore*/ inode->i_ctime = h_path.dentry->d_inode->i_ctime; } else /* todo: this timestamp may be reverted later */ inode->i_ctime = h_dir->i_ctime; goto out_unpin; /* success */ } /* revert */ if (wh_dentry) { int rerr; rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, &dt); if (rerr) err = rerr; } out_unpin: au_unpin(&pin); dput(wh_dentry); dput(h_path.dentry); out_parent: di_write_unlock(parent); out_unlock: aufs_read_unlock(dentry, AuLock_DW); out: return err; }
int aufs_rmdir(struct inode *dir, struct dentry *dentry) { int err, rmdir_later; aufs_bindex_t bwh, bindex, bstart; struct au_dtime dt; struct au_pin pin; struct inode *inode; struct dentry *parent, *wh_dentry, *h_dentry; struct au_whtmp_rmdir *args; IMustLock(dir); err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); if (unlikely(err)) goto out; err = au_alive_dir(dentry); if (unlikely(err)) goto out_unlock; inode = dentry->d_inode; IMustLock(inode); err = -ENOTDIR; if (unlikely(!S_ISDIR(inode->i_mode))) goto out_unlock; /* possible? */ err = -ENOMEM; args = au_whtmp_rmdir_alloc(dir->i_sb, GFP_NOFS); if (unlikely(!args)) goto out_unlock; parent = dentry->d_parent; /* dir inode is locked */ di_write_lock_parent(parent); err = au_test_empty(dentry, &args->whlist); if (unlikely(err)) goto out_parent; bstart = au_dbstart(dentry); bwh = au_dbwh(dentry); bindex = -1; wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &dt, &pin); err = PTR_ERR(wh_dentry); if (IS_ERR(wh_dentry)) goto out_parent; h_dentry = au_h_dptr(dentry, bstart); dget(h_dentry); rmdir_later = 0; if (bindex == bstart) { err = renwh_and_rmdir(dentry, bstart, &args->whlist, dir); if (err > 0) { rmdir_later = err; err = 0; } } else { /* stop monitoring */ au_hn_free(au_hi(inode, bstart)); /* dir inode is locked */ IMustLock(wh_dentry->d_parent->d_inode); err = 0; } if (!err) { vfsub_dead_dir(inode); au_set_dbdiropq(dentry, -1); epilog(dir, dentry, bindex); if (rmdir_later) { au_whtmp_kick_rmdir(dir, bstart, h_dentry, args); args = NULL; } goto out_unpin; /* success */ } /* revert */ AuLabel(revert); if (wh_dentry) { int rerr; rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, &dt); if (rerr) err = rerr; } out_unpin: au_unpin(&pin); dput(wh_dentry); dput(h_dentry); out_parent: di_write_unlock(parent); if (args) au_whtmp_rmdir_free(args); out_unlock: aufs_read_unlock(dentry, AuLock_DW); out: AuTraceErr(err); return err; }
int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode) { int err, rerr; aufs_bindex_t bindex; unsigned char diropq; struct path h_path; struct dentry *wh_dentry, *parent, *opq_dentry; struct mutex *h_mtx; struct super_block *sb; struct { struct au_pin pin; struct au_dtime dt; } *a; /* reduce the stack usage */ struct au_wr_dir_args wr_dir_args = { .force_btgt = -1, .flags = AuWrDir_ADD_ENTRY | AuWrDir_ISDIR }; IMustLock(dir); err = -ENOMEM; a = kmalloc(sizeof(*a), GFP_NOFS); if (unlikely(!a)) goto out; err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); if (unlikely(err)) goto out_free; err = au_d_may_add(dentry); if (unlikely(err)) goto out_unlock; parent = dentry->d_parent; /* dir inode is locked */ di_write_lock_parent(parent); wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL, &a->pin, &wr_dir_args); err = PTR_ERR(wh_dentry); if (IS_ERR(wh_dentry)) goto out_parent; sb = dentry->d_sb; bindex = au_dbstart(dentry); h_path.dentry = au_h_dptr(dentry, bindex); h_path.mnt = au_sbr_mnt(sb, bindex); err = vfsub_mkdir(au_pinned_h_dir(&a->pin), &h_path, mode); if (unlikely(err)) goto out_unpin; /* make the dir opaque */ diropq = 0; h_mtx = &h_path.dentry->d_inode->i_mutex; if (wh_dentry || au_opt_test(au_mntflags(sb), ALWAYS_DIROPQ)) { mutex_lock_nested(h_mtx, AuLsc_I_CHILD); opq_dentry = au_diropq_create(dentry, bindex); mutex_unlock(h_mtx); err = PTR_ERR(opq_dentry); if (IS_ERR(opq_dentry)) goto out_dir; dput(opq_dentry); diropq = 1; } err = epilog(dir, bindex, wh_dentry, dentry); if (!err) { inc_nlink(dir); goto out_unpin; /* success */ } /* revert */ if (diropq) { AuLabel(revert opq); mutex_lock_nested(h_mtx, AuLsc_I_CHILD); rerr = au_diropq_remove(dentry, bindex); mutex_unlock(h_mtx); if (rerr) { AuIOErr("%.*s reverting diropq failed(%d, %d)\n", AuDLNPair(dentry), err, rerr); err = -EIO; } } out_dir: AuLabel(revert dir); rerr = vfsub_rmdir(au_pinned_h_dir(&a->pin), &h_path); if (rerr) { AuIOErr("%.*s reverting dir failed(%d, %d)\n", AuDLNPair(dentry), err, rerr); err = -EIO; } au_dtime_revert(&a->dt); out_unpin: au_unpin(&a->pin); dput(wh_dentry); out_parent: di_write_unlock(parent); out_unlock: if (unlikely(err)) { au_update_dbstart(dentry); d_drop(dentry); } aufs_read_unlock(dentry, AuLock_DW); out_free: kfree(a); out: return err; }
static int au_file_refresh_by_inode(struct file *file, int *need_reopen) { int err; aufs_bindex_t bstart; struct au_pin pin; struct au_finfo *finfo; struct dentry *dentry, *parent, *hi_wh; struct inode *inode; struct super_block *sb; FiMustWriteLock(file); err = 0; finfo = au_fi(file); dentry = file->f_dentry; sb = dentry->d_sb; inode = dentry->d_inode; bstart = au_ibstart(inode); if (bstart == finfo->fi_btop || IS_ROOT(dentry)) goto out; parent = dget_parent(dentry); if (au_test_ro(sb, bstart, inode)) { di_read_lock_parent(parent, !AuLock_IR); err = AuWbrCopyup(au_sbi(sb), dentry); bstart = err; di_read_unlock(parent, !AuLock_IR); if (unlikely(err < 0)) goto out_parent; err = 0; } di_read_lock_parent(parent, AuLock_IR); hi_wh = au_hi_wh(inode, bstart); if (!S_ISDIR(inode->i_mode) && au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(inode) && !d_unhashed(dentry)) { err = au_test_and_cpup_dirs(dentry, bstart); if (unlikely(err)) goto out_unlock; /* always superio. */ err = au_pin(&pin, dentry, bstart, AuOpt_UDBA_NONE, AuPin_DI_LOCKED | AuPin_MNT_WRITE); if (!err) err = au_sio_cpup_simple(dentry, bstart, -1, AuCpup_DTIME); au_unpin(&pin); } else if (hi_wh) { /* already copied-up after unlink */ err = au_reopen_wh(file, bstart, hi_wh); *need_reopen = 0; } out_unlock: di_read_unlock(parent, AuLock_IR); out_parent: dput(parent); out: 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; }