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

aufs: copy-up 2/7, internal lookup for the target



Basically copy-up is done by these steps using au_pin (in another
commit).
- lock the target parent mutex
- lookup a negative dentry with a whiteout-ed temporary unique name
- create it
- unlock the target parent mutex
- copy filedata
- copy metadata (inode attributes)
- lock the target parent mutex
- rename the temporary name to the target name
- unlock the target parent mutex

This commit contains step2 mainly.
I hope someday aufs uses O_TMPFILE for this.
Signed-off-by: default avatarJ. R. Okajima <hooanon05g@gmail.com>
parent f7f1bacc
......@@ -11,6 +11,241 @@
#include <linux/namei.h>
#include "aufs.h"
/*
* returns positive/negative dentry, NULL or an error.
* NULL means whiteout-ed or not-found.
*/
static struct dentry*
au_do_lookup(struct dentry *h_parent, struct dentry *dentry,
aufs_bindex_t bindex, struct au_do_lookup_args *args)
{
struct dentry *h_dentry;
struct inode *h_inode;
struct au_branch *br;
int wh_found, opq;
unsigned char wh_able;
const unsigned char allow_neg = !!au_ftest_lkup(args->flags, ALLOW_NEG);
const unsigned char ignore_perm = !!au_ftest_lkup(args->flags,
IGNORE_PERM);
wh_found = 0;
br = au_sbr(dentry->d_sb, bindex);
wh_able = !!au_br_whable(br->br_perm);
if (wh_able)
wh_found = au_wh_test(h_parent, &args->whname, ignore_perm);
h_dentry = ERR_PTR(wh_found);
if (!wh_found)
goto real_lookup;
if (unlikely(wh_found < 0))
goto out;
/* We found a whiteout */
/* au_set_dbbot(dentry, bindex); */
au_set_dbwh(dentry, bindex);
if (!allow_neg)
return NULL; /* success */
real_lookup:
if (!ignore_perm)
h_dentry = vfsub_lkup_one(args->name, h_parent);
else
h_dentry = au_sio_lkup_one(args->name, h_parent);
if (IS_ERR(h_dentry)) {
if (PTR_ERR(h_dentry) == -ENAMETOOLONG
&& !allow_neg)
h_dentry = NULL;
goto out;
}
h_inode = d_inode(h_dentry);
if (d_is_negative(h_dentry)) {
if (!allow_neg)
goto out_neg;
} else if (wh_found
|| (args->type && args->type != (h_inode->i_mode & S_IFMT)))
goto out_neg;
if (au_dbbot(dentry) <= bindex)
au_set_dbbot(dentry, bindex);
if (au_dbtop(dentry) < 0 || bindex < au_dbtop(dentry))
au_set_dbtop(dentry, bindex);
au_set_h_dptr(dentry, bindex, h_dentry);
if (!d_is_dir(h_dentry)
|| !wh_able
|| (d_really_is_positive(dentry) && !d_is_dir(dentry)))
goto out; /* success */
inode_lock_shared_nested(h_inode, AuLsc_I_CHILD);
opq = au_diropq_test(h_dentry);
inode_unlock_shared(h_inode);
if (opq > 0)
au_set_dbdiropq(dentry, bindex);
else if (unlikely(opq < 0)) {
au_set_h_dptr(dentry, bindex, NULL);
h_dentry = ERR_PTR(opq);
}
goto out;
out_neg:
dput(h_dentry);
h_dentry = NULL;
out:
return h_dentry;
}
/*
* returns the number of lower positive dentries,
* otherwise an error.
* can be called at unlinking with @type is zero.
*/
int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop,
unsigned int flags)
{
int npositive, err;
aufs_bindex_t bindex, btail, bdiropq;
unsigned char isdir;
struct au_do_lookup_args args = {
.flags = flags,
.name = &dentry->d_name
};
struct dentry *parent;
struct super_block *sb;
sb = dentry->d_sb;
err = au_wh_name_alloc(&args.whname, args.name);
if (unlikely(err))
goto out;
isdir = !!d_is_dir(dentry);
npositive = 0;
parent = dget_parent(dentry);
btail = au_dbtaildir(parent);
for (bindex = btop; bindex <= btail; bindex++) {
struct dentry *h_parent, *h_dentry;
struct inode *h_inode, *h_dir;
h_dentry = au_h_dptr(dentry, bindex);
if (h_dentry) {
if (d_is_positive(h_dentry))
npositive++;
break;
}
h_parent = au_h_dptr(parent, bindex);
if (!h_parent || !d_is_dir(h_parent))
continue;
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);
inode_unlock_shared(h_dir);
err = PTR_ERR(h_dentry);
if (IS_ERR(h_dentry))
goto out_parent;
if (h_dentry)
au_fclr_lkup(args.flags, ALLOW_NEG);
if (au_dbwh(dentry) == bindex)
break;
if (!h_dentry)
continue;
if (d_is_negative(h_dentry))
continue;
h_inode = d_inode(h_dentry);
npositive++;
if (!args.type)
args.type = h_inode->i_mode & S_IFMT;
if (args.type != S_IFDIR)
break;
else if (isdir) {
/* the type of lower may be different */
bdiropq = au_dbdiropq(dentry);
if (bdiropq >= 0 && bdiropq <= bindex)
break;
}
}
if (npositive) {
AuLabel(positive);
au_update_dbtop(dentry);
}
err = npositive;
if (unlikely(au_dbtop(dentry) < 0)) {
err = -EIO;
AuIOErr("both of real entry and whiteout found, %pd, err %d\n",
dentry, err);
}
out_parent:
dput(parent);
au_kfree_try_rcu(args.whname.name);
out:
return err;
}
struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent)
{
struct dentry *dentry;
int wkq_err;
if (!au_test_h_perm_sio(d_inode(parent), MAY_EXEC))
dentry = vfsub_lkup_one(name, parent);
else {
struct vfsub_lkup_one_args args = {
.errp = &dentry,
.name = name,
.parent = parent
};
wkq_err = au_wkq_wait(vfsub_call_lkup_one, &args);
if (unlikely(wkq_err))
dentry = ERR_PTR(wkq_err);
}
return dentry;
}
/*
* lookup @dentry on @bindex which should be negative.
*/
int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh)
{
int err;
struct dentry *parent, *h_parent, *h_dentry;
struct au_branch *br;
parent = dget_parent(dentry);
h_parent = au_h_dptr(parent, bindex);
br = au_sbr(dentry->d_sb, bindex);
if (wh)
h_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name);
else
h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent);
err = PTR_ERR(h_dentry);
if (IS_ERR(h_dentry))
goto out;
if (unlikely(d_is_positive(h_dentry))) {
err = -EIO;
AuIOErr("%pd should be negative on b%d.\n", h_dentry, bindex);
dput(h_dentry);
goto out;
}
err = 0;
if (bindex < au_dbtop(dentry))
au_set_dbtop(dentry, bindex);
if (au_dbbot(dentry) < bindex)
au_set_dbbot(dentry, bindex);
au_set_h_dptr(dentry, bindex, h_dentry);
out:
dput(parent);
return err;
}
/* ---------------------------------------------------------------------- */
/* subset of struct inode */
struct au_iattr {
unsigned long i_ino;
......
......@@ -31,11 +31,33 @@ struct au_dinfo {
/* ---------------------------------------------------------------------- */
/* flags for au_lkup_dentry() */
#define AuLkup_ALLOW_NEG 1
#define AuLkup_IGNORE_PERM (1 << 1)
#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)
struct au_do_lookup_args {
unsigned int flags;
mode_t type;
struct qstr whname, *name;
};
/* ---------------------------------------------------------------------- */
/* dentry.c */
struct au_branch;
struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent);
int au_h_verify(struct dentry *h_dentry, struct inode *h_dir,
struct dentry *h_parent, struct au_branch *br);
int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop,
unsigned int flags);
int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh);
/* dinfo.c */
void au_di_init_once(void *_di);
struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc);
......
......@@ -57,14 +57,10 @@ int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, int try_sio)
int err;
struct dentry *wh_dentry;
#if 0 /* re-commit later */
if (!try_sio)
wh_dentry = vfsub_lkup_one(wh_name, h_parent);
else
wh_dentry = au_sio_lkup_one(wh_name, h_parent);
#else
wh_dentry = ERR_PTR(-ENOENT);
#endif
err = PTR_ERR(wh_dentry);
if (IS_ERR(wh_dentry)) {
if (err == -ENAMETOOLONG)
......@@ -104,6 +100,63 @@ int au_diropq_test(struct dentry *h_dentry)
return err;
}
/*
* returns a negative dentry whose name is unique and temporary.
*/
struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br,
struct qstr *prefix)
{
struct dentry *dentry;
int i;
char defname[NAME_MAX - AUFS_MAX_NAMELEN + DNAME_INLINE_LEN + 1],
*name, *p;
/* strict atomic_t is unnecessary here */
static unsigned short cnt;
struct qstr qs;
BUILD_BUG_ON(sizeof(cnt) * 2 > AUFS_WH_TMP_LEN);
name = defname;
qs.len = sizeof(defname) - DNAME_INLINE_LEN + prefix->len - 1;
if (unlikely(prefix->len > DNAME_INLINE_LEN)) {
dentry = ERR_PTR(-ENAMETOOLONG);
if (unlikely(qs.len > NAME_MAX))
goto out;
dentry = ERR_PTR(-ENOMEM);
name = kmalloc(qs.len + 1, GFP_NOFS);
if (unlikely(!name))
goto out;
}
/* doubly whiteout-ed */
memcpy(name, AUFS_WH_PFX AUFS_WH_PFX, AUFS_WH_PFX_LEN * 2);
p = name + AUFS_WH_PFX_LEN * 2;
memcpy(p, prefix->name, prefix->len);
p += prefix->len;
*p++ = '.';
AuDebugOn(name + qs.len + 1 - p <= AUFS_WH_TMP_LEN);
qs.name = name;
for (i = 0; i < 3; i++) {
sprintf(p, "%.*x", AUFS_WH_TMP_LEN, cnt++);
dentry = au_sio_lkup_one(&qs, h_parent);
if (IS_ERR(dentry) || d_is_negative(dentry))
goto out_name;
dput(dentry);
}
/* pr_warn("could not get random name\n"); */
dentry = ERR_PTR(-EEXIST);
AuDbg("%.*s\n", AuLNPair(&qs));
BUG();
out_name:
if (name != defname)
au_kfree_try_rcu(name);
out:
AuTraceErrPtr(dentry);
return dentry;
}
/* ---------------------------------------------------------------------- */
/*
* functions for removing a whiteout
......
......@@ -18,11 +18,13 @@ int au_wh_name_alloc(struct qstr *wh, const struct qstr *name);
struct dentry;
int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, int try_sio);
int au_diropq_test(struct dentry *h_dentry);
struct au_branch;
struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br,
struct qstr *prefix);
struct inode;
struct path;
int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path,
struct dentry *dentry);
struct au_branch;
struct super_block;
int au_wh_init(struct au_branch *br, struct super_block *sb);
......
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