[PATCH 31/76] union-mount: Add union_create_topmost_dir()

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

 



From: Valerie Aurora <vaurora@xxxxxxxxxx>

Union mounts design requires that the topmost directory exist for
every single directory at the time lookup completes.  This is so that
we don't have to double back and create a whole path's worth of
directories whenever we copy up a file in a directory for the first
time.  This greatly simplifies locking and error handling.
---

 fs/union.c |  114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/union.h |    3 ++
 2 files changed, 117 insertions(+), 0 deletions(-)

diff --git a/fs/union.c b/fs/union.c
index ee4613b..e0b1678 100644
--- a/fs/union.c
+++ b/fs/union.c
@@ -20,6 +20,8 @@
 #include <linux/fs_struct.h>
 #include <linux/slab.h>
 #include <linux/namei.h>
+#include <linux/fsnotify.h>
+#include <linux/xattr.h>
 
 #include "union.h"
 
@@ -87,3 +89,115 @@ int union_add_dir(struct path *topmost, struct path *lower,
 	*path = *lower;
 	return 0;
 }
+
+/**
+ * union_copyup_xattr
+ * @old: dentry of original file
+ * @new: dentry of new copy
+ *
+ * Copy up extended attributes from the original file to the new one.
+ *
+ * XXX - Permissions?  For now, copying up every xattr.
+ */
+static int union_copyup_xattr(struct dentry *old, struct dentry *new)
+{
+	ssize_t list_size, size;
+	char *buf, *name, *value;
+	int error;
+
+	/* Check for xattr support */
+	if (!old->d_inode->i_op->getxattr ||
+	    !new->d_inode->i_op->getxattr)
+		return 0;
+
+	/* Find out how big the list of xattrs is */
+	list_size = vfs_listxattr(old, NULL, 0);
+	if (list_size <= 0)
+		return list_size;
+
+	/* Allocate memory for the list */
+	buf = kzalloc(list_size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	/* Allocate memory for the xattr's value */
+	error = -ENOMEM;
+	value = kmalloc(XATTR_SIZE_MAX, GFP_KERNEL);
+	if (!value)
+		goto out;
+
+	/* Actually get the list of xattrs */
+	list_size = vfs_listxattr(old, buf, list_size);
+	if (list_size <= 0) {
+		error = list_size;
+		goto out_free_value;
+	}
+
+	for (name = buf; name < (buf + list_size); name += strlen(name) + 1) {
+		/* XXX Locking? old is on read-only fs */
+		size = vfs_getxattr(old, name, value, XATTR_SIZE_MAX);
+		if (size <= 0) {
+			error = size;
+			goto out_free_value;
+		}
+		/* XXX do we really need to check for size overflow? */
+		/* XXX locks new dentry, lock ordering problems? */
+		error = vfs_setxattr(new, name, value, size, 0);
+		if (error)
+			goto out_free_value;
+	}
+
+out_free_value:
+	kfree(value);
+out:
+	kfree(buf);
+	return error;
+}
+
+/**
+ * union_create_topmost_dir - Create a matching dir in the topmost file system
+ * @parent - parent of target on topmost layer
+ * @name - name of target
+ * @topmost - path of target on topmost layer
+ * @lower - path of source on lower layer
+ *
+ * As we lookup each directory on the lower layer of a union, we create a
+ * matching directory on the topmost layer if it does not already exist.
+ *
+ * We don't use vfs_mkdir() for a few reasons: don't want to do the security
+ * check, don't want to make the dir opaque, don't need to sanitize the mode.
+ *
+ * XXX - owner is wrong, set credentials properly
+ * XXX - rmdir() directory on failure of xattr copyup
+ * XXX - not atomic w/ respect to crash
+ */
+int union_create_topmost_dir(struct path *parent, struct qstr *name,
+			     struct path *topmost, struct path *lower)
+{
+	struct inode *dir = parent->dentry->d_inode;
+	int mode = lower->dentry->d_inode->i_mode;
+	int error;
+
+	BUG_ON(topmost->dentry->d_inode);
+
+	/* XXX - Do we even need to check this? */
+	if (!dir->i_op->mkdir)
+		return -EPERM;
+
+	error = mnt_want_write(parent->mnt);
+	if (error)
+		return error;
+
+	error = dir->i_op->mkdir(dir, topmost->dentry, mode);
+	if (error)
+		goto out;
+
+	error = union_copyup_xattr(lower->dentry, topmost->dentry);
+	if (error)
+		dput(topmost->dentry);
+
+	fsnotify_mkdir(dir, topmost->dentry);
+out:
+	mnt_drop_write(parent->mnt);
+	return error;
+}
diff --git a/fs/union.h b/fs/union.h
index 51a851b..db7c70c 100644
--- a/fs/union.h
+++ b/fs/union.h
@@ -53,6 +53,8 @@ struct union_stack {
 
 extern void d_free_unions(struct dentry *);
 extern int union_add_dir(struct path *, struct path *, unsigned int);
+extern int union_create_topmost_dir(struct path *, struct qstr *, struct path *,
+				    struct path *);
 
 static inline
 struct path *union_find_dir(struct dentry *dentry, unsigned int layer)
@@ -68,6 +70,7 @@ struct path *union_find_dir(struct dentry *dentry, unsigned int layer)
 #define d_free_unions(x)		do { } while (0)
 #define union_add_dir(x, y, z)		({ BUG(); (0); })
 #define union_find_dir(x, y)		({ BUG(); (NULL); })
+#define union_create_topmost_dir(w, x, y, z)	({ BUG(); (0); })
 
 #endif	/* CONFIG_UNION_MOUNT */
 #endif	/* __KERNEL__ */

--
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