linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Valerie Aurora <vaurora@redhat.com>
To: Jan Blunck <jblunck@suse.de>,
	Alexander Viro <viro@zeniv.linux.org.uk>,
	Christoph Hellwig <hch@infradead.org>,
	Andy Whitcroft <apw@canonical.com>,
	Scott James Remnant <scott@canonica
Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org,
	Felix Fietkau <nbd@openwrt.org>
Subject: [PATCH 35/41] union-mount: Copy up directory entries on first readdir()
Date: Wed, 21 Oct 2009 12:19:33 -0700	[thread overview]
Message-ID: <1256152779-10054-36-git-send-email-vaurora@redhat.com> (raw)
In-Reply-To: <1256152779-10054-35-git-send-email-vaurora@redhat.com>

readdir() in union mounts is implemented by copying up all visible
directory entries from the lower level directories to the topmost
directory.  Directory entries that refer to lower level file system
objects are marked as "fallthru" in the topmost directory.

Thanks to Felix Fietkau <nbd@openwrt.org> for a bug fix.

XXX - Do we need i_mutex on lower layer?
XXX - Rewrite for two layers only?

Signed-off-by: Valerie Aurora <vaurora@redhat.com>
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
---
 fs/readdir.c          |   17 +++++
 fs/union.c            |  171 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/union.h |    2 +
 3 files changed, 190 insertions(+), 0 deletions(-)

diff --git a/fs/readdir.c b/fs/readdir.c
index 3a48491..cfeacd8 100644
--- a/fs/readdir.c
+++ b/fs/readdir.c
@@ -16,6 +16,8 @@
 #include <linux/security.h>
 #include <linux/syscalls.h>
 #include <linux/unistd.h>
+#include <linux/union.h>
+#include <linux/mount.h>
 
 #include <asm/uaccess.h>
 
@@ -36,9 +38,24 @@ int vfs_readdir(struct file *file, filldir_t filler, void *buf)
 
 	res = -ENOENT;
 	if (!IS_DEADDIR(inode)) {
+		/*
+		 * XXX Think harder about locking for
+		 * union_copyup_dir.  Currently we lock the topmost
+		 * directory and hold that lock while sequentially
+		 * acquiring and dropping locks for the directories
+		 * below this one in the union stack.
+		 */
+		if (is_unionized(file->f_path.dentry, file->f_path.mnt) &&
+		    !IS_OPAQUE(inode) && IS_MNT_UNION(file->f_path.mnt)) {
+			res = union_copyup_dir(&file->f_path);
+			if (res)
+				goto out_unlock;
+		}
+
 		res = file->f_op->readdir(file, buf, filler);
 		file_accessed(file);
 	}
+out_unlock:
 	mutex_unlock(&inode->i_mutex);
 out:
 	return res;
diff --git a/fs/union.c b/fs/union.c
index de31fc9..d56b829 100644
--- a/fs/union.c
+++ b/fs/union.c
@@ -5,6 +5,7 @@
  * Copyright (C) 2007-2009 Novell Inc.
  *
  *   Author(s): Jan Blunck (j.blunck@tu-harburg.de)
+ *              Valerie Aurora <vaurora@redhat.com>
  *
  * This program 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
@@ -777,3 +778,173 @@ void detach_mnt_union(struct vfsmount *mnt)
 	union_put(um);
 	return;
 }
