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