From: Valerie Aurora <vaurora@redhat.com>
To: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org,
Valerie Aurora <vaurora@redhat.com>,
Felix Fietkau <nbd@openwrt.org>
Subject: [PATCH 23/35] union-mount: Copy up directory entries on first readdir()
Date: Thu, 15 Apr 2010 16:04:30 -0700 [thread overview]
Message-ID: <1271372682-21225-24-git-send-email-vaurora@redhat.com> (raw)
In-Reply-To: <1271372682-21225-23-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.
Signed-off-by: Valerie Aurora <vaurora@redhat.com>
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
---
fs/readdir.c | 9 +++
fs/union.c | 160 +++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/union.h | 2 +
3 files changed, 171 insertions(+), 0 deletions(-)
diff --git a/fs/readdir.c b/fs/readdir.c
index 3a48491..da71515 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,16 @@ int vfs_readdir(struct file *file, filldir_t filler, void *buf)
res = -ENOENT;
if (!IS_DEADDIR(inode)) {
+ if (IS_UNIONED_DIR(&file->f_path) && !IS_OPAQUE(inode)) {
+ 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 8ad9de7..e2384ad 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
@@ -23,6 +24,8 @@
#include <linux/slab.h>
#include <linux/union.h>
#include <linux/namei.h>
+#include <linux/file.h>
+#include <linux/security.h>
/*
* This is borrowed from fs/inode.c. The hashtable for lookups. Somebody
@@ -473,3 +476,160 @@ out:
mnt_drop_write(parent->mnt);
return dentry;
}
+
+/**
+ * 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_WARNING "%s: error looking up %s\n", __func__,
+ dentry->d_name.name);
+ err = PTR_ERR(dentry);
+ 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))
+ 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.
+ */
+ err = topmost_dentry->d_inode->i_op->fallthru(topmost_dentry->d_inode,
+ dentry);
+out_dput:
+ dput(dentry);
+out:
+ return err;
+}
+
+/**
+ * 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.
+ *
+ * Locking strategy: We hold the topmost dir's i_mutex on entry. We
+ * grab the i_mutex on lower directories one by one. So the locking
+ * order is:
+ *
+ * Writable/topmost layers > Read-only/lower layers
+ *
+ * So there is no problem with lock ordering for union stacks with
+ * multiple lower layers. E.g.:
+ *
+ * (topmost) A->B->C (bottom)
+ * (topmost) D->C->B (bottom)
+ *
+ * (Not that we support more than two layers at the moment.)
+ */
+
+int union_copyup_dir(struct path *topmost_path)
+{
+ struct dentry *topmost_dentry = topmost_path->dentry;
+ struct path path = *topmost_path;
+ int res = 0;
+
+ BUG_ON(IS_OPAQUE(topmost_dentry->d_inode));
+
+ 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.
+ */
+ topmost_dentry->d_inode->i_flags |= S_OPAQUE;
+ mark_inode_dirty(topmost_dentry->d_inode);
+
+ path_get(&path);
+ while (union_down_one(&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));
+ path_put(&path);
+ 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 189a84d..66deeb2 100644
--- a/include/linux/union.h
+++ b/include/linux/union.h
@@ -51,6 +51,7 @@ extern struct dentry * union_create_topmost_dir(struct path *, struct qstr *,
struct path *);
extern int attach_mnt_union(struct vfsmount *, struct vfsmount *);
extern void detach_mnt_union(struct vfsmount *);
+extern int union_copyup_dir(struct path *);
#else /* CONFIG_UNION_MOUNT */
@@ -64,6 +65,7 @@ extern void detach_mnt_union(struct vfsmount *);
#define union_create_topmost_dir(x, y, z) ({ BUG(); (NULL); })
#define attach_mnt_union(x, y) do { } while (0)
#define detach_mnt_union(x) do { } while (0)
+#define union_copyup_dir(x) ({ BUG(); (0); })
#endif /* CONFIG_UNION_MOUNT */
#endif /* __KERNEL__ */
--
1.6.3.3
next prev parent reply other threads:[~2010-04-15 23:11 UTC|newest]
Thread overview: 69+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-04-15 23:04 [PATCH 00/35] Union mounts - everything but the xattrs Valerie Aurora
2010-04-15 23:04 ` [PATCH 01/35] VFS: Make lookup_hash() return a struct path Valerie Aurora
2010-04-15 23:04 ` [PATCH 02/35] VFS: Add read-only users count to superblock Valerie Aurora
[not found] ` <1271372682-21225-4-git-send-email-vaurora@redhat.com>
2010-04-15 23:04 ` [PATCH 04/35] whiteout/NFSD: Don't return information about whiteouts to userspace Valerie Aurora
2010-04-15 23:04 ` [PATCH 05/35] whiteout: Add vfs_whiteout() and whiteout inode operation Valerie Aurora
2010-04-15 23:04 ` [PATCH 06/35] whiteout: Set S_OPAQUE inode flag when creating directories Valerie Aurora
2010-04-15 23:04 ` [PATCH 07/35] whiteout: Allow removal of a directory with whiteouts Valerie Aurora
2010-04-15 23:04 ` [PATCH 08/35] whiteout: tmpfs whiteout support Valerie Aurora
2010-04-15 23:04 ` [PATCH 09/35] whiteout: Split of ext2_append_link() from ext2_add_link() Valerie Aurora
2010-04-15 23:04 ` [PATCH 10/35] whiteout: ext2 whiteout support Valerie Aurora
2010-04-15 23:04 ` [PATCH 11/35] whiteout: jffs2 " Valerie Aurora
2010-04-15 23:04 ` [PATCH 12/35] fallthru: Basic fallthru definitions Valerie Aurora
2010-04-15 23:04 ` [PATCH 13/35] fallthru: ext2 fallthru support Valerie Aurora
2010-04-15 23:04 ` [PATCH 14/35] fallthru: jffs2 " Valerie Aurora
2010-04-15 23:04 ` [PATCH 15/35] fallthru: tmpfs " Valerie Aurora
2010-04-15 23:04 ` [PATCH 16/35] union-mount: Writable overlays/union mounts documentation Valerie Aurora
2010-04-15 23:04 ` [PATCH 17/35] union-mount: Introduce MNT_UNION and MS_UNION flags Valerie Aurora
2010-04-15 23:04 ` [PATCH 18/35] union-mount: Introduce union_mount structure and basic operations Valerie Aurora
2010-04-15 23:04 ` [PATCH 19/35] union-mount: Drive the union cache via dcache Valerie Aurora
2010-04-15 23:04 ` [PATCH 20/35] union-mount: Implement union lookup Valerie Aurora
2010-04-15 23:04 ` [PATCH 21/35] union-mount: Support for mounting union mount file systems Valerie Aurora
2010-04-15 23:04 ` [PATCH 22/35] union-mount: Call do_whiteout() on unlink and rmdir in unions Valerie Aurora
2010-04-15 23:04 ` Valerie Aurora [this message]
2010-04-15 23:04 ` [PATCH 24/35] VFS: Split inode_permission() and create path_permission() Valerie Aurora
2010-04-15 23:04 ` [PATCH 25/35] VFS: Create user_path_nd() to lookup both parent and target Valerie Aurora
2010-04-15 23:04 ` [PATCH 26/35] union-mount: In-kernel copyup routines Valerie Aurora
2010-04-15 23:04 ` [PATCH 27/35] union-mount: Implement union-aware access()/faccessat() Valerie Aurora
2010-04-15 23:04 ` [PATCH 28/35] union-mount: Implement union-aware link() Valerie Aurora
2010-04-15 23:04 ` [PATCH 29/35] union-mount: Implement union-aware rename() Valerie Aurora
2010-04-15 23:04 ` [PATCH 30/35] union-mount: Implement union-aware writable open() Valerie Aurora
2010-04-15 23:04 ` [PATCH 31/35] union-mount: Implement union-aware chown() Valerie Aurora
2010-04-15 23:04 ` [PATCH 32/35] union-mount: Implement union-aware truncate() Valerie Aurora
2010-04-15 23:04 ` [PATCH 33/35] union-mount: Implement union-aware chmod()/fchmodat() Valerie Aurora
2010-04-15 23:04 ` [PATCH 34/35] union-mount: Implement union-aware lchown() Valerie Aurora
2010-04-15 23:04 ` [PATCH 35/35] union-mount: Implement union-aware utimensat() Valerie Aurora
2010-04-20 16:30 ` [PATCH 16/35] union-mount: Writable overlays/union mounts documentation Miklos Szeredi
2010-04-28 20:19 ` Valerie Aurora
2010-04-29 9:33 ` Miklos Szeredi
2010-04-29 20:20 ` Valerie Aurora
2010-05-10 12:57 ` Miklos Szeredi
2010-05-17 19:55 ` Valerie Aurora
2010-04-29 16:10 ` J. R. Okajima
2010-04-19 12:40 ` [PATCH 13/35] fallthru: ext2 fallthru support Jan Blunck
2010-04-19 13:02 ` David Woodhouse
2010-04-19 13:23 ` Jan Blunck
2010-04-19 13:30 ` Jamie Lokier
2010-04-19 14:12 ` Jan Blunck
2010-04-19 14:23 ` Valerie Aurora
2010-04-19 14:53 ` Miklos Szeredi
2010-04-20 21:34 ` Jamie Lokier
2010-04-21 8:42 ` Jan Blunck
2010-04-21 9:22 ` Jamie Lokier
2010-04-21 9:34 ` Miklos Szeredi
2010-04-21 9:52 ` Jamie Lokier
2010-04-21 10:17 ` Miklos Szeredi
2010-04-21 17:36 ` Jamie Lokier
2010-04-21 21:34 ` Valerie Aurora
2010-04-21 21:38 ` Valerie Aurora
2010-04-21 22:10 ` Jamie Lokier
2010-04-22 10:30 ` J. R. Okajima
2010-04-20 21:40 ` Jamie Lokier
2010-04-19 13:03 ` [PATCH 11/35] whiteout: jffs2 whiteout support David Woodhouse
2010-04-19 14:26 ` Valerie Aurora
2010-04-16 15:59 ` [PATCH 04/35] whiteout/NFSD: Don't return information about whiteouts to userspace J. Bruce Fields
2010-04-19 12:37 ` Jan Blunck
2010-04-19 13:54 ` J. Bruce Fields
2010-04-15 23:45 ` [PATCH 03/35] autofs4: Save autofs trigger's vfsmount in super block info Valerie Aurora
2010-04-21 22:06 ` [PATCH 00/35] Union mounts - everything but the xattrs Randy Dunlap
2010-04-21 23:35 ` 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=1271372682-21225-24-git-send-email-vaurora@redhat.com \
--to=vaurora@redhat.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=nbd@openwrt.org \
--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
Powered by JetHome