static void au_ren_rev_whsrc(int err, struct au_ren_args *a) { int rerr; a->h_path.dentry = a->src_wh_dentry; rerr = au_wh_unlink_dentry(a->src_h_dir, &a->h_path, a->src_dentry); au_set_dbwh(a->src_dentry, a->src_bwh); if (rerr) RevertFailure("unlink %.*s", AuDLNPair(a->src_wh_dentry)); }
/* * 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; }
static int au_cpdown_dir_wh(struct dentry *dentry, struct dentry *h_parent, struct inode *dir, aufs_bindex_t bdst) { int err; struct path h_path; struct au_branch *br; br = au_sbr(dentry->d_sb, bdst); h_path.dentry = au_wh_lkup(h_parent, &dentry->d_name, br); err = PTR_ERR(h_path.dentry); if (IS_ERR(h_path.dentry)) goto out; err = 0; if (h_path.dentry->d_inode) { h_path.mnt = br->br_mnt; err = au_wh_unlink_dentry(au_h_iptr(dir, bdst), &h_path, dentry); } dput(h_path.dentry); out: 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("%.*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; }
/* * 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 inode *inode, *h_dir; struct dentry *wh; struct au_ndx ndx; struct super_block *sb; LKTRTrace("wh %p, %.*s\n", wh_dentry, AuDLNPair(dentry)); sb = dentry->d_sb; 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); err = au_wh_unlink_dentry(au_hi(dir, bindex), wh_dentry, dentry, /*dlgt*/0); 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); /* or always cpup dir mtime? */ 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 */ ndx.flags = 0; if (au_test_dlgt(au_mntflags(sb))) au_fset_ndx(ndx.flags, DLGT); ndx.nfsmnt = au_nfsmnt(sb, bwh); ndx.nd = NULL; /* ndx.br = NULL; */ /* dir inode is locked */ wh = au_wh_create(dentry, bwh, wh_dentry->d_parent, &ndx); 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: AuTraceErr(err); return err; }
static int do_rename(struct au_ren_args *a) { int err; struct dentry *d, *h_d; /* prepare workqueue args for asynchronous rmdir */ h_d = a->dst_h_dentry; if (au_ftest_ren(a->flags, ISDIR) && h_d->d_inode) { err = -ENOMEM; a->thargs = au_whtmp_rmdir_alloc(a->src_dentry->d_sb, GFP_NOFS); if (unlikely(!a->thargs)) goto out; a->h_dst = dget(h_d); } /* create whiteout for src_dentry */ if (au_ftest_ren(a->flags, WHSRC)) { a->src_bwh = au_dbwh(a->src_dentry); AuDebugOn(a->src_bwh >= 0); a->src_wh_dentry = au_wh_create(a->src_dentry, a->btgt, a->src_h_parent); err = PTR_ERR(a->src_wh_dentry); if (IS_ERR(a->src_wh_dentry)) goto out_thargs; } /* lookup whiteout for dentry */ if (au_ftest_ren(a->flags, WHDST)) { h_d = au_wh_lkup(a->dst_h_parent, &a->dst_dentry->d_name, a->br); err = PTR_ERR(h_d); if (IS_ERR(h_d)) goto out_whsrc; if (!h_d->d_inode) dput(h_d); else a->dst_wh_dentry = h_d; } /* rename dentry to tmpwh */ if (a->thargs) { err = au_whtmp_ren(a->dst_h_dentry, a->br); if (unlikely(err)) goto out_whdst; d = a->dst_dentry; au_set_h_dptr(d, a->btgt, NULL); err = au_lkup_neg(d, a->btgt); if (unlikely(err)) goto out_whtmp; a->dst_h_dentry = au_h_dptr(d, a->btgt); } /* cpup src */ if (a->dst_h_dentry->d_inode && a->src_bstart != a->btgt) { struct mutex *h_mtx = &a->src_h_dentry->d_inode->i_mutex; struct file *h_file; mutex_lock_nested(h_mtx, AuLsc_I_CHILD); AuDebugOn(au_dbstart(a->src_dentry) != a->src_bstart); h_file = au_h_open_pre(a->src_dentry, a->src_bstart); if (IS_ERR(h_file)) { err = PTR_ERR(h_file); h_file = NULL; } else err = au_sio_cpup_simple(a->src_dentry, a->btgt, -1, !AuCpup_DTIME); mutex_unlock(h_mtx); au_h_open_post(a->src_dentry, a->src_bstart, h_file); if (unlikely(err)) goto out_whtmp; } /* rename by vfs_rename or cpup */ d = a->dst_dentry; if (au_ftest_ren(a->flags, ISDIR) && (a->dst_wh_dentry || au_dbdiropq(d) == a->btgt /* hide the lower to keep xino */ || a->btgt < au_dbend(d) || au_opt_test(au_mntflags(d->d_sb), ALWAYS_DIROPQ))) au_fset_ren(a->flags, DIROPQ); err = au_ren_or_cpup(a); if (unlikely(err)) /* leave the copied-up one */ goto out_whtmp; /* make dir opaque */ if (au_ftest_ren(a->flags, DIROPQ)) { err = au_ren_diropq(a); if (unlikely(err)) goto out_rename; } /* update target timestamps */ AuDebugOn(au_dbstart(a->src_dentry) != a->btgt); a->h_path.dentry = au_h_dptr(a->src_dentry, a->btgt); vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/ a->src_inode->i_ctime = a->h_path.dentry->d_inode->i_ctime; /* remove whiteout for dentry */ if (a->dst_wh_dentry) { a->h_path.dentry = a->dst_wh_dentry; err = au_wh_unlink_dentry(a->dst_h_dir, &a->h_path, a->dst_dentry); if (unlikely(err)) goto out_diropq; } /* remove whtmp */ if (a->thargs) au_ren_del_whtmp(a); /* ignore this error */ err = 0; goto out_success; out_diropq: if (au_ftest_ren(a->flags, DIROPQ)) au_ren_rev_diropq(err, a); out_rename: if (!au_ftest_ren(a->flags, CPUP)) au_ren_rev_rename(err, a); else au_ren_rev_cpup(err, a); dput(a->h_dst); out_whtmp: if (a->thargs) au_ren_rev_whtmp(err, a); out_whdst: dput(a->dst_wh_dentry); a->dst_wh_dentry = NULL; out_whsrc: if (a->src_wh_dentry) au_ren_rev_whsrc(err, a); out_success: dput(a->src_wh_dentry); dput(a->dst_wh_dentry); out_thargs: if (a->thargs) { dput(a->h_dst); au_whtmp_rmdir_free(a->thargs); a->thargs = NULL; } 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; }
/* * 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; }