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

aufs: dentry op



Implement d_op->d_revalidate().
This is another core part of UDBA (cf. lookup.txt in another commit).
Signed-off-by: default avatarJ. R. Okajima <hooanon05g@gmail.com>
parent 3cb74551
...@@ -735,6 +735,154 @@ out: ...@@ -735,6 +735,154 @@ out:
return err; return err;
} }
static int au_do_h_d_reval(struct dentry *h_dentry, unsigned int flags,
struct dentry *dentry, aufs_bindex_t bindex)
{
int err, valid;
err = 0;
if (!(h_dentry->d_flags & DCACHE_OP_REVALIDATE))
goto out;
AuDbg("b%d\n", bindex);
/*
* gave up supporting LOOKUP_CREATE/OPEN for lower fs,
* due to whiteout and branch permission.
*/
flags &= ~(/*LOOKUP_PARENT |*/ LOOKUP_OPEN | LOOKUP_CREATE
| LOOKUP_FOLLOW | LOOKUP_EXCL);
/* it may return tri-state */
valid = h_dentry->d_op->d_revalidate(h_dentry, flags);
if (unlikely(valid < 0))
err = valid;
else if (!valid)
err = -EINVAL;
out:
AuTraceErr(err);
return err;
}
/* todo: remove this */
static int h_d_revalidate(struct dentry *dentry, struct inode *inode,
unsigned int flags, int do_udba)
{
int err;
umode_t mode, h_mode;
aufs_bindex_t bindex, btail, btop, ibs, ibe;
unsigned char plus, unhashed, is_root, h_plus, h_nfs;
struct inode *h_inode, *h_cached_inode;
struct dentry *h_dentry;
struct qstr *name, *h_name;
err = 0;
plus = 0;
mode = 0;
ibs = -1;
ibe = -1;
unhashed = !!d_unhashed(dentry);
is_root = !!IS_ROOT(dentry);
name = &dentry->d_name;
/*
* Theoretically, REVAL test should be unnecessary in case of
* {FS,I}NOTIFY.
* But {fs,i}notify doesn't fire some necessary events,
* IN_ATTRIB for atime/nlink/pageio
* Let's do REVAL test too.
*/
if (do_udba && inode) {
mode = (inode->i_mode & S_IFMT);
plus = (inode->i_nlink > 0);
ibs = au_ibtop(inode);
ibe = au_ibbot(inode);
}
btop = au_dbtop(dentry);
btail = btop;
if (inode && S_ISDIR(inode->i_mode))
btail = au_dbtaildir(dentry);
for (bindex = btop; bindex <= btail; bindex++) {
h_dentry = au_h_dptr(dentry, bindex);
if (!h_dentry)
continue;
AuDbg("b%d, %pd\n", bindex, h_dentry);
h_nfs = !!au_test_nfs(h_dentry->d_sb);
spin_lock(&h_dentry->d_lock);
h_name = &h_dentry->d_name;
if (unlikely(do_udba
&& !is_root
&& ((!h_nfs
&& (unhashed != !!d_unhashed(h_dentry)
|| !au_qstreq(name, h_name)
))
|| (h_nfs
&& !(flags & LOOKUP_OPEN)
&& (h_dentry->d_flags
& DCACHE_NFSFS_RENAMED)))
)) {
int h_unhashed;
h_unhashed = d_unhashed(h_dentry);
spin_unlock(&h_dentry->d_lock);
AuDbg("unhash 0x%x 0x%x, %pd %pd\n",
unhashed, h_unhashed, dentry, h_dentry);
goto err;
}
spin_unlock(&h_dentry->d_lock);
err = au_do_h_d_reval(h_dentry, flags, dentry, bindex);
if (unlikely(err))
/* do not goto err, to keep the errno */
break;
/* todo: plink too? */
if (!do_udba)
continue;
/* UDBA tests */
if (unlikely(!!inode != d_is_positive(h_dentry)))
goto err;
h_inode = NULL;
if (d_is_positive(h_dentry))
h_inode = d_inode(h_dentry);
h_plus = plus;
h_mode = mode;
h_cached_inode = h_inode;
if (h_inode) {
h_mode = (h_inode->i_mode & S_IFMT);
h_plus = (h_inode->i_nlink > 0);
}
if (inode && ibs <= bindex && bindex <= ibe)
h_cached_inode = au_h_iptr(inode, bindex);
if (!h_nfs) {
if (unlikely(plus != h_plus))
goto err;
} else {
if (unlikely(!(h_dentry->d_flags & DCACHE_NFSFS_RENAMED)
&& !is_root
&& !IS_ROOT(h_dentry)
&& unhashed != d_unhashed(h_dentry)))
goto err;
}
if (unlikely(mode != h_mode
|| h_cached_inode != h_inode))
goto err;
continue;
err:
err = -EINVAL;
break;
}
AuTraceErr(err);
return err;
}
/* todo: consolidate with do_refresh() and au_reval_for_attr() */ /* todo: consolidate with do_refresh() and au_reval_for_attr() */
static int simple_reval_dpath(struct dentry *dentry, unsigned int sigen) static int simple_reval_dpath(struct dentry *dentry, unsigned int sigen)
{ {
...@@ -800,3 +948,119 @@ int au_reval_dpath(struct dentry *dentry, unsigned int sigen) ...@@ -800,3 +948,119 @@ int au_reval_dpath(struct dentry *dentry, unsigned int sigen)
return err; return err;
} }
/*
* if valid returns 1, otherwise 0.
*/
static int aufs_d_revalidate(struct dentry *dentry, unsigned int flags)
{
int valid, err;
unsigned int sigen;
unsigned char do_udba;
struct super_block *sb;
struct inode *inode;
/* todo: support rcu-walk? */
if (flags & LOOKUP_RCU)
return -ECHILD;
valid = 0;
if (unlikely(!au_di(dentry)))
goto out;
valid = 1;
sb = dentry->d_sb;
/*
* todo: very ugly
* i_mutex of parent dir may be held,
* but we should not return 'invalid' due to busy.
*/
err = aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW | AuLock_NOPLM);
if (unlikely(err)) {
valid = err;
AuTraceErr(err);
goto out;
}
inode = NULL;
if (d_really_is_positive(dentry))
inode = d_inode(dentry);
if (unlikely(inode && au_is_bad_inode(inode))) {
err = -EINVAL;
AuTraceErr(err);
goto out_dgrade;
}
if (unlikely(au_dbrange_test(dentry))) {
err = -EINVAL;
AuTraceErr(err);
goto out_dgrade;
}
sigen = au_sigen(sb);
if (au_digen_test(dentry, sigen)) {
AuDebugOn(IS_ROOT(dentry));
err = au_reval_dpath(dentry, sigen);
if (unlikely(err)) {
AuTraceErr(err);
goto out_dgrade;
}
}
di_downgrade_lock(dentry, AuLock_IR);
err = -EINVAL;
if (!(flags & (LOOKUP_OPEN | LOOKUP_EMPTY))
&& inode
&& !(inode->i_state && I_LINKABLE)
&& (IS_DEADDIR(inode) || !inode->i_nlink)) {
AuTraceErr(err);
goto out_inval;
}
do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE);
if (do_udba && inode) {
aufs_bindex_t btop = au_ibtop(inode);
struct inode *h_inode;
if (btop >= 0) {
h_inode = au_h_iptr(inode, btop);
if (h_inode && au_test_higen(inode, h_inode)) {
AuTraceErr(err);
goto out_inval;
}
}
}
err = h_d_revalidate(dentry, inode, flags, do_udba);
if (unlikely(!err && do_udba && au_dbtop(dentry) < 0)) {
err = -EIO;
AuDbg("both of real entry and whiteout found, %p, err %d\n",
dentry, err);
}
goto out_inval;
out_dgrade:
di_downgrade_lock(dentry, AuLock_IR);
out_inval:
aufs_read_unlock(dentry, AuLock_IR);
AuTraceErr(err);
valid = !err;
out:
if (!valid) {
AuDbg("%pd invalid, %d\n", dentry, valid);
d_drop(dentry);
}
return valid;
}
static void aufs_d_release(struct dentry *dentry)
{
if (au_di(dentry)) {
au_di_fin(dentry);
au_hn_di_reinit(dentry);
}
}
const struct dentry_operations aufs_dop = {
.d_revalidate = aufs_d_revalidate,
.d_weak_revalidate = aufs_d_revalidate,
.d_release = aufs_d_release
};
...@@ -49,6 +49,7 @@ struct au_do_lookup_args { ...@@ -49,6 +49,7 @@ struct au_do_lookup_args {
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
/* dentry.c */ /* dentry.c */
extern const struct dentry_operations aufs_dop;
struct au_branch; struct au_branch;
struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent); struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent);
int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir, int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir,
......
...@@ -772,7 +772,7 @@ static int aufs_fill_super(struct super_block *sb, void *raw_data, ...@@ -772,7 +772,7 @@ static int aufs_fill_super(struct super_block *sb, void *raw_data,
sb->s_flags |= SB_NOATIME | SB_NODIRATIME; sb->s_flags |= SB_NOATIME | SB_NODIRATIME;
sb->s_flags |= SB_I_VERSION; /* do we really need this? */ sb->s_flags |= SB_I_VERSION; /* do we really need this? */
sb->s_op = &aufs_sop; sb->s_op = &aufs_sop;
sb->s_d_op = &simple_dentry_operations; /* replace later */ sb->s_d_op = &aufs_dop;
sb->s_magic = AUFS_SUPER_MAGIC; sb->s_magic = AUFS_SUPER_MAGIC;
sb->s_maxbytes = 0; sb->s_maxbytes = 0;
sb->s_stack_depth = 1; sb->s_stack_depth = 1;
......
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