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

aufs: xino 2/2, callers



XINO and XIB files are read and written frequently after unlinked, and
it means that the remote filesystems are not suitable for them.
Additionally aufs shows their metadata via debugfs (in later commit).
To make it easier to do this, aufs expects branch filesystems to
maintain their i_size and i_blocks. And it means some filesystem are not
suitable for XINO.
Signed-off-by: default avatarJ. R. Okajima <hooanon05g@gmail.com>
parent 6fe05098
......@@ -39,6 +39,70 @@ About the policies which supports copy-down a directory, see
wbr_policy.txt too.
Branch and XINO(External Inode Number Translation Table)
----------------------------------------------------------------------
Every branch has its own xino (external inode number translation table)
file. The xino file is created and unlinked by aufs internally. When two
members of a union exist on the same filesystem, they share the single
xino file.
The struct of a xino file is simple, just a sequence of aufs inode
numbers which is indexed by the lower inode number.
In the above sample, assume the inode number of /ro/fileA is i111 and
aufs assigns the inode number i999 for fileA. Then aufs writes 999 as
4(8) bytes at 111 * 4(8) bytes offset in the xino file.
When the inode numbers are not contiguous, the xino file will be sparse
which has a hole in it and doesn't consume as much disk space as it
might appear. If your branch filesystem consumes disk space for such
holes, then you should specify 'xino=' option at mounting aufs.
Aufs has a mount option to free the disk blocks for such holes in XINO
files on tmpfs or ramdisk. But it is not so effective actually. If you
meet a problem of disk shortage due to XINO files, then you should try
"tmpfs-ino.patch" (and "vfs-ino.patch" too) in aufs4-standalone.git.
The patch localizes the assignment inumbers per tmpfs-mount and avoid
the holes in XINO files.
Also a writable branch has three kinds of "whiteout bases". All these
are existed when the branch is joined to aufs, and their names are
whiteout-ed doubly, so that users will never see their names in aufs
hierarchy.
1. a regular file which will be hardlinked to all whiteouts.
2. a directory to store a pseudo-link.
3. a directory to store an "orphan"-ed file temporary.
1. Whiteout Base
When you remove a file on a readonly branch, aufs handles it as a
logical deletion and creates a whiteout on the upper writable branch
as a hardlink of this file in order not to consume inode on the
writable branch.
2. Pseudo-link Dir
See below, Pseudo-link.
3. Step-Parent Dir
When "fileC" exists on the lower readonly branch only and it is
opened and removed with its parent dir, and then user writes
something into it, then aufs copies-up fileC to this
directory. Because there is no other dir to store fileC. After
creating a file under this dir, the file is unlinked.
Because aufs supports manipulating branches, ie. add/delete/change
dynamically, a branch has its own id. When the branch order changes,
aufs finds the new index by searching the branch id.
XIB(external inode number bitmap)
----------------------------------------------------------------------
Addition to the xino file per a branch, aufs has an external inode number
bitmap in a superblock object. It is also an internal file such like a
xino file.
It is a simple bitmap to mark whether the aufs inode number is in-use or
not.
To reduce the file I/O, aufs prepares a single memory page to cache xib.
As well as XINO files, aufs has a feature to truncate/refresh XIB to
reduce the number of consumed disk blocks for these files.
Workqueue
----------------------------------------------------------------------
Aufs sometimes requires privilege access to a branch. For instance,
......
......@@ -7,6 +7,7 @@
* branch management
*/
#include <linux/file.h>
#include "aufs.h"
/*
......@@ -14,6 +15,8 @@
*/
static void au_br_do_free(struct au_branch *br)
{
au_xino_put(br);
AuLCntZero(au_lcnt_read(&br->br_count, /*do_rev*/0));
au_lcnt_fin(&br->br_count, /*do_sync*/0);
......@@ -91,6 +94,9 @@ static struct au_branch *au_br_alloc(struct super_block *sb, int new_nbranch,
add_branch = kzalloc(sizeof(*add_branch), GFP_NOFS);
if (unlikely(!add_branch))
goto out;
add_branch->br_xino = au_xino_alloc(/*nfile*/1);
if (unlikely(!add_branch->br_xino))
goto out_br;
root = sb->s_root;
err = au_sbr_realloc(au_sbi(sb), new_nbranch, /*may_shrink*/0);
......@@ -104,6 +110,9 @@ 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);
out_br:
au_kfree_rcu(add_branch);
out:
return ERR_PTR(err);
......@@ -191,6 +200,9 @@ static int au_br_init(struct au_branch *br, struct super_block *sb,
struct au_opt_add *add)
{
int err;
struct au_branch *brbase;
struct file *xf;
struct inode *h_inode;
err = 0;
br->br_perm = add->perm;
......@@ -199,9 +211,22 @@ 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_opt_test(au_mntflags(sb), XINO)) {
brbase = au_sbr(sb, 0);
xf = au_xino_file(brbase->br_xino, /*idx*/-1);
AuDebugOn(!xf);
h_inode = d_inode(add->path.dentry);
err = au_xino_init_br(sb, br, h_inode->i_ino, &xf->f_path);
if (unlikely(err)) {
AuDebugOn(au_xino_file(br->br_xino, /*idx*/-1));
goto out_err;
}
}
path_get(&br->br_path);
goto out; /* success */
out_err:
memset(&br->br_path, 0, sizeof(br->br_path));
out:
return err;
......
......@@ -123,6 +123,13 @@ struct au_xino *au_xino_alloc(unsigned int nfile);
int au_xino_put(struct au_branch *br);
struct file *au_xino_file1(struct au_xino *xi);
struct au_opt_xino;
void au_xino_clr(struct super_block *sb);
int au_xino_set(struct super_block *sb, struct au_opt_xino *xiopt);
struct file *au_xino_def(struct super_block *sb);
int au_xino_init_br(struct super_block *sb, struct au_branch *br, ino_t hino,
struct path *base);
ino_t au_xino_new_ino(struct super_block *sb);
void au_xino_delete_inode(struct inode *inode, const int unlinked);
......
......@@ -162,11 +162,13 @@ static int do_pri_br(aufs_bindex_t bindex, struct au_branch *br)
goto out;
dpri("s%d: {perm 0x%x, id %d}, "
"%s, dev 0x%02x%02x, flags 0x%lx, cnt %d, active %d\n",
"%s, dev 0x%02x%02x, flags 0x%lx, cnt %d, active %d, "
"xino %d\n",
bindex, br->br_perm, br->br_id,
au_sbtype(sb), MAJOR(sb->s_dev), MINOR(sb->s_dev),
sb->s_flags, sb->s_count,
atomic_read(&sb->s_active));
atomic_read(&sb->s_active),
!!au_xino_file(br->br_xino, /*idx*/-1));
return 0;
out:
......
......@@ -25,6 +25,33 @@ static inline const char *au_sbtype(struct super_block *sb)
return sb->s_type->name;
}
static inline int au_test_nfs(struct super_block *sb __maybe_unused)
{
#if IS_ENABLED(CONFIG_NFS_FS)
return sb->s_magic == NFS_SUPER_MAGIC;
#else
return 0;
#endif
}
static inline int au_test_xfs(struct super_block *sb __maybe_unused)
{
#if IS_ENABLED(CONFIG_XFS_FS)
return sb->s_magic == XFS_SB_MAGIC;
#else
return 0;
#endif
}
static inline int au_test_tmpfs(struct super_block *sb __maybe_unused)
{
#ifdef CONFIG_TMPFS
return sb->s_magic == TMPFS_MAGIC;
#else
return 0;
#endif
}
static inline int au_test_ecryptfs(struct super_block *sb __maybe_unused)
{
#if IS_ENABLED(CONFIG_ECRYPT_FS)
......@@ -39,6 +66,15 @@ static inline int au_test_ramfs(struct super_block *sb)
return sb->s_magic == RAMFS_MAGIC;
}
static inline int au_test_ubifs(struct super_block *sb __maybe_unused)
{
#if IS_ENABLED(CONFIG_UBIFS_FS)
return sb->s_magic == UBIFS_SUPER_MAGIC;
#else
return 0;
#endif
}
static inline int au_test_procfs(struct super_block *sb __maybe_unused)
{
#ifdef CONFIG_PROC_FS
......@@ -66,6 +102,19 @@ static inline int au_test_configfs(struct super_block *sb __maybe_unused)
#endif
}
static inline int au_test_minix(struct super_block *sb __maybe_unused)
{
#if IS_ENABLED(CONFIG_MINIX_FS)
return sb->s_magic == MINIX3_SUPER_MAGIC
|| sb->s_magic == MINIX2_SUPER_MAGIC
|| sb->s_magic == MINIX2_SUPER_MAGIC2
|| sb->s_magic == MINIX_SUPER_MAGIC
|| sb->s_magic == MINIX_SUPER_MAGIC2;
#else
return 0;
#endif
}
static inline int au_test_securityfs(struct super_block *sb __maybe_unused)
{
#ifdef CONFIG_SECURITYFS
......@@ -75,6 +124,15 @@ static inline int au_test_securityfs(struct super_block *sb __maybe_unused)
#endif
}
static inline int au_test_btrfs(struct super_block *sb __maybe_unused)
{
#if IS_ENABLED(CONFIG_BTRFS_FS)
return sb->s_magic == BTRFS_SUPER_MAGIC;
#else
return 0;
#endif
}
static inline int au_test_xenfs(struct super_block *sb __maybe_unused)
{
#if IS_ENABLED(CONFIG_XENFS)
......@@ -93,6 +151,15 @@ static inline int au_test_debugfs(struct super_block *sb __maybe_unused)
#endif
}
static inline int au_test_nilfs(struct super_block *sb __maybe_unused)
{
#if IS_ENABLED(CONFIG_NILFS)
return sb->s_magic == NILFS_SUPER_MAGIC;
#else
return 0;
#endif
}
/* ---------------------------------------------------------------------- */
/*
* they can't be an aufs branch.
......@@ -112,5 +179,46 @@ static inline int au_test_fs_unsuppoted(struct super_block *sb)
|| au_test_aufs(sb); /* will be supported in next version */
}
static inline int au_test_fs_remote(struct super_block *sb)
{
return !au_test_tmpfs(sb)
&& !(sb->s_type->fs_flags & FS_REQUIRES_DEV);
}
/* ---------------------------------------------------------------------- */
/*
* Note: these functions (below) are created after reading ->getattr() in all
* filesystems under linux/fs. it means we have to do so in every update...
*/
/*
* filesystems which don't maintain i_size or i_blocks.
*/
static inline int au_test_fs_bad_iattr_size(struct super_block *sb)
{
return au_test_xfs(sb)
|| au_test_btrfs(sb)
|| au_test_ubifs(sb)
/* || au_test_minix(sb) */ /* untested */
;
}
/* ---------------------------------------------------------------------- */
/*
* the filesystem where the xino files placed must support i/o after unlink and
* maintain i_size and i_blocks.
*/
static inline int au_test_fs_bad_xino(struct super_block *sb)
{
return au_test_fs_remote(sb)
|| au_test_fs_bad_iattr_size(sb)
/* don't want unnecessary work for xino */
|| au_test_aufs(sb)
|| au_test_ecryptfs(sb)
|| au_test_nilfs(sb);
}
#endif /* __KERNEL__ */
#endif /* __AUFS_FSTYPE_H__ */
......@@ -28,6 +28,17 @@ void au_hiput(struct au_hinode *hinode)
iput(hinode->hi_inode);
}
unsigned int au_hi_flags(struct inode *inode, int isdir)
{
unsigned int flags;
const unsigned int mnt_flags = au_mntflags(inode->i_sb);
flags = 0;
if (au_opt_test(mnt_flags, XINO))
au_fset_hi(flags, XINO);
return flags;
}
void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex,
struct inode *h_inode, unsigned int flags)
{
......@@ -45,6 +56,7 @@ void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex,
au_hiput(hinode);
hinode->hi_inode = h_inode;
if (h_inode) {
int err;
struct super_block *sb = inode->i_sb;
struct au_branch *br;
......@@ -55,6 +67,12 @@ void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex,
au_cpup_igen(inode, h_inode);
br = au_sbr(sb, bindex);
hinode->hi_id = br->br_id;
if (au_ftest_hi(flags, XINO)) {
err = au_xino_write(sb, bindex, h_inode->i_ino,
inode->i_ino);
if (unlikely(err))
AuIOErr1("failed au_xino_write() %d\n", err);
}
}
}
......@@ -143,9 +161,12 @@ void au_iinfo_fin(struct inode *inode)
struct au_iinfo *iinfo;
struct au_hinode *hi;
aufs_bindex_t bindex, bbot;
const unsigned char unlinked = !inode->i_nlink;
AuDebugOn(au_is_bad_inode(inode));
au_xino_delete_inode(inode, unlinked);
iinfo = au_ii(inode);
bindex = iinfo->ii_btop;
if (bindex >= 0) {
......
......@@ -57,6 +57,15 @@ struct inode *au_igrab(struct inode *inode);
/* iinfo.c */
struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex);
void au_hiput(struct au_hinode *hinode);
unsigned int au_hi_flags(struct inode *inode, int isdir);
/* hinode flags */
#define AuHi_XINO 1
#define au_ftest_hi(flags, name) ((flags) & AuHi_##name)
#define au_fset_hi(flags, name) \
do { (flags) |= AuHi_##name; } while (0)
#define au_fclr_hi(flags, name) \
do { (flags) &= ~AuHi_##name; } while (0)
void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex,
struct inode *h_inode, unsigned int flags);
......
# SPDX-License-Identifier: GPL-2.0
# defined in ${srctree}/fs/xfs/xfs_sb.h
# tristate
ifdef CONFIG_XFS_FS
ccflags-y += -DXFS_SB_MAGIC=0x58465342
endif
# defined in ${srctree}/fs/configfs/mount.c
# tristate
ifdef CONFIG_CONFIGFS_FS
ccflags-y += -DCONFIGFS_MAGIC=0x62656570
endif
# defined in ${srctree}/fs/ubifs/ubifs.h
# tristate
ifdef CONFIG_UBIFS_FS
ccflags-y += -DUBIFS_SUPER_MAGIC=0x24051905
endif
......@@ -7,6 +7,7 @@
* mount options/flags
*/
#include <linux/file.h>
#include <linux/namei.h>
#include <linux/types.h> /* a distribution requires */
#include <linux/parser.h>
......@@ -17,6 +18,7 @@
enum {
Opt_br,
Opt_add,
Opt_xino, Opt_noxino,
Opt_tail, Opt_ignore, Opt_ignore_silent, Opt_err
};
......@@ -24,6 +26,9 @@ static match_table_t options = {
{Opt_br, "br=%s"},
{Opt_br, "br:%s"},
{Opt_xino, "xino=%s"},
{Opt_noxino, "noxino"},
/* internal use for the scripts */
{Opt_ignore_silent, "si=%s"},
......@@ -185,6 +190,7 @@ static void dump_opts(struct au_opts *opts)
/* reduce stack space */
union {
struct au_opt_add *add;
struct au_opt_xino *xino;
} u;
struct au_opt *opt;
......@@ -197,6 +203,13 @@ static void dump_opts(struct au_opts *opts)
u.add->bindex, u.add->pathname, u.add->perm,
u.add->path.dentry);
break;
case Opt_xino:
u.xino = &opt->xino;
AuDbg("xino {%s %pD}\n", u.xino->path, u.xino->file);
break;
case Opt_noxino:
AuLabel(noxino);
break;
default:
BUG();
}
......@@ -215,6 +228,9 @@ void au_opts_free(struct au_opts *opts)
case Opt_add:
path_put(&opt->add.path);
break;
case Opt_xino:
fput(opt->xino.file);
break;
}
opt++;
}
......@@ -252,6 +268,32 @@ out:
return err;
}
static int au_opts_parse_xino(struct super_block *sb, struct au_opt_xino *xino,
substring_t args[])
{
int err;
struct file *file;
file = au_xino_create(sb, args[0].from, /*silent*/0);
err = PTR_ERR(file);
if (IS_ERR(file))
goto out;
err = -EINVAL;
if (unlikely(file->f_path.dentry->d_sb == sb)) {
fput(file);
pr_err("%s must be outside\n", args[0].from);
goto out;
}
err = 0;
xino->file = file;
xino->path = args[0].from;
out:
return err;
}
/* called without aufs lock */
int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts)
{
......@@ -308,6 +350,17 @@ int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts)
opt->type = token;
break;
case Opt_xino:
err = au_opts_parse_xino(sb, &opt->xino, a->args);
if (!err)
opt->type = token;
break;
case Opt_noxino:
err = 0;
opt->type = token;
break;
case Opt_ignore:
pr_warn("ignored %s\n", opt_str);
/*FALLTHROUGH*/
......@@ -361,6 +414,30 @@ static int au_opt_br(struct super_block *sb, struct au_opt *opt,
}
break;
}
return err;
}
static int au_opt_xino(struct super_block *sb, struct au_opt *opt,
struct au_opt_xino **opt_xino,
struct au_opts *opts)
{
int err;
err = 0;
switch (opt->type) {
case Opt_xino:
err = au_xino_set(sb, &opt->xino);
if (unlikely(err))
break;
*opt_xino = &opt->xino;
break;
case Opt_noxino:
au_xino_clr(sb);
*opt_xino = (void *)-1;
break;
}
return err;
}
......@@ -371,11 +448,13 @@ int au_opts_mount(struct super_block *sb, struct au_opts *opts)
unsigned int tmp;
aufs_bindex_t bbot;
struct au_opt *opt;
struct au_opt_xino *opt_xino, xino;
struct au_sbinfo *sbinfo;
SiMustWriteLock(sb);
err = 0;
opt_xino = NULL;
opt = opts->opt;
while (err >= 0 && opt->type != Opt_tail)
/* re-commit later */
......@@ -386,8 +465,11 @@ int au_opts_mount(struct super_block *sb, struct au_opts *opts)
else if (unlikely(err < 0))
goto out;
/* disable xino temporary */
sbinfo = au_sbi(sb);
tmp = sbinfo->si_mntflags;
au_opt_clr(sbinfo->si_mntflags, XINO);
opt = opts->opt;
while (err >= 0 && opt->type != Opt_tail)
err = au_opt_br(sb, opt++, opts);
......@@ -403,6 +485,29 @@ int au_opts_mount(struct super_block *sb, struct au_opts *opts)
goto out;
}
if (au_opt_test(tmp, XINO))
au_opt_set(sbinfo->si_mntflags, XINO);
opt = opts->opt;
while (!err && opt->type != Opt_tail)
err = au_opt_xino(sb, opt++, &opt_xino, opts);
if (unlikely(err))
goto out;
/* restore xino */
if (au_opt_test(tmp, XINO) && !opt_xino) {
xino.file = au_xino_def(sb);
err = PTR_ERR(xino.file);
if (IS_ERR(xino.file))
goto out;
err = au_xino_set(sb, &xino);
fput(xino.file);
if (unlikely(err))
goto out;
}
bbot = au_sbbot(sb);
out:
return err;
}
......@@ -14,6 +14,8 @@
#include <linux/path.h>
struct file;
/* ---------------------------------------------------------------------- */
/* mount flags */
......@@ -39,9 +41,15 @@ struct au_opt_add {
struct path path;
};
struct au_opt_xino {
char *path;
struct file *file;
};
struct au_opt {
int type;
union {
struct au_opt_xino xino;
struct au_opt_add add;
/* add more later */
};
......
......@@ -25,6 +25,7 @@ void au_si_free(struct kobject *kobj)
au_rw_write_unlock(&sbinfo->si_rwsem);
au_kfree_try_rcu(sbinfo->si_branch);
mutex_destroy(&sbinfo->si_xib_mtx);
AuRwDestroy(&sbinfo->si_rwsem);
au_kfree_rcu(sbinfo);
......@@ -51,6 +52,11 @@ int au_si_alloc(struct super_block *sb)
sbinfo->si_bbot = -1;
sbinfo->si_last_br_id = AUFS_BRANCH_MAX / 2;
sbinfo->si_mntflags = AuOpt_Def;
mutex_init(&sbinfo->si_xib_mtx);
/* leave si_xib_last_pindex and si_xib_next_bit */
/* leave other members for sysaufs and si_mnt. */
sb->s_fs_info = sbinfo;
return 0; /* success */
......
......@@ -80,9 +80,8 @@ static void call_unlink(void *args)
struct unlink_args *a = args;
struct dentry *d = a->path->dentry;
struct inode *h_inode;
/* re-commit later */
const int stop_sillyrename = 0; /* (au_test_nfs(d->d_sb)
* && au_dcount(d) == 1); */
const int stop_sillyrename = (au_test_nfs(d->d_sb)
&& au_dcount(d) == 1);
IMustLock(a->dir);
......
......@@ -28,6 +28,37 @@
#include <linux/uaccess.h>
#include "aufs.h"
static aufs_bindex_t sbr_find_shared(struct super_block *sb, aufs_bindex_t btop,
aufs_bindex_t bbot,
struct super_block *h_sb)
{
/* todo: try binary-search if the branches are many */
for (; btop <= bbot; btop++)
if (h_sb == au_sbr_sb(sb, btop))
return btop;
return -1;
}
/*
* find another branch who is on the same filesystem of the specified
* branch{@btgt}. search until @bbot.
*/
static aufs_bindex_t is_sb_shared(struct super_block *sb, aufs_bindex_t btgt,
aufs_bindex_t bbot)
{
aufs_bindex_t bindex;
struct super_block *tgt_sb;
tgt_sb = au_sbr_sb(sb, btgt);
bindex = sbr_find_shared(sb, /*btop*/0, btgt - 1, tgt_sb);
if (bindex < 0)
bindex = sbr_find_shared(sb, btgt + 1, bbot, tgt_sb);
return bindex;
}
/* ---------------------------------------------------------------------- */
/*
* stop unnecessary notify events at creating xino files
*/
......@@ -164,14 +195,12 @@ struct file *au_xino_create(struct super_block *sb, char *fpath, int silent)
pr_err("%s must be outside\n", fpath);
goto out;
}
#if 0 /* re-commit later */
if (unlikely(au_test_fs_bad_xino(d->d_sb))) {
if (!silent)
pr_err("xino doesn't support %s(%s)\n",
fpath, au_sbtype(d->d_sb));
goto out;
}
#endif
return file; /* success */
out:
......@@ -924,7 +953,6 @@ out:
return xi;
}
/* re-commit later */ __maybe_unused
static int au_xino_init(struct au_branch *br, int idx, struct file *file)
{
int err;
......@@ -996,6 +1024,322 @@ int au_xino_put(struct au_branch *br)
/* ---------------------------------------------------------------------- */
/*
* xino mount option handlers
*/
/* xino bitmap */
static void xino_clear_xib(struct super_block *sb)
{
struct au_sbinfo *sbinfo;
SiMustWriteLock(sb);
sbinfo = au_sbi(sb);
/* unnecessary to clear sbinfo->si_xread and ->si_xwrite */
if (sbinfo->si_xib)
fput(sbinfo->si_xib);
sbinfo->si_xib = NULL;
if (sbinfo->si_xib_buf)
free_page((unsigned long)sbinfo->si_xib_buf);
sbinfo->si_xib_buf = NULL;
}
static int au_xino_set_xib(struct super_block *sb, struct path *path)
{
int err;
loff_t pos;
struct au_sbinfo *sbinfo;
struct file *file;
struct super_block *xi_sb;
SiMustWriteLock(sb);
sbinfo = au_sbi(sb);
file = au_xino_create2(sb, path, sbinfo->si_xib);
err = PTR_ERR(file);
if (IS_ERR(file))
goto out;
if (sbinfo->si_xib)
fput(sbinfo->si_xib);
sbinfo->si_xib = file;
sbinfo->si_xread = vfs_readf(file);
sbinfo->si_xwrite = vfs_writef(file);
xi_sb = file_inode(file)->i_sb;
sbinfo->si_ximaxent = xi_sb->s_maxbytes;
if (unlikely(sbinfo->si_ximaxent < PAGE_SIZE)) {
err = -EIO;
pr_err("s_maxbytes(%llu) on %s is too small\n",
(u64)sbinfo->si_ximaxent, au_sbtype(xi_sb));
goto out_unset;
}
sbinfo->si_ximaxent /= sizeof(ino_t);
err = -ENOMEM;
if (!sbinfo->si_xib_buf)
sbinfo->si_xib_buf = (void *)get_zeroed_page(GFP_NOFS);
if (unlikely(!sbinfo->si_xib_buf))
goto out_unset;
sbinfo->si_xib_last_pindex = 0;
sbinfo->si_xib_next_bit = 0;
if (vfsub_f_size_read(file) < PAGE_SIZE) {
pos = 0;
err = xino_fwrite(sbinfo->si_xwrite, file, sbinfo->si_xib_buf,
PAGE_SIZE, &pos);
if (unlikely(err != PAGE_SIZE))
goto out_free;
}
err = 0;
goto out; /* success */
out_free:
if (sbinfo->si_xib_buf)
free_page((unsigned long)sbinfo->si_xib_buf);
sbinfo->si_xib_buf = NULL;
if (err >= 0)
err = -EIO;
out_unset:
fput(sbinfo->si_xib);
sbinfo->si_xib = NULL;
out:
AuTraceErr(err);
return err;
}
/* xino for each branch */
static void xino_clear_br(struct super_block *sb)
{
aufs_bindex_t bindex, bbot;
struct au_branch *br;
bbot = au_sbbot(sb);
for (bindex = 0; bindex <= bbot; bindex++) {
br = au_sbr(sb, bindex);
AuDebugOn(!br);
au_xino_put(br);
}
}
static void au_xino_set_br_shared(struct super_block *sb, struct au_branch *br,
aufs_bindex_t bshared)
{
struct au_branch *brshared;
brshared = au_sbr(sb, bshared);
AuDebugOn(!brshared->br_xino);
AuDebugOn(!brshared->br_xino->xi_file);
if (br->br_xino != brshared->br_xino) {
au_xino_get(brshared);
au_xino_put(br);
br->br_xino = brshared->br_xino;
}
}
struct au_xino_do_set_br {
vfs_writef_t writef;
struct au_branch *br;
ino_t h_ino;
aufs_bindex_t bshared;
};
static int au_xino_do_set_br(struct super_block *sb, struct path *path,
struct au_xino_do_set_br *args)
{
int err;
struct au_xi_calc calc;
struct file *file;
struct au_branch *br;
struct au_xi_new xinew = {
.base = path
};
br = args->br;
xinew.xi = br->br_xino;
au_xi_calc(sb, args->h_ino, &calc);
xinew.copy_src = au_xino_file(xinew.xi, calc.idx);
if (args->bshared >= 0)
/* shared xino */
au_xino_set_br_shared(sb, br, args->bshared);
else if (!xinew.xi) {
/* new xino */
err = au_xino_init(br, calc.idx, xinew.copy_src);
if (unlikely(err))
goto out;
}
/* force re-creating */
xinew.xi = br->br_xino;
xinew.idx = calc.idx;
mutex_lock(&xinew.xi->xi_mtx);
file = au_xi_new(sb, &xinew);
mutex_unlock(&xinew.xi->xi_mtx);
err = PTR_ERR(file);
if (IS_ERR(file))
goto out;
AuDebugOn(!file);
err = au_xino_do_write(args->writef, file, &calc, AUFS_ROOT_INO);
if (unlikely(err))
au_xino_put(br);
out:
AuTraceErr(err);
return err;
}
static int au_xino_set_br(struct super_block *sb, struct path *path)
{
int err;
aufs_bindex_t bindex, bbot;
struct au_xino_do_set_br args;
struct inode *inode;
SiMustWriteLock(sb);
bbot = au_sbbot(sb);
inode = d_inode(sb->s_root);
args.writef = au_sbi(sb)->si_xwrite;
for (bindex = 0; bindex <= bbot; bindex++) {
args.h_ino = au_h_iptr(inode, bindex)->i_ino;
args.br = au_sbr(sb, bindex);
args.bshared = is_sb_shared(sb, bindex, bindex - 1);
err = au_xino_do_set_br(sb, path, &args);
if (unlikely(err))
break;
}
AuTraceErr(err);
return err;
}
void au_xino_clr(struct super_block *sb)
{
struct au_sbinfo *sbinfo;
xino_clear_xib(sb);
xino_clear_br(sb);
sbinfo = au_sbi(sb);
/* lvalue, do not call au_mntflags() */
au_opt_clr(sbinfo->si_mntflags, XINO);
}
int au_xino_set(struct super_block *sb, struct au_opt_xino *xiopt)
{
int err;
struct dentry *dentry, *parent;
struct au_sbinfo *sbinfo;
struct path *path;
SiMustWriteLock(sb);
err = 0;
sbinfo = au_sbi(sb);
path = &xiopt->file->f_path;
dentry = path->dentry;
parent = dget_parent(dentry);
au_opt_set(sbinfo->si_mntflags, XINO);
err = au_xino_set_xib(sb, path);
/* si_x{read,write} are set */
if (!err)
err = au_xino_set_br(sb, path);
if (!err)
goto out; /* success */
/* reset all */
AuIOErr("failed setting xino(%d).\n", err);
au_xino_clr(sb);
out:
dput(parent);
return err;
}
/*
* create a xinofile at the default place/path.
*/
struct file *au_xino_def(struct super_block *sb)
{
struct file *file;
char *page, *p;
struct au_branch *br;
struct super_block *h_sb;
struct path path;
aufs_bindex_t bbot, bindex, bwr;
br = NULL;
bbot = au_sbbot(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 */
&& !au_test_fs_bad_xino(au_br_sb(br))) {
bwr = bindex;
break;
}
}
if (bwr >= 0) {
file = ERR_PTR(-ENOMEM);
page = (void *)__get_free_page(GFP_NOFS);
if (unlikely(!page))
goto out;
path.mnt = au_br_mnt(br);
path.dentry = au_h_dptr(sb->s_root, bwr);
p = d_path(&path, page, PATH_MAX - sizeof(AUFS_XINO_FNAME));
file = (void *)p;
if (!IS_ERR(p)) {
strcat(p, "/" AUFS_XINO_FNAME);
AuDbg("%s\n", p);
file = au_xino_create(sb, p, /*silent*/0);
}
free_page((unsigned long)page);
} else {
file = au_xino_create(sb, AUFS_XINO_DEFPATH, /*silent*/0);
if (IS_ERR(file))
goto out;
h_sb = file->f_path.dentry->d_sb;
if (unlikely(au_test_fs_bad_xino(h_sb))) {
pr_err("xino doesn't support %s(%s)\n",
AUFS_XINO_DEFPATH, au_sbtype(h_sb));
fput(file);
file = ERR_PTR(-EINVAL);
}
}
out:
return file;
}
/* ---------------------------------------------------------------------- */
/*
* initialize the xinofile for the specified branch @br
* at the place/path where @base_file indicates.
* test whether another branch is on the same filesystem or not,
* if found then share the xinofile with another branch.
*/
int au_xino_init_br(struct super_block *sb, struct au_branch *br, ino_t h_ino,
struct path *base)
{
int err;
struct au_xino_do_set_br args = {
.h_ino = h_ino,
.br = br
};
args.writef = au_sbi(sb)->si_xwrite;
args.bshared = sbr_find_shared(sb, /*btop*/0, au_sbbot(sb),
au_br_sb(br));
err = au_xino_do_set_br(sb, base, &args);
if (unlikely(err))
au_xino_put(br);
return err;
}
/* ---------------------------------------------------------------------- */
/*
* get an unused inode number from bitmap
*/
......
......@@ -61,6 +61,8 @@ typedef int16_t aufs_bindex_t;
#define AUFS_ROOT_INO 2
#define AUFS_FIRST_INO 11
#define AUFS_XINO_FNAME "." AUFS_NAME ".xino"
#define AUFS_XINO_DEFPATH "/tmp/" AUFS_XINO_FNAME
#define AUFS_WKQ_NAME AUFS_NAME "d"
/* branch permissions and attributes */
......
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