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

aufs: copy-up 4/7, body



The functions for
- create the copy-up target file
- copy filedata
- copy metadata

In copying filedata, I had tried splice_direct() instead of repeating
read/write. Surprisingly, I could not see a big difference. So let's
keep this approach for a while. Someday SEEK_DATA/SEEK_HOLE become more
popular, it may help optimizing this read/write.
Signed-off-by: default avatarJ. R. Okajima <hooanon05g@gmail.com>
parent e989fe7b
......@@ -221,6 +221,12 @@ aufs_bindex_t au_sbr_id(struct super_block *sb, aufs_bindex_t bindex)
return au_sbr(sb, bindex)->br_id;
}
static inline
struct vfsmount *au_sbr_mnt(struct super_block *sb, aufs_bindex_t bindex)
{
return au_br_mnt(au_sbr(sb, bindex));
}
static inline
struct super_block *au_sbr_sb(struct super_block *sb, aufs_bindex_t bindex)
{
......
......@@ -8,6 +8,9 @@
*/
#include <linux/fs_stack.h>
#include <linux/mm.h>
#include <linux/task_work.h>
#include <linux/uaccess.h>
#include "aufs.h"
void au_cpup_attr_flags(struct inode *dst, unsigned int iflags)
......@@ -138,3 +141,789 @@ void au_dtime_revert(struct au_dtime *dt)
if (unlikely(err))
pr_warn("restoring timestamps failed(%d). ignored\n", err);
}
/* ---------------------------------------------------------------------- */
/* internal use only */
struct au_cpup_reg_attr {
int valid;
struct kstat st;
unsigned int iflags; /* inode->i_flags */
};
static noinline_for_stack
int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src,
struct au_cpup_reg_attr *h_src_attr)
{
int err, sbits;
struct iattr ia;
struct path h_path;
struct inode *h_isrc, *h_idst;
struct kstat *h_st;
h_path.dentry = au_h_dptr(dst, bindex);
h_idst = d_inode(h_path.dentry);
h_path.mnt = au_sbr_mnt(dst->d_sb, bindex);
h_isrc = d_inode(h_src);
ia.ia_valid = ATTR_FORCE | ATTR_UID | ATTR_GID
| ATTR_ATIME | ATTR_MTIME
| ATTR_ATIME_SET | ATTR_MTIME_SET;
if (h_src_attr && h_src_attr->valid) {
h_st = &h_src_attr->st;
ia.ia_uid = h_st->uid;
ia.ia_gid = h_st->gid;
ia.ia_atime = h_st->atime;
ia.ia_mtime = h_st->mtime;
if (h_idst->i_mode != h_st->mode
&& !S_ISLNK(h_idst->i_mode)) {
ia.ia_valid |= ATTR_MODE;
ia.ia_mode = h_st->mode;
}
sbits = !!(h_st->mode & (S_ISUID | S_ISGID));
au_cpup_attr_flags(h_idst, h_src_attr->iflags);
} else {
ia.ia_uid = h_isrc->i_uid;
ia.ia_gid = h_isrc->i_gid;
ia.ia_atime = h_isrc->i_atime;
ia.ia_mtime = h_isrc->i_mtime;
if (h_idst->i_mode != h_isrc->i_mode
&& !S_ISLNK(h_idst->i_mode)) {
ia.ia_valid |= ATTR_MODE;
ia.ia_mode = h_isrc->i_mode;
}
sbits = !!(h_isrc->i_mode & (S_ISUID | S_ISGID));
au_cpup_attr_flags(h_idst, h_isrc->i_flags);
}
/* no delegation since it is just created */
err = vfsub_notify_change(&h_path, &ia, /*delegated*/NULL);
/* is this nfs only? */
if (!err && sbits && au_test_nfs(h_path.dentry->d_sb)) {
ia.ia_valid = ATTR_FORCE | ATTR_MODE;
ia.ia_mode = h_isrc->i_mode;
err = vfsub_notify_change(&h_path, &ia, /*delegated*/NULL);
}
return err;
}
/* ---------------------------------------------------------------------- */
static int au_do_copy_file(struct file *dst, struct file *src, loff_t len,
char *buf, unsigned long blksize)
{
int err;
size_t sz, rbytes, wbytes;
unsigned char all_zero;
char *p, *zp;
struct inode *h_inode;
/* reduce stack usage */
struct iattr *ia;
zp = page_address(ZERO_PAGE(0));
if (unlikely(!zp))
return -ENOMEM; /* possible? */
err = 0;
all_zero = 0;
while (len) {
AuDbg("len %lld\n", len);
sz = blksize;
if (len < blksize)
sz = len;
rbytes = 0;
/* todo: signal_pending? */
while (!rbytes || err == -EAGAIN || err == -EINTR) {
rbytes = vfsub_read_k(src, buf, sz, &src->f_pos);
err = rbytes;
}
if (unlikely(err < 0))
break;
all_zero = 0;
if (len >= rbytes && rbytes == blksize)
all_zero = !memcmp(buf, zp, rbytes);
if (!all_zero) {
wbytes = rbytes;
p = buf;
while (wbytes) {
size_t b;
b = vfsub_write_k(dst, p, wbytes, &dst->f_pos);
err = b;
/* todo: signal_pending? */
if (unlikely(err == -EAGAIN || err == -EINTR))
continue;
if (unlikely(err < 0))
break;
wbytes -= b;
p += b;
}
if (unlikely(err < 0))
break;
} else {
loff_t res;
AuLabel(hole);
res = vfsub_llseek(dst, rbytes, SEEK_CUR);
err = res;
if (unlikely(res < 0))
break;
}
len -= rbytes;
err = 0;
}
/* the last block may be a hole */
if (!err && all_zero) {
AuLabel(last hole);
err = 1;
if (au_test_nfs(dst->f_path.dentry->d_sb)) {
/* nfs requires this step to make last hole */
/* is this only nfs? */
do {
/* todo: signal_pending? */
err = vfsub_write_k(dst, "\0", 1, &dst->f_pos);
} while (err == -EAGAIN || err == -EINTR);
if (err == 1)
dst->f_pos--;
}
if (err == 1) {
ia = (void *)buf;
ia->ia_size = dst->f_pos;
ia->ia_valid = ATTR_SIZE | ATTR_FILE;
ia->ia_file = dst;
h_inode = file_inode(dst);
inode_lock_nested(h_inode, AuLsc_I_CHILD2);
/* no delegation since it is just created */
err = vfsub_notify_change(&dst->f_path, ia,
/*delegated*/NULL);
inode_unlock(h_inode);
}
}
return err;
}
int au_copy_file(struct file *dst, struct file *src, loff_t len)
{
int err;
unsigned long blksize;
unsigned char do_kfree;
char *buf;
struct super_block *h_sb;
err = -ENOMEM;
h_sb = file_inode(dst)->i_sb;
blksize = h_sb->s_blocksize;
if (!blksize || PAGE_SIZE < blksize)
blksize = PAGE_SIZE;
AuDbg("blksize %lu\n", blksize);
do_kfree = (blksize != PAGE_SIZE && blksize >= sizeof(struct iattr *));
if (do_kfree)
buf = kmalloc(blksize, GFP_NOFS);
else
buf = (void *)__get_free_page(GFP_NOFS);
if (unlikely(!buf))
goto out;
if (len > (1 << 22))
AuDbg("copying a large file %lld\n", (long long)len);
src->f_pos = 0;
dst->f_pos = 0;
err = au_do_copy_file(dst, src, len, buf, blksize);
if (do_kfree) {
AuDebugOn(!au_kfree_do_sz_test(blksize));
au_kfree_do_rcu(buf);
} else
free_page((unsigned long)buf);
out:
return err;
}
static int au_do_copy(struct file *dst, struct file *src, loff_t len)
{
int err;
struct super_block *h_src_sb;
struct inode *h_src_inode;
h_src_inode = file_inode(src);
h_src_sb = h_src_inode->i_sb;
/* XFS acquires inode_lock */
if (!au_test_xfs(h_src_sb))
err = au_copy_file(dst, src, len);
else {
inode_unlock_shared(h_src_inode);
err = au_copy_file(dst, src, len);
inode_lock_shared_nested(h_src_inode, AuLsc_I_CHILD);
}
return err;
}
static int au_clone_or_copy(struct file *dst, struct file *src, loff_t len)
{
int err;
loff_t lo;
struct super_block *h_src_sb;
struct inode *h_src_inode;
h_src_inode = file_inode(src);
h_src_sb = h_src_inode->i_sb;
if (h_src_sb != file_inode(dst)->i_sb
|| !dst->f_op->remap_file_range) {
err = au_do_copy(dst, src, len);
goto out;
}
if (!au_test_nfs(h_src_sb)) {
inode_unlock_shared(h_src_inode);
lo = vfsub_clone_file_range(src, dst, len);
inode_lock_shared_nested(h_src_inode, AuLsc_I_CHILD);
} else
lo = vfsub_clone_file_range(src, dst, len);
if (lo == len) {
err = 0;
goto out; /* success */
} else if (lo >= 0)
/* todo: possible? */
/* paritially succeeded */
AuDbg("lo %lld, len %lld. Retrying.\n", lo, len);
else if (lo != -EOPNOTSUPP) {
/* older XFS has a condition in cloning */
err = lo;
goto out;
}
/* the backend fs on NFS may not support cloning */
err = au_do_copy(dst, src, len);
out:
AuTraceErr(err);
return err;
}
/*
* to support a sparse file which is opened with O_APPEND,
* we need to close the file.
*/
static int au_cp_regular(struct au_cp_generic *cpg)
{
int err, i;
enum { SRC, DST };
struct {
aufs_bindex_t bindex;
unsigned int flags;
struct dentry *dentry;
struct file *file;
} *f, file[] = {
{
.bindex = cpg->bsrc,
.flags = O_RDONLY | O_NOATIME | O_LARGEFILE,
},
{
.bindex = cpg->bdst,
.flags = O_WRONLY | O_NOATIME | O_LARGEFILE,
}
};
struct au_branch *br;
struct super_block *sb, *h_src_sb;
struct inode *h_src_inode;
struct task_struct *tsk = current;
/* bsrc branch can be ro/rw. */
sb = cpg->dentry->d_sb;
f = file;
for (i = 0; i < 2; i++, f++) {
f->dentry = au_h_dptr(cpg->dentry, f->bindex);
f->file = au_h_open(cpg->dentry, f->bindex, f->flags,
/*file*/NULL);
if (IS_ERR(f->file)) {
err = PTR_ERR(f->file);
if (i == SRC)
goto out;
else
goto out_src;
}
}
/* try stopping to update while we copyup */
h_src_inode = d_inode(file[SRC].dentry);
h_src_sb = h_src_inode->i_sb;
if (!au_test_nfs(h_src_sb))
IMustLock(h_src_inode);
err = au_clone_or_copy(file[DST].file, file[SRC].file, cpg->len);
/* i wonder if we had O_NO_DELAY_FPUT flag */
if (tsk->flags & PF_KTHREAD)
__fput_sync(file[DST].file);
else {
/* it happened actually */
fput(file[DST].file);
/*
* too bad.
* we have to call both since we don't know which place the file
* was added to.
*/
task_work_run();
flush_delayed_fput();
}
br = au_sbr(sb, file[DST].bindex);
au_lcnt_dec(&br->br_nfiles);
out_src:
fput(file[SRC].file);
br = au_sbr(sb, file[SRC].bindex);
au_lcnt_dec(&br->br_nfiles);
out:
return err;
}
static int au_do_cpup_regular(struct au_cp_generic *cpg,
struct au_cpup_reg_attr *h_src_attr)
{
int err, rerr;
loff_t l;
struct path h_path;
struct inode *h_src_inode, *h_dst_inode;
err = 0;
h_src_inode = au_h_iptr(d_inode(cpg->dentry), cpg->bsrc);
l = i_size_read(h_src_inode);
if (cpg->len == -1 || l < cpg->len)
cpg->len = l;
if (cpg->len) {
/* try stopping to update while we are referencing */
inode_lock_shared_nested(h_src_inode, AuLsc_I_CHILD);
au_pin_hdir_unlock(cpg->pin);
h_path.dentry = au_h_dptr(cpg->dentry, cpg->bsrc);
h_path.mnt = au_sbr_mnt(cpg->dentry->d_sb, cpg->bsrc);
h_src_attr->iflags = h_src_inode->i_flags;
if (!au_test_nfs(h_src_inode->i_sb))
err = vfsub_getattr(&h_path, &h_src_attr->st);
else {
inode_unlock_shared(h_src_inode);
err = vfsub_getattr(&h_path, &h_src_attr->st);
inode_lock_shared_nested(h_src_inode, AuLsc_I_CHILD);
}
if (unlikely(err)) {
inode_unlock_shared(h_src_inode);
goto out;
}
h_src_attr->valid = 1;
if (!au_test_nfs(h_src_inode->i_sb)) {
err = au_cp_regular(cpg);
inode_unlock_shared(h_src_inode);
} else {
inode_unlock_shared(h_src_inode);
err = au_cp_regular(cpg);
}
rerr = au_pin_hdir_relock(cpg->pin);
if (!err && rerr)
err = rerr;
}
if (!err && (h_src_inode->i_state & I_LINKABLE)) {
h_path.dentry = au_h_dptr(cpg->dentry, cpg->bdst);
h_dst_inode = d_inode(h_path.dentry);
spin_lock(&h_dst_inode->i_lock);
h_dst_inode->i_state |= I_LINKABLE;
spin_unlock(&h_dst_inode->i_lock);
}
out:
return err;
}
static int au_do_cpup_symlink(struct path *h_path, struct dentry *h_src,
struct inode *h_dir)
{
int err, symlen;
mm_segment_t old_fs;
union {
char *k;
char __user *u;
} sym;
err = -ENOMEM;
sym.k = (void *)__get_free_page(GFP_NOFS);
if (unlikely(!sym.k))
goto out;
/* unnecessary to support mmap_sem since symlink is not mmap-able */
old_fs = get_fs();
set_fs(KERNEL_DS);
symlen = vfs_readlink(h_src, sym.u, PATH_MAX);
err = symlen;
set_fs(old_fs);
if (symlen > 0) {
sym.k[symlen] = 0;
err = vfsub_symlink(h_dir, h_path, sym.k);
}
free_page((unsigned long)sym.k);
out:
return err;
}
static void au_do_cpup_dir(struct au_cp_generic *cpg, struct dentry *dst_parent)
{
struct inode *dir, *inode;
/*
* strange behaviour from the users view,
* particularly setattr case
*/
dir = d_inode(dst_parent);
if (au_ibtop(dir) == cpg->bdst)
au_cpup_attr_nlink(dir, /*force*/1);
inode = d_inode(cpg->dentry);
au_cpup_attr_nlink(inode, /*force*/1);
}
static noinline_for_stack
int cpup_entry(struct au_cp_generic *cpg, struct dentry *dst_parent,
struct au_cpup_reg_attr *h_src_attr)
{
int err;
umode_t mode;
unsigned int mnt_flags;
unsigned char isdir, isreg, force;
const unsigned char do_dt = !!au_ftest_cpup(cpg->flags, DTIME);
struct au_dtime dt;
struct path h_path;
struct dentry *h_src, *h_dst, *h_parent;
struct inode *h_inode, *h_dir;
struct super_block *sb;
/* bsrc branch can be ro/rw. */
h_src = au_h_dptr(cpg->dentry, cpg->bsrc);
h_inode = d_inode(h_src);
AuDebugOn(h_inode != au_h_iptr(d_inode(cpg->dentry), cpg->bsrc));
/* try stopping to be referenced while we are creating */
h_dst = au_h_dptr(cpg->dentry, cpg->bdst);
if (au_ftest_cpup(cpg->flags, RENAME))
AuDebugOn(strncmp(h_dst->d_name.name, AUFS_WH_PFX,
AUFS_WH_PFX_LEN));
h_parent = h_dst->d_parent; /* dir inode is locked */
h_dir = d_inode(h_parent);
IMustLock(h_dir);
AuDebugOn(h_parent != h_dst->d_parent);
sb = cpg->dentry->d_sb;
h_path.mnt = au_sbr_mnt(sb, cpg->bdst);
if (do_dt) {
h_path.dentry = h_parent;
au_dtime_store(&dt, dst_parent, &h_path);
}
h_path.dentry = h_dst;
isreg = 0;
isdir = 0;
mode = h_inode->i_mode;
switch (mode & S_IFMT) {
case S_IFREG:
isreg = 1;
err = vfsub_create(h_dir, &h_path, 0600, /*want_excl*/true);
if (!err)
err = au_do_cpup_regular(cpg, h_src_attr);
break;
case S_IFDIR:
isdir = 1;
err = vfsub_mkdir(h_dir, &h_path, mode);
if (!err)
au_do_cpup_dir(cpg, dst_parent);
break;
case S_IFLNK:
err = au_do_cpup_symlink(&h_path, h_src, h_dir);
break;
case S_IFCHR:
case S_IFBLK:
AuDebugOn(!capable(CAP_MKNOD));
/*FALLTHROUGH*/
case S_IFIFO:
case S_IFSOCK:
err = vfsub_mknod(h_dir, &h_path, mode, h_inode->i_rdev);
break;
default:
AuIOErr("Unknown inode type 0%o\n", mode);
err = -EIO;
}
mnt_flags = au_mntflags(sb);
if (!isdir
&& au_opt_test(mnt_flags, XINO)
&& (h_inode->i_nlink == 1
|| (h_inode->i_state & I_LINKABLE))
/* todo: unnecessary? */
/* && d_inode(cpg->dentry)->i_nlink == 1 */
&& cpg->bdst < cpg->bsrc
&& !au_ftest_cpup(cpg->flags, KEEPLINO))
au_xino_write(sb, cpg->bsrc, h_inode->i_ino, /*ino*/0);
/* ignore this error */
if (!err) {
force = 0;
if (isreg) {
force = !!cpg->len;
if (cpg->len == -1)
force = !!i_size_read(h_inode);
}
}
if (do_dt)
au_dtime_revert(&dt);
return err;
}
static int au_do_ren_after_cpup(struct au_cp_generic *cpg, struct path *h_path)
{
int err;
struct dentry *dentry, *h_dentry, *h_parent;
struct inode *h_dir;
aufs_bindex_t bdst;
dentry = cpg->dentry;
bdst = cpg->bdst;
h_dentry = au_h_dptr(dentry, bdst);
dget(h_dentry);
au_set_h_dptr(dentry, bdst, NULL);
err = au_lkup_neg(dentry, bdst, /*wh*/0);
if (!err)
h_path->dentry = dget(au_h_dptr(dentry, bdst));
au_set_h_dptr(dentry, bdst, h_dentry);
if (unlikely(err))
goto out;
h_parent = h_dentry->d_parent; /* dir inode is locked */
h_dir = d_inode(h_parent);
IMustLock(h_dir);
AuDbg("%pd %pd\n", h_dentry, h_path->dentry);
/* no delegation since it is just created */
err = vfsub_rename(h_dir, h_dentry, h_dir, h_path, /*delegated*/NULL,
/*flags*/0);
dput(h_path->dentry);
out:
return err;
}
/*
* copyup the @dentry from @bsrc to @bdst.
* the caller must set the both of lower dentries.
* @len is for truncating when it is -1 copyup the entire file.
* in link/rename cases, @dst_parent may be different from the real one.
* basic->bsrc can be larger than basic->bdst.
* aufs doesn't touch the credential so
* security_inode_copy_up{,_xattr}() are unnecessary.
*/
/* re-commit later */ __maybe_unused
static int au_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent)
{
int err, rerr;
aufs_bindex_t old_ibtop;
unsigned char isdir, plink;
struct dentry *h_src, *h_dst, *h_parent;
struct inode *dst_inode, *h_dir, *inode, *delegated, *src_inode;
struct super_block *sb;
struct au_branch *br;
/* to reduce stack size */
struct {
struct au_dtime dt;
struct path h_path;
struct au_cpup_reg_attr h_src_attr;
} *a;
err = -ENOMEM;
a = kmalloc(sizeof(*a), GFP_NOFS);
if (unlikely(!a))
goto out;
a->h_src_attr.valid = 0;
sb = cpg->dentry->d_sb;
br = au_sbr(sb, cpg->bdst);
a->h_path.mnt = au_br_mnt(br);
h_dst = au_h_dptr(cpg->dentry, cpg->bdst);
h_parent = h_dst->d_parent; /* dir inode is locked */
h_dir = d_inode(h_parent);
IMustLock(h_dir);
h_src = au_h_dptr(cpg->dentry, cpg->bsrc);
inode = d_inode(cpg->dentry);
if (!dst_parent)
dst_parent = dget_parent(cpg->dentry);
else
dget(dst_parent);
plink = !!au_opt_test(au_mntflags(sb), PLINK);
dst_inode = au_h_iptr(inode, cpg->bdst);
if (dst_inode) {
if (unlikely(!plink)) {
err = -EIO;
AuIOErr("hi%lu(i%lu) exists on b%d "
"but plink is disabled\n",
dst_inode->i_ino, inode->i_ino, cpg->bdst);
goto out_parent;
}
if (dst_inode->i_nlink) {
const int do_dt = au_ftest_cpup(cpg->flags, DTIME);
h_src = au_plink_lkup(inode, cpg->bdst);
err = PTR_ERR(h_src);
if (IS_ERR(h_src))
goto out_parent;
if (unlikely(d_is_negative(h_src))) {
err = -EIO;
AuIOErr("i%lu exists on b%d "
"but not pseudo-linked\n",
inode->i_ino, cpg->bdst);
dput(h_src);
goto out_parent;
}
if (do_dt) {
a->h_path.dentry = h_parent;
au_dtime_store(&a->dt, dst_parent, &a->h_path);
}
a->h_path.dentry = h_dst;
delegated = NULL;
err = vfsub_link(h_src, h_dir, &a->h_path, &delegated);
if (!err && au_ftest_cpup(cpg->flags, RENAME))
err = au_do_ren_after_cpup(cpg, &a->h_path);
if (do_dt)
au_dtime_revert(&a->dt);
if (unlikely(err == -EWOULDBLOCK)) {
pr_warn("cannot retry for NFSv4 delegation"
" for an internal link\n");
iput(delegated);
}
dput(h_src);
goto out_parent;
} else
/* todo: cpup_wh_file? */
/* udba work */
au_update_ibrange(inode, /*do_put_zero*/1);
}
isdir = S_ISDIR(inode->i_mode);
old_ibtop = au_ibtop(inode);
err = cpup_entry(cpg, dst_parent, &a->h_src_attr);
if (unlikely(err))
goto out_rev;
dst_inode = d_inode(h_dst);
inode_lock_nested(dst_inode, AuLsc_I_CHILD2);
/* todo: necessary? */
/* au_pin_hdir_unlock(cpg->pin); */
err = cpup_iattr(cpg->dentry, cpg->bdst, h_src, &a->h_src_attr);
if (unlikely(err)) {
/* todo: necessary? */
/* au_pin_hdir_relock(cpg->pin); */ /* ignore an error */
inode_unlock(dst_inode);
goto out_rev;
}
if (cpg->bdst < old_ibtop) {
au_set_ibtop(inode, cpg->bdst);
} else
au_set_ibbot(inode, cpg->bdst);
au_set_h_iptr(inode, cpg->bdst, au_igrab(dst_inode),
au_hi_flags(inode, isdir));
/* todo: necessary? */
/* err = au_pin_hdir_relock(cpg->pin); */
inode_unlock(dst_inode);
if (unlikely(err))
goto out_rev;
src_inode = d_inode(h_src);
if (!isdir
&& (src_inode->i_nlink > 1
|| src_inode->i_state & I_LINKABLE)
&& plink)
au_plink_append(inode, cpg->bdst, h_dst);
if (au_ftest_cpup(cpg->flags, RENAME)) {
a->h_path.dentry = h_dst;
err = au_do_ren_after_cpup(cpg, &a->h_path);
}
if (!err)
goto out_parent; /* success */
/* revert */
out_rev:
a->h_path.dentry = h_parent;
au_dtime_store(&a->dt, dst_parent, &a->h_path);
a->h_path.dentry = h_dst;
rerr = 0;
if (d_is_positive(h_dst)) {
if (!isdir) {
/* no delegation since it is just created */
rerr = vfsub_unlink(h_dir, &a->h_path,
/*delegated*/NULL, /*force*/0);
} else
rerr = vfsub_rmdir(h_dir, &a->h_path);
}
au_dtime_revert(&a->dt);
if (rerr) {
AuIOErr("failed removing broken entry(%d, %d)\n", err, rerr);
err = -EIO;
}
out_parent:
dput(dst_parent);
au_kfree_rcu(a);
out:
return err;
}
#if 0 /* reserved */
struct au_cpup_single_args {
int *errp;
struct au_cp_generic *cpg;
struct dentry *dst_parent;
};
static void au_call_cpup_single(void *args)
{
struct au_cpup_single_args *a = args;
au_pin_hdir_acquire_nest(a->cpg->pin);
*a->errp = au_cpup_single(a->cpg, a->dst_parent);
au_pin_hdir_release(a->cpg->pin);
}
#endif
#if 0 /* reserved */
int au_sio_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent)
{
int err, wkq_err;
struct dentry *h_dentry;
h_dentry = au_h_dptr(cpg->dentry, cpg->bsrc);
if (!au_cpup_sio_test(pin, d_inode(h_dentry)->i_mode))
err = au_cpup_single(cpg, dst_parent);
else {
struct au_cpup_single_args args = {
.errp = &err,
.cpg = cpg,
.dst_parent = dst_parent
};
wkq_err = au_wkq_wait(au_call_cpup_single, &args);
if (unlikely(wkq_err))
err = wkq_err;
}
return err;
}
#endif
......@@ -15,6 +15,7 @@
#include <linux/path.h>
struct inode;
struct au_pin;
void au_cpup_attr_flags(struct inode *dst, unsigned int iflags);
void au_cpup_attr_timesizes(struct inode *inode);
......@@ -25,6 +26,30 @@ void au_cpup_attr_all(struct inode *inode, int force);
/* ---------------------------------------------------------------------- */
struct au_cp_generic {
struct dentry *dentry;
aufs_bindex_t bdst, bsrc;
loff_t len;
struct au_pin *pin;
unsigned int flags;
};
/* cpup flags */
#define AuCpup_DTIME 1 /* do dtime_store/revert */
#define AuCpup_KEEPLINO (1 << 1) /* do not clear the lower xino,
for link(2) */
#define AuCpup_RENAME (1 << 2) /* rename after cpup */
#define au_ftest_cpup(flags, name) ((flags) & AuCpup_##name)
#define au_fset_cpup(flags, name) \
do { (flags) |= AuCpup_##name; } while (0)
#define au_fclr_cpup(flags, name) \
do { (flags) &= ~AuCpup_##name; } while (0)
int au_copy_file(struct file *dst, struct file *src, loff_t len);
/* ---------------------------------------------------------------------- */
/* keep timestamps when copyup */
struct au_dtime {
struct dentry *dt_dentry;
......
......@@ -90,6 +90,46 @@ void au_update_iigen(struct inode *inode, int half)
spin_unlock(&iigen->ig_spin);
}
/* it may be called at remount time, too */
void au_update_ibrange(struct inode *inode, int do_put_zero)
{
struct au_iinfo *iinfo;
aufs_bindex_t bindex, bbot;
AuDebugOn(au_is_bad_inode(inode));
IiMustWriteLock(inode);
iinfo = au_ii(inode);
if (do_put_zero && iinfo->ii_btop >= 0) {
for (bindex = iinfo->ii_btop; bindex <= iinfo->ii_bbot;
bindex++) {
struct inode *h_i;
h_i = au_hinode(iinfo, bindex)->hi_inode;
if (h_i
&& !h_i->i_nlink
&& !(h_i->i_state & I_LINKABLE))
au_set_h_iptr(inode, bindex, NULL, 0);
}
}
iinfo->ii_btop = -1;
iinfo->ii_bbot = -1;
bbot = au_sbbot(inode->i_sb);
for (bindex = 0; bindex <= bbot; bindex++)
if (au_hinode(iinfo, bindex)->hi_inode) {
iinfo->ii_btop = bindex;
break;
}
if (iinfo->ii_btop >= 0)
for (bindex = bbot; bindex >= iinfo->ii_btop; bindex--)
if (au_hinode(iinfo, bindex)->hi_inode) {
iinfo->ii_bbot = bindex;
break;
}
AuDebugOn(iinfo->ii_btop > iinfo->ii_bbot);
}
/* ---------------------------------------------------------------------- */
void au_icntnr_init_once(void *_c)
......
......@@ -120,6 +120,7 @@ void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex,
struct inode *h_inode, unsigned int flags);
void au_update_iigen(struct inode *inode, int half);
void au_update_ibrange(struct inode *inode, int do_put_zero);
void au_icntnr_init_once(void *_c);
void au_hinode_init(struct au_hinode *hinode);
......
......@@ -97,6 +97,50 @@ out:
return err;
}
int vfsub_symlink(struct inode *dir, struct path *path, const char *symname)
{
int err;
struct dentry *d;
IMustLock(dir);
d = path->dentry;
path->dentry = d->d_parent;
err = security_path_symlink(path, d, symname);
path->dentry = d;
if (unlikely(err))
goto out;
lockdep_off();
err = vfs_symlink(dir, path->dentry, symname);
lockdep_on();
out:
return err;
}
int vfsub_mknod(struct inode *dir, struct path *path, int mode, dev_t dev)
{
int err;
struct dentry *d;
IMustLock(dir);
d = path->dentry;
path->dentry = d->d_parent;
err = security_path_mknod(path, d, mode, new_encode_dev(dev));
path->dentry = d;
if (unlikely(err))
goto out;
lockdep_off();
err = vfs_mknod(dir, path->dentry, mode, dev);
lockdep_on();
out:
return err;
}
static int au_test_nlink(struct inode *inode)
{
const unsigned int link_max = UINT_MAX >> 1; /* rough margin */
......@@ -135,6 +179,36 @@ out:
return err;
}
int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry,
struct inode *dir, struct path *path,
struct inode **delegated_inode, unsigned int flags)
{
int err;
struct path tmp = {
.mnt = path->mnt
};
struct dentry *d;
IMustLock(dir);
IMustLock(src_dir);
d = path->dentry;
path->dentry = d->d_parent;
tmp.dentry = src_dentry->d_parent;
err = security_path_rename(&tmp, src_dentry, path, d, /*flags*/0);
path->dentry = d;
if (unlikely(err))
goto out;
lockdep_off();
err = vfs_rename(src_dir, src_dentry, dir, path->dentry,
delegated_inode, flags);
lockdep_on();
out:
return err;
}
int vfsub_mkdir(struct inode *dir, struct path *path, int mode)
{
int err;
......
......@@ -81,8 +81,14 @@ static inline void vfsub_mnt_drop_write(struct vfsmount *mnt)
int vfsub_create(struct inode *dir, struct path *path, int mode,
bool want_excl);
int vfsub_symlink(struct inode *dir, struct path *path,
const char *symname);
int vfsub_mknod(struct inode *dir, struct path *path, int mode, dev_t dev);
int vfsub_link(struct dentry *src_dentry, struct inode *dir,
struct path *path, struct inode **delegated_inode);
int vfsub_rename(struct inode *src_hdir, struct dentry *src_dentry,
struct inode *hdir, struct path *path,
struct inode **delegated_inode, unsigned int flags);
int vfsub_mkdir(struct inode *dir, struct path *path, int mode);
int vfsub_rmdir(struct inode *dir, struct path *path);
......@@ -102,6 +108,34 @@ static inline loff_t vfsub_f_size_read(struct file *file)
return i_size_read(file_inode(file));
}
/*
* re-use branch fs's ioctl(FICLONE) while aufs itself doesn't support such
* ioctl.
*/
static inline loff_t vfsub_clone_file_range(struct file *src, struct file *dst,
loff_t len)
{
loff_t err;
lockdep_off();
err = vfs_clone_file_range(src, 0, dst, 0, len, /*remap_flags*/0);
lockdep_on();
return err;
}
/* ---------------------------------------------------------------------- */
static inline loff_t vfsub_llseek(struct file *file, loff_t offset, int origin)
{
loff_t err;
lockdep_off();
err = vfs_llseek(file, offset, origin);
lockdep_on();
return err;
}
/* ---------------------------------------------------------------------- */
int vfsub_sio_notify_change(struct path *path, struct iattr *ia,
......@@ -111,5 +145,10 @@ int vfsub_notify_change(struct path *path, struct iattr *ia,
int vfsub_unlink(struct inode *dir, struct path *path,
struct inode **delegated_inode, int force);
static inline int vfsub_getattr(const struct path *path, struct kstat *st)
{
return vfs_getattr(path, st, STATX_BASIC_STATS, AT_STATX_SYNC_AS_STAT);
}
#endif /* __KERNEL__ */
#endif /* __AUFS_VFSUB_H__ */
......@@ -271,7 +271,7 @@ struct file *au_xino_create2(struct super_block *sb, struct path *base,
if (copy_src) {
/* no one can touch copy_src xino */
//err = au_copy_file(file, copy_src, vfsub_f_size_read(copy_src));
err = au_copy_file(file, copy_src, vfsub_f_size_read(copy_src));
if (unlikely(err)) {
pr_err("%pd copy err %d\n", dentry, err);
goto out_fput;
......
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