lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Wed, 21 May 2008 12:22:44 +0900
From:	hooanon05@...oo.co.jp
To:	linux-fsdevel@...r.kernel.org, linux-kernel@...r.kernel.org
Cc:	Junjiro Okajima <hooanon05@...oo.co.jp>
Subject: [AUFS PATCH v2.6.26-rc2-mm1 14/39] aufs copy-up

From: Junjiro Okajima <hooanon05@...oo.co.jp>

initial commit
internal copy-up functions
see wbr_policy.c for copy-down

Signed-off-by: Junjiro Okajima <hooanon05@...oo.co.jp>
---
 fs/aufs/cpup.c | 1021 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/aufs/cpup.h |   80 +++++
 2 files changed, 1101 insertions(+), 0 deletions(-)
 create mode 100644 fs/aufs/cpup.c
 create mode 100644 fs/aufs/cpup.h

diff --git a/fs/aufs/cpup.c b/fs/aufs/cpup.c
new file mode 100644
index 0000000..2fbfe9b
--- /dev/null
+++ b/fs/aufs/cpup.c
@@ -0,0 +1,1021 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/*
+ * copy-up functions, see wbr_policy.c for copy-down
+ */
+
+#include <linux/fs_stack.h>
+#include <linux/uaccess.h>
+#include "aufs.h"
+
+/* todo? violent cpup_attr_*() functions don't care inode lock */
+void au_cpup_attr_timesizes(struct inode *inode)
+{
+	struct inode *h_inode;
+
+	LKTRTrace("i%lu\n", inode->i_ino);
+	/* todo? IMustLock(inode); */
+	h_inode = au_h_iptr(inode, au_ibstart(inode));
+	AuDebugOn(!h_inode);
+	/* todo? IMustLock(!h_inode); */
+
+	fsstack_copy_attr_times(inode, h_inode);
+	/* todo: this spin_lock conflicts the new unionfs patch in -mm tree */
+	/* spin_lock(&inode->i_lock); */
+	fsstack_copy_inode_size(inode, h_inode);
+	/* spin_unlock(&inode->i_lock); */
+}
+
+void au_cpup_attr_nlink(struct inode *inode)
+{
+	struct inode *h_inode;
+
+	LKTRTrace("i%lu\n", inode->i_ino);
+	/* todo? IMustLock(inode); */
+	AuDebugOn(!inode->i_mode);
+
+	h_inode = au_h_iptr(inode, au_ibstart(inode));
+	inode->i_nlink = h_inode->i_nlink;
+
+	/*
+	 * fewer nlink makes find(1) noisy, but larger nlink doesn't.
+	 * it may includes whplink directory.
+	 */
+	if (unlikely(S_ISDIR(h_inode->i_mode))) {
+		aufs_bindex_t bindex, bend;
+		bend = au_ibend(inode);
+		for (bindex = au_ibstart(inode) + 1; 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;
+
+	LKTRTrace("i%lu\n", inode->i_ino);
+	/* todo? IMustLock(inode); */
+	h_inode = au_h_iptr(inode, au_ibstart(inode));
+	AuDebugOn(!h_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);
+
+	/* todo: remove this? */
+	inode->i_flags = h_inode->i_flags;
+}
+
+void au_cpup_igen(struct inode *inode, struct inode *h_inode)
+{
+	inode->i_generation = h_inode->i_generation;
+	au_ii(inode)->ii_hsb1 = h_inode->i_sb;
+}
+
+void au_cpup_attr_all(struct inode *inode)
+{
+	struct inode *h_inode;
+
+	LKTRTrace("i%lu\n", inode->i_ino);
+	/* todo? IMustLock(inode); */
+	h_inode = au_h_iptr(inode, au_ibstart(inode));
+	AuDebugOn(!h_inode);
+
+	au_cpup_attr_changeable(inode);
+	if (inode->i_nlink > 0)
+		au_cpup_attr_nlink(inode);
+
+	switch (inode->i_mode & S_IFMT) {
+	case S_IFBLK:
+	case S_IFCHR:
+		inode->i_rdev = au_h_rdev(h_inode, /*h_mnt*/NULL,
+					  /*h_dentry*/NULL);
+	}
+	inode->i_blkbits = h_inode->i_blkbits;
+	au_cpup_igen(inode, h_inode);
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* Note: dt_dentry and dt_hidden_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 dentry *h_dentry, struct au_hinode *hdir)
+{
+	struct inode *inode;
+
+	LKTRTrace("%.*s, hdir %d\n", AuDLNPair(dentry), !!hdir);
+	AuDebugOn(!dentry || !h_dentry || !h_dentry->d_inode);
+
+	dt->dt_dentry = dentry;
+	dt->dt_h_dentry = h_dentry;
+	dt->dt_hdir = hdir;
+	inode = h_dentry->d_inode;
+	dt->dt_atime = inode->i_atime;
+	dt->dt_mtime = inode->i_mtime;
+	/* smp_mb(); */
+}
+
+void au_dtime_revert(struct au_dtime *dt)
+{
+	struct iattr attr;
+	int err;
+	struct au_hin_ignore ign;
+	struct vfsub_args vargs;
+
+	LKTRTrace("%.*s\n", AuDLNPair(dt->dt_dentry));
+
+	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;
+
+	vfsub_args_init(&vargs, &ign,
+			au_opt_test_dlgt(au_mntflags(dt->dt_dentry->d_sb)), 0);
+	if (unlikely(dt->dt_hdir))
+		vfsub_ign_hinode(&vargs, IN_ATTRIB, dt->dt_hdir);
+	err = vfsub_notify_change(dt->dt_h_dentry, &attr, &vargs);
+	if (unlikely(err))
+		AuWarn("restoring timestamps failed(%d). ignored\n", err);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static noinline_for_stack int
+cpup_iattr(struct dentry *h_dst, struct dentry *h_src, int dlgt)
+{
+	int err, sbits;
+	struct iattr ia;
+	struct inode *h_isrc, *h_idst;
+	struct vfsub_args vargs;
+
+	LKTRTrace("%.*s\n", AuDLNPair(h_dst));
+	h_idst = h_dst->d_inode;
+	/* todo? IMustLock(h_idst); */
+	h_isrc = h_src->d_inode;
+	/* todo? IMustLock(h_isrc); */
+
+	ia.ia_valid = ATTR_FORCE | ATTR_MODE | ATTR_UID | ATTR_GID
+		| ATTR_ATIME | ATTR_MTIME
+		| ATTR_ATIME_SET | ATTR_MTIME_SET;
+	ia.ia_mode = h_isrc->i_mode;
+	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;
+	sbits = !!(ia.ia_mode & (S_ISUID | S_ISGID));
+
+	vfsub_args_init(&vargs, NULL, dlgt, /*force_unlink*/0);
+	err = vfsub_notify_change(h_dst, &ia, &vargs);
+
+	/* is this nfs only? */
+	if (!err && sbits && au_test_nfs(h_dst->d_sb)) {
+		ia.ia_valid = ATTR_FORCE | ATTR_MODE;
+		ia.ia_mode = h_isrc->i_mode;
+		err = vfsub_notify_change(h_dst, &ia, &vargs);
+	}
+
+	/* todo? remove this? */
+	if (!err)
+		h_idst->i_flags = h_isrc->i_flags;
+
+	AuTraceErr(err);
+	return err;
+}
+
+/*
+ * to support a sparse file which is opened with O_APPEND,
+ * we need to close the file.
+ */
+static noinline_for_stack int
+cpup_regular(struct dentry *dentry, aufs_bindex_t bdst, aufs_bindex_t bsrc,
+	     loff_t len)
+{
+	int err, i;
+	struct super_block *sb;
+	struct inode *h_inode;
+	enum { SRC, DST };
+	struct {
+		aufs_bindex_t bindex;
+		unsigned int flags;
+		struct dentry *dentry;
+		struct file *file;
+		void *label, *label_file;
+	} *h, hidden[] = {
+		{
+			.bindex = bsrc,
+			.flags = O_RDONLY | O_NOATIME | O_LARGEFILE,
+			.file = NULL,
+			.label = &&out,
+			.label_file = &&out_src_file
+		},
+		{
+			.bindex = bdst,
+			.flags = O_WRONLY | O_NOATIME | O_LARGEFILE,
+			.file = NULL,
+			.label = &&out_src_file,
+			.label_file = &&out_dst_file
+		}
+	};
+
+	LKTRTrace("dentry %.*s, bdst %d, bsrc %d, len %lld\n",
+		  AuDLNPair(dentry), bdst, bsrc, len);
+	AuDebugOn(bsrc <= bdst);
+	AuDebugOn(!len);
+	sb = dentry->d_sb;
+	AuDebugOn(au_test_ro(sb, bdst, dentry->d_inode));
+	/* bsrc branch can be ro/rw. */
+
+	h = hidden;
+	for (i = 0; i < 2; i++, h++) {
+		h->dentry = au_h_dptr(dentry, h->bindex);
+		AuDebugOn(!h->dentry);
+		h_inode = h->dentry->d_inode;
+		AuDebugOn(!h_inode || !S_ISREG(h_inode->i_mode));
+		h->file = au_h_open(dentry, h->bindex, h->flags, /*file*/NULL);
+		err = PTR_ERR(h->file);
+		if (IS_ERR(h->file))
+			goto *h->label;
+		err = -EINVAL;
+		if (unlikely(!h->file->f_op))
+			goto *h->label_file;
+	}
+
+	/* stop updating while we copyup */
+	IMustLock(hidden[SRC].dentry->d_inode);
+	err = au_copy_file(hidden[DST].file, hidden[SRC].file, len, sb);
+
+ out_dst_file:
+	fput(hidden[DST].file);
+	au_sbr_put(sb, hidden[DST].bindex);
+ out_src_file:
+	fput(hidden[SRC].file);
+	au_sbr_put(sb, hidden[SRC].bindex);
+ out:
+	AuTraceErr(err);
+	return err;
+}
+
+static noinline_for_stack int
+au_do_cpup_regular(struct dentry *dentry, aufs_bindex_t bdst,
+		   aufs_bindex_t bsrc, loff_t len, struct inode *h_inode,
+		   struct inode *h_dir, struct dentry *h_dst, int dlgt)
+{
+	int err, rerr;
+	loff_t l;
+	struct vfsub_args vargs;
+
+	AuTraceEnter();
+
+	err = 0;
+	l = i_size_read(h_inode);
+	if (len == -1 || l < len)
+		len = l;
+	if (len)
+		err = cpup_regular(dentry, bdst, bsrc, len);
+	if (!err)
+		goto out; /* success */
+
+	vfsub_args_init(&vargs, NULL, dlgt, 0);
+	rerr = vfsub_unlink(h_dir, h_dst, &vargs);
+	if (rerr) {
+		AuIOErr("failed unlinking cpup-ed %.*s(%d, %d)\n",
+			AuDLNPair(h_dst), err, rerr);
+		err = -EIO;
+	}
+
+ out:
+	AuTraceErr(err);
+	return err;
+}
+
+static noinline_for_stack int
+au_do_cpup_symlink(struct dentry *h_dst, struct dentry *h_src,
+		   struct inode *h_dir, umode_t mode, int dlgt)
+{
+	int err, symlen;
+	char *sym;
+	mm_segment_t old_fs;
+
+	AuTraceEnter();
+
+	err = -ENOMEM;
+	sym = __getname();
+	if (unlikely(!sym))
+		goto out;
+
+	old_fs = get_fs();
+	set_fs(KERNEL_DS);
+	symlen = h_src->d_inode->i_op->readlink(h_src, (char __user *)sym,
+						PATH_MAX);
+	err = symlen;
+	set_fs(old_fs);
+
+	if (symlen > 0) {
+		sym[symlen] = 0;
+		err = vfsub_symlink(h_dir, h_dst, sym, mode, dlgt);
+	}
+	__putname(sym);
+
+ out:
+	AuTraceErr(err);
+	return err;
+}
+
+/* return with hidden dst inode is locked */
+static noinline_for_stack int
+cpup_entry(struct dentry *dentry, aufs_bindex_t bdst, aufs_bindex_t bsrc,
+	   loff_t len, unsigned int flags, int dlgt)
+{
+	int err, isdir, hinotify;
+	struct dentry *h_src, *h_dst, *h_parent, *parent;
+	struct inode *h_inode, *h_dir;
+	struct au_dtime dt;
+	umode_t mode;
+	struct super_block *sb;
+	struct au_hinode *hgdir;
+	const int do_dt = au_ftest_cpup(flags, DTIME);
+	unsigned int mnt_flags;
+
+	LKTRTrace("%.*s, i%lu, bdst %d, bsrc %d, len %Ld, dtime %u\n",
+		  AuDLNPair(dentry), dentry->d_inode->i_ino, bdst, bsrc, len,
+		  do_dt);
+	sb = dentry->d_sb;
+	AuDebugOn(bdst >= bsrc || au_test_ro(sb, bdst, NULL));
+	/* bsrc branch can be ro/rw. */
+
+	h_src = au_h_dptr(dentry, bsrc);
+	AuDebugOn(!h_src);
+	h_inode = h_src->d_inode;
+	AuDebugOn(!h_inode);
+
+	/* stop referencing while we are creating */
+	parent = dget_parent(dentry);
+	h_dst = au_h_dptr(dentry, bdst);
+	AuDebugOn(h_dst && h_dst->d_inode);
+	h_parent = h_dst->d_parent; /* dir inode is locked */
+	h_dir = h_parent->d_inode;
+	IMustLock(h_dir);
+
+	mnt_flags = au_mntflags(sb);
+	hinotify = !!au_opt_test(mnt_flags, UDBA_INOTIFY);
+	if (do_dt) {
+		hgdir = NULL;
+		if (unlikely(hinotify && !IS_ROOT(parent))) {
+			struct dentry *gparent;
+			gparent = dget_parent(parent);
+			hgdir = au_hi(gparent->d_inode, bdst);
+			dput(gparent);
+		}
+		au_dtime_store(&dt, parent, h_parent, hgdir);
+	}
+
+	isdir = 0;
+	mode = h_inode->i_mode;
+	switch (mode & S_IFMT) {
+	case S_IFREG:
+		/* stop updating while we are referencing */
+		IMustLock(h_inode);
+		err = au_h_create(h_dir, h_dst, mode | S_IWUSR, dlgt, NULL,
+				  au_nfsmnt(sb, bdst));
+		if (!err)
+			err = au_do_cpup_regular(dentry, bdst, bsrc, len,
+						 h_inode, h_dir, h_dst, dlgt);
+		break;
+	case S_IFDIR:
+		isdir = 1;
+		err = vfsub_mkdir(h_dir, h_dst, mode, dlgt);
+		if (!err) {
+			/* setattr case: dir is not locked */
+			if (0 && au_ibstart(parent->d_inode) == bdst)
+				au_cpup_attr_nlink(parent->d_inode);
+			au_cpup_attr_nlink(dentry->d_inode);
+		}
+		break;
+	case S_IFLNK:
+		err = au_do_cpup_symlink(h_dst, h_src, h_dir, mode, dlgt);
+		break;
+	case S_IFCHR:
+	case S_IFBLK:
+		AuDebugOn(!capable(CAP_MKNOD));
+		/*FALLTHROUGH*/
+	case S_IFIFO:
+	case S_IFSOCK:
+		err = vfsub_mknod(h_dir, h_dst, mode,
+				  au_h_rdev(h_inode, /*h_mnt*/NULL, h_src),
+				  dlgt);
+		break;
+	default:
+		AuIOErr("Unknown inode type 0%o\n", mode);
+		err = -EIO;
+	}
+
+	/* todo: should it be always? */
+	if (unlikely(hinotify
+		     && !isdir
+		     && au_opt_test(mnt_flags, XINO)
+		     && h_inode->i_nlink == 1
+		     && bdst < bsrc))
+		au_xino_write0(sb, bsrc, h_inode->i_ino, /*ino*/0);
+		/* ignore this error */
+
+	if (do_dt)
+		au_dtime_revert(&dt);
+	dput(parent);
+	AuTraceErr(err);
+	return err;
+}
+
+/*
+ * copyup the @dentry from @bsrc to @bdst.
+ * the caller must set the both of hidden dentries.
+ * @len is for truncating when it is -1 copyup the entire file.
+ */
+int au_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,
+		   aufs_bindex_t bsrc, loff_t len, unsigned int flags)
+{
+	int err, rerr, isdir, dlgt, plink;
+	struct dentry *h_src, *h_dst, *parent, *h_parent;
+	struct inode *dst_inode, *h_dir, *inode;
+	struct super_block *sb;
+	aufs_bindex_t old_ibstart;
+	struct au_dtime dt;
+	struct vfsub_args vargs;
+	struct au_hinode *hgdir;
+	unsigned int mnt_flags;
+
+	LKTRTrace("%.*s, i%lu, bdst %d, bsrc %d, len %Ld, flags 0x%x\n",
+		  AuDLNPair(dentry), dentry->d_inode->i_ino, bdst, bsrc, len,
+		  flags);
+	sb = dentry->d_sb;
+	AuDebugOn(bsrc <= bdst);
+	h_dst = au_h_dptr(dentry, bdst);
+	AuDebugOn(!h_dst || h_dst->d_inode);
+	h_parent = h_dst->d_parent; /* dir inode is locked */
+	h_dir = h_parent->d_inode;
+	IMustLock(h_dir);
+	h_src = au_h_dptr(dentry, bsrc);
+	AuDebugOn(!h_src || !h_src->d_inode);
+	inode = dentry->d_inode;
+	IiMustWriteLock(inode);
+	parent = dget_parent(dentry);
+
+	mnt_flags = au_mntflags(sb);
+	dlgt = !!au_opt_test_dlgt(mnt_flags);
+	plink = !!au_opt_test(mnt_flags, PLINK);
+	dst_inode = au_h_iptr(inode, bdst);
+	if (unlikely(dst_inode)) {
+		if (unlikely(!plink)) {
+			err = -EIO;
+			AuIOErr("i%lu exists on a upper branch "
+				"but plink is disabled\n", inode->i_ino);
+			goto out;
+		}
+
+		if (dst_inode->i_nlink) {
+			h_src = au_plink_lkup(sb, bdst, inode);
+			err = PTR_ERR(h_src);
+			if (IS_ERR(h_src))
+				goto out;
+			AuDebugOn(!h_src->d_inode);
+			err = vfsub_link(h_src, h_dir, h_dst, dlgt);
+			dput(h_src);
+			goto out;
+		} else
+			/* todo: cpup_wh_file? */
+			/* udba work */
+			au_update_brange(inode, 1);
+	}
+
+	old_ibstart = au_ibstart(inode);
+	err = cpup_entry(dentry, bdst, bsrc, len, flags, dlgt);
+	if (unlikely(err))
+		goto out;
+	dst_inode = h_dst->d_inode;
+	mutex_lock_nested(&dst_inode->i_mutex, AuLsc_I_CHILD2);
+
+	/* todo: test dlgt? */
+	err = cpup_iattr(h_dst, h_src, dlgt);
+#if 0 /* reserved for future use */
+	if (0 && !err)
+		err = cpup_xattrs(h_src, h_dst);
+#endif
+	isdir = S_ISDIR(dst_inode->i_mode);
+	if (!err) {
+		if (bdst < old_ibstart)
+			au_set_ibstart(inode, bdst);
+		au_set_h_iptr(inode, bdst, igrab(dst_inode),
+			      au_hi_flags(inode, isdir));
+		mutex_unlock(&dst_inode->i_mutex);
+		if (!isdir
+		    && h_src->d_inode->i_nlink > 1
+		    && plink)
+			au_plink_append(sb, inode, h_dst, bdst);
+		goto out; /* success */
+	}
+
+	/* revert */
+	mutex_unlock(&dst_inode->i_mutex);
+	hgdir = NULL;
+	if (unlikely(au_opt_test(mnt_flags, UDBA_INOTIFY)
+		     && !IS_ROOT(parent))) {
+		struct dentry *gparent;
+		gparent = dget_parent(parent);
+		hgdir = au_hi(gparent->d_inode, bdst);
+		dput(gparent);
+	}
+	au_dtime_store(&dt, parent, h_parent, hgdir);
+	vfsub_args_init(&vargs, NULL, dlgt, 0);
+	if (!isdir)
+		rerr = vfsub_unlink(h_dir, h_dst, &vargs);
+	else
+		rerr = vfsub_rmdir(h_dir, h_dst, &vargs);
+	au_dtime_revert(&dt);
+	if (rerr) {
+		AuIOErr("failed removing broken entry(%d, %d)\n", err, rerr);
+		err = -EIO;
+	}
+
+ out:
+	dput(parent);
+	AuTraceErr(err);
+	return err;
+}
+
+struct au_cpup_single_args {
+	int *errp;
+	struct dentry *dentry;
+	aufs_bindex_t bdst, bsrc;
+	loff_t len;
+	unsigned int flags;
+};
+
+static void au_call_cpup_single(void *args)
+{
+	struct au_cpup_single_args *a = args;
+	*a->errp = au_cpup_single(a->dentry, a->bdst, a->bsrc, a->len,
+				  a->flags);
+}
+
+int au_sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,
+		       aufs_bindex_t bsrc, loff_t len, unsigned int flags)
+{
+	int err, wkq_err;
+	struct dentry *h_dentry;
+	umode_t mode;
+
+	LKTRTrace("%.*s, i%lu, bdst %d, bsrc %d, len %Ld, flags 0x%x\n",
+		  AuDLNPair(dentry), dentry->d_inode->i_ino, bdst, bsrc, len,
+		  flags);
+
+	h_dentry = au_h_dptr(dentry, bsrc);
+	mode = h_dentry->d_inode->i_mode & S_IFMT;
+	if ((mode != S_IFCHR && mode != S_IFBLK)
+	    || capable(CAP_MKNOD))
+		err = au_cpup_single(dentry, bdst, bsrc, len, flags);
+	else {
+		struct au_cpup_single_args args = {
+			.errp		= &err,
+			.dentry		= dentry,
+			.bdst		= bdst,
+			.bsrc		= bsrc,
+			.len		= len,
+			.flags		= flags
+		};
+		wkq_err = au_wkq_wait(au_call_cpup_single, &args, /*dlgt*/0);
+		if (unlikely(wkq_err))
+			err = wkq_err;
+	}
+
+	AuTraceErr(err);
+	return err;
+}
+
+/*
+ * copyup the @dentry from the first active hidden branch to @bdst,
+ * using au_cpup_single().
+ */
+int au_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
+		   unsigned int flags)
+{
+	int err;
+	struct inode *inode;
+	aufs_bindex_t bsrc, bend;
+
+	LKTRTrace("%.*s, bdst %d, len %Ld, flags 0x%x\n",
+		  AuDLNPair(dentry), bdst, len, flags);
+	inode = dentry->d_inode;
+	AuDebugOn(!S_ISDIR(inode->i_mode) && au_dbstart(dentry) < bdst);
+
+	bend = au_dbend(dentry);
+	for (bsrc = bdst + 1; bsrc <= bend; bsrc++)
+		if (au_h_dptr(dentry, bsrc))
+			break;
+	AuDebugOn(!au_h_dptr(dentry, bsrc));
+
+	err = au_lkup_neg(dentry, bdst);
+	if (!err) {
+		err = au_cpup_single(dentry, bdst, bsrc, len, flags);
+		if (!err)
+			return 0; /* success */
+
+		/* revert */
+		au_set_h_dptr(dentry, bdst, NULL);
+		au_set_dbstart(dentry, bsrc);
+	}
+
+	AuTraceErr(err);
+	return err;
+}
+
+struct au_cpup_simple_args {
+	int *errp;
+	struct dentry *dentry;
+	aufs_bindex_t bdst;
+	loff_t len;
+	unsigned int flags;
+};
+
+static void au_call_cpup_simple(void *args)
+{
+	struct au_cpup_simple_args *a = args;
+	*a->errp = au_cpup_simple(a->dentry, a->bdst, a->len, a->flags);
+}
+
+int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
+		       unsigned int flags)
+{
+	int err, do_sio, dlgt, wkq_err;
+	struct dentry *parent;
+	struct inode *h_dir, *dir;
+
+	LKTRTrace("%.*s, b%d, len %Ld, flags 0x%x\n",
+		  AuDLNPair(dentry), bdst, len, flags);
+
+	parent = dget_parent(dentry);
+	dir = parent->d_inode;
+	h_dir = au_h_iptr(dir, bdst);
+	dlgt = !!au_opt_test_dlgt(au_mntflags(dir->i_sb));
+	do_sio = au_test_h_perm_sio(h_dir, MAY_EXEC | MAY_WRITE, dlgt);
+	if (!do_sio) {
+		/*
+		 * testing CAP_MKNOD is for generic fs,
+		 * but CAP_FSETID is for xfs only, currently.
+		 */
+		umode_t mode = dentry->d_inode->i_mode;
+		do_sio = (((mode & (S_IFCHR | S_IFBLK))
+			   && !capable(CAP_MKNOD))
+			  || ((mode & (S_ISUID | S_ISGID))
+			      && !capable(CAP_FSETID)));
+	}
+	if (!do_sio)
+		err = au_cpup_simple(dentry, bdst, len, flags);
+	else {
+		struct au_cpup_simple_args args = {
+			.errp		= &err,
+			.dentry		= dentry,
+			.bdst		= bdst,
+			.len		= len,
+			.flags		= flags
+		};
+		wkq_err = au_wkq_wait(au_call_cpup_simple, &args, /*dlgt*/0);
+		if (unlikely(wkq_err))
+			err = wkq_err;
+	}
+
+	dput(parent);
+	AuTraceErr(err);
+	return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static noinline_for_stack int
+au_do_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst,
+	      struct dentry *wh_dentry, struct file *file, loff_t len)
+{
+	int err;
+	struct au_dinfo *dinfo;
+	aufs_bindex_t bstart;
+	struct dentry *h_d_bdst, *h_d_bstart;
+
+	AuTraceEnter();
+
+	dinfo = au_di(dentry);
+	bstart = dinfo->di_bstart;
+	h_d_bdst = dinfo->di_hdentry[0 + bdst].hd_dentry;
+	dinfo->di_bstart = bdst;
+	dinfo->di_hdentry[0 + bdst].hd_dentry = wh_dentry;
+	h_d_bstart = dinfo->di_hdentry[0 + bstart].hd_dentry;
+	if (file)
+		dinfo->di_hdentry[0 + bstart].hd_dentry
+			= au_h_fptr(file, au_fbstart(file))->f_dentry;
+	err = au_cpup_single(dentry, bdst, bstart, len, !AuCpup_DTIME);
+	if (!err && file) {
+		err = au_reopen_nondir(file);
+		dinfo->di_hdentry[0 + bstart].hd_dentry = h_d_bstart;
+	}
+	dinfo->di_hdentry[0 + bdst].hd_dentry = h_d_bdst;
+	dinfo->di_bstart = bstart;
+
+	AuTraceErr(err);
+	return err;
+}
+
+/*
+ * copyup the deleted file for writing.
+ */
+int au_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
+	       struct file *file)
+{
+	int err, dlgt;
+	struct dentry *parent, *h_parent, *wh_dentry;
+	struct super_block *sb;
+	unsigned int mnt_flags;
+	struct au_dtime dt;
+	struct vfsub_args vargs;
+	struct au_hinode *hgdir;
+	struct au_ndx ndx = {
+		.nd	= NULL,
+		.flags	= 0,
+		/* .br	= NULL */
+	};
+
+	LKTRTrace("%.*s, bdst %d, len %Lu\n", AuDLNPair(dentry), bdst, len);
+	AuDebugOn(S_ISDIR(dentry->d_inode->i_mode)
+		  || (file && !(file->f_mode & FMODE_WRITE)));
+	DiMustWriteLock(dentry);
+
+	parent = dget_parent(dentry);
+	IiMustAnyLock(parent->d_inode);
+	h_parent = au_h_dptr(parent, bdst);
+	AuDebugOn(!h_parent);
+
+	sb = parent->d_sb;
+	mnt_flags = au_mntflags(sb);
+	dlgt = 0;
+	ndx.nfsmnt = au_nfsmnt(sb, bdst);
+	if (unlikely(au_opt_test_dlgt(mnt_flags))) {
+		dlgt = 1;
+		au_fset_ndx(ndx.flags, DLGT);
+	}
+	wh_dentry = au_whtmp_lkup(h_parent, &dentry->d_name, &ndx);
+	err = PTR_ERR(wh_dentry);
+	if (IS_ERR(wh_dentry))
+		goto out;
+
+	hgdir = NULL;
+	if (unlikely(au_opt_test(mnt_flags, UDBA_INOTIFY)
+		     && !IS_ROOT(parent))) {
+		struct dentry *gparent;
+		gparent = dget_parent(parent);
+		hgdir = au_hi(gparent->d_inode, bdst);
+		dput(gparent);
+	}
+	au_dtime_store(&dt, parent, h_parent, hgdir);
+	err = au_do_cpup_wh(dentry, bdst, wh_dentry, file, len);
+	if (unlikely(err))
+		goto out_wh;
+
+	AuDebugOn(!d_unhashed(dentry));
+	/* dget first to force sillyrename on nfs */
+	dget(wh_dentry);
+	vfsub_args_init(&vargs, NULL, dlgt, 0);
+	err = vfsub_unlink(h_parent->d_inode, wh_dentry, &vargs);
+	if (unlikely(err)) {
+		AuIOErr("failed remove copied-up tmp file %.*s(%d)\n",
+			AuDLNPair(wh_dentry), err);
+		err = -EIO;
+	}
+	au_dtime_revert(&dt);
+	au_set_hi_wh(dentry->d_inode, bdst, wh_dentry);
+
+ out_wh:
+	dput(wh_dentry);
+ out:
+	dput(parent);
+	AuTraceErr(err);
+	return err;
+}
+
+struct au_cpup_wh_args {
+	int *errp;
+	struct dentry *dentry;
+	aufs_bindex_t bdst;
+	loff_t len;
+	struct file *file;
+};
+
+static void au_call_cpup_wh(void *args)
+{
+	struct au_cpup_wh_args *a = args;
+	*a->errp = au_cpup_wh(a->dentry, a->bdst, a->len, a->file);
+}
+
+int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
+		   struct file *file)
+{
+	int err, wkq_err;
+	struct dentry *parent;
+	struct inode *dir, *h_dir;
+
+	AuTraceEnter();
+	parent = dget_parent(dentry);
+	dir = parent->d_inode;
+	IiMustAnyLock(dir);
+	h_dir = au_h_iptr(dir, bdst);
+
+	if (!au_test_h_perm_sio
+	    (h_dir, MAY_EXEC | MAY_WRITE,
+	     au_opt_test_dlgt(au_mntflags(dentry->d_sb))))
+		err = au_cpup_wh(dentry, bdst, len, file);
+	else {
+		struct au_cpup_wh_args args = {
+			.errp	= &err,
+			.dentry	= dentry,
+			.bdst	= bdst,
+			.len	= len,
+			.file	= file
+		};
+		wkq_err = au_wkq_wait(au_call_cpup_wh, &args, /*dlgt*/0);
+		if (unlikely(wkq_err))
+			err = wkq_err;
+	}
+	dput(parent);
+
+	AuTraceErr(err);
+	return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * generic routine for both of copy-up and copy-down.
+ * Although I've tried building a path by dcsub, I gave up this approach.
+ * Since the ancestor directory may be moved/renamed during copy.
+ */
+/* cf. revalidate function in file.c */
+int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, struct dentry *locked,
+	       int (*cp)(struct dentry *dentry, aufs_bindex_t bdst,
+			 struct dentry *h_parent, void *arg),
+	       void *arg)
+{
+	int err, hinotify;
+	struct super_block *sb;
+	struct dentry *d, *parent, *h_parent, *gparent, *real_parent;
+
+	LKTRTrace("%.*s, b%d, parent i%lu, locked %p\n",
+		  AuDLNPair(dentry), bdst, parent_ino(dentry), locked);
+	sb = dentry->d_sb;
+	AuDebugOn(au_test_ro(sb, bdst, NULL));
+	err = 0;
+	parent = dget_parent(dentry);
+	IiMustWriteLock(parent->d_inode);
+	if (unlikely(IS_ROOT(parent)))
+		goto out;
+	if (locked) {
+		DiMustAnyLock(locked);
+		IiMustAnyLock(locked->d_inode);
+	}
+
+	/* slow loop, keep it simple and stupid */
+	real_parent = parent;
+	hinotify = !!au_opt_test(au_mntflags(sb), UDBA_INOTIFY);
+	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 needed to cpup */
+		do {
+			d = parent;
+			dput(parent);
+			parent = dget_parent(d);
+			if (parent != locked) {
+				di_read_lock_parent3(parent, !AuLock_IR);
+				h_parent = au_h_dptr(parent, bdst);
+				di_read_unlock(parent, !AuLock_IR);
+			} else
+				h_parent = au_h_dptr(parent, bdst);
+		} while (!h_parent);
+
+		if (d != real_parent)
+			di_write_lock_child3(d);
+
+		/* somebody else might create while we were sleeping */
+		if (!au_h_dptr(d, bdst) || !au_h_dptr(d, bdst)->d_inode) {
+			struct inode *h_dir = h_parent->d_inode,
+				*dir = parent->d_inode;
+
+			if (au_h_dptr(d, bdst))
+				au_update_dbstart(d);
+			if (parent != locked)
+				di_read_lock_parent3(parent, AuLock_IR);
+			gparent = NULL;
+			if (unlikely(hinotify && !IS_ROOT(parent))) {
+				gparent = dget_parent(parent);
+				if (gparent != locked)
+					ii_read_lock_parent4(gparent->d_inode);
+				else {
+					dput(gparent);
+					gparent = NULL;
+				}
+			}
+			au_hdir_lock(h_dir, dir, bdst);
+			err = cp(d, bdst, h_parent, arg);
+			au_hdir_unlock(h_dir, dir, bdst);
+			if (unlikely(gparent)) {
+				ii_read_unlock(gparent->d_inode);
+				dput(gparent);
+			}
+			if (parent != locked)
+				di_read_unlock(parent, AuLock_IR);
+		}
+
+		if (d != real_parent)
+			di_write_unlock(d);
+		if (unlikely(err))
+			break;
+	}
+
+ out:
+	dput(parent);
+	AuTraceErr(err);
+	return err;
+}
+
+static int au_cpup_dir(struct dentry *dentry, aufs_bindex_t bdst,
+		       struct dentry *h_parent, void *arg)
+{
+	int err;
+
+	err = au_sio_cpup_simple(dentry, bdst, -1, AuCpup_DTIME);
+
+	AuTraceErr(err);
+	return err;
+}
+
+int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst,
+		 struct dentry *locked)
+{
+	int err;
+
+	err = au_cp_dirs(dentry, bdst, locked, au_cpup_dir, NULL);
+
+	AuTraceErr(err);
+	return err;
+}
+
+int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst,
+			  struct dentry *locked)
+{
+	int err;
+	struct dentry *parent;
+	struct inode *dir;
+
+	parent = dget_parent(dentry);
+	dir = parent->d_inode;
+	LKTRTrace("%.*s, b%d, parent i%lu, locked %p\n",
+		  AuDLNPair(dentry), bdst, dir->i_ino, locked);
+	DiMustReadLock(parent);
+	IiMustReadLock(dir);
+
+	err = 0;
+	if (au_h_iptr(dir, bdst))
+		goto out;
+
+	di_read_unlock(parent, AuLock_IR);
+	di_write_lock_parent2(parent);
+	/* someone else might change our inode while we were sleeping */
+	if (unlikely(!au_h_iptr(dir, bdst)))
+		err = au_cpup_dirs(dentry, bdst, locked);
+	di_downgrade_lock(parent, AuLock_IR);
+
+ out:
+	dput(parent);
+	AuTraceErr(err);
+	return err;
+}
diff --git a/fs/aufs/cpup.h b/fs/aufs/cpup.h
new file mode 100644
index 0000000..94816e6
--- /dev/null
+++ b/fs/aufs/cpup.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/*
+ * copy-up/down functions
+ */
+
+#ifndef __AUFS_CPUP_H__
+#define __AUFS_CPUP_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/aufs_type.h>
+
+void au_cpup_attr_timesizes(struct inode *inode);
+void au_cpup_attr_nlink(struct inode *inode);
+void au_cpup_attr_changeable(struct inode *inode);
+void au_cpup_igen(struct inode *inode, struct inode *h_inode);
+void au_cpup_attr_all(struct inode *inode);
+
+/* ---------------------------------------------------------------------- */
+
+/* cpup flags */
+#define AuCpup_DTIME	1	/* do dtime_store/revert */
+#define au_ftest_cpup(flags, name)	((flags) & AuCpup_##name)
+#define au_fset_cpup(flags, name)	{ (flags) |= AuCpup_##name; }
+#define au_fclr_cpup(flags, name)	{ (flags) &= ~AuCpup_##name; }
+
+int au_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,
+		   aufs_bindex_t bsrc, loff_t len, unsigned int flags);
+int au_sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,
+		       aufs_bindex_t bsrc, loff_t len, unsigned int flags);
+int au_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
+		   unsigned int flags);
+int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
+		       unsigned int flags);
+int au_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
+	       struct file *file);
+int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
+		   struct file *file);
+
+int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, struct dentry *locked,
+	       int (*cp)(struct dentry *dentry, aufs_bindex_t bdst,
+			 struct dentry *h_parent, void *arg),
+	       void *arg);
+int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst,
+		 struct dentry *locked);
+int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst,
+			  struct dentry *locked);
+
+/* ---------------------------------------------------------------------- */
+
+/* keep timestamps when copyup */
+struct au_dtime {
+	struct dentry *dt_dentry, *dt_h_dentry;
+	struct au_hinode *dt_hdir;
+	struct timespec dt_atime, dt_mtime;
+};
+void au_dtime_store(struct au_dtime *dt, struct dentry *dentry,
+		    struct dentry *h_dentry, struct au_hinode *hdir);
+void au_dtime_revert(struct au_dtime *dt);
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_CPUP_H__ */
-- 
1.5.5.1.308.g1fbb5.dirty

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