[PATCH 19/38] union-mount: Introduce union_dir structure and basic operations

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch adds the basic structures and operations of VFS-based union
mounts (but not the ability to mount or lookup unioned file systems).
Each directory in a unioned file system has an associated union stack
created when the directory is first looked up.  The union stack is a
union_dir structure kept in a hash table indexed by mount and dentry
of the directory; thus, specific paths are unioned, not dentries
alone.  The union_dir keeps a pointer to the upper path and the lower
path and can be looked up by either path.  Currently only two layers
are supported, but the union_dir struct is flexible enough to allow
more than two layers.

This particular version of union mounts is based on ideas by Jan
Blunck, Bharata Rao, and many others.

Signed-off-by: Valerie Aurora <vaurora@xxxxxxxxxx>
---
 fs/Kconfig             |   13 +++++
 fs/Makefile            |    1 +
 fs/dcache.c            |    3 +
 fs/union.c             |  119 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/union.h             |   66 ++++++++++++++++++++++++++
 include/linux/dcache.h |    4 +-
 include/linux/fs.h     |    1 +
 7 files changed, 206 insertions(+), 1 deletions(-)
 create mode 100644 fs/union.c
 create mode 100644 fs/union.h

diff --git a/fs/Kconfig b/fs/Kconfig
index 5f85b59..f99c3a9 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -59,6 +59,19 @@ source "fs/notify/Kconfig"
 
 source "fs/quota/Kconfig"
 
+config UNION_MOUNT
+       bool "Union mounts (writable overlasy) (EXPERIMENTAL)"
+       depends on EXPERIMENTAL
+       help
+         Union mounts allow you to mount a transparent writable
+	 layer over a read-only file system, for example, an ext3
+	 partition on a hard drive over a CD-ROM root file system
+	 image.
+
+	 See <file:Documentation/filesystems/union-mounts.txt> for details.
+
+	 If unsure, say N.
+
 source "fs/autofs/Kconfig"
 source "fs/autofs4/Kconfig"
 source "fs/fuse/Kconfig"
diff --git a/fs/Makefile b/fs/Makefile
index 97f340f..1949af2 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_NFS_COMMON)	+= nfs_common/
 obj-$(CONFIG_GENERIC_ACL)	+= generic_acl.o
 
 obj-y				+= quota/
+obj-$(CONFIG_UNION_MOUNT)	+= union.o
 
 obj-$(CONFIG_PROC_FS)		+= proc/
 obj-y				+= partitions/
diff --git a/fs/dcache.c b/fs/dcache.c
index 1575af4..54ff5a3 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -960,6 +960,9 @@ struct dentry *d_alloc(struct dentry * parent, const struct qstr *name)
 	INIT_LIST_HEAD(&dentry->d_lru);
 	INIT_LIST_HEAD(&dentry->d_subdirs);
 	INIT_LIST_HEAD(&dentry->d_alias);
