void au_fhsm_wrote(struct super_block *sb, aufs_bindex_t bindex, int force) { int err; struct au_sbinfo *sbinfo; struct au_fhsm *fhsm; struct au_branch *br; struct au_br_fhsm *bf; AuDbg("b%d, force %d\n", bindex, force); SiMustAnyLock(sb); sbinfo = au_sbi(sb); fhsm = &sbinfo->si_fhsm; if (!au_ftest_si(sbinfo, FHSM) || fhsm->fhsm_bottom == bindex) return; br = au_sbr(sb, bindex); bf = br->br_fhsm; AuDebugOn(!bf); mutex_lock(&bf->bf_lock); if (force || au_fhsm_pid(fhsm) || au_fhsm_test_jiffy(sbinfo, br)) err = au_fhsm_stfs(sb, bindex, /*rstfs*/NULL, /*do_lock*/0, /*do_notify*/1); mutex_unlock(&bf->bf_lock); }
struct aufs_branch *stobr(struct super_block *sb, aufs_bindex_t bindex) { SiMustAnyLock(sb); AuDebugOn(bindex < 0 || sbend(sb) < bindex || !stosi(sb)->si_branch[0 + bindex]); return stosi(sb)->si_branch[0 + bindex]; }
/* cf. aufs_rmdir() */ static int au_ren_del_whtmp(struct au_ren_args *a) { int err; struct inode *dir; dir = a->dst_dir; SiMustAnyLock(dir->i_sb); if (!au_nhash_test_longer_wh(&a->whlist, a->btgt, au_sbi(dir->i_sb)->si_dirwh) || au_test_fs_remote(a->h_dst->d_sb)) { err = au_whtmp_rmdir(dir, a->btgt, a->h_dst, &a->whlist); if (unlikely(err)) pr_warning("failed removing whtmp dir %.*s (%d), " "ignored.\n", AuDLNPair(a->h_dst), err); } else { au_nhash_wh_free(&a->thargs->whlist); a->thargs->whlist = a->whlist; a->whlist.nh_num = 0; au_whtmp_kick_rmdir(dir, a->btgt, a->h_dst, a->thargs); dput(a->h_dst); a->thargs = NULL; } return 0; }
void di_downgrade_lock(struct dentry *d, int flags) { SiMustAnyLock(d->d_sb); au_rw_dgrade_lock(&au_di(d)->di_rwsem); if (d->d_inode && au_ftest_lock(flags, IR)) ii_downgrade_lock(d->d_inode); }
/* * during a user process maintains the pseudo-links, * prohibit adding a new plink and branch manipulation. */ void au_plink_block_maintain(struct super_block *sb) { struct au_sbinfo *sbi = au_sbi(sb); SiMustAnyLock(sb); /* gave up wake_up_bit() */ wait_event(sbi->si_plink_wq, !au_ftest_si(sbi, MAINTAIN_PLINK)); }
/* * during a user process maintains the pseudo-links, * prohibit adding a new plink and branch manipulation. */ void au_plink_maint_block(struct super_block *sb) { struct au_sbinfo *sbi = au_sbi(sb); SiMustAnyLock(sb); /* gave up wake_up_bit() */ wait_event(sbi->si_plink_wq, !sbi->si_plink_maint); }
void di_write_unlock(struct dentry *d) { LKTRTrace("%.*s\n", AuDLNPair(d)); SiMustAnyLock(d->d_sb); if (d->d_inode) ii_write_unlock(d->d_inode); au_rw_write_unlock(&au_di(d)->di_rwsem); au_dbg_locked_di_unreg(d, AuLock_IW); }
static aufs_bindex_t au_fhsm_bottom(struct super_block *sb) { struct au_sbinfo *sbinfo; struct au_fhsm *fhsm; SiMustAnyLock(sb); sbinfo = au_sbi(sb); fhsm = &sbinfo->si_fhsm; AuDebugOn(!fhsm); return fhsm->fhsm_bottom; }
int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb) { int err; SiMustAnyLock(sb); err = 0; if (au_opt_test(au_mntflags(sb), XINO)) { err = au_xino_path(seq, au_sbi(sb)->si_xib); seq_putc(seq, '\n'); } return err; }
void di_write_lock(struct dentry *d, unsigned int lsc) { LKTRTrace("%.*s, %u\n", AuDLNPair(d), lsc); SiMustAnyLock(d->d_sb); /* todo: always nested? */ au_dbg_locking_di_reg(d, AuLock_IW, lsc); au_rw_write_lock_nested(&au_di(d)->di_rwsem, lsc); au_dbg_locking_di_unreg(d, AuLock_IW); au_dbg_locked_di_reg(d, AuLock_IW, lsc); if (d->d_inode) do_ii_write_lock(d->d_inode, lsc); }
int au_xigen_new(struct inode *inode) { int err; loff_t pos; ssize_t sz; struct super_block *sb; struct au_sbinfo *sbinfo; struct file *file; err = 0; /* todo: dirty, at mount time */ if (inode->i_ino == AUFS_ROOT_INO) goto out; sb = inode->i_sb; SiMustAnyLock(sb); if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) goto out; err = -EFBIG; pos = inode->i_ino; if (unlikely(au_loff_max / sizeof(inode->i_generation) - 1 < pos)) { AuIOErr1("too large i%lld\n", pos); goto out; } pos *= sizeof(inode->i_generation); err = 0; sbinfo = au_sbi(sb); file = sbinfo->si_xigen; BUG_ON(!file); if (i_size_read(file->f_dentry->d_inode) < pos + sizeof(inode->i_generation)) { inode->i_generation = atomic_inc_return(&sbinfo->si_xigen_next); sz = xino_fwrite(sbinfo->si_xwrite, file, &inode->i_generation, sizeof(inode->i_generation), &pos); } else sz = xino_fread(sbinfo->si_xread, file, &inode->i_generation, sizeof(inode->i_generation), &pos); if (sz == sizeof(inode->i_generation)) goto out; /* success */ err = sz; if (unlikely(sz >= 0)) { err = -EIO; AuIOErr("xigen error (%zd)\n", sz); } out: return err; }
void di_read_unlock(struct dentry *d, int flags) { LKTRTrace("%.*s\n", AuDLNPair(d)); SiMustAnyLock(d->d_sb); if (d->d_inode) { if (au_ftest_lock(flags, IW)) ii_write_unlock(d->d_inode); else if (au_ftest_lock(flags, IR)) ii_read_unlock(d->d_inode); } au_rw_read_unlock(&au_di(d)->di_rwsem); au_dbg_locked_di_unreg(d, flags); }
static void au_fhsm_notify(struct super_block *sb, int val) { struct au_sbinfo *sbinfo; struct au_fhsm *fhsm; SiMustAnyLock(sb); sbinfo = au_sbi(sb); fhsm = &sbinfo->si_fhsm; if (au_fhsm_pid(fhsm) && atomic_read(&fhsm->fhsm_readable) != -1) { atomic_set(&fhsm->fhsm_readable, val); if (val) wake_up(&fhsm->fhsm_wqh); } }
void di_read_lock(struct dentry *d, int flags, unsigned int lsc) { LKTRTrace("%.*s, %u\n", AuDLNPair(d), lsc); SiMustAnyLock(d->d_sb); /* todo: always nested? */ au_dbg_locking_di_reg(d, flags, lsc); au_rw_read_lock_nested(&au_di(d)->di_rwsem, lsc); au_dbg_locking_di_unreg(d, flags); au_dbg_locked_di_reg(d, flags, lsc); if (d->d_inode) { if (au_ftest_lock(flags, IW)) do_ii_write_lock(d->d_inode, lsc); else if (au_ftest_lock(flags, IR)) do_ii_read_lock(d->d_inode, lsc); } }
void au_plink_list(struct super_block *sb) { struct au_sbinfo *sbinfo; struct list_head *plink_list; struct pseudo_link *plink; SiMustAnyLock(sb); sbinfo = au_sbi(sb); AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); plink_list = &sbinfo->si_plink.head; rcu_read_lock(); list_for_each_entry_rcu(plink, plink_list, list) AuDbg("%lu\n", plink->inode->i_ino); rcu_read_unlock(); }
/* * when removing a dir, rename it to a unique temporary whiteout-ed name first * in order to be revertible and save time for removing many child whiteouts * under the dir. * returns 1 when there are too many child whiteout and caller should remove * them asynchronously. returns 0 when the number of children is enough small to * remove now or the branch fs is a remote fs. * otherwise return an error. */ static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex, struct au_nhash *whlist, struct inode *dir) { int rmdir_later, err, dirwh; struct dentry *h_dentry; struct super_block *sb; struct inode *inode; sb = dentry->d_sb; SiMustAnyLock(sb); h_dentry = au_h_dptr(dentry, bindex); err = au_whtmp_ren(h_dentry, au_sbr(sb, bindex)); if (unlikely(err)) goto out; /* stop monitoring */ inode = d_inode(dentry); au_hn_free(au_hi(inode, bindex)); if (!au_test_fs_remote(h_dentry->d_sb)) { dirwh = au_sbi(sb)->si_dirwh; rmdir_later = (dirwh <= 1); if (!rmdir_later) rmdir_later = au_nhash_test_longer_wh(whlist, bindex, dirwh); if (rmdir_later) return rmdir_later; } err = au_whtmp_rmdir(dir, bindex, h_dentry, whlist); if (unlikely(err)) { AuIOErr("rmdir %pd, b%d failed, %d. ignored\n", h_dentry, bindex, err); err = 0; } out: AuTraceErr(err); return err; }
/* side effect: sets whlist and h_dentry */ static int au_ren_may_dir(struct au_ren_args *a) { int err; unsigned int rdhash; struct dentry *d; d = a->dst_dentry; SiMustAnyLock(d->d_sb); err = 0; if (au_ftest_ren(a->flags, ISDIR) && a->dst_inode) { rdhash = au_sbi(d->d_sb)->si_rdhash; if (!rdhash) rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, d)); err = au_nhash_alloc(&a->whlist, rdhash, GFP_NOFS); if (unlikely(err)) goto out; au_set_dbstart(d, a->dst_bstart); err = may_rename_dstdir(d, &a->whlist); au_set_dbstart(d, a->btgt); } a->dst_h_dentry = au_h_dptr(d, au_dbstart(d)); if (unlikely(err)) goto out; d = a->src_dentry; a->src_h_dentry = au_h_dptr(d, au_dbstart(d)); if (au_ftest_ren(a->flags, ISDIR)) { err = may_rename_srcdir(d, a->btgt); if (unlikely(err)) { au_nhash_wh_free(&a->whlist); a->whlist.nh_num = 0; } } out: return err; }
/* * test if @dentry dir can be rename source or not. * if it can, return 0 and @children is filled. * success means, * - it is a logically empty dir. * - or, it exists on writable branch and has no children including whiteouts * on the lower branch. */ static int may_rename_srcdir(struct dentry *dentry, aufs_bindex_t btgt) { int err; unsigned int rdhash; aufs_bindex_t bstart; bstart = au_dbstart(dentry); if (bstart != btgt) { struct au_nhash whlist; SiMustAnyLock(dentry->d_sb); rdhash = au_sbi(dentry->d_sb)->si_rdhash; if (!rdhash) rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, dentry)); err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS); if (unlikely(err)) goto out; err = au_test_empty(dentry, &whlist); au_nhash_wh_free(&whlist); goto out; } if (bstart == au_dbtaildir(dentry)) return 0; /* success */ err = au_test_empty_lower(dentry); out: if (err == -ENOTEMPTY) { AuWarn1("renaming dir who has child(ren) on multiple branches," " is not supported\n"); err = -EXDEV; } return err; }
int au_opts_verify(struct super_block *sb, unsigned long sb_flags, unsigned int pending) { int err, fhsm; aufs_bindex_t bindex, bend; unsigned char do_plink, skip, do_free; struct au_branch *br; struct au_wbr *wbr; struct dentry *root; struct inode *dir, *h_dir; struct au_sbinfo *sbinfo; struct au_hinode *hdir; SiMustAnyLock(sb); sbinfo = au_sbi(sb); AuDebugOn(!(sbinfo->si_mntflags & AuOptMask_UDBA)); if (!(sb_flags & MS_RDONLY)) { if (unlikely(!au_br_writable(au_sbr_perm(sb, 0)))) pr_warn("first branch should be rw\n"); if (unlikely(au_opt_test(sbinfo->si_mntflags, SHWH))) pr_warn("shwh should be used with ro\n"); } if (au_opt_test((sbinfo->si_mntflags | pending), UDBA_HNOTIFY) && !au_opt_test(sbinfo->si_mntflags, XINO)) pr_warn("udba=*notify requires xino\n"); if (au_opt_test(sbinfo->si_mntflags, DIRPERM1)) pr_warn("dirperm1 breaks the protection" " by the permission bits on the lower branch\n"); err = 0; fhsm = 0; root = sb->s_root; dir = root->d_inode; do_plink = !!au_opt_test(sbinfo->si_mntflags, PLINK); bend = au_sbend(sb); for (bindex = 0; !err && bindex <= bend; bindex++) { skip = 0; h_dir = au_h_iptr(dir, bindex); br = au_sbr(sb, bindex); if ((br->br_perm & AuBrAttr_ICEX) && !h_dir->i_op->listxattr) br->br_perm &= ~AuBrAttr_ICEX; #if 0 if ((br->br_perm & AuBrAttr_ICEX_SEC) && (au_br_sb(br)->s_flags & MS_NOSEC)) br->br_perm &= ~AuBrAttr_ICEX_SEC; #endif do_free = 0; wbr = br->br_wbr; if (wbr) wbr_wh_read_lock(wbr); if (!au_br_writable(br->br_perm)) { do_free = !!wbr; skip = (!wbr || (!wbr->wbr_whbase && !wbr->wbr_plink && !wbr->wbr_orph)); } else if (!au_br_wh_linkable(br->br_perm)) { /* skip = (!br->br_whbase && !br->br_orph); */ skip = (!wbr || !wbr->wbr_whbase); if (skip && wbr) { if (do_plink) skip = !!wbr->wbr_plink; else skip = !wbr->wbr_plink; } } else { /* skip = (br->br_whbase && br->br_ohph); */ skip = (wbr && wbr->wbr_whbase); if (skip) { if (do_plink) skip = !!wbr->wbr_plink; else skip = !wbr->wbr_plink; } } if (wbr) wbr_wh_read_unlock(wbr); if (au_br_fhsm(br->br_perm)) { fhsm++; AuDebugOn(!br->br_fhsm); } if (skip) continue; hdir = au_hi(dir, bindex); au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); if (wbr) wbr_wh_write_lock(wbr); err = au_wh_init(br, sb); if (wbr) wbr_wh_write_unlock(wbr); au_hn_imtx_unlock(hdir); if (!err && do_free) { kfree(wbr); br->br_wbr = NULL; } } if (fhsm >= 2) { au_fset_si(sbinfo, FHSM); for (bindex = bend; bindex >= 0; bindex--) { br = au_sbr(sb, bindex); if (au_br_fhsm(br->br_perm)) { au_fhsm_set_bottom(sb, bindex); break; } } } else { au_fclr_si(sbinfo, FHSM); au_fhsm_set_bottom(sb, -1); } return err; }
aufs_bindex_t sbend(struct super_block *sb) { SiMustAnyLock(sb); return stosi(sb)->si_bend; }
int au_opts_verify(struct super_block *sb, unsigned long sb_flags, unsigned int pending) { int err; aufs_bindex_t bindex, bend; unsigned char do_plink, skip, do_free; struct au_branch *br; struct au_wbr *wbr; struct dentry *root; struct inode *dir, *h_dir; struct au_sbinfo *sbinfo; struct au_hinode *hdir; SiMustAnyLock(sb); sbinfo = au_sbi(sb); AuDebugOn(!(sbinfo->si_mntflags & AuOptMask_UDBA)); if (!(sb_flags & MS_RDONLY)) { if (unlikely(!au_br_writable(au_sbr_perm(sb, 0)))) pr_warn("first branch should be rw\n"); if (unlikely(au_opt_test(sbinfo->si_mntflags, SHWH))) pr_warn("shwh should be used with ro\n"); } if (au_opt_test((sbinfo->si_mntflags | pending), UDBA_HNOTIFY) && !au_opt_test(sbinfo->si_mntflags, XINO)) pr_warn("udba=*notify requires xino\n"); err = 0; root = sb->s_root; dir = root->d_inode; do_plink = !!au_opt_test(sbinfo->si_mntflags, PLINK); bend = au_sbend(sb); for (bindex = 0; !err && bindex <= bend; bindex++) { skip = 0; h_dir = au_h_iptr(dir, bindex); br = au_sbr(sb, bindex); do_free = 0; wbr = br->br_wbr; if (wbr) wbr_wh_read_lock(wbr); if (!au_br_writable(br->br_perm)) { do_free = !!wbr; skip = (!wbr || (!wbr->wbr_whbase && !wbr->wbr_plink && !wbr->wbr_orph)); } else if (!au_br_wh_linkable(br->br_perm)) { /* skip = (!br->br_whbase && !br->br_orph); */ skip = (!wbr || !wbr->wbr_whbase); if (skip && wbr) { if (do_plink) skip = !!wbr->wbr_plink; else skip = !wbr->wbr_plink; } } else { /* skip = (br->br_whbase && br->br_ohph); */ skip = (wbr && wbr->wbr_whbase); if (skip) { if (do_plink) skip = !!wbr->wbr_plink; else skip = !wbr->wbr_plink; } } if (wbr) wbr_wh_read_unlock(wbr); if (skip) continue; hdir = au_hi(dir, bindex); au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); if (wbr) wbr_wh_write_lock(wbr); err = au_wh_init(br, sb); if (wbr) wbr_wh_write_unlock(wbr); au_hn_imtx_unlock(hdir); if (!err && do_free) { kfree(wbr); br->br_wbr = NULL; } } return err; }
int au_sigen(struct super_block *sb) { SiMustAnyLock(sb); return stosi(sb)->si_generation; }
static noinline_for_stack struct dentry *decode_by_path(struct super_block *sb, aufs_bindex_t bindex, ino_t ino, __u32 *fh, int fh_len, struct au_nfsd_si_lock *nsi_lock) { struct dentry *dentry, *h_parent, *root; struct super_block *h_sb; char *pathname, *p; struct vfsmount *h_mnt; struct au_branch *br; int err; struct nameidata nd; struct path path; LKTRTrace("b%d\n", bindex); SiMustAnyLock(sb); br = au_sbr(sb, bindex); /* au_br_get(br); */ h_mnt = br->br_mnt; h_sb = h_mnt->mnt_sb; LKTRTrace("%s, h_decode_fh\n", au_sbtype(h_sb)); h_parent = au_call_decode_fh(h_mnt, fh + Fh_tail, fh_len - Fh_tail, fh[Fh_h_type], h_acceptable, /*context*/NULL); dentry = h_parent; if (unlikely(!h_parent || IS_ERR(h_parent))) { AuWarn1("%s decode_fh failed, %ld\n", au_sbtype(h_sb), PTR_ERR(h_parent)); goto out; } dentry = NULL; if (unlikely(au_test_anon(h_parent))) { AuWarn1("%s decode_fh returned a disconnected dentry\n", au_sbtype(h_sb)); goto out_h_parent; } dentry = ERR_PTR(-ENOMEM); pathname = (void *)__get_free_page(GFP_NOFS); if (unlikely(!pathname)) goto out_h_parent; root = sb->s_root; path.mnt = h_mnt; di_read_lock_parent(root, !AuLock_IR); path.dentry = au_h_dptr(root, bindex); di_read_unlock(root, !AuLock_IR); p = au_build_path(h_parent, &path, pathname, PAGE_SIZE, sb); dentry = (void *)p; if (IS_ERR(p)) goto out_pathname; LKTRTrace("%s\n", p); si_read_unlock(sb); err = vfsub_path_lookup(p, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &nd); dentry = ERR_PTR(err); if (unlikely(err)) goto out_relock; dentry = ERR_PTR(-ENOENT); AuDebugOn(au_test_anon(nd.dentry)); if (unlikely(!nd.dentry->d_inode)) goto out_nd; if (ino != nd.dentry->d_inode->i_ino) { path.mnt = nd.mnt; path.dentry = nd.dentry; dentry = au_lkup_by_ino(&path, ino, /*nsi_lock*/NULL); } else dentry = dget(nd.dentry); out_nd: path_release(&nd); out_relock: if (unlikely(si_nfsd_read_lock(sb, nsi_lock) < 0)) if (!IS_ERR(dentry)) { dput(dentry); dentry = ERR_PTR(-ESTALE); } out_pathname: free_page((unsigned long)pathname); out_h_parent: dput(h_parent); out: /* au_br_put(br); */ AuTraceErrPtr(dentry); return dentry; }