/* * 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 { umode_t mode; bool want_excl; } c; struct { const char *symname; } s; struct { umode_t 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, arg->u.c.want_excl); 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, umode_t 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, umode_t mode, bool want_excl) { struct simple_arg arg = { .type = Creat, .u.c = { .mode = mode, .want_excl = want_excl } }; 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, -1, 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; }
int au_mvdown(struct dentry *dentry, struct aufs_mvdown __user *uarg) { int err, e; unsigned char dmsg; struct au_mvd_args *args; struct inode *inode; inode = d_inode(dentry); err = -EPERM; if (unlikely(!capable(CAP_SYS_ADMIN))) goto out; err = -ENOMEM; args = kmalloc(sizeof(*args), GFP_NOFS); if (unlikely(!args)) goto out; err = copy_from_user(&args->mvdown, uarg, sizeof(args->mvdown)); if (!err) err = !access_ok(VERIFY_WRITE, uarg, sizeof(*uarg)); if (unlikely(err)) { err = -EFAULT; AuTraceErr(err); goto out_free; } AuDbg("flags 0x%x\n", args->mvdown.flags); args->mvdown.flags &= ~(AUFS_MVDOWN_ROLOWER_R | AUFS_MVDOWN_ROUPPER_R); args->mvdown.au_errno = 0; args->dentry = dentry; args->inode = inode; args->sb = dentry->d_sb; err = -ENOENT; dmsg = !!(args->mvdown.flags & AUFS_MVDOWN_DMSG); args->parent = dget_parent(dentry); args->dir = d_inode(args->parent); inode_lock_nested(args->dir, I_MUTEX_PARENT); dput(args->parent); if (unlikely(args->parent != dentry->d_parent)) { AU_MVD_PR(dmsg, "parent dir is moved\n"); goto out_dir; } inode_lock_nested(inode, I_MUTEX_CHILD); err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_NOPLMW); if (unlikely(err)) goto out_inode; di_write_lock_parent(args->parent); err = au_mvd_args(dmsg, args); if (unlikely(err)) goto out_parent; err = au_do_mvdown(dmsg, args); if (unlikely(err)) goto out_parent; au_cpup_attr_timesizes(args->dir); au_cpup_attr_timesizes(inode); if (!(args->mvdown.flags & AUFS_MVDOWN_KUPPER)) au_cpup_igen(inode, au_h_iptr(inode, args->mvd_bdst)); /* au_digen_dec(dentry); */ out_parent: di_write_unlock(args->parent); aufs_read_unlock(dentry, AuLock_DW); out_inode: inode_unlock(inode); out_dir: inode_unlock(args->dir); out_free: e = copy_to_user(uarg, &args->mvdown, sizeof(args->mvdown)); if (unlikely(e)) err = -EFAULT; kfree(args); out: AuTraceErr(err); return err; }
static int au_ii_refresh(struct inode *inode, int *update) { int err, e; umode_t type; aufs_bindex_t bindex, new_bindex; struct super_block *sb; struct au_iinfo *iinfo; struct au_hinode *p, *q, tmp; IiMustWriteLock(inode); *update = 0; sb = inode->i_sb; type = inode->i_mode & S_IFMT; iinfo = au_ii(inode); err = au_ii_realloc(iinfo, au_sbend(sb) + 1); if (unlikely(err)) goto out; AuDebugOn(iinfo->ii_bstart < 0); p = iinfo->ii_hinode + iinfo->ii_bstart; for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; bindex++, p++) { if (!p->hi_inode) continue; AuDebugOn(type != (p->hi_inode->i_mode & S_IFMT)); new_bindex = au_br_index(sb, p->hi_id); if (new_bindex == bindex) continue; if (new_bindex < 0) { *update = 1; au_hiput(p); p->hi_inode = NULL; continue; } if (new_bindex < iinfo->ii_bstart) iinfo->ii_bstart = new_bindex; if (iinfo->ii_bend < new_bindex) iinfo->ii_bend = new_bindex; /* swap two lower inode, and loop again */ q = iinfo->ii_hinode + new_bindex; tmp = *q; *q = *p; *p = tmp; if (tmp.hi_inode) { bindex--; p--; } } au_update_ibrange(inode, /*do_put_zero*/0); e = au_dy_irefresh(inode); if (unlikely(e && !err)) err = e; out: AuTraceErr(err); return err; }
static int au_mvd_args_intermediate(const unsigned char dmsg, struct au_mvd_args *a) { int err; struct au_dinfo *dinfo, *tmp; /* lookup the next lower positive entry */ err = -ENOMEM; tmp = au_di_alloc(a->sb, AuLsc_DI_TMP); if (unlikely(!tmp)) goto out; a->bfound = -1; a->bwh = -1; dinfo = au_di(a->dentry); au_di_cp(tmp, dinfo); au_di_swap(tmp, dinfo); /* returns the number of positive dentries */ err = au_lkup_dentry(a->dentry, a->mvd_bsrc + 1, /* AuLkup_IGNORE_PERM */ 0); if (!err) a->bwh = au_dbwh(a->dentry); else if (err > 0) a->bfound = au_dbtop(a->dentry); au_di_swap(tmp, dinfo); au_rw_write_unlock(&tmp->di_rwsem); au_di_free(tmp); if (unlikely(err < 0)) AU_MVD_PR(dmsg, "failed look-up lower\n"); /* * here, we have these cases. * bfound == -1 * no positive dentry under bsrc. there are more sub-cases. * bwh < 0 * there no whiteout, we can safely move-down. * bwh <= bsrc * impossible * bsrc < bwh && bwh < bdst * there is a whiteout on RO branch. cannot proceed. * bwh == bdst * there is a whiteout on the RW target branch. it should * be removed. * bdst < bwh * there is a whiteout somewhere unrelated branch. * -1 < bfound && bfound <= bsrc * impossible. * bfound < bdst * found, but it is on RO branch between bsrc and bdst. cannot * proceed. * bfound == bdst * found, replace it if AUFS_MVDOWN_FORCE is set. otherwise return * error. * bdst < bfound * found, after we create the file on bdst, it will be hidden. */ AuDebugOn(a->bfound == -1 && a->bwh != -1 && a->bwh <= a->mvd_bsrc); AuDebugOn(-1 < a->bfound && a->bfound <= a->mvd_bsrc); err = -EINVAL; if (a->bfound == -1 && a->mvd_bsrc < a->bwh && a->bwh != -1 && a->bwh < a->mvd_bdst) { a->mvd_errno = EAU_MVDOWN_WHITEOUT; AU_MVD_PR(dmsg, "bsrc %d, bdst %d, bfound %d, bwh %d\n", a->mvd_bsrc, a->mvd_bdst, a->bfound, a->bwh); goto out; } else if (a->bfound != -1 && a->bfound < a->mvd_bdst) { a->mvd_errno = EAU_MVDOWN_UPPER; AU_MVD_PR(dmsg, "bdst %d, bfound %d\n", a->mvd_bdst, a->bfound); goto out; } err = 0; /* success */ out: AuTraceErr(err); return err; }
static int au_mvd_args(const unsigned char dmsg, struct au_mvd_args *a) { int err; struct au_branch *br; err = -EISDIR; if (unlikely(S_ISDIR(a->inode->i_mode))) goto out; err = -EINVAL; if (!(a->mvdown.flags & AUFS_MVDOWN_BRID_UPPER)) a->mvd_bsrc = au_ibtop(a->inode); else { a->mvd_bsrc = au_br_index(a->sb, a->mvd_src_brid); if (unlikely(a->mvd_bsrc < 0 || (a->mvd_bsrc < au_dbtop(a->dentry) || au_dbbot(a->dentry) < a->mvd_bsrc || !au_h_dptr(a->dentry, a->mvd_bsrc)) || (a->mvd_bsrc < au_ibtop(a->inode) || au_ibbot(a->inode) < a->mvd_bsrc || !au_h_iptr(a->inode, a->mvd_bsrc)))) { a->mvd_errno = EAU_MVDOWN_NOUPPER; AU_MVD_PR(dmsg, "no upper\n"); goto out; } } if (unlikely(a->mvd_bsrc == au_sbbot(a->sb))) { a->mvd_errno = EAU_MVDOWN_BOTTOM; AU_MVD_PR(dmsg, "on the bottom\n"); goto out; } a->mvd_h_src_inode = au_h_iptr(a->inode, a->mvd_bsrc); br = au_sbr(a->sb, a->mvd_bsrc); err = au_br_rdonly(br); if (!(a->mvdown.flags & AUFS_MVDOWN_ROUPPER)) { if (unlikely(err)) goto out; } else if (!(vfsub_native_ro(a->mvd_h_src_inode) || IS_APPEND(a->mvd_h_src_inode))) { if (err) a->mvdown.flags |= AUFS_MVDOWN_ROUPPER_R; /* go on */ } else goto out; err = -EINVAL; if (!(a->mvdown.flags & AUFS_MVDOWN_BRID_LOWER)) { a->mvd_bdst = find_lower_writable(a); if (unlikely(a->mvd_bdst < 0)) { a->mvd_errno = EAU_MVDOWN_BOTTOM; AU_MVD_PR(dmsg, "no writable lower branch\n"); goto out; } } else { a->mvd_bdst = au_br_index(a->sb, a->mvd_dst_brid); if (unlikely(a->mvd_bdst < 0 || au_sbbot(a->sb) < a->mvd_bdst)) { a->mvd_errno = EAU_MVDOWN_NOLOWERBR; AU_MVD_PR(dmsg, "no lower brid\n"); goto out; } } err = au_mvd_args_busy(dmsg, a); if (!err) err = au_mvd_args_parent(dmsg, a); if (!err) err = au_mvd_args_intermediate(dmsg, a); if (!err) err = au_mvd_args_exist(dmsg, a); if (!err) AuDbg("b%d, b%d\n", a->mvd_bsrc, a->mvd_bdst); out: AuTraceErr(err); return err; }
int au_refresh_dentry(struct dentry *dentry, struct dentry *parent) { int err, ebrange; unsigned int sigen; struct au_dinfo *dinfo, *tmp; struct super_block *sb; struct inode *inode; DiMustWriteLock(dentry); AuDebugOn(IS_ROOT(dentry)); AuDebugOn(!parent->d_inode); sb = dentry->d_sb; inode = dentry->d_inode; sigen = au_sigen(sb); err = au_digen_test(parent, sigen); if (unlikely(err)) goto out; dinfo = au_di(dentry); err = au_di_realloc(dinfo, au_sbend(sb) + 1); if (unlikely(err)) goto out; ebrange = au_dbrange_test(dentry); if (!ebrange) ebrange = au_do_refresh_hdentry(dentry, parent); if (d_unhashed(dentry) || ebrange) { AuDebugOn(au_dbstart(dentry) < 0 && au_dbend(dentry) >= 0); if (inode) err = au_refresh_hinode_self(inode); au_dbg_verify_dinode(dentry); if (!err) goto out_dgen; /* success */ goto out; } /* temporary dinfo */ AuDbgDentry(dentry); err = -ENOMEM; tmp = au_di_alloc(sb, AuLsc_DI_TMP); if (unlikely(!tmp)) goto out; au_di_swap(tmp, dinfo); /* returns the number of positive dentries */ /* * if current working dir is removed, it returns an error. * but the dentry is legal. */ err = au_lkup_dentry(dentry, /*bstart*/0, /*type*/0); AuDbgDentry(dentry); au_di_swap(tmp, dinfo); if (err == -ENOENT) err = 0; if (err >= 0) { /* compare/refresh by dinfo */ AuDbgDentry(dentry); err = au_refresh_by_dinfo(dentry, dinfo, tmp); au_dbg_verify_dinode(dentry); AuTraceErr(err); } au_rw_write_unlock(&tmp->di_rwsem); au_di_free(tmp); if (unlikely(err)) goto out; out_dgen: au_update_digen(dentry); out: if (unlikely(err && !(dentry->d_flags & DCACHE_NFSFS_RENAMED))) { AuIOErr("failed refreshing %pd, %d\n", dentry, err); AuDbgDentry(dentry); } AuTraceErr(err); return err; }
/* copy-down the file */ static int au_do_cpdown(const unsigned char dmsg, struct au_mvd_args *a) { int err; struct au_cp_generic cpg = { .dentry = a->dentry, .bdst = a->mvd_bdst, .bsrc = a->mvd_bsrc, .len = -1, .pin = &a->mvd_pin_dst, .flags = AuCpup_DTIME | AuCpup_HOPEN }; AuDbg("b%d, b%d\n", cpg.bsrc, cpg.bdst); if (a->mvdown.flags & AUFS_MVDOWN_OWLOWER) au_fset_cpup(cpg.flags, OVERWRITE); if (a->mvdown.flags & AUFS_MVDOWN_ROLOWER) au_fset_cpup(cpg.flags, RWDST); err = au_sio_cpdown_simple(&cpg); if (unlikely(err)) AU_MVD_PR(dmsg, "cpdown failed\n"); AuTraceErr(err); return err; } /* * unlink the whiteout on bdst if exist which may be created by UDBA while we * were sleeping */ static int au_do_unlink_wh(const unsigned char dmsg, struct au_mvd_args *a) { int err; struct path h_path; struct au_branch *br; struct inode *delegated; br = au_sbr(a->sb, a->mvd_bdst); h_path.dentry = au_wh_lkup(a->mvd_h_dst_parent, &a->dentry->d_name, br); err = PTR_ERR(h_path.dentry); if (IS_ERR(h_path.dentry)) { AU_MVD_PR(dmsg, "wh_lkup failed\n"); goto out; } err = 0; if (d_is_positive(h_path.dentry)) { h_path.mnt = au_br_mnt(br); delegated = NULL; err = vfsub_unlink(d_inode(a->mvd_h_dst_parent), &h_path, &delegated, /*force*/0); if (unlikely(err == -EWOULDBLOCK)) { pr_warn("cannot retry for NFSv4 delegation" " for an internal unlink\n"); iput(delegated); } if (unlikely(err)) AU_MVD_PR(dmsg, "wh_unlink failed\n"); } dput(h_path.dentry); out: AuTraceErr(err); 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; /* * is it possible? * yes, it happend (in linux-3.3-rcN) but I don't know why. * there may exist a problem somewhere else. */ err = -EINVAL; if (unlikely(a->dst_parent->d_inode == a->src_dentry->d_inode)) 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; }
/* * 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; valid = 1; sb = dentry->d_sb; inode = dentry->d_inode; /* * todo: very ugly * i_mutex of parent dir may be held, * but we should not return 'invalid' due to busy. */ err = aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW | AuLock_NOPLM); if (unlikely(err)) { valid = err; goto out; } 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; } if (inode && au_iigen(inode) != sigen) { AuDebugOn(IS_ROOT(dentry)); err = au_refresh_hinode(inode, dentry); if (unlikely(err)) goto out_dgrade; } di_downgrade_lock(dentry, AuLock_IR); 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_inval; } 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_inval; out_dgrade: di_downgrade_lock(dentry, AuLock_IR); out_inval: aufs_read_unlock(dentry, AuLock_IR); AuTraceErr(err); valid = !err; out: if (!valid) AuDbg("%.*s invalid, %d\n", AuDLNPair(dentry), valid); return valid; }
static struct dentry * aufs_decode_fh(struct super_block *sb, __u32 *fh, int fh_len, int fh_type, int (*acceptable)(void *context, struct dentry *de), void *context) { struct dentry *dentry; ino_t ino, dir_ino; aufs_bindex_t bindex; struct au_nfsd_si_lock nsi_lock = { .sigen = fh[Fh_sigen], .br_id = fh[Fh_br_id], .force_lock = 0 }; LKTRTrace("%d, fh{br_id %u, sigen %u, i%u, diri%u, g%u}\n", fh_type, fh[Fh_br_id], fh[Fh_sigen], fh[Fh_ino], fh[Fh_dir_ino], fh[Fh_igen]); AuDebugOn(fh_len < Fh_tail); dentry = ERR_PTR(-ESTALE); /* branch id may be wrapped around */ bindex = si_nfsd_read_lock(sb, &nsi_lock); if (unlikely(bindex < 0)) goto out; nsi_lock.force_lock = 1; /* is this inode still cached? */ ino = decode_ino(fh + Fh_ino); AuDebugOn(ino == AUFS_ROOT_INO); dir_ino = decode_ino(fh + Fh_dir_ino); dentry = decode_by_ino(sb, ino, dir_ino); if (IS_ERR(dentry)) goto out_unlock; if (dentry) goto accept; /* is the parent dir cached? */ dentry = decode_by_dir_ino(sb, ino, dir_ino, &nsi_lock); if (IS_ERR(dentry)) goto out_unlock; if (dentry) goto accept; /* lookup path */ dentry = decode_by_path(sb, bindex, ino, fh, fh_len, &nsi_lock); if (IS_ERR(dentry)) goto out_unlock; if (unlikely(!dentry)) goto out_unlock; accept: LKTRLabel(accept); if (dentry->d_inode->i_generation == fh[Fh_igen] && acceptable(context, dentry)) goto out_unlock; /* success */ LKTRLabel(stale); dput(dentry); dentry = ERR_PTR(-ESTALE); out_unlock: LKTRLabel(out_unlock); si_read_unlock(sb); out: LKTRLabel(out); if (0 && IS_ERR(dentry)) dentry = ERR_PTR(-ESTALE); AuTraceErrPtr(dentry); return dentry; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) static struct dentry * aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, int fh_type) { return aufs_decode_fh(sb, fid->raw, fh_len, fh_type, h_acceptable, /*context*/NULL); } #endif /* KERNEL_VERSION */ /* ---------------------------------------------------------------------- */ static int aufs_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len, int connectable) { int err; aufs_bindex_t bindex, bend; struct super_block *sb, *h_sb; struct inode *inode; struct dentry *parent, *h_parent; struct au_branch *br; LKTRTrace("%.*s, max %d, conn %d\n", AuDLNPair(dentry), *max_len, connectable); AuDebugOn(au_test_anon(dentry)); parent = NULL; err = -ENOSPC; if (unlikely(*max_len <= Fh_tail)) { AuWarn1("NFSv2 client (max_len %d)?\n", *max_len); goto out; } err = 0; //FILEID_ROOT; if (IS_ROOT(dentry)) { AuDebugOn(dentry->d_inode->i_ino != AUFS_ROOT_INO); goto out; } err = -EIO; h_parent = NULL; sb = dentry->d_sb; aufs_read_lock(dentry, AuLock_FLUSH | AuLock_IR); parent = dget_parent(dentry); di_read_lock_parent(parent, !AuLock_IR); inode = dentry->d_inode; AuDebugOn(!inode); #ifdef CONFIG_AUFS_DEBUG { unsigned int mnt_flags = au_mntflags(sb); if (unlikely(!au_opt_test_xino(mnt_flags))) AuWarn1("NFS-exporting requires xino\n"); if (unlikely(0 && !au_opt_test(mnt_flags, UDBA_INOTIFY))) AuWarn1("udba=inotify is recommended " "for NFS-exporting\n"); } #endif bend = au_dbtaildir(parent); for (bindex = au_dbstart(parent); bindex <= bend; bindex++) { h_parent = au_h_dptr(parent, bindex); if (h_parent) { dget(h_parent); break; } } if (unlikely(!h_parent)) goto out_unlock; LKTRTrace("b%d\n", bindex); err = -EPERM; br = au_sbr(sb, bindex); h_sb = br->br_mnt->mnt_sb; if (unlikely(!h_sb->s_export_op)) { AuErr1("%s branch is not exportable\n", au_sbtype(h_sb)); goto out_dput; } fh[Fh_br_id] = br->br_id; fh[Fh_sigen] = au_sigen(sb); encode_ino(fh + Fh_ino, inode->i_ino); encode_ino(fh + Fh_dir_ino, parent->d_inode->i_ino); fh[Fh_igen] = inode->i_generation; #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) /* it should be set at exporting time */ if (unlikely(!h_sb->s_export_op->find_exported_dentry)) { AuWarn("set default find_exported_dentry for %s\n", au_sbtype(h_sb)); h_sb->s_export_op->find_exported_dentry = find_exported_dentry; } #endif *max_len -= Fh_tail; fh[Fh_h_type] = au_call_encode_fh(h_parent, fh + Fh_tail, max_len, /*connectable or subtreecheck*/0); err = fh[Fh_h_type]; *max_len += Fh_tail; /* todo: macros? */ if (err != 255) err = 99; else AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb)); out_dput: dput(h_parent); out_unlock: di_read_unlock(parent, !AuLock_IR); dput(parent); aufs_read_unlock(dentry, AuLock_IR); out: AuTraceErr(err); if (unlikely(err < 0)) err = 255; return err; }
static int h_permission(struct inode *h_inode, int mask, struct path *h_path, int brperm) { int err; const unsigned char write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); err = -EPERM; if (write_mask && IS_IMMUTABLE(h_inode)) goto out; err = -EACCES; if (((mask & MAY_EXEC) && S_ISREG(h_inode->i_mode) && (path_noexec(h_path) || !(h_inode->i_mode & S_IXUGO)))) goto out; /* * - skip the lower fs test in the case of write to ro branch. * - nfs dir permission write check is optimized, but a policy for * link/rename requires a real check. * - nfs always sets SB_POSIXACL regardless its mount option 'noacl.' * in this case, generic_permission() returns -EOPNOTSUPP. */ if ((write_mask && !au_br_writable(brperm)) || (au_test_nfs(h_inode->i_sb) && S_ISDIR(h_inode->i_mode) && write_mask && !(mask & MAY_READ)) || !h_inode->i_op->permission) { /* AuLabel(generic_permission); */ /* AuDbg("get_acl %pf\n", h_inode->i_op->get_acl); */ err = generic_permission(h_inode, mask); if (err == -EOPNOTSUPP && au_test_nfs_noacl(h_inode)) err = h_inode->i_op->permission(h_inode, mask); AuTraceErr(err); } else { /* AuLabel(h_inode->permission); */ err = h_inode->i_op->permission(h_inode, mask); AuTraceErr(err); } if (!err) err = devcgroup_inode_permission(h_inode, mask); if (!err) err = security_inode_permission(h_inode, mask); #if 0 if (!err) { /* todo: do we need to call ima_path_check()? */ struct path h_path = { .dentry = .mnt = h_mnt }; err = ima_path_check(&h_path, mask & (MAY_READ | MAY_WRITE | MAY_EXEC), IMA_COUNT_LEAVE); } #endif 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; }
/* * if valid returns 1, otherwise 0. */ static int aufs_d_revalidate(struct dentry *dentry, unsigned int flags) { int valid, err; unsigned int sigen; unsigned char do_udba; struct super_block *sb; struct inode *inode; /* todo: support rcu-walk? */ if (flags & LOOKUP_RCU) return -ECHILD; valid = 0; if (unlikely(!au_di(dentry))) goto out; inode = dentry->d_inode; if (inode && is_bad_inode(inode)) goto out; valid = 1; sb = dentry->d_sb; /* * todo: very ugly * i_mutex of parent dir may be held, * but we should not return 'invalid' due to busy. */ err = aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW | AuLock_NOPLM); if (unlikely(err)) { valid = err; AuTraceErr(err); goto out; } if (unlikely(au_dbrange_test(dentry))) { err = -EINVAL; AuTraceErr(err); goto out_dgrade; } sigen = au_sigen(sb); if (au_digen_test(dentry, sigen)) { AuDebugOn(IS_ROOT(dentry)); err = au_reval_dpath(dentry, sigen); if (unlikely(err)) { AuTraceErr(err); goto out_dgrade; } } di_downgrade_lock(dentry, AuLock_IR); err = -EINVAL; if (!(flags & LOOKUP_OPEN) && inode && (IS_DEADDIR(inode) || !inode->i_nlink)) goto out_inval; do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE); if (do_udba && inode) { aufs_bindex_t bstart = au_ibstart(inode); struct inode *h_inode; if (bstart >= 0) { h_inode = au_h_iptr(inode, bstart); if (h_inode && au_test_higen(inode, h_inode)) goto out_inval; } } err = h_d_revalidate(dentry, inode, flags, do_udba); if (unlikely(!err && do_udba && au_dbstart(dentry) < 0)) { err = -EIO; AuDbg("both of real entry and whiteout found, %p, err %d\n", dentry, err); } goto out_inval; out_dgrade: di_downgrade_lock(dentry, AuLock_IR); out_inval: aufs_read_unlock(dentry, AuLock_IR); AuTraceErr(err); valid = !err; out: if (!valid) { AuDbg("%pd invalid, %d\n", dentry, valid); d_drop(dentry); } return valid; }
/* todo: remove this */ static int h_d_revalidate(struct dentry *dentry, struct inode *inode, unsigned int flags, int do_udba) { int err; umode_t mode, h_mode; aufs_bindex_t bindex, btail, bstart, ibs, ibe; unsigned char plus, unhashed, is_root, h_plus, h_nfs; struct inode *h_inode, *h_cached_inode; struct dentry *h_dentry; struct qstr *name, *h_name; err = 0; plus = 0; mode = 0; ibs = -1; ibe = -1; unhashed = !!d_unhashed(dentry); is_root = !!IS_ROOT(dentry); name = &dentry->d_name; /* * Theoretically, REVAL test should be unnecessary in case of * {FS,I}NOTIFY. * But {fs,i}notify doesn't fire some necessary events, * IN_ATTRIB for atime/nlink/pageio * IN_DELETE for NFS dentry * Let's do REVAL test too. */ if (do_udba && inode) { mode = (inode->i_mode & S_IFMT); plus = (inode->i_nlink > 0); ibs = au_ibstart(inode); ibe = au_ibend(inode); } bstart = au_dbstart(dentry); btail = bstart; if (inode && S_ISDIR(inode->i_mode)) btail = au_dbtaildir(dentry); for (bindex = bstart; bindex <= btail; bindex++) { h_dentry = au_h_dptr(dentry, bindex); if (!h_dentry) continue; AuDbg("b%d, %pd\n", bindex, h_dentry); h_nfs = !!au_test_nfs(h_dentry->d_sb); spin_lock(&h_dentry->d_lock); h_name = &h_dentry->d_name; if (unlikely(do_udba && !is_root && ((!h_nfs && (unhashed != !!d_unhashed(h_dentry) || name->len != h_name->len || memcmp(name->name, h_name->name, name->len))) || (h_nfs && !(flags & LOOKUP_OPEN) && (h_dentry->d_flags & DCACHE_NFSFS_RENAMED))) )) { AuDbg("unhash 0x%x 0x%x, %pd %pd\n", unhashed, d_unhashed(h_dentry), dentry, h_dentry); spin_unlock(&h_dentry->d_lock); goto err; } spin_unlock(&h_dentry->d_lock); err = au_do_h_d_reval(h_dentry, flags, dentry, bindex); if (unlikely(err)) /* do not goto err, to keep the errno */ break; /* todo: plink too? */ if (!do_udba) continue; /* UDBA tests */ h_inode = h_dentry->d_inode; if (unlikely(!!inode != !!h_inode)) goto err; h_plus = plus; h_mode = mode; h_cached_inode = h_inode; if (h_inode) { h_mode = (h_inode->i_mode & S_IFMT); h_plus = (h_inode->i_nlink > 0); } if (inode && ibs <= bindex && bindex <= ibe) h_cached_inode = au_h_iptr(inode, bindex); if (!h_nfs) { if (unlikely(plus != h_plus)) goto err; } else { if (unlikely(!(h_dentry->d_flags & DCACHE_NFSFS_RENAMED) && !is_root && !IS_ROOT(h_dentry) && unhashed != d_unhashed(h_dentry))) goto err; } if (unlikely(mode != h_mode || h_cached_inode != h_inode)) goto err; continue; err: err = -EINVAL; break; } AuTraceErr(err); 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; /* VFS already unhashes it */ inode = dentry->d_inode; err = -ENOENT; if (unlikely(!inode || !inode->i_nlink || IS_DEADDIR(inode))) goto out_unlock; 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; }
/* 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 __init aufs_init(void) { int err, i; char *p; au_debug_init(); #ifdef CONFIG_AUFS_INO_T_64 BUILD_BUG_ON(sizeof(ino_t) != sizeof(long long)); #else BUILD_BUG_ON(sizeof(ino_t) != sizeof(int)); #endif p = au_esc_chars; for (i = 1; i <= ' '; i++) *p++ = i; *p++ = '\\'; *p++ = '\x7f'; *p = 0; au_dir_roflags = au_file_roflags(O_DIRECTORY | O_LARGEFILE); err = -EINVAL; if (unlikely(aufs_nwkq <= 0)) goto out; err = sysaufs_init(); if (unlikely(err)) goto out; err = au_wkq_init(); if (unlikely(err)) goto out_sysaufs; err = au_inotify_init(); if (unlikely(err)) goto out_wkq; err = au_sysrq_init(); if (unlikely(err)) goto out_inotify; err = create_cache(); if (unlikely(err)) goto out_sysrq; err = register_filesystem(&aufs_fs_type); if (unlikely(err)) goto out_cache; pr_info(AUFS_NAME " " AUFS_VERSION "\n"); return 0; /* success */ out_cache: destroy_cache(); out_sysrq: au_sysrq_fin(); out_inotify: au_inotify_fin(); out_wkq: au_wkq_fin(); out_sysaufs: sysaufs_fin(); out: AuTraceErr(err); return err; }
static int au_wbr_fd(struct path *path, struct aufs_wbr_fd __user *arg) { int err, fd; aufs_bindex_t wbi, bindex, bend; struct file *h_file; struct super_block *sb; struct dentry *root; struct au_branch *br; struct aufs_wbr_fd wbrfd = { .oflags = au_dir_roflags, .brid = -1 }; const int valid = O_RDONLY | O_NONBLOCK | O_LARGEFILE | O_DIRECTORY | O_NOATIME | O_CLOEXEC; AuDebugOn(wbrfd.oflags & ~valid); if (arg) { err = copy_from_user(&wbrfd, arg, sizeof(wbrfd)); if (unlikely(err)) { err = -EFAULT; goto out; } err = -EINVAL; AuDbg("wbrfd{0%o, %d}\n", wbrfd.oflags, wbrfd.brid); wbrfd.oflags |= au_dir_roflags; AuDbg("0%o\n", wbrfd.oflags); if (unlikely(wbrfd.oflags & ~valid)) goto out; } fd = get_unused_fd(); err = fd; if (unlikely(fd < 0)) goto out; h_file = ERR_PTR(-EINVAL); wbi = 0; br = NULL; sb = path->dentry->d_sb; root = sb->s_root; aufs_read_lock(root, AuLock_IR); bend = au_sbend(sb); if (wbrfd.brid >= 0) { wbi = au_br_index(sb, wbrfd.brid); if (unlikely(wbi < 0 || wbi > bend)) goto out_unlock; } h_file = ERR_PTR(-ENOENT); br = au_sbr(sb, wbi); if (!au_br_writable(br->br_perm)) { if (arg) goto out_unlock; bindex = wbi + 1; wbi = -1; for (; bindex <= bend; bindex++) { br = au_sbr(sb, bindex); if (au_br_writable(br->br_perm)) { wbi = bindex; br = au_sbr(sb, wbi); break; } } } AuDbg("wbi %d\n", wbi); if (wbi >= 0) h_file = au_h_open(root, wbi, wbrfd.oflags, NULL, /*force_wr*/0); out_unlock: aufs_read_unlock(root, AuLock_IR); err = PTR_ERR(h_file); if (IS_ERR(h_file)) goto out_fd; atomic_dec(&br->br_count); /* cf. au_h_open() */ fd_install(fd, h_file); err = fd; goto out; /* success */ out_fd: put_unused_fd(fd); out: AuTraceErr(err); return err; } /* ---------------------------------------------------------------------- */ long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg) { long err; struct dentry *dentry; switch (cmd) { case AUFS_CTL_RDU: case AUFS_CTL_RDU_INO: err = au_rdu_ioctl(file, cmd, arg); break; case AUFS_CTL_WBR_FD: err = au_wbr_fd(&file->f_path, (void __user *)arg); break; case AUFS_CTL_IBUSY: err = au_ibusy_ioctl(file, arg); break; case AUFS_CTL_BRINFO: err = au_brinfo_ioctl(file, arg); break; case AUFS_CTL_FHSM_FD: dentry = file->f_dentry; if (IS_ROOT(dentry)) err = au_fhsm_fd(dentry->d_sb, arg); else err = -ENOTTY; break; default: /* do not call the lower */ AuDbg("0x%x\n", cmd); err = -ENOTTY; } AuTraceErr(err); return err; }