+#ifdef CONFIG_UNION_MOUNT
+	dentry->d_union_dir = NULL;
+#endif
 
 	if (parent) {
 		dentry->d_parent = dget(parent);
diff --git a/fs/union.c b/fs/union.c
new file mode 100644
index 0000000..02abb7c
--- /dev/null
+++ b/fs/union.c
@@ -0,0 +1,119 @@
+ /*
+ * VFS-based union mounts for Linux
+ *
+ * Copyright (C) 2004-2007 IBM Corporation, IBM Deutschland Entwicklung GmbH.
+ * Copyright (C) 2007-2009 Novell Inc.
+ * Copyright (C) 2009-2010 Red Hat, Inc.
+ *
+ *   Author(s): Jan Blunck (j.blunck@xxxxxxxxxxxxx)
+ *              Valerie Aurora <vaurora@xxxxxxxxxx>
+ *
+ * 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 Software Foundation; version 2
+ * of the License.
+ */
+
+#include <linux/bootmem.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/fs_struct.h>
+#include <linux/slab.h>
+
+#include "union.h"
+
+static struct kmem_cache *union_cache;
+
+static int __init init_union(void)
+{
+	union_cache = KMEM_CACHE(union_dir, SLAB_PANIC | SLAB_MEM_SPREAD);
+	return 0;
+}
+
+fs_initcall(init_union);
+
+/**
+ * union_alloc - allocate a union_dir
+ *
+ * @path: path of directory underneath another directory
+ *
+ * Allocate a union_dir for this directory.  We only allocate
+ * union_dirs for the second and lower layers - the read-only layers.
+ * Top-level dentries don't have a union_dir, just a pointer to the
+ * union_dir of the directory in the layer below it.  u_lower is
+ * initialized to NULL by default.  If there is another layer below
+ * this and a matching directory in the layer, then we allocate a
+ * union_dir for it and then set u_lower of the above union_dir to
+ * point to it.
+ */
+
+static struct union_dir *union_alloc(struct path *path)
+{
+	struct union_dir *ud;
+
+	BUG_ON(!S_ISDIR(path->dentry->d_inode->i_mode));
+
+	ud = kmem_cache_alloc(union_cache, GFP_ATOMIC);
+	if (!ud)
+		return NULL;
+
+	ud->u_this = *path;
+	ud->u_lower = NULL;
+
+	return ud;
+}
+
+static void union_put(struct union_dir *ud)
+{
+	path_put(&ud->u_this);
+	kmem_cache_free(union_cache, ud);
+}
+
+/**
+ * union_add_dir - Add another layer to a unioned directory
+ *
+ * @upper - directory in the previous layer
+ * @lower - directory in the current layer
+ * @next_ud - location of pointer to this union_dir
+ *
+ * Must have a reference (i.e., call path_get()) to @lower before
+ * passing to this function.
+ */
+
+int union_add_dir(struct path *upper, struct path *lower,
+		  struct union_dir **next_ud)
+{
+	struct union_dir *ud;
+
+	BUG_ON(*next_ud != NULL);
+
+	ud = union_alloc(lower);
+	if (!ud)
+		return -ENOMEM;
+	*next_ud = ud;
+
+	return 0;
+}
+
+/**
+ * d_free_unions - free all unions for this dentry
+ *
+ * @dentry - topmost dentry in the union stack to remove
+ *
+ * This must be called when freeing a dentry.
+ */
+void d_free_unions(struct dentry *dentry)
+{
+	struct union_dir *this, *next;
+
+	this = dentry->d_union_dir;
+
+	while (this != NULL) {
+		next = this->u_lower;
+		union_put(this);
+		this = next;
+	}
+	dentry->d_union_dir = NULL;
+}
diff --git a/fs/union.h b/fs/union.h
new file mode 100644
index 0000000..04efc1f
--- /dev/null
+++ b/fs/union.h
@@ -0,0 +1,66 @@
+ /*
+ * VFS-based union mounts for Linux
+ *
+ * Copyright (C) 2004-2007 IBM Corporation, IBM Deutschland Entwicklung GmbH.
+ * Copyright (C) 2007-2009 Novell Inc.
+ * Copyright (C) 2009-2010 Red Hat, Inc.
+ *
+ *   Author(s): Jan Blunck (j.blunck@xxxxxxxxxxxxx)
+ *              Valerie Aurora <vaurora@xxxxxxxxxx>
+ *
+ * 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 Software Foundation; version 2
+ * of the License.
+ */
+#ifndef __LINUX_UNION_H
+#define __LINUX_UNION_H
+#ifdef __KERNEL__
+
+#ifdef CONFIG_UNION_MOUNT
+
+/*
+ * WARNING! Confusing terminology alert.
+ *
+ * Note that the directions "up" and "down" in union mounts are the
+ * opposite of "up" and "down" in normal VFS operation terminology.
+ * "up" in the rest of the VFS means "towards the root of the mount
+ * tree."  If you mount B on top of A, following B "up" will get you
+ * A.  In union mounts, "up" means "towards the most recently mounted
+ * layer of the union stack."  If you union mount B on top of A,
+ * following A "up" will get you to B.  Another way to put it is that
+ * "up" in the VFS means going from this mount towards the direction
+ * of its mnt->mnt_parent pointer, but "up" in union mounts means
+ * going in the opposite direction (until you run out of union
+ * layers).
+ */
+
+/*
+ * The union_dir structure.  Basically just a singly-linked list with
+ * a pointer to the referenced dentry, whose head is d_union_dir in
+ * the dentry of the topmost directory.  We can't link this list
+ * purely through list elements in the dentry because lower layer
+ * dentries can be part of multiple union stacks.  However, the
+ * topmost dentry is only part of one union stack.  So we point at the
+ * lower layer dentries through a linked list rooted in the topmost
+ * dentry.
+ */
+struct union_dir {
+	struct path u_this;		/* this is me */
+	struct union_dir *u_lower;	/* this is what I overlay */
+};
+
+#define IS_MNT_UNION(mnt)	((mnt)->mnt_flags & MNT_UNION)
+
+extern int union_add_dir(struct path *, struct path *, struct union_dir **);
+extern void d_free_unions(struct dentry *);
+
+#else /* CONFIG_UNION_MOUNT */
+
+#define IS_MNT_UNION(x)			(0)
+#define union_add_dir(x, y, z)		({ BUG(); (NULL); })
+#define d_free_unions(x)		do { } while (0)
+
+#endif	/* CONFIG_UNION_MOUNT */
+#endif	/* __KERNEL__ */
+#endif	/* __LINUX_UNION_H */
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index 01d6011..509a637 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -100,7 +100,9 @@ struct dentry {
 	struct hlist_node d_hash;	/* lookup hash list */
 	struct dentry *d_parent;	/* parent directory */
 	struct qstr d_name;
-
+#ifdef CONFIG_UNION_MOUNT
+	struct union_dir *d_union_dir;	/* head of union stack */
+#endif
 	struct list_head d_lru;		/* LRU list */
 	/*
 	 * d_child and d_rcu can share memory
diff --git a/include/linux/fs.h b/include/linux/fs.h
index dbd9881..32e6988 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1395,6 +1395,7 @@ struct super_block {
 	 * read-only.
 	 */
 	int s_hard_readonly_users;
+
 };
 
 extern struct timespec current_fs_time(struct super_block *sb);
-- 
1.6.3.3

--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux