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

aufs: dirperm1 option



Sometimes the aufs policy to respect the branch fs's permission bits
makes users confused. IE. the direcotry permission bits on the top branch
allows users to read, but the lower branch prohibts. This option may be
useful for such case.
See also the document in this commit.
Signed-off-by: default avatarJ. R. Okajima <hooanon05g@gmail.com>
parent 6462d8eb
...@@ -17,6 +17,35 @@ For d_revalidate, aufs implements three levels of revalidate tests. See ...@@ -17,6 +17,35 @@ For d_revalidate, aufs implements three levels of revalidate tests. See
"Revalidate Dentry and UDBA" in detail. "Revalidate Dentry and UDBA" in detail.
Test Only the Highest One for the Directory Permission (dirperm1 option)
----------------------------------------------------------------------
Let's try case study.
- aufs has two branches, upper readwrite and lower readonly.
/au = /rw + /ro
- "dirA" exists under /ro, but /rw. and its mode is 0700.
- user invoked "chmod a+rx /au/dirA"
- the internal copy-up is activated and "/rw/dirA" is created and its
permission bits are set to world readable.
- then "/au/dirA" becomes world readable?
In this case, /ro/dirA is still 0700 since it exists in readonly branch,
or it may be a natively readonly filesystem. If aufs respects the lower
branch, it should not respond readdir request from other users. But user
allowed it by chmod. Should really aufs rejects showing the entries
under /ro/dirA?
To be honest, I don't have a good solution for this case. So aufs
implements 'dirperm1' and 'nodirperm1' mount options, and leave it to
users.
When dirperm1 is specified, aufs checks only the highest one for the
directory permission, and shows the entries. Otherwise, as usual, checks
every dir existing on all branches and rejects the request.
As a side effect, dirperm1 option improves the performance of aufs
because the number of permission check is reduced when the number of
branch is many.
Revalidate Dentry and UDBA (User's Direct Branch Access) Revalidate Dentry and UDBA (User's Direct Branch Access)
---------------------------------------------------------------------- ----------------------------------------------------------------------
Generally VFS helpers re-validate a dentry as a part of lookup. Generally VFS helpers re-validate a dentry as a part of lookup.
......
...@@ -104,7 +104,7 @@ int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop, ...@@ -104,7 +104,7 @@ int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop,
{ {
int npositive, err; int npositive, err;
aufs_bindex_t bindex, btail, bdiropq; aufs_bindex_t bindex, btail, bdiropq;
unsigned char isdir; unsigned char isdir, dirperm1;
struct au_do_lookup_args args = { struct au_do_lookup_args args = {
.flags = flags, .flags = flags,
.name = &dentry->d_name .name = &dentry->d_name
...@@ -118,6 +118,7 @@ int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop, ...@@ -118,6 +118,7 @@ int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop,
goto out; goto out;
isdir = !!d_is_dir(dentry); isdir = !!d_is_dir(dentry);
dirperm1 = !!au_opt_test(au_mntflags(sb), DIRPERM1);
npositive = 0; npositive = 0;
parent = dget_parent(dentry); parent = dget_parent(dentry);
...@@ -145,6 +146,8 @@ int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop, ...@@ -145,6 +146,8 @@ int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop,
goto out_parent; goto out_parent;
if (h_dentry) if (h_dentry)
au_fclr_lkup(args.flags, ALLOW_NEG); au_fclr_lkup(args.flags, ALLOW_NEG);
if (dirperm1)
au_fset_lkup(args.flags, IGNORE_PERM);
if (au_dbwh(dentry) == bindex) if (au_dbwh(dentry) == bindex)
break; break;
......
...@@ -666,6 +666,8 @@ int au_test_empty_lower(struct dentry *dentry) ...@@ -666,6 +666,8 @@ int au_test_empty_lower(struct dentry *dentry)
arg.whlist = &whlist; arg.whlist = &whlist;
btop = au_dbtop(dentry); btop = au_dbtop(dentry);
test_empty = do_test_empty; test_empty = do_test_empty;
if (au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1))
test_empty = sio_test_empty;
arg.bindex = btop; arg.bindex = btop;
err = test_empty(dentry, &arg); err = test_empty(dentry, &arg);
if (unlikely(err)) if (unlikely(err))
......
...@@ -100,7 +100,8 @@ static int aufs_permission(struct inode *inode, int mask) ...@@ -100,7 +100,8 @@ static int aufs_permission(struct inode *inode, int mask)
#endif #endif
if (!isdir if (!isdir
|| write_mask) { || write_mask
|| au_opt_test(au_mntflags(sb), DIRPERM1)) {
err = au_busy_or_stale(); err = au_busy_or_stale();
h_inode = au_h_iptr(inode, au_ibtop(inode)); h_inode = au_h_iptr(inode, au_ibtop(inode));
if (unlikely(!h_inode if (unlikely(!h_inode
......
...@@ -118,7 +118,9 @@ int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, ...@@ -118,7 +118,9 @@ int au_may_del(struct dentry *dentry, aufs_bindex_t bindex,
* let's try heavy test. * let's try heavy test.
*/ */
err = -EACCES; err = -EACCES;
if (unlikely(au_test_h_perm(d_inode(h_parent), MAY_EXEC | MAY_WRITE))) if (unlikely(!au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1)
&& au_test_h_perm(d_inode(h_parent),
MAY_EXEC | MAY_WRITE)))
goto out; goto out;
h_latest = au_sio_lkup_one(&dentry->d_name, h_parent); h_latest = au_sio_lkup_one(&dentry->d_name, h_parent);
......
...@@ -29,6 +29,7 @@ enum { ...@@ -29,6 +29,7 @@ enum {
Opt_dio, Opt_nodio, Opt_dio, Opt_nodio,
Opt_wbr_copyup, Opt_wbr_create, Opt_wbr_copyup, Opt_wbr_create,
Opt_verbose, Opt_noverbose, Opt_verbose, Opt_noverbose,
Opt_dirperm1, Opt_nodirperm1,
Opt_acl, Opt_noacl, Opt_acl, Opt_noacl,
Opt_tail, Opt_ignore, Opt_ignore_silent, Opt_err Opt_tail, Opt_ignore, Opt_ignore_silent, Opt_err
}; };
...@@ -81,6 +82,9 @@ static match_table_t options = { ...@@ -81,6 +82,9 @@ static match_table_t options = {
{Opt_dio, "dio"}, {Opt_dio, "dio"},
{Opt_nodio, "nodio"}, {Opt_nodio, "nodio"},
{Opt_dirperm1, "dirperm1"},
{Opt_nodirperm1, "nodirperm1"},
{Opt_verbose, "verbose"}, {Opt_verbose, "verbose"},
{Opt_verbose, "v"}, {Opt_verbose, "v"},
{Opt_noverbose, "noverbose"}, {Opt_noverbose, "noverbose"},
...@@ -553,6 +557,12 @@ static void dump_opts(struct au_opts *opts) ...@@ -553,6 +557,12 @@ static void dump_opts(struct au_opts *opts)
case Opt_notrunc_xib: case Opt_notrunc_xib:
AuLabel(notrunc_xib); AuLabel(notrunc_xib);
break; break;
case Opt_dirperm1:
AuLabel(dirperm1);
break;
case Opt_nodirperm1:
AuLabel(nodirperm1);
break;
case Opt_plink: case Opt_plink:
AuLabel(plink); AuLabel(plink);
break; break;
...@@ -1028,6 +1038,8 @@ int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts) ...@@ -1028,6 +1038,8 @@ int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts)
case Opt_noxino: case Opt_noxino:
case Opt_trunc_xib: case Opt_trunc_xib:
case Opt_notrunc_xib: case Opt_notrunc_xib:
case Opt_dirperm1:
case Opt_nodirperm1:
case Opt_plink: case Opt_plink:
case Opt_noplink: case Opt_noplink:
case Opt_list_plink: case Opt_list_plink:
...@@ -1221,6 +1233,13 @@ static int au_opt_simple(struct super_block *sb, struct au_opt *opt, ...@@ -1221,6 +1233,13 @@ static int au_opt_simple(struct super_block *sb, struct au_opt *opt,
sbinfo->si_rdhash = AUFS_RDHASH_DEF; sbinfo->si_rdhash = AUFS_RDHASH_DEF;
break; break;
case Opt_dirperm1:
au_opt_set(sbinfo->si_mntflags, DIRPERM1);
break;
case Opt_nodirperm1:
au_opt_clr(sbinfo->si_mntflags, DIRPERM1);
break;
case Opt_trunc_xino: case Opt_trunc_xino:
au_opt_set(sbinfo->si_mntflags, TRUNC_XINO); au_opt_set(sbinfo->si_mntflags, TRUNC_XINO);
break; break;
...@@ -1363,6 +1382,14 @@ int au_opts_verify(struct super_block *sb, unsigned long sb_flags, ...@@ -1363,6 +1382,14 @@ int au_opts_verify(struct super_block *sb, unsigned long sb_flags,
pr_warn("first branch should be rw\n"); pr_warn("first branch should be rw\n");
} }
if (au_opt_test((sbinfo->si_mntflags | pending), UDBA_HNOTIFY)
&& !au_opt_test(sbinfo->si_mntflags, XINO))
pr_warn_once("udba=*notify requires xino\n");
if (au_opt_test(sbinfo->si_mntflags, DIRPERM1))
pr_warn_once("dirperm1 breaks the protection"
" by the permission bits on the lower branch\n");
err = 0; err = 0;
root = sb->s_root; root = sb->s_root;
dir = d_inode(root); dir = d_inode(root);
......
...@@ -26,6 +26,8 @@ struct file; ...@@ -26,6 +26,8 @@ struct file;
#define AuOpt_UDBA_REVAL (1 << 3) #define AuOpt_UDBA_REVAL (1 << 3)
#define AuOpt_UDBA_HNOTIFY (1 << 4) #define AuOpt_UDBA_HNOTIFY (1 << 4)
#define AuOpt_PLINK (1 << 6) /* pseudo-link */ #define AuOpt_PLINK (1 << 6) /* pseudo-link */
#define AuOpt_DIRPERM1 (1 << 7) /* ignore the lower dir's perm
bits */
#define AuOpt_VERBOSE (1 << 13) /* print the cause of error */ #define AuOpt_VERBOSE (1 << 13) /* print the cause of error */
#define AuOpt_DIO (1 << 14) /* direct io */ #define AuOpt_DIO (1 << 14) /* direct io */
...@@ -36,7 +38,9 @@ struct file; ...@@ -36,7 +38,9 @@ struct file;
#define AuOpt_Def (AuOpt_XINO \ #define AuOpt_Def (AuOpt_XINO \
| AuOpt_UDBA_REVAL \ | AuOpt_UDBA_REVAL \
| AuOpt_PLINK) | AuOpt_PLINK \
/* | AuOpt_DIRPERM1 */ \
)
#define AuOptMask_UDBA (AuOpt_UDBA_NONE \ #define AuOptMask_UDBA (AuOpt_UDBA_NONE \
| AuOpt_UDBA_REVAL \ | AuOpt_UDBA_REVAL \
| AuOpt_UDBA_HNOTIFY) | AuOpt_UDBA_HNOTIFY)
......
...@@ -258,6 +258,7 @@ static int aufs_show_options(struct seq_file *m, struct dentry *dentry) ...@@ -258,6 +258,7 @@ static int aufs_show_options(struct seq_file *m, struct dentry *dentry)
AuStr(UDBA, udba); AuStr(UDBA, udba);
AuBool(PLINK, plink); AuBool(PLINK, plink);
AuBool(DIO, dio); AuBool(DIO, dio);
AuBool(DIRPERM1, dirperm1);
v = sbinfo->si_wbr_create; v = sbinfo->si_wbr_create;
if (v != AuWbrCreate_Def) if (v != AuWbrCreate_Def)
......
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