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@xxxxxxxxxx> Signed-off-by: David Howells <dhowells@xxxxxxxxxx> --- 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-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html