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

aufs: export via NFS 1/2, body



Implement exporting via NFS.
The file handle is rather large (40 bytes at most + the file handle on a
branch).
The non-virtual filesystems can use an anonymous (disconnected) dentry
as long as the inode is identified, but aufs needs a dentry with dinfo
which is usually constructed.  So aufs has to find or generate the
normal dentry from the file handle in decoding.  Eg. in aufs, there
should never be the anonymous dentry.

In decoding the file handle, if both of the dentry and the inode which
are corresponding the file handle are still in cache, then they are
returned immediately.  Otherwise aufs has to find the cached parent dir
from the file handle.  If the parent dir is not cached either, the aufs
tries these steps.
- decode the branch fs's file handle and get the parent dir
- generate the path of the parent dir on the branch
- convert the branch path to aufs's path
- lookup the inode number under the aufs' path
The last one is the slowest case.

exportfs_decode_fh() (actually reconnect_path()) acquires mutex, and
this behaviour violates the locking order between aufs si_rwsem.  This
is not a problem since internal exportfs_decode_fh() is called for the
branch fs.
Simply use lockdep_off/on to silence the lockdep message.

See also the document in later commit.
This is compiled only when CONFIG_AUFS_EXPORT is enabled.
Signed-off-by: default avatarJ. R. Okajima <hooanon05g@gmail.com>
parent 5b336293
......@@ -52,6 +52,24 @@ config AUFS_SBILIST
Automatic configuration for internal use.
When aufs supports Magic SysRq or /proc, enabled automatically.
config AUFS_EXPORT
bool "NFS-exportable aufs"
depends on EXPORTFS = y
help
If you want to export your mounted aufs via NFS, then enable this
option. There are several requirements for this configuration.
See detail in aufs.5.
config AUFS_INO_T_64
bool
depends on AUFS_EXPORT
depends on 64BIT && !(ALPHA || S390)
default y
help
Automatic configuration for internal use.
/* typedef unsigned long/int __kernel_ino_t */
/* alpha and s390x are int */
config AUFS_DEBUG
bool "Debug aufs"
help
......
......@@ -22,4 +22,5 @@ aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \
# all are boolean
aufs-$(CONFIG_PROC_FS) += procfs.o plink.o
aufs-$(CONFIG_SYSFS) += sysfs.o
aufs-$(CONFIG_AUFS_EXPORT) += export.o
aufs-$(CONFIG_AUFS_DEBUG) += debug.o
......@@ -160,6 +160,8 @@ struct au_opt_add;
int au_br_add(struct super_block *sb, struct au_opt_add *add);
/* xino.c */
static const loff_t au_loff_max = LLONG_MAX;
aufs_bindex_t au_xi_root(struct super_block *sb, struct dentry *dentry);
struct file *au_xino_create(struct super_block *sb, char *fpath, int silent);
struct file *au_xino_create2(struct super_block *sb, struct path *base,
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2005-2019 Junjiro R. Okajima
*/
/*
* export via nfs
*/
#include <linux/exportfs.h>
#include <linux/fs_struct.h>
#include <linux/namei.h>
#include <linux/nsproxy.h>
#include <linux/random.h>
#include <linux/writeback.h>
#include "aufs.h"
union conv {
#ifdef CONFIG_AUFS_INO_T_64
__u32 a[2];
#else
__u32 a[1];
#endif
ino_t ino;
};
static ino_t decode_ino(__u32 *a)
{
union conv u;
BUILD_BUG_ON(sizeof(u.ino) != sizeof(u.a));
u.a[0] = a[0];
#ifdef CONFIG_AUFS_INO_T_64
u.a[1] = a[1];
#endif
return u.ino;
}
static void encode_ino(__u32 *a, ino_t ino)
{
union conv u;
u.ino = ino;
a[0] = u.a[0];
#ifdef CONFIG_AUFS_INO_T_64
a[1] = u.a[1];
#endif
}
/* NFS file handle */
enum {
Fh_br_id,
Fh_sigen,
#ifdef CONFIG_AUFS_INO_T_64
/* support 64bit inode number */
Fh_ino1,
Fh_ino2,
Fh_dir_ino1,
Fh_dir_ino2,
#else
Fh_ino1,
Fh_dir_ino1,
#endif
Fh_igen,
Fh_h_type,
Fh_tail,
Fh_ino = Fh_ino1,
Fh_dir_ino = Fh_dir_ino1
};
static int au_test_anon(struct dentry *dentry)
{
/* note: read d_flags without d_lock */
return !!(dentry->d_flags & DCACHE_DISCONNECTED);
}
int au_test_nfsd(void)
{
int ret;
struct task_struct *tsk = current;
char comm[sizeof(tsk->comm)];
ret = 0;
if (tsk->flags & PF_KTHREAD) {
get_task_comm(comm, tsk);
ret = !strcmp(comm, "nfsd");
}
return ret;
}
/* ---------------------------------------------------------------------- */
/* inode generation external table */
void au_xigen_inc(struct inode *inode)
{
loff_t pos;
ssize_t sz;
__u32 igen;
struct super_block *sb;
struct au_sbinfo *sbinfo;
sb = inode->i_sb;
AuDebugOn(!au_opt_test(au_mntflags(sb), XINO));
sbinfo = au_sbi(sb);
pos = inode->i_ino;
pos *= sizeof(igen);
igen = inode->i_generation + 1;
sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xigen, &igen,
sizeof(igen), &pos);
if (sz == sizeof(igen))
return; /* success */
if (unlikely(sz >= 0))
AuIOErr("xigen error (%zd)\n", sz);
}
int au_xigen_new(struct inode *inode)
{
int err;
loff_t pos;
ssize_t sz;
struct super_block *sb;
struct au_sbinfo *sbinfo;
struct file *file;
err = 0;
/* todo: dirty, at mount time */
if (inode->i_ino == AUFS_ROOT_INO)
goto out;
sb = inode->i_sb;
SiMustAnyLock(sb);
if (unlikely(!au_opt_test(au_mntflags(sb), XINO)))
goto out;
err = -EFBIG;
pos = inode->i_ino;
if (unlikely(au_loff_max / sizeof(inode->i_generation) - 1 < pos)) {
AuIOErr1("too large i%lld\n", pos);
goto out;
}
pos *= sizeof(inode->i_generation);
err = 0;
sbinfo = au_sbi(sb);
file = sbinfo->si_xigen;
BUG_ON(!file);
if (vfsub_f_size_read(file)
< pos + sizeof(inode->i_generation)) {
inode->i_generation = atomic_inc_return(&sbinfo->si_xigen_next);
sz = xino_fwrite(sbinfo->si_xwrite, file, &inode->i_generation,
sizeof(inode->i_generation), &pos);
} else
sz = xino_fread(sbinfo->si_xread, file, &inode->i_generation,
sizeof(inode->i_generation), &pos);
if (sz == sizeof(inode->i_generation))
goto out; /* success */
err = sz;
if (unlikely(sz >= 0)) {
err = -EIO;
AuIOErr("xigen error (%zd)\n", sz);
}
out:
return err;
}
int au_xigen_set(struct super_block *sb, struct path *path)
{
int err;
struct au_sbinfo *sbinfo;
struct file *file;
SiMustWriteLock(sb);
sbinfo = au_sbi(sb);
file = au_xino_create2(sb, path, sbinfo->si_xigen);
err = PTR_ERR(file);
if (IS_ERR(file))
goto out;
err = 0;
if (sbinfo->si_xigen)
fput(sbinfo->si_xigen);
sbinfo->si_xigen = file;
out:
AuTraceErr(err);
return err;
}
void au_xigen_clr(struct super_block *sb)
{
struct au_sbinfo *sbinfo;
SiMustWriteLock(sb);
sbinfo = au_sbi(sb);
if (sbinfo->si_xigen) {
fput(sbinfo->si_xigen);
sbinfo->si_xigen = NULL;
}
}
/* ---------------------------------------------------------------------- */
static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino,
ino_t dir_ino)
{
struct dentry *dentry, *d;
struct inode *inode;
unsigned int sigen;
dentry = NULL;
inode = ilookup(sb, ino);
if (!inode)
goto out;
dentry = ERR_PTR(-ESTALE);
sigen = au_sigen(sb);
if (unlikely(au_is_bad_inode(inode)
|| IS_DEADDIR(inode)
|| sigen != au_iigen(inode)))
goto out_iput;
dentry = NULL;
if (!dir_ino || S_ISDIR(inode->i_mode))
dentry = d_find_alias(inode);
else {
spin_lock(&inode->i_lock);
hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) {
spin_lock(&d->d_lock);
if (!au_test_anon(d)
&& d_inode(d->d_parent)->i_ino == dir_ino) {
dentry = dget_dlock(d);
spin_unlock(&d->d_lock);
break;
}
spin_unlock(&d->d_lock);
}
spin_unlock(&inode->i_lock);
}
if (unlikely(dentry && au_digen_test(dentry, sigen))) {
/* need to refresh */
dput(dentry);
dentry = NULL;
}
out_iput:
iput(inode);
out:
AuTraceErrPtr(dentry);
return dentry;
}
/* ---------------------------------------------------------------------- */
/* todo: dirty? */
/* if exportfs_decode_fh() passed vfsmount*, we could be happy */
struct au_compare_mnt_args {
/* input */
struct super_block *sb;
/* output */
struct vfsmount *mnt;
};
static int au_compare_mnt(struct vfsmount *mnt, void *arg)
{
struct au_compare_mnt_args *a = arg;
if (mnt->mnt_sb != a->sb)
return 0;
a->mnt = mntget(mnt);
return 1;
}
static struct vfsmount *au_mnt_get(struct super_block *sb)
{
int err;
struct path root;
struct au_compare_mnt_args args = {
.sb = sb
};
get_fs_root(current->fs, &root);
rcu_read_lock();
err = iterate_mounts(au_compare_mnt, &args, root.mnt);
rcu_read_unlock();
path_put(&root);
AuDebugOn(!err);
AuDebugOn(!args.mnt);
return args.mnt;
}
struct au_nfsd_si_lock {
unsigned int sigen;
aufs_bindex_t bindex, br_id;
unsigned char force_lock;
};
static int si_nfsd_read_lock(struct super_block *sb,
struct au_nfsd_si_lock *nsi_lock)
{
int err;
aufs_bindex_t bindex;
si_read_lock(sb, AuLock_FLUSH);
/* branch id may be wrapped around */
err = 0;
bindex = au_br_index(sb, nsi_lock->br_id);
if (bindex >= 0 && nsi_lock->sigen + AUFS_BRANCH_MAX > au_sigen(sb))
goto out; /* success */
err = -ESTALE;
bindex = -1;
if (!nsi_lock->force_lock)
si_read_unlock(sb);
out:
nsi_lock->bindex = bindex;
return err;
}
struct find_name_by_ino {
struct dir_context ctx;
int called, found;
ino_t ino;
char *name;
int namelen;
};
static int
find_name_by_ino(struct dir_context *ctx, const char *name, int namelen,
loff_t offset, u64 ino, unsigned int d_type)
{
struct find_name_by_ino *a = container_of(ctx, struct find_name_by_ino,
ctx);
a->called++;
if (a->ino != ino)
return 0;
memcpy(a->name, name, namelen);
a->namelen = namelen;
a->found = 1;
return 1;
}
static struct dentry *au_lkup_by_ino(struct path *path, ino_t ino,
struct au_nfsd_si_lock *nsi_lock)
{
struct dentry *dentry, *parent;
struct file *file;
struct inode *dir;
struct find_name_by_ino arg = {
.ctx = {
.actor = find_name_by_ino
}
};
int err;
parent = path->dentry;
if (nsi_lock)
si_read_unlock(parent->d_sb);
file = vfsub_dentry_open(path, au_dir_roflags);
dentry = (void *)file;
if (IS_ERR(file))
goto out;
dentry = ERR_PTR(-ENOMEM);
arg.name = (void *)__get_free_page(GFP_NOFS);
if (unlikely(!arg.name))
goto out_file;
arg.ino = ino;
arg.found = 0;
do {
arg.called = 0;
/* smp_mb(); */
err = vfsub_iterate_dir(file, &arg.ctx);
} while (!err && !arg.found && arg.called);
dentry = ERR_PTR(err);
if (unlikely(err))
goto out_name;
/* instead of ENOENT */
dentry = ERR_PTR(-ESTALE);
if (!arg.found)
goto out_name;
/* do not call vfsub_lkup_one() */
dir = d_inode(parent);
dentry = vfsub_lookup_one_len_unlocked(arg.name, parent, arg.namelen);
AuTraceErrPtr(dentry);
if (IS_ERR(dentry))
goto out_name;
AuDebugOn(au_test_anon(dentry));
if (unlikely(d_really_is_negative(dentry))) {
dput(dentry);
dentry = ERR_PTR(-ENOENT);
}
out_name:
free_page((unsigned long)arg.name);
out_file:
fput(file);
out:
if (unlikely(nsi_lock
&& si_nfsd_read_lock(parent->d_sb, nsi_lock) < 0))
if (!IS_ERR(dentry)) {
dput(dentry);
dentry = ERR_PTR(-ESTALE);
}
AuTraceErrPtr(dentry);
return dentry;
}
static struct dentry *decode_by_dir_ino(struct super_block *sb, ino_t ino,
ino_t dir_ino,
struct au_nfsd_si_lock *nsi_lock)
{
struct dentry *dentry;
struct path path;
if (dir_ino != AUFS_ROOT_INO) {
path.dentry = decode_by_ino(sb, dir_ino, 0);
dentry = path.dentry;
if (!path.dentry || IS_ERR(path.dentry))
goto out;
AuDebugOn(au_test_anon(path.dentry));
} else
path.dentry = dget(sb->s_root);
path.mnt = au_mnt_get(sb);
dentry = au_lkup_by_ino(&path, ino, nsi_lock);
path_put(&path);
out:
AuTraceErrPtr(dentry);
return dentry;
}
/* ---------------------------------------------------------------------- */
static int h_acceptable(void *expv, struct dentry *dentry)
{
return 1;
}
static char *au_build_path(struct dentry *h_parent, struct path *h_rootpath,
char *buf, int len, struct super_block *sb)
{
char *p;
int n;
struct path path;
p = d_path(h_rootpath, buf, len);
if (IS_ERR(p))
goto out;
n = strlen(p);
path.mnt = h_rootpath->mnt;
path.dentry = h_parent;
p = d_path(&path, buf, len);
if (IS_ERR(p))
goto out;
if (n != 1)
p += n;
path.mnt = au_mnt_get(sb);
path.dentry = sb->s_root;
p = d_path(&path, buf, len - strlen(p));
mntput(path.mnt);
if (IS_ERR(p))
goto out;
if (n != 1)
p[strlen(p)] = '/';
out:
AuTraceErrPtr(p);
return p;
}
static
struct dentry *decode_by_path(struct super_block *sb, ino_t ino, __u32 *fh,
int fh_len, struct au_nfsd_si_lock *nsi_lock)
{
struct dentry *dentry, *h_parent, *root;
struct super_block *h_sb;
char *pathname, *p;
struct vfsmount *h_mnt;
struct au_branch *br;
int err;
struct path path;
br = au_sbr(sb, nsi_lock->bindex);
h_mnt = au_br_mnt(br);
h_sb = h_mnt->mnt_sb;
/* todo: call lower fh_to_dentry()? fh_to_parent()? */
lockdep_off();
h_parent = exportfs_decode_fh(h_mnt, (void *)(fh + Fh_tail),
fh_len - Fh_tail, fh[Fh_h_type],
h_acceptable, /*context*/NULL);
lockdep_on();
dentry = h_parent;
if (unlikely(!h_parent || IS_ERR(h_parent))) {
AuWarn1("%s decode_fh failed, %ld\n",
au_sbtype(h_sb), PTR_ERR(h_parent));
goto out;
}
dentry = NULL;
if (unlikely(au_test_anon(h_parent))) {
AuWarn1("%s decode_fh returned a disconnected dentry\n",
au_sbtype(h_sb));
goto out_h_parent;
}
dentry = ERR_PTR(-ENOMEM);
pathname = (void *)__get_free_page(GFP_NOFS);
if (unlikely(!pathname))
goto out_h_parent;
root = sb->s_root;
path.mnt = h_mnt;
di_read_lock_parent(root, !AuLock_IR);
path.dentry = au_h_dptr(root, nsi_lock->bindex);
di_read_unlock(root, !AuLock_IR);
p = au_build_path(h_parent, &path, pathname, PAGE_SIZE, sb);
dentry = (void *)p;
if (IS_ERR(p))
goto out_pathname;
si_read_unlock(sb);
err = vfsub_kern_path(p, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path);
dentry = ERR_PTR(err);
if (unlikely(err))
goto out_relock;
dentry = ERR_PTR(-ENOENT);
AuDebugOn(au_test_anon(path.dentry));
if (unlikely(d_really_is_negative(path.dentry)))
goto out_path;
if (ino != d_inode(path.dentry)->i_ino)
dentry = au_lkup_by_ino(&path, ino, /*nsi_lock*/NULL);
else
dentry = dget(path.dentry);
out_path:
path_put(&path);
out_relock:
if (unlikely(si_nfsd_read_lock(sb, nsi_lock) < 0))
if (!IS_ERR(dentry)) {
dput(dentry);
dentry = ERR_PTR(-ESTALE);
}
out_pathname:
free_page((unsigned long)pathname);
out_h_parent:
dput(h_parent);
out:
AuTraceErrPtr(dentry);
return dentry;
}
/* ---------------------------------------------------------------------- */
static struct dentry *
aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len,
int fh_type)
{
struct dentry *dentry;
__u32 *fh = fid->raw;
struct au_branch *br;
ino_t ino, dir_ino;
struct au_nfsd_si_lock nsi_lock = {
.force_lock = 0
};
dentry = ERR_PTR(-ESTALE);
/* it should never happen, but the file handle is unreliable */
if (unlikely(fh_len < Fh_tail))
goto out;
nsi_lock.sigen = fh[Fh_sigen];
nsi_lock.br_id = fh[Fh_br_id];
/* branch id may be wrapped around */
br = NULL;
if (unlikely(si_nfsd_read_lock(sb, &nsi_lock)))
goto out;
nsi_lock.force_lock = 1;
/* is this inode still cached? */
ino = decode_ino(fh + Fh_ino);
/* it should never happen */
if (unlikely(ino == AUFS_ROOT_INO))
goto out_unlock;
dir_ino = decode_ino(fh + Fh_dir_ino);
dentry = decode_by_ino(sb, ino, dir_ino);
if (IS_ERR(dentry))
goto out_unlock;
if (dentry)
goto accept;
/* is the parent dir cached? */
br = au_sbr(sb, nsi_lock.bindex);
au_lcnt_inc(&br->br_nfiles);
dentry = decode_by_dir_ino(sb, ino, dir_ino, &nsi_lock);
if (IS_ERR(dentry))
goto out_unlock;
if (dentry)
goto accept;
/* lookup path */
dentry = decode_by_path(sb, ino, fh, fh_len, &nsi_lock);
if (IS_ERR(dentry))
goto out_unlock;
if (unlikely(!dentry))
/* todo?: make it ESTALE */
goto out_unlock;
accept:
if (!au_digen_test(dentry, au_sigen(sb))
&& d_inode(dentry)->i_generation == fh[Fh_igen])
goto out_unlock; /* success */
dput(dentry);
dentry = ERR_PTR(-ESTALE);
out_unlock:
if (br)
au_lcnt_dec(&br->br_nfiles);
si_read_unlock(sb);
out:
AuTraceErrPtr(dentry);
return dentry;
}
#if 0 /* reserved for future use */
/* support subtreecheck option */
static struct dentry *aufs_fh_to_parent(struct super_block *sb, struct fid *fid,
int fh_len, int fh_type)
{
struct dentry *parent;
__u32 *fh = fid->raw;
ino_t dir_ino;
dir_ino = decode_ino(fh + Fh_dir_ino);
parent = decode_by_ino(sb, dir_ino, 0);
if (IS_ERR(parent))
goto out;
if (!parent)
parent = decode_by_path(sb, au_br_index(sb, fh[Fh_br_id]),
dir_ino, fh, fh_len);
out:
AuTraceErrPtr(parent);
return parent;
}
#endif
/* ---------------------------------------------------------------------- */
static int aufs_encode_fh(struct inode *inode, __u32 *fh, int *max_len,
struct inode *dir)
{
int err;
aufs_bindex_t bindex;
struct super_block *sb, *h_sb;
struct dentry *dentry, *parent, *h_parent;
struct inode *h_dir;
struct au_branch *br;
err = -ENOSPC;
if (unlikely(*max_len <= Fh_tail)) {
AuWarn1("NFSv2 client (max_len %d)?\n", *max_len);
goto out;
}
err = FILEID_ROOT;
if (inode->i_ino == AUFS_ROOT_INO) {
AuDebugOn(inode->i_ino != AUFS_ROOT_INO);
goto out;
}
h_parent = NULL;
sb = inode->i_sb;
err = si_read_lock(sb, AuLock_FLUSH);
if (unlikely(err))
goto out;
#ifdef CONFIG_AUFS_DEBUG
if (unlikely(!au_opt_test(au_mntflags(sb), XINO)))
AuWarn1("NFS-exporting requires xino\n");
#endif
err = -EIO;
parent = NULL;
ii_read_lock_child(inode);
bindex = au_ibtop(inode);
if (!dir) {
dentry = d_find_any_alias(inode);
if (unlikely(!dentry))
goto out_unlock;
AuDebugOn(au_test_anon(dentry));
parent = dget_parent(dentry);
dput(dentry);
if (unlikely(!parent))
goto out_unlock;
if (d_really_is_positive(parent))
dir = d_inode(parent);
}
ii_read_lock_parent(dir);
h_dir = au_h_iptr(dir, bindex);
ii_read_unlock(dir);
if (unlikely(!h_dir))
goto out_parent;
h_parent = d_find_any_alias(h_dir);
if (unlikely(!h_parent))
goto out_hparent;
err = -EPERM;
br = au_sbr(sb, bindex);
h_sb = au_br_sb(br);
if (unlikely(!h_sb->s_export_op)) {
AuErr1("%s branch is not exportable\n", au_sbtype(h_sb));
goto out_hparent;
}
fh[Fh_br_id] = br->br_id;
fh[Fh_sigen] = au_sigen(sb);
encode_ino(fh + Fh_ino, inode->i_ino);
encode_ino(fh + Fh_dir_ino, dir->i_ino);
fh[Fh_igen] = inode->i_generation;
*max_len -= Fh_tail;
fh[Fh_h_type] = exportfs_encode_fh(h_parent, (void *)(fh + Fh_tail),
max_len,
/*connectable or subtreecheck*/0);
err = fh[Fh_h_type];
*max_len += Fh_tail;
/* todo: macros? */
if (err != FILEID_INVALID)
err = 99;
else
AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb));
out_hparent:
dput(h_parent);
out_parent:
dput(parent);
out_unlock:
ii_read_unlock(inode);
si_read_unlock(sb);
out:
if (unlikely(err < 0))
err = FILEID_INVALID;
return err;
}
/* ---------------------------------------------------------------------- */
static int aufs_commit_metadata(struct inode *inode)
{
int err;
aufs_bindex_t bindex;
struct super_block *sb;
struct inode *h_inode;
int (*f)(struct inode *inode);
sb = inode->i_sb;
si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
ii_write_lock_child(inode);
bindex = au_ibtop(inode);
AuDebugOn(bindex < 0);
h_inode = au_h_iptr(inode, bindex);
f = h_inode->i_sb->s_export_op->commit_metadata;
if (f)
err = f(h_inode);
else {
struct writeback_control wbc = {
.sync_mode = WB_SYNC_ALL,
.nr_to_write = 0 /* metadata only */
};
err = sync_inode(h_inode, &wbc);
}
au_cpup_attr_timesizes(inode);
ii_write_unlock(inode);
si_read_unlock(sb);
return err;
}
/* ---------------------------------------------------------------------- */
static struct export_operations aufs_export_op = {
.fh_to_dentry = aufs_fh_to_dentry,
/* .fh_to_parent = aufs_fh_to_parent, */
.encode_fh = aufs_encode_fh,
.commit_metadata = aufs_commit_metadata
};
void au_export_init(struct super_block *sb)
{
struct au_sbinfo *sbinfo;
__u32 u;
BUILD_BUG_ON_MSG(IS_BUILTIN(CONFIG_AUFS_FS)
&& IS_MODULE(CONFIG_EXPORTFS),
AUFS_NAME ": unsupported configuration "
"CONFIG_EXPORTFS=m and CONFIG_AUFS_FS=y");
sb->s_export_op = &aufs_export_op;
sbinfo = au_sbi(sb);
sbinfo->si_xigen = NULL;
get_random_bytes(&u, sizeof(u));
BUILD_BUG_ON(sizeof(u) != sizeof(int));
atomic_set(&sbinfo->si_xigen_next, u);
}
......@@ -103,6 +103,8 @@ static int __init au_cache_init(void)
/* ---------------------------------------------------------------------- */
int au_dir_roflags;
#ifdef CONFIG_AUFS_SBILIST
/*
* iterate_supers_type() doesn't protect us from
......@@ -157,6 +159,8 @@ static int __init aufs_init(void)
*p++ = '\x7f';
*p = 0;
au_dir_roflags = au_file_roflags(O_DIRECTORY | O_LARGEFILE);
memset(au_cache, 0, sizeof(au_cache));
au_sbilist_init();
......
......@@ -25,6 +25,8 @@ extern int sysaufs_brs;
/* ---------------------------------------------------------------------- */
extern int au_dir_roflags;
void *au_krealloc(void *p, unsigned int new_sz, gfp_t gfp, int may_shrink);
void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp,
int may_shrink);
......
......@@ -105,6 +105,13 @@ struct au_sbinfo {
/* reserved for future use */
/* unsigned long long si_xib_limit; */ /* Max xib file size */
#ifdef CONFIG_AUFS_EXPORT
/* i_generation */
/* todo: make xigen file an array to support many inode numbers */
struct file *si_xigen;
atomic_t si_xigen_next;
#endif
/* pseudo_link list */
struct hlist_bl_head si_plink[AuPlink_NHASH];
wait_queue_head_t si_plink_wq;
......@@ -180,6 +187,32 @@ static inline struct au_sbinfo *au_sbi(struct super_block *sb)
/* ---------------------------------------------------------------------- */
#ifdef CONFIG_AUFS_EXPORT
int au_test_nfsd(void);
void au_export_init(struct super_block *sb);
void au_xigen_inc(struct inode *inode);
int au_xigen_new(struct inode *inode);
int au_xigen_set(struct super_block *sb, struct path *path);
void au_xigen_clr(struct super_block *sb);
static inline int au_busy_or_stale(void)
{
if (!au_test_nfsd())
return -EBUSY;
return -ESTALE;
}
#else
AuStubInt0(au_test_nfsd, void)
AuStubVoid(au_export_init, struct super_block *sb)
AuStubVoid(au_xigen_inc, struct inode *inode)
AuStubInt0(au_xigen_new, struct inode *inode)
AuStubInt0(au_xigen_set, struct super_block *sb, struct path *path)
AuStubVoid(au_xigen_clr, struct super_block *sb)
AuStub(int, au_busy_or_stale, return -EBUSY, void)
#endif /* CONFIG_AUFS_EXPORT */
/* ---------------------------------------------------------------------- */
#ifdef CONFIG_AUFS_SBILIST
/* module.c */
extern struct hlist_bl_head au_sbilist;
......
......@@ -48,6 +48,18 @@ int vfsub_kern_path(const char *name, unsigned int flags, struct path *path)
return err;
}
struct dentry *vfsub_lookup_one_len_unlocked(const char *name,
struct dentry *parent, int len)
{
struct path path = {
.mnt = NULL
};
path.dentry = lookup_one_len_unlocked(name, parent, len);
AuTraceErrPtr(path.dentry);
return path.dentry;
}
struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent,
int len)
{
......@@ -314,6 +326,19 @@ ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos)
return err;
}
int vfsub_iterate_dir(struct file *file, struct dir_context *ctx)
{
int err;
AuDbg("%pD, ctx{%ps, %llu}\n", file, ctx->actor, ctx->pos);
lockdep_off();
err = iterate_dir(file, ctx);
lockdep_on();
return err;
}
/* ---------------------------------------------------------------------- */
struct au_vfsub_mkdir_args {
......
......@@ -41,6 +41,8 @@ struct file *vfsub_dentry_open(struct path *path, int flags);
struct file *vfsub_filp_open(const char *path, int oflags, int mode);
int vfsub_kern_path(const char *name, unsigned int flags, struct path *path);
struct dentry *vfsub_lookup_one_len_unlocked(const char *name,
struct dentry *parent, int len);
struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent,
int len);
......@@ -102,6 +104,7 @@ ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count,
loff_t *ppos);
ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count,
loff_t *ppos);
int vfsub_iterate_dir(struct file *file, struct dir_context *ctx);
static inline loff_t vfsub_f_size_read(struct file *file)
{
......
......@@ -1764,8 +1764,10 @@ void au_xino_delete_inode(struct inode *inode, const int unlinked)
|| inode->i_ino == AUFS_ROOT_INO)
return;
if (unlinked)
if (unlinked) {
au_xigen_inc(inode);
au_xib_clear_bit(inode);
}
iinfo = au_ii(inode);
bindex = iinfo->ii_btop;
......
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