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

aufs: copy-up 7/7, white-out



Imagine a sequence such like this.
- user opens a file which exists on the lower readonly branch.
- user unlinks the file (still opened). its parent dir may be removed
  too.
- user writes something to the file.

Then aufs has to copy-up the unlinked file.
Note that the copy-up in aufs is not done in open(2) by default.
This commit handles this case by copy-up the file in write(2).
The target directory is the one which aufs created internally for this
purpose.
Signed-off-by: default avatarJ. R. Okajima <hooanon05g@gmail.com>
parent 377c9279
...@@ -1065,6 +1065,182 @@ int au_sio_cpup_simple(struct au_cp_generic *cpg) ...@@ -1065,6 +1065,182 @@ int au_sio_cpup_simple(struct au_cp_generic *cpg)
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
/*
* copyup the deleted file for writing.
*/
static int au_do_cpup_wh(struct au_cp_generic *cpg, struct dentry *wh_dentry,
struct file *file)
{
int err;
unsigned int flags_orig;
aufs_bindex_t bsrc_orig;
struct au_dinfo *dinfo;
struct {
struct au_hdentry *hd;
struct dentry *h_dentry;
} hdst, hsrc;
dinfo = au_di(cpg->dentry);
AuRwMustWriteLock(&dinfo->di_rwsem);
bsrc_orig = cpg->bsrc;
cpg->bsrc = dinfo->di_btop;
hdst.hd = au_hdentry(dinfo, cpg->bdst);
hdst.h_dentry = hdst.hd->hd_dentry;
hdst.hd->hd_dentry = wh_dentry;
dinfo->di_btop = cpg->bdst;
hsrc.h_dentry = NULL;
if (file) {
hsrc.hd = au_hdentry(dinfo, cpg->bsrc);
hsrc.h_dentry = hsrc.hd->hd_dentry;
hsrc.hd->hd_dentry = au_hf_top(file)->f_path.dentry;
}
flags_orig = cpg->flags;
cpg->flags = !AuCpup_DTIME;
err = au_cpup_single(cpg, /*h_parent*/NULL);
cpg->flags = flags_orig;
if (file) {
if (!err)
/* err = au_reopen_nondir(file); re-commit later */
err = 0;
hsrc.hd->hd_dentry = hsrc.h_dentry;
}
hdst.hd->hd_dentry = hdst.h_dentry;
dinfo->di_btop = cpg->bsrc;
cpg->bsrc = bsrc_orig;
return err;
}
static int au_cpup_wh(struct au_cp_generic *cpg, struct file *file)
{
int err;
aufs_bindex_t bdst;
struct au_dtime dt;
struct dentry *dentry, *parent, *h_parent, *wh_dentry;
struct au_branch *br;
struct path h_path;
dentry = cpg->dentry;
bdst = cpg->bdst;
br = au_sbr(dentry->d_sb, bdst);
parent = dget_parent(dentry);
h_parent = au_h_dptr(parent, bdst);
wh_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name);
err = PTR_ERR(wh_dentry);
if (IS_ERR(wh_dentry))
goto out;
h_path.dentry = h_parent;
h_path.mnt = au_br_mnt(br);
au_dtime_store(&dt, parent, &h_path);
err = au_do_cpup_wh(cpg, wh_dentry, file);
if (unlikely(err))
goto out_wh;
dget(wh_dentry);
h_path.dentry = wh_dentry;
if (!d_is_dir(wh_dentry)) {
/* no delegation since it is just created */
err = vfsub_unlink(d_inode(h_parent), &h_path,
/*delegated*/NULL, /*force*/0);
} else
err = vfsub_rmdir(d_inode(h_parent), &h_path);
if (unlikely(err)) {
AuIOErr("failed remove copied-up tmp file %pd(%d)\n",
wh_dentry, err);
err = -EIO;
}
au_dtime_revert(&dt);
au_set_hi_wh(d_inode(dentry), bdst, wh_dentry);
out_wh:
dput(wh_dentry);
out:
dput(parent);
return err;
}
struct au_cpup_wh_args {
int *errp;
struct au_cp_generic *cpg;
struct file *file;
};
static void au_call_cpup_wh(void *args)
{
struct au_cpup_wh_args *a = args;
au_pin_hdir_acquire_nest(a->cpg->pin);
*a->errp = au_cpup_wh(a->cpg, a->file);
au_pin_hdir_release(a->cpg->pin);
}
int au_sio_cpup_wh(struct au_cp_generic *cpg, struct file *file)
{
int err, wkq_err;
aufs_bindex_t bdst;
struct dentry *dentry, *parent, *h_orph, *h_parent;
struct inode *dir, *h_dir, *h_tmpdir;
struct au_wbr *wbr;
struct au_pin wh_pin, *pin_orig;
dentry = cpg->dentry;
bdst = cpg->bdst;
parent = dget_parent(dentry);
dir = d_inode(parent);
h_orph = NULL;
h_parent = NULL;
h_dir = au_igrab(au_h_iptr(dir, bdst));
h_tmpdir = h_dir;
pin_orig = NULL;
if (!h_dir->i_nlink) {
wbr = au_sbr(dentry->d_sb, bdst)->br_wbr;
h_orph = wbr->wbr_orph;
h_parent = dget(au_h_dptr(parent, bdst));
au_set_h_dptr(parent, bdst, dget(h_orph));
h_tmpdir = d_inode(h_orph);
au_set_h_iptr(dir, bdst, au_igrab(h_tmpdir), /*flags*/0);
inode_lock_nested(h_tmpdir, AuLsc_I_PARENT3);
pin_orig = cpg->pin;
au_pin_init(&wh_pin, dentry, bdst, AuLsc_DI_PARENT,
AuLsc_I_PARENT3, AuPin_DI_LOCKED);
cpg->pin = &wh_pin;
}
if (!au_test_h_perm_sio(h_tmpdir, MAY_EXEC | MAY_WRITE)
&& !au_cpup_sio_test(cpg->pin, d_inode(dentry)->i_mode))
err = au_cpup_wh(cpg, file);
else {
struct au_cpup_wh_args args = {
.errp = &err,
.cpg = cpg,
.file = file
};
wkq_err = au_wkq_wait(au_call_cpup_wh, &args);
if (unlikely(wkq_err))
err = wkq_err;
}
if (h_orph) {
inode_unlock(h_tmpdir);
au_set_h_iptr(dir, bdst, au_igrab(h_dir), /*flags*/0);
au_set_h_dptr(parent, bdst, h_parent);
AuDebugOn(!pin_orig);
cpg->pin = pin_orig;
}
iput(h_dir);
dput(parent);
return err;
}
/* ---------------------------------------------------------------------- */
/* /*
* generic routine for both of copy-up and copy-down. * generic routine for both of copy-up and copy-down.
*/ */
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/path.h> #include <linux/path.h>
struct inode; struct inode;
struct file;
struct au_pin; struct au_pin;
void au_cpup_attr_flags(struct inode *dst, unsigned int iflags); void au_cpup_attr_flags(struct inode *dst, unsigned int iflags);
...@@ -48,6 +49,7 @@ struct au_cp_generic { ...@@ -48,6 +49,7 @@ struct au_cp_generic {
int au_copy_file(struct file *dst, struct file *src, loff_t len); int au_copy_file(struct file *dst, struct file *src, loff_t len);
int au_sio_cpup_simple(struct au_cp_generic *cpg); int au_sio_cpup_simple(struct au_cp_generic *cpg);
int au_sio_cpup_wh(struct au_cp_generic *cpg, struct file *file);
int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst,
int (*cp)(struct dentry *dentry, aufs_bindex_t bdst, int (*cp)(struct dentry *dentry, aufs_bindex_t bdst,
......
...@@ -55,8 +55,12 @@ char *au_plevel = KERN_DEBUG; ...@@ -55,8 +55,12 @@ char *au_plevel = KERN_DEBUG;
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
static int do_pri_inode(aufs_bindex_t bindex, struct inode *inode) static int do_pri_inode(aufs_bindex_t bindex, struct inode *inode,
struct dentry *wh)
{ {
char *n = NULL;
int l = 0;
if (!inode || IS_ERR(inode)) { if (!inode || IS_ERR(inode)) {
dpri("i%d: err %ld\n", bindex, PTR_ERR(inode)); dpri("i%d: err %ld\n", bindex, PTR_ERR(inode));
return -1; return -1;
...@@ -65,9 +69,13 @@ static int do_pri_inode(aufs_bindex_t bindex, struct inode *inode) ...@@ -65,9 +69,13 @@ static int do_pri_inode(aufs_bindex_t bindex, struct inode *inode)
/* the type of i_blocks depends upon CONFIG_LBDAF */ /* the type of i_blocks depends upon CONFIG_LBDAF */
BUILD_BUG_ON(sizeof(inode->i_blocks) != sizeof(unsigned long) BUILD_BUG_ON(sizeof(inode->i_blocks) != sizeof(unsigned long)
&& sizeof(inode->i_blocks) != sizeof(u64)); && sizeof(inode->i_blocks) != sizeof(u64));
if (wh) {
n = (void *)wh->d_name.name;
l = wh->d_name.len;
}
dpri("i%d: %p, i%lu, %s, cnt %d, nl %u, 0%o, sz %llu, blk %llu," dpri("i%d: %p, i%lu, %s, cnt %d, nl %u, 0%o, sz %llu, blk %llu,"
" ct %lld, np %lu, st 0x%lx, f 0x%x, v %llu, g %x\n", " ct %lld, np %lu, st 0x%lx, f 0x%x, v %llu, g %x%s%.*s\n",
bindex, inode, bindex, inode,
inode->i_ino, inode->i_sb ? au_sbtype(inode->i_sb) : "??", inode->i_ino, inode->i_sb ? au_sbtype(inode->i_sb) : "??",
atomic_read(&inode->i_count), inode->i_nlink, inode->i_mode, atomic_read(&inode->i_count), inode->i_nlink, inode->i_mode,
...@@ -75,7 +83,8 @@ static int do_pri_inode(aufs_bindex_t bindex, struct inode *inode) ...@@ -75,7 +83,8 @@ static int do_pri_inode(aufs_bindex_t bindex, struct inode *inode)
(long long)timespec64_to_ns(&inode->i_ctime) & 0x0ffff, (long long)timespec64_to_ns(&inode->i_ctime) & 0x0ffff,
inode->i_mapping ? inode->i_mapping->nrpages : 0, inode->i_mapping ? inode->i_mapping->nrpages : 0,
inode->i_state, inode->i_flags, inode_peek_iversion(inode), inode->i_state, inode->i_flags, inode_peek_iversion(inode),
inode->i_generation); inode->i_generation,
l ? ", wh " : "", l, n);
return 0; return 0;
} }
...@@ -85,7 +94,7 @@ void au_dpri_inode(struct inode *inode) ...@@ -85,7 +94,7 @@ void au_dpri_inode(struct inode *inode)
aufs_bindex_t bindex; aufs_bindex_t bindex;
int err; int err;
err = do_pri_inode(-1, inode); err = do_pri_inode(-1, inode, NULL);
if (err || !au_test_aufs(inode->i_sb) || au_is_bad_inode(inode)) if (err || !au_test_aufs(inode->i_sb) || au_is_bad_inode(inode))
return; return;
...@@ -95,7 +104,8 @@ void au_dpri_inode(struct inode *inode) ...@@ -95,7 +104,8 @@ void au_dpri_inode(struct inode *inode)
if (iinfo->ii_btop < 0) if (iinfo->ii_btop < 0)
return; return;
for (bindex = iinfo->ii_btop; bindex <= iinfo->ii_bbot; bindex++) for (bindex = iinfo->ii_btop; bindex <= iinfo->ii_bbot; bindex++)
do_pri_inode(bindex, iinfo->ii_hinode[0 + bindex].hi_inode); do_pri_inode(bindex, iinfo->ii_hinode[0 + bindex].hi_inode,
iinfo->ii_hinode[0 + bindex].hi_whdentry);
} }
void au_dpri_dalias(struct inode *inode) void au_dpri_dalias(struct inode *inode)
...@@ -110,6 +120,11 @@ void au_dpri_dalias(struct inode *inode) ...@@ -110,6 +120,11 @@ void au_dpri_dalias(struct inode *inode)
static int do_pri_dentry(aufs_bindex_t bindex, struct dentry *dentry) static int do_pri_dentry(aufs_bindex_t bindex, struct dentry *dentry)
{ {
struct dentry *wh = NULL;
struct inode *inode;
struct au_iinfo *iinfo;
struct au_hinode *hi;
if (!dentry || IS_ERR(dentry)) { if (!dentry || IS_ERR(dentry)) {
dpri("d%d: err %ld\n", bindex, PTR_ERR(dentry)); dpri("d%d: err %ld\n", bindex, PTR_ERR(dentry));
return -1; return -1;
...@@ -121,7 +136,18 @@ static int do_pri_dentry(aufs_bindex_t bindex, struct dentry *dentry) ...@@ -121,7 +136,18 @@ static int do_pri_dentry(aufs_bindex_t bindex, struct dentry *dentry)
dentry->d_sb ? au_sbtype(dentry->d_sb) : "??", dentry->d_sb ? au_sbtype(dentry->d_sb) : "??",
au_dcount(dentry), dentry->d_flags, au_dcount(dentry), dentry->d_flags,
d_unhashed(dentry) ? "un" : ""); d_unhashed(dentry) ? "un" : "");
do_pri_inode(bindex, d_inode(dentry)); inode = NULL;
if (d_is_positive(dentry))
inode = d_inode(dentry);
if (inode
&& au_test_aufs(dentry->d_sb)
&& bindex >= 0
&& !au_is_bad_inode(inode)) {
iinfo = au_ii(inode);
hi = au_hinode(iinfo, bindex);
wh = hi->hi_whdentry;
}
do_pri_inode(bindex, inode, wh);
return 0; return 0;
} }
......
...@@ -25,6 +25,7 @@ struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex) ...@@ -25,6 +25,7 @@ struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex)
/* todo: hard/soft set? */ /* todo: hard/soft set? */
void au_hiput(struct au_hinode *hinode) void au_hiput(struct au_hinode *hinode)
{ {
dput(hinode->hi_whdentry);
iput(hinode->hi_inode); iput(hinode->hi_inode);
} }
...@@ -76,6 +77,18 @@ void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex, ...@@ -76,6 +77,18 @@ void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex,
} }
} }
void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex,
struct dentry *h_wh)
{
struct au_hinode *hinode;
IiMustWriteLock(inode);
hinode = au_hinode(au_ii(inode), bindex);
AuDebugOn(hinode->hi_whdentry);
hinode->hi_whdentry = h_wh;
}
void au_update_iigen(struct inode *inode, int half) void au_update_iigen(struct inode *inode, int half)
{ {
struct au_iinfo *iinfo; struct au_iinfo *iinfo;
...@@ -146,6 +159,7 @@ void au_hinode_init(struct au_hinode *hinode) ...@@ -146,6 +159,7 @@ void au_hinode_init(struct au_hinode *hinode)
{ {
hinode->hi_inode = NULL; hinode->hi_inode = NULL;
hinode->hi_id = -1; hinode->hi_id = -1;
hinode->hi_whdentry = NULL;
} }
int au_iinfo_init(struct inode *inode) int au_iinfo_init(struct inode *inode)
......
...@@ -20,6 +20,9 @@ struct vfsmount; ...@@ -20,6 +20,9 @@ struct vfsmount;
struct au_hinode { struct au_hinode {
struct inode *hi_inode; struct inode *hi_inode;
aufs_bindex_t hi_id; aufs_bindex_t hi_id;
/* reference to the copied-up whiteout with get/put */
struct dentry *hi_whdentry;
}; };
struct au_iigen { struct au_iigen {
...@@ -106,6 +109,8 @@ void au_unpin(struct au_pin *pin); ...@@ -106,6 +109,8 @@ void au_unpin(struct au_pin *pin);
/* 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);
void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex,
struct dentry *h_wh);
unsigned int au_hi_flags(struct inode *inode, int isdir); unsigned int au_hi_flags(struct inode *inode, int isdir);
/* hinode flags */ /* hinode flags */
...@@ -308,6 +313,12 @@ static inline aufs_bindex_t au_ibbot(struct inode *inode) ...@@ -308,6 +313,12 @@ static inline aufs_bindex_t au_ibbot(struct inode *inode)
return au_ii(inode)->ii_bbot; return au_ii(inode)->ii_bbot;
} }
static inline struct dentry *au_hi_wh(struct inode *inode, aufs_bindex_t bindex)
{
IiMustAnyLock(inode);
return au_hinode(au_ii(inode), bindex)->hi_whdentry;
}
static inline void au_set_ibtop(struct inode *inode, aufs_bindex_t bindex) static inline void au_set_ibtop(struct inode *inode, aufs_bindex_t bindex)
{ {
IiMustWriteLock(inode); IiMustWriteLock(inode);
......
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