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

aufs: dirren 5/6, lookup and revalidate with loading the rename info



When aufs meets a new dir inode on a branch in lookup, it tests whether
the inode is in the list which the branch has. If the inode is found, it
means the dir has ever been logically renamed and there is some info
about the name under that dir. Then aufs tries loading the info, and
continues looking up using the before-renamed name on the lower
branches.
Signed-off-by: default avatarJ. R. Okajima <hooanon05g@gmail.com>
parent 39d4a736
......@@ -64,6 +64,13 @@ real_lookup:
} else if (wh_found
|| (args->type && args->type != (h_inode->i_mode & S_IFMT)))
goto out_neg;
else if (au_ftest_lkup(args->flags, DIRREN)
/* && h_inode */
&& !au_dr_lkup_h_ino(args, bindex, h_inode->i_ino)) {
AuDbg("b%d %pd ignored hi%llu\n", bindex, h_dentry,
(unsigned long long)h_inode->i_ino);
goto out_neg;
}
if (au_dbbot(dentry) <= bindex)
au_set_dbbot(dentry, bindex);
......@@ -104,7 +111,7 @@ int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop,
{
int npositive, err;
aufs_bindex_t bindex, btail, bdiropq;
unsigned char isdir, dirperm1;
unsigned char isdir, dirperm1, dirren;
struct au_do_lookup_args args = {
.flags = flags,
.name = &dentry->d_name
......@@ -119,6 +126,9 @@ int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop,
isdir = !!d_is_dir(dentry);
dirperm1 = !!au_opt_test(au_mntflags(sb), DIRPERM1);
dirren = !!au_opt_test(au_mntflags(sb), DIRREN);
if (dirren)
au_fset_lkup(args.flags, DIRREN);
npositive = 0;
parent = dget_parent(dentry);
......@@ -126,6 +136,7 @@ int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop,
for (bindex = btop; bindex <= btail; bindex++) {
struct dentry *h_parent, *h_dentry;
struct inode *h_inode, *h_dir;
struct au_branch *br;
h_dentry = au_h_dptr(dentry, bindex);
if (h_dentry) {
......@@ -137,6 +148,13 @@ int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop,
if (!h_parent || !d_is_dir(h_parent))
continue;
if (dirren) {
/* if the inum matches, then use the prepared name */
err = au_dr_lkup_name(&args, bindex);
if (unlikely(err))
goto out_parent;
}
h_dir = d_inode(h_parent);
inode_lock_shared_nested(h_dir, AuLsc_I_PARENT);
h_dentry = au_do_lookup(h_parent, dentry, bindex, &args);
......@@ -167,6 +185,15 @@ int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop,
if (bdiropq >= 0 && bdiropq <= bindex)
break;
}
br = au_sbr(sb, bindex);
if (dirren
&& au_dr_hino_test_add(&br->br_dirren, h_inode->i_ino,
/*add_ent*/NULL)) {
/* prepare next name to lookup */
err = au_dr_lkup(&args, dentry, bindex);
if (unlikely(err))
goto out_parent;
}
}
if (npositive) {
......@@ -184,6 +211,8 @@ int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop,
out_parent:
dput(parent);
au_kfree_try_rcu(args.whname.name);
if (dirren)
au_dr_lkup_fin(&args);
out:
return err;
}
......@@ -791,7 +820,7 @@ out:
/* todo: remove this */
static int h_d_revalidate(struct dentry *dentry, struct inode *inode,
unsigned int flags, int do_udba)
unsigned int flags, int do_udba, int dirren)
{
int err;
umode_t mode, h_mode;
......@@ -842,7 +871,7 @@ static int h_d_revalidate(struct dentry *dentry, struct inode *inode,
&& !is_root
&& ((!h_nfs
&& (unhashed != !!d_unhashed(h_dentry)
|| (!tmpfile
|| (!tmpfile && !dirren
&& !au_qstreq(name, h_name))
))
|| (h_nfs
......@@ -983,7 +1012,7 @@ static int aufs_d_revalidate(struct dentry *dentry, unsigned int flags)
{
int valid, err;
unsigned int sigen;
unsigned char do_udba;
unsigned char do_udba, dirren;
struct super_block *sb;
struct inode *inode;
......@@ -1056,7 +1085,8 @@ static int aufs_d_revalidate(struct dentry *dentry, unsigned int flags)
}
}
err = h_d_revalidate(dentry, inode, flags, do_udba);
dirren = !!au_opt_test(au_mntflags(sb), DIRREN);
err = h_d_revalidate(dentry, inode, flags, do_udba, dirren);
if (unlikely(!err && do_udba && au_dbtop(dentry) < 0)) {
err = -EIO;
AuDbg("both of real entry and whiteout found, %p, err %d\n",
......
......@@ -13,6 +13,7 @@
#ifdef __KERNEL__
#include <linux/dcache.h>
#include "dirren.h"
#include "rwsem.h"
struct au_hdentry {
......@@ -35,16 +36,23 @@ struct au_dinfo {
/* flags for au_lkup_dentry() */
#define AuLkup_ALLOW_NEG 1
#define AuLkup_IGNORE_PERM (1 << 1)
#define AuLkup_DIRREN (1 << 2)
#define au_ftest_lkup(flags, name) ((flags) & AuLkup_##name)
#define au_fset_lkup(flags, name) \
do { (flags) |= AuLkup_##name; } while (0)
#define au_fclr_lkup(flags, name) \
do { (flags) &= ~AuLkup_##name; } while (0)
#ifndef CONFIG_AUFS_DIRREN
#undef AuLkup_DIRREN
#define AuLkup_DIRREN 0
#endif
struct au_do_lookup_args {
unsigned int flags;
mode_t type;
struct qstr whname, *name;
struct au_dr_lookup dirren;
};
/* ---------------------------------------------------------------------- */
......
......@@ -970,3 +970,276 @@ out:
if (unlikely(err))
pr_err("failed to remove dirren info\n");
}
/* ---------------------------------------------------------------------- */
static struct au_drinfo *au_drinfo_do_load(struct path *h_ppath,
char *whname, int whnamelen,
struct dentry **info_dentry)
{
struct au_drinfo *drinfo;
struct file *f;
struct inode *h_dir;
struct path infopath;
int unlocked;
AuDbg("%pd/%.*s\n", h_ppath->dentry, whnamelen, whname);
*info_dentry = NULL;
drinfo = NULL;
unlocked = 0;
h_dir = d_inode(h_ppath->dentry);
inode_lock_shared_nested(h_dir, AuLsc_I_PARENT);
infopath.dentry = vfsub_lookup_one_len(whname, h_ppath->dentry,
whnamelen);
if (IS_ERR(infopath.dentry)) {
drinfo = (void *)infopath.dentry;
goto out;
}
if (d_is_negative(infopath.dentry))
goto out_dput; /* success */
infopath.mnt = h_ppath->mnt;
f = vfsub_dentry_open(&infopath, O_RDONLY);
inode_unlock_shared(h_dir);
unlocked = 1;
if (IS_ERR(f)) {
drinfo = (void *)f;
goto out_dput;
}
drinfo = au_drinfo_read_k(f, /*h_ino*/0);
if (IS_ERR_OR_NULL(drinfo))
goto out_fput;
AuDbg("oldname %.*s\n", drinfo->oldnamelen, drinfo->oldname);
*info_dentry = dget(infopath.dentry); /* keep it alive */
out_fput:
fput(f);
out_dput:
dput(infopath.dentry);
out:
if (!unlocked)
inode_unlock_shared(h_dir);
AuTraceErrPtr(drinfo);
return drinfo;
}
struct au_drinfo_do_load_args {
struct au_drinfo **drinfop;
struct path *h_ppath;
char *whname;
int whnamelen;
struct dentry **info_dentry;
};
static void au_call_drinfo_do_load(void *args)
{
struct au_drinfo_do_load_args *a = args;
*a->drinfop = au_drinfo_do_load(a->h_ppath, a->whname, a->whnamelen,
a->info_dentry);
}
struct au_drinfo_load {
struct path h_ppath;
struct qstr *qname;
unsigned char no_sio;
aufs_bindex_t ninfo;
struct au_drinfo **drinfo;
};
static int au_drinfo_load(struct au_drinfo_load *w, aufs_bindex_t bindex,
struct au_branch *br)
{
int err, wkq_err, whnamelen, e;
char whname[sizeof(AUFS_WH_DR_INFO_PFX) + AUFS_DIRREN_ENV_VAL_SZ]
= AUFS_WH_DR_INFO_PFX;
struct au_drinfo *drinfo;
struct qstr oldname;
struct inode *h_dir, *delegated;
struct dentry *info_dentry;
struct path infopath;
whnamelen = sizeof(AUFS_WH_DR_INFO_PFX) - 1;
whnamelen += au_drinfo_name(br, whname + whnamelen,
sizeof(whname) - whnamelen);
if (w->no_sio)
drinfo = au_drinfo_do_load(&w->h_ppath, whname, whnamelen,
&info_dentry);
else {
struct au_drinfo_do_load_args args = {
.drinfop = &drinfo,
.h_ppath = &w->h_ppath,
.whname = whname,
.whnamelen = whnamelen,
.info_dentry = &info_dentry
};
wkq_err = au_wkq_wait(au_call_drinfo_do_load, &args);
if (unlikely(wkq_err))
drinfo = ERR_PTR(wkq_err);
}
err = PTR_ERR(drinfo);
if (IS_ERR_OR_NULL(drinfo))
goto out;
err = 0;
oldname.len = drinfo->oldnamelen;
oldname.name = drinfo->oldname;
if (au_qstreq(w->qname, &oldname)) {
/* the name is renamed back */
au_kfree_rcu(drinfo);
drinfo = NULL;
infopath.dentry = info_dentry;
infopath.mnt = w->h_ppath.mnt;
h_dir = d_inode(w->h_ppath.dentry);
delegated = NULL;
inode_lock_nested(h_dir, AuLsc_I_PARENT);
e = vfsub_unlink(h_dir, &infopath, &delegated, !w->no_sio);
inode_unlock(h_dir);
if (unlikely(e))
AuIOErr("ignored %d, %pd2\n", e, &infopath.dentry);
if (unlikely(e == -EWOULDBLOCK))
iput(delegated);
}
au_kfree_rcu(w->drinfo[bindex]);
w->drinfo[bindex] = drinfo;
dput(info_dentry);
out:
AuTraceErr(err);
return err;
}
/* ---------------------------------------------------------------------- */
static void au_dr_lkup_free(struct au_drinfo **drinfo, int n)
{
struct au_drinfo **p = drinfo;
while (n-- > 0)
au_kfree_rcu(*drinfo++);
au_kfree_try_rcu(p);
}
int au_dr_lkup(struct au_do_lookup_args *lkup, struct dentry *dentry,
aufs_bindex_t btgt)
{
int err, ninfo;
struct au_drinfo_load w;
aufs_bindex_t bindex, bbot;
struct au_branch *br;
struct inode *h_dir;
struct au_dr_hino *ent;
struct super_block *sb;
AuDbg("%.*s, name %.*s, whname %.*s, b%d\n",
AuLNPair(&dentry->d_name), AuLNPair(&lkup->dirren.dr_name),
AuLNPair(&lkup->whname), btgt);
sb = dentry->d_sb;
bbot = au_sbbot(sb);
w.ninfo = bbot + 1;
if (!lkup->dirren.drinfo) {
lkup->dirren.drinfo = kcalloc(w.ninfo,
sizeof(*lkup->dirren.drinfo),
GFP_NOFS);
if (unlikely(!lkup->dirren.drinfo)) {
err = -ENOMEM;
goto out;
}
lkup->dirren.ninfo = w.ninfo;
}
w.drinfo = lkup->dirren.drinfo;
w.no_sio = !!uid_eq(current_fsuid(), GLOBAL_ROOT_UID);
w.h_ppath.dentry = au_h_dptr(dentry, btgt);
AuDebugOn(!w.h_ppath.dentry);
w.h_ppath.mnt = au_sbr_mnt(sb, btgt);
w.qname = &dentry->d_name;
ninfo = 0;
for (bindex = btgt + 1; bindex <= bbot; bindex++) {
br = au_sbr(sb, bindex);
err = au_drinfo_load(&w, bindex, br);
if (unlikely(err))
goto out_free;
if (w.drinfo[bindex])
ninfo++;
}
if (!ninfo) {
br = au_sbr(sb, btgt);
h_dir = d_inode(w.h_ppath.dentry);
ent = au_dr_hino_find(&br->br_dirren, h_dir->i_ino);
AuDebugOn(!ent);
au_dr_hino_del(&br->br_dirren, ent);
au_kfree_rcu(ent);
}
goto out; /* success */
out_free:
au_dr_lkup_free(lkup->dirren.drinfo, lkup->dirren.ninfo);
lkup->dirren.ninfo = 0;
lkup->dirren.drinfo = NULL;
out:
AuTraceErr(err);
return err;
}
void au_dr_lkup_fin(struct au_do_lookup_args *lkup)
{
au_dr_lkup_free(lkup->dirren.drinfo, lkup->dirren.ninfo);
}
int au_dr_lkup_name(struct au_do_lookup_args *lkup, aufs_bindex_t btgt)
{
int err;
struct au_drinfo *drinfo;
err = 0;
if (!lkup->dirren.drinfo)
goto out;
AuDebugOn(lkup->dirren.ninfo < btgt + 1);
drinfo = lkup->dirren.drinfo[btgt + 1];
if (!drinfo)
goto out;
au_kfree_try_rcu(lkup->whname.name);
lkup->whname.name = NULL;
lkup->dirren.dr_name.len = drinfo->oldnamelen;
lkup->dirren.dr_name.name = drinfo->oldname;
lkup->name = &lkup->dirren.dr_name;
err = au_wh_name_alloc(&lkup->whname, lkup->name);
if (!err)
AuDbg("name %.*s, whname %.*s, b%d\n",
AuLNPair(lkup->name), AuLNPair(&lkup->whname),
btgt);
out:
AuTraceErr(err);
return err;
}
int au_dr_lkup_h_ino(struct au_do_lookup_args *lkup, aufs_bindex_t bindex,
ino_t h_ino)
{
int match;
struct au_drinfo *drinfo;
match = 1;
if (!lkup->dirren.drinfo)
goto out;
AuDebugOn(lkup->dirren.ninfo < bindex + 1);
drinfo = lkup->dirren.drinfo[bindex + 1];
if (!drinfo)
goto out;
match = (drinfo->ino == h_ino);
AuDbg("match %d\n", match);
out:
return match;
}
......@@ -12,6 +12,7 @@
#ifdef __KERNEL__
#include <linux/dcache.h>
#include <linux/statfs.h>
#include <linux/uuid.h>
#include "hbl.h"
......@@ -49,20 +50,25 @@ struct au_dr_br {
struct hlist_bl_head dr_h_ino[AuDirren_NHASH];
struct au_dr_brid dr_brid;
};
struct au_dr_lookup {
/* dr_name is pointed by struct au_do_lookup_args.name */
struct qstr dr_name; /* subset of dr_info */
aufs_bindex_t ninfo;
struct au_drinfo **drinfo;
};
#else
struct au_dr_hino;
/* empty */
struct au_dr_br { };
struct au_dr_lookup { };
#endif
/* ---------------------------------------------------------------------- */
struct qstr;
struct au_branch;
struct au_do_lookup_args;
struct au_hinode;
struct path;
struct super_block;
struct dentry;
#ifdef CONFIG_AUFS_DIRREN
int au_dr_hino_test_add(struct au_dr_br *dr, ino_t h_ino,
struct au_dr_hino *add_ent);
......@@ -74,6 +80,12 @@ int au_dr_rename(struct dentry *src, aufs_bindex_t bindex,
struct qstr *dst_name, void *_rev);
void au_dr_rename_fin(struct dentry *src, aufs_bindex_t btgt, void *rev);
void au_dr_rename_rev(struct dentry *src, aufs_bindex_t bindex, void *rev);
int au_dr_lkup(struct au_do_lookup_args *lkup, struct dentry *dentry,
aufs_bindex_t bindex);
int au_dr_lkup_name(struct au_do_lookup_args *lkup, aufs_bindex_t btgt);
int au_dr_lkup_h_ino(struct au_do_lookup_args *lkup, aufs_bindex_t bindex,
ino_t h_ino);
void au_dr_lkup_fin(struct au_do_lookup_args *lkup);
#else
AuStubInt0(au_dr_hino_test_add, struct au_dr_br *dr, ino_t h_ino,
struct au_dr_hino *add_ent);
......@@ -86,6 +98,12 @@ AuStubInt0(au_dr_rename, struct dentry *src, aufs_bindex_t bindex,
AuStubVoid(au_dr_rename_fin, struct dentry *src, aufs_bindex_t btgt, void *rev);
AuStubVoid(au_dr_rename_rev, struct dentry *src, aufs_bindex_t bindex,
void *rev);
AuStubInt0(au_dr_lkup, struct au_do_lookup_args *lkup, struct dentry *dentry,
aufs_bindex_t bindex);
AuStubInt0(au_dr_lkup_name, struct au_do_lookup_args *lkup, aufs_bindex_t btgt);
AuStubInt0(au_dr_lkup_h_ino, struct au_do_lookup_args *lkup,
aufs_bindex_t bindex, ino_t h_ino);
AuStubVoid(au_dr_lkup_fin, struct au_do_lookup_args *lkup);
#endif
/* ---------------------------------------------------------------------- */
......
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