static void au_br_do_del(struct super_block *sb, aufs_bindex_t bindex, struct au_branch *br) { aufs_bindex_t bend; struct au_sbinfo *sbinfo; struct dentry *root, *h_root; struct inode *inode, *h_inode; struct au_hinode *hinode; SiMustWriteLock(sb); root = sb->s_root; inode = root->d_inode; sbinfo = au_sbi(sb); bend = sbinfo->si_bend; h_root = au_h_dptr(root, bindex); hinode = au_hi(inode, bindex); h_inode = au_igrab(hinode->hi_inode); au_hiput(hinode); au_sbilist_lock(); au_br_do_del_brp(sbinfo, bindex, bend); au_br_do_del_hdp(au_di(root), bindex, bend); au_br_do_del_hip(au_ii(inode), bindex, bend); au_sbilist_unlock(); dput(h_root); iput(h_inode); au_br_do_free(br); }
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_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; }
static void au_br_do_add(struct super_block *sb, struct dentry *h_dentry, struct au_branch *br, aufs_bindex_t bindex) { struct dentry *root; struct inode *root_inode; aufs_bindex_t bend, amount; root = sb->s_root; root_inode = root->d_inode; bend = au_sbend(sb); amount = bend + 1 - bindex; au_br_do_add_brp(au_sbi(sb), bindex, br, bend, amount); au_br_do_add_hdp(au_di(root), bindex, bend, amount); au_br_do_add_hip(au_ii(root_inode), bindex, bend, amount); au_set_h_dptr(root, bindex, dget(h_dentry)); au_set_h_iptr(root_inode, bindex, au_igrab(h_dentry->d_inode), /*flags*/0); }
static int set_inode(struct inode *inode, struct dentry *dentry) { int err; unsigned int flags; umode_t mode; aufs_bindex_t bindex, bstart, btail; unsigned char isdir; struct dentry *h_dentry; struct inode *h_inode; struct au_iinfo *iinfo; IiMustWriteLock(inode); err = 0; isdir = 0; bstart = au_dbstart(dentry); h_inode = au_h_dptr(dentry, bstart)->d_inode; mode = h_inode->i_mode; switch (mode & S_IFMT) { case S_IFREG: btail = au_dbtail(dentry); inode->i_op = &aufs_iop; inode->i_fop = &aufs_file_fop; inode->i_mapping->a_ops = &aufs_aop; break; case S_IFDIR: isdir = 1; btail = au_dbtaildir(dentry); inode->i_op = &aufs_dir_iop; inode->i_fop = &aufs_dir_fop; break; case S_IFLNK: btail = au_dbtail(dentry); inode->i_op = &aufs_symlink_iop; break; case S_IFBLK: case S_IFCHR: case S_IFIFO: case S_IFSOCK: btail = au_dbtail(dentry); inode->i_op = &aufs_iop; au_init_special_fop(inode, mode, h_inode->i_rdev); break; default: AuIOErr("Unknown file type 0%o\n", mode); err = -EIO; goto out; } /* do not set inotify for whiteouted dirs (SHWH mode) */ flags = au_hi_flags(inode, isdir); if (au_opt_test(au_mntflags(dentry->d_sb), SHWH) && au_ftest_hi(flags, HINOTIFY) && dentry->d_name.len > AUFS_WH_PFX_LEN && !memcmp(dentry->d_name.name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) au_fclr_hi(flags, HINOTIFY); iinfo = au_ii(inode); iinfo->ii_bstart = bstart; iinfo->ii_bend = btail; for (bindex = bstart; bindex <= btail; bindex++) { h_dentry = au_h_dptr(dentry, bindex); if (h_dentry) au_set_h_iptr(inode, bindex, au_igrab(h_dentry->d_inode), flags); } au_cpup_attr_all(inode, /*force*/1); out: return err; }
int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry, struct inode *_dst_dir, struct dentry *_dst_dentry) { int err, flags; /* reduce stack space */ struct au_ren_args *a; AuDbg("%.*s, %.*s\n", AuDLNPair(_src_dentry), AuDLNPair(_dst_dentry)); IMustLock(_src_dir); IMustLock(_dst_dir); err = -ENOMEM; BUILD_BUG_ON(sizeof(*a) > PAGE_SIZE); a = kzalloc(sizeof(*a), GFP_NOFS); if (unlikely(!a)) goto out; a->src_dir = _src_dir; a->src_dentry = _src_dentry; a->src_inode = a->src_dentry->d_inode; a->src_parent = a->src_dentry->d_parent; /* dir inode is locked */ a->dst_dir = _dst_dir; a->dst_dentry = _dst_dentry; a->dst_inode = a->dst_dentry->d_inode; a->dst_parent = a->dst_dentry->d_parent; /* dir inode is locked */ if (a->dst_inode) { IMustLock(a->dst_inode); au_igrab(a->dst_inode); } err = -ENOTDIR; flags = AuLock_FLUSH | AuLock_NOPLM | AuLock_GEN; if (S_ISDIR(a->src_inode->i_mode)) { au_fset_ren(a->flags, ISDIR); if (unlikely(a->dst_inode && !S_ISDIR(a->dst_inode->i_mode))) goto out_free; err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, AuLock_DIR | flags); } else err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, flags); if (unlikely(err)) goto out_free; err = au_d_hashed_positive(a->src_dentry); if (unlikely(err)) goto out_unlock; err = -ENOENT; if (a->dst_inode) { /* * If it is a dir, VFS unhash dst_dentry before this * function. It means we cannot rely upon d_unhashed(). */ if (unlikely(!a->dst_inode->i_nlink)) goto out_unlock; if (!S_ISDIR(a->dst_inode->i_mode)) { err = au_d_hashed_positive(a->dst_dentry); if (unlikely(err)) goto out_unlock; } else if (unlikely(IS_DEADDIR(a->dst_inode))) goto out_unlock; } else if (unlikely(d_unhashed(a->dst_dentry))) goto out_unlock; au_fset_ren(a->flags, ISSAMEDIR); /* temporary */ di_write_lock_parent(a->dst_parent); /* which branch we process */ err = au_ren_wbr(a); if (unlikely(err < 0)) goto out_parent; a->br = au_sbr(a->dst_dentry->d_sb, a->btgt); a->h_path.mnt = a->br->br_mnt; /* are they available to be renamed */ err = au_ren_may_dir(a); if (unlikely(err)) goto out_children; /* prepare the writable parent dir on the same branch */ if (a->dst_bstart == a->btgt) { au_fset_ren(a->flags, WHDST); } else { err = au_cpup_dirs(a->dst_dentry, a->btgt); if (unlikely(err)) goto out_children; } if (a->src_dir != a->dst_dir) { /* * this temporary unlock is safe, * because both dir->i_mutex are locked. */ di_write_unlock(a->dst_parent); di_write_lock_parent(a->src_parent); err = au_wr_dir_need_wh(a->src_dentry, au_ftest_ren(a->flags, ISDIR), &a->btgt); di_write_unlock(a->src_parent); di_write_lock2_parent(a->src_parent, a->dst_parent, /*isdir*/1); au_fclr_ren(a->flags, ISSAMEDIR); } else err = au_wr_dir_need_wh(a->src_dentry, au_ftest_ren(a->flags, ISDIR), &a->btgt); if (unlikely(err < 0)) goto out_children; if (err) au_fset_ren(a->flags, WHSRC); /* lock them all */ err = au_ren_lock(a); if (unlikely(err)) goto out_children; if (!au_opt_test(au_mntflags(a->dst_dir->i_sb), UDBA_NONE)) err = au_may_ren(a); else if (unlikely(a->dst_dentry->d_name.len > AUFS_MAX_NAMELEN)) err = -ENAMETOOLONG; if (unlikely(err)) goto out_hdir; /* store timestamps to be revertible */ au_ren_dt(a); /* here we go */ err = do_rename(a); if (unlikely(err)) goto out_dt; /* update dir attributes */ au_ren_refresh_dir(a); /* dput/iput all lower dentries */ au_ren_refresh(a); goto out_hdir; /* success */ out_dt: au_ren_rev_dt(err, a); out_hdir: au_ren_unlock(a); out_children: au_nhash_wh_free(&a->whlist); if (err && a->dst_inode && a->dst_bstart != a->btgt) { AuDbg("bstart %d, btgt %d\n", a->dst_bstart, a->btgt); au_set_h_dptr(a->dst_dentry, a->btgt, NULL); au_set_dbstart(a->dst_dentry, a->dst_bstart); } out_parent: if (!err) d_move(a->src_dentry, a->dst_dentry); else { au_update_dbstart(a->dst_dentry); if (!a->dst_inode) d_drop(a->dst_dentry); } if (au_ftest_ren(a->flags, ISSAMEDIR)) di_write_unlock(a->dst_parent); else di_write_unlock2(a->src_parent, a->dst_parent); out_unlock: aufs_read_and_write_unlock2(a->dst_dentry, a->src_dentry); out_free: iput(a->dst_inode); if (a->thargs) au_whtmp_rmdir_free(a->thargs); kfree(a); out: AuTraceErr(err); return err; }
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; }
static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst, struct dentry *h_parent, void *arg) { int err, rerr; aufs_bindex_t bopq, bstart; struct path h_path; struct dentry *parent; struct inode *h_dir, *h_inode, *inode, *dir; struct au_cpdown_dir_args *args = arg; bstart = au_dbstart(dentry); /* dentry is di-locked */ parent = dget_parent(dentry); dir = parent->d_inode; h_dir = h_parent->d_inode; AuDebugOn(h_dir != au_h_iptr(dir, bdst)); IMustLock(h_dir); err = au_lkup_neg(dentry, bdst); if (unlikely(err < 0)) goto out; h_path.dentry = au_h_dptr(dentry, bdst); h_path.mnt = au_sbr_mnt(dentry->d_sb, bdst); err = vfsub_sio_mkdir(au_h_iptr(dir, bdst), &h_path, S_IRWXU | S_IRUGO | S_IXUGO); if (unlikely(err)) goto out_put; au_fset_cpdown(args->flags, MADE_DIR); bopq = au_dbdiropq(dentry); au_fclr_cpdown(args->flags, WHED); au_fclr_cpdown(args->flags, DIROPQ); if (au_dbwh(dentry) == bdst) au_fset_cpdown(args->flags, WHED); if (!au_ftest_cpdown(args->flags, PARENT_OPQ) && bopq <= bdst) au_fset_cpdown(args->flags, PARENT_OPQ); h_inode = h_path.dentry->d_inode; mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); if (au_ftest_cpdown(args->flags, WHED)) { err = au_cpdown_dir_opq(dentry, bdst, args); if (unlikely(err)) { mutex_unlock(&h_inode->i_mutex); goto out_dir; } } err = au_cpdown_attr(&h_path, au_h_dptr(dentry, bstart)); mutex_unlock(&h_inode->i_mutex); if (unlikely(err)) goto out_opq; if (au_ftest_cpdown(args->flags, WHED)) { err = au_cpdown_dir_wh(dentry, h_parent, dir, bdst); if (unlikely(err)) goto out_opq; } inode = dentry->d_inode; if (au_ibend(inode) < bdst) au_set_ibend(inode, bdst); au_set_h_iptr(inode, bdst, au_igrab(h_inode), au_hi_flags(inode, /*isdir*/1)); goto out; /* success */ /* revert */ out_opq: if (au_ftest_cpdown(args->flags, DIROPQ)) { mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); rerr = au_diropq_remove(dentry, bdst); mutex_unlock(&h_inode->i_mutex); if (unlikely(rerr)) { AuIOErr("failed removing diropq for %.*s b%d (%d)\n", AuDLNPair(dentry), bdst, rerr); err = -EIO; goto out; } } out_dir: if (au_ftest_cpdown(args->flags, MADE_DIR)) { rerr = vfsub_sio_rmdir(au_h_iptr(dir, bdst), &h_path); if (unlikely(rerr)) { AuIOErr("failed removing %.*s b%d (%d)\n", AuDLNPair(dentry), bdst, rerr); err = -EIO; } } out_put: au_set_h_dptr(dentry, bdst, NULL); if (au_dbend(dentry) == bdst) au_update_dbend(dentry); out: dput(parent); 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; }