+
+/**
+ * union_copyup_dir_one - copy up a single directory entry
+ *
+ * Individual directory entry copyup function for union_copyup_dir.
+ * We get the entries from higher level layers first.
+ */
+
+static int union_copyup_dir_one(void *buf, const char *name, int namlen,
+				loff_t offset, u64 ino, unsigned int d_type)
+{
+	struct dentry *topmost_dentry = (struct dentry *) buf;
+	struct dentry *dentry;
+	int err = 0;
+
+	switch (namlen) {
+	case 2:
+		if (name[1] != '.')
+			break;
+	case 1:
+		if (name[0] != '.')
+			break;
+		return 0;
+	}
+
+	/* Lookup this entry in the topmost directory */
+	dentry = lookup_one_len(name, topmost_dentry, namlen);
+
+	if (IS_ERR(dentry)) {
+		printk(KERN_INFO "error looking up %s\n", dentry->d_name.name);
+		goto out;
+	}
+
+	/*
+	 * If the entry already exists, one of the following is true:
+	 * it was already copied up (due to an earlier lookup), an
+	 * entry with the same name already exists on the topmost file
+	 * system, it is a whiteout, or it is a fallthru.  In each
+	 * case, the top level entry masks any entries from lower file
+	 * systems, so don't copy up this entry.
+	 */
+	if (dentry->d_inode || d_is_whiteout(dentry) ||
+	    d_is_fallthru(dentry)) {
+		printk(KERN_INFO "skipping copy of %s\n", dentry->d_name.name);
+		goto out_dput;
+	}
+
+	/*
+	 * If the entry doesn't exist, create a fallthru entry in the
+	 * topmost file system.  All possible directory types are
+	 * used, so each file system must implement its own way of
+	 * storing a fallthru entry.
+	 */
+	printk(KERN_INFO "creating fallthru for %s\n", dentry->d_name.name);
+	err = topmost_dentry->d_inode->i_op->fallthru(topmost_dentry->d_inode,
+						      dentry);
+	/* FIXME */
+	BUG_ON(err);
+	/*
+	 * At this point, we have a negative dentry marked as fallthru
+	 * in the cache.  We could potentially lookup the entry lower
+	 * level file system and turn this into a positive dentry
+	 * right now, but it is not clear that would be a performance
+	 * win and adds more opportunities to fail.
+	 */
+out_dput:
+	dput(dentry);
+out:
+	return 0;
+}
+
+/**
+ * union_copyup_dir - copy up low-level directory entries to topmost dir
+ *
+ * readdir() is difficult to support on union file systems for two
+ * reasons: We must eliminate duplicates and apply whiteouts, and we
+ * must return something in f_pos that lets us restart in the same
+ * place when we return.  Our solution is to, on first readdir() of
+ * the directory, copy up all visible entries from the low-level file
+ * systems and mark the entries that refer to low-level file system
+ * objects as "fallthru" entries.
+ */
+
+int union_copyup_dir(struct path *topmost_path)
+{
+	struct dentry *topmost_dentry = topmost_path->dentry;
+	struct path path = *topmost_path;
+	int res = 0;
+
+	/*
+	 * Skip opaque dirs.
+	 */
+	if (IS_OPAQUE(topmost_dentry->d_inode))
+		return 0;
+
+	res = mnt_want_write(topmost_path->mnt);
+	if (res)
+		return res;
+
+	/*
+	 * Mark this dir opaque to show that we have already copied up
+	 * the lower entries.  Only fallthru entries pass through to
+	 * the underlying file system.
+	 *
+	 * XXX Deal with the lower file system changing.  This could
+	 * be through running a tool over the top level file system to
+	 * make directories transparent again, or we could check the
+	 * mtime of the underlying directory.
+	 */
+
+	topmost_dentry->d_inode->i_flags |= S_OPAQUE;
+	mark_inode_dirty(topmost_dentry->d_inode);
+
+	/*
+	 * Loop through each dir on each level copying up the entries
+	 * to the topmost.
+	 */
+
+	/* Don't drop the caller's reference to the topmost path */
+	path_get(&path);
+	while (follow_union_down(&path.mnt, &path.dentry)) {
+		struct file * ftmp;
+		struct inode * inode;
+
+		/* XXX Permit fallthrus on lower-level? Would need to
+		 * pass in opaque flag to union_copyup_dir_one() and
+		 * only copy up fallthru entries there.  We allow
+		 * fallthrus in lower level opaque directories on
+		 * lookup, so for consistency we should do one or the
+		 * other in both places. */
+		if (IS_OPAQUE(path.dentry->d_inode))
+			break;
+
+		/* dentry_open() doesn't get a path reference itself */
+		path_get(&path);
+		ftmp = dentry_open(path.dentry, path.mnt,
+				   O_RDONLY | O_DIRECTORY | O_NOATIME,
+				   current_cred());
+		if (IS_ERR(ftmp)) {
+			printk (KERN_ERR "unable to open dir %s for "
+				"directory copyup: %ld\n",
+				path.dentry->d_name.name, PTR_ERR(ftmp));
+			continue;
+		}
+
+		inode = path.dentry->d_inode;
+		mutex_lock(&inode->i_mutex);
+
+		res = -ENOENT;
+		if (IS_DEADDIR(inode))
+			goto out_fput;
+		/*
+		 * Read the whole directory, calling our directory
+		 * entry copyup function on each entry.  Pass in the
+		 * topmost dentry as our private data so we can create
+		 * new entries in the topmost directory.
+		 */
+		res = ftmp->f_op->readdir(ftmp, topmost_dentry,
+					  union_copyup_dir_one);
+out_fput:
+		mutex_unlock(&inode->i_mutex);
+		fput(ftmp);
+
+		if (res)
+			break;
+	}
+	path_put(&path);
+	mnt_drop_write(topmost_path->mnt);
+	return res;
+}
diff --git a/include/linux/union.h b/include/linux/union.h
index 405baa9..a0656b3 100644
--- a/include/linux/union.h
+++ b/include/linux/union.h
@@ -57,6 +57,7 @@ extern struct dentry *union_create_topmost(struct nameidata *, struct qstr *,
 					   struct path *);
 extern int __union_copyup(struct path *, struct nameidata *, struct path *);
 extern int union_copyup(struct nameidata *, int);
