Commit 6462d8eb authored by J. R. Okajima's avatar J. R. Okajima
Browse files

aufs: branch management, modify the permission and attribute



The permissions and attributes of a branch can be modified dynamically.
See also the document in this commit.
Signed-off-by: default avatarJ. R. Okajima <hooanon05g@gmail.com>
parent dea1d77c
......@@ -47,3 +47,15 @@ o Confirm the deleting branch is not busy
If any file on the deleting branch is opened by aufs, then aufs
rejects deleting.
Modify the Permission of a Branch
----------------------------------------------------------------------
o Re-initialize or remove the xino file and whiteout bases if necessary.
See struct.txt.
o rw --> ro: Confirm the modifying branch is not busy
Aufs rejects the request if any of these conditions are true.
- a file on the branch is mmap-ed.
- a regular file on the branch is opened for write and there is no
same named entry on the upper branch.
......@@ -1055,3 +1055,199 @@ out:
au_farray_free(to_free, opened);
return err;
}
/* ---------------------------------------------------------------------- */
/*
* change a branch permission
*/
static void au_warn_ima(void)
{
#ifdef CONFIG_IMA
/* since it doesn't support mark_files_ro() */
AuWarn1("RW -> RO makes IMA to produce wrong message\n");
#endif
}
static int do_need_sigen_inc(int a, int b)
{
return au_br_whable(a) && !au_br_whable(b);
}
static int need_sigen_inc(int old, int new)
{
return do_need_sigen_inc(old, new)
|| do_need_sigen_inc(new, old);
}
static int au_br_mod_files_ro(struct super_block *sb, aufs_bindex_t bindex)
{
int err, do_warn;
unsigned int mnt_flags;
unsigned long long ull, max;
aufs_bindex_t br_id;
unsigned char verbose, writer;
struct file *file, *hf, **array;
struct au_hfile *hfile;
mnt_flags = au_mntflags(sb);
verbose = !!au_opt_test(mnt_flags, VERBOSE);
array = au_farray_alloc(sb, &max);
err = PTR_ERR(array);
if (IS_ERR(array))
goto out;
do_warn = 0;
br_id = au_sbr_id(sb, bindex);
for (ull = 0; ull < max; ull++) {
file = array[ull];
if (unlikely(!file))
break;
/* AuDbg("%pD\n", file); */
fi_read_lock(file);
if (unlikely(au_test_mmapped(file))) {
err = -EBUSY;
AuVerbose(verbose, "mmapped %pD\n", file);
AuDbgFile(file);
FiMustNoWaiters(file);
fi_read_unlock(file);
goto out_array;
}
hfile = &au_fi(file)->fi_htop;
hf = hfile->hf_file;
if (!d_is_reg(file->f_path.dentry)
|| !(file->f_mode & FMODE_WRITE)
|| hfile->hf_br->br_id != br_id
|| !(hf->f_mode & FMODE_WRITE))
array[ull] = NULL;
else {
do_warn = 1;
get_file(file);
}
FiMustNoWaiters(file);
fi_read_unlock(file);
fput(file);
}
err = 0;
if (do_warn)
au_warn_ima();
for (ull = 0; ull < max; ull++) {
file = array[ull];
if (!file)
continue;
/* todo: already flushed? */
/*
* fs/super.c:mark_files_ro() is gone, but aufs keeps its
* approach which resets f_mode and calls mnt_drop_write() and
* file_release_write() for each file, because the branch
* attribute in aufs world is totally different from the native
* fs rw/ro mode.
*/
/* fi_read_lock(file); */
hfile = &au_fi(file)->fi_htop;
hf = hfile->hf_file;
/* fi_read_unlock(file); */
spin_lock(&hf->f_lock);
writer = !!(hf->f_mode & FMODE_WRITER);
hf->f_mode &= ~(FMODE_WRITE | FMODE_WRITER);
spin_unlock(&hf->f_lock);
if (writer) {
put_write_access(file_inode(hf));
__mnt_drop_write(hf->f_path.mnt);
}
}
out_array:
au_farray_free(array, max);
out:
AuTraceErr(err);
return err;
}
int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount,
int *do_refresh)
{
int err, rerr;
aufs_bindex_t bindex;
struct dentry *root;
struct au_branch *br;
root = sb->s_root;
bindex = au_find_dbindex(root, mod->h_root);
if (bindex < 0) {
if (remount)
return 0; /* success */
err = -ENOENT;
pr_err("%s no such branch\n", mod->path);
goto out;
}
AuDbg("bindex b%d\n", bindex);
err = test_br(d_inode(mod->h_root), mod->perm, mod->path);
if (unlikely(err))
goto out;
br = au_sbr(sb, bindex);
AuDebugOn(mod->h_root != au_br_dentry(br));
if (br->br_perm == mod->perm)
return 0; /* success */
if (au_br_writable(br->br_perm)) {
/* remove whiteout base */
err = au_br_init_wh(sb, br, mod->perm);
if (unlikely(err))
goto out;
if (!au_br_writable(mod->perm)) {
/* rw --> ro, file might be mmapped */
DiMustNoWaiters(root);
IiMustNoWaiters(d_inode(root));
di_write_unlock(root);
err = au_br_mod_files_ro(sb, bindex);
/* aufs_write_lock() calls ..._child() */
di_write_lock_child(root);
if (unlikely(err)) {
rerr = -ENOMEM;
br->br_wbr = kzalloc(sizeof(*br->br_wbr),
GFP_NOFS);
if (br->br_wbr)
rerr = au_wbr_init(br, sb, br->br_perm);
if (unlikely(rerr)) {
AuIOErr("nested error %d (%d)\n",
rerr, err);
br->br_perm = mod->perm;
}
}
}
} else if (au_br_writable(mod->perm)) {
/* ro --> rw */
err = -ENOMEM;
br->br_wbr = kzalloc(sizeof(*br->br_wbr), GFP_NOFS);
if (br->br_wbr) {
err = au_wbr_init(br, sb, mod->perm);
if (unlikely(err)) {
au_kfree_rcu(br->br_wbr);
br->br_wbr = NULL;
}
}
}
if (unlikely(err))
goto out;
*do_refresh |= need_sigen_inc(br->br_perm, mod->perm);
br->br_perm = mod->perm;
goto out; /* success */
out:
AuTraceErr(err);
return err;
}
......@@ -180,6 +180,9 @@ struct au_opt_add;
int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount);
struct au_opt_del;
int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount);
struct au_opt_mod;
int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount,
int *do_refresh);
/* xino.c */
static const loff_t au_loff_max = LLONG_MAX;
......
......@@ -16,8 +16,8 @@
enum {
Opt_br,
Opt_add, Opt_del, Opt_append, Opt_prepend,
Opt_idel,
Opt_add, Opt_del, Opt_mod, Opt_append, Opt_prepend,
Opt_idel, Opt_imod,
Opt_rdcache, Opt_rdblk, Opt_rdhash,
Opt_rdblk_def, Opt_rdhash_def,
Opt_xino, Opt_noxino,
......@@ -49,6 +49,9 @@ static match_table_t options = {
{Opt_del, "del=%s"},
{Opt_del, "del:%s"},
/* {Opt_idel, "idel:%d"}, */
{Opt_mod, "mod=%s"},
{Opt_mod, "mod:%s"},
/* {Opt_imod, "imod:%d:%s"}, */
{Opt_xino, "xino=%s"},
{Opt_noxino, "noxino"},
......@@ -471,6 +474,7 @@ static void dump_opts(struct au_opts *opts)
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;
......@@ -492,6 +496,12 @@ static void dump_opts(struct au_opts *opts)
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",
......@@ -625,6 +635,10 @@ void au_opts_free(struct au_opts *opts)
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;
......@@ -708,6 +722,64 @@ out:
}
#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 permission %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_sbbot(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;
}
#endif
static int au_opts_parse_xino(struct super_block *sb, struct au_opt_xino *xino,
substring_t args[])
{
......@@ -859,6 +931,23 @@ int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts)
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);
......@@ -1178,7 +1267,7 @@ static int au_opt_simple(struct super_block *sb, struct au_opt *opt,
static int au_opt_br(struct super_block *sb, struct au_opt *opt,
struct au_opts *opts)
{
int err;
int err, do_refresh;
err = 0;
switch (opt->type) {
......@@ -1209,6 +1298,18 @@ static int au_opt_br(struct super_block *sb, struct au_opt *opt,
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;
}
......
......@@ -105,6 +105,12 @@ struct au_opt_del {
struct path h_path;
};
struct au_opt_mod {
char *path;
int perm;
struct dentry *h_root;
};
struct au_opt_xino {
char *path;
struct file *file;
......@@ -127,6 +133,7 @@ struct au_opt {
struct au_opt_xino_itrunc xino_itrunc;
struct au_opt_add add;
struct au_opt_del del;
struct au_opt_mod mod;
int rdcache;
unsigned int rdblk;
unsigned int rdhash;
......
......@@ -20,6 +20,7 @@
/* copied from linux/fs/internal.h */
/* todo: BAD approach!! */
extern void __mnt_drop_write(struct vfsmount *);
extern struct file *alloc_empty_file(int, const struct cred *);
/* ---------------------------------------------------------------------- */
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment