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 @@
*/
#include <linux/file.h>
#include <linux/statfs.h>
#include "aufs.h"
/*
......@@ -15,16 +16,28 @@
*/
static void au_br_do_free(struct au_branch *br)
{
int i;
struct au_wbr *wbr;
au_xino_put(br);
AuLCntZero(au_lcnt_read(&br->br_count, /*do_rev*/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 */
/* synchronize_rcu(); */ /* why? */
lockdep_off();
path_put(&br->br_path);
lockdep_on();
au_kfree_rcu(wbr);
au_lcnt_wait_for_fin(&br->br_count);
/* I don't know why, but percpu_refcount requires this */
/* synchronize_rcu(); */
......@@ -98,6 +111,14 @@ static struct au_branch *au_br_alloc(struct super_block *sb, int new_nbranch,
if (unlikely(!add_branch->br_xino))
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;
err = au_sbr_realloc(au_sbi(sb), new_nbranch, /*may_shrink*/0);
if (!err)
......@@ -110,14 +131,34 @@ static struct au_branch *au_br_alloc(struct super_block *sb, int new_nbranch,
if (!err)
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:
au_kfree_rcu(add_branch);
out:
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:
* 0: success, the caller will add it
......@@ -178,6 +219,10 @@ static int test_add(struct super_block *sb, struct au_opt_add *add)
goto out;
}
err = test_br(d_inode(add->path.dentry), add->perm, add->pathname);
if (unlikely(err))
goto out;
if (bbot < 0)
return 0; /* success */
......@@ -195,6 +240,90 @@ out:
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 */
static int au_br_init(struct au_branch *br, struct super_block *sb,
struct au_opt_add *add)
......@@ -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);
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)) {
brbase = au_sbr(sb, 0);
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)
if (!sb || IS_ERR(sb))
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, "
"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),
sb->s_flags, sb->s_count,
atomic_read(&sb->s_active),
......
......@@ -7,6 +7,7 @@
* inode functions
*/
#include <linux/cred.h>
#include "aufs.h"
struct inode *au_igrab(struct inode *inode)
......@@ -17,3 +18,19 @@ struct inode *au_igrab(struct inode *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)
/* inode.c */
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 */
struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex);
......
......@@ -90,6 +90,7 @@ out:
static match_table_t brperm = {
{AuBrPerm_RO, AUFS_BRPERM_RO},
{AuBrPerm_RW, AUFS_BRPERM_RW},
/* add more later */
{0, NULL}
};
......@@ -97,6 +98,10 @@ static match_table_t brperm = {
static match_table_t brattr = {
/* ro/rr branch */
{AuBrRAttr_WH, AUFS_BRRATTR_WH},
/* rw branch */
{AuBrWAttr_NoLinkWH, AUFS_BRWATTR_NLWH},
/* add more later */
{0, NULL}
};
......@@ -159,9 +164,10 @@ out:
static int noinline_for_stack br_perm_val(char *perm)
{
int val;
int val, bad, sz;
char *p;
substring_t args[MAX_OPT_ARGS];
au_br_perm_str_t attr;
p = strchr(perm, '+');
if (p)
......@@ -179,6 +185,23 @@ static int noinline_for_stack br_perm_val(char *perm)
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:
return val;
}
......@@ -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);
if (!err) {
if (!p)
if (!p) {
add->perm = AuBrPerm_RO;
/* re-commit later */
if (!bindex && !(sb_flags & SB_RDONLY))
add->perm = AuBrPerm_RW;
}
opt->type = Opt_add;
goto out;
}
......@@ -633,6 +658,92 @@ static int au_opt_xino(struct super_block *sb, struct au_opt *opt,
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 err;
......@@ -682,6 +793,10 @@ int au_opts_mount(struct super_block *sb, struct au_opts *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);
......
......@@ -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);
struct super_block;
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);
#endif /* __KERNEL__ */
......
......@@ -180,7 +180,6 @@ int au_plink_test(struct inode *inode)
/* 20 is max digits length of ulong 64 */
#define PLINK_NAME_LEN ((20 + 1) * 2)
/* re-commit later */ __maybe_unused
static int plink_name(char *name, int len, 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));
br = au_sbr(inode->i_sb, bindex);
/* h_parent = br->br_wbr->wbr_plink; */ /* re-commit later */
h_parent = NULL;
h_parent = br->br_wbr->wbr_plink;
tgtname.len = plink_name(a, sizeof(a), inode, bindex);
if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) {
......@@ -253,7 +251,6 @@ struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex)
}
/* create a pseudo-link */
/* re-commit later */ __maybe_unused
static int do_whplink(struct qstr *tgt, struct dentry *h_parent,
struct dentry *h_dentry, struct au_branch *br)
{
......@@ -312,13 +309,43 @@ struct do_whplink_args {
struct au_branch *br;
};
/* re-commit later */ __maybe_unused
static void call_do_whplink(void *args)
{
struct do_whplink_args *a = args;
*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.
* the linked inode is held in aufs @inode.
......@@ -364,8 +391,7 @@ void au_plink_append(struct inode *inode, aufs_bindex_t bindex,
if (cnt > AUFS_PLINK_WARN)
AuWarn1(msg ", %d\n", cnt);
#undef msg
/* err = whplink(h_dentry, inode, bindex, au_sbr(sb, bindex)); re-commit later */
err = 0;
err = whplink(h_dentry, inode, bindex, au_sbr(sb, bindex));
if (unlikely(err)) {
pr_warn("err %d, damaged pseudo link.\n", err);
au_hbl_del(&icntnr->plink, hbl);
......
......@@ -128,6 +128,50 @@ out:
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 {
......
......@@ -13,6 +13,7 @@
#ifdef __KERNEL__
#include <linux/fs.h>
#include <linux/mount.h>
#include "debug.h"
/* ---------------------------------------------------------------------- */
......@@ -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,
bool want_excl);
int vfsub_link(struct dentry *src_dentry, struct inode *dir,
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,
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.
......@@ -182,10 +445,8 @@ static void reinit_br_wh(void *arg)
}
dput(wbr->wbr_whbase);
wbr->wbr_whbase = NULL;
#if 0 /* re-commit later */
if (!err)
err = au_wh_init(a->br, a->sb);
#endif
wbr_wh_write_unlock(wbr);
inode_unlock(hdir->hi_inode);
di_read_unlock(a->sb->s_root, AuLock_IR);
......
......@@ -22,6 +22,9 @@ struct path;
int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path,
struct dentry *dentry);
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 au_branch *br);
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)
bwr = -1;
for (bindex = 0; bindex <= bbot; 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))) {
bwr = bindex;
break;
......
......@@ -64,6 +64,11 @@ typedef int16_t aufs_bindex_t;
#define AUFS_WH_PFX ".wh."
#define AUFS_WH_PFX_LEN ((int)sizeof(AUFS_WH_PFX) - 1)
#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_DEFPATH "/tmp/" AUFS_XINO_FNAME
#define AUFS_XINO_DEF_SEC 30 /* seconds */
......@@ -76,6 +81,15 @@ typedef int16_t aufs_bindex_t;
#define AUFS_PLINK_MAINT_DIR "fs/" AUFS_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 */
#define AUFS_BRPERM_RW "rw"
#define AUFS_BRPERM_RO "ro"
......@@ -110,4 +124,9 @@ static inline int au_br_whable(int brperm)
return brperm & (AuBrPerm_RW | AuBrRAttr_WH);
}
static inline int au_br_wh_linkable(int brperm)
{
return !(brperm & AuBrWAttr_NoLinkWH);
}
#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