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; }
int au_finfo_init(struct file *file) { struct au_finfo *finfo; struct dentry *dentry; unsigned long ul; dentry = file->f_dentry; finfo = au_cache_alloc_finfo(); if (unlikely(!finfo)) goto out; finfo->fi_hfile = kcalloc(au_sbend(dentry->d_sb) + 1, sizeof(*finfo->fi_hfile), GFP_NOFS); if (unlikely(!finfo->fi_hfile)) goto out_finfo; au_rw_init_wlock(&finfo->fi_rwsem); finfo->fi_bstart = -1; finfo->fi_bend = -1; atomic_set(&finfo->fi_generation, au_digen(dentry)); /* smp_mb(); */ /* atomic_set */ /* cf. au_store_oflag() */ /* suppress a warning in lp64 */ ul = (unsigned long)file->private_data; file->f_mode |= (vfsub_uint_to_fmode(ul) & FMODE_EXEC); file->private_data = finfo; return 0; /* success */ out_finfo: au_cache_free_finfo(finfo); out: return -ENOMEM; }
/* * after branch manipulating, refresh the file. */ static int refresh_file(struct file *file, int (*reopen)(struct file *file)) { int err, need_reopen; struct dentry *dentry; aufs_bindex_t bend, bindex; dentry = file->f_dentry; err = au_fi_realloc(au_fi(file), au_sbend(dentry->d_sb) + 1); if (unlikely(err)) goto out; au_do_refresh_file(file); err = 0; need_reopen = 1; if (!au_test_mmapped(file)) err = au_file_refresh_by_inode(file, &need_reopen); if (!err && need_reopen && !d_unhashed(dentry)) err = reopen(file); if (!err) { au_update_figen(file); return 0; /* success */ } /* error, close all lower files */ bend = au_fbend(file); for (bindex = au_fbstart(file); bindex <= bend; bindex++) au_set_h_fptr(file, bindex, NULL); out: return err; }
struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc) { struct au_dinfo *dinfo; int nbr, i; dinfo = au_cache_alloc_dinfo(); if (unlikely(!dinfo)) goto out; nbr = au_sbend(sb) + 1; if (nbr <= 0) nbr = 1; dinfo->di_hdentry = kcalloc(nbr, sizeof(*dinfo->di_hdentry), GFP_NOFS); if (dinfo->di_hdentry) { au_rw_write_lock_nested(&dinfo->di_rwsem, lsc); dinfo->di_bstart = -1; dinfo->di_bend = -1; dinfo->di_bwh = -1; dinfo->di_bdiropq = -1; dinfo->di_tmpfile = 0; for (i = 0; i < nbr; i++) dinfo->di_hdentry[i].hd_id = -1; goto out; } au_cache_free_dinfo(dinfo); dinfo = NULL; out: return dinfo; }
int au_alloc_dinfo(struct dentry *dentry) { struct au_dinfo *dinfo; struct super_block *sb; int nbr; dinfo = au_cache_alloc_dinfo(); if (unlikely(!dinfo)) goto out; sb = dentry->d_sb; nbr = au_sbend(sb) + 1; if (nbr <= 0) nbr = 1; dinfo->di_hdentry = kcalloc(nbr, sizeof(*dinfo->di_hdentry), GFP_NOFS); if (unlikely(!dinfo->di_hdentry)) goto out_dinfo; atomic_set(&dinfo->di_generation, au_sigen(sb)); /* smp_mb(); */ /* atomic_set */ au_rw_init_wlock_nested(&dinfo->di_rwsem, AuLsc_DI_CHILD); dinfo->di_bstart = -1; dinfo->di_bend = -1; dinfo->di_bwh = -1; dinfo->di_bdiropq = -1; dentry->d_fsdata = dinfo; dentry->d_op = &aufs_dop; return 0; /* success */ out_dinfo: au_cache_free_dinfo(dinfo); out: return -ENOMEM; }
int au_refresh_hinode_self(struct inode *inode, int do_attr) { int err, e; aufs_bindex_t bindex, new_bindex; unsigned char update; struct au_hinode *p, *q, tmp; struct super_block *sb; struct au_iinfo *iinfo; IiMustWriteLock(inode); update = 0; sb = inode->i_sb; iinfo = au_ii(inode); err = au_ii_realloc(iinfo, au_sbend(sb) + 1); if (unlikely(err)) goto out; p = iinfo->ii_hinode + iinfo->ii_bstart; err = 0; for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; bindex++, p++) { if (!p->hi_inode) continue; 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; if (do_attr) au_refresh_hinode_attr(inode, update && S_ISDIR(inode->i_mode)); out: return err; }
static int au_ibusy(struct super_block *sb, struct aufs_ibusy __user *arg) { int err; aufs_bindex_t bstart, bend; struct aufs_ibusy ibusy; struct inode *inode, *h_inode; err = -EPERM; if (unlikely(!capable(CAP_SYS_ADMIN))) goto out; err = copy_from_user(&ibusy, arg, sizeof(ibusy)); if (!err) err = !access_ok(VERIFY_WRITE, &arg->h_ino, sizeof(arg->h_ino)); if (unlikely(err)) { err = -EFAULT; AuTraceErr(err); goto out; } err = -EINVAL; si_read_lock(sb, AuLock_FLUSH); if (unlikely(ibusy.bindex < 0 || ibusy.bindex > au_sbend(sb))) goto out_unlock; err = 0; ibusy.h_ino = 0; /* invalid */ inode = ilookup(sb, ibusy.ino); if (!inode || inode->i_ino == AUFS_ROOT_INO || is_bad_inode(inode)) goto out_unlock; ii_read_lock_child(inode); bstart = au_ibstart(inode); bend = au_ibend(inode); if (bstart <= ibusy.bindex && ibusy.bindex <= bend) { h_inode = au_h_iptr(inode, ibusy.bindex); if (h_inode && au_test_ibusy(inode, bstart, bend)) ibusy.h_ino = h_inode->i_ino; } ii_read_unlock(inode); iput(inode); out_unlock: si_read_unlock(sb); if (!err) { err = __put_user(ibusy.h_ino, &arg->h_ino); if (unlikely(err)) { err = -EFAULT; AuTraceErr(err); } } out: return err; }
/* * find the index of a branch which is specified by @br_id. */ int au_br_index(struct super_block *sb, aufs_bindex_t br_id) { aufs_bindex_t bindex, bend; bend = au_sbend(sb); for (bindex = 0; bindex <= bend; bindex++) if (au_sbr_id(sb, bindex) == br_id) return bindex; return -1; }
/* most free space */ static void au_mfs(struct dentry *dentry) { struct super_block *sb; struct au_branch *br; struct au_wbr_mfs *mfs; aufs_bindex_t bindex, bend; int err; unsigned long long b, bavail; struct path h_path; /* reduce the stack usage */ struct kstatfs *st; st = kmalloc(sizeof(*st), GFP_NOFS); if (unlikely(!st)) { AuWarn1("failed updating mfs(%d), ignored\n", -ENOMEM); return; } bavail = 0; sb = dentry->d_sb; mfs = &au_sbi(sb)->si_wbr_mfs; MtxMustLock(&mfs->mfs_lock); mfs->mfs_bindex = -EROFS; mfs->mfsrr_bytes = 0; bend = au_sbend(sb); for (bindex = 0; bindex <= bend; bindex++) { br = au_sbr(sb, bindex); if (au_br_rdonly(br)) continue; /* sb->s_root for NFS is unreliable */ h_path.mnt = br->br_mnt; h_path.dentry = h_path.mnt->mnt_root; err = vfs_statfs(&h_path, st); if (unlikely(err)) { AuWarn1("failed statfs, b%d, %d\n", bindex, err); continue; } /* when the available size is equal, select the lower one */ BUILD_BUG_ON(sizeof(b) < sizeof(st->f_bavail) || sizeof(b) < sizeof(st->f_bsize)); b = st->f_bavail * st->f_bsize; br->br_wbr->wbr_bytes = b; if (b >= bavail) { bavail = b; mfs->mfs_bindex = bindex; mfs->mfs_jiffy = jiffies; } } mfs->mfsrr_bytes = bavail; AuDbg("b%d\n", mfs->mfs_bindex); kfree(st); }
/* round robin */ static int au_wbr_create_init_rr(struct super_block *sb) { int err; err = au_wbr_bu(sb, au_sbend(sb)); atomic_set(&au_sbi(sb)->si_wbr_rr_next, -err); /* less important */ /* smp_mb(); */ AuDbg("b%d\n", err); return err; }
void au_set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex) { DiMustWriteLock(dentry); AuDebugOn(au_sbend(dentry->d_sb) < bindex); AuDebugOn((bindex >= 0 && (bindex < au_dbstart(dentry) || au_dbend(dentry) < bindex)) || (dentry->d_inode && dentry->d_inode->i_mode && !S_ISDIR(dentry->d_inode->i_mode))); au_di(dentry)->di_bdiropq = bindex; }
/* * returns tri-state. * plus: processed without an error * zero: unprocessed * minus: error */ static int au_opt_br(struct super_block *sb, struct au_opt *opt, struct au_opts *opts) { int err, do_refresh; err = 0; switch (opt->type) { case Opt_append: opt->add.bindex = au_sbend(sb) + 1; if (opt->add.bindex < 0) opt->add.bindex = 0; goto add; case Opt_prepend: opt->add.bindex = 0; add: case Opt_add: err = au_br_add(sb, &opt->add, au_ftest_opts(opts->flags, REMOUNT)); if (!err) { err = 1; au_fset_opts(opts->flags, REFRESH); } break; case Opt_del: case Opt_idel: err = au_br_del(sb, &opt->del, au_ftest_opts(opts->flags, REMOUNT)); if (!err) { err = 1; au_fset_opts(opts->flags, TRUNC_XIB); au_fset_opts(opts->flags, REFRESH); } break; case Opt_mod: case Opt_imod: err = au_br_mod(sb, &opt->mod, au_ftest_opts(opts->flags, REMOUNT), &do_refresh); if (!err) { err = 1; if (do_refresh) au_fset_opts(opts->flags, REFRESH); } break; } return err; }
static int au_wbr_fd(struct path *path) { int err, fd; aufs_bindex_t wbi, bindex, bend; struct file *h_file; struct super_block *sb; struct dentry *root; struct au_branch *wbr; err = get_unused_fd(); if (unlikely(err < 0)) goto out; fd = err; wbi = 0; sb = path->dentry->d_sb; root = sb->s_root; aufs_read_lock(root, AuLock_IR); wbr = au_sbr(sb, wbi); if (!(path->mnt->mnt_flags & MNT_READONLY) && !au_br_writable(wbr->br_perm)) { bend = au_sbend(sb); for (bindex = 1; bindex <= bend; bindex++) { wbr = au_sbr(sb, bindex); if (au_br_writable(wbr->br_perm)) { wbi = bindex; break; } } wbr = au_sbr(sb, wbi); } AuDbg("wbi %d\n", wbi); h_file = au_h_open(root, wbi, O_RDONLY | O_DIRECTORY | O_LARGEFILE, NULL); aufs_read_unlock(root, AuLock_IR); err = PTR_ERR(h_file); if (IS_ERR(h_file)) goto out_fd; atomic_dec(&wbr->br_count); /* cf. au_h_open() */ fd_install(fd, h_file); err = fd; goto out; /* success */ out_fd: put_unused_fd(fd); out: return err; }
struct au_fidir *au_fidir_alloc(struct super_block *sb) { struct au_fidir *fidir; int nbr; nbr = au_sbend(sb) + 1; if (nbr < 2) nbr = 2; /* initial allocate for 2 branches */ fidir = kzalloc(au_fidir_sz(nbr), GFP_NOFS); if (fidir) { fidir->fd_bbot = -1; fidir->fd_nent = nbr; fidir->fd_vdir_cache = NULL; } return fidir; }
/* * after branch manipulating, refresh the file. */ static int refresh_file(struct file *file, int (*reopen)(struct file *file)) { int err, need_reopen; aufs_bindex_t bend, bindex; struct dentry *dentry; struct au_finfo *finfo; struct au_hfile *hfile; dentry = file->f_dentry; finfo = au_fi(file); if (!finfo->fi_hdir) { hfile = &finfo->fi_htop; AuDebugOn(!hfile->hf_file); bindex = au_br_index(dentry->d_sb, hfile->hf_br->br_id); AuDebugOn(bindex < 0); if (bindex != finfo->fi_btop) au_set_fbstart(file, bindex); } else { err = au_fidir_realloc(finfo, au_sbend(dentry->d_sb) + 1); if (unlikely(err)) goto out; au_do_refresh_dir(file); } err = 0; need_reopen = 1; if (!au_test_mmapped(file)) err = au_file_refresh_by_inode(file, &need_reopen); if (!err && need_reopen && !d_unlinked(dentry)) err = reopen(file); if (!err) { au_update_figen(file); goto out; /* success */ } /* error, close all lower files */ if (finfo->fi_hdir) { bend = au_fbend_dir(file); for (bindex = au_fbstart(file); bindex <= bend; bindex++) au_set_h_fptr(file, bindex, NULL); } 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); }
/* * returns the number of found lower positive dentries, * otherwise an error. */ int au_refresh_hdentry(struct dentry *dentry, mode_t type) { int npositive, err; unsigned int sigen; aufs_bindex_t bstart; struct au_dinfo *dinfo; struct super_block *sb; struct dentry *parent; DiMustWriteLock(dentry); sb = dentry->d_sb; AuDebugOn(IS_ROOT(dentry)); sigen = au_sigen(sb); parent = dget_parent(dentry); AuDebugOn(au_digen(parent) != sigen || au_iigen(parent->d_inode) != sigen); dinfo = au_di(dentry); err = au_di_realloc(dinfo, au_sbend(sb) + 1); npositive = err; if (unlikely(err)) goto out; au_do_refresh_hdentry(dinfo->di_hdentry + dinfo->di_bstart, dinfo, parent); npositive = 0; bstart = au_dbstart(parent); if (type != S_IFDIR && dinfo->di_bstart == bstart) goto out_dgen; /* success */ npositive = au_lkup_dentry(dentry, bstart, type, /*nd*/NULL); if (npositive < 0) goto out; if (dinfo->di_bwh >= 0 && dinfo->di_bwh <= dinfo->di_bstart) d_drop(dentry); out_dgen: au_update_digen(dentry); out: dput(parent); AuTraceErr(npositive); return npositive; }
static int au_opt_xino(struct super_block *sb, struct au_opt *opt, struct au_opt_xino **opt_xino, struct au_opts *opts) { int err; aufs_bindex_t bend, bindex; struct dentry *root, *parent, *h_root; err = 0; switch (opt->type) { case Opt_xino: err = au_xino_set(sb, &opt->xino, !!au_ftest_opts(opts->flags, REMOUNT)); if (unlikely(err)) break; *opt_xino = &opt->xino; au_xino_brid_set(sb, -1); /* safe d_parent access */ parent = opt->xino.file->f_dentry->d_parent; root = sb->s_root; bend = au_sbend(sb); for (bindex = 0; bindex <= bend; bindex++) { h_root = au_h_dptr(root, bindex); if (h_root == parent) { au_xino_brid_set(sb, au_sbr_id(sb, bindex)); break; } } break; case Opt_noxino: au_xino_clr(sb); au_xino_brid_set(sb, -1); *opt_xino = (void *)-1; break; } return err; }
static int au_wbr_create_rr(struct dentry *dentry, int isdir) { int err, nbr; unsigned int u; aufs_bindex_t bindex, bend; struct super_block *sb; atomic_t *next; err = au_wbr_create_exp(dentry); if (err >= 0) goto out; sb = dentry->d_sb; next = &au_sbi(sb)->si_wbr_rr_next; bend = au_sbend(sb); nbr = bend + 1; for (bindex = 0; bindex <= bend; bindex++) { if (!isdir) { err = atomic_dec_return(next) + 1; /* modulo for 0 is meaningless */ if (unlikely(!err)) err = atomic_dec_return(next) + 1; } else err = atomic_read(next); AuDbg("%d\n", err); u = err; err = u % nbr; AuDbg("%d\n", err); if (!au_br_rdonly(au_sbr(sb, err))) break; err = -EROFS; } if (err >= 0) err = au_wbr_nonopq(dentry, err); out: AuDbg("%d\n", err); return err; }
/* reduce stack space */ union { struct au_opt_add *add; struct au_opt_del *del; struct au_opt_mod *mod; struct au_opt_xino *xino; struct au_opt_xino_itrunc *xino_itrunc; struct au_opt_wbr_create *create; } u; struct au_opt *opt; opt = opts->opt; while (opt->type != Opt_tail) { switch (opt->type) { case Opt_add: u.add = &opt->add; AuDbg("add {b%d, %s, 0x%x, %p}\n", u.add->bindex, u.add->pathname, u.add->perm, u.add->path.dentry); break; case Opt_del: case Opt_idel: u.del = &opt->del; AuDbg("del {%s, %p}\n", u.del->pathname, u.del->h_path.dentry); break; case Opt_mod: case Opt_imod: u.mod = &opt->mod; AuDbg("mod {%s, 0x%x, %p}\n", u.mod->path, u.mod->perm, u.mod->h_root); break; case Opt_append: u.add = &opt->add; AuDbg("append {b%d, %s, 0x%x, %p}\n", u.add->bindex, u.add->pathname, u.add->perm, u.add->path.dentry); break; case Opt_prepend: u.add = &opt->add; AuDbg("prepend {b%d, %s, 0x%x, %p}\n", u.add->bindex, u.add->pathname, u.add->perm, u.add->path.dentry); break; case Opt_dirwh: AuDbg("dirwh %d\n", opt->dirwh); break; case Opt_rdcache: AuDbg("rdcache %d\n", opt->rdcache); break; case Opt_rdblk: AuDbg("rdblk %u\n", opt->rdblk); break; case Opt_rdblk_def: AuDbg("rdblk_def\n"); break; case Opt_rdhash: AuDbg("rdhash %u\n", opt->rdhash); break; case Opt_rdhash_def: AuDbg("rdhash_def\n"); break; case Opt_xino: u.xino = &opt->xino; AuDbg("xino {%s %.*s}\n", u.xino->path, AuDLNPair(u.xino->file->f_dentry)); break; case Opt_trunc_xino: AuLabel(trunc_xino); break; case Opt_notrunc_xino: AuLabel(notrunc_xino); break; case Opt_trunc_xino_path: case Opt_itrunc_xino: u.xino_itrunc = &opt->xino_itrunc; AuDbg("trunc_xino %d\n", u.xino_itrunc->bindex); break; case Opt_noxino: AuLabel(noxino); break; case Opt_trunc_xib: AuLabel(trunc_xib); break; case Opt_notrunc_xib: AuLabel(notrunc_xib); break; case Opt_shwh: AuLabel(shwh); break; case Opt_noshwh: AuLabel(noshwh); break; case Opt_plink: AuLabel(plink); break; case Opt_noplink: AuLabel(noplink); break; case Opt_list_plink: AuLabel(list_plink); break; case Opt_udba: AuDbg("udba %d, %s\n", opt->udba, au_optstr_udba(opt->udba)); break; case Opt_dio: AuLabel(dio); break; case Opt_nodio: AuLabel(nodio); break; case Opt_diropq_a: AuLabel(diropq_a); break; case Opt_diropq_w: AuLabel(diropq_w); break; case Opt_warn_perm: AuLabel(warn_perm); break; case Opt_nowarn_perm: AuLabel(nowarn_perm); break; case Opt_refrof: AuLabel(refrof); break; case Opt_norefrof: AuLabel(norefrof); break; case Opt_verbose: AuLabel(verbose); break; case Opt_noverbose: AuLabel(noverbose); break; case Opt_sum: AuLabel(sum); break; case Opt_nosum: AuLabel(nosum); break; case Opt_wsum: AuLabel(wsum); break; case Opt_wbr_create: u.create = &opt->wbr_create; AuDbg("create %d, %s\n", u.create->wbr_create, au_optstr_wbr_create(u.create->wbr_create)); switch (u.create->wbr_create) { case AuWbrCreate_MFSV: case AuWbrCreate_PMFSV: AuDbg("%d sec\n", u.create->mfs_second); break; case AuWbrCreate_MFSRR: AuDbg("%llu watermark\n", u.create->mfsrr_watermark); break; case AuWbrCreate_MFSRRV: AuDbg("%llu watermark, %d sec\n", u.create->mfsrr_watermark, u.create->mfs_second); break; } break; case Opt_wbr_copyup: AuDbg("copyup %d, %s\n", opt->wbr_copyup, au_optstr_wbr_copyup(opt->wbr_copyup)); break; default: BUG(); } opt++; } #endif } void au_opts_free(struct au_opts *opts) { struct au_opt *opt; opt = opts->opt; while (opt->type != Opt_tail) { switch (opt->type) { case Opt_add: case Opt_append: case Opt_prepend: path_put(&opt->add.path); break; case Opt_del: case Opt_idel: path_put(&opt->del.h_path); break; case Opt_mod: case Opt_imod: dput(opt->mod.h_root); break; case Opt_xino: fput(opt->xino.file); break; } opt++; } } static int opt_add(struct au_opt *opt, char *opt_str, unsigned long sb_flags, aufs_bindex_t bindex) { int err; struct au_opt_add *add = &opt->add; char *p; add->bindex = bindex; add->perm = AuBrPerm_RO; add->pathname = opt_str; p = strchr(opt_str, '='); if (p) { *p++ = 0; if (*p) add->perm = br_perm_val(p); } err = vfsub_kern_path(add->pathname, lkup_dirflags, &add->path); if (!err) { if (!p) { add->perm = AuBrPerm_RO; if (au_test_fs_rr(add->path.dentry->d_sb)) add->perm = AuBrPerm_RR; else if (!bindex && !(sb_flags & MS_RDONLY)) add->perm = AuBrPerm_RW; } opt->type = Opt_add; goto out; } pr_err("lookup failed %s (%d)\n", add->pathname, err); err = -EINVAL; out: return err; } static int au_opts_parse_del(struct au_opt_del *del, substring_t args[]) { int err; del->pathname = args[0].from; AuDbg("del path %s\n", del->pathname); err = vfsub_kern_path(del->pathname, lkup_dirflags, &del->h_path); if (unlikely(err)) pr_err("lookup failed %s (%d)\n", del->pathname, err); return err; } #if 0 /* reserved for future use */ static int au_opts_parse_idel(struct super_block *sb, aufs_bindex_t bindex, struct au_opt_del *del, substring_t args[]) { int err; struct dentry *root; err = -EINVAL; root = sb->s_root; aufs_read_lock(root, AuLock_FLUSH); if (bindex < 0 || au_sbend(sb) < bindex) { pr_err("out of bounds, %d\n", bindex); goto out; } err = 0; del->h_path.dentry = dget(au_h_dptr(root, bindex)); del->h_path.mnt = mntget(au_sbr_mnt(sb, bindex)); out: aufs_read_unlock(root, !AuLock_IR); return err; }
int au_alloc_dinfo(struct dentry *dentry) { struct au_dinfo *dinfo; struct super_block *sb; int nbr; LKTRTrace("%.*s\n", AuDLNPair(dentry)); AuDebugOn(dentry->d_fsdata); dinfo = au_cache_alloc_dinfo(); if (dinfo) { sb = dentry->d_sb; nbr = au_sbend(sb) + 1; if (nbr <= 0) nbr = 1; dinfo->di_hdentry = kcalloc(nbr, sizeof(*dinfo->di_hdentry), GFP_NOFS); if (dinfo->di_hdentry) { au_h_dentry_init_all(dinfo->di_hdentry, nbr); atomic_set(&dinfo->di_generation, au_sigen(sb)); /* smp_mb(); */ /* atomic_set */ au_rw_init_wlock_nested(&dinfo->di_rwsem, AuLsc_DI_CHILD); au_dbg_locked_di_reg(dentry, AuLock_DW, AuLsc_DI_CHILD); dinfo->di_bstart = -1; dinfo->di_bend = -1; dinfo->di_bwh = -1; dinfo->di_bdiropq = -1; dentry->d_fsdata = dinfo; dentry->d_op = &aufs_dop; return 0; /* success */ } au_cache_free_dinfo(dinfo); } AuTraceErr(-ENOMEM); return -ENOMEM; }
static int noinline_for_stack au_opts_parse_xino_itrunc_path(struct super_block *sb, struct au_opt_xino_itrunc *xino_itrunc, substring_t args[]) { int err; aufs_bindex_t bend, bindex; struct path path; struct dentry *root; err = vfsub_kern_path(args[0].from, lkup_dirflags, &path); if (unlikely(err)) { pr_err("lookup failed %s (%d)\n", args[0].from, err); goto out; } xino_itrunc->bindex = -1; root = sb->s_root; aufs_read_lock(root, AuLock_FLUSH); bend = au_sbend(sb); for (bindex = 0; bindex <= bend; bindex++) { if (au_h_dptr(root, bindex) == path.dentry) { xino_itrunc->bindex = bindex; break; } } aufs_read_unlock(root, !AuLock_IR); path_put(&path); if (unlikely(xino_itrunc->bindex < 0)) { pr_err("no such branch %s\n", args[0].from); err = -EINVAL; } out: return err; }
/* reduce stack space */ union { struct au_opt_add *add; struct au_opt_del *del; struct au_opt_mod *mod; struct au_opt_xino *xino; struct au_opt_xino_itrunc *xino_itrunc; struct au_opt_wbr_create *create; } u; struct au_opt *opt; opt = opts->opt; while (opt->type != Opt_tail) { switch (opt->type) { case Opt_add: u.add = &opt->add; AuDbg("add {b%d, %s, 0x%x, %p}\n", u.add->bindex, u.add->pathname, u.add->perm, u.add->path.dentry); break; case Opt_del: case Opt_idel: u.del = &opt->del; AuDbg("del {%s, %p}\n", u.del->pathname, u.del->h_path.dentry); break; case Opt_mod: case Opt_imod: u.mod = &opt->mod; AuDbg("mod {%s, 0x%x, %p}\n", u.mod->path, u.mod->perm, u.mod->h_root); break; case Opt_append: u.add = &opt->add; AuDbg("append {b%d, %s, 0x%x, %p}\n", u.add->bindex, u.add->pathname, u.add->perm, u.add->path.dentry); break; case Opt_prepend: u.add = &opt->add; AuDbg("prepend {b%d, %s, 0x%x, %p}\n", u.add->bindex, u.add->pathname, u.add->perm, u.add->path.dentry); break; case Opt_dirwh: AuDbg("dirwh %d\n", opt->dirwh); break; case Opt_rdcache: AuDbg("rdcache %d\n", opt->rdcache); break; case Opt_rdblk: AuDbg("rdblk %u\n", opt->rdblk); break; case Opt_rdblk_def: AuDbg("rdblk_def\n"); break; case Opt_rdhash: AuDbg("rdhash %u\n", opt->rdhash); break; case Opt_rdhash_def: AuDbg("rdhash_def\n"); break; case Opt_xino: u.xino = &opt->xino; AuDbg("xino {%s %.*s}\n", u.xino->path, AuDLNPair(u.xino->file->f_dentry)); break; case Opt_trunc_xino: AuLabel(trunc_xino); break; case Opt_notrunc_xino: AuLabel(notrunc_xino); break; case Opt_trunc_xino_path: case Opt_itrunc_xino: u.xino_itrunc = &opt->xino_itrunc; AuDbg("trunc_xino %d\n", u.xino_itrunc->bindex); break; case Opt_noxino: AuLabel(noxino); break; case Opt_trunc_xib: AuLabel(trunc_xib); break; case Opt_notrunc_xib: AuLabel(notrunc_xib); break; case Opt_shwh: AuLabel(shwh); break; case Opt_noshwh: AuLabel(noshwh); break; case Opt_plink: AuLabel(plink); break; case Opt_noplink: AuLabel(noplink); break; case Opt_list_plink: AuLabel(list_plink); break; case Opt_udba: AuDbg("udba %d, %s\n", opt->udba, au_optstr_udba(opt->udba)); break; case Opt_dio: AuLabel(dio); break; case Opt_nodio: AuLabel(nodio); break; case Opt_diropq_a: AuLabel(diropq_a); break; case Opt_diropq_w: AuLabel(diropq_w); break; case Opt_warn_perm: AuLabel(warn_perm); break; case Opt_nowarn_perm: AuLabel(nowarn_perm); break; case Opt_refrof: AuLabel(refrof); break; case Opt_norefrof: AuLabel(norefrof); break; case Opt_verbose: AuLabel(verbose); break; case Opt_noverbose: AuLabel(noverbose); break; case Opt_sum: AuLabel(sum); break; case Opt_nosum: AuLabel(nosum); break; case Opt_wsum: AuLabel(wsum); break; case Opt_wbr_create: u.create = &opt->wbr_create; AuDbg("create %d, %s\n", u.create->wbr_create, au_optstr_wbr_create(u.create->wbr_create)); switch (u.create->wbr_create) { case AuWbrCreate_MFSV: case AuWbrCreate_PMFSV: AuDbg("%d sec\n", u.create->mfs_second); break; case AuWbrCreate_MFSRR: AuDbg("%llu watermark\n", u.create->mfsrr_watermark); break; case AuWbrCreate_MFSRRV: AuDbg("%llu watermark, %d sec\n", u.create->mfsrr_watermark, u.create->mfs_second); break; } break; case Opt_wbr_copyup: AuDbg("copyup %d, %s\n", opt->wbr_copyup, au_optstr_wbr_copyup(opt->wbr_copyup)); break; default: BUG(); } opt++; } #endif } void au_opts_free(struct au_opts *opts) { struct au_opt *opt; opt = opts->opt; while (opt->type != Opt_tail) { switch (opt->type) { case Opt_add: case Opt_append: case Opt_prepend: path_put(&opt->add.path); break; case Opt_del: case Opt_idel: path_put(&opt->del.h_path); break; case Opt_mod: case Opt_imod: dput(opt->mod.h_root); break; case Opt_xino: fput(opt->xino.file); break; } opt++; } } static int opt_add(struct au_opt *opt, char *opt_str, unsigned long sb_flags, aufs_bindex_t bindex) { int err; struct au_opt_add *add = &opt->add; char *p; add->bindex = bindex; add->perm = AuBrPerm_RO; add->pathname = opt_str; p = strchr(opt_str, '='); if (p) { *p++ = 0; if (*p) add->perm = br_perm_val(p); } err = vfsub_kern_path(add->pathname, lkup_dirflags, &add->path); if (!err) { if (!p) { add->perm = AuBrPerm_RO; if (au_test_fs_rr(add->path.dentry->d_sb)) add->perm = AuBrPerm_RR; else if (!bindex && !(sb_flags & MS_RDONLY)) add->perm = AuBrPerm_RW; } opt->type = Opt_add; goto out; } pr_err("lookup failed %s (%d)\n", add->pathname, err); err = -EINVAL; out: return err; } static int au_opts_parse_del(struct au_opt_del *del, substring_t args[]) { int err; del->pathname = args[0].from; AuDbg("del path %s\n", del->pathname); err = vfsub_kern_path(del->pathname, lkup_dirflags, &del->h_path); if (unlikely(err)) pr_err("lookup failed %s (%d)\n", del->pathname, err); return err; } #if 0 /* reserved for future use */ static int au_opts_parse_idel(struct super_block *sb, aufs_bindex_t bindex, struct au_opt_del *del, substring_t args[]) { int err; struct dentry *root; err = -EINVAL; root = sb->s_root; aufs_read_lock(root, AuLock_FLUSH); if (bindex < 0 || au_sbend(sb) < bindex) { pr_err("out of bounds, %d\n", bindex); goto out; } err = 0; del->h_path.dentry = dget(au_h_dptr(root, bindex)); del->h_path.mnt = mntget(au_sbr_mnt(sb, bindex)); out: aufs_read_unlock(root, !AuLock_IR); return err; } #endif static int noinline_for_stack au_opts_parse_mod(struct au_opt_mod *mod, substring_t args[]) { int err; struct path path; char *p; err = -EINVAL; mod->path = args[0].from; p = strchr(mod->path, '='); if (unlikely(!p)) { pr_err("no permssion %s\n", args[0].from); goto out; } *p++ = 0; err = vfsub_kern_path(mod->path, lkup_dirflags, &path); if (unlikely(err)) { pr_err("lookup failed %s (%d)\n", mod->path, err); goto out; } mod->perm = br_perm_val(p); AuDbg("mod path %s, perm 0x%x, %s\n", mod->path, mod->perm, p); mod->h_root = dget(path.dentry); path_put(&path); out: return err; } #if 0 /* reserved for future use */ static int au_opts_parse_imod(struct super_block *sb, aufs_bindex_t bindex, struct au_opt_mod *mod, substring_t args[]) { int err; struct dentry *root; err = -EINVAL; root = sb->s_root; aufs_read_lock(root, AuLock_FLUSH); if (bindex < 0 || au_sbend(sb) < bindex) { pr_err("out of bounds, %d\n", bindex); goto out; } err = 0; mod->perm = br_perm_val(args[1].from); AuDbg("mod path %s, perm 0x%x, %s\n", mod->path, mod->perm, args[1].from); mod->h_root = dget(au_h_dptr(root, bindex)); out: aufs_read_unlock(root, !AuLock_IR); return err; }
static void au_do_refresh_dir(struct file *file) { aufs_bindex_t bindex, bend, new_bindex, brid; struct au_hfile *p, tmp, *q; struct au_finfo *finfo; struct super_block *sb; struct au_fidir *fidir; FiMustWriteLock(file); sb = file->f_dentry->d_sb; finfo = au_fi(file); fidir = finfo->fi_hdir; AuDebugOn(!fidir); p = fidir->fd_hfile + finfo->fi_btop; brid = p->hf_br->br_id; bend = fidir->fd_bbot; for (bindex = finfo->fi_btop; bindex <= bend; bindex++, p++) { if (!p->hf_file) continue; new_bindex = au_br_index(sb, p->hf_br->br_id); if (new_bindex == bindex) continue; if (new_bindex < 0) { au_set_h_fptr(file, bindex, NULL); continue; } /* swap two lower inode, and loop again */ q = fidir->fd_hfile + new_bindex; tmp = *q; *q = *p; *p = tmp; if (tmp.hf_file) { bindex--; p--; } } p = fidir->fd_hfile; if (!au_test_mmapped(file) && !d_unlinked(file->f_dentry)) { bend = au_sbend(sb); for (finfo->fi_btop = 0; finfo->fi_btop <= bend; finfo->fi_btop++, p++) if (p->hf_file) { if (p->hf_file->f_dentry && p->hf_file->f_dentry->d_inode) break; else au_hfput(p, file); } } else { bend = au_br_index(sb, brid); for (finfo->fi_btop = 0; finfo->fi_btop < bend; finfo->fi_btop++, p++) if (p->hf_file) au_hfput(p, file); bend = au_sbend(sb); } p = fidir->fd_hfile + bend; for (fidir->fd_bbot = bend; fidir->fd_bbot >= finfo->fi_btop; fidir->fd_bbot--, p--) if (p->hf_file) { if (p->hf_file->f_dentry && p->hf_file->f_dentry->d_inode) break; else au_hfput(p, file); } AuDebugOn(fidir->fd_bbot < finfo->fi_btop); }
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_ibstart(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_dbstart(a->dentry) || au_dbend(a->dentry) < a->mvd_bsrc || !au_h_dptr(a->dentry, a->mvd_bsrc)) || (a->mvd_bsrc < au_ibstart(a->inode) || au_ibend(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_sbend(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_sbend(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; }
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_flags(0); 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_path.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; }
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; }
err = PTR_ERR(seq); if (IS_ERR(seq)) goto out_unlock; name = (void *)attr->name; cattr = sysaufs_si_attrs; while (*cattr) { if (!strcmp(name, (*cattr)->name)) { err = container_of(*cattr, struct sysaufs_si_attr, attr) ->show(seq, sb); goto out_seq; } cattr++; } bend = au_sbend(sb); if (!strncmp(name, SysaufsBr_PREFIX, sizeof(SysaufsBr_PREFIX) - 1)) { name += sizeof(SysaufsBr_PREFIX) - 1; err = kstrtol(name, 10, &l); if (!err) { if (l <= bend) err = sysaufs_si_br(seq, sb, (aufs_bindex_t)l); else err = -ENOENT; } goto out_seq; } BUG(); out_seq: if (!err) {
/* called without aufs lock */ int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts) { int err, n, token; aufs_bindex_t bindex; unsigned char skipped; struct dentry *root; struct au_opt *opt, *opt_tail; char *opt_str; /* reduce the stack space */ union { struct au_opt_xino_itrunc *xino_itrunc; struct au_opt_wbr_create *create; } u; struct { substring_t args[MAX_OPT_ARGS]; } *a; err = -ENOMEM; a = kmalloc(sizeof(*a), GFP_NOFS); if (unlikely(!a)) goto out; root = sb->s_root; err = 0; bindex = 0; opt = opts->opt; opt_tail = opt + opts->max_opt - 1; opt->type = Opt_tail; while (!err && (opt_str = strsep(&str, ",")) && *opt_str) { err = -EINVAL; skipped = 0; token = match_token(opt_str, options, a->args); switch (token) { case Opt_br: err = 0; while (!err && (opt_str = strsep(&a->args[0].from, ":")) && *opt_str) { err = opt_add(opt, opt_str, opts->sb_flags, bindex++); if (unlikely(!err && ++opt > opt_tail)) { err = -E2BIG; break; } opt->type = Opt_tail; skipped = 1; } break; case Opt_add: if (unlikely(match_int(&a->args[0], &n))) { pr_err("bad integer in %s\n", opt_str); break; } bindex = n; err = opt_add(opt, a->args[1].from, opts->sb_flags, bindex); if (!err) opt->type = token; break; case Opt_append: err = opt_add(opt, a->args[0].from, opts->sb_flags, /*dummy bindex*/1); if (!err) opt->type = token; break; case Opt_prepend: err = opt_add(opt, a->args[0].from, opts->sb_flags, /*bindex*/0); if (!err) opt->type = token; break; case Opt_del: err = au_opts_parse_del(&opt->del, a->args); if (!err) opt->type = token; break; #if 0 /* reserved for future use */ case Opt_idel: del->pathname = "(indexed)"; if (unlikely(match_int(&args[0], &n))) { pr_err("bad integer in %s\n", opt_str); break; } err = au_opts_parse_idel(sb, n, &opt->del, a->args); if (!err) opt->type = token; break; #endif case Opt_mod: err = au_opts_parse_mod(&opt->mod, a->args); if (!err) opt->type = token; break; #ifdef IMOD /* reserved for future use */ case Opt_imod: u.mod->path = "(indexed)"; if (unlikely(match_int(&a->args[0], &n))) { pr_err("bad integer in %s\n", opt_str); break; } err = au_opts_parse_imod(sb, n, &opt->mod, a->args); if (!err) opt->type = token; break; #endif case Opt_xino: err = au_opts_parse_xino(sb, &opt->xino, a->args); if (!err) opt->type = token; break; case Opt_trunc_xino_path: err = au_opts_parse_xino_itrunc_path (sb, &opt->xino_itrunc, a->args); if (!err) opt->type = token; break; case Opt_itrunc_xino: u.xino_itrunc = &opt->xino_itrunc; if (unlikely(match_int(&a->args[0], &n))) { pr_err("bad integer in %s\n", opt_str); break; } u.xino_itrunc->bindex = n; aufs_read_lock(root, AuLock_FLUSH); if (n < 0 || au_sbend(sb) < n) { pr_err("out of bounds, %d\n", n); aufs_read_unlock(root, !AuLock_IR); break; } aufs_read_unlock(root, !AuLock_IR); err = 0; opt->type = token; break; case Opt_dirwh: if (unlikely(match_int(&a->args[0], &opt->dirwh))) break; err = 0; opt->type = token; break; case Opt_rdcache: if (unlikely(match_int(&a->args[0], &n))) { pr_err("bad integer in %s\n", opt_str); break; } if (unlikely(n > AUFS_RDCACHE_MAX)) { pr_err("rdcache must be smaller than %d\n", AUFS_RDCACHE_MAX); break; } opt->rdcache = n; err = 0; opt->type = token; break; case Opt_rdblk: if (unlikely(match_int(&a->args[0], &n) || n < 0 || n > KMALLOC_MAX_SIZE)) { pr_err("bad integer in %s\n", opt_str); break; } if (unlikely(n && n < NAME_MAX)) { pr_err("rdblk must be larger than %d\n", NAME_MAX); break; } opt->rdblk = n; err = 0; opt->type = token; break; case Opt_rdhash: if (unlikely(match_int(&a->args[0], &n) || n < 0 || n * sizeof(struct hlist_head) > KMALLOC_MAX_SIZE)) { pr_err("bad integer in %s\n", opt_str); break; } opt->rdhash = n; err = 0; opt->type = token; break; case Opt_trunc_xino: case Opt_notrunc_xino: case Opt_noxino: case Opt_trunc_xib: case Opt_notrunc_xib: case Opt_shwh: case Opt_noshwh: case Opt_plink: case Opt_noplink: case Opt_list_plink: case Opt_dio: case Opt_nodio: case Opt_diropq_a: case Opt_diropq_w: case Opt_warn_perm: case Opt_nowarn_perm: case Opt_refrof: case Opt_norefrof: case Opt_verbose: case Opt_noverbose: case Opt_sum: case Opt_nosum: case Opt_wsum: case Opt_rdblk_def: case Opt_rdhash_def: err = 0; opt->type = token; break; case Opt_udba: opt->udba = udba_val(a->args[0].from); if (opt->udba >= 0) { err = 0; opt->type = token; } else pr_err("wrong value, %s\n", opt_str); break; case Opt_wbr_create: u.create = &opt->wbr_create; u.create->wbr_create = au_wbr_create_val(a->args[0].from, u.create); if (u.create->wbr_create >= 0) { err = 0; opt->type = token; } else pr_err("wrong value, %s\n", opt_str); break; case Opt_wbr_copyup: opt->wbr_copyup = au_wbr_copyup_val(a->args[0].from); if (opt->wbr_copyup >= 0) { err = 0; opt->type = token; } else pr_err("wrong value, %s\n", opt_str); break; case Opt_ignore: pr_warn("ignored %s\n", opt_str); /*FALLTHROUGH*/ case Opt_ignore_silent: skipped = 1; err = 0; break; case Opt_err: pr_err("unknown option %s\n", opt_str); break; } if (!err && !skipped) { if (unlikely(++opt > opt_tail)) { err = -E2BIG; opt--; opt->type = Opt_tail; break; } opt->type = Opt_tail; } } kfree(a); dump_opts(opts); if (unlikely(err)) au_opts_free(opts); out: return err; }
int au_opts_mount(struct super_block *sb, struct au_opts *opts) { int err; unsigned int tmp; aufs_bindex_t bindex, bend; struct au_opt *opt; struct au_opt_xino *opt_xino, xino; struct au_sbinfo *sbinfo; struct au_branch *br; SiMustWriteLock(sb); err = 0; opt_xino = NULL; opt = opts->opt; while (err >= 0 && opt->type != Opt_tail) err = au_opt_simple(sb, opt++, opts); if (err > 0) err = 0; else if (unlikely(err < 0)) goto out; /* disable xino and udba temporary */ sbinfo = au_sbi(sb); tmp = sbinfo->si_mntflags; au_opt_clr(sbinfo->si_mntflags, XINO); au_opt_set_udba(sbinfo->si_mntflags, UDBA_REVAL); opt = opts->opt; while (err >= 0 && opt->type != Opt_tail) err = au_opt_br(sb, opt++, opts); if (err > 0) err = 0; else if (unlikely(err < 0)) goto out; bend = au_sbend(sb); if (unlikely(bend < 0)) { err = -EINVAL; pr_err("no branches\n"); goto out; } if (au_opt_test(tmp, XINO)) au_opt_set(sbinfo->si_mntflags, XINO); opt = opts->opt; while (!err && opt->type != Opt_tail) err = au_opt_xino(sb, opt++, &opt_xino, opts); if (unlikely(err)) goto out; err = au_opts_verify(sb, sb->s_flags, tmp); if (unlikely(err)) goto out; /* restore xino */ if (au_opt_test(tmp, XINO) && !opt_xino) { xino.file = au_xino_def(sb); err = PTR_ERR(xino.file); if (IS_ERR(xino.file)) goto out; err = au_xino_set(sb, &xino, /*remount*/0); fput(xino.file); if (unlikely(err)) goto out; } /* restore udba */ tmp &= AuOptMask_UDBA; sbinfo->si_mntflags &= ~AuOptMask_UDBA; sbinfo->si_mntflags |= tmp; bend = au_sbend(sb); for (bindex = 0; bindex <= bend; bindex++) { br = au_sbr(sb, bindex); err = au_hnotify_reset_br(tmp, br, br->br_perm); if (unlikely(err)) AuIOErr("hnotify failed on br %d, %d, ignored\n", bindex, err); /* go on even if err */ } if (au_opt_test(tmp, UDBA_HNOTIFY)) { struct inode *dir = sb->s_root->d_inode; au_hn_reset(dir, au_hi_flags(dir, /*isdir*/1) & ~AuHi_XINO); } out: return err; }