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 ...@@ -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 If any file on the deleting branch is opened by aufs, then aufs
rejects deleting. 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: ...@@ -1055,3 +1055,199 @@ out:
au_farray_free(to_free, opened); au_farray_free(to_free, opened);
return err; 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; ...@@ -180,6 +180,9 @@ struct au_opt_add;
int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount); int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount);
struct au_opt_del; struct au_opt_del;
int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount); 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 */ /* xino.c */
static const loff_t au_loff_max = LLONG_MAX; static const loff_t au_loff_max = LLONG_MAX;
......
...@@ -16,8 +16,8 @@ ...@@ -16,8 +16,8 @@
enum { enum {
Opt_br, Opt_br,
Opt_add, Opt_del, Opt_append, Opt_prepend, Opt_add, Opt_del, Opt_mod, Opt_append, Opt_prepend,
Opt_idel, Opt_idel, Opt_imod,
Opt_rdcache, Opt_rdblk, Opt_rdhash, Opt_rdcache, Opt_rdblk, Opt_rdhash,
Opt_rdblk_def, Opt_rdhash_def, Opt_rdblk_def, Opt_rdhash_def,
Opt_xino, Opt_noxino, Opt_xino, Opt_noxino,
...@@ -49,6 +49,9 @@ static match_table_t options = { ...@@ -49,6 +49,9 @@ static match_table_t options = {
{Opt_del, "del=%s"}, {Opt_del, "del=%s"},
{Opt_del, "del:%s"}, {Opt_del, "del:%s"},
/* {Opt_idel, "idel:%d"}, */ /* {Opt_idel, "idel:%d"}, */
{Opt_mod, "mod=%s"},
{Opt_mod, "mod:%s"},
/* {Opt_imod, "imod:%d:%s"}, */
{Opt_xino, "xino=%s"}, {Opt_xino, "xino=%s"},
{Opt_noxino, "noxino"}, {Opt_noxino, "noxino"},
...@@ -471,6 +474,7 @@ static void dump_opts(struct au_opts *opts) ...@@ -471,6 +474,7 @@ static void dump_opts(struct au_opts *opts)
union { union {
struct au_opt_add *add; struct au_opt_add *add;
struct au_opt_del *del; struct au_opt_del *del;
struct au_opt_mod *mod;
struct au_opt_xino *xino; struct au_opt_xino *xino;
struct au_opt_xino_itrunc *xino_itrunc; struct au_opt_xino_itrunc *xino_itrunc;
struct au_opt_wbr_create *create; struct au_opt_wbr_create *create;
...@@ -492,6 +496,12 @@ static void dump_opts(struct au_opts *opts) ...@@ -492,6 +496,12 @@ static void dump_opts(struct au_opts *opts)
AuDbg("del {%s, %p}\n", AuDbg("del {%s, %p}\n",
u.del->pathname, u.del->h_path.dentry); u.del->pathname, u.del->h_path.dentry);
break; 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: case Opt_append:
u.add = &opt->add; u.add = &opt->add;
AuDbg("append {b%d, %s, 0x%x, %p}\n", AuDbg("append {b%d, %s, 0x%x, %p}\n",
...@@ -625,6 +635,10 @@ void au_opts_free(struct au_opts *opts) ...@@ -625,6 +635,10 @@ void au_opts_free(struct au_opts *opts)
case Opt_idel: case Opt_idel:
path_put(&opt->del.h_path); path_put(&opt->del.h_path);
break; break;
case Opt_mod:
case Opt_imod:
dput(opt->mod.h_root);
break;
case Opt_xino: case Opt_xino:
fput(opt->xino.file); fput(opt->xino.file);
break; break;
...@@ -708,6 +722,64 @@ out: ...@@ -708,6 +722,64 @@ out:
} }
#endif #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, static int au_opts_parse_xino(struct super_block *sb, struct au_opt_xino *xino,
substring_t args[]) substring_t args[])
{ {
...@@ -859,6 +931,23 @@ int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts) ...@@ -859,6 +931,23 @@ int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts)
if (!err) if (!err)
opt->type = token; opt->type = token;
break; 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 #endif
case Opt_xino: case Opt_xino:
err = au_opts_parse_xino(sb, &opt->xino, a->args); 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, ...@@ -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, static int au_opt_br(struct super_block *sb, struct au_opt *opt,
struct au_opts *opts) struct au_opts *opts)
{ {
int err; int err, do_refresh;
err = 0; err = 0;
switch (opt->type) { switch (opt->type) {
...@@ -1209,6 +1298,18 @@ static int au_opt_br(struct super_block *sb, struct au_opt *opt, ...@@ -1209,6 +1298,18 @@ static int au_opt_br(struct super_block *sb, struct au_opt *opt,
au_fset_opts(opts->flags, REFRESH); au_fset_opts(opts->flags, REFRESH);
} }
break; 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; return err;
} }
......
...@@ -105,6 +105,12 @@ struct au_opt_del { ...@@ -105,6 +105,12 @@ struct au_opt_del {
struct path h_path; struct path h_path;
}; };
struct au_opt_mod {
char *path;
int perm;
struct dentry *h_root;
};
struct au_opt_xino { struct au_opt_xino {
char *path; char *path;
struct file *file; struct file *file;
...@@ -127,6 +133,7 @@ struct au_opt { ...@@ -127,6 +133,7 @@ struct au_opt {
struct au_opt_xino_itrunc xino_itrunc; struct au_opt_xino_itrunc xino_itrunc;
struct au_opt_add add; struct au_opt_add add;
struct au_opt_del del; struct au_opt_del del;
struct au_opt_mod mod;
int rdcache; int rdcache;
unsigned int rdblk; unsigned int rdblk;
unsigned int rdhash; unsigned int rdhash;
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
/* copied from linux/fs/internal.h */ /* copied from linux/fs/internal.h */
/* todo: BAD approach!! */ /* todo: BAD approach!! */
extern void __mnt_drop_write(struct vfsmount *);
extern struct file *alloc_empty_file(int, const struct cred *); 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