+extern int union_copyup_dir(struct path *path);
 
 #else /* CONFIG_UNION_MOUNT */
 
@@ -74,6 +75,7 @@ extern int union_copyup(struct nameidata *, int);
 #define union_create_topmost(x, y, z)	({ BUG(); (NULL); })
 #define __union_copyup(x, y, z)		({ BUG(); (0); })
 #define union_copyup(x, y)		({ (0); })
+#define union_copyup_dir(x)		({ BUG(); (0); })
 
 #endif	/* CONFIG_UNION_MOUNT */
 #endif	/* __KERNEL__ */
-- 
1.6.3.3

  reply	other threads:[~2009-10-21 19:19 UTC|newest]

Thread overview: 100+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-10-21 19:18 [RFC PATCH 00/40] Writable overlays (union mounts) Valerie Aurora
2009-10-21 19:18 ` [PATCH 01/41] VFS: BUG() if somebody tries to rehash an already hashed dentry Valerie Aurora
2009-10-21 19:19   ` [PATCH 02/41] VFS: propagate mnt_flags into do_loopback Valerie Aurora
2009-10-21 19:19     ` [PATCH 03/41] VFS: Make lookup_hash() return a struct path Valerie Aurora
2009-10-21 19:19       ` [PATCH 04/41] VFS: Remove unnecessary micro-optimization in cached_lookup() Valerie Aurora
2009-10-21 19:19         ` [PATCH 05/41] VFS: Make real_lookup() return a struct path Valerie Aurora
2009-10-21 19:19           ` [PATCH 06/41] VFS: Introduce dput() variant that maintains a kill-list Valerie Aurora
2009-10-21 19:19             ` [PATCH 07/41] VFS: Add read-only users count to superblock Valerie Aurora
2009-10-21 19:19               ` [PATCH 08/41] Don't replace nameidata path when following links Valerie Aurora
2009-10-21 19:19                 ` [PATCH 09/41] whiteout: Don't return information about whiteouts to userspace Valerie Aurora
2009-10-21 19:19                   ` [PATCH 10/41] whiteout: Add vfs_whiteout() and whiteout inode operation Valerie Aurora
2009-10-21 19:19                     ` [PATCH 11/41] whiteout: Set S_OPAQUE inode flag when creating directories Valerie Aurora
2009-10-21 19:19                       ` [PATCH 12/41] union-mount: Allow removal of a directory Valerie Aurora
2009-10-21 19:19                         ` [PATCH 13/41] whiteout: tmpfs whiteout support Valerie Aurora
2009-10-21 19:19                           ` [PATCH 14/41] whiteout: Split of ext2_append_link() from ext2_add_link() Valerie Aurora
2009-10-21 19:19                             ` [PATCH 15/41] whiteout: ext2 whiteout support Valerie Aurora
2009-10-21 19:19                               ` [PATCH 16/41] whiteout: jffs2 " Valerie Aurora
2009-10-21 19:19                                 ` [PATCH 17/41] whiteout: Add path_whiteout() helper Valerie Aurora
2009-10-21 19:19                                   ` [PATCH 18/41] union-mount: Documentation Valerie Aurora
2009-10-21 19:19                                     ` [PATCH 19/41] union-mount: Introduce MNT_UNION and MS_UNION flags Valerie Aurora
2009-10-21 19:19                                       ` [PATCH 20/41] union-mount: Introduce union_mount structure Valerie Aurora
2009-10-21 19:19                                         ` [PATCH 21/41] union-mount: Drive the union cache via dcache Valerie Aurora
2009-10-21 19:19                                           ` [PATCH 22/41] union-mount: Some checks during namespace changes Valerie Aurora
2009-10-21 19:19                                             ` [PATCH 23/41] union-mount: Changes to the namespace handling Valerie Aurora
2009-10-21 19:19                                               ` [PATCH 24/41] union-mount: Make lookup work for union-mounted file systems Valerie Aurora
2009-10-21 19:19                                                 ` [PATCH 25/41] union-mount: stop lookup when directory has S_OPAQUE flag set Valerie Aurora
2009-10-21 19:19                                                   ` [PATCH 26/41] union-mount: stop lookup when finding a whiteout Valerie Aurora
2009-10-21 19:19                                                     ` [PATCH 27/41] union-mount: in-kernel file copy between union mounted filesystems Valerie Aurora
2009-10-21 19:19                                                       ` [PATCH 28/41] union-mount: call do_whiteout() on unlink and rmdir Valerie Aurora
2009-10-21 19:19                                                         ` [PATCH 29/41] union-mount: Always create topmost directory on open Valerie Aurora
2009-10-21 19:19                                                           ` [PATCH 30/41] fallthru: Basic fallthru definitions Valerie Aurora
2009-10-21 19:19                                                             ` [PATCH 31/41] fallthru: Support for fallthru entries in union mount lookup Valerie Aurora
2009-10-21 19:19                                                               ` [PATCH 32/41] fallthru: ext2 fallthru support Valerie Aurora
2009-10-21 19:19                                                                 ` [PATCH 33/41] fallthru: jffs2 " Valerie Aurora
2009-10-21 19:19                                                                   ` [PATCH 34/41] fallthru: tmpfs " Valerie Aurora
2009-10-21 19:19                                                                     ` Valerie Aurora [this message]
2009-10-21 19:19                                                                       ` [PATCH 36/41] union-mount: Increment read-only users count for read-only layer Valerie Aurora
2009-10-21 19:19                                                                         ` [PATCH 37/41] union-mount: Check read-only/read-write status of layers Valerie Aurora
2009-10-21 19:19                                                                           ` [PATCH 38/41] union-mount: Make pivot_root work with union mounts Valerie Aurora
2009-10-21 19:19                                                                             ` [PATCH 39/41] union-mount: Ignore read-only file system in permission checks Valerie Aurora
2009-10-21 19:19                                                                               ` [PATCH 40/41] union-mount: Make truncate work in all its glorious UNIX variations Valerie Aurora
2009-10-21 19:19                                                                                 ` [PATCH 41/41] union-mount: Add support for rename by __union_copyup() Valerie Aurora
2009-12-01  4:57                                                                                   ` Erez Zadok
2009-12-01  4:50                                                                                 ` [PATCH 40/41] union-mount: Make truncate work in all its glorious UNIX variations Erez Zadok
2009-12-01  4:34                                                                               ` [PATCH 39/41] union-mount: Ignore read-only file system in permission checks Erez Zadok
2009-12-01  4:26                                                                             ` [PATCH 38/41] union-mount: Make pivot_root work with union mounts Erez Zadok
2009-12-01  4:18                                                                       ` [PATCH 35/41] union-mount: Copy up directory entries on first readdir() Erez Zadok
2009-12-01  4:17                                                                     ` [PATCH 34/41] fallthru: tmpfs fallthru support Erez Zadok
2009-12-01  4:17                                                                   ` [PATCH 33/41] fallthru: jffs2 " Erez Zadok
2009-12-01  4:17                                                                 ` [PATCH 32/41] fallthru: ext2 " Erez Zadok
2009-12-01  4:15                                                               ` [PATCH 31/41] fallthru: Support for fallthru entries in union mount lookup Erez Zadok
2009-12-01  4:14                                                             ` [PATCH 30/41] fallthru: Basic fallthru definitions Erez Zadok
2009-12-01  4:14                                                           ` [PATCH 29/41] union-mount: Always create topmost directory on open Erez Zadok
2009-12-01  4:13                                                       ` [PATCH 27/41] union-mount: in-kernel file copy between union mounted filesystems Erez Zadok
2009-12-01  4:11                                                     ` [PATCH 26/41] union-mount: stop lookup when finding a whiteout Erez Zadok
2009-12-01  4:10                                                   ` [PATCH 25/41] union-mount: stop lookup when directory has S_OPAQUE flag set Erez Zadok
2009-12-01  4:10                                                 ` [PATCH 24/41] union-mount: Make lookup work for union-mounted file systems Erez Zadok
2009-11-30  9:15                                               ` [PATCH 23/41] union-mount: Changes to the namespace handling Erez Zadok
2009-11-30  9:04                                             ` [PATCH 22/41] union-mount: Some checks during namespace changes Erez Zadok
2009-11-30  8:57                                           ` [PATCH 21/41] union-mount: Drive the union cache via dcache Erez Zadok
2009-11-30  8:46                                         ` [PATCH 20/41] union-mount: Introduce union_mount structure Erez Zadok
2010-01-26 22:38                                           ` Valerie Aurora
2009-11-30  8:02                                       ` [PATCH 19/41] union-mount: Introduce MNT_UNION and MS_UNION flags Erez Zadok
2010-01-26 20:03                                         ` Valerie Aurora
2009-12-01  5:37                                     ` [PATCH 18/41] union-mount: Documentation Erez Zadok
2009-11-30  7:57                                   ` [PATCH 17/41] whiteout: Add path_whiteout() helper Erez Zadok
2010-01-26 20:02                                     ` Valerie Aurora
2009-10-21 22:50                                 ` [PATCH 16/41] whiteout: jffs2 whiteout support David Woodhouse
2009-10-27  2:21                                   ` Valerie Aurora
2009-11-30  7:51                                 ` Erez Zadok
2010-01-26 19:52                                   ` Valerie Aurora
2009-10-21 21:17                               ` [PATCH 15/41] whiteout: ext2 " Andreas Dilger
2009-10-27  2:14                                 ` Valerie Aurora
2009-11-30  7:45                               ` Erez Zadok
2009-11-30  6:32                             ` [PATCH 14/41] whiteout: Split of ext2_append_link() from ext2_add_link() Erez Zadok
2009-11-30  6:26                           ` [PATCH 13/41] whiteout: tmpfs whiteout support Erez Zadok
2010-01-21  2:02                             ` Valerie Aurora
2009-11-30  6:13                         ` [PATCH 12/41] union-mount: Allow removal of a directory Erez Zadok
2010-01-21  0:52                           ` Valerie Aurora
2009-10-27 14:36                     ` [PATCH 10/41] whiteout: Add vfs_whiteout() and whiteout inode operation Eric Paris
2009-10-27 21:22                       ` Valerie Aurora
2009-11-30  3:04                     ` Erez Zadok
2010-01-21  0:35                       ` Valerie Aurora
2009-11-30  2:53                   ` [PATCH 09/41] whiteout: Don't return information about whiteouts to userspace Erez Zadok
2010-01-21  0:19                     ` Valerie Aurora
2009-11-30  2:44                 ` [PATCH 08/41] Don't replace nameidata path when following links Erez Zadok
2009-11-30  2:33               ` [PATCH 07/41] VFS: Add read-only users count to superblock Erez Zadok
2009-11-30  2:28             ` [PATCH 06/41] VFS: Introduce dput() variant that maintains a kill-list Erez Zadok
2010-01-20 23:31               ` Valerie Aurora
2009-11-30  2:11           ` [PATCH 05/41] VFS: Make real_lookup() return a struct path Erez Zadok
2009-11-30  2:07         ` [PATCH 04/41] VFS: Remove unnecessary micro-optimization in cached_lookup() Erez Zadok
2009-12-10 21:25           ` Valerie Aurora
2009-11-30  2:02       ` [PATCH 03/41] VFS: Make lookup_hash() return a struct path Erez Zadok
2009-12-10 21:23         ` Valerie Aurora
2009-11-30  6:04       ` Erez Zadok
2009-12-10 21:24         ` Valerie Aurora
2009-11-30  1:43   ` [PATCH 01/41] VFS: BUG() if somebody tries to rehash an already hashed dentry Erez Zadok
2009-12-10 20:20     ` Valerie Aurora
2009-10-22  2:44 ` [RFC PATCH 00/40] Writable overlays (union mounts) hooanon05
2009-10-27  2:23   ` Valerie Aurora

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1256152779-10054-36-git-send-email-vaurora@redhat.com \
    --to=vaurora@redhat.com \
    --cc=apw@canonical.com \
    --cc=hch@infradead.org \
    --cc=jblunck@suse.de \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=nbd@openwrt.org \
    --cc=scott@canonica \
    --cc=viro@zeniv.linux.org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).