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

aufs: writable branch select policy 1/2, core



Aufs can have multiple writable branches, and there are several
policies to select one among them.
This commit implements default "top-down-parent" for both of
creating-policy and copyup-policy.

See also the document in this commit.
Signed-off-by: default avatarJ. R. Okajima <hooanon05g@gmail.com>
parent e4226da5
# Copyright (C) 2005-2019 Junjiro R. Okajima
Policies to Select One among Multiple Writable Branches
----------------------------------------------------------------------
When the number of writable branch is more than one, aufs has to decide
the target branch for file creation or copy-up. By default, the highest
writable branch which has the parent (or ancestor) dir of the target
file is chosen (top-down-parent policy).
By user's request, aufs implements some other policies to select the
writable branch, for file creation several policies, round-robin,
most-free-space, and other policies. For copy-up, top-down-parent,
bottom-up-parent, bottom-up and others.
As expected, the round-robin policy selects the branch in circular. When
you have two writable branches and creates 10 new files, 5 files will be
created for each branch. mkdir(2) systemcall is an exception. When you
create 10 new directories, all will be created on the same branch.
And the most-free-space policy selects the one which has most free
space among the writable branches. The amount of free space will be
checked by aufs internally, and users can specify its time interval.
The policies for copy-up is more simple,
top-down-parent is equivalent to the same named on in create policy,
bottom-up-parent selects the writable branch where the parent dir
exists and the nearest upper one from the copyup-source,
bottom-up selects the nearest upper writable branch from the
copyup-source, regardless the existence of the parent dir.
There are some rules or exceptions to apply these policies.
- If there is a readonly branch above the policy-selected branch and
the parent dir is marked as opaque (a variation of whiteout), or the
target (creating) file is whiteout-ed on the upper readonly branch,
then the result of the policy is ignored and the target file will be
created on the nearest upper writable branch than the readonly branch.
- If there is a writable branch above the policy-selected branch and
the parent dir is marked as opaque or the target file is whiteouted
on the branch, then the result of the policy is ignored and the target
file will be created on the highest one among the upper writable
branches who has diropq or whiteout. In case of whiteout, aufs removes
it as usual.
- link(2) and rename(2) systemcalls are exceptions in every policy.
They try selecting the branch where the source exists as possible
since copyup a large file will take long time. If it can't be,
ie. the branch where the source exists is readonly, then they will
follow the copyup policy.
- There is an exception for rename(2) when the target exists.
If the rename target exists, aufs compares the index of the branches
where the source and the target exists and selects the higher
one. If the selected branch is readonly, then aufs follows the
copyup policy.
...@@ -12,7 +12,7 @@ ccflags-y += -include ${srctree}/include/uapi/linux/aufs_type.h ...@@ -12,7 +12,7 @@ ccflags-y += -include ${srctree}/include/uapi/linux/aufs_type.h
obj-$(CONFIG_AUFS_FS) += aufs.o obj-$(CONFIG_AUFS_FS) += aufs.o
aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \ aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \
wkq.o vfsub.o dcsub.o \ wkq.o vfsub.o dcsub.o \
cpup.o whout.o \ cpup.o whout.o wbr_policy.o \
dinfo.o dentry.o \ dinfo.o dentry.o \
file.o \ file.o \
dir.o \ dir.o \
......
...@@ -22,6 +22,7 @@ enum { ...@@ -22,6 +22,7 @@ enum {
Opt_trunc_xino_path, Opt_itrunc_xino, Opt_trunc_xino_path, Opt_itrunc_xino,
Opt_trunc_xib, Opt_notrunc_xib, Opt_trunc_xib, Opt_notrunc_xib,
Opt_plink, Opt_noplink, Opt_list_plink, Opt_plink, Opt_noplink, Opt_list_plink,
Opt_wbr_copyup, Opt_wbr_create,
Opt_tail, Opt_ignore, Opt_ignore_silent, Opt_err Opt_tail, Opt_ignore, Opt_ignore_silent, Opt_err
}; };
...@@ -52,6 +53,12 @@ static match_table_t options = { ...@@ -52,6 +53,12 @@ static match_table_t options = {
{Opt_list_plink, "list_plink"}, {Opt_list_plink, "list_plink"},
#endif #endif
{Opt_wbr_create, "create=%s"},
{Opt_wbr_create, "create_policy=%s"},
{Opt_wbr_copyup, "cpup=%s"},
{Opt_wbr_copyup, "copyup=%s"},
{Opt_wbr_copyup, "copyup_policy=%s"},
/* internal use for the scripts */ /* internal use for the scripts */
{Opt_ignore_silent, "si=%s"}, {Opt_ignore_silent, "si=%s"},
...@@ -63,6 +70,20 @@ static match_table_t options = { ...@@ -63,6 +70,20 @@ static match_table_t options = {
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
static const char *au_parser_pattern(int val, match_table_t tbl)
{
struct match_token *p;
p = tbl;
while (p->pattern) {
if (p->token == val)
return p->pattern;
p++;
}
BUG();
return "??";
}
static const char *au_optstr(int *val, match_table_t tbl) static const char *au_optstr(int *val, match_table_t tbl)
{ {
struct match_token *p; struct match_token *p;
...@@ -231,6 +252,51 @@ void au_optstr_br_perm(au_br_perm_str_t *str, int perm) ...@@ -231,6 +252,51 @@ void au_optstr_br_perm(au_br_perm_str_t *str, int perm)
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
static match_table_t au_wbr_create_policy = {
{AuWbrCreate_TDP, "tdp"},
{AuWbrCreate_TDP, "top-down-parent"},
/* add more later */
{-1, NULL}
};
static int noinline_for_stack
au_wbr_create_val(char *str, struct au_opt_wbr_create *create)
{
int err;
substring_t args[MAX_OPT_ARGS];
err = match_token(str, au_wbr_create_policy, args);
create->wbr_create = err;
return err;
}
const char *au_optstr_wbr_create(int wbr_create)
{
return au_parser_pattern(wbr_create, au_wbr_create_policy);
}
static match_table_t au_wbr_copyup_policy = {
{AuWbrCopyup_TDP, "tdp"},
{AuWbrCopyup_TDP, "top-down-parent"},
{-1, NULL}
};
static int noinline_for_stack au_wbr_copyup_val(char *str)
{
substring_t args[MAX_OPT_ARGS];
return match_token(str, au_wbr_copyup_policy, args);
}
const char *au_optstr_wbr_copyup(int wbr_copyup)
{
return au_parser_pattern(wbr_copyup, au_wbr_copyup_policy);
}
/* ---------------------------------------------------------------------- */
static const int lkup_dirflags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY; static const int lkup_dirflags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
static void dump_opts(struct au_opts *opts) static void dump_opts(struct au_opts *opts)
...@@ -241,6 +307,7 @@ static void dump_opts(struct au_opts *opts) ...@@ -241,6 +307,7 @@ static void dump_opts(struct au_opts *opts)
struct au_opt_add *add; struct au_opt_add *add;
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;
} u; } u;
struct au_opt *opt; struct au_opt *opt;
...@@ -286,6 +353,15 @@ static void dump_opts(struct au_opts *opts) ...@@ -286,6 +353,15 @@ static void dump_opts(struct au_opts *opts)
case Opt_list_plink: case Opt_list_plink:
AuLabel(list_plink); AuLabel(list_plink);
break; break;
case Opt_wbr_create:
u.create = &opt->wbr_create;
AuDbg("create %d, %s\n", u.create->wbr_create,
au_optstr_wbr_create(u.create->wbr_create));
break;
case Opt_wbr_copyup:
AuDbg("copyup %d, %s\n", opt->wbr_copyup,
au_optstr_wbr_copyup(opt->wbr_copyup));
break;
default: default:
BUG(); BUG();
} }
...@@ -520,6 +596,25 @@ int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts) ...@@ -520,6 +596,25 @@ int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts)
opt->type = token; opt->type = token;
break; break;
case Opt_wbr_create:
u.create = &opt->wbr_create;
u.create->wbr_create
= au_wbr_create_val(a->args[0].from, u.create);
if (u.create->wbr_create >= 0) {
err = 0;
opt->type = token;
} else
pr_err("wrong value, %s\n", opt_str);
break;
case Opt_wbr_copyup:
opt->wbr_copyup = au_wbr_copyup_val(a->args[0].from);
if (opt->wbr_copyup >= 0) {
err = 0;
opt->type = token;
} else
pr_err("wrong value, %s\n", opt_str);
break;
case Opt_ignore: case Opt_ignore:
pr_warn("ignored %s\n", opt_str); pr_warn("ignored %s\n", opt_str);
/*FALLTHROUGH*/ /*FALLTHROUGH*/
...@@ -552,6 +647,31 @@ out: ...@@ -552,6 +647,31 @@ out:
return err; return err;
} }
static int au_opt_wbr_create(struct super_block *sb,
struct au_opt_wbr_create *create)
{
int err;
struct au_sbinfo *sbinfo;
SiMustWriteLock(sb);
err = 1; /* handled */
sbinfo = au_sbi(sb);
if (sbinfo->si_wbr_create_ops->fin) {
err = sbinfo->si_wbr_create_ops->fin(sb);
if (!err)
err = 1;
}
sbinfo->si_wbr_create = create->wbr_create;
sbinfo->si_wbr_create_ops = au_wbr_create_ops + create->wbr_create;
if (sbinfo->si_wbr_create_ops->init)
sbinfo->si_wbr_create_ops->init(sb); /* ignore */
return err;
}
/* /*
* returns, * returns,
* plus: processed without an error * plus: processed without an error
...@@ -581,6 +701,14 @@ static int au_opt_simple(struct super_block *sb, struct au_opt *opt, ...@@ -581,6 +701,14 @@ static int au_opt_simple(struct super_block *sb, struct au_opt *opt,
au_plink_list(sb); au_plink_list(sb);
break; break;
case Opt_wbr_create:
err = au_opt_wbr_create(sb, &opt->wbr_create);
break;
case Opt_wbr_copyup:
sbinfo->si_wbr_copyup = opt->wbr_copyup;
sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + opt->wbr_copyup;
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;
......
...@@ -46,6 +46,23 @@ static inline unsigned int au_opts_plink(unsigned int mntflags) ...@@ -46,6 +46,23 @@ static inline unsigned int au_opts_plink(unsigned int mntflags)
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
/* policies to select one among multiple writable branches */
enum {
AuWbrCreate_TDP, /* top down parent */
/* add more later */
AuWbrCreate_Def = AuWbrCreate_TDP
};
enum {
AuWbrCopyup_TDP, /* top down parent */
/* add more later */
AuWbrCopyup_Def = AuWbrCopyup_TDP
};
/* ---------------------------------------------------------------------- */
struct au_opt_add { struct au_opt_add {
aufs_bindex_t bindex; aufs_bindex_t bindex;
char *pathname; char *pathname;
...@@ -62,12 +79,19 @@ struct au_opt_xino_itrunc { ...@@ -62,12 +79,19 @@ struct au_opt_xino_itrunc {
aufs_bindex_t bindex; aufs_bindex_t bindex;
}; };
struct au_opt_wbr_create {
int wbr_create;
/* add more later */
};
struct au_opt { struct au_opt {
int type; int type;
union { union {
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_add add; struct au_opt_add add;
struct au_opt_wbr_create wbr_create;
int wbr_copyup;
/* add more later */ /* add more later */
}; };
}; };
...@@ -92,6 +116,8 @@ struct au_opts { ...@@ -92,6 +116,8 @@ struct au_opts {
/* opts.c */ /* opts.c */
void au_optstr_br_perm(au_br_perm_str_t *str, int perm); void au_optstr_br_perm(au_br_perm_str_t *str, int perm);
const char *au_optstr_wbr_copyup(int wbr_copyup);
const char *au_optstr_wbr_create(int wbr_create);
void au_opts_free(struct au_opts *opts); void au_opts_free(struct au_opts *opts);
struct super_block; struct super_block;
......
...@@ -60,6 +60,11 @@ int au_si_alloc(struct super_block *sb) ...@@ -60,6 +60,11 @@ int au_si_alloc(struct super_block *sb)
sbinfo->si_bbot = -1; sbinfo->si_bbot = -1;
sbinfo->si_last_br_id = AUFS_BRANCH_MAX / 2; sbinfo->si_last_br_id = AUFS_BRANCH_MAX / 2;
sbinfo->si_wbr_copyup = AuWbrCopyup_Def;
sbinfo->si_wbr_create = AuWbrCreate_Def;
sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + sbinfo->si_wbr_copyup;
sbinfo->si_wbr_create_ops = au_wbr_create_ops + sbinfo->si_wbr_create;
sbinfo->si_mntflags = au_opts_plink(AuOpt_Def); sbinfo->si_mntflags = au_opts_plink(AuOpt_Def);
sbinfo->si_xino_jiffy = jiffies; sbinfo->si_xino_jiffy = jiffies;
......
...@@ -103,6 +103,24 @@ static int au_show_brs(struct seq_file *seq, struct super_block *sb) ...@@ -103,6 +103,24 @@ static int au_show_brs(struct seq_file *seq, struct super_block *sb)
return err; return err;
} }
/* re-commit later */ __maybe_unused
static void au_show_wbr_create(struct seq_file *m, int v,
struct au_sbinfo *sbinfo)
{
const char *pat;
AuRwMustAnyLock(&sbinfo->si_rwsem);
seq_puts(m, ",create=");
pat = au_optstr_wbr_create(v);
switch (v) {
case AuWbrCreate_TDP:
seq_puts(m, pat);
break;
break;
}
}
/* re-commit later */ __maybe_unused /* re-commit later */ __maybe_unused
static int au_show_xino(struct seq_file *seq, struct super_block *sb) static int au_show_xino(struct seq_file *seq, struct super_block *sb)
{ {
......
...@@ -18,6 +18,24 @@ ...@@ -18,6 +18,24 @@
#include "rwsem.h" #include "rwsem.h"
#include "wkq.h" #include "wkq.h"
/* policies to select one among multiple writable branches */
struct au_wbr_copyup_operations {
int (*copyup)(struct dentry *dentry);
};
#define AuWbr_DIR 1 /* target is a dir */
#define AuWbr_PARENT (1 << 1) /* always require a parent */
#define au_ftest_wbr(flags, name) ((flags) & AuWbr_##name)
#define au_fset_wbr(flags, name) { (flags) |= AuWbr_##name; }
#define au_fclr_wbr(flags, name) { (flags) &= ~AuWbr_##name; }
struct au_wbr_create_operations {
int (*create)(struct dentry *dentry, unsigned int flags);
int (*init)(struct super_block *sb);
int (*fin)(struct super_block *sb);
};
#define AuPlink_NHASH 100 #define AuPlink_NHASH 100
static inline int au_plink_hash(ino_t ino) static inline int au_plink_hash(ino_t ino)
{ {
...@@ -45,6 +63,12 @@ struct au_sbinfo { ...@@ -45,6 +63,12 @@ struct au_sbinfo {
sizeof(aufs_bindex_t) * BITS_PER_BYTE - 1; sizeof(aufs_bindex_t) * BITS_PER_BYTE - 1;
struct au_branch **si_branch; struct au_branch **si_branch;
/* policy to select a writable branch */
unsigned char si_wbr_copyup;
unsigned char si_wbr_create;
struct au_wbr_copyup_operations *si_wbr_copyup_ops;
struct au_wbr_create_operations *si_wbr_create_ops;
/* mount flags */ /* mount flags */
/* include/asm-ia64/siginfo.h defines a macro named si_flags */ /* include/asm-ia64/siginfo.h defines a macro named si_flags */
unsigned int si_mntflags; unsigned int si_mntflags;
...@@ -89,6 +113,12 @@ struct au_sbinfo { ...@@ -89,6 +113,12 @@ struct au_sbinfo {
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
/* policy to select one among writable branches */
#define AuWbrCopyup(sbinfo, ...) \
((sbinfo)->si_wbr_copyup_ops->copyup(__VA_ARGS__))
#define AuWbrCreate(sbinfo, ...) \
((sbinfo)->si_wbr_create_ops->create(__VA_ARGS__))
/* flags for si_read_lock()/aufs_read_lock()/di_read_lock() */ /* flags for si_read_lock()/aufs_read_lock()/di_read_lock() */
#define AuLock_DW 1 /* write-lock dentry */ #define AuLock_DW 1 /* write-lock dentry */
#define AuLock_IR (1 << 1) /* read-lock inode */ #define AuLock_IR (1 << 1) /* read-lock inode */
...@@ -118,6 +148,13 @@ aufs_bindex_t au_new_br_id(struct super_block *sb); ...@@ -118,6 +148,13 @@ aufs_bindex_t au_new_br_id(struct super_block *sb);
int si_read_lock(struct super_block *sb, int flags); int si_read_lock(struct super_block *sb, int flags);
int si_write_lock(struct super_block *sb, int flags); int si_write_lock(struct super_block *sb, int flags);
/* wbr_policy.c */
extern struct au_wbr_copyup_operations au_wbr_copyup_ops[];
extern struct au_wbr_create_operations au_wbr_create_ops[];
int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst);
int au_wbr_nonopq(struct dentry *dentry, aufs_bindex_t bindex);
int au_wbr_do_copyup_bu(struct dentry *dentry, aufs_bindex_t btop);
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
static inline struct au_sbinfo *au_sbi(struct super_block *sb) static inline struct au_sbinfo *au_sbi(struct super_block *sb)
......
...@@ -316,6 +316,80 @@ ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos) ...@@ -316,6 +316,80 @@ ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos)
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
struct au_vfsub_mkdir_args {
int *errp;
struct inode *dir;
struct path *path;
int mode;
};
static void au_call_vfsub_mkdir(void *args)
{
struct au_vfsub_mkdir_args *a = args;
*a->errp = vfsub_mkdir(a->dir, a->path, a->mode);
}
int vfsub_sio_mkdir(struct inode *dir, struct path *path, int mode)
{
int err, do_sio, wkq_err;
do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE);
if (!do_sio) {
lockdep_off();
err = vfsub_mkdir(dir, path, mode);
lockdep_on();
} else {
struct au_vfsub_mkdir_args args = {
.errp = &err,
.dir = dir,
.path = path,
.mode = mode
};
wkq_err = au_wkq_wait(au_call_vfsub_mkdir, &args);
if (unlikely(wkq_err))
err = wkq_err;
}
return err;
}
struct au_vfsub_rmdir_args {
int *errp;
struct inode *dir;
struct path *path;
};
static void au_call_vfsub_rmdir(void *args)
{
struct au_vfsub_rmdir_args *a = args;
*a->errp = vfsub_rmdir(a->dir, a->path);
}
int vfsub_sio_rmdir(struct inode *dir, struct path *path)
{
int err, do_sio, wkq_err;
do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE);
if (!do_sio) {
lockdep_off();
err = vfsub_rmdir(dir, path);
lockdep_on();
} else {
struct au_vfsub_rmdir_args args = {
.errp = &err,
.dir = dir,
.path = path
};
wkq_err = au_wkq_wait(au_call_vfsub_rmdir, &args);
if (unlikely(wkq_err))
err = wkq_err;
}
return err;
}
/* ---------------------------------------------------------------------- */
struct notify_change_args { struct notify_change_args {
int *errp; int *errp;
struct path *path; struct path *path;
......
...@@ -138,6 +138,8 @@ static inline loff_t vfsub_llseek(struct file *file, loff_t offset, int origin) ...@@ -138,6 +138,8 @@ static inline loff_t vfsub_llseek(struct file *file, loff_t offset, int origin)
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
int vfsub_sio_mkdir(struct inode *dir, struct path *path, int mode);
int vfsub_sio_rmdir(struct inode *dir, struct path *path);
int vfsub_sio_notify_change(struct path *path, struct iattr *ia, int vfsub_sio_notify_change(struct path *path, struct iattr *ia,
struct inode **delegated_inode); struct inode **delegated_inode);
int vfsub_notify_change(struct path *path, struct iattr *ia, int vfsub_notify_change(struct path *path, struct iattr *ia,
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2005-2019 Junjiro R. Okajima
*/
/*
* policies for selecting one among multiple writable branches
*/
#include "aufs.h"
/* subset of cpup_attr() */
static noinline_for_stack
int au_cpdown_attr(struct path *h_path, struct dentry *h_src)
{
int err, sbits;
struct iattr ia;
struct inode *h_isrc;
h_isrc = d_inode(h_src);
ia.ia_valid = ATTR_FORCE | ATTR_MODE | ATTR_UID | ATTR_GID;
ia.ia_mode = h_isrc->i_mode;
ia.ia_uid = h_isrc->i_uid;
ia.ia_gid = h_isrc->i_gid;
sbits = !!(ia.ia_mode & (S_ISUID | S_ISGID));
au_cpup_attr_flags(d_inode(h_path->dentry), h_isrc->i_flags);
/* no delegation since it is just created */
err = vfsub_sio_notify_change(h_path, &ia, /*delegated*/NULL);
/* is this nfs only? */
if (!err && sbits && au_test_nfs(h_path->dentry->d_sb)) {
ia.ia_valid = ATTR_FORCE | ATTR_MODE;
ia.ia_mode = h_isrc->i_mode;
err = vfsub_sio_notify_change(h_path, &ia, /*delegated*/NULL);
}
return err;
}
#define AuCpdown_PARENT_OPQ 1
#define AuCpdown_WHED (1 << 1)
#define AuCpdown_MADE_DIR (1 << 2)
#define AuCpdown_DIROPQ (1 << 3)
#define au_ftest_cpdown(flags, name) ((flags) & AuCpdown_##name)
#define au_fset_cpdown(flags, name) \
do { (flags) |= AuCpdown_##name; } while (0)
#define au_fclr_cpdown(flags, name) \
do { (flags) &= ~AuCpdown_##name; } while (0)
static int au_cpdown_dir_opq(struct dentry *dentry, aufs_bindex_t bdst,
unsigned int *flags)
{
int err;
struct dentry *opq_dentry;
opq_dentry = au_diropq_create(dentry, bdst);
err = PTR_ERR(opq_dentry);
if (IS_ERR(opq_dentry))
goto out;
dput(opq_dentry);
au_fset_cpdown(*flags, DIROPQ);
out:
return err;
}
static int au_cpdown_dir_wh(struct dentry *dentry, struct dentry *h_parent,
struct inode *dir, aufs_bindex_t bdst)
{
int err;
struct path h_path;
struct au_branch *br;
br = au_sbr(dentry->d_sb, bdst);
h_path.dentry = au_wh_lkup(h_parent, &dentry->d_name, br);
err = PTR_ERR(h_path.dentry);
if (IS_ERR(h_path.dentry))
goto out;
err = 0;
if (d_is_positive(h_path.dentry)) {
h_path.mnt = au_br_mnt(br);
err = au_wh_unlink_dentry(au_h_iptr(dir, bdst), &h_path,
dentry);
}
dput(h_path.dentry);
out:
return err;
}
static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst,
struct au_pin *pin,
struct dentry *h_parent, void *arg)
{
int err, rerr;
aufs_bindex_t bopq, btop;
struct path h_path;
struct dentry *parent;
struct inode *h_dir, *h_inode, *inode, *dir;
unsigned int *flags = arg;
btop = au_dbtop(dentry);
/* dentry is di-locked */
parent = dget_parent(dentry);
dir = d_inode(parent);
h_dir = d_inode(h_parent);
AuDebugOn(h_dir != au_h_iptr(dir, bdst));
IMustLock(h_dir);
err = au_lkup_neg(dentry, bdst, /*wh*/0);
if (unlikely(err < 0))
goto out;
h_path.dentry = au_h_dptr(dentry, bdst);
h_path.mnt = au_sbr_mnt(dentry->d_sb, bdst);
err = vfsub_sio_mkdir(au_h_iptr(dir, bdst), &h_path, 0755);
if (unlikely(err))
goto out_put;
au_fset_cpdown(*flags, MADE_DIR);
bopq = au_dbdiropq(dentry);
au_fclr_cpdown(*flags, WHED);
au_fclr_cpdown(*flags, DIROPQ);
if (au_dbwh(dentry) == bdst)
au_fset_cpdown(*flags, WHED);
if (!au_ftest_cpdown(*flags, PARENT_OPQ) && bopq <= bdst)
au_fset_cpdown(*flags, PARENT_OPQ);
h_inode = d_inode(h_path.dentry);
inode_lock_nested(h_inode, AuLsc_I_CHILD);
if (au_ftest_cpdown(*flags, WHED)) {
err = au_cpdown_dir_opq(dentry, bdst, flags);
if (unlikely(err)) {
inode_unlock(h_inode);
goto out_dir;
}
}
err = au_cpdown_attr(&h_path, au_h_dptr(dentry, btop));
inode_unlock(h_inode);
if (unlikely(err))
goto out_opq;
if (au_ftest_cpdown(*flags, WHED)) {
err = au_cpdown_dir_wh(dentry, h_parent, dir, bdst);
if (unlikely(err))
goto out_opq;
}
inode = d_inode(dentry);
if (au_ibbot(inode) < bdst)
au_set_ibbot(inode, bdst);
au_set_h_iptr(inode, bdst, au_igrab(h_inode),
au_hi_flags(inode, /*isdir*/1));
goto out; /* success */
/* revert */
out_opq:
if (au_ftest_cpdown(*flags, DIROPQ)) {
inode_lock_nested(h_inode, AuLsc_I_CHILD);
rerr = au_diropq_remove(dentry, bdst);
inode_unlock(h_inode);
if (unlikely(rerr)) {
AuIOErr("failed removing diropq for %pd b%d (%d)\n",
dentry, bdst, rerr);
err = -EIO;
goto out;
}
}
out_dir:
if (au_ftest_cpdown(*flags, MADE_DIR)) {
rerr = vfsub_sio_rmdir(au_h_iptr(dir, bdst), &h_path);
if (unlikely(rerr)) {
AuIOErr("failed removing %pd b%d (%d)\n",
dentry, bdst, rerr);
err = -EIO;
}
}
out_put:
au_set_h_dptr(dentry, bdst, NULL);
if (au_dbbot(dentry) == bdst)
au_update_dbbot(dentry);
out:
dput(parent);
return err;
}
int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst)
{
int err;
unsigned int flags;
flags = 0;
err = au_cp_dirs(dentry, bdst, au_cpdown_dir, &flags);
return err;
}
/* ---------------------------------------------------------------------- */
/* policies for create */
int au_wbr_nonopq(struct dentry *dentry, aufs_bindex_t bindex)
{
int err, i, j, ndentry;
aufs_bindex_t bopq;
struct au_dcsub_pages dpages;
struct au_dpage *dpage;
struct dentry **dentries, *parent, *d;
err = au_dpages_init(&dpages, GFP_NOFS);
if (unlikely(err))
goto out;
parent = dget_parent(dentry);
err = au_dcsub_pages_rev_aufs(&dpages, parent, /*do_include*/0);
if (unlikely(err))
goto out_free;
err = bindex;
for (i = 0; i < dpages.ndpage; i++) {
dpage = dpages.dpages + i;
dentries = dpage->dentries;
ndentry = dpage->ndentry;
for (j = 0; j < ndentry; j++) {
d = dentries[j];
di_read_lock_parent2(d, !AuLock_IR);
bopq = au_dbdiropq(d);
di_read_unlock(d, !AuLock_IR);
if (bopq >= 0 && bopq < err)
err = bopq;
}
}
out_free:
dput(parent);
au_dpages_free(&dpages);
out:
return err;
}
static int au_wbr_bu(struct super_block *sb, aufs_bindex_t bindex)
{
for (; bindex >= 0; bindex--)
if (!au_br_rdonly(au_sbr(sb, bindex)))
return bindex;
return -EROFS;
}
/* top down parent */
static int au_wbr_create_tdp(struct dentry *dentry,
unsigned int flags __maybe_unused)
{
int err;
aufs_bindex_t btop, bindex;
struct super_block *sb;
struct dentry *parent, *h_parent;
sb = dentry->d_sb;
btop = au_dbtop(dentry);
err = btop;
if (!au_br_rdonly(au_sbr(sb, btop)))
goto out;
err = -EROFS;
parent = dget_parent(dentry);
for (bindex = au_dbtop(parent); bindex < btop; bindex++) {
h_parent = au_h_dptr(parent, bindex);
if (!h_parent || d_is_negative(h_parent))
continue;
if (!au_br_rdonly(au_sbr(sb, bindex))) {
err = bindex;
break;
}
}
dput(parent);
/* bottom up here */
if (unlikely(err < 0)) {
err = au_wbr_bu(sb, btop - 1);
if (err >= 0)
err = au_wbr_nonopq(dentry, err);
}
out:
AuDbg("b%d\n", err);
return err;
}
/* ---------------------------------------------------------------------- */
/* policies for copyup */
/* top down parent */
static int au_wbr_copyup_tdp(struct dentry *dentry)
{
return au_wbr_create_tdp(dentry, /*flags, anything is ok*/0);
}
/* ---------------------------------------------------------------------- */
struct au_wbr_copyup_operations au_wbr_copyup_ops[] = {
[AuWbrCopyup_TDP] = {
.copyup = au_wbr_copyup_tdp
}
};
struct au_wbr_create_operations au_wbr_create_ops[] = {
[AuWbrCreate_TDP] = {
.create = au_wbr_create_tdp
}
};
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