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

aufs: inode op, add an entry



Here are entry adding inode operations, i_op->create(), symlink(),
mkdir(), mknod(), and tmpfile().
Obviously they return EOPNOTSUPP when the target branch fs doesn't
support the operation.
Signed-off-by: default avatarJ. R. Okajima <hooanon05g@gmail.com>
parent 7213fdc8
...@@ -17,7 +17,7 @@ aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \ ...@@ -17,7 +17,7 @@ aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \
dynop.o \ dynop.o \
finfo.o file.o \ finfo.o file.o \
dir.o vdir.o \ dir.o vdir.o \
iinfo.o inode.o i_op.o iinfo.o inode.o i_op.o i_op_add.o
# all are boolean # all are boolean
aufs-$(CONFIG_PROC_FS) += procfs.o plink.o aufs-$(CONFIG_PROC_FS) += procfs.o plink.o
......
...@@ -210,9 +210,10 @@ void au_dpri_dentry(struct dentry *dentry) ...@@ -210,9 +210,10 @@ void au_dpri_dentry(struct dentry *dentry)
dinfo = au_di(dentry); dinfo = au_di(dentry);
if (!dinfo) if (!dinfo)
return; return;
dpri("d-1: btop %d, bbot %d, bwh %d, bdiropq %d, gen %d\n", dpri("d-1: btop %d, bbot %d, bwh %d, bdiropq %d, gen %d, tmp %d\n",
dinfo->di_btop, dinfo->di_bbot, dinfo->di_btop, dinfo->di_bbot,
dinfo->di_bwh, dinfo->di_bdiropq, au_digen(dentry)); dinfo->di_bwh, dinfo->di_bdiropq, au_digen(dentry),
dinfo->di_tmpfile);
if (dinfo->di_btop < 0) if (dinfo->di_btop < 0)
return; return;
for (bindex = dinfo->di_btop; bindex <= dinfo->di_bbot; bindex++) for (bindex = dinfo->di_btop; bindex <= dinfo->di_bbot; bindex++)
......
...@@ -704,7 +704,7 @@ int au_refresh_dentry(struct dentry *dentry, struct dentry *parent) ...@@ -704,7 +704,7 @@ int au_refresh_dentry(struct dentry *dentry, struct dentry *parent)
if (!ebrange) if (!ebrange)
ebrange = au_do_refresh_hdentry(dentry, parent); ebrange = au_do_refresh_hdentry(dentry, parent);
if (d_unhashed(dentry) || ebrange) { if (d_unhashed(dentry) || ebrange /* || dinfo->di_tmpfile */) {
AuDebugOn(au_dbtop(dentry) < 0 && au_dbbot(dentry) >= 0); AuDebugOn(au_dbtop(dentry) < 0 && au_dbbot(dentry) >= 0);
if (d_really_is_positive(dentry)) { if (d_really_is_positive(dentry)) {
inode = d_inode(dentry); inode = d_inode(dentry);
...@@ -793,7 +793,7 @@ static int h_d_revalidate(struct dentry *dentry, struct inode *inode, ...@@ -793,7 +793,7 @@ static int h_d_revalidate(struct dentry *dentry, struct inode *inode,
int err; int err;
umode_t mode, h_mode; umode_t mode, h_mode;
aufs_bindex_t bindex, btail, btop, ibs, ibe; aufs_bindex_t bindex, btail, btop, ibs, ibe;
unsigned char plus, unhashed, is_root, h_plus, h_nfs; unsigned char plus, unhashed, is_root, h_plus, h_nfs, tmpfile;
struct inode *h_inode, *h_cached_inode; struct inode *h_inode, *h_cached_inode;
struct dentry *h_dentry; struct dentry *h_dentry;
struct qstr *name, *h_name; struct qstr *name, *h_name;
...@@ -806,6 +806,7 @@ static int h_d_revalidate(struct dentry *dentry, struct inode *inode, ...@@ -806,6 +806,7 @@ static int h_d_revalidate(struct dentry *dentry, struct inode *inode,
unhashed = !!d_unhashed(dentry); unhashed = !!d_unhashed(dentry);
is_root = !!IS_ROOT(dentry); is_root = !!IS_ROOT(dentry);
name = &dentry->d_name; name = &dentry->d_name;
tmpfile = au_di(dentry)->di_tmpfile;
/* /*
* Theoretically, REVAL test should be unnecessary in case of * Theoretically, REVAL test should be unnecessary in case of
...@@ -838,7 +839,8 @@ static int h_d_revalidate(struct dentry *dentry, struct inode *inode, ...@@ -838,7 +839,8 @@ static int h_d_revalidate(struct dentry *dentry, struct inode *inode,
&& !is_root && !is_root
&& ((!h_nfs && ((!h_nfs
&& (unhashed != !!d_unhashed(h_dentry) && (unhashed != !!d_unhashed(h_dentry)
|| !au_qstreq(name, h_name) || (!tmpfile
&& !au_qstreq(name, h_name))
)) ))
|| (h_nfs || (h_nfs
&& !(flags & LOOKUP_OPEN) && !(flags & LOOKUP_OPEN)
...@@ -882,7 +884,7 @@ static int h_d_revalidate(struct dentry *dentry, struct inode *inode, ...@@ -882,7 +884,7 @@ static int h_d_revalidate(struct dentry *dentry, struct inode *inode,
h_cached_inode = au_h_iptr(inode, bindex); h_cached_inode = au_h_iptr(inode, bindex);
if (!h_nfs) { if (!h_nfs) {
if (unlikely(plus != h_plus)) if (unlikely(plus != h_plus && !tmpfile))
goto err; goto err;
} else { } else {
if (unlikely(!(h_dentry->d_flags & DCACHE_NFSFS_RENAMED) if (unlikely(!(h_dentry->d_flags & DCACHE_NFSFS_RENAMED)
......
...@@ -25,6 +25,7 @@ struct au_dinfo { ...@@ -25,6 +25,7 @@ struct au_dinfo {
struct au_rwsem di_rwsem; struct au_rwsem di_rwsem;
aufs_bindex_t di_btop, di_bbot, di_bwh, di_bdiropq; aufs_bindex_t di_btop, di_bbot, di_bwh, di_bdiropq;
unsigned char di_tmpfile; /* to allow the different name */
struct au_hdentry *di_hdentry; struct au_hdentry *di_hdentry;
struct rcu_head rcu; struct rcu_head rcu;
} ____cacheline_aligned_in_smp; } ____cacheline_aligned_in_smp;
...@@ -87,6 +88,7 @@ void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, ...@@ -87,6 +88,7 @@ void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex,
int au_digen_test(struct dentry *dentry, unsigned int sigen); int au_digen_test(struct dentry *dentry, unsigned int sigen);
int au_dbrange_test(struct dentry *dentry); int au_dbrange_test(struct dentry *dentry);
void au_update_digen(struct dentry *dentry); void au_update_digen(struct dentry *dentry);
void au_update_dbrange(struct dentry *dentry, int do_put_zero);
void au_update_dbtop(struct dentry *dentry); void au_update_dbtop(struct dentry *dentry);
void au_update_dbbot(struct dentry *dentry); void au_update_dbbot(struct dentry *dentry);
int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry); int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry);
......
...@@ -35,6 +35,7 @@ struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc) ...@@ -35,6 +35,7 @@ struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc)
dinfo->di_bbot = -1; dinfo->di_bbot = -1;
dinfo->di_bwh = -1; dinfo->di_bwh = -1;
dinfo->di_bdiropq = -1; dinfo->di_bdiropq = -1;
dinfo->di_tmpfile = 0;
for (i = 0; i < nbr; i++) for (i = 0; i < nbr; i++)
dinfo->di_hdentry[i].hd_id = -1; dinfo->di_hdentry[i].hd_id = -1;
goto out; goto out;
...@@ -358,6 +359,48 @@ void au_update_digen(struct dentry *dentry) ...@@ -358,6 +359,48 @@ void au_update_digen(struct dentry *dentry)
/* smp_mb(); */ /* atomic_set */ /* smp_mb(); */ /* atomic_set */
} }
void au_update_dbrange(struct dentry *dentry, int do_put_zero)
{
struct au_dinfo *dinfo;
struct dentry *h_d;
struct au_hdentry *hdp;
aufs_bindex_t bindex, bbot;
DiMustWriteLock(dentry);
dinfo = au_di(dentry);
if (!dinfo || dinfo->di_btop < 0)
return;
if (do_put_zero) {
bbot = dinfo->di_bbot;
bindex = dinfo->di_btop;
hdp = au_hdentry(dinfo, bindex);
for (; bindex <= bbot; bindex++, hdp++) {
h_d = hdp->hd_dentry;
if (h_d && d_is_negative(h_d))
au_set_h_dptr(dentry, bindex, NULL);
}
}
dinfo->di_btop = 0;
hdp = au_hdentry(dinfo, dinfo->di_btop);
for (; dinfo->di_btop <= dinfo->di_bbot; dinfo->di_btop++, hdp++)
if (hdp->hd_dentry)
break;
if (dinfo->di_btop > dinfo->di_bbot) {
dinfo->di_btop = -1;
dinfo->di_bbot = -1;
return;
}
hdp = au_hdentry(dinfo, dinfo->di_bbot);
for (; dinfo->di_bbot >= 0; dinfo->di_bbot--, hdp--)
if (hdp->hd_dentry)
break;
AuDebugOn(dinfo->di_btop > dinfo->di_bbot || dinfo->di_bbot < 0);
}
void au_update_dbtop(struct dentry *dentry) void au_update_dbtop(struct dentry *dentry)
{ {
aufs_bindex_t bindex, bbot; aufs_bindex_t bindex, bbot;
......
...@@ -232,6 +232,134 @@ out: ...@@ -232,6 +232,134 @@ out:
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
static int au_wr_dir_cpup(struct dentry *dentry, struct dentry *parent,
const unsigned char add_entry, aufs_bindex_t bcpup,
aufs_bindex_t btop)
{
int err;
struct dentry *h_parent;
struct inode *h_dir;
if (add_entry)
IMustLock(d_inode(parent));
else
di_write_lock_parent(parent);
err = 0;
if (!au_h_dptr(parent, bcpup)) {
if (btop > bcpup)
err = au_cpup_dirs(dentry, bcpup);
else if (btop < bcpup)
err = au_cpdown_dirs(dentry, bcpup);
else
BUG();
}
if (!err && add_entry && !au_ftest_wrdir(add_entry, TMPFILE)) {
h_parent = au_h_dptr(parent, bcpup);
h_dir = d_inode(h_parent);
inode_lock_shared_nested(h_dir, AuLsc_I_PARENT);
err = au_lkup_neg(dentry, bcpup, /*wh*/0);
/* todo: no unlock here */
inode_unlock_shared(h_dir);
AuDbg("bcpup %d\n", bcpup);
if (!err) {
if (d_really_is_negative(dentry))
au_set_h_dptr(dentry, btop, NULL);
au_update_dbrange(dentry, /*do_put_zero*/0);
}
}
if (!add_entry)
di_write_unlock(parent);
if (!err)
err = bcpup; /* success */
AuTraceErr(err);
return err;
}
/*
* decide the branch and the parent dir where we will create a new entry.
* returns new bindex or an error.
* copyup the parent dir if needed.
*/
int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry,
struct au_wr_dir_args *args)
{
int err;
unsigned int flags;
aufs_bindex_t bcpup, btop, src_btop;
const unsigned char add_entry
= au_ftest_wrdir(args->flags, ADD_ENTRY)
| au_ftest_wrdir(args->flags, TMPFILE);
struct super_block *sb;
struct dentry *parent;
struct au_sbinfo *sbinfo;
sb = dentry->d_sb;
sbinfo = au_sbi(sb);
parent = dget_parent(dentry);
btop = au_dbtop(dentry);
bcpup = btop;
if (args->force_btgt < 0) {
if (src_dentry) {
src_btop = au_dbtop(src_dentry);
if (src_btop < btop)
bcpup = src_btop;
} else if (add_entry) {
flags = 0;
if (au_ftest_wrdir(args->flags, ISDIR))
au_fset_wbr(flags, DIR);
err = AuWbrCreate(sbinfo, dentry, flags);
bcpup = err;
}
if (bcpup < 0 || au_test_ro(sb, bcpup, d_inode(dentry))) {
if (add_entry)
err = AuWbrCopyup(sbinfo, dentry);
else {
if (!IS_ROOT(dentry)) {
di_read_lock_parent(parent, !AuLock_IR);
err = AuWbrCopyup(sbinfo, dentry);
di_read_unlock(parent, !AuLock_IR);
} else
err = AuWbrCopyup(sbinfo, dentry);
}
bcpup = err;
if (unlikely(err < 0))
goto out;
}
} else {
bcpup = args->force_btgt;
AuDebugOn(au_test_ro(sb, bcpup, d_inode(dentry)));
}
AuDbg("btop %d, bcpup %d\n", btop, bcpup);
err = bcpup;
if (bcpup == btop)
goto out; /* success */
/* copyup the new parent into the branch we process */
err = au_wr_dir_cpup(dentry, parent, add_entry, bcpup, btop);
if (err >= 0) {
if (d_really_is_negative(dentry)) {
au_set_h_dptr(dentry, btop, NULL);
au_set_dbtop(dentry, bcpup);
au_set_dbbot(dentry, bcpup);
}
AuDebugOn(add_entry
&& !au_ftest_wrdir(args->flags, TMPFILE)
&& !au_h_dptr(dentry, bcpup));
}
out:
dput(parent);
return err;
}
/* ---------------------------------------------------------------------- */
void au_pin_hdir_unlock(struct au_pin *p) void au_pin_hdir_unlock(struct au_pin *p)
{ {
if (p->hdir) if (p->hdir)
...@@ -517,9 +645,15 @@ struct inode_operations aufs_iop[] = { ...@@ -517,9 +645,15 @@ struct inode_operations aufs_iop[] = {
.get_link = aufs_get_link .get_link = aufs_get_link
}, },
[AuIop_DIR] = { [AuIop_DIR] = {
.create = aufs_create,
.lookup = aufs_lookup, .lookup = aufs_lookup,
.symlink = aufs_symlink,
.mkdir = aufs_mkdir,
.mknod = aufs_mknod,
.permission = aufs_permission .permission = aufs_permission,
.tmpfile = aufs_tmpfile
}, },
[AuIop_OTHER] = { [AuIop_OTHER] = {
.permission = aufs_permission .permission = aufs_permission
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2005-2019 Junjiro R. Okajima
*/
/*
* inode operations (add entry)
*/
#include <linux/iversion.h>
#include "aufs.h"
/*
* final procedure of adding a new entry, except link(2).
* remove whiteout, instantiate, copyup the parent dir's times and size
* and update version.
* if it failed, re-create the removed whiteout.
*/
static int epilog(struct inode *dir, aufs_bindex_t bindex,
struct dentry *wh_dentry, struct dentry *dentry)
{
int err, rerr;
aufs_bindex_t bwh;
struct path h_path;
struct super_block *sb;
struct inode *inode, *h_dir;
struct dentry *wh;
bwh = -1;
sb = dir->i_sb;
if (wh_dentry) {
h_dir = d_inode(wh_dentry->d_parent); /* dir inode is locked */
IMustLock(h_dir);
AuDebugOn(au_h_iptr(dir, bindex) != h_dir);
bwh = au_dbwh(dentry);
h_path.dentry = wh_dentry;
h_path.mnt = au_sbr_mnt(sb, bindex);
err = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path,
dentry);
if (unlikely(err))
goto out;
}
inode = au_new_inode(dentry, /*must_new*/1);
if (!IS_ERR(inode)) {
d_instantiate(dentry, inode);
dir = d_inode(dentry->d_parent); /* dir inode is locked */
IMustLock(dir);
au_dir_ts(dir, bindex);
inode_inc_iversion(dir);
return 0; /* success */
}
err = PTR_ERR(inode);
if (!wh_dentry)
goto out;
/* revert */
/* dir inode is locked */
wh = au_wh_create(dentry, bwh, wh_dentry->d_parent);
rerr = PTR_ERR(wh);
if (IS_ERR(wh)) {
AuIOErr("%pd reverting whiteout failed(%d, %d)\n",
dentry, err, rerr);
err = -EIO;
} else
dput(wh);
out:
return err;
}
static int au_d_may_add(struct dentry *dentry)
{
int err;
err = 0;
if (unlikely(d_unhashed(dentry)))
err = -ENOENT;
if (unlikely(d_really_is_positive(dentry)))
err = -EEXIST;
return err;
}
/*
* simple tests for the adding inode operations.
* following the checks in vfs, plus the parent-child relationship.
*/
int au_may_add(struct dentry *dentry, aufs_bindex_t bindex,
struct dentry *h_parent, int isdir)
{
int err;
umode_t h_mode;
struct dentry *h_dentry;
struct inode *h_inode;
err = -ENAMETOOLONG;
if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN))
goto out;
h_dentry = au_h_dptr(dentry, bindex);
if (d_really_is_negative(dentry)) {
err = -EEXIST;
if (unlikely(d_is_positive(h_dentry)))
goto out;
} else {
/* rename(2) case */
err = -EIO;
if (unlikely(d_is_negative(h_dentry)))
goto out;
h_inode = d_inode(h_dentry);
if (unlikely(!h_inode->i_nlink))
goto out;
h_mode = h_inode->i_mode;
if (!isdir) {
err = -EISDIR;
if (unlikely(S_ISDIR(h_mode)))
goto out;
} else if (unlikely(!S_ISDIR(h_mode))) {
err = -ENOTDIR;
goto out;
}
}
err = 0;
/* expected parent dir is locked */
if (unlikely(h_parent != h_dentry->d_parent))
err = -EIO;
out:
AuTraceErr(err);
return err;
}
/*
* initial procedure of adding a new entry.
* prepare writable branch and the parent dir, lock it,
* and lookup whiteout for the new entry.
*/
static struct dentry*
lock_hdir_lkup_wh(struct dentry *dentry, struct au_dtime *dt,
struct dentry *src_dentry, struct au_pin *pin,
struct au_wr_dir_args *wr_dir_args)
{
struct dentry *wh_dentry, *h_parent;
struct super_block *sb;
struct au_branch *br;
int err;
unsigned int udba;
aufs_bindex_t bcpup;
AuDbg("%pd\n", dentry);
err = au_wr_dir(dentry, src_dentry, wr_dir_args);
bcpup = err;
wh_dentry = ERR_PTR(err);
if (unlikely(err < 0))
goto out;
sb = dentry->d_sb;
udba = au_opt_udba(sb);
err = au_pin(pin, dentry, bcpup, udba,
AuPin_DI_LOCKED | AuPin_MNT_WRITE);
wh_dentry = ERR_PTR(err);
if (unlikely(err))
goto out;
h_parent = au_pinned_h_parent(pin);
if (udba != AuOpt_UDBA_NONE
&& au_dbtop(dentry) == bcpup)
err = au_may_add(dentry, bcpup, h_parent,
au_ftest_wrdir(wr_dir_args->flags, ISDIR));
else if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN))
err = -ENAMETOOLONG;
wh_dentry = ERR_PTR(err);
if (unlikely(err))
goto out_unpin;
br = au_sbr(sb, bcpup);
if (dt) {
struct path tmp = {
.dentry = h_parent,
.mnt = au_br_mnt(br)
};
au_dtime_store(dt, au_pinned_parent(pin), &tmp);
}
wh_dentry = NULL;
if (bcpup != au_dbwh(dentry))
goto out; /* success */
/*
* ENAMETOOLONG here means that if we allowed create such name, then it
* would not be able to removed in the future. So we don't allow such
* name here and we don't handle ENAMETOOLONG differently here.
*/
wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, br);
out_unpin:
if (IS_ERR(wh_dentry))
au_unpin(pin);
out:
return wh_dentry;
}
/* ---------------------------------------------------------------------- */
enum { Mknod, Symlink, Creat };
struct simple_arg {
int type;
union {
struct {
umode_t mode;
bool want_excl;
} c;
struct {
const char *symname;
} s;
struct {
umode_t mode;
dev_t dev;
} m;
} u;
};
static int add_simple(struct inode *dir, struct dentry *dentry,
struct simple_arg *arg)
{
int err, rerr;
aufs_bindex_t btop;
unsigned char created;
struct dentry *wh_dentry, *parent;
struct inode *h_dir;
/* to reduce stack size */
struct {
struct au_dtime dt;
struct au_pin pin;
struct path h_path;
struct au_wr_dir_args wr_dir_args;
} *a;
AuDbg("%pd\n", dentry);
IMustLock(dir);
err = -ENOMEM;
a = kmalloc(sizeof(*a), GFP_NOFS);
if (unlikely(!a))
goto out;
a->wr_dir_args.force_btgt = -1;
a->wr_dir_args.flags = AuWrDir_ADD_ENTRY;
parent = dentry->d_parent; /* dir inode is locked */
err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN);
if (unlikely(err))
goto out_free;
err = au_d_may_add(dentry);
if (unlikely(err))
goto out_unlock;
di_write_lock_parent(parent);
wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL,
&a->pin, &a->wr_dir_args);
err = PTR_ERR(wh_dentry);
if (IS_ERR(wh_dentry))
goto out_parent;
btop = au_dbtop(dentry);
a->h_path.dentry = au_h_dptr(dentry, btop);
a->h_path.mnt = au_sbr_mnt(dentry->d_sb, btop);
h_dir = au_pinned_h_dir(&a->pin);
switch (arg->type) {
case Creat:
err = vfsub_create(h_dir, &a->h_path, arg->u.c.mode,
arg->u.c.want_excl);
break;
case Symlink:
err = vfsub_symlink(h_dir, &a->h_path, arg->u.s.symname);
break;
case Mknod:
err = vfsub_mknod(h_dir, &a->h_path, arg->u.m.mode,
arg->u.m.dev);
break;
default:
BUG();
}
created = !err;
if (!err)
err = epilog(dir, btop, wh_dentry, dentry);
/* revert */
if (unlikely(created && err && d_is_positive(a->h_path.dentry))) {
/* no delegation since it is just created */
rerr = vfsub_unlink(h_dir, &a->h_path, /*delegated*/NULL,
/*force*/0);
if (rerr) {
AuIOErr("%pd revert failure(%d, %d)\n",
dentry, err, rerr);
err = -EIO;
}
au_dtime_revert(&a->dt);
}
au_unpin(&a->pin);
dput(wh_dentry);
out_parent:
di_write_unlock(parent);
out_unlock:
if (unlikely(err)) {
au_update_dbtop(dentry);
d_drop(dentry);
}
aufs_read_unlock(dentry, AuLock_DW);
out_free:
au_kfree_rcu(a);
out:
return err;
}
int aufs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
dev_t dev)
{
struct simple_arg arg = {
.type = Mknod,
.u.m = {
.mode = mode,
.dev = dev
}
};
return add_simple(dir, dentry, &arg);
}
int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
{
struct simple_arg arg = {
.type = Symlink,
.u.s.symname = symname
};
return add_simple(dir, dentry, &arg);
}
int aufs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
bool want_excl)
{
struct simple_arg arg = {
.type = Creat,
.u.c = {
.mode = mode,
.want_excl = want_excl
}
};
return add_simple(dir, dentry, &arg);
}
int aufs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
{
int err;
aufs_bindex_t bindex;
struct super_block *sb;
struct dentry *parent, *h_parent, *h_dentry;
struct inode *h_dir, *inode;
struct vfsmount *h_mnt;
struct au_wr_dir_args wr_dir_args = {
.force_btgt = -1,
.flags = AuWrDir_TMPFILE
};
/* copy-up may happen */
inode_lock(dir);
sb = dir->i_sb;
err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
if (unlikely(err))
goto out;
err = au_di_init(dentry);
if (unlikely(err))
goto out_si;
err = -EBUSY;
parent = d_find_any_alias(dir);
AuDebugOn(!parent);
di_write_lock_parent(parent);
if (unlikely(d_inode(parent) != dir))
goto out_parent;
err = au_digen_test(parent, au_sigen(sb));
if (unlikely(err))
goto out_parent;
bindex = au_dbtop(parent);
au_set_dbtop(dentry, bindex);
au_set_dbbot(dentry, bindex);
err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args);
bindex = err;
if (unlikely(err < 0))
goto out_parent;
err = -EOPNOTSUPP;
h_dir = au_h_iptr(dir, bindex);
if (unlikely(!h_dir->i_op->tmpfile))
goto out_parent;
h_mnt = au_sbr_mnt(sb, bindex);
err = vfsub_mnt_want_write(h_mnt);
if (unlikely(err))
goto out_parent;
h_parent = au_h_dptr(parent, bindex);
h_dentry = vfs_tmpfile(h_parent, mode, /*open_flag*/0);
if (IS_ERR(h_dentry)) {
err = PTR_ERR(h_dentry);
goto out_mnt;
}
au_set_dbtop(dentry, bindex);
au_set_dbbot(dentry, bindex);
au_set_h_dptr(dentry, bindex, dget(h_dentry));
inode = au_new_inode(dentry, /*must_new*/1);
if (IS_ERR(inode)) {
err = PTR_ERR(inode);
au_set_h_dptr(dentry, bindex, NULL);
au_set_dbtop(dentry, -1);
au_set_dbbot(dentry, -1);
} else {
if (!inode->i_nlink)
set_nlink(inode, 1);
d_tmpfile(dentry, inode);
au_di(dentry)->di_tmpfile = 1;
/* update without i_mutex */
if (au_ibtop(dir) == au_dbtop(dentry))
au_cpup_attr_timesizes(dir);
}
dput(h_dentry);
out_mnt:
vfsub_mnt_drop_write(h_mnt);
out_parent:
di_write_unlock(parent);
dput(parent);
di_write_unlock(dentry);
if (unlikely(err)) {
au_di_fin(dentry);
dentry->d_fsdata = NULL;
}
out_si:
si_read_unlock(sb);
out:
inode_unlock(dir);
return err;
}
/* ---------------------------------------------------------------------- */
int aufs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
{
int err, rerr;
aufs_bindex_t bindex;
unsigned char diropq;
struct path h_path;
struct dentry *wh_dentry, *parent, *opq_dentry;
struct inode *h_inode;
struct super_block *sb;
struct {
struct au_pin pin;
struct au_dtime dt;
} *a; /* reduce the stack usage */
struct au_wr_dir_args wr_dir_args = {
.force_btgt = -1,
.flags = AuWrDir_ADD_ENTRY | AuWrDir_ISDIR
};
IMustLock(dir);
err = -ENOMEM;
a = kmalloc(sizeof(*a), GFP_NOFS);
if (unlikely(!a))
goto out;
err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN);
if (unlikely(err))
goto out_free;
err = au_d_may_add(dentry);
if (unlikely(err))
goto out_unlock;
parent = dentry->d_parent; /* dir inode is locked */
di_write_lock_parent(parent);
wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL,
&a->pin, &wr_dir_args);
err = PTR_ERR(wh_dentry);
if (IS_ERR(wh_dentry))
goto out_parent;
sb = dentry->d_sb;
bindex = au_dbtop(dentry);
h_path.dentry = au_h_dptr(dentry, bindex);
h_path.mnt = au_sbr_mnt(sb, bindex);
err = vfsub_mkdir(au_pinned_h_dir(&a->pin), &h_path, mode);
if (unlikely(err))
goto out_unpin;
/* make the dir opaque */
diropq = 0;
h_inode = d_inode(h_path.dentry);
if (wh_dentry) {
inode_lock_nested(h_inode, AuLsc_I_CHILD);
opq_dentry = au_diropq_create(dentry, bindex);
inode_unlock(h_inode);
err = PTR_ERR(opq_dentry);
if (IS_ERR(opq_dentry))
goto out_dir;
dput(opq_dentry);
diropq = 1;
}
err = epilog(dir, bindex, wh_dentry, dentry);
if (!err) {
inc_nlink(dir);
goto out_unpin; /* success */
}
/* revert */
if (diropq) {
AuLabel(revert opq);
inode_lock_nested(h_inode, AuLsc_I_CHILD);
rerr = au_diropq_remove(dentry, bindex);
inode_unlock(h_inode);
if (rerr) {
AuIOErr("%pd reverting diropq failed(%d, %d)\n",
dentry, err, rerr);
err = -EIO;
}
}
out_dir:
AuLabel(revert dir);
rerr = vfsub_rmdir(au_pinned_h_dir(&a->pin), &h_path);
if (rerr) {
AuIOErr("%pd reverting dir failed(%d, %d)\n",
dentry, err, rerr);
err = -EIO;
}
au_dtime_revert(&a->dt);
out_unpin:
au_unpin(&a->pin);
dput(wh_dentry);
out_parent:
di_write_unlock(parent);
out_unlock:
if (unlikely(err)) {
au_update_dbtop(dentry);
d_drop(dentry);
}
aufs_read_unlock(dentry, AuLock_DW);
out_free:
au_kfree_rcu(a);
out:
return err;
}
...@@ -135,6 +135,23 @@ enum { ...@@ -135,6 +135,23 @@ enum {
}; };
extern struct inode_operations aufs_iop[AuIop_Last]; extern struct inode_operations aufs_iop[AuIop_Last];
/* au_wr_dir flags */
#define AuWrDir_ADD_ENTRY 1
#define AuWrDir_ISDIR (1 << 1)
#define AuWrDir_TMPFILE (1 << 2)
#define au_ftest_wrdir(flags, name) ((flags) & AuWrDir_##name)
#define au_fset_wrdir(flags, name) \
do { (flags) |= AuWrDir_##name; } while (0)
#define au_fclr_wrdir(flags, name) \
do { (flags) &= ~AuWrDir_##name; } while (0)
struct au_wr_dir_args {
aufs_bindex_t force_btgt;
unsigned char flags;
};
int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry,
struct au_wr_dir_args *args);
struct dentry *au_pinned_h_parent(struct au_pin *pin); struct dentry *au_pinned_h_parent(struct au_pin *pin);
void au_pin_init(struct au_pin *pin, struct dentry *dentry, void au_pin_init(struct au_pin *pin, struct dentry *dentry,
aufs_bindex_t bindex, int lsc_di, int lsc_hi, aufs_bindex_t bindex, int lsc_di, int lsc_hi,
...@@ -144,6 +161,17 @@ int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex, ...@@ -144,6 +161,17 @@ int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex,
int au_do_pin(struct au_pin *pin) __must_check; int au_do_pin(struct au_pin *pin) __must_check;
void au_unpin(struct au_pin *pin); void au_unpin(struct au_pin *pin);
/* i_op_add.c */
int au_may_add(struct dentry *dentry, aufs_bindex_t bindex,
struct dentry *h_parent, int isdir);
int aufs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
dev_t dev);
int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname);
int aufs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
bool want_excl);
int aufs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode);
int aufs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode);
/* iinfo.c */ /* iinfo.c */
struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex); struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex);
void au_hiput(struct au_hinode *hinode); void au_hiput(struct au_hinode *hinode);
......
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