static int find_lower_writable(struct au_mvd_args *a) { struct super_block *sb; aufs_bindex_t bindex, bend; struct au_branch *br; sb = a->sb; bindex = a->mvd_bsrc; bend = au_sbend(sb); if (a->mvdown.flags & AUFS_MVDOWN_FHSM_LOWER) for (bindex++; bindex <= bend; bindex++) { br = au_sbr(sb, bindex); if (au_br_fhsm(br->br_perm) && (!(au_br_sb(br)->s_flags & MS_RDONLY))) return bindex; } else if (!(a->mvdown.flags & AUFS_MVDOWN_ROLOWER)) for (bindex++; bindex <= bend; bindex++) { br = au_sbr(sb, bindex); if (!au_br_rdonly(br)) return bindex; } else for (bindex++; bindex <= bend; bindex++) { br = au_sbr(sb, bindex); if (!(au_br_sb(br)->s_flags & MS_RDONLY)) { if (au_br_rdonly(br)) a->mvdown.flags |= AUFS_MVDOWN_ROLOWER_R; return bindex; } } return -1; }
static ssize_t au_fhsm_read(struct file *file, char __user *buf, size_t count, loff_t *pos) { ssize_t err; int readable; aufs_bindex_t nfhsm, bindex, bend; struct au_sbinfo *sbinfo; struct au_fhsm *fhsm; struct au_branch *br; struct super_block *sb; err = 0; sbinfo = file->private_data; fhsm = &sbinfo->si_fhsm; need_data: spin_lock_irq(&fhsm->fhsm_wqh.lock); if (!atomic_read(&fhsm->fhsm_readable)) { if (vfsub_file_flags(file) & O_NONBLOCK) err = -EAGAIN; else err = wait_event_interruptible_locked_irq (fhsm->fhsm_wqh, atomic_read(&fhsm->fhsm_readable)); } spin_unlock_irq(&fhsm->fhsm_wqh.lock); if (unlikely(err)) goto out; /* sb may already be dead */ au_rw_read_lock(&sbinfo->si_rwsem); readable = atomic_read(&fhsm->fhsm_readable); if (readable > 0) { sb = sbinfo->si_sb; AuDebugOn(!sb); /* exclude the bottom branch */ nfhsm = 0; bend = au_fhsm_bottom(sb); for (bindex = 0; bindex < bend; bindex++) { br = au_sbr(sb, bindex); if (au_br_fhsm(br->br_perm)) nfhsm++; } err = -EMSGSIZE; if (nfhsm * sizeof(struct aufs_stbr) <= count) { atomic_set(&fhsm->fhsm_readable, 0); err = au_fhsm_do_read(sbinfo->si_sb, (void __user *)buf, count); } } au_rw_read_unlock(&sbinfo->si_rwsem); if (!readable) goto need_data; out: return err; }
void au_fhsm_wrote_all(struct super_block *sb, int force) { aufs_bindex_t bindex, bend; struct au_branch *br; /* exclude the bottom */ bend = au_fhsm_bottom(sb); for (bindex = 0; bindex < bend; bindex++) { br = au_sbr(sb, bindex); if (au_br_fhsm(br->br_perm)) au_fhsm_wrote(sb, bindex, force); } }
static ssize_t au_fhsm_do_read(struct super_block *sb, struct aufs_stbr __user *stbr, size_t count) { ssize_t err; int nstbr; aufs_bindex_t bindex, bend; struct au_branch *br; struct au_br_fhsm *bf; /* except the bottom branch */ err = 0; nstbr = 0; bend = au_fhsm_bottom(sb); for (bindex = 0; !err && bindex < bend; bindex++) { br = au_sbr(sb, bindex); if (!au_br_fhsm(br->br_perm)) continue; bf = br->br_fhsm; mutex_lock(&bf->bf_lock); if (bf->bf_readable) { err = -EFAULT; if (count >= sizeof(*stbr)) err = au_fhsm_do_read_one(stbr++, &bf->bf_stfs, br->br_id); if (!err) { bf->bf_readable = 0; count -= sizeof(*stbr); nstbr++; } } mutex_unlock(&bf->bf_lock); } if (!err) err = sizeof(*stbr) * nstbr; 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; }