[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20120221180103.25235.24872.stgit@warthog.procyon.org.uk>
Date: Tue, 21 Feb 2012 18:01:03 +0000
From: David Howells <dhowells@...hat.com>
To: linux-fsdevel@...r.kernel.org, viro@...IV.linux.org.uk,
valerie.aurora@...il.com
Cc: linux-kernel@...r.kernel.org, David Howells <dhowells@...hat.com>
Subject: [PATCH 28/73] union-mount: Add union_create_topmost_dir() [ver #2]
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.
Original-author: Valerie Aurora <vaurora@...hat.com>
Signed-off-by: David Howells <dhowells@...hat.com>
---
fs/union.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
fs/union.h | 9 +++++
2 files changed, 123 insertions(+), 0 deletions(-)
diff --git a/fs/union.c b/fs/union.c
index 1e459b0..f183051 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"
@@ -88,3 +90,115 @@ int union_add_dir(struct path *topmost, struct path *lower, unsigned layer)
*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 f39c88d..e918a04 100644
--- a/fs/union.h
+++ b/fs/union.h
@@ -58,6 +58,8 @@ static inline bool IS_DIR_UNIONED(struct dentry *dentry)
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)
@@ -85,4 +87,11 @@ int union_add_dir(struct path *topmost, struct path *lower, unsigned layer)
return 0;
}
+static inline int union_create_topmost_dir(struct path *parent, struct qstr *name,
+ struct path *topmost, struct path *lower)
+{
+ BUG();
+ return 0;
+}
+
#endif /* CONFIG_UNION_MOUNT */
--
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