summaryrefslogtreecommitdiff
path: root/fs/aufs/cpup.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/aufs/cpup.c')
-rw-r--r--fs/aufs/cpup.c1319
1 files changed, 0 insertions, 1319 deletions
diff --git a/fs/aufs/cpup.c b/fs/aufs/cpup.c
deleted file mode 100644
index 3ea177a5d..000000000
--- a/fs/aufs/cpup.c
+++ /dev/null
@@ -1,1319 +0,0 @@
-/*
- * Copyright (C) 2005-2015 Junjiro R. Okajima
- *
- * This program, aufs is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-/*
- * copy-up functions, see wbr_policy.c for copy-down
- */
-
-#include <linux/fs_stack.h>
-#include <linux/mm.h>
-#include "aufs.h"
-
-void au_cpup_attr_flags(struct inode *dst, unsigned int iflags)
-{
- const unsigned int mask = S_DEAD | S_SWAPFILE | S_PRIVATE
- | S_NOATIME | S_NOCMTIME | S_AUTOMOUNT;
-
- BUILD_BUG_ON(sizeof(iflags) != sizeof(dst->i_flags));
-
- dst->i_flags |= iflags & ~mask;
- if (au_test_fs_notime(dst->i_sb))
- dst->i_flags |= S_NOATIME | S_NOCMTIME;
-}
-
-void au_cpup_attr_timesizes(struct inode *inode)
-{
- struct inode *h_inode;
-
- h_inode = au_h_iptr(inode, au_ibstart(inode));
- fsstack_copy_attr_times(inode, h_inode);
- fsstack_copy_inode_size(inode, h_inode);
-}
-
-void au_cpup_attr_nlink(struct inode *inode, int force)
-{
- struct inode *h_inode;
- struct super_block *sb;
- aufs_bindex_t bindex, bend;
-
- sb = inode->i_sb;
- bindex = au_ibstart(inode);
- h_inode = au_h_iptr(inode, bindex);
- if (!force
- && !S_ISDIR(h_inode->i_mode)
- && au_opt_test(au_mntflags(sb), PLINK)
- && au_plink_test(inode))
- return;
-
- /*
- * 0 can happen in revalidating.
- * h_inode->i_mutex may not be held here, but it is harmless since once
- * i_nlink reaches 0, it will never become positive except O_TMPFILE
- * case.
- * todo: O_TMPFILE+linkat(AT_SYMLINK_FOLLOW) bypassing aufs may cause
- * the incorrect link count.
- */
- set_nlink(inode, h_inode->i_nlink);
-
- /*
- * fewer nlink makes find(1) noisy, but larger nlink doesn't.
- * it may includes whplink directory.
- */
- if (S_ISDIR(h_inode->i_mode)) {
- bend = au_ibend(inode);
- for (bindex++; bindex <= bend; bindex++) {
- h_inode = au_h_iptr(inode, bindex);
- if (h_inode)
- au_add_nlink(inode, h_inode);
- }
- }
-}
-
-void au_cpup_attr_changeable(struct inode *inode)
-{
- struct inode *h_inode;
-
- h_inode = au_h_iptr(inode, au_ibstart(inode));
- inode->i_mode = h_inode->i_mode;
- inode->i_uid = h_inode->i_uid;
- inode->i_gid = h_inode->i_gid;
- au_cpup_attr_timesizes(inode);
- au_cpup_attr_flags(inode, h_inode->i_flags);
-}
-
-void au_cpup_igen(struct inode *inode, struct inode *h_inode)
-{
- struct au_iinfo *iinfo = au_ii(inode);
-
- IiMustWriteLock(inode);
-
- iinfo->ii_higen = h_inode->i_generation;
- iinfo->ii_hsb1 = h_inode->i_sb;
-}
-
-void au_cpup_attr_all(struct inode *inode, int force)
-{
- struct inode *h_inode;
-
- h_inode = au_h_iptr(inode, au_ibstart(inode));
- au_cpup_attr_changeable(inode);
- if (inode->i_nlink > 0)
- au_cpup_attr_nlink(inode, force);
- inode->i_rdev = h_inode->i_rdev;
- inode->i_blkbits = h_inode->i_blkbits;
- au_cpup_igen(inode, h_inode);
-}
-
-/* ---------------------------------------------------------------------- */
-
-/* Note: dt_dentry and dt_h_dentry are not dget/dput-ed */
-
-/* keep the timestamps of the parent dir when cpup */
-void au_dtime_store(struct au_dtime *dt, struct dentry *dentry,
- struct path *h_path)
-{
- struct inode *h_inode;
-
- dt->dt_dentry = dentry;
- dt->dt_h_path = *h_path;
- h_inode = d_inode(h_path->dentry);
- dt->dt_atime = h_inode->i_atime;
- dt->dt_mtime = h_inode->i_mtime;
- /* smp_mb(); */
-}
-
-void au_dtime_revert(struct au_dtime *dt)
-{
- struct iattr attr;
- int err;
-
- attr.ia_atime = dt->dt_atime;
- attr.ia_mtime = dt->dt_mtime;
- attr.ia_valid = ATTR_FORCE | ATTR_MTIME | ATTR_MTIME_SET
- | ATTR_ATIME | ATTR_ATIME_SET;
-
- /* no delegation since this is a directory */
- err = vfsub_notify_change(&dt->dt_h_path, &attr, /*delegated*/NULL);
- 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, icex;
- unsigned int mnt_flags;
- unsigned char verbose;
- struct iattr ia;
- struct path h_path;
- struct inode *h_isrc, *h_idst;
- struct kstat *h_st;
- struct au_branch *br;
-
- h_path.dentry = au_h_dptr(dst, bindex);
- h_idst = d_inode(h_path.dentry);
- br = au_sbr(dst->d_sb, bindex);
- h_path.mnt = au_br_mnt(br);
- 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);
- }
-
- icex = br->br_perm & AuBrAttr_ICEX;
- if (!err) {
- mnt_flags = au_mntflags(dst->d_sb);
- verbose = !!au_opt_test(mnt_flags, VERBOSE);
- err = au_cpup_xattr(h_path.dentry, h_src, icex, verbose);
- }
-
- 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 mutex *h_mtx;
- /* 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_mtx = &file_inode(dst)->i_mutex;
- mutex_lock_nested(h_mtx, AuLsc_I_CHILD2);
- /* no delegation since it is just created */
- err = vfsub_notify_change(&dst->f_path, ia,
- /*delegated*/NULL);
- mutex_unlock(h_mtx);
- }
- }
-
- 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;
-
- err = -ENOMEM;
- blksize = dst->f_path.dentry->d_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)
- kfree(buf);
- else
- free_page((unsigned long)buf);
-
-out:
- 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;
- int force_wr;
- struct file *file;
- void *label;
- } *f, file[] = {
- {
- .bindex = cpg->bsrc,
- .flags = O_RDONLY | O_NOATIME | O_LARGEFILE,
- .label = &&out
- },
- {
- .bindex = cpg->bdst,
- .flags = O_WRONLY | O_NOATIME | O_LARGEFILE,
- .force_wr = !!au_ftest_cpup(cpg->flags, RWDST),
- .label = &&out_src
- }
- };
- struct super_block *sb;
-
- /* 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, f->force_wr);
- err = PTR_ERR(f->file);
- if (IS_ERR(f->file))
- goto *f->label;
- }
-
- /* try stopping to update while we copyup */
- IMustLock(d_inode(file[SRC].dentry));
- err = au_copy_file(file[DST].file, file[SRC].file, cpg->len);
-
- fput(file[DST].file);
- au_sbr_put(sb, file[DST].bindex);
-
-out_src:
- fput(file[SRC].file);
- au_sbr_put(sb, file[SRC].bindex);
-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 */
- mutex_lock_nested(&h_src_inode->i_mutex, 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 = vfs_getattr(&h_path, &h_src_attr->st);
- else {
- mutex_unlock(&h_src_inode->i_mutex);
- err = vfs_getattr(&h_path, &h_src_attr->st);
- mutex_lock_nested(&h_src_inode->i_mutex, AuLsc_I_CHILD);
- }
- if (unlikely(err)) {
- mutex_unlock(&h_src_inode->i_mutex);
- goto out;
- }
- h_src_attr->valid = 1;
- err = au_cp_regular(cpg);
- mutex_unlock(&h_src_inode->i_mutex);
- 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;
- struct inode *h_inode = d_inode(h_src);
- const struct inode_operations *h_iop = h_inode->i_op;
-
- err = -ENOSYS;
- if (unlikely(!h_iop->readlink))
- goto out;
-
- 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 = h_iop->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 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, *dir, *inode;
- 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, mode | S_IWUSR,
- /*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) {
- /*
- * strange behaviour from the users view,
- * particularry setattr case
- */
- dir = d_inode(dst_parent);
- if (au_ibstart(dir) == cpg->bdst)
- au_cpup_attr_nlink(dir, /*force*/1);
- inode = d_inode(cpg->dentry);
- au_cpup_attr_nlink(inode, /*force*/1);
- }
- 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 (!au_opt_test(mnt_flags, UDBA_NONE)
- && !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);
- }
- au_fhsm_wrote(sb, cpg->bdst, force);
- }
-
- 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, *parent;
- struct inode *h_dir;
- aufs_bindex_t bdst;
-
- dentry = cpg->dentry;
- bdst = cpg->bdst;
- h_dentry = au_h_dptr(dentry, bdst);
- if (!au_ftest_cpup(cpg->flags, OVERWRITE)) {
- 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);
- } else {
- err = 0;
- parent = dget_parent(dentry);
- h_parent = au_h_dptr(parent, bdst);
- dput(parent);
- h_path->dentry = vfsub_lkup_one(&dentry->d_name, h_parent);
- if (IS_ERR(h_path->dentry))
- err = PTR_ERR(h_path->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);
- 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.
- */
-static int au_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent)
-{
- int err, rerr;
- aufs_bindex_t old_ibstart;
- 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 reuduce 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 a upper branch "
- "but not pseudo-linked\n",
- inode->i_ino);
- 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_ibstart = au_ibstart(inode);
- err = cpup_entry(cpg, dst_parent, &a->h_src_attr);
- if (unlikely(err))
- goto out_rev;
- dst_inode = d_inode(h_dst);
- mutex_lock_nested(&dst_inode->i_mutex, 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 */
- mutex_unlock(&dst_inode->i_mutex);
- goto out_rev;
- }
-
- if (cpg->bdst < old_ibstart) {
- if (S_ISREG(inode->i_mode)) {
- err = au_dy_iaop(inode, cpg->bdst, dst_inode);
- if (unlikely(err)) {
- /* ignore an error */
- /* au_pin_hdir_relock(cpg->pin); */
- mutex_unlock(&dst_inode->i_mutex);
- goto out_rev;
- }
- }
- au_set_ibstart(inode, cpg->bdst);
- } else
- au_set_ibend(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); */
- mutex_unlock(&dst_inode->i_mutex);
- 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);
- kfree(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
-
-/*
- * prevent SIGXFSZ in copy-up.
- * testing CAP_MKNOD is for generic fs,
- * but CAP_FSETID is for xfs only, currently.
- */
-static int au_cpup_sio_test(struct au_pin *pin, umode_t mode)
-{
- int do_sio;
- struct super_block *sb;
- struct inode *h_dir;
-
- do_sio = 0;
- sb = au_pinned_parent(pin)->d_sb;
- if (!au_wkq_test()
- && (!au_sbi(sb)->si_plink_maint_pid
- || au_plink_maint(sb, AuLock_NOPLM))) {
- switch (mode & S_IFMT) {
- case S_IFREG:
- /* no condition about RLIMIT_FSIZE and the file size */
- do_sio = 1;
- break;
- case S_IFCHR:
- case S_IFBLK:
- do_sio = !capable(CAP_MKNOD);
- break;
- }
- if (!do_sio)
- do_sio = ((mode & (S_ISUID | S_ISGID))
- && !capable(CAP_FSETID));
- /* this workaround may be removed in the future */
- if (!do_sio) {
- h_dir = au_pinned_h_dir(pin);
- do_sio = h_dir->i_mode & S_ISVTX;
- }
- }
-
- return do_sio;
-}
-
-#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
-
-/*
- * copyup the @dentry from the first active lower branch to @bdst,
- * using au_cpup_single().
- */
-static int au_cpup_simple(struct au_cp_generic *cpg)
-{
- int err;
- unsigned int flags_orig;
- struct dentry *dentry;
-
- AuDebugOn(cpg->bsrc < 0);
-
- dentry = cpg->dentry;
- DiMustWriteLock(dentry);
-
- err = au_lkup_neg(dentry, cpg->bdst, /*wh*/1);
- if (!err) {
- flags_orig = cpg->flags;
- au_fset_cpup(cpg->flags, RENAME);
- err = au_cpup_single(cpg, NULL);
- cpg->flags = flags_orig;
- if (!err)
- return 0; /* success */
-
- /* revert */
- au_set_h_dptr(dentry, cpg->bdst, NULL);
- au_set_dbstart(dentry, cpg->bsrc);
- }
-
- return err;
-}
-
-struct au_cpup_simple_args {
- int *errp;
- struct au_cp_generic *cpg;
-};
-
-static void au_call_cpup_simple(void *args)
-{
- struct au_cpup_simple_args *a = args;
-
- au_pin_hdir_acquire_nest(a->cpg->pin);
- *a->errp = au_cpup_simple(a->cpg);
- au_pin_hdir_release(a->cpg->pin);
-}
-
-static int au_do_sio_cpup_simple(struct au_cp_generic *cpg)
-{
- int err, wkq_err;
- struct dentry *dentry, *parent;
- struct file *h_file;
- struct inode *h_dir;
-
- dentry = cpg->dentry;
- h_file = NULL;
- if (au_ftest_cpup(cpg->flags, HOPEN)) {
- AuDebugOn(cpg->bsrc < 0);
- h_file = au_h_open_pre(dentry, cpg->bsrc, /*force_wr*/0);
- err = PTR_ERR(h_file);
- if (IS_ERR(h_file))
- goto out;
- }
-
- parent = dget_parent(dentry);
- h_dir = au_h_iptr(d_inode(parent), cpg->bdst);
- if (!au_test_h_perm_sio(h_dir, MAY_EXEC | MAY_WRITE)
- && !au_cpup_sio_test(cpg->pin, d_inode(dentry)->i_mode))
- err = au_cpup_simple(cpg);
- else {
- struct au_cpup_simple_args args = {
- .errp = &err,
- .cpg = cpg
- };
- wkq_err = au_wkq_wait(au_call_cpup_simple, &args);
- if (unlikely(wkq_err))
- err = wkq_err;
- }
-
- dput(parent);
- if (h_file)
- au_h_open_post(dentry, cpg->bsrc, h_file);
-
-out:
- return err;
-}
-
-int au_sio_cpup_simple(struct au_cp_generic *cpg)
-{
- aufs_bindex_t bsrc, bend;
- struct dentry *dentry, *h_dentry;
-
- if (cpg->bsrc < 0) {
- dentry = cpg->dentry;
- bend = au_dbend(dentry);
- for (bsrc = cpg->bdst + 1; bsrc <= bend; bsrc++) {
- h_dentry = au_h_dptr(dentry, bsrc);
- if (h_dentry) {
- AuDebugOn(d_is_negative(h_dentry));
- break;
- }
- }
- AuDebugOn(bsrc > bend);
- cpg->bsrc = bsrc;
- }
- AuDebugOn(cpg->bsrc <= cpg->bdst);
- return au_do_sio_cpup_simple(cpg);
-}
-
-int au_sio_cpdown_simple(struct au_cp_generic *cpg)
-{
- AuDebugOn(cpg->bdst <= cpg->bsrc);
- return au_do_sio_cpup_simple(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 dentry *h_d_dst, *h_d_start;
- struct au_dinfo *dinfo;
- struct au_hdentry *hdp;
-
- dinfo = au_di(cpg->dentry);
- AuRwMustWriteLock(&dinfo->di_rwsem);
-
- bsrc_orig = cpg->bsrc;
- cpg->bsrc = dinfo->di_bstart;
- hdp = dinfo->di_hdentry;
- h_d_dst = hdp[0 + cpg->bdst].hd_dentry;
- dinfo->di_bstart = cpg->bdst;
- hdp[0 + cpg->bdst].hd_dentry = wh_dentry;
- h_d_start = NULL;
- if (file) {
- h_d_start = hdp[0 + cpg->bsrc].hd_dentry;
- hdp[0 + cpg->bsrc].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);
- hdp[0 + cpg->bsrc].hd_dentry = h_d_start;
- }
- hdp[0 + cpg->bdst].hd_dentry = h_d_dst;
- dinfo->di_bstart = 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);
-
- mutex_lock_nested(&h_tmpdir->i_mutex, AuLsc_I_PARENT3);
- /* todo: au_h_open_pre()? */
-
- pin_orig = cpg->pin;
- au_pin_init(&wh_pin, dentry, bdst, AuLsc_DI_PARENT,
- AuLsc_I_PARENT3, cpg->pin->udba, 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) {
- mutex_unlock(&h_tmpdir->i_mutex);
- /* todo: au_h_open_post()? */
- 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.
- */
-/* cf. revalidate function in file.c */
-int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst,
- int (*cp)(struct dentry *dentry, aufs_bindex_t bdst,
- struct au_pin *pin,
- struct dentry *h_parent, void *arg),
- void *arg)
-{
- int err;
- struct au_pin pin;
- struct dentry *d, *parent, *h_parent, *real_parent, *h_dentry;
-
- err = 0;
- parent = dget_parent(dentry);
- if (IS_ROOT(parent))
- goto out;
-
- au_pin_init(&pin, dentry, bdst, AuLsc_DI_PARENT2, AuLsc_I_PARENT2,
- au_opt_udba(dentry->d_sb), AuPin_MNT_WRITE);
-
- /* do not use au_dpage */
- real_parent = parent;
- while (1) {
- dput(parent);
- parent = dget_parent(dentry);
- h_parent = au_h_dptr(parent, bdst);
- if (h_parent)
- goto out; /* success */
-
- /* find top dir which is necessary to cpup */
- do {
- d = parent;
- dput(parent);
- parent = dget_parent(d);
- di_read_lock_parent3(parent, !AuLock_IR);
- h_parent = au_h_dptr(parent, bdst);
- di_read_unlock(parent, !AuLock_IR);
- } while (!h_parent);
-
- if (d != real_parent)
- di_write_lock_child3(d);
-
- /* somebody else might create while we were sleeping */
- h_dentry = au_h_dptr(d, bdst);
- if (!h_dentry || d_is_negative(h_dentry)) {
- if (h_dentry)
- au_update_dbstart(d);
-
- au_pin_set_dentry(&pin, d);
- err = au_do_pin(&pin);
- if (!err) {
- err = cp(d, bdst, &pin, h_parent, arg);
- au_unpin(&pin);
- }
- }
-
- if (d != real_parent)
- di_write_unlock(d);
- if (unlikely(err))
- break;
- }
-
-out:
- dput(parent);
- return err;
-}
-
-static int au_cpup_dir(struct dentry *dentry, aufs_bindex_t bdst,
- struct au_pin *pin,
- struct dentry *h_parent __maybe_unused,
- void *arg __maybe_unused)
-{
- struct au_cp_generic cpg = {
- .dentry = dentry,
- .bdst = bdst,
- .bsrc = -1,
- .len = 0,
- .pin = pin,
- .flags = AuCpup_DTIME
- };
- return au_sio_cpup_simple(&cpg);
-}
-
-int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst)
-{
- return au_cp_dirs(dentry, bdst, au_cpup_dir, NULL);
-}
-
-int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst)
-{
- int err;
- struct dentry *parent;
- struct inode *dir;
-
- parent = dget_parent(dentry);
- dir = d_inode(parent);
- err = 0;
- if (au_h_iptr(dir, bdst))
- goto out;
-
- di_read_unlock(parent, AuLock_IR);
- di_write_lock_parent(parent);
- /* someone else might change our inode while we were sleeping */
- if (!au_h_iptr(dir, bdst))
- err = au_cpup_dirs(dentry, bdst);
- di_downgrade_lock(parent, AuLock_IR);
-
-out:
- dput(parent);
- return err;
-}