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

aufs: writable branch 2/3, body



Actually prepare the whiteout bases on the adding writable branch.
For details, refer to previous commit.
Signed-off-by: default avatarJ. R. Okajima <hooanon05g@gmail.com>
parent 72a970fc
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
*/ */
#include <linux/file.h> #include <linux/file.h>
#include <linux/statfs.h>
#include "aufs.h" #include "aufs.h"
/* /*
...@@ -15,16 +16,28 @@ ...@@ -15,16 +16,28 @@
*/ */
static void au_br_do_free(struct au_branch *br) static void au_br_do_free(struct au_branch *br)
{ {
int i;
struct au_wbr *wbr;
au_xino_put(br); au_xino_put(br);
AuLCntZero(au_lcnt_read(&br->br_count, /*do_rev*/0)); AuLCntZero(au_lcnt_read(&br->br_count, /*do_rev*/0));
au_lcnt_fin(&br->br_count, /*do_sync*/0); au_lcnt_fin(&br->br_count, /*do_sync*/0);
wbr = br->br_wbr;
if (wbr) {
for (i = 0; i < AuBrWh_Last; i++)
dput(wbr->wbr_wh[i]);
AuDebugOn(atomic_read(&wbr->wbr_wh_running));
AuRwDestroy(&wbr->wbr_wh_rwsem);
}
/* recursive lock, s_umount of branch's */ /* recursive lock, s_umount of branch's */
/* synchronize_rcu(); */ /* why? */ /* synchronize_rcu(); */ /* why? */
lockdep_off(); lockdep_off();
path_put(&br->br_path); path_put(&br->br_path);
lockdep_on(); lockdep_on();
au_kfree_rcu(wbr);
au_lcnt_wait_for_fin(&br->br_count); au_lcnt_wait_for_fin(&br->br_count);
/* I don't know why, but percpu_refcount requires this */ /* I don't know why, but percpu_refcount requires this */
/* synchronize_rcu(); */ /* synchronize_rcu(); */
...@@ -98,6 +111,14 @@ static struct au_branch *au_br_alloc(struct super_block *sb, int new_nbranch, ...@@ -98,6 +111,14 @@ static struct au_branch *au_br_alloc(struct super_block *sb, int new_nbranch,
if (unlikely(!add_branch->br_xino)) if (unlikely(!add_branch->br_xino))
goto out_br; goto out_br;
if (au_br_writable(perm)) {
/* may be freed separately at changing the branch permission */
add_branch->br_wbr = kzalloc(sizeof(*add_branch->br_wbr),
GFP_NOFS);
if (unlikely(!add_branch->br_wbr))
goto out_xino;
}
root = sb->s_root; root = sb->s_root;
err = au_sbr_realloc(au_sbi(sb), new_nbranch, /*may_shrink*/0); err = au_sbr_realloc(au_sbi(sb), new_nbranch, /*may_shrink*/0);
if (!err) if (!err)
...@@ -110,14 +131,34 @@ static struct au_branch *au_br_alloc(struct super_block *sb, int new_nbranch, ...@@ -110,14 +131,34 @@ static struct au_branch *au_br_alloc(struct super_block *sb, int new_nbranch,
if (!err) if (!err)
return add_branch; /* success */ return add_branch; /* success */
au_xino_put(add_branch); au_kfree_rcu(add_branch->br_wbr);
out_xino:
au_xino_put(add_branch);
out_br: out_br:
au_kfree_rcu(add_branch); au_kfree_rcu(add_branch);
out: out:
return ERR_PTR(err); return ERR_PTR(err);
} }
/*
* test if the branch permission is legal or not.
*/
static int test_br(struct inode *inode, int brperm, char *path)
{
int err;
err = (au_br_writable(brperm) && IS_RDONLY(inode));
if (!err)
goto out;
err = -EINVAL;
pr_err("write permission for readonly mount or inode, %s\n", path);
out:
return err;
}
/* /*
* returns: * returns:
* 0: success, the caller will add it * 0: success, the caller will add it
...@@ -178,6 +219,10 @@ static int test_add(struct super_block *sb, struct au_opt_add *add) ...@@ -178,6 +219,10 @@ static int test_add(struct super_block *sb, struct au_opt_add *add)
goto out; goto out;
} }
err = test_br(d_inode(add->path.dentry), add->perm, add->pathname);
if (unlikely(err))
goto out;
if (bbot < 0) if (bbot < 0)
return 0; /* success */ return 0; /* success */
...@@ -195,6 +240,90 @@ out: ...@@ -195,6 +240,90 @@ out:
return err; return err;
} }
/*
* initialize or clean the whiteouts for an adding branch
*/
static int au_br_init_wh(struct super_block *sb, struct au_branch *br,
int new_perm)
{
int err, old_perm;
aufs_bindex_t bindex;
struct inode *h_inode;
struct au_wbr *wbr;
struct au_hinode *hdir;
struct dentry *h_dentry;
err = vfsub_mnt_want_write(au_br_mnt(br));
if (unlikely(err))
goto out;
wbr = br->br_wbr;
old_perm = br->br_perm;
br->br_perm = new_perm;
hdir = NULL;
h_inode = NULL;
bindex = au_br_index(sb, br->br_id);
if (0 <= bindex) {
hdir = au_hi(d_inode(sb->s_root), bindex);
inode_lock_nested(hdir->hi_inode, AuLsc_I_PARENT);
} else {
h_dentry = au_br_dentry(br);
h_inode = d_inode(h_dentry);
inode_lock_nested(h_inode, AuLsc_I_PARENT);
}
if (!wbr)
err = au_wh_init(br, sb);
else {
wbr_wh_write_lock(wbr);
err = au_wh_init(br, sb);
wbr_wh_write_unlock(wbr);
}
if (hdir)
inode_unlock(hdir->hi_inode);
else
inode_unlock(h_inode);
vfsub_mnt_drop_write(au_br_mnt(br));
br->br_perm = old_perm;
if (!err && wbr && !au_br_writable(new_perm)) {
au_kfree_rcu(wbr);
br->br_wbr = NULL;
}
out:
return err;
}
static int au_wbr_init(struct au_branch *br, struct super_block *sb,
int perm)
{
int err;
struct kstatfs kst;
struct au_wbr *wbr;
wbr = br->br_wbr;
au_rw_init(&wbr->wbr_wh_rwsem);
atomic_set(&wbr->wbr_wh_running, 0);
/*
* a limit for rmdir/rename a dir
* cf. AUFS_MAX_NAMELEN in include/uapi/linux/aufs_type.h
*/
err = vfs_statfs(&br->br_path, &kst);
if (unlikely(err))
goto out;
err = -EINVAL;
if (kst.f_namelen >= NAME_MAX)
err = au_br_init_wh(sb, br, perm);
else
pr_err("%pd(%s), unsupported namelen %ld\n",
au_br_dentry(br),
au_sbtype(au_br_dentry(br)->d_sb), kst.f_namelen);
out:
return err;
}
/* initialize a new branch */ /* initialize a new branch */
static int au_br_init(struct au_branch *br, struct super_block *sb, static int au_br_init(struct au_branch *br, struct super_block *sb,
struct au_opt_add *add) struct au_opt_add *add)
...@@ -211,6 +340,12 @@ static int au_br_init(struct au_branch *br, struct super_block *sb, ...@@ -211,6 +340,12 @@ static int au_br_init(struct au_branch *br, struct super_block *sb,
br->br_id = au_new_br_id(sb); br->br_id = au_new_br_id(sb);
AuDebugOn(br->br_id < 0); AuDebugOn(br->br_id < 0);
if (au_br_writable(add->perm)) {
err = au_wbr_init(br, sb, add->perm);
if (unlikely(err))
goto out_err;
}
if (au_opt_test(au_mntflags(sb), XINO)) { if (au_opt_test(au_mntflags(sb), XINO)) {
brbase = au_sbr(sb, 0); brbase = au_sbr(sb, 0);
xf = au_xino_file(brbase->br_xino, /*idx*/-1); xf = au_xino_file(brbase->br_xino, /*idx*/-1);
......
...@@ -161,10 +161,10 @@ static int do_pri_br(aufs_bindex_t bindex, struct au_branch *br) ...@@ -161,10 +161,10 @@ static int do_pri_br(aufs_bindex_t bindex, struct au_branch *br)
if (!sb || IS_ERR(sb)) if (!sb || IS_ERR(sb))
goto out; goto out;
dpri("s%d: {perm 0x%x, id %d}, " dpri("s%d: {perm 0x%x, id %d, wbr %p}, "
"%s, dev 0x%02x%02x, flags 0x%lx, cnt %d, active %d, " "%s, dev 0x%02x%02x, flags 0x%lx, cnt %d, active %d, "
"xino %d\n", "xino %d\n",
bindex, br->br_perm, br->br_id, bindex, br->br_perm, br->br_id, br->br_wbr,
au_sbtype(sb), MAJOR(sb->s_dev), MINOR(sb->s_dev), au_sbtype(sb), MAJOR(sb->s_dev), MINOR(sb->s_dev),
sb->s_flags, sb->s_count, sb->s_flags, sb->s_count,
atomic_read(&sb->s_active), atomic_read(&sb->s_active),
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
* inode functions * inode functions
*/ */
#include <linux/cred.h>
#include "aufs.h" #include "aufs.h"
struct inode *au_igrab(struct inode *inode) struct inode *au_igrab(struct inode *inode)
...@@ -17,3 +18,19 @@ struct inode *au_igrab(struct inode *inode) ...@@ -17,3 +18,19 @@ struct inode *au_igrab(struct inode *inode)
} }
return inode; return inode;
} }
int au_test_h_perm(struct inode *h_inode, int mask)
{
if (uid_eq(current_fsuid(), GLOBAL_ROOT_UID))
return 0;
return inode_permission(h_inode, mask);
}
int au_test_h_perm_sio(struct inode *h_inode, int mask)
{
if (au_test_nfs(h_inode->i_sb)
&& (mask & MAY_WRITE)
&& S_ISDIR(h_inode->i_mode))
mask |= MAY_READ; /* force permission check */
return au_test_h_perm(h_inode, mask);
}
...@@ -54,6 +54,8 @@ static inline struct au_iinfo *au_ii(struct inode *inode) ...@@ -54,6 +54,8 @@ static inline struct au_iinfo *au_ii(struct inode *inode)
/* inode.c */ /* inode.c */
struct inode *au_igrab(struct inode *inode); struct inode *au_igrab(struct inode *inode);
int au_test_h_perm(struct inode *h_inode, int mask);
int au_test_h_perm_sio(struct inode *h_inode, int mask);
/* iinfo.c */ /* iinfo.c */
struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex); struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex);
......
...@@ -90,6 +90,7 @@ out: ...@@ -90,6 +90,7 @@ out:
static match_table_t brperm = { static match_table_t brperm = {
{AuBrPerm_RO, AUFS_BRPERM_RO}, {AuBrPerm_RO, AUFS_BRPERM_RO},
{AuBrPerm_RW, AUFS_BRPERM_RW},
/* add more later */ /* add more later */
{0, NULL} {0, NULL}
}; };
...@@ -97,6 +98,10 @@ static match_table_t brperm = { ...@@ -97,6 +98,10 @@ static match_table_t brperm = {
static match_table_t brattr = { static match_table_t brattr = {
/* ro/rr branch */ /* ro/rr branch */
{AuBrRAttr_WH, AUFS_BRRATTR_WH}, {AuBrRAttr_WH, AUFS_BRRATTR_WH},
/* rw branch */
{AuBrWAttr_NoLinkWH, AUFS_BRWATTR_NLWH},
/* add more later */ /* add more later */
{0, NULL} {0, NULL}
}; };
...@@ -159,9 +164,10 @@ out: ...@@ -159,9 +164,10 @@ out:
static int noinline_for_stack br_perm_val(char *perm) static int noinline_for_stack br_perm_val(char *perm)
{ {
int val; int val, bad, sz;
char *p; char *p;
substring_t args[MAX_OPT_ARGS]; substring_t args[MAX_OPT_ARGS];
au_br_perm_str_t attr;
p = strchr(perm, '+'); p = strchr(perm, '+');
if (p) if (p)
...@@ -179,6 +185,23 @@ static int noinline_for_stack br_perm_val(char *perm) ...@@ -179,6 +185,23 @@ static int noinline_for_stack br_perm_val(char *perm)
val |= br_attr_val(p + 1, brattr, args); val |= br_attr_val(p + 1, brattr, args);
bad = 0;
switch (val & AuBrPerm_Mask) {
case AuBrPerm_RO:
bad = val & AuBrWAttr_Mask;
val &= ~AuBrWAttr_Mask;
break;
case AuBrPerm_RW:
bad = val & AuBrRAttr_Mask;
val &= ~AuBrRAttr_Mask;
break;
}
if (unlikely(bad)) {
sz = au_do_optstr_br_attr(&attr, bad);
AuDebugOn(!sz);
pr_warn("ignored branch attribute %s\n", attr.a);
}
out: out:
return val; return val;
} }
...@@ -308,9 +331,11 @@ static int opt_add(struct au_opt *opt, char *opt_str, unsigned long sb_flags, ...@@ -308,9 +331,11 @@ static int opt_add(struct au_opt *opt, char *opt_str, unsigned long sb_flags,
err = vfsub_kern_path(add->pathname, lkup_dirflags, &add->path); err = vfsub_kern_path(add->pathname, lkup_dirflags, &add->path);
if (!err) { if (!err) {
if (!p) if (!p) {
add->perm = AuBrPerm_RO; add->perm = AuBrPerm_RO;
/* re-commit later */ if (!bindex && !(sb_flags & SB_RDONLY))
add->perm = AuBrPerm_RW;
}
opt->type = Opt_add; opt->type = Opt_add;
goto out; goto out;
} }
...@@ -633,6 +658,92 @@ static int au_opt_xino(struct super_block *sb, struct au_opt *opt, ...@@ -633,6 +658,92 @@ static int au_opt_xino(struct super_block *sb, struct au_opt *opt,
return err; return err;
} }
int au_opts_verify(struct super_block *sb, unsigned long sb_flags,
unsigned int pending)
{
int err;
aufs_bindex_t bindex, bbot;
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);
if (!(sb_flags & SB_RDONLY)) {
if (unlikely(!au_br_writable(au_sbr_perm(sb, 0))))
pr_warn("first branch should be rw\n");
}
err = 0;
root = sb->s_root;
dir = d_inode(root);
do_plink = !!au_opt_test(sbinfo->si_mntflags, PLINK);
bbot = au_sbbot(sb);
for (bindex = 0; !err && bindex <= bbot; 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);
inode_lock_nested(hdir->hi_inode, AuLsc_I_PARENT);
if (wbr)
wbr_wh_write_lock(wbr);
err = au_wh_init(br, sb);
if (wbr)
wbr_wh_write_unlock(wbr);
inode_unlock(hdir->hi_inode);
if (!err && do_free) {
au_kfree_rcu(wbr);
br->br_wbr = NULL;
}
}
return err;
}
int au_opts_mount(struct super_block *sb, struct au_opts *opts) int au_opts_mount(struct super_block *sb, struct au_opts *opts)
{ {
int err; int err;
...@@ -682,6 +793,10 @@ int au_opts_mount(struct super_block *sb, struct au_opts *opts) ...@@ -682,6 +793,10 @@ int au_opts_mount(struct super_block *sb, struct au_opts *opts)
if (unlikely(err)) if (unlikely(err))
goto out; goto out;
err = au_opts_verify(sb, sb->s_flags, tmp);
if (unlikely(err))
goto out;
/* restore xino */ /* restore xino */
if (au_opt_test(tmp, XINO) && !opt_xino) { if (au_opt_test(tmp, XINO) && !opt_xino) {
xino.file = au_xino_def(sb); xino.file = au_xino_def(sb);
......
...@@ -96,6 +96,8 @@ void au_optstr_br_perm(au_br_perm_str_t *str, int perm); ...@@ -96,6 +96,8 @@ void au_optstr_br_perm(au_br_perm_str_t *str, int perm);
void au_opts_free(struct au_opts *opts); void au_opts_free(struct au_opts *opts);
struct super_block; struct super_block;
int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts); int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts);
int au_opts_verify(struct super_block *sb, unsigned long sb_flags,
unsigned int pending);
int au_opts_mount(struct super_block *sb, struct au_opts *opts); int au_opts_mount(struct super_block *sb, struct au_opts *opts);
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
......
...@@ -180,7 +180,6 @@ int au_plink_test(struct inode *inode) ...@@ -180,7 +180,6 @@ int au_plink_test(struct inode *inode)
/* 20 is max digits length of ulong 64 */ /* 20 is max digits length of ulong 64 */
#define PLINK_NAME_LEN ((20 + 1) * 2) #define PLINK_NAME_LEN ((20 + 1) * 2)
/* re-commit later */ __maybe_unused
static int plink_name(char *name, int len, struct inode *inode, static int plink_name(char *name, int len, struct inode *inode,
aufs_bindex_t bindex) aufs_bindex_t bindex)
{ {
...@@ -231,8 +230,7 @@ struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex) ...@@ -231,8 +230,7 @@ struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex)
AuDebugOn(au_plink_maint(inode->i_sb, AuLock_NOPLM)); AuDebugOn(au_plink_maint(inode->i_sb, AuLock_NOPLM));
br = au_sbr(inode->i_sb, bindex); br = au_sbr(inode->i_sb, bindex);
/* h_parent = br->br_wbr->wbr_plink; */ /* re-commit later */ h_parent = br->br_wbr->wbr_plink;
h_parent = NULL;
tgtname.len = plink_name(a, sizeof(a), inode, bindex); tgtname.len = plink_name(a, sizeof(a), inode, bindex);
if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) { if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) {
...@@ -253,7 +251,6 @@ struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex) ...@@ -253,7 +251,6 @@ struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex)
} }
/* create a pseudo-link */ /* create a pseudo-link */
/* re-commit later */ __maybe_unused
static int do_whplink(struct qstr *tgt, struct dentry *h_parent, static int do_whplink(struct qstr *tgt, struct dentry *h_parent,
struct dentry *h_dentry, struct au_branch *br) struct dentry *h_dentry, struct au_branch *br)
{ {
...@@ -312,13 +309,43 @@ struct do_whplink_args { ...@@ -312,13 +309,43 @@ struct do_whplink_args {
struct au_branch *br; struct au_branch *br;
}; };
/* re-commit later */ __maybe_unused
static void call_do_whplink(void *args) static void call_do_whplink(void *args)
{ {
struct do_whplink_args *a = args; struct do_whplink_args *a = args;
*a->errp = do_whplink(a->tgt, a->h_parent, a->h_dentry, a->br); *a->errp = do_whplink(a->tgt, a->h_parent, a->h_dentry, a->br);
} }
static int whplink(struct dentry *h_dentry, struct inode *inode,
aufs_bindex_t bindex, struct au_branch *br)
{
int err, wkq_err;
struct au_wbr *wbr;
struct dentry *h_parent;
char a[PLINK_NAME_LEN];
struct qstr tgtname = QSTR_INIT(a, 0);
wbr = au_sbr(inode->i_sb, bindex)->br_wbr;
h_parent = wbr->wbr_plink;
tgtname.len = plink_name(a, sizeof(a), inode, bindex);
/* always superio. */
if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) {
struct do_whplink_args args = {
.errp = &err,
.tgt = &tgtname,
.h_parent = h_parent,
.h_dentry = h_dentry,
.br = br
};
wkq_err = au_wkq_wait(call_do_whplink, &args);
if (unlikely(wkq_err))
err = wkq_err;
} else
err = do_whplink(&tgtname, h_parent, h_dentry, br);
return err;
}
/* /*
* create a new pseudo-link for @h_dentry on @bindex. * create a new pseudo-link for @h_dentry on @bindex.
* the linked inode is held in aufs @inode. * the linked inode is held in aufs @inode.
...@@ -364,8 +391,7 @@ void au_plink_append(struct inode *inode, aufs_bindex_t bindex, ...@@ -364,8 +391,7 @@ void au_plink_append(struct inode *inode, aufs_bindex_t bindex,
if (cnt > AUFS_PLINK_WARN) if (cnt > AUFS_PLINK_WARN)
AuWarn1(msg ", %d\n", cnt); AuWarn1(msg ", %d\n", cnt);
#undef msg #undef msg
/* err = whplink(h_dentry, inode, bindex, au_sbr(sb, bindex)); re-commit later */ err = whplink(h_dentry, inode, bindex, au_sbr(sb, bindex));
err = 0;
if (unlikely(err)) { if (unlikely(err)) {
pr_warn("err %d, damaged pseudo link.\n", err); pr_warn("err %d, damaged pseudo link.\n", err);
au_hbl_del(&icntnr->plink, hbl); au_hbl_del(&icntnr->plink, hbl);
......
...@@ -128,6 +128,50 @@ out: ...@@ -128,6 +128,50 @@ out:
return err; return err;
} }
int vfsub_mkdir(struct inode *dir, struct path *path, int mode)
{
int err;
struct dentry *d;
IMustLock(dir);
d = path->dentry;
path->dentry = d->d_parent;
err = security_path_mkdir(path, d, mode);
path->dentry = d;
if (unlikely(err))
goto out;
lockdep_off();
err = vfs_mkdir(dir, path->dentry, mode);
lockdep_on();
out:
return err;
}
int vfsub_rmdir(struct inode *dir, struct path *path)
{
int err;
struct dentry *d;
IMustLock(dir);
d = path->dentry;
path->dentry = d->d_parent;
err = security_path_rmdir(path, d);
path->dentry = d;
if (unlikely(err))
goto out;
lockdep_off();
err = vfs_rmdir(dir, path->dentry);
lockdep_on();
out:
return err;
}
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
struct unlink_args { struct unlink_args {
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/mount.h>
#include "debug.h" #include "debug.h"
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
...@@ -51,10 +52,31 @@ static inline struct dentry *vfsub_lkup_one(struct qstr *name, ...@@ -51,10 +52,31 @@ static inline struct dentry *vfsub_lkup_one(struct qstr *name,
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
static inline int vfsub_mnt_want_write(struct vfsmount *mnt)
{
int err;
lockdep_off();
err = mnt_want_write(mnt);
lockdep_on();
return err;
}
static inline void vfsub_mnt_drop_write(struct vfsmount *mnt)
{
lockdep_off();
mnt_drop_write(mnt);
lockdep_on();
}
/* ---------------------------------------------------------------------- */
int vfsub_create(struct inode *dir, struct path *path, int mode, int vfsub_create(struct inode *dir, struct path *path, int mode,
bool want_excl); bool want_excl);
int vfsub_link(struct dentry *src_dentry, struct inode *dir, int vfsub_link(struct dentry *src_dentry, struct inode *dir,
struct path *path, struct inode **delegated_inode); struct path *path, struct inode **delegated_inode);
int vfsub_mkdir(struct inode *dir, struct path *path, int mode);
int vfsub_rmdir(struct inode *dir, struct path *path);
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
......
...@@ -121,6 +121,269 @@ int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path, ...@@ -121,6 +121,269 @@ int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path,
return err; return err;
} }
/* ---------------------------------------------------------------------- */
/*
* initialize/clean whiteout for a branch
*/
static void au_wh_clean(struct inode *h_dir, struct path *whpath,
const int isdir)
{
int err;
struct inode *delegated;
if (d_is_negative(whpath->dentry))
return;
if (isdir)
err = vfsub_rmdir(h_dir, whpath);
else {
delegated = NULL;
err = vfsub_unlink(h_dir, whpath, &delegated, /*force*/0);
if (unlikely(err == -EWOULDBLOCK)) {
pr_warn("cannot retry for NFSv4 delegation"
" for an internal unlink\n");
iput(delegated);
}
}
if (unlikely(err))
pr_warn("failed removing %pd (%d), ignored.\n",
whpath->dentry, err);
}
static int test_linkable(struct dentry *h_root)
{
struct inode *h_dir = d_inode(h_root);
if (h_dir->i_op->link)
return 0;
pr_err("%pd (%s) doesn't support link(2), use noplink and rw+nolwh\n",
h_root, au_sbtype(h_root->d_sb));
return -ENOSYS;
}
/* todo: should this mkdir be done in /sbin/mount.aufs helper? */
static int au_whdir(struct inode *h_dir, struct path *path)
{
int err;
err = -EEXIST;
if (d_is_negative(path->dentry)) {
int mode = 0700;
if (au_test_nfs(path->dentry->d_sb))
mode |= 0111;
err = vfsub_mkdir(h_dir, path, mode);
} else if (d_is_dir(path->dentry))
err = 0;
else
pr_err("unknown %pd exists\n", path->dentry);
return err;
}
struct au_wh_base {
const struct qstr *name;
struct dentry *dentry;
};
static void au_wh_init_ro(struct inode *h_dir, struct au_wh_base base[],
struct path *h_path)
{
h_path->dentry = base[AuBrWh_BASE].dentry;
au_wh_clean(h_dir, h_path, /*isdir*/0);
h_path->dentry = base[AuBrWh_PLINK].dentry;
au_wh_clean(h_dir, h_path, /*isdir*/1);
h_path->dentry = base[AuBrWh_ORPH].dentry;
au_wh_clean(h_dir, h_path, /*isdir*/1);
}
/*
* returns tri-state,
* minus: error, caller should print the message
* zero: success
* plus: error, caller should NOT print the message
*/
static int au_wh_init_rw_nolink(struct dentry *h_root, struct au_wbr *wbr,
int do_plink, struct au_wh_base base[],
struct path *h_path)
{
int err;
struct inode *h_dir;
h_dir = d_inode(h_root);
h_path->dentry = base[AuBrWh_BASE].dentry;
au_wh_clean(h_dir, h_path, /*isdir*/0);
h_path->dentry = base[AuBrWh_PLINK].dentry;
if (do_plink) {
err = test_linkable(h_root);
if (unlikely(err)) {
err = 1;
goto out;
}
err = au_whdir(h_dir, h_path);
if (unlikely(err))
goto out;
wbr->wbr_plink = dget(base[AuBrWh_PLINK].dentry);
} else
au_wh_clean(h_dir, h_path, /*isdir*/1);
h_path->dentry = base[AuBrWh_ORPH].dentry;
err = au_whdir(h_dir, h_path);
if (unlikely(err))
goto out;
wbr->wbr_orph = dget(base[AuBrWh_ORPH].dentry);
out:
return err;
}
/*
* for the moment, aufs supports the branch filesystem which does not support
* link(2). testing on FAT which does not support i_op->setattr() fully either,
* copyup failed. finally, such filesystem will not be used as the writable
* branch.
*
* returns tri-state, see above.
*/
static int au_wh_init_rw(struct dentry *h_root, struct au_wbr *wbr,
int do_plink, struct au_wh_base base[],
struct path *h_path)
{
int err;
struct inode *h_dir;
WbrWhMustWriteLock(wbr);
err = test_linkable(h_root);
if (unlikely(err)) {
err = 1;
goto out;
}
/*
* todo: should this create be done in /sbin/mount.aufs helper?
*/
err = -EEXIST;
h_dir = d_inode(h_root);
if (d_is_negative(base[AuBrWh_BASE].dentry)) {
h_path->dentry = base[AuBrWh_BASE].dentry;
err = vfsub_create(h_dir, h_path, WH_MASK, /*want_excl*/true);
} else if (d_is_reg(base[AuBrWh_BASE].dentry))
err = 0;
else
pr_err("unknown %pd2 exists\n", base[AuBrWh_BASE].dentry);
if (unlikely(err))
goto out;
h_path->dentry = base[AuBrWh_PLINK].dentry;
if (do_plink) {
err = au_whdir(h_dir, h_path);
if (unlikely(err))
goto out;
wbr->wbr_plink = dget(base[AuBrWh_PLINK].dentry);
} else
au_wh_clean(h_dir, h_path, /*isdir*/1);
wbr->wbr_whbase = dget(base[AuBrWh_BASE].dentry);
h_path->dentry = base[AuBrWh_ORPH].dentry;
err = au_whdir(h_dir, h_path);
if (unlikely(err))
goto out;
wbr->wbr_orph = dget(base[AuBrWh_ORPH].dentry);
out:
return err;
}
/*
* initialize the whiteout base file/dir for @br.
*/
int au_wh_init(struct au_branch *br, struct super_block *sb)
{
int err, i;
const unsigned char do_plink
= !!au_opt_test(au_mntflags(sb), PLINK);
struct inode *h_dir;
struct path path = br->br_path;
struct dentry *h_root = path.dentry;
struct au_wbr *wbr = br->br_wbr;
static const struct qstr base_name[] = {
[AuBrWh_BASE] = QSTR_INIT(AUFS_BASE_NAME,
sizeof(AUFS_BASE_NAME) - 1),
[AuBrWh_PLINK] = QSTR_INIT(AUFS_PLINKDIR_NAME,
sizeof(AUFS_PLINKDIR_NAME) - 1),
[AuBrWh_ORPH] = QSTR_INIT(AUFS_ORPHDIR_NAME,
sizeof(AUFS_ORPHDIR_NAME) - 1)
};
struct au_wh_base base[] = {
[AuBrWh_BASE] = {
.name = base_name + AuBrWh_BASE,
.dentry = NULL
},
[AuBrWh_PLINK] = {
.name = base_name + AuBrWh_PLINK,
.dentry = NULL
},
[AuBrWh_ORPH] = {
.name = base_name + AuBrWh_ORPH,
.dentry = NULL
}
};
if (wbr)
WbrWhMustWriteLock(wbr);
for (i = 0; i < AuBrWh_Last; i++) {
/* doubly whiteouted */
struct dentry *d;
d = au_wh_lkup(h_root, (void *)base[i].name, br);
err = PTR_ERR(d);
if (IS_ERR(d))
goto out;
base[i].dentry = d;
AuDebugOn(wbr
&& wbr->wbr_wh[i]
&& wbr->wbr_wh[i] != base[i].dentry);
}
if (wbr)
for (i = 0; i < AuBrWh_Last; i++) {
dput(wbr->wbr_wh[i]);
wbr->wbr_wh[i] = NULL;
}
err = 0;
if (!au_br_writable(br->br_perm)) {
h_dir = d_inode(h_root);
au_wh_init_ro(h_dir, base, &path);
} else if (!au_br_wh_linkable(br->br_perm)) {
err = au_wh_init_rw_nolink(h_root, wbr, do_plink, base, &path);
if (err > 0)
goto out;
else if (err)
goto out_err;
} else {
err = au_wh_init_rw(h_root, wbr, do_plink, base, &path);
if (err > 0)
goto out;
else if (err)
goto out_err;
}
goto out; /* success */
out_err:
pr_err("an error(%d) on the writable branch %pd(%s)\n",
err, h_root, au_sbtype(h_root->d_sb));
out:
for (i = 0; i < AuBrWh_Last; i++)
dput(base[i].dentry);
return err;
}
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
/* /*
* whiteouts are all hard-linked usually. * whiteouts are all hard-linked usually.
...@@ -182,10 +445,8 @@ static void reinit_br_wh(void *arg) ...@@ -182,10 +445,8 @@ static void reinit_br_wh(void *arg)
} }
dput(wbr->wbr_whbase); dput(wbr->wbr_whbase);
wbr->wbr_whbase = NULL; wbr->wbr_whbase = NULL;
#if 0 /* re-commit later */
if (!err) if (!err)
err = au_wh_init(a->br, a->sb); err = au_wh_init(a->br, a->sb);
#endif
wbr_wh_write_unlock(wbr); wbr_wh_write_unlock(wbr);
inode_unlock(hdir->hi_inode); inode_unlock(hdir->hi_inode);
di_read_unlock(a->sb->s_root, AuLock_IR); di_read_unlock(a->sb->s_root, AuLock_IR);
......
...@@ -22,6 +22,9 @@ struct path; ...@@ -22,6 +22,9 @@ struct path;
int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path, int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path,
struct dentry *dentry); struct dentry *dentry);
struct au_branch; struct au_branch;
struct super_block;
int au_wh_init(struct au_branch *br, struct super_block *sb);
struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name, struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name,
struct au_branch *br); struct au_branch *br);
struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex, struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex,
......
...@@ -1613,7 +1613,7 @@ struct file *au_xino_def(struct super_block *sb) ...@@ -1613,7 +1613,7 @@ struct file *au_xino_def(struct super_block *sb)
bwr = -1; bwr = -1;
for (bindex = 0; bindex <= bbot; bindex++) { for (bindex = 0; bindex <= bbot; bindex++) {
br = au_sbr(sb, bindex); br = au_sbr(sb, bindex);
if (0 /* au_br_writable(br->br_perm) */ /* re-commit later */ if (au_br_writable(br->br_perm)
&& !au_test_fs_bad_xino(au_br_sb(br))) { && !au_test_fs_bad_xino(au_br_sb(br))) {
bwr = bindex; bwr = bindex;
break; break;
......
...@@ -64,6 +64,11 @@ typedef int16_t aufs_bindex_t; ...@@ -64,6 +64,11 @@ typedef int16_t aufs_bindex_t;
#define AUFS_WH_PFX ".wh." #define AUFS_WH_PFX ".wh."
#define AUFS_WH_PFX_LEN ((int)sizeof(AUFS_WH_PFX) - 1) #define AUFS_WH_PFX_LEN ((int)sizeof(AUFS_WH_PFX) - 1)
#define AUFS_WH_TMP_LEN 4 #define AUFS_WH_TMP_LEN 4
/* a limit for rmdir/rename a dir and copyup */
#define AUFS_MAX_NAMELEN (NAME_MAX \
- AUFS_WH_PFX_LEN * 2 /* doubly whiteouted */\
- 1 /* dot */\
- AUFS_WH_TMP_LEN) /* hex */
#define AUFS_XINO_FNAME "." AUFS_NAME ".xino" #define AUFS_XINO_FNAME "." AUFS_NAME ".xino"
#define AUFS_XINO_DEFPATH "/tmp/" AUFS_XINO_FNAME #define AUFS_XINO_DEFPATH "/tmp/" AUFS_XINO_FNAME
#define AUFS_XINO_DEF_SEC 30 /* seconds */ #define AUFS_XINO_DEF_SEC 30 /* seconds */
...@@ -76,6 +81,15 @@ typedef int16_t aufs_bindex_t; ...@@ -76,6 +81,15 @@ typedef int16_t aufs_bindex_t;
#define AUFS_PLINK_MAINT_DIR "fs/" AUFS_NAME #define AUFS_PLINK_MAINT_DIR "fs/" AUFS_NAME
#define AUFS_PLINK_MAINT_PATH AUFS_PLINK_MAINT_DIR "/" AUFS_PLINK_MAINT_NAME #define AUFS_PLINK_MAINT_PATH AUFS_PLINK_MAINT_DIR "/" AUFS_PLINK_MAINT_NAME
#define AUFS_BASE_NAME AUFS_WH_PFX AUFS_NAME
#define AUFS_PLINKDIR_NAME AUFS_WH_PFX "plnk"
#define AUFS_ORPHDIR_NAME AUFS_WH_PFX "orph"
/* doubly whiteouted */
#define AUFS_WH_BASE AUFS_WH_PFX AUFS_BASE_NAME
#define AUFS_WH_PLINKDIR AUFS_WH_PFX AUFS_PLINKDIR_NAME
#define AUFS_WH_ORPHDIR AUFS_WH_PFX AUFS_ORPHDIR_NAME
/* branch permissions and attributes */ /* branch permissions and attributes */
#define AUFS_BRPERM_RW "rw" #define AUFS_BRPERM_RW "rw"
#define AUFS_BRPERM_RO "ro" #define AUFS_BRPERM_RO "ro"
...@@ -110,4 +124,9 @@ static inline int au_br_whable(int brperm) ...@@ -110,4 +124,9 @@ static inline int au_br_whable(int brperm)
return brperm & (AuBrPerm_RW | AuBrRAttr_WH); return brperm & (AuBrPerm_RW | AuBrRAttr_WH);
} }
static inline int au_br_wh_linkable(int brperm)
{
return !(brperm & AuBrWAttr_NoLinkWH);
}
#endif /* __AUFS_TYPE_H__ */ #endif /* __AUFS_TYPE_H__ */
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