2006-12-04 12:46:06

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: Unionfs: Stackable namespace unification filesystem

The following patches are in a git repo at:

git://git.kernel.org/pub/scm/linux/kernel/git/jsipek/unionfs.git

(master.kernel.org:/pub/scm/linux/kernel/git/jsipek/unionfs.git)

The repository contains the following 35 commits (also available as patches
in replies to this email).

Commits 1..9 (already in -mm):
These patches are already in Andrew Morton's -mm tree.

fsstack: Introduce fsstack_copy_{attr,inode}_*
fsstack: Remove unneeded wrapper
eCryptfs: Use fsstack's generic copy inode attr functions
fsstack: Fix up eCryptfs compilation
struct path: Rename Reiserfs's struct path
struct path: Rename DM's struct path
struct path: Move struct path from fs/namei.c into include/linux
struct path: make eCryptfs a user of struct path
fs/stack.c should #include <linux/fs_stack.h>

Commits 10..11 (additional fixes to the above)
These patches are not yet in -mm, and they fix two things the above
patches missed.

fsstack: Make fsstack_copy_attr_all copy inode size
fsstack: Fix up ecryptfs's fsstack usage

Commits 12..35 (Unionfs):
These patches make up stripped down version of Unionfs. They depend
on fsstack (see above patches).

Unionfs: Documentation
lookup_one_len_nd - lookup_one_len with nameidata argument
Unionfs: Branch management functionality
Unionfs: Common file operations
Unionfs: Copyup Functionality
Unionfs: Dentry operations
Unionfs: File operations
Unionfs: Directory file operations
Unionfs: Directory manipulation helper functions
Unionfs: Inode operations
Unionfs: Lookup helper functions
Unionfs: Main module functions
Unionfs: Readdir state
Unionfs: Rename
Unionfs: Privileged operations workqueue
Unionfs: Handling of stale inodes
Unionfs: Miscellaneous helper functions
Unionfs: Superblock operations
Unionfs: Helper macros/inlines
Unionfs: Internal include file
Unionfs: Include file
Unionfs: Unlink
Unionfs: Kconfig and Makefile
Unionfs: Extended Attributes support


As always, comments are welcomed.

Thanks,

Josef "Jeff" Sipek.
[email protected]


2006-12-04 12:32:34

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 17/35] Unionfs: Dentry operations

From: Josef "Jeff" Sipek <[email protected]>

This patch contains the dentry operations for Unionfs.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
Signed-off-by: David Quigley <[email protected]>
Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/dentry.c | 236 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 236 insertions(+), 0 deletions(-)

diff --git a/fs/unionfs/dentry.c b/fs/unionfs/dentry.c
new file mode 100644
index 0000000..4e2ffa1
--- /dev/null
+++ b/fs/unionfs/dentry.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2003-2006 Erez Zadok
+ * Copyright (c) 2003-2006 Charles P. Wright
+ * Copyright (c) 2005-2006 Josef 'Jeff' Sipek
+ * Copyright (c) 2005-2006 Junjiro Okajima
+ * Copyright (c) 2005 Arun M. Krishnakumar
+ * Copyright (c) 2004-2006 David P. Quigley
+ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair
+ * Copyright (c) 2003 Puja Gupta
+ * Copyright (c) 2003 Harikesavan Krishnan
+ * Copyright (c) 2003-2006 Stony Brook University
+ * Copyright (c) 2003-2006 The Research Foundation of State University of New York
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "union.h"
+
+/* declarations added for "sparse" */
+extern int unionfs_d_revalidate_wrap(struct dentry *dentry,
+ struct nameidata *nd);
+extern void unionfs_d_release(struct dentry *dentry);
+extern void unionfs_d_iput(struct dentry *dentry, struct inode *inode);
+
+/*
+ * returns 1 if valid, 0 otherwise.
+ */
+int unionfs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
+{
+ int valid = 1; /* default is valid (1); invalid is 0. */
+ struct dentry *hidden_dentry;
+ int bindex, bstart, bend;
+ int sbgen, dgen;
+ int positive = 0;
+ int locked = 0;
+ int restart = 0;
+ int interpose_flag;
+
+ struct nameidata lowernd; /* TODO: be gentler to the stack */
+
+ if (nd)
+ memcpy(&lowernd, nd, sizeof(struct nameidata));
+ else
+ memset(&lowernd, 0, sizeof(struct nameidata));
+
+restart:
+ verify_locked(dentry);
+
+ /* if the dentry is unhashed, do NOT revalidate */
+ if (d_deleted(dentry)) {
+ printk(KERN_DEBUG "unhashed dentry being revalidated: %*s\n",
+ dentry->d_name.len, dentry->d_name.name);
+ goto out;
+ }
+
+ BUG_ON(dbstart(dentry) == -1);
+ if (dentry->d_inode)
+ positive = 1;
+ dgen = atomic_read(&UNIONFS_D(dentry)->generation);
+ sbgen = atomic_read(&UNIONFS_SB(dentry->d_sb)->generation);
+ /* If we are working on an unconnected dentry, then there is no
+ * revalidation to be done, because this file does not exist within the
+ * namespace, and Unionfs operates on the namespace, not data.
+ */
+ if (sbgen != dgen) {
+ struct dentry *result;
+ int pdgen;
+
+ unionfs_read_lock(dentry->d_sb);
+ locked = 1;
+
+ /* The root entry should always be valid */
+ BUG_ON(IS_ROOT(dentry));
+
+ /* We can't work correctly if our parent isn't valid. */
+ pdgen = atomic_read(&UNIONFS_D(dentry->d_parent)->generation);
+ if (!restart && (pdgen != sbgen)) {
+ unionfs_read_unlock(dentry->d_sb);
+ locked = 0;
+ /* We must be locked before our parent. */
+ if (!
+ (dentry->d_parent->d_op->
+ d_revalidate(dentry->d_parent, nd))) {
+ valid = 0;
+ goto out;
+ }
+ restart = 1;
+ goto restart;
+ }
+ BUG_ON(pdgen != sbgen);
+
+ /* Free the pointers for our inodes and this dentry. */
+ bstart = dbstart(dentry);
+ bend = dbend(dentry);
+ if (bstart >= 0) {
+ struct dentry *hidden_dentry;
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ hidden_dentry =
+ unionfs_lower_dentry_idx(dentry, bindex);
+ dput(hidden_dentry);
+ }
+ }
+ set_dbstart(dentry, -1);
+ set_dbend(dentry, -1);
+
+ interpose_flag = INTERPOSE_REVAL_NEG;
+ if (positive) {
+ interpose_flag = INTERPOSE_REVAL;
+ mutex_lock(&dentry->d_inode->i_mutex);
+ bstart = ibstart(dentry->d_inode);
+ bend = ibend(dentry->d_inode);
+ if (bstart >= 0) {
+ struct inode *hidden_inode;
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ hidden_inode =
+ unionfs_lower_inode_idx(dentry->d_inode,
+ bindex);
+ iput(hidden_inode);
+ }
+ }
+ kfree(UNIONFS_I(dentry->d_inode)->lower_inodes);
+ UNIONFS_I(dentry->d_inode)->lower_inodes = NULL;
+ ibstart(dentry->d_inode) = -1;
+ ibend(dentry->d_inode) = -1;
+ mutex_unlock(&dentry->d_inode->i_mutex);
+ }
+
+ result = unionfs_lookup_backend(dentry, &lowernd, interpose_flag);
+ if (result) {
+ if (IS_ERR(result)) {
+ valid = 0;
+ goto out;
+ }
+ /* current unionfs_lookup_backend() doesn't return
+ a valid dentry */
+ dput(dentry);
+ dentry = result;
+ }
+
+ if (positive && UNIONFS_I(dentry->d_inode)->stale) {
+ make_stale_inode(dentry->d_inode);
+ d_drop(dentry);
+ valid = 0;
+ goto out;
+ }
+ goto out;
+ }
+
+ /* The revalidation must occur across all branches */
+ bstart = dbstart(dentry);
+ bend = dbend(dentry);
+ BUG_ON(bstart == -1);
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ if (!hidden_dentry || !hidden_dentry->d_op
+ || !hidden_dentry->d_op->d_revalidate)
+ continue;
+
+ if (!hidden_dentry->d_op->d_revalidate(hidden_dentry, nd))
+ valid = 0;
+ }
+
+ if (!dentry->d_inode)
+ valid = 0;
+ if (valid)
+ fsstack_copy_attr_all(dentry->d_inode, unionfs_lower_inode(dentry->d_inode),
+ unionfs_get_nlinks);
+
+out:
+ if (locked)
+ unionfs_read_unlock(dentry->d_sb);
+ return valid;
+}
+
+int unionfs_d_revalidate_wrap(struct dentry *dentry, struct nameidata *nd)
+{
+ int err;
+
+ lock_dentry(dentry);
+ err = unionfs_d_revalidate(dentry, nd);
+ unlock_dentry(dentry);
+
+ return err;
+}
+
+void unionfs_d_release(struct dentry *dentry)
+{
+ int bindex, bstart, bend;
+
+ /* There is no reason to lock the dentry, because we have the only
+ * reference, but the printing functions verify that we have a lock
+ * on the dentry before calling dbstart, etc. */
+ lock_dentry(dentry);
+
+ /* this could be a negative dentry, so check first */
+ if (!UNIONFS_D(dentry)) {
+ printk(KERN_DEBUG "dentry without private data: %.*s",
+ dentry->d_name.len, dentry->d_name.name);
+ goto out;
+ } else if (dbstart(dentry) < 0) {
+ /* this is due to a failed lookup */
+ printk(KERN_DEBUG "dentry without hidden dentries : %.*s",
+ dentry->d_name.len, dentry->d_name.name);
+ goto out_free;
+ }
+
+ /* Release all the hidden dentries */
+ bstart = dbstart(dentry);
+ bend = dbend(dentry);
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ dput(unionfs_lower_dentry_idx(dentry, bindex));
+ mntput(unionfs_lower_mnt_idx(dentry, bindex));
+
+ unionfs_set_lower_dentry_idx(dentry, bindex, NULL);
+ unionfs_set_lower_mnt_idx(dentry, bindex, NULL);
+ }
+ /* free private data (unionfs_dentry_info) here */
+ kfree(UNIONFS_D(dentry)->lower_paths);
+ UNIONFS_D(dentry)->lower_paths = NULL;
+
+out_free:
+ /* No need to unlock it, because it is disappeared. */
+ free_dentry_private_data(UNIONFS_D(dentry));
+ dentry->d_fsdata = NULL; /* just to be safe */
+
+out:
+ return;
+}
+
+struct dentry_operations unionfs_dops = {
+ .d_revalidate = unionfs_d_revalidate_wrap,
+ .d_release = unionfs_d_release,
+};
+
--
1.4.3.3

2006-12-04 12:32:51

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 16/35] Unionfs: Copyup Functionality

From: Josef "Jeff" Sipek <[email protected]>

This patch contains the functions used to perform copyup operations in unionfs.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
Signed-off-by: David Quigley <[email protected]>
Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/copyup.c | 665 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 665 insertions(+), 0 deletions(-)

diff --git a/fs/unionfs/copyup.c b/fs/unionfs/copyup.c
new file mode 100644
index 0000000..0557c02
--- /dev/null
+++ b/fs/unionfs/copyup.c
@@ -0,0 +1,665 @@
+/*
+ * Copyright (c) 2003-2006 Erez Zadok
+ * Copyright (c) 2003-2006 Charles P. Wright
+ * Copyright (c) 2005-2006 Josef 'Jeff' Sipek
+ * Copyright (c) 2005-2006 Junjiro Okajima
+ * Copyright (c) 2005 Arun M. Krishnakumar
+ * Copyright (c) 2004-2006 David P. Quigley
+ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair
+ * Copyright (c) 2003 Puja Gupta
+ * Copyright (c) 2003 Harikesavan Krishnan
+ * Copyright (c) 2003-2006 Stony Brook University
+ * Copyright (c) 2003-2006 The Research Foundation of State University of New York*
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "union.h"
+
+/* Determine the mode based on the copyup flags, and the existing dentry. */
+static int copyup_permissions(struct super_block *sb,
+ struct dentry *old_hidden_dentry,
+ struct dentry *new_hidden_dentry)
+{
+ struct inode *i = old_hidden_dentry->d_inode;
+ struct iattr newattrs;
+ int err;
+
+ newattrs.ia_atime = i->i_atime;
+ newattrs.ia_mtime = i->i_mtime;
+ newattrs.ia_ctime = i->i_ctime;
+
+ newattrs.ia_gid = i->i_gid;
+ newattrs.ia_uid = i->i_uid;
+
+ newattrs.ia_mode = i->i_mode;
+
+ newattrs.ia_valid = ATTR_CTIME | ATTR_ATIME | ATTR_MTIME |
+ ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_FORCE |
+ ATTR_GID | ATTR_UID | ATTR_MODE;
+
+ err = notify_change(new_hidden_dentry, &newattrs);
+
+ return err;
+}
+
+int copyup_dentry(struct inode *dir, struct dentry *dentry,
+ int bstart, int new_bindex,
+ struct file **copyup_file, loff_t len)
+{
+ return copyup_named_dentry(dir, dentry, bstart, new_bindex,
+ dentry->d_name.name,
+ dentry->d_name.len, copyup_file, len);
+}
+
+/* create the new device/file/directory - use copyup_permission to copyup
+ * times, and mode
+ *
+ * if the object being copied up is a regular file, the file is only created,
+ * the contents have to be copied up separately
+ */
+static inline int __copyup_ndentry(struct dentry *old_hidden_dentry,
+ struct dentry *new_hidden_dentry,
+ struct dentry *new_hidden_parent_dentry,
+ char *symbuf)
+{
+ int err = 0;
+ umode_t old_mode = old_hidden_dentry->d_inode->i_mode;
+ struct sioq_args args;
+
+ if (S_ISDIR(old_mode)) {
+ args.mkdir.parent = new_hidden_parent_dentry->d_inode;
+ args.mkdir.dentry = new_hidden_dentry;
+ args.mkdir.mode = old_mode;
+
+ run_sioq(__unionfs_mkdir, &args);
+ err = args.err;
+ } else if (S_ISLNK(old_mode)) {
+ args.symlink.parent = new_hidden_parent_dentry->d_inode;
+ args.symlink.dentry = new_hidden_dentry;
+ args.symlink.symbuf = symbuf;
+ args.symlink.mode = old_mode;
+
+ run_sioq(__unionfs_symlink, &args);
+ err = args.err;
+ } else if (S_ISBLK(old_mode)
+ || S_ISCHR(old_mode)
+ || S_ISFIFO(old_mode)
+ || S_ISSOCK(old_mode)) {
+ args.mknod.parent = new_hidden_parent_dentry->d_inode;
+ args.mknod.dentry = new_hidden_dentry;
+ args.mknod.mode = old_mode;
+ args.mknod.dev = old_hidden_dentry->d_inode->i_rdev;
+
+ run_sioq(__unionfs_mknod, &args);
+ err = args.err;
+ } else if (S_ISREG(old_mode)) {
+ args.create.parent = new_hidden_parent_dentry->d_inode;
+ args.create.dentry = new_hidden_dentry;
+ args.create.mode = old_mode;
+ args.create.nd = NULL;
+
+ run_sioq(__unionfs_create, &args);
+ err = args.err;
+ } else {
+ printk(KERN_ERR "Unknown inode type %d\n",
+ old_mode);
+ BUG();
+ }
+
+ return err;
+}
+
+static inline int __copyup_reg_data(struct dentry *dentry,
+ struct dentry *new_hidden_dentry,
+ int new_bindex,
+ struct dentry *old_hidden_dentry,
+ int old_bindex,
+ struct file **copyup_file,
+ loff_t len)
+{
+ struct super_block *sb = dentry->d_sb;
+ struct file *input_file;
+ struct file *output_file;
+ mm_segment_t old_fs;
+ char *buf = NULL;
+ ssize_t read_bytes, write_bytes;
+ loff_t size;
+ int err = 0;
+
+ /* open old file */
+ mntget(unionfs_lower_mnt_idx(dentry, old_bindex));
+ branchget(sb, old_bindex);
+ input_file = dentry_open(old_hidden_dentry,
+ unionfs_lower_mnt_idx(dentry, old_bindex), O_RDONLY | O_LARGEFILE);
+ if (IS_ERR(input_file)) {
+ dput(old_hidden_dentry);
+ err = PTR_ERR(input_file);
+ goto out;
+ }
+ if (!input_file->f_op || !input_file->f_op->read) {
+ err = -EINVAL;
+ goto out_close_in;
+ }
+
+ /* open new file */
+ dget(new_hidden_dentry);
+ mntget(unionfs_lower_mnt_idx(dentry, new_bindex));
+ branchget(sb, new_bindex);
+ output_file = dentry_open(new_hidden_dentry,
+ unionfs_lower_mnt_idx(dentry, new_bindex), O_WRONLY | O_LARGEFILE);
+ if (IS_ERR(output_file)) {
+ err = PTR_ERR(output_file);
+ goto out_close_in2;
+ }
+ if (!output_file->f_op || !output_file->f_op->write) {
+ err = -EINVAL;
+ goto out_close_out;
+ }
+
+ /* allocating a buffer */
+ buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!buf) {
+ err = -ENOMEM;
+ goto out_close_out;
+ }
+
+ input_file->f_pos = 0;
+ output_file->f_pos = 0;
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ size = len;
+ err = 0;
+ do {
+ if (len >= PAGE_SIZE)
+ size = PAGE_SIZE;
+ else if ((len < PAGE_SIZE) && (len > 0))
+ size = len;
+
+ len -= PAGE_SIZE;
+
+ read_bytes =
+ input_file->f_op->read(input_file,
+ (char __user *)buf, size,
+ &input_file->f_pos);
+ if (read_bytes <= 0) {
+ err = read_bytes;
+ break;
+ }
+
+ write_bytes =
+ output_file->f_op->write(output_file,
+ (char __user *)buf,
+ read_bytes,
+ &output_file->f_pos);
+ if (write_bytes < 0 || (write_bytes < read_bytes)) {
+ err = write_bytes;
+ break;
+ }
+ } while ((read_bytes > 0) && (len > 0));
+
+ set_fs(old_fs);
+
+ kfree(buf);
+
+ if (err)
+ goto out_close_out;
+ if (copyup_file) {
+ *copyup_file = output_file;
+ goto out_close_in;
+ }
+
+out_close_out:
+ fput(output_file);
+
+out_close_in2:
+ branchput(sb, new_bindex);
+
+out_close_in:
+ fput(input_file);
+
+out:
+ branchput(sb, old_bindex);
+
+ return err;
+}
+
+/* dput the lower references for old and new dentry & clear a lower dentry
+ * pointer */
+static inline void __clear(struct dentry *dentry,
+ struct dentry *old_hidden_dentry,
+ int old_bstart, int old_bend,
+ struct dentry *new_hidden_dentry,
+ int new_bindex)
+{
+ /* get rid of the hidden dentry and all its traces */
+ unionfs_set_lower_dentry_idx(dentry, new_bindex, NULL);
+ set_dbstart(dentry, old_bstart);
+ set_dbend(dentry, old_bend);
+
+ dput(new_hidden_dentry);
+ dput(old_hidden_dentry);
+}
+
+/* copy up a dentry to a file of specified name */
+int copyup_named_dentry(struct inode *dir, struct dentry *dentry,
+ int bstart, int new_bindex, const char *name,
+ int namelen, struct file **copyup_file, loff_t len)
+{
+ struct dentry *new_hidden_dentry;
+ struct dentry *old_hidden_dentry = NULL;
+ struct super_block *sb;
+ int err = 0;
+ int old_bindex;
+ int old_bstart;
+ int old_bend;
+ struct dentry *new_hidden_parent_dentry = NULL;
+ mm_segment_t oldfs;
+ char *symbuf = NULL;
+
+ verify_locked(dentry);
+
+ old_bindex = bstart;
+ old_bstart = dbstart(dentry);
+ old_bend = dbend(dentry);
+
+ BUG_ON(new_bindex < 0);
+ BUG_ON(new_bindex >= old_bindex);
+
+ sb = dir->i_sb;
+
+ unionfs_read_lock(sb);
+
+ if ((err = is_robranch_super(sb, new_bindex))) {
+ dput(old_hidden_dentry);
+ goto out;
+ }
+
+ /* Create the directory structure above this dentry. */
+ new_hidden_dentry = create_parents_named(dir, dentry, name, new_bindex);
+ if (IS_ERR(new_hidden_dentry)) {
+ dput(old_hidden_dentry);
+ err = PTR_ERR(new_hidden_dentry);
+ goto out;
+ }
+
+ old_hidden_dentry = unionfs_lower_dentry_idx(dentry, old_bindex);
+ dget(old_hidden_dentry);
+
+ /* For symlinks, we must read the link before we lock the directory. */
+ if (S_ISLNK(old_hidden_dentry->d_inode->i_mode)) {
+
+ symbuf = kmalloc(PATH_MAX, GFP_KERNEL);
+ if (!symbuf) {
+ __clear(dentry, old_hidden_dentry,
+ old_bstart, old_bend,
+ new_hidden_dentry, new_bindex);
+ err = -ENOMEM;
+ goto out_free;
+ }
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ err = old_hidden_dentry->d_inode->i_op->readlink(
+ old_hidden_dentry,
+ (char __user *)symbuf,
+ PATH_MAX);
+ set_fs(oldfs);
+ if (err) {
+ __clear(dentry, old_hidden_dentry,
+ old_bstart, old_bend,
+ new_hidden_dentry, new_bindex);
+ goto out_free;
+ }
+ symbuf[err] = '\0';
+ }
+
+ /* Now we lock the parent, and create the object in the new branch. */
+ new_hidden_parent_dentry = lock_parent(new_hidden_dentry);
+
+ /* create the new inode */
+ err = __copyup_ndentry(old_hidden_dentry, new_hidden_dentry,
+ new_hidden_parent_dentry, symbuf);
+
+ if (err) {
+ __clear(dentry, old_hidden_dentry,
+ old_bstart, old_bend,
+ new_hidden_dentry, new_bindex);
+ goto out_unlock;
+ }
+
+ /* We actually copyup the file here. */
+ if (S_ISREG(old_hidden_dentry->d_inode->i_mode))
+ err = __copyup_reg_data(dentry, new_hidden_dentry, new_bindex,
+ old_hidden_dentry, old_bindex, copyup_file, len);
+ if (err)
+ goto out_unlink;
+
+ /* Set permissions. */
+ if ((err = copyup_permissions(sb, old_hidden_dentry, new_hidden_dentry)))
+ goto out_unlink;
+
+ /* do not allow files getting deleted to be reinterposed */
+ if (!d_deleted(dentry))
+ unionfs_reinterpose(dentry);
+
+ goto out_unlock;
+ /****/
+
+out_unlink:
+ /* copyup failed, because we possibly ran out of space or
+ * quota, or something else happened so let's unlink; we don't
+ * really care about the return value of vfs_unlink */
+ vfs_unlink(new_hidden_parent_dentry->d_inode, new_hidden_dentry);
+
+ if (copyup_file) {
+ /* need to close the file */
+
+ fput(*copyup_file);
+ branchput(sb, new_bindex);
+ }
+
+ /* TODO: should we reset the error to something like -EIO? */
+
+out_unlock:
+ unlock_dir(new_hidden_parent_dentry);
+
+out_free:
+ kfree(symbuf);
+
+out:
+ unionfs_read_unlock(sb);
+
+ return err;
+}
+
+/* This function creates a copy of a file represented by 'file' which currently
+ * resides in branch 'bstart' to branch 'new_bindex.' The copy will be named
+ * "name". */
+int copyup_named_file(struct inode *dir, struct file *file, char *name,
+ int bstart, int new_bindex, loff_t len)
+{
+ int err = 0;
+ struct file *output_file = NULL;
+
+ err = copyup_named_dentry(dir, file->f_dentry, bstart,
+ new_bindex, name, strlen(name), &output_file,
+ len);
+ if (!err) {
+ fbstart(file) = new_bindex;
+ unionfs_set_lower_file_idx(file, new_bindex, output_file);
+ }
+
+ return err;
+}
+
+/* This function creates a copy of a file represented by 'file' which currently
+ * resides in branch 'bstart' to branch 'new_bindex'.
+ */
+int copyup_file(struct inode *dir, struct file *file, int bstart,
+ int new_bindex, loff_t len)
+{
+ int err = 0;
+ struct file *output_file = NULL;
+
+ err = copyup_dentry(dir, file->f_dentry, bstart, new_bindex,
+ &output_file, len);
+ if (!err) {
+ fbstart(file) = new_bindex;
+ unionfs_set_lower_file_idx(file, new_bindex, output_file);
+ }
+
+ return err;
+}
+
+/* This function replicates the directory structure upto given dentry
+ * in the bindex branch. Can create directory structure recursively to the right
+ * also.
+ */
+struct dentry *create_parents(struct inode *dir, struct dentry *dentry,
+ int bindex)
+{
+ return create_parents_named(dir, dentry, dentry->d_name.name, bindex);
+}
+
+static inline void __cleanup_dentry(struct dentry * dentry, int bindex,
+ int old_bstart, int old_bend)
+{
+ int loop_start;
+ int loop_end;
+ int new_bstart = -1;
+ int new_bend = -1;
+ int i;
+
+ loop_start = min(old_bstart, bindex);
+ loop_end = max(old_bend, bindex);
+
+ /* This loop sets the bstart and bend for the new
+ * dentry by traversing from left to right.
+ * It also dputs all negative dentries except
+ * bindex (the newly looked dentry
+ */
+ for (i = loop_start; i <= loop_end; i++) {
+ if (!unionfs_lower_dentry_idx(dentry, i))
+ continue;
+
+ if (i == bindex) {
+ new_bend = i;
+ if (new_bstart < 0)
+ new_bstart = i;
+ continue;
+ }
+
+ if (!unionfs_lower_dentry_idx(dentry, i)->d_inode) {
+ dput(unionfs_lower_dentry_idx(dentry, i));
+ unionfs_set_lower_dentry_idx(dentry, i, NULL);
+ } else {
+ if (new_bstart < 0)
+ new_bstart = i;
+ new_bend = i;
+ }
+ }
+
+ if (new_bstart < 0)
+ new_bstart = bindex;
+ if (new_bend < 0)
+ new_bend = bindex;
+ set_dbstart(dentry, new_bstart);
+ set_dbend(dentry, new_bend);
+
+}
+
+/* set lower inode ptr and update bstart & bend if necessary */
+static inline void __set_inode(struct dentry * upper, struct dentry * lower,
+ int bindex)
+{
+ unionfs_set_lower_inode_idx(upper->d_inode, bindex,
+ igrab(lower->d_inode));
+ if (likely(ibstart(upper->d_inode) > bindex))
+ ibstart(upper->d_inode) = bindex;
+ if (likely(ibend(upper->d_inode) < bindex))
+ ibend(upper->d_inode) = bindex;
+
+}
+
+/* set lower dentry ptr and update bstart & bend if necessary */
+static inline void __set_dentry(struct dentry * upper, struct dentry * lower,
+ int bindex)
+{
+ unionfs_set_lower_dentry_idx(upper, bindex, lower);
+ if (likely(dbstart(upper) > bindex))
+ set_dbstart(upper, bindex);
+ if (likely(dbend(upper) < bindex))
+ set_dbend(upper, bindex);
+}
+
+/* This function replicates the directory structure upto given dentry
+ * in the bindex branch. */
+struct dentry *create_parents_named(struct inode *dir, struct dentry *dentry,
+ const char *name, int bindex)
+{
+ int err;
+ struct dentry *child_dentry;
+ struct dentry *parent_dentry;
+ struct dentry *hidden_parent_dentry = NULL;
+ struct dentry *hidden_dentry = NULL;
+ const char *childname;
+ unsigned int childnamelen;
+
+ int old_kmalloc_size;
+ int kmalloc_size;
+ int num_dentry;
+ int count;
+
+ int old_bstart;
+ int old_bend;
+ struct dentry **path = NULL;
+ struct dentry **tmp_path;
+ struct super_block *sb;
+
+ verify_locked(dentry);
+
+ /* There is no sense allocating any less than the minimum. */
+ kmalloc_size = malloc_sizes[0].cs_size;
+ num_dentry = kmalloc_size / sizeof(struct dentry *);
+
+ if ((err = is_robranch_super(dir->i_sb, bindex))) {
+ hidden_dentry = ERR_PTR(err);
+ goto out;
+ }
+
+ old_bstart = dbstart(dentry);
+ old_bend = dbend(dentry);
+
+ hidden_dentry = ERR_PTR(-ENOMEM);
+ path = kzalloc(kmalloc_size, GFP_KERNEL);
+ if (!path)
+ goto out;
+
+ /* assume the negative dentry of unionfs as the parent dentry */
+ parent_dentry = dentry;
+
+ count = 0;
+ /* This loop finds the first parent that exists in the given branch.
+ * We start building the directory structure from there. At the end
+ * of the loop, the following should hold:
+ * child_dentry is the first nonexistent child
+ * parent_dentry is the first existent parent
+ * path[0] is the = deepest child
+ * path[count] is the first child to create
+ */
+ do {
+ child_dentry = parent_dentry;
+
+ /* find the parent directory dentry in unionfs */
+ parent_dentry = child_dentry->d_parent;
+ lock_dentry(parent_dentry);
+
+ /* find out the hidden_parent_dentry in the given branch */
+ hidden_parent_dentry = unionfs_lower_dentry_idx(parent_dentry, bindex);
+
+ /* store the child dentry */
+ path[count++] = child_dentry;
+
+ /* grow path table */
+ if (count == num_dentry) {
+ old_kmalloc_size = kmalloc_size;
+ kmalloc_size *= 2;
+ num_dentry = kmalloc_size / sizeof(struct dentry *);
+
+ tmp_path = kzalloc(kmalloc_size, GFP_KERNEL);
+ if (!tmp_path) {
+ hidden_dentry = ERR_PTR(-ENOMEM);
+ goto out;
+ }
+ memcpy(tmp_path, path, old_kmalloc_size);
+ kfree(path);
+ path = tmp_path;
+ tmp_path = NULL;
+ }
+
+ } while (!hidden_parent_dentry);
+ count--;
+
+ sb = dentry->d_sb;
+
+ /* This is basically while(child_dentry != dentry). This loop is
+ * horrible to follow and should be replaced with cleaner code. */
+ while (1) {
+ /* get hidden parent dir in the current branch */
+ hidden_parent_dentry = unionfs_lower_dentry_idx(parent_dentry, bindex);
+ unlock_dentry(parent_dentry);
+
+ /* init the values to lookup */
+ childname = child_dentry->d_name.name;
+ childnamelen = child_dentry->d_name.len;
+
+ if (child_dentry != dentry) {
+ /* lookup child in the underlying file system */
+ hidden_dentry =
+ lookup_one_len(childname, hidden_parent_dentry,
+ childnamelen);
+ if (IS_ERR(hidden_dentry))
+ goto out;
+ } else {
+
+ /* is the name a whiteout of the childname ? */
+ /* lookup the whiteout child in the underlying file system */
+ hidden_dentry =
+ lookup_one_len(name, hidden_parent_dentry,
+ strlen(name));
+ if (IS_ERR(hidden_dentry))
+ goto out;
+
+ /* Replace the current dentry (if any) with the new one. */
+ dput(unionfs_lower_dentry_idx(dentry, bindex));
+ unionfs_set_lower_dentry_idx(dentry, bindex, hidden_dentry);
+
+ __cleanup_dentry(dentry, bindex, old_bstart, old_bend);
+ break;
+ }
+
+ if (hidden_dentry->d_inode) {
+ /* since this already exists we dput to avoid
+ * multiple references on the same dentry */
+ dput(hidden_dentry);
+ } else {
+ struct sioq_args args;
+
+ /* its a negative dentry, create a new dir */
+ hidden_parent_dentry = lock_parent(hidden_dentry);
+
+ args.mkdir.parent = hidden_parent_dentry->d_inode;
+ args.mkdir.dentry = hidden_dentry;
+ args.mkdir.mode = child_dentry->d_inode->i_mode;
+
+ run_sioq(__unionfs_mkdir, &args);
+ err = args.err;
+
+ if (!err)
+ err = copyup_permissions(dir->i_sb,
+ child_dentry, hidden_dentry);
+ unlock_dir(hidden_parent_dentry);
+ if (err) {
+ dput(hidden_dentry);
+ hidden_dentry = ERR_PTR(err);
+ goto out;
+ }
+
+ }
+
+ __set_inode(child_dentry, hidden_dentry, bindex);
+ __set_dentry(child_dentry, hidden_dentry, bindex);
+
+ parent_dentry = child_dentry;
+ child_dentry = path[--count];
+ }
+out:
+ kfree(path);
+ return hidden_dentry;
+}
+
--
1.4.3.3

2006-12-04 12:33:37

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 06/35] struct path: Rename DM's struct path

From: Josef "Jeff" Sipek <[email protected]>

Rename DM's struct path to struct dm_path.

Cc: [email protected]
Cc: [email protected]
Signed-off-by: Josef "Jeff" Sipek <[email protected]>
---
drivers/md/dm-emc.c | 10 +++++-----
drivers/md/dm-hw-handler.h | 2 +-
drivers/md/dm-mpath.c | 6 +++---
drivers/md/dm-mpath.h | 4 ++--
drivers/md/dm-path-selector.h | 12 ++++++------
drivers/md/dm-round-robin.c | 12 ++++++------
6 files changed, 23 insertions(+), 23 deletions(-)

diff --git a/drivers/md/dm-emc.c b/drivers/md/dm-emc.c
index 2b2d45d..265c467 100644
--- a/drivers/md/dm-emc.c
+++ b/drivers/md/dm-emc.c
@@ -40,7 +40,7 @@ static inline void free_bio(struct bio *

static int emc_endio(struct bio *bio, unsigned int bytes_done, int error)
{
- struct path *path = bio->bi_private;
+ struct dm_path *path = bio->bi_private;

if (bio->bi_size)
return 1;
@@ -61,7 +61,7 @@ static int emc_endio(struct bio *bio, un
return 0;
}

-static struct bio *get_failover_bio(struct path *path, unsigned data_size)
+static struct bio *get_failover_bio(struct dm_path *path, unsigned data_size)
{
struct bio *bio;
struct page *page;
@@ -96,7 +96,7 @@ static struct bio *get_failover_bio(stru
}

static struct request *get_failover_req(struct emc_handler *h,
- struct bio *bio, struct path *path)
+ struct bio *bio, struct dm_path *path)
{
struct request *rq;
struct block_device *bdev = bio->bi_bdev;
@@ -133,7 +133,7 @@ static struct request *get_failover_req(
}

static struct request *emc_trespass_get(struct emc_handler *h,
- struct path *path)
+ struct dm_path *path)
{
struct bio *bio;
struct request *rq;
@@ -191,7 +191,7 @@ static struct request *emc_trespass_get(
}

static void emc_pg_init(struct hw_handler *hwh, unsigned bypassed,
- struct path *path)
+ struct dm_path *path)
{
struct request *rq;
struct request_queue *q = bdev_get_queue(path->dev->bdev);
diff --git a/drivers/md/dm-hw-handler.h b/drivers/md/dm-hw-handler.h
index 15f5629..32eff28 100644
--- a/drivers/md/dm-hw-handler.h
+++ b/drivers/md/dm-hw-handler.h
@@ -32,7 +32,7 @@ struct hw_handler_type {
void (*destroy) (struct hw_handler *hwh);

void (*pg_init) (struct hw_handler *hwh, unsigned bypassed,
- struct path *path);
+ struct dm_path *path);
unsigned (*error) (struct hw_handler *hwh, struct bio *bio);
int (*status) (struct hw_handler *hwh, status_type_t type,
char *result, unsigned int maxlen);
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index d754e0b..b5348b1 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -31,7 +31,7 @@ struct pgpath {
struct priority_group *pg; /* Owning PG */
unsigned fail_count; /* Cumulative failure count */

- struct path path;
+ struct dm_path path;
};

#define path_to_pgpath(__pgp) container_of((__pgp), struct pgpath, path)
@@ -229,7 +229,7 @@ static void __switch_pg(struct multipath

static int __choose_path_in_pg(struct multipath *m, struct priority_group *pg)
{
- struct path *path;
+ struct dm_path *path;

path = pg->ps.type->select_path(&pg->ps, &m->repeat_count);
if (!path)
@@ -955,7 +955,7 @@ static int bypass_pg_num(struct multipat
/*
* pg_init must call this when it has completed its initialisation
*/
-void dm_pg_init_complete(struct path *path, unsigned err_flags)
+void dm_pg_init_complete(struct dm_path *path, unsigned err_flags)
{
struct pgpath *pgpath = path_to_pgpath(path);
struct priority_group *pg = pgpath->pg;
diff --git a/drivers/md/dm-mpath.h b/drivers/md/dm-mpath.h
index 8a4bf2b..b9cdcbb 100644
--- a/drivers/md/dm-mpath.h
+++ b/drivers/md/dm-mpath.h
@@ -11,7 +11,7 @@

struct dm_dev;

-struct path {
+struct dm_path {
struct dm_dev *dev; /* Read-only */
unsigned is_active; /* Read-only */

@@ -20,6 +20,6 @@ struct path {
};

/* Callback for hwh_pg_init_fn to use when complete */
-void dm_pg_init_complete(struct path *path, unsigned err_flags);
+void dm_pg_init_complete(struct dm_path *path, unsigned err_flags);

#endif
diff --git a/drivers/md/dm-path-selector.h b/drivers/md/dm-path-selector.h
index 732d06a..27357b8 100644
--- a/drivers/md/dm-path-selector.h
+++ b/drivers/md/dm-path-selector.h
@@ -44,7 +44,7 @@ struct path_selector_type {
* Add an opaque path object, along with some selector specific
* path args (eg, path priority).
*/
- int (*add_path) (struct path_selector *ps, struct path *path,
+ int (*add_path) (struct path_selector *ps, struct dm_path *path,
int argc, char **argv, char **error);

/*
@@ -55,27 +55,27 @@ struct path_selector_type {
* calling the function again. 0 means don't call it again unless
* the path fails.
*/
- struct path *(*select_path) (struct path_selector *ps,
+ struct dm_path *(*select_path) (struct path_selector *ps,
unsigned *repeat_count);

/*
* Notify the selector that a path has failed.
*/
- void (*fail_path) (struct path_selector *ps, struct path *p);
+ void (*fail_path) (struct path_selector *ps, struct dm_path *p);

/*
* Ask selector to reinstate a path.
*/
- int (*reinstate_path) (struct path_selector *ps, struct path *p);
+ int (*reinstate_path) (struct path_selector *ps, struct dm_path *p);

/*
* Table content based on parameters added in ps_add_path_fn
* or path selector status
*/
- int (*status) (struct path_selector *ps, struct path *path,
+ int (*status) (struct path_selector *ps, struct dm_path *path,
status_type_t type, char *result, unsigned int maxlen);

- int (*end_io) (struct path_selector *ps, struct path *path);
+ int (*end_io) (struct path_selector *ps, struct dm_path *path);
};

/* Register a path selector */
diff --git a/drivers/md/dm-round-robin.c b/drivers/md/dm-round-robin.c
index 6f9fcd4..a348a97 100644
--- a/drivers/md/dm-round-robin.c
+++ b/drivers/md/dm-round-robin.c
@@ -21,7 +21,7 @@
*---------------------------------------------------------------*/
struct path_info {
struct list_head list;
- struct path *path;
+ struct dm_path *path;
unsigned repeat_count;
};

@@ -80,7 +80,7 @@ static void rr_destroy(struct path_selec
ps->context = NULL;
}

-static int rr_status(struct path_selector *ps, struct path *path,
+static int rr_status(struct path_selector *ps, struct dm_path *path,
status_type_t type, char *result, unsigned int maxlen)
{
struct path_info *pi;
@@ -106,7 +106,7 @@ static int rr_status(struct path_selecto
* Called during initialisation to register each path with an
* optional repeat_count.
*/
-static int rr_add_path(struct path_selector *ps, struct path *path,
+static int rr_add_path(struct path_selector *ps, struct dm_path *path,
int argc, char **argv, char **error)
{
struct selector *s = (struct selector *) ps->context;
@@ -141,7 +141,7 @@ static int rr_add_path(struct path_selec
return 0;
}

-static void rr_fail_path(struct path_selector *ps, struct path *p)
+static void rr_fail_path(struct path_selector *ps, struct dm_path *p)
{
struct selector *s = (struct selector *) ps->context;
struct path_info *pi = p->pscontext;
@@ -149,7 +149,7 @@ static void rr_fail_path(struct path_sel
list_move(&pi->list, &s->invalid_paths);
}

-static int rr_reinstate_path(struct path_selector *ps, struct path *p)
+static int rr_reinstate_path(struct path_selector *ps, struct dm_path *p)
{
struct selector *s = (struct selector *) ps->context;
struct path_info *pi = p->pscontext;
@@ -159,7 +159,7 @@ static int rr_reinstate_path(struct path
return 0;
}

-static struct path *rr_select_path(struct path_selector *ps,
+static struct dm_path *rr_select_path(struct path_selector *ps,
unsigned *repeat_count)
{
struct selector *s = (struct selector *) ps->context;
--
1.4.3.3

2006-12-04 12:34:32

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 35/35] Unionfs: Extended Attributes support

From: Josef "Jeff" Sipek <[email protected]>

Extended attribute support.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
Signed-off-by: David Quigley <[email protected]>
Signed-off-by: Erez Zadok <[email protected]>
---
fs/Kconfig | 9 ++++
fs/unionfs/Makefile | 2 +
fs/unionfs/copyup.c | 75 +++++++++++++++++++++++++++++++
fs/unionfs/inode.c | 12 +++++
fs/unionfs/union.h | 15 ++++++
fs/unionfs/xattr.c | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 236 insertions(+), 0 deletions(-)

diff --git a/fs/Kconfig b/fs/Kconfig
index 4b31ea4..b8b8e45 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -1567,6 +1567,15 @@ config UNION_FS

See <http://www.unionfs.org> for details

+config UNION_FS_XATTR
+ bool "Unionfs extended attributes"
+ depends on UNION_FS
+ help
+ Extended attributes are name:value pairs associated with inodes by
+ the kernel or by users (see the attr(5) manual page).
+
+ If unsure, say N.
+
endmenu

menu "Network File Systems"
diff --git a/fs/unionfs/Makefile b/fs/unionfs/Makefile
index 25dd78f..7c98325 100644
--- a/fs/unionfs/Makefile
+++ b/fs/unionfs/Makefile
@@ -3,3 +3,5 @@ obj-$(CONFIG_UNION_FS) += unionfs.o
unionfs-y := subr.o dentry.o file.o inode.o main.o super.o \
stale_inode.o branchman.o rdstate.o copyup.o dirhelper.o \
rename.o unlink.o lookup.o commonfops.o dirfops.o sioq.o
+
+unionfs-$(CONFIG_UNION_FS_XATTR) += xattr.o
diff --git a/fs/unionfs/copyup.c b/fs/unionfs/copyup.c
index 0557c02..ec1c649 100644
--- a/fs/unionfs/copyup.c
+++ b/fs/unionfs/copyup.c
@@ -18,6 +18,75 @@

#include "union.h"

+#ifdef CONFIG_UNION_FS_XATTR
+/* copyup all extended attrs for a given dentry */
+static int copyup_xattrs(struct dentry *old_hidden_dentry,
+ struct dentry *new_hidden_dentry)
+{
+ int err = 0;
+ ssize_t list_size = -1;
+ char *name_list = NULL;
+ char *attr_value = NULL;
+ char *name_list_orig = NULL;
+
+ list_size = vfs_listxattr(old_hidden_dentry, NULL, 0);
+
+ if (list_size <= 0) {
+ err = list_size;
+ goto out;
+ }
+
+ name_list = xattr_alloc(list_size + 1, XATTR_LIST_MAX);
+ if (!name_list || IS_ERR(name_list)) {
+ err = PTR_ERR(name_list);
+ goto out;
+ }
+ list_size = vfs_listxattr(old_hidden_dentry, name_list, list_size);
+ attr_value = xattr_alloc(XATTR_SIZE_MAX, XATTR_SIZE_MAX);
+ if (!attr_value || IS_ERR(attr_value)) {
+ err = PTR_ERR(name_list);
+ goto out;
+ }
+ name_list_orig = name_list;
+ while (*name_list) {
+ ssize_t size;
+
+ //We need to lock here since vfs_getxattr doesn't lock for us.
+ mutex_lock(&old_hidden_dentry->d_inode->i_mutex);
+ size = vfs_getxattr(old_hidden_dentry, name_list,
+ attr_value, XATTR_SIZE_MAX);
+ mutex_unlock(&old_hidden_dentry->d_inode->i_mutex);
+ if (size < 0) {
+ err = size;
+ goto out;
+ }
+
+ if (size > XATTR_SIZE_MAX) {
+ err = -E2BIG;
+ goto out;
+ }
+ //We don't need to lock here since vfs_setxattr does it for us.
+ err = vfs_setxattr(new_hidden_dentry, name_list, attr_value,
+ size, 0);
+
+ if (err < 0)
+ goto out;
+ name_list += strlen(name_list) + 1;
+ }
+ out:
+ name_list = name_list_orig;
+
+ if (name_list)
+ xattr_free(name_list, list_size + 1);
+ if (attr_value)
+ xattr_free(attr_value, XATTR_SIZE_MAX);
+ /* It is no big deal if this fails, we just roll with the punches. */
+ if (err == -ENOTSUPP || err == -EOPNOTSUPP)
+ err = 0;
+ return err;
+}
+#endif /* CONFIG_UNION_FS_XATTR */
+
/* Determine the mode based on the copyup flags, and the existing dentry. */
static int copyup_permissions(struct super_block *sb,
struct dentry *old_hidden_dentry,
@@ -343,6 +412,12 @@ int copyup_named_dentry(struct inode *di
if ((err = copyup_permissions(sb, old_hidden_dentry, new_hidden_dentry)))
goto out_unlink;

+#ifdef CONFIG_UNION_FS_XATTR
+ /* Selinux uses extended attributes for permissions. */
+ if ((err = copyup_xattrs(old_hidden_dentry, new_hidden_dentry)))
+ goto out_unlink;
+#endif
+
/* do not allow files getting deleted to be reinterposed */
if (!d_deleted(dentry))
unionfs_reinterpose(dentry);
diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c
index c7806e9..2e45fb0 100644
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -917,10 +917,22 @@ struct inode_operations unionfs_dir_iops
.rename = unionfs_rename,
.permission = unionfs_permission,
.setattr = unionfs_setattr,
+#ifdef CONFIG_UNION_FS_XATTR
+ .setxattr = unionfs_setxattr,
+ .getxattr = unionfs_getxattr,
+ .removexattr = unionfs_removexattr,
+ .listxattr = unionfs_listxattr,
+#endif
};

struct inode_operations unionfs_main_iops = {
.permission = unionfs_permission,
.setattr = unionfs_setattr,
+#ifdef CONFIG_UNION_FS_XATTR
+ .setxattr = unionfs_setxattr,
+ .getxattr = unionfs_getxattr,
+ .removexattr = unionfs_removexattr,
+ .listxattr = unionfs_listxattr,
+#endif
};

diff --git a/fs/unionfs/union.h b/fs/unionfs/union.h
index ff0b814..58e0cfb 100644
--- a/fs/unionfs/union.h
+++ b/fs/unionfs/union.h
@@ -40,6 +40,7 @@
#include <linux/string.h>
#include <linux/vmalloc.h>
#include <linux/writeback.h>
+#include <linux/xattr.h>
#include <linux/fs_stack.h>

#include <linux/union_fs.h>
@@ -315,6 +316,20 @@ int unionfs_ioctl_queryfile(struct file
/* Verify that a branch is valid. */
int check_branch(struct nameidata *nd);

+#ifdef CONFIG_UNION_FS_XATTR
+/* Extended attribute functions. */
+extern void *xattr_alloc(size_t size, size_t limit);
+extern void xattr_free(void *ptr, size_t size);
+
+extern ssize_t unionfs_getxattr(struct dentry *dentry, const char *name,
+ void *value, size_t size);
+extern int unionfs_removexattr(struct dentry *dentry, const char *name);
+extern ssize_t unionfs_listxattr(struct dentry *dentry, char *list,
+ size_t size);
+extern int unionfs_setxattr(struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags);
+#endif /* CONFIG_UNION_FS_XATTR */
+
/* The root directory is unhashed, but isn't deleted. */
static inline int d_deleted(struct dentry *d)
{
diff --git a/fs/unionfs/xattr.c b/fs/unionfs/xattr.c
new file mode 100644
index 0000000..9f804bf
--- /dev/null
+++ b/fs/unionfs/xattr.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2003-2006 Erez Zadok
+ * Copyright (c) 2003-2006 Charles P. Wright
+ * Copyright (c) 2005-2006 Josef 'Jeff' Sipek
+ * Copyright (c) 2005-2006 Junjiro Okajima
+ * Copyright (c) 2005 Arun M. Krishnakumar
+ * Copyright (c) 2004-2006 David P. Quigley
+ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair
+ * Copyright (c) 2003 Puja Gupta
+ * Copyright (c) 2003 Harikesavan Krishnan
+ * Copyright (c) 2003-2006 Stony Brook University
+ * Copyright (c) 2003-2006 The Research Foundation of State University of New York*
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "union.h"
+
+/* This is lifted from fs/xattr.c */
+void *xattr_alloc(size_t size, size_t limit)
+{
+ void *ptr;
+
+ if (size > limit)
+ return ERR_PTR(-E2BIG);
+
+ if (!size) /* size request, no buffer is needed */
+ return NULL;
+ else if (size <= PAGE_SIZE)
+ ptr = kmalloc(size, GFP_KERNEL);
+ else
+ ptr = vmalloc(size);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+ return ptr;
+}
+
+void xattr_free(void *ptr, size_t size)
+{
+ if (!size) /* size request, no buffer was needed */
+ return;
+ else if (size <= PAGE_SIZE)
+ kfree(ptr);
+ else
+ vfree(ptr);
+}
+
+/* BKL held by caller.
+ * dentry->d_inode->i_mutex locked
+ */
+ssize_t unionfs_getxattr(struct dentry * dentry, const char *name, void *value,
+ size_t size)
+{
+ struct dentry *hidden_dentry = NULL;
+ int err = -EOPNOTSUPP;
+
+ lock_dentry(dentry);
+
+ hidden_dentry = unionfs_lower_dentry(dentry);
+
+ err = vfs_getxattr(hidden_dentry, (char*) name, value, size);
+
+ unlock_dentry(dentry);
+ return err;
+}
+
+/* BKL held by caller.
+ * dentry->d_inode->i_mutex locked
+ */
+int unionfs_setxattr(struct dentry *dentry, const char *name, const void *value,
+ size_t size, int flags)
+{
+ struct dentry *hidden_dentry = NULL;
+ int err = -EOPNOTSUPP;
+
+ lock_dentry(dentry);
+ hidden_dentry = unionfs_lower_dentry(dentry);
+
+ err = vfs_setxattr(hidden_dentry, (char*) name, (void*) value, size, flags);
+
+ unlock_dentry(dentry);
+ return err;
+}
+
+/* BKL held by caller.
+ * dentry->d_inode->i_mutex locked
+ */
+int unionfs_removexattr(struct dentry *dentry, const char *name)
+{
+ struct dentry *hidden_dentry = NULL;
+ int err = -EOPNOTSUPP;
+
+ lock_dentry(dentry);
+ hidden_dentry = unionfs_lower_dentry(dentry);
+
+ err = vfs_removexattr(hidden_dentry, (char*) name);
+
+ unlock_dentry(dentry);
+ return err;
+}
+
+/* BKL held by caller.
+ * dentry->d_inode->i_mutex locked
+ */
+ssize_t unionfs_listxattr(struct dentry * dentry, char *list, size_t size)
+{
+ struct dentry *hidden_dentry = NULL;
+ int err = -EOPNOTSUPP;
+ char *encoded_list = NULL;
+
+ lock_dentry(dentry);
+
+ hidden_dentry = unionfs_lower_dentry(dentry);
+
+ encoded_list = list;
+ err = vfs_listxattr(hidden_dentry, encoded_list, size);
+
+ unlock_dentry(dentry);
+ return err;
+}
+
--
1.4.3.3

2006-12-04 12:34:36

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 01/35] fsstack: Introduce fsstack_copy_{attr,inode}_*

From: Josef "Jeff" Sipek <[email protected]>

Introduce several fsstack_copy_* functions which allow stackable filesystems
(such as eCryptfs and Unionfs) to easily copy over (currently only) inode
attributes. This prevents code duplication and allows for code reuse.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
Cc: Michael Halcrow <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
---
fs/Makefile | 3 ++-
fs/stack.c | 39 +++++++++++++++++++++++++++++++++++++++
include/linux/fs_stack.h | 39 +++++++++++++++++++++++++++++++++++++++
3 files changed, 80 insertions(+), 1 deletions(-)

diff --git a/fs/Makefile b/fs/Makefile
index 9a5ce93..b9ffa63 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -10,7 +10,8 @@ obj-y := open.o read_write.o file_table.
ioctl.o readdir.o select.o fifo.o locks.o dcache.o inode.o \
attr.o bad_inode.o file.o filesystems.o namespace.o aio.o \
seq_file.o xattr.o libfs.o fs-writeback.o \
- pnode.o drop_caches.o splice.o sync.o utimes.o
+ pnode.o drop_caches.o splice.o sync.o utimes.o \
+ stack.o

ifeq ($(CONFIG_BLOCK),y)
obj-y += buffer.o bio.o block_dev.o direct-io.o mpage.o ioprio.o
diff --git a/fs/stack.c b/fs/stack.c
new file mode 100644
index 0000000..5f6f12d
--- /dev/null
+++ b/fs/stack.c
@@ -0,0 +1,39 @@
+#include <linux/module.h>
+#include <linux/fs.h>
+
+/* does _NOT_ require i_mutex to be held.
+ *
+ * This function cannot be inlined since i_size_{read,write} is rather
+ * heavy-weight on 32-bit systems
+ */
+void fsstack_copy_inode_size(struct inode *dst, const struct inode *src)
+{
+ i_size_write(dst, i_size_read((struct inode *)src));
+ dst->i_blocks = src->i_blocks;
+}
+
+/* copy all attributes; get_nlinks is optional way to override the i_nlink
+ * copying
+ */
+void __fsstack_copy_attr_all(struct inode *dest,
+ const struct inode *src,
+ int (*get_nlinks)(struct inode *))
+{
+ if (!get_nlinks)
+ dest->i_nlink = src->i_nlink;
+ else
+ dest->i_nlink = (*get_nlinks)(dest);
+
+ dest->i_mode = src->i_mode;
+ dest->i_uid = src->i_uid;
+ dest->i_gid = src->i_gid;
+ dest->i_rdev = src->i_rdev;
+ dest->i_atime = src->i_atime;
+ dest->i_mtime = src->i_mtime;
+ dest->i_ctime = src->i_ctime;
+ dest->i_blkbits = src->i_blkbits;
+ dest->i_flags = src->i_flags;
+}
+
+EXPORT_SYMBOL_GPL(fsstack_copy_inode_size);
+EXPORT_SYMBOL_GPL(__fsstack_copy_attr_all);
diff --git a/include/linux/fs_stack.h b/include/linux/fs_stack.h
new file mode 100644
index 0000000..56b3e09
--- /dev/null
+++ b/include/linux/fs_stack.h
@@ -0,0 +1,39 @@
+#ifndef _LINUX_FS_STACK_H
+#define _LINUX_FS_STACK_H
+
+/* This file defines generic functions used primarily by stackable
+ * filesystems; none of these functions require i_mutex to be held.
+ */
+
+#include <linux/fs.h>
+
+/* externs for fs/stack.c */
+extern void __fsstack_copy_attr_all(struct inode *dest,
+ const struct inode *src,
+ int (*get_nlinks)(struct inode *));
+
+extern void fsstack_copy_inode_size(struct inode *dst, const struct inode *src);
+
+/* inlines */
+static inline void fsstack_copy_attr_atime(struct inode *dest,
+ const struct inode *src)
+{
+ dest->i_atime = src->i_atime;
+}
+
+static inline void fsstack_copy_attr_times(struct inode *dest,
+ const struct inode *src)
+{
+ dest->i_atime = src->i_atime;
+ dest->i_mtime = src->i_mtime;
+ dest->i_ctime = src->i_ctime;
+}
+
+static inline void fsstack_copy_attr_all(struct inode *dest,
+ const struct inode *src)
+{
+ __fsstack_copy_attr_all(dest, src, NULL);
+}
+
+#endif /* _LINUX_FS_STACK_H */
+
--
1.4.3.3

2006-12-04 12:35:00

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 07/35] struct path: Move struct path from fs/namei.c into include/linux

From: Josef "Jeff" Sipek <[email protected]>

Moved struct path from fs/namei.c to include/linux/namei.h. This allows many
places in the VFS, as well as any stackable filesystem to easily keep track
of dentry-vfsmount pairs.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
---
fs/namei.c | 5 -----
include/linux/namei.h | 5 +++++
2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 28d49b3..8a7b923 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -570,11 +570,6 @@ fail:
return PTR_ERR(link);
}

-struct path {
- struct vfsmount *mnt;
- struct dentry *dentry;
-};
-
static inline void dput_path(struct path *path, struct nameidata *nd)
{
dput(path->dentry);
diff --git a/include/linux/namei.h b/include/linux/namei.h
index f5f1960..d39a5a6 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -29,6 +29,11 @@ struct nameidata {
} intent;
};

+struct path {
+ struct vfsmount *mnt;
+ struct dentry *dentry;
+};
+
/*
* Type of the last component on LOOKUP_PARENT
*/
--
1.4.3.3

2006-12-04 12:35:57

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 19/35] Unionfs: Directory file operations

From: Josef "Jeff" Sipek <[email protected]>

This patch provides directory file operations.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
Signed-off-by: David Quigley <[email protected]>
Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/dirfops.c | 264 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 264 insertions(+), 0 deletions(-)

diff --git a/fs/unionfs/dirfops.c b/fs/unionfs/dirfops.c
new file mode 100644
index 0000000..ab389eb
--- /dev/null
+++ b/fs/unionfs/dirfops.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2003-2006 Erez Zadok
+ * Copyright (c) 2003-2006 Charles P. Wright
+ * Copyright (c) 2005-2006 Josef 'Jeff' Sipek
+ * Copyright (c) 2005-2006 Junjiro Okajima
+ * Copyright (c) 2005 Arun M. Krishnakumar
+ * Copyright (c) 2004-2006 David P. Quigley
+ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair
+ * Copyright (c) 2003 Puja Gupta
+ * Copyright (c) 2003 Harikesavan Krishnan
+ * Copyright (c) 2003-2006 Stony Brook University
+ * Copyright (c) 2003-2006 The Research Foundation of State University of New York
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "union.h"
+
+/* Make sure our rdstate is playing by the rules. */
+static void verify_rdstate_offset(struct unionfs_dir_state *rdstate)
+{
+ BUG_ON(rdstate->offset >= DIREOF);
+ BUG_ON(rdstate->cookie >= MAXRDCOOKIE);
+}
+
+struct unionfs_getdents_callback {
+ struct unionfs_dir_state *rdstate;
+ void *dirent;
+ int entries_written;
+ int filldir_called;
+ int filldir_error;
+ filldir_t filldir;
+ struct super_block *sb;
+};
+
+/* based on generic filldir in fs/readir.c */
+static int unionfs_filldir(void *dirent, const char *name, int namelen,
+ loff_t offset, u64 ino, unsigned int d_type)
+{
+ struct unionfs_getdents_callback *buf = dirent;
+ struct filldir_node *found = NULL;
+ int err = 0;
+ int is_wh_entry = 0;
+
+ buf->filldir_called++;
+
+ if ((namelen > UNIONFS_WHLEN) &&
+ !strncmp(name, UNIONFS_WHPFX, UNIONFS_WHLEN)) {
+ name += UNIONFS_WHLEN;
+ namelen -= UNIONFS_WHLEN;
+ is_wh_entry = 1;
+ }
+
+ found = find_filldir_node(buf->rdstate, name, namelen);
+
+ if (found)
+ goto out;
+
+ /* if 'name' isn't a whiteout, filldir it. */
+ if (!is_wh_entry) {
+ off_t pos = rdstate2offset(buf->rdstate);
+ u64 unionfs_ino = ino;
+
+ if (!err) {
+ err = buf->filldir(buf->dirent, name, namelen, pos,
+ unionfs_ino, d_type);
+ buf->rdstate->offset++;
+ verify_rdstate_offset(buf->rdstate);
+ }
+ }
+ /* If we did fill it, stuff it in our hash, otherwise return an error */
+ if (err) {
+ buf->filldir_error = err;
+ goto out;
+ }
+ buf->entries_written++;
+ if ((err = add_filldir_node(buf->rdstate, name, namelen,
+ buf->rdstate->bindex, is_wh_entry)))
+ buf->filldir_error = err;
+
+out:
+ return err;
+}
+
+static int unionfs_readdir(struct file *file, void *dirent, filldir_t filldir)
+{
+ int err = 0;
+ struct file *hidden_file = NULL;
+ struct inode *inode = NULL;
+ struct unionfs_getdents_callback buf;
+ struct unionfs_dir_state *uds;
+ int bend;
+ loff_t offset;
+
+ if ((err = unionfs_file_revalidate(file, 0)))
+ goto out;
+
+ inode = file->f_dentry->d_inode;
+
+ uds = UNIONFS_F(file)->rdstate;
+ if (!uds) {
+ if (file->f_pos == DIREOF) {
+ goto out;
+ } else if (file->f_pos > 0) {
+ uds = find_rdstate(inode, file->f_pos);
+ if (!uds) {
+ err = -ESTALE;
+ goto out;
+ }
+ UNIONFS_F(file)->rdstate = uds;
+ } else {
+ init_rdstate(file);
+ uds = UNIONFS_F(file)->rdstate;
+ }
+ }
+ bend = fbend(file);
+
+ while (uds->bindex <= bend) {
+ hidden_file = unionfs_lower_file_idx(file, uds->bindex);
+ if (!hidden_file) {
+ uds->bindex++;
+ uds->dirpos = 0;
+ continue;
+ }
+
+ /* prepare callback buffer */
+ buf.filldir_called = 0;
+ buf.filldir_error = 0;
+ buf.entries_written = 0;
+ buf.dirent = dirent;
+ buf.filldir = filldir;
+ buf.rdstate = uds;
+ buf.sb = inode->i_sb;
+
+ /* Read starting from where we last left off. */
+ offset = vfs_llseek(hidden_file, uds->dirpos, 0);
+ if (offset < 0) {
+ err = offset;
+ goto out;
+ }
+ err = vfs_readdir(hidden_file, unionfs_filldir, &buf);
+ /* Save the position for when we continue. */
+
+ offset = vfs_llseek(hidden_file, 0, 1);
+ if (offset < 0) {
+ err = offset;
+ goto out;
+ }
+ uds->dirpos = offset;
+
+ /* Copy the atime. */
+ fsstack_copy_attr_atime(inode, hidden_file->f_dentry->d_inode);
+
+ if (err < 0)
+ goto out;
+
+ if (buf.filldir_error)
+ break;
+
+ if (!buf.entries_written) {
+ uds->bindex++;
+ uds->dirpos = 0;
+ }
+ }
+
+ if (!buf.filldir_error && uds->bindex >= bend) {
+ /* Save the number of hash entries for next time. */
+ UNIONFS_I(inode)->hashsize = uds->hashentries;
+ free_rdstate(uds);
+ UNIONFS_F(file)->rdstate = NULL;
+ file->f_pos = DIREOF;
+ } else
+ file->f_pos = rdstate2offset(uds);
+
+out:
+ return err;
+}
+
+/* This is not meant to be a generic repositioning function. If you do
+ * things that aren't supported, then we return EINVAL.
+ *
+ * What is allowed:
+ * (1) seeking to the same position that you are currently at
+ * This really has no effect, but returns where you are.
+ * (2) seeking to the end of the file, if you've read everything
+ * This really has no effect, but returns where you are.
+ * (3) seeking to the beginning of the file
+ * This throws out all state, and lets you begin again.
+ */
+static loff_t unionfs_dir_llseek(struct file *file, loff_t offset, int origin)
+{
+ struct unionfs_dir_state *rdstate;
+ loff_t err;
+
+ if ((err = unionfs_file_revalidate(file, 0)))
+ goto out;
+
+ rdstate = UNIONFS_F(file)->rdstate;
+
+ /* We let users seek to their current position, but not anywhere else. */
+ if (!offset) {
+ switch (origin) {
+ case SEEK_SET:
+ if (rdstate) {
+ free_rdstate(rdstate);
+ UNIONFS_F(file)->rdstate = NULL;
+ }
+ init_rdstate(file);
+ err = 0;
+ break;
+ case SEEK_CUR:
+ err = file->f_pos;
+ break;
+ case SEEK_END:
+ /* Unsupported, because we would break everything. */
+ err = -EINVAL;
+ break;
+ }
+ } else {
+ switch (origin) {
+ case SEEK_SET:
+ if (rdstate) {
+ if (offset == rdstate2offset(rdstate))
+ err = offset;
+ else if (file->f_pos == DIREOF)
+ err = DIREOF;
+ else
+ err = -EINVAL;
+ } else {
+ if ((rdstate = find_rdstate(file->f_dentry->d_inode,
+ offset))) {
+ UNIONFS_F(file)->rdstate = rdstate;
+ err = rdstate->offset;
+ } else
+ err = -EINVAL;
+ }
+ break;
+ case SEEK_CUR:
+ case SEEK_END:
+ /* Unsupported, because we would break everything. */
+ err = -EINVAL;
+ break;
+ }
+ }
+
+out:
+ return err;
+}
+
+/* Trimmed directory options, we shouldn't pass everything down since
+ * we don't want to operate on partial directories.
+ */
+struct file_operations unionfs_dir_fops = {
+ .llseek = unionfs_dir_llseek,
+ .read = generic_read_dir,
+ .readdir = unionfs_readdir,
+ .unlocked_ioctl = unionfs_ioctl,
+ .open = unionfs_open,
+ .release = unionfs_file_release,
+ .flush = unionfs_flush,
+};
+
--
1.4.3.3

2006-12-04 12:35:14

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 04/35] fsstack: Fix up eCryptfs compilation

From: Josef "Jeff" Sipek <[email protected]>

The fsstack tidy patch broke eCryptfs. This patch makes eCryptfs compile
again.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
---
fs/ecryptfs/inode.c | 6 +++---
fs/ecryptfs/main.c | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index 3e2a786..d798d9f 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -589,9 +589,9 @@ ecryptfs_rename(struct inode *old_dir, s
lower_new_dir_dentry->d_inode, lower_new_dentry);
if (rc)
goto out_lock;
- fsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode);
+ fsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode, NULL);
if (new_dir != old_dir)
- fsstack_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode);
+ fsstack_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode, NULL);
out_lock:
unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
dput(lower_new_dentry->d_parent);
@@ -878,7 +878,7 @@ static int ecryptfs_setattr(struct dentr
}
rc = notify_change(lower_dentry, ia);
out:
- fsstack_copy_attr_all(inode, lower_inode);
+ fsstack_copy_attr_all(inode, lower_inode, NULL);
return rc;
}

diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c
index 5982931..a4aee57 100644
--- a/fs/ecryptfs/main.c
+++ b/fs/ecryptfs/main.c
@@ -113,7 +113,7 @@ int ecryptfs_interpose(struct dentry *lo
d_add(dentry, inode);
else
d_instantiate(dentry, inode);
- fsstack_copy_attr_all(inode, lower_inode);
+ fsstack_copy_attr_all(inode, lower_inode, NULL);
/* This size will be overwritten for real files w/ headers and
* other metadata */
fsstack_copy_inode_size(inode, lower_inode);
--
1.4.3.3

2006-12-04 12:36:34

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 23/35] Unionfs: Main module functions

From: Josef "Jeff" Sipek <[email protected]>

Module init & cleanup code, as well as interposition functions.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
Signed-off-by: David Quigley <[email protected]>
Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/main.c | 685 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 685 insertions(+), 0 deletions(-)

diff --git a/fs/unionfs/main.c b/fs/unionfs/main.c
new file mode 100644
index 0000000..34fb5f8
--- /dev/null
+++ b/fs/unionfs/main.c
@@ -0,0 +1,685 @@
+/*
+ * Copyright (c) 2003-2006 Erez Zadok
+ * Copyright (c) 2003-2006 Charles P. Wright
+ * Copyright (c) 2005-2006 Josef 'Jeff' Sipek
+ * Copyright (c) 2005-2006 Junjiro Okajima
+ * Copyright (c) 2005 Arun M. Krishnakumar
+ * Copyright (c) 2004-2006 David P. Quigley
+ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair
+ * Copyright (c) 2003 Puja Gupta
+ * Copyright (c) 2003 Harikesavan Krishnan
+ * Copyright (c) 2003-2006 Stony Brook University
+ * Copyright (c) 2003-2006 The Research Foundation of State University of New York
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "union.h"
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+/* sb we pass is unionfs's super_block */
+int unionfs_interpose(struct dentry *dentry, struct super_block *sb, int flag)
+{
+ struct inode *hidden_inode;
+ struct dentry *hidden_dentry;
+ int err = 0;
+ struct inode *inode;
+ int is_negative_dentry = 1;
+ int bindex, bstart, bend;
+
+ verify_locked(dentry);
+
+ bstart = dbstart(dentry);
+ bend = dbend(dentry);
+
+ /* Make sure that we didn't get a negative dentry. */
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ if (unionfs_lower_dentry_idx(dentry, bindex) &&
+ unionfs_lower_dentry_idx(dentry, bindex)->d_inode) {
+ is_negative_dentry = 0;
+ break;
+ }
+ }
+ BUG_ON(is_negative_dentry);
+
+ /* We allocate our new inode below, by calling iget.
+ * iget will call our read_inode which will initialize some
+ * of the new inode's fields
+ */
+
+ /* On revalidate we've already got our own inode and just need
+ * to fix it up. */
+ if (flag == INTERPOSE_REVAL) {
+ inode = dentry->d_inode;
+ UNIONFS_I(inode)->bstart = -1;
+ UNIONFS_I(inode)->bend = -1;
+ atomic_set(&UNIONFS_I(inode)->generation,
+ atomic_read(&UNIONFS_SB(sb)->generation));
+
+ UNIONFS_I(inode)->lower_inodes =
+ kcalloc(sbmax(sb), sizeof(struct inode *), GFP_KERNEL);
+ if (!UNIONFS_I(inode)->lower_inodes) {
+ err = -ENOMEM;
+ goto out;
+ }
+ mutex_lock(&inode->i_mutex);
+ } else {
+ ino_t ino;
+ /* get unique inode number for unionfs */
+ ino = iunique(sb, UNIONFS_ROOT_INO);
+
+ inode = iget(sb, ino);
+ if (!inode) {
+ err = -EACCES; /* should be impossible??? */
+ goto out;
+ }
+
+ mutex_lock(&inode->i_mutex);
+ if (atomic_read(&inode->i_count) > 1)
+ goto skip;
+ }
+
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ if (!hidden_dentry) {
+ unionfs_set_lower_inode_idx(inode, bindex, NULL);
+ continue;
+ }
+
+ /* Initialize the hidden inode to the new hidden inode. */
+ if (!hidden_dentry->d_inode)
+ continue;
+
+ unionfs_set_lower_inode_idx(inode, bindex,
+ igrab(hidden_dentry->d_inode));
+ }
+
+ ibstart(inode) = dbstart(dentry);
+ ibend(inode) = dbend(dentry);
+
+ /* Use attributes from the first branch. */
+ hidden_inode = unionfs_lower_inode(inode);
+
+ /* Use different set of inode ops for symlinks & directories */
+ if (S_ISLNK(hidden_inode->i_mode))
+ inode->i_op = &unionfs_symlink_iops;
+ else if (S_ISDIR(hidden_inode->i_mode))
+ inode->i_op = &unionfs_dir_iops;
+
+ /* Use different set of file ops for directories */
+ if (S_ISDIR(hidden_inode->i_mode))
+ inode->i_fop = &unionfs_dir_fops;
+
+ /* properly initialize special inodes */
+ if (S_ISBLK(hidden_inode->i_mode) || S_ISCHR(hidden_inode->i_mode) ||
+ S_ISFIFO(hidden_inode->i_mode) || S_ISSOCK(hidden_inode->i_mode))
+ init_special_inode(inode, hidden_inode->i_mode,
+ hidden_inode->i_rdev);
+ /* Fix our inode's address operations to that of the lower inode (Unionfs is FiST-Lite) */
+ if (inode->i_mapping->a_ops != hidden_inode->i_mapping->a_ops)
+ inode->i_mapping->a_ops = hidden_inode->i_mapping->a_ops;
+
+ /* all well, copy inode attributes */
+ fsstack_copy_attr_all(inode, hidden_inode, unionfs_get_nlinks);
+
+skip:
+ /* only (our) lookup wants to do a d_add */
+ switch (flag) {
+ case INTERPOSE_DEFAULT:
+ case INTERPOSE_REVAL_NEG:
+ d_instantiate(dentry, inode);
+ break;
+ case INTERPOSE_LOOKUP:
+ err = PTR_ERR(d_splice_alias(inode, dentry));
+ break;
+ case INTERPOSE_REVAL:
+ /* Do nothing. */
+ break;
+ default:
+ printk(KERN_ERR "Invalid interpose flag passed!");
+ BUG();
+ }
+
+ mutex_unlock(&inode->i_mutex);
+
+out:
+ return err;
+}
+
+void unionfs_reinterpose(struct dentry *dentry)
+{
+ struct dentry *hidden_dentry;
+ struct inode *inode;
+ int bindex, bstart, bend;
+
+ verify_locked(dentry);
+
+ /* This is pre-allocated inode */
+ inode = dentry->d_inode;
+
+ bstart = dbstart(dentry);
+ bend = dbend(dentry);
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ if (!hidden_dentry)
+ continue;
+
+ if (!hidden_dentry->d_inode)
+ continue;
+ if (unionfs_lower_inode_idx(inode, bindex))
+ continue;
+ unionfs_set_lower_inode_idx(inode, bindex, igrab(hidden_dentry->d_inode));
+ }
+ ibstart(inode) = dbstart(dentry);
+ ibend(inode) = dbend(dentry);
+}
+
+/* make sure the branch we just looked up (nd) makes sense:
+ *
+ * 1) we're not trying to stack unionfs on top of unionfs
+ * 2) it exists
+ * 3) is a directory
+ */
+int check_branch(struct nameidata *nd)
+{
+ if (!strcmp(nd->dentry->d_sb->s_type->name, "unionfs"))
+ return -EINVAL;
+ if (!nd->dentry->d_inode)
+ return -ENOENT;
+ if (!S_ISDIR(nd->dentry->d_inode->i_mode))
+ return -ENOTDIR;
+ return 0;
+}
+
+/* checks if two hidden_dentries have overlapping branches */
+int is_branch_overlap(struct dentry *dent1, struct dentry *dent2)
+{
+ struct dentry *dent = NULL;
+
+ dent = dent1;
+ while ((dent != dent2) && (dent->d_parent != dent))
+ dent = dent->d_parent;
+
+ if (dent == dent2)
+ return 1;
+
+ dent = dent2;
+ while ((dent != dent1) && (dent->d_parent != dent))
+ dent = dent->d_parent;
+
+ return (dent == dent1);
+}
+
+/* parse branch mode */
+static int parse_branch_mode(char *name)
+{
+ int perms;
+ int l = strlen(name);
+ if (!strcmp(name + l - 3, "=ro")) {
+ perms = MAY_READ;
+ name[l - 3] = '\0';
+ } else if (!strcmp(name + l - 3, "=rw")) {
+ perms = MAY_READ | MAY_WRITE;
+ name[l - 3] = '\0';
+ } else
+ perms = MAY_READ | MAY_WRITE;
+
+ return perms;
+}
+
+/* parse the dirs= mount argument */
+static int parse_dirs_option(struct super_block *sb, struct unionfs_dentry_info
+ *hidden_root_info, char *options)
+{
+ struct nameidata nd;
+ char *name;
+ int err = 0;
+ int branches = 1;
+ int bindex = 0;
+ int i = 0;
+ int j = 0;
+
+ struct dentry *dent1;
+ struct dentry *dent2;
+
+ if (options[0] == '\0') {
+ printk(KERN_WARNING "unionfs: no branches specified\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ /* Each colon means we have a separator, this is really just a rough
+ * guess, since strsep will handle empty fields for us. */
+ for (i = 0; options[i]; i++)
+ if (options[i] == ':')
+ branches++;
+
+ /* allocate space for underlying pointers to hidden dentry */
+ UNIONFS_SB(sb)->data = kcalloc(branches,
+ sizeof(struct unionfs_data), GFP_KERNEL);
+ if (!UNIONFS_SB(sb)->data) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ hidden_root_info->lower_paths = kcalloc(branches,
+ sizeof(struct path), GFP_KERNEL);
+ if (!hidden_root_info->lower_paths) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ /* now parsing the string b1:b2=rw:b3=ro:b4 */
+ branches = 0;
+ while ((name = strsep(&options, ":")) != NULL) {
+ int perms;
+
+ if (!*name)
+ continue;
+
+ branches++;
+
+ /* strip off =rw or =ro if it is specified. */
+ perms = parse_branch_mode(name);
+ if (!bindex && !(perms & MAY_WRITE)) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ err = path_lookup(name, LOOKUP_FOLLOW, &nd);
+ if (err) {
+ printk(KERN_WARNING "unionfs: error accessing "
+ "hidden directory '%s' (error %d)\n", name, err);
+ goto out;
+ }
+
+ if ((err = check_branch(&nd))) {
+ printk(KERN_WARNING "unionfs: hidden directory "
+ "'%s' is not a valid branch\n", name);
+ path_release(&nd);
+ goto out;
+ }
+
+ hidden_root_info->lower_paths[bindex].dentry = nd.dentry;
+ hidden_root_info->lower_paths[bindex].mnt = nd.mnt;
+
+ set_branchperms(sb, bindex, perms);
+ set_branch_count(sb, bindex, 0);
+
+ if (hidden_root_info->bstart < 0)
+ hidden_root_info->bstart = bindex;
+ hidden_root_info->bend = bindex;
+ bindex++;
+ }
+
+ if (branches == 0) {
+ printk(KERN_WARNING "unionfs: no branches specified\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ BUG_ON(branches != (hidden_root_info->bend + 1));
+
+ /* ensure that no overlaps exist in the branches */
+ for (i = 0; i < branches; i++) {
+ for (j = i + 1; j < branches; j++) {
+ dent1 = hidden_root_info->lower_paths[i].dentry;
+ dent2 = hidden_root_info->lower_paths[j].dentry;
+
+ if (is_branch_overlap(dent1, dent2)) {
+ printk(KERN_WARNING "unionfs: branches %d and "
+ "%d overlap\n", i, j);
+ err = -EINVAL;
+ goto out;
+ }
+ }
+ }
+
+out:
+ if (err) {
+ for (i = 0; i < branches; i++)
+ if (hidden_root_info->lower_paths[i].dentry) {
+ dput(hidden_root_info->lower_paths[i].dentry);
+ mntput(hidden_root_info->lower_paths[i].mnt);
+ }
+
+ kfree(hidden_root_info->lower_paths);
+ kfree(UNIONFS_SB(sb)->data);
+
+ /* MUST clear the pointers to prevent potential double free if
+ * the caller dies later on
+ */
+ hidden_root_info->lower_paths = NULL;
+ UNIONFS_SB(sb)->data = NULL;
+ }
+ return err;
+}
+
+/*
+ * Parse mount options. See the manual page for usage instructions.
+ *
+ * Returns the dentry object of the lower-level (hidden) directory;
+ * We want to mount our stackable file system on top of that hidden directory.
+ */
+static struct unionfs_dentry_info *unionfs_parse_options(struct super_block *sb,
+ char *options)
+{
+ struct unionfs_dentry_info *hidden_root_info;
+ char *optname;
+ int err = 0;
+ int bindex;
+ int dirsfound = 0;
+
+ /* allocate private data area */
+ err = -ENOMEM;
+ hidden_root_info =
+ kzalloc(sizeof(struct unionfs_dentry_info), GFP_KERNEL);
+ if (!hidden_root_info)
+ goto out_error;
+ hidden_root_info->bstart = -1;
+ hidden_root_info->bend = -1;
+ hidden_root_info->bopaque = -1;
+
+ while ((optname = strsep(&options, ",")) != NULL) {
+ char *optarg;
+ char *endptr;
+ int intval;
+
+ if (!*optname)
+ continue;
+
+ optarg = strchr(optname, '=');
+ if (optarg)
+ *optarg++ = '\0';
+
+ /* All of our options take an argument now. Insert ones that
+ * don't, above this check. */
+ if (!optarg) {
+ printk("unionfs: %s requires an argument.\n", optname);
+ err = -EINVAL;
+ goto out_error;
+ }
+
+ if (!strcmp("dirs", optname)) {
+ if (++dirsfound > 1) {
+ printk(KERN_WARNING
+ "unionfs: multiple dirs specified\n");
+ err = -EINVAL;
+ goto out_error;
+ }
+ err = parse_dirs_option(sb, hidden_root_info, optarg);
+ if (err)
+ goto out_error;
+ continue;
+ }
+
+ /* All of these options require an integer argument. */
+ intval = simple_strtoul(optarg, &endptr, 0);
+ if (*endptr) {
+ printk(KERN_WARNING
+ "unionfs: invalid %s option '%s'\n",
+ optname, optarg);
+ err = -EINVAL;
+ goto out_error;
+ }
+
+ err = -EINVAL;
+ printk(KERN_WARNING
+ "unionfs: unrecognized option '%s'\n", optname);
+ goto out_error;
+ }
+ if (dirsfound != 1) {
+ printk(KERN_WARNING "unionfs: dirs option required\n");
+ err = -EINVAL;
+ goto out_error;
+ }
+ goto out;
+
+out_error:
+ if (hidden_root_info && hidden_root_info->lower_paths) {
+ for (bindex = hidden_root_info->bstart;
+ bindex >= 0 && bindex <= hidden_root_info->bend;
+ bindex++) {
+ struct dentry *d;
+ struct vfsmount *m;
+
+ d = hidden_root_info->lower_paths[bindex].dentry;
+ m = hidden_root_info->lower_paths[bindex].mnt;
+
+ dput(d);
+
+ if (m)
+ mntput(m);
+ }
+ }
+
+ kfree(hidden_root_info->lower_paths);
+ kfree(hidden_root_info);
+
+ kfree(UNIONFS_SB(sb)->data);
+ UNIONFS_SB(sb)->data = NULL;
+
+ hidden_root_info = ERR_PTR(err);
+out:
+ return hidden_root_info;
+}
+
+/* our custom d_alloc_root workalike
+ *
+ * we can't use d_alloc_root if we want to use our own interpose function
+ * unchanged, so we simply call our own "fake" d_alloc_root
+ */
+static struct dentry *unionfs_d_alloc_root(struct super_block *sb)
+{
+ struct dentry *ret = NULL;
+
+ if (sb) {
+ static const struct qstr name = {.name = "/",.len = 1 };
+
+ ret = d_alloc(NULL, &name);
+ if (ret) {
+ ret->d_op = &unionfs_dops;
+ ret->d_sb = sb;
+ ret->d_parent = ret;
+ }
+ }
+ return ret;
+}
+
+static int unionfs_read_super(struct super_block *sb, void *raw_data,
+ int silent)
+{
+ int err = 0;
+
+ struct unionfs_dentry_info *hidden_root_info = NULL;
+ int bindex, bstart, bend;
+
+ if (!raw_data) {
+ printk(KERN_WARNING
+ "unionfs_read_super: missing data argument\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Allocate superblock private data
+ */
+ sb->s_fs_info = kzalloc(sizeof(struct unionfs_sb_info), GFP_KERNEL);
+ if (!UNIONFS_SB(sb)) {
+ printk(KERN_WARNING "%s: out of memory\n", __FUNCTION__);
+ err = -ENOMEM;
+ goto out;
+ }
+
+ UNIONFS_SB(sb)->bend = -1;
+ atomic_set(&UNIONFS_SB(sb)->generation, 1);
+ init_rwsem(&UNIONFS_SB(sb)->rwsem);
+
+ hidden_root_info = unionfs_parse_options(sb, raw_data);
+ if (IS_ERR(hidden_root_info)) {
+ printk(KERN_WARNING
+ "unionfs_read_super: error while parsing options "
+ "(err = %ld)\n", PTR_ERR(hidden_root_info));
+ err = PTR_ERR(hidden_root_info);
+ hidden_root_info = NULL;
+ goto out_free;
+ }
+ if (hidden_root_info->bstart == -1) {
+ err = -ENOENT;
+ goto out_free;
+ }
+
+ /* set the hidden superblock field of upper superblock */
+ bstart = hidden_root_info->bstart;
+ BUG_ON(bstart != 0);
+ sbend(sb) = bend = hidden_root_info->bend;
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ struct dentry *d;
+
+ d = hidden_root_info->lower_paths[bindex].dentry;
+
+ unionfs_set_lower_super_idx(sb, bindex, d->d_sb);
+ }
+
+ /* Unionfs: Max Bytes is the maximum bytes from highest priority branch */
+ sb->s_maxbytes = unionfs_lower_super_idx(sb, 0)->s_maxbytes;
+
+ sb->s_op = &unionfs_sops;
+
+ /* See comment next to the definition of unionfs_d_alloc_root */
+ sb->s_root = unionfs_d_alloc_root(sb);
+ if (!sb->s_root) {
+ err = -ENOMEM;
+ goto out_dput;
+ }
+
+ /* link the upper and lower dentries */
+ sb->s_root->d_fsdata = NULL;
+ if ((err = new_dentry_private_data(sb->s_root)))
+ goto out_freedpd;
+
+ /* Set the hidden dentries for s_root */
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ struct dentry *d;
+ struct vfsmount *m;
+
+ d = hidden_root_info->lower_paths[bindex].dentry;
+ m = hidden_root_info->lower_paths[bindex].mnt;
+
+ unionfs_set_lower_dentry_idx(sb->s_root, bindex, d);
+ unionfs_set_lower_mnt_idx(sb->s_root, bindex, m);
+ }
+ set_dbstart(sb->s_root, bstart);
+ set_dbend(sb->s_root, bend);
+
+ /* Set the generation number to one, since this is for the mount. */
+ atomic_set(&UNIONFS_D(sb->s_root)->generation, 1);
+
+ /* call interpose to create the upper level inode */
+ if ((err = unionfs_interpose(sb->s_root, sb, 0)))
+ goto out_freedpd;
+ unlock_dentry(sb->s_root);
+ goto out;
+
+out_freedpd:
+ if (UNIONFS_D(sb->s_root)) {
+ kfree(UNIONFS_D(sb->s_root)->lower_paths);
+ free_dentry_private_data(UNIONFS_D(sb->s_root));
+ }
+ dput(sb->s_root);
+
+out_dput:
+ if (hidden_root_info && !IS_ERR(hidden_root_info)) {
+ for (bindex = hidden_root_info->bstart;
+ bindex <= hidden_root_info->bend; bindex++) {
+ struct dentry *d;
+ struct vfsmount *m;
+
+ d = hidden_root_info->lower_paths[bindex].dentry;
+ m = hidden_root_info->lower_paths[bindex].mnt;
+
+ dput(d);
+ mntput(m);
+ }
+ kfree(hidden_root_info->lower_paths);
+ kfree(hidden_root_info);
+ hidden_root_info = NULL;
+ }
+
+out_free:
+ kfree(UNIONFS_SB(sb)->data);
+ kfree(UNIONFS_SB(sb));
+ sb->s_fs_info = NULL;
+
+out:
+ if (hidden_root_info && !IS_ERR(hidden_root_info)) {
+ kfree(hidden_root_info->lower_paths);
+ kfree(hidden_root_info);
+ }
+ return err;
+}
+
+static int unionfs_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name,
+ void *raw_data, struct vfsmount *mnt)
+{
+ return get_sb_nodev(fs_type, flags, raw_data, unionfs_read_super, mnt);
+}
+
+static struct file_system_type unionfs_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "unionfs",
+ .get_sb = unionfs_get_sb,
+ .kill_sb = generic_shutdown_super,
+ .fs_flags = FS_REVAL_DOT,
+};
+
+static int init_debug = 0;
+module_param_named(debug, init_debug, int, S_IRUGO);
+MODULE_PARM_DESC(debug, "Initial Unionfs debug value.");
+
+static int __init init_unionfs_fs(void)
+{
+ int err;
+ printk("Registering unionfs " UNIONFS_VERSION "\n");
+
+ if ((err = init_filldir_cache()))
+ goto out;
+ if ((err = init_inode_cache()))
+ goto out;
+ if ((err = init_dentry_cache()))
+ goto out;
+ if ((err = init_sioq()))
+ goto out;
+ err = register_filesystem(&unionfs_fs_type);
+out:
+ if (err) {
+ fin_sioq();
+ destroy_filldir_cache();
+ destroy_inode_cache();
+ destroy_dentry_cache();
+ }
+ return err;
+}
+static void __exit exit_unionfs_fs(void)
+{
+ fin_sioq();
+ destroy_filldir_cache();
+ destroy_inode_cache();
+ destroy_dentry_cache();
+ unregister_filesystem(&unionfs_fs_type);
+ printk("Completed unionfs module unload.\n");
+}
+
+MODULE_AUTHOR("Erez Zadok, Filesystems and Storage Lab, Stony Brook University"
+ " (http://www.fsl.cs.sunysb.edu)");
+MODULE_DESCRIPTION("Unionfs " UNIONFS_VERSION
+ " (http://www.unionfs.org)");
+MODULE_LICENSE("GPL");
+
+module_init(init_unionfs_fs);
+module_exit(exit_unionfs_fs);
+
--
1.4.3.3

2006-12-04 12:36:57

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 14/35] Unionfs: Branch management functionality

From: Josef "Jeff" Sipek <[email protected]>

This patch contains the ioctls to increase the union generation and to query
which branch a file exists on.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
Signed-off-by: David Quigley <[email protected]>
Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/branchman.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 81 insertions(+), 0 deletions(-)

diff --git a/fs/unionfs/branchman.c b/fs/unionfs/branchman.c
new file mode 100644
index 0000000..168c5d5
--- /dev/null
+++ b/fs/unionfs/branchman.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2003-2006 Erez Zadok
+ * Copyright (c) 2003-2006 Charles P. Wright
+ * Copyright (c) 2005-2006 Josef 'Jeff' Sipek
+ * Copyright (c) 2005-2006 Junjiro Okajima
+ * Copyright (c) 2005 Arun M. Krishnakumar
+ * Copyright (c) 2004-2006 David P. Quigley
+ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair
+ * Copyright (c) 2003 Puja Gupta
+ * Copyright (c) 2003 Harikesavan Krishnan
+ * Copyright (c) 2003-2006 Stony Brook University
+ * Copyright (c) 2003-2006 The Research Foundation of State University of New York
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "union.h"
+
+/* increase the superblock generation count; effectively invalidating every
+ * upper inode, dentry and file object */
+int unionfs_ioctl_incgen(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct super_block *sb;
+ int gen;
+
+ sb = file->f_dentry->d_sb;
+
+ unionfs_write_lock(sb);
+
+ gen = atomic_inc_return(&UNIONFS_SB(sb)->generation);
+
+ atomic_set(&UNIONFS_D(sb->s_root)->generation, gen);
+ atomic_set(&UNIONFS_I(sb->s_root->d_inode)->generation, gen);
+
+ unionfs_write_unlock(sb);
+
+ return gen;
+}
+
+/* return to userspace the branch indices containing the file in question
+ *
+ * We use fd_set and therefore we are limited to the number of the branches
+ * to FD_SETSIZE, which is currently 1024 - plenty for most people
+ */
+int unionfs_ioctl_queryfile(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int err = 0;
+ fd_set branchlist;
+
+ int bstart = 0, bend = 0, bindex = 0;
+ struct dentry *dentry, *hidden_dentry;
+
+ dentry = file->f_dentry;
+ lock_dentry(dentry);
+ if ((err = unionfs_partial_lookup(dentry)))
+ goto out;
+ bstart = dbstart(dentry);
+ bend = dbend(dentry);
+
+ FD_ZERO(&branchlist);
+
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ if (!hidden_dentry)
+ continue;
+ if (hidden_dentry->d_inode)
+ FD_SET(bindex, &branchlist);
+ }
+
+ err = copy_to_user((void __user *)arg, &branchlist, sizeof(fd_set));
+ if (err)
+ err = -EFAULT;
+
+out:
+ unlock_dentry(dentry);
+ return err < 0 ? err : bend;
+}
+
--
1.4.3.3

2006-12-04 12:37:05

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 09/35] fs/stack.c should #include <linux/fs_stack.h>

From: Adrian Bunk <[email protected]>

Every file should #include the headers containing the prototypes for
its global functions.

Signed-off-by: Adrian Bunk <[email protected]>
Acked-by: Josef Sipek <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
---
fs/stack.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/fs/stack.c b/fs/stack.c
index 03987f2..8ffb880 100644
--- a/fs/stack.c
+++ b/fs/stack.c
@@ -1,5 +1,6 @@
#include <linux/module.h>
#include <linux/fs.h>
+#include <linux/fs_stack.h>

/* does _NOT_ require i_mutex to be held.
*
--
1.4.3.3

2006-12-04 12:36:13

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 31/35] Unionfs: Internal include file

From: Josef "Jeff" Sipek <[email protected]>

This patch contains an internal Unionfs include file. The include file is
specific to kernel code only, and therefore is separate from
include/linux/unionfs.h.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
Signed-off-by: David Quigley <[email protected]>
Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/union.h | 479 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 479 insertions(+), 0 deletions(-)

diff --git a/fs/unionfs/union.h b/fs/unionfs/union.h
new file mode 100644
index 0000000..ff0b814
--- /dev/null
+++ b/fs/unionfs/union.h
@@ -0,0 +1,479 @@
+/*
+ * Copyright (c) 2003-2006 Erez Zadok
+ * Copyright (c) 2003-2006 Charles P. Wright
+ * Copyright (c) 2005-2006 Josef Sipek
+ * Copyright (c) 2005 Arun M. Krishnakumar
+ * Copyright (c) 2004-2006 David P. Quigley
+ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair
+ * Copyright (c) 2003 Puja Gupta
+ * Copyright (c) 2003 Harikesavan Krishnan
+ * Copyright (c) 2003-2006 Stony Brook University
+ * Copyright (c) 2003-2006 The Research Foundation of State University of New York
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _UNION_H_
+#define _UNION_H_
+
+#include <asm/mman.h>
+#include <asm/system.h>
+#include <linux/dcache.h>
+#include <linux/file.h>
+#include <linux/list.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/page-flags.h>
+#include <linux/pagemap.h>
+#include <linux/poll.h>
+#include <linux/security.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <linux/statfs.h>
+#include <linux/string.h>
+#include <linux/vmalloc.h>
+#include <linux/writeback.h>
+#include <linux/fs_stack.h>
+
+#include <linux/union_fs.h>
+/* the file system name */
+#define UNIONFS_NAME "unionfs"
+
+/* unionfs file systems superblock magic */
+#define UNIONFS_SUPER_MAGIC 0xf15f083d
+
+/* unionfs root inode number */
+#define UNIONFS_ROOT_INO 1
+
+/* Mount time flags */
+#define MOUNT_FLAG(sb) (UNIONFS_SB(sb)->mount_flag)
+
+/* number of characters while generating unique temporary file names */
+#define UNIONFS_TMPNAM_LEN 12
+
+/* number of times we try to get a unique temporary file name */
+#define GET_TMPNAM_MAX_RETRY 5
+
+/* Operations vectors defined in specific files. */
+extern struct file_operations unionfs_main_fops;
+extern struct file_operations unionfs_dir_fops;
+extern struct inode_operations unionfs_main_iops;
+extern struct inode_operations unionfs_dir_iops;
+extern struct inode_operations unionfs_symlink_iops;
+extern struct super_operations unionfs_sops;
+extern struct dentry_operations unionfs_dops;
+
+/* How long should an entry be allowed to persist */
+#define RDCACHE_JIFFIES 5*HZ
+
+/* file private data. */
+struct unionfs_file_info {
+ int bstart;
+ int bend;
+ atomic_t generation;
+
+ struct unionfs_dir_state *rdstate;
+ struct file **lower_files;
+};
+
+/* unionfs inode data in memory */
+struct unionfs_inode_info {
+ int bstart;
+ int bend;
+ atomic_t generation;
+ int stale;
+ /* Stuff for readdir over NFS. */
+ spinlock_t rdlock;
+ struct list_head readdircache;
+ int rdcount;
+ int hashsize;
+ int cookie;
+
+ /* The hidden inodes */
+ struct inode **lower_inodes;
+ /* to keep track of reads/writes for unlinks before closes */
+ atomic_t totalopens;
+
+ struct inode vfs_inode;
+};
+
+/* unionfs dentry data in memory */
+struct unionfs_dentry_info {
+ /* The semaphore is used to lock the dentry as soon as we get into a
+ * unionfs function from the VFS. Our lock ordering is that children
+ * go before their parents. */
+ struct semaphore sem;
+ int bstart;
+ int bend;
+ int bopaque;
+ int bcount;
+ atomic_t generation;
+ struct path *lower_paths;
+};
+
+/* These are the pointers to our various objects. */
+struct unionfs_data {
+ struct super_block *sb;
+ struct vfsmount *hidden_mnt;
+ atomic_t sbcount;
+ int branchperms;
+};
+
+/* unionfs super-block data in memory */
+struct unionfs_sb_info {
+ int bend;
+
+ atomic_t generation;
+ unsigned long mount_flag;
+ struct rw_semaphore rwsem;
+
+ struct unionfs_data *data;
+};
+
+/*
+ * structure for making the linked list of entries by readdir on left branch
+ * to compare with entries on right branch
+ */
+struct filldir_node {
+ struct list_head file_list; /* list for directory entries */
+ char *name; /* name entry */
+ int hash; /* name hash */
+ int namelen; /* name len since name is not 0 terminated */
+ int bindex; /* we can check for duplicate whiteouts and files in the same branch in order to return -EIO. */
+ int whiteout; /* is this a whiteout entry? */
+ char iname[DNAME_INLINE_LEN_MIN]; /* Inline name, so we don't need to separately kmalloc small ones */
+};
+
+/* Directory hash table. */
+struct unionfs_dir_state {
+ unsigned int cookie; /* The cookie, which is based off of rdversion */
+ unsigned int offset; /* The entry we have returned. */
+ int bindex;
+ loff_t dirpos; /* The offset within the lower level directory. */
+ int size; /* How big is the hash table? */
+ int hashentries; /* How many entries have been inserted? */
+ unsigned long access;
+ /* This cache list is used when the inode keeps us around. */
+ struct list_head cache;
+ struct list_head list[0];
+};
+
+/* include miscellaneous macros */
+#include "fanout.h"
+#include "sioq.h"
+
+/* Cache creation/deletion routines. */
+void destroy_filldir_cache(void);
+int init_filldir_cache(void);
+int init_inode_cache(void);
+void destroy_inode_cache(void);
+int init_dentry_cache(void);
+void destroy_dentry_cache(void);
+
+/* Initialize and free readdir-specific state. */
+int init_rdstate(struct file *file);
+struct unionfs_dir_state *alloc_rdstate(struct inode *inode, int bindex);
+struct unionfs_dir_state *find_rdstate(struct inode *inode, loff_t fpos);
+void free_rdstate(struct unionfs_dir_state *state);
+int add_filldir_node(struct unionfs_dir_state *rdstate, const char *name,
+ int namelen, int bindex, int whiteout);
+struct filldir_node *find_filldir_node(struct unionfs_dir_state *rdstate,
+ const char *name, int namelen);
+
+struct dentry **alloc_new_dentries(int objs);
+struct unionfs_data *alloc_new_data(int objs);
+
+/* We can only use 32-bits of offset for rdstate --- blech! */
+#define DIREOF (0xfffff)
+#define RDOFFBITS 20 /* This is the number of bits in DIREOF. */
+#define MAXRDCOOKIE (0xfff)
+/* Turn an rdstate into an offset. */
+static inline off_t rdstate2offset(struct unionfs_dir_state *buf)
+{
+ off_t tmp;
+ tmp = ((buf->cookie & MAXRDCOOKIE) << RDOFFBITS)
+ | (buf->offset & DIREOF);
+ return tmp;
+}
+
+#define unionfs_read_lock(sb) down_read(&UNIONFS_SB(sb)->rwsem)
+#define unionfs_read_unlock(sb) up_read(&UNIONFS_SB(sb)->rwsem)
+#define unionfs_write_lock(sb) down_write(&UNIONFS_SB(sb)->rwsem)
+#define unionfs_write_unlock(sb) up_write(&UNIONFS_SB(sb)->rwsem)
+
+/* The double lock function needs to go after the debugmacros, so that
+ * dtopd is defined. */
+static inline void double_lock_dentry(struct dentry *d1, struct dentry *d2)
+{
+ if (d2 < d1) {
+ struct dentry *tmp = d1;
+ d1 = d2;
+ d2 = tmp;
+ }
+ lock_dentry(d1);
+ lock_dentry(d2);
+}
+
+extern int new_dentry_private_data(struct dentry *dentry);
+void free_dentry_private_data(struct unionfs_dentry_info *udi);
+void update_bstart(struct dentry *dentry);
+
+/*
+ * EXTERNALS:
+ */
+/* replicates the directory structure upto given dentry in given branch */
+extern struct dentry *create_parents(struct inode *dir, struct dentry *dentry,
+ int bindex);
+struct dentry *create_parents_named(struct inode *dir, struct dentry *dentry,
+ const char *name, int bindex);
+
+/* check if two branches overlap */
+extern int is_branch_overlap(struct dentry *dent1, struct dentry *dent2);
+
+/* partial lookup */
+extern int unionfs_partial_lookup(struct dentry *dentry);
+
+/* Pass an unionfs dentry and an index and it will try to create a whiteout in branch 'index'.
+ On error, it will proceed to a branch to the left */
+extern int create_whiteout(struct dentry *dentry, int start);
+/* copies a file from dbstart to newbindex branch */
+extern int copyup_file(struct inode *dir, struct file *file, int bstart,
+ int newbindex, loff_t size);
+extern int copyup_named_file(struct inode *dir, struct file *file,
+ char *name, int bstart, int new_bindex,
+ loff_t len);
+/* copies a dentry from dbstart to newbindex branch */
+extern int copyup_dentry(struct inode *dir, struct dentry *dentry, int bstart,
+ int new_bindex, struct file **copyup_file, loff_t len);
+extern int copyup_named_dentry(struct inode *dir, struct dentry *dentry,
+ int bstart, int new_bindex, const char *name,
+ int namelen, struct file **copyup_file,
+ loff_t len);
+
+extern int remove_whiteouts(struct dentry *dentry, struct dentry *hidden_dentry,
+ int bindex);
+
+extern int do_delete_whiteouts(struct dentry *dentry, int bindex,
+ struct unionfs_dir_state *namelist);
+
+/* Is this directory empty: 0 if it is empty, -ENOTEMPTY if not. */
+extern int check_empty(struct dentry *dentry,
+ struct unionfs_dir_state **namelist);
+/* Delete whiteouts from this directory in branch bindex. */
+extern int delete_whiteouts(struct dentry *dentry, int bindex,
+ struct unionfs_dir_state *namelist);
+
+/* Re-lookup a hidden dentry. */
+extern int unionfs_refresh_hidden_dentry(struct dentry *dentry, int bindex);
+
+extern void unionfs_reinterpose(struct dentry *this_dentry);
+extern struct super_block *unionfs_duplicate_super(struct super_block *sb);
+
+/* Locking functions. */
+extern int unionfs_setlk(struct file *file, int cmd, struct file_lock *fl);
+extern int unionfs_getlk(struct file *file, struct file_lock *fl);
+
+/* Common file operations. */
+extern int unionfs_file_revalidate(struct file *file, int willwrite);
+extern int unionfs_open(struct inode *inode, struct file *file);
+extern int unionfs_file_release(struct inode *inode, struct file *file);
+extern int unionfs_flush(struct file *file, fl_owner_t id);
+extern long unionfs_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg);
+
+/* Inode operations */
+extern int unionfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry);
+int unionfs_unlink(struct inode *dir, struct dentry *dentry);
+int unionfs_rmdir(struct inode *dir, struct dentry *dentry);
+
+int unionfs_d_revalidate(struct dentry *dentry, struct nameidata *nd);
+
+/* The values for unionfs_interpose's flag. */
+#define INTERPOSE_DEFAULT 0
+#define INTERPOSE_LOOKUP 1
+#define INTERPOSE_REVAL 2
+#define INTERPOSE_REVAL_NEG 3
+#define INTERPOSE_PARTIAL 4
+
+extern int unionfs_interpose(struct dentry *this_dentry, struct super_block *sb,
+ int flag);
+
+/* Branch management ioctls. */
+int unionfs_ioctl_incgen(struct file *file, unsigned int cmd,
+ unsigned long arg);
+int unionfs_ioctl_queryfile(struct file *file, unsigned int cmd,
+ unsigned long arg);
+
+/* Verify that a branch is valid. */
+int check_branch(struct nameidata *nd);
+
+/* The root directory is unhashed, but isn't deleted. */
+static inline int d_deleted(struct dentry *d)
+{
+ return d_unhashed(d) && (d != d->d_sb->s_root);
+}
+
+/* returns the sum of the n_link values of all the underlying inodes of the
+ * passed inode */
+static inline int unionfs_get_nlinks(struct inode *inode)
+{
+ int sum_nlinks = 0;
+ int dirs = 0;
+ int bindex;
+ struct inode *hidden_inode;
+
+ /* don't bother to do all the work since we're unlinked */
+ if (inode->i_nlink == 0)
+ return 0;
+
+ if (!S_ISDIR(inode->i_mode))
+ return unionfs_lower_inode(inode)->i_nlink;
+
+ for (bindex = ibstart(inode); bindex <= ibend(inode); bindex++) {
+ hidden_inode = unionfs_lower_inode_idx(inode, bindex);
+ if (!hidden_inode || !S_ISDIR(hidden_inode->i_mode))
+ continue;
+ BUG_ON(hidden_inode->i_nlink < 0);
+
+ /* A deleted directory. */
+ if (hidden_inode->i_nlink == 0)
+ continue;
+ dirs++;
+ /* A broken directory (e.g., squashfs). */
+ if (hidden_inode->i_nlink == 1)
+ sum_nlinks += 2;
+ else
+ sum_nlinks += (hidden_inode->i_nlink - 2);
+ }
+
+ if (!dirs)
+ return 0;
+ return sum_nlinks + 2;
+}
+
+struct dentry *unionfs_lookup_backend(struct dentry *dentry, struct nameidata *nd, int lookupmode);
+int is_stale_inode(struct inode *inode);
+void make_stale_inode(struct inode *inode);
+
+#define IS_SET(sb, check_flag) (check_flag & MOUNT_FLAG(sb))
+
+/* unionfs_permission, check if we should bypass error to facilitate copyup */
+#define IS_COPYUP_ERR(err) ((err) == -EROFS)
+
+/* unionfs_open, check if we need to copyup the file */
+#define OPEN_WRITE_FLAGS (O_WRONLY | O_RDWR | O_APPEND)
+#define IS_WRITE_FLAG(flag) ((flag) & (OPEN_WRITE_FLAGS))
+
+static inline int branchperms(struct super_block *sb, int index)
+{
+ BUG_ON(index < 0);
+
+ return UNIONFS_SB(sb)->data[index].branchperms;
+}
+
+static inline int set_branchperms(struct super_block *sb, int index, int perms)
+{
+ BUG_ON(index < 0);
+
+ UNIONFS_SB(sb)->data[index].branchperms = perms;
+
+ return perms;
+}
+
+/* Is this file on a read-only branch? */
+static inline int is_robranch_super(struct super_block *sb, int index)
+{
+ return (!(branchperms(sb, index) & MAY_WRITE)) ? -EROFS : 0;
+}
+
+/* Is this file on a read-only branch? */
+static inline int is_robranch_idx(struct dentry *dentry, int index)
+{
+ int err = 0;
+
+ BUG_ON(index < 0);
+
+ if ((!(branchperms(dentry->d_sb, index) & MAY_WRITE)) ||
+ IS_RDONLY(unionfs_lower_dentry_idx(dentry, index)->d_inode))
+ err = -EROFS;
+
+ return err;
+}
+
+static inline int is_robranch(struct dentry *dentry)
+{
+ int index;
+
+ index = UNIONFS_D(dentry)->bstart;
+ BUG_ON(index < 0);
+
+ return is_robranch_idx(dentry, index);
+}
+
+/* What do we use for whiteouts. */
+#define UNIONFS_WHPFX ".wh."
+#define UNIONFS_WHLEN 4
+/* If a directory contains this file, then it is opaque. We start with the
+ * .wh. flag so that it is blocked by lookup.
+ */
+#define UNIONFS_DIR_OPAQUE_NAME "__dir_opaque"
+#define UNIONFS_DIR_OPAQUE UNIONFS_WHPFX UNIONFS_DIR_OPAQUE_NAME
+
+/* construct whiteout filename */
+static inline char *alloc_whname(const char *name, int len)
+{
+ char *buf;
+
+ buf = kmalloc(len + UNIONFS_WHLEN + 1, GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ strcpy(buf, UNIONFS_WHPFX);
+ strlcat(buf, name, len + UNIONFS_WHLEN + 1);
+
+ return buf;
+}
+
+#define VALID_MOUNT_FLAGS (0)
+
+/*
+ * MACROS:
+ */
+
+#ifndef DEFAULT_POLLMASK
+#define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)
+#endif
+
+/*
+ * EXTERNALS:
+ */
+
+/* These two functions are here because it is kind of daft to copy and paste the
+ * contents of the two functions to 32+ places in unionfs
+ */
+static inline struct dentry *lock_parent(struct dentry *dentry)
+{
+ struct dentry *dir = dget(dentry->d_parent);
+
+ mutex_lock(&dir->d_inode->i_mutex);
+ return dir;
+}
+
+static inline void unlock_dir(struct dentry *dir)
+{
+ mutex_unlock(&dir->d_inode->i_mutex);
+ dput(dir);
+}
+
+extern int make_dir_opaque(struct dentry *dir, int bindex);
+
+#endif /* not _UNION_H_ */
+
--
1.4.3.3

2006-12-04 12:38:05

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 02/35] fsstack: Remove unneeded wrapper

From: Andrew Morton <[email protected]>

Remove unneeded wrapper.

Cc: Josef "Jeff" Sipek <[email protected]>
Cc: Michael Halcrow <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
---
fs/stack.c | 10 ++++------
include/linux/fs_stack.h | 12 ++----------
2 files changed, 6 insertions(+), 16 deletions(-)

diff --git a/fs/stack.c b/fs/stack.c
index 5f6f12d..03987f2 100644
--- a/fs/stack.c
+++ b/fs/stack.c
@@ -11,13 +11,13 @@ void fsstack_copy_inode_size(struct inod
i_size_write(dst, i_size_read((struct inode *)src));
dst->i_blocks = src->i_blocks;
}
+EXPORT_SYMBOL_GPL(fsstack_copy_inode_size);

/* copy all attributes; get_nlinks is optional way to override the i_nlink
* copying
*/
-void __fsstack_copy_attr_all(struct inode *dest,
- const struct inode *src,
- int (*get_nlinks)(struct inode *))
+void fsstack_copy_attr_all(struct inode *dest, const struct inode *src,
+ int (*get_nlinks)(struct inode *))
{
if (!get_nlinks)
dest->i_nlink = src->i_nlink;
@@ -34,6 +34,4 @@ void __fsstack_copy_attr_all(struct inod
dest->i_blkbits = src->i_blkbits;
dest->i_flags = src->i_flags;
}
-
-EXPORT_SYMBOL_GPL(fsstack_copy_inode_size);
-EXPORT_SYMBOL_GPL(__fsstack_copy_attr_all);
+EXPORT_SYMBOL_GPL(fsstack_copy_attr_all);
diff --git a/include/linux/fs_stack.h b/include/linux/fs_stack.h
index 56b3e09..bb516ce 100644
--- a/include/linux/fs_stack.h
+++ b/include/linux/fs_stack.h
@@ -8,9 +8,8 @@
#include <linux/fs.h>

/* externs for fs/stack.c */
-extern void __fsstack_copy_attr_all(struct inode *dest,
- const struct inode *src,
- int (*get_nlinks)(struct inode *));
+extern void fsstack_copy_attr_all(struct inode *dest, const struct inode *src,
+ int (*get_nlinks)(struct inode *));

extern void fsstack_copy_inode_size(struct inode *dst, const struct inode *src);

@@ -29,11 +28,4 @@ static inline void fsstack_copy_attr_tim
dest->i_ctime = src->i_ctime;
}

-static inline void fsstack_copy_attr_all(struct inode *dest,
- const struct inode *src)
-{
- __fsstack_copy_attr_all(dest, src, NULL);
-}
-
#endif /* _LINUX_FS_STACK_H */
-
--
1.4.3.3

2006-12-04 12:36:13

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 27/35] Unionfs: Handling of stale inodes

From: Josef "Jeff" Sipek <[email protected]>

Provides nicer handling of stale inodes.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
Signed-off-by: David Quigley <[email protected]>
Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/stale_inode.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 114 insertions(+), 0 deletions(-)

diff --git a/fs/unionfs/stale_inode.c b/fs/unionfs/stale_inode.c
new file mode 100644
index 0000000..4255dfd
--- /dev/null
+++ b/fs/unionfs/stale_inode.c
@@ -0,0 +1,114 @@
+/*
+ * Adpated from linux/fs/bad_inode.c
+ *
+ * Copyright (C) 1997, Stephen Tweedie
+ *
+ * Provide stub functions for "stale" inodes, a bit friendlier than the
+ * -EIO that bad_inode.c does.
+ */
+
+#include <linux/version.h>
+
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/sched.h>
+
+static struct address_space_operations unionfs_stale_aops;
+
+/* declarations for "sparse */
+extern struct inode_operations stale_inode_ops;
+
+/*
+ * The follow_link operation is special: it must behave as a no-op
+ * so that a stale root inode can at least be unmounted. To do this
+ * we must dput() the base and return the dentry with a dget().
+ */
+static void *stale_follow_link(struct dentry *dent, struct nameidata *nd)
+{
+ return ERR_PTR(vfs_follow_link(nd, ERR_PTR(-ESTALE)));
+}
+
+static int return_ESTALE(void)
+{
+ return -ESTALE;
+}
+
+#define ESTALE_ERROR ((void *) (return_ESTALE))
+
+static struct file_operations stale_file_ops = {
+ .llseek = ESTALE_ERROR,
+ .read = ESTALE_ERROR,
+ .write = ESTALE_ERROR,
+ .readdir = ESTALE_ERROR,
+ .poll = ESTALE_ERROR,
+ .ioctl = ESTALE_ERROR,
+ .mmap = ESTALE_ERROR,
+ .open = ESTALE_ERROR,
+ .flush = ESTALE_ERROR,
+ .release = ESTALE_ERROR,
+ .fsync = ESTALE_ERROR,
+ .fasync = ESTALE_ERROR,
+ .lock = ESTALE_ERROR,
+};
+
+struct inode_operations stale_inode_ops = {
+ .create = ESTALE_ERROR,
+ .lookup = ESTALE_ERROR,
+ .link = ESTALE_ERROR,
+ .unlink = ESTALE_ERROR,
+ .symlink = ESTALE_ERROR,
+ .mkdir = ESTALE_ERROR,
+ .rmdir = ESTALE_ERROR,
+ .mknod = ESTALE_ERROR,
+ .rename = ESTALE_ERROR,
+ .readlink = ESTALE_ERROR,
+ .follow_link = stale_follow_link,
+ .truncate = ESTALE_ERROR,
+ .permission = ESTALE_ERROR,
+};
+
+/*
+ * When a filesystem is unable to read an inode due to an I/O error in
+ * its read_inode() function, it can call make_stale_inode() to return a
+ * set of stubs which will return ESTALE errors as required.
+ *
+ * We only need to do limited initialisation: all other fields are
+ * preinitialised to zero automatically.
+ */
+
+/**
+ * make_stale_inode - mark an inode stale due to an I/O error
+ * @inode: Inode to mark stale
+ *
+ * When an inode cannot be read due to a media or remote network
+ * failure this function makes the inode "stale" and causes I/O operations
+ * on it to fail from this point on.
+ */
+
+void make_stale_inode(struct inode *inode)
+{
+ inode->i_mode = S_IFREG;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ inode->i_op = &stale_inode_ops;
+ inode->i_fop = &stale_file_ops;
+ inode->i_mapping->a_ops = &unionfs_stale_aops;
+}
+
+/*
+ * This tests whether an inode has been flagged as stale. The test uses
+ * &stale_inode_ops to cover the case of invalidated inodes as well as
+ * those created by make_stale_inode() above.
+ */
+
+/**
+ * is_stale_inode - is an inode errored
+ * @inode: inode to test
+ *
+ * Returns true if the inode in question has been marked as stale.
+ */
+
+int is_stale_inode(struct inode *inode)
+{
+ return (inode->i_op == &stale_inode_ops);
+}
+
--
1.4.3.3

2006-12-04 12:38:59

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 18/35] Unionfs: File operations

From: Josef "Jeff" Sipek <[email protected]>

This patch provides the file operations for Unionfs.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
Signed-off-by: David Quigley <[email protected]>
Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/file.c | 258 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 258 insertions(+), 0 deletions(-)

diff --git a/fs/unionfs/file.c b/fs/unionfs/file.c
new file mode 100644
index 0000000..3dc9f8f
--- /dev/null
+++ b/fs/unionfs/file.c
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2003-2006 Erez Zadok
+ * Copyright (c) 2003-2006 Charles P. Wright
+ * Copyright (c) 2005-2006 Josef 'Jeff' Sipek
+ * Copyright (c) 2005-2006 Junjiro Okajima
+ * Copyright (c) 2005 Arun M. Krishnakumar
+ * Copyright (c) 2004-2006 David P. Quigley
+ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair
+ * Copyright (c) 2003 Puja Gupta
+ * Copyright (c) 2003 Harikesavan Krishnan
+ * Copyright (c) 2003-2006 Stony Brook University
+ * Copyright (c) 2003-2006 The Research Foundation of State University of New York
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "union.h"
+
+/* declarations for sparse */
+extern ssize_t unionfs_read(struct file *, char __user *, size_t, loff_t *);
+extern ssize_t unionfs_write(struct file *, const char __user *, size_t,
+ loff_t *);
+
+/*******************
+ * File Operations *
+ *******************/
+
+static loff_t unionfs_llseek(struct file *file, loff_t offset, int origin)
+{
+ loff_t err;
+ struct file *hidden_file = NULL;
+
+ if ((err = unionfs_file_revalidate(file, 0)))
+ goto out;
+
+ hidden_file = unionfs_lower_file(file);
+ /* always set hidden position to this one */
+ hidden_file->f_pos = file->f_pos;
+
+ memcpy(&hidden_file->f_ra, &file->f_ra, sizeof(struct file_ra_state));
+
+ if (hidden_file->f_op && hidden_file->f_op->llseek)
+ err = hidden_file->f_op->llseek(hidden_file, offset, origin);
+ else
+ err = generic_file_llseek(hidden_file, offset, origin);
+
+ if (err < 0)
+ goto out;
+ if (err != file->f_pos) {
+ file->f_pos = err;
+ file->f_version++;
+ }
+out:
+ return err;
+}
+
+ssize_t unionfs_read(struct file * file, char __user * buf, size_t count,
+ loff_t * ppos)
+{
+ struct file *hidden_file;
+ loff_t pos = *ppos;
+ int err;
+
+ if ((err = unionfs_file_revalidate(file, 0)))
+ goto out;
+
+ err = -EINVAL;
+ hidden_file = unionfs_lower_file(file);
+ if (!hidden_file->f_op || !hidden_file->f_op->read)
+ goto out;
+
+ err = hidden_file->f_op->read(hidden_file, buf, count, &pos);
+ *ppos = pos;
+
+out:
+ return err;
+}
+
+ssize_t __unionfs_write(struct file * file, const char __user * buf,
+ size_t count, loff_t * ppos)
+{
+ int err = -EINVAL;
+ struct file *hidden_file = NULL;
+ struct inode *inode;
+ struct inode *hidden_inode;
+ loff_t pos = *ppos;
+ int bstart, bend;
+
+ inode = file->f_dentry->d_inode;
+
+ bstart = fbstart(file);
+ bend = fbend(file);
+
+ BUG_ON(bstart == -1);
+
+ hidden_file = unionfs_lower_file(file);
+ hidden_inode = hidden_file->f_dentry->d_inode;
+
+ if (!hidden_file->f_op || !hidden_file->f_op->write)
+ goto out;
+
+ /* adjust for append -- seek to the end of the file */
+ if (file->f_flags & O_APPEND)
+ pos = inode->i_size;
+
+ err = hidden_file->f_op->write(hidden_file, buf, count, &pos);
+
+ /*
+ * copy ctime and mtime from lower layer attributes
+ * atime is unchanged for both layers
+ */
+ if (err >= 0)
+ fsstack_copy_attr_times(inode, hidden_inode);
+
+ *ppos = pos;
+
+ /* update this inode's size */
+ if (pos > inode->i_size)
+ inode->i_size = pos;
+out:
+ return err;
+}
+
+ssize_t unionfs_write(struct file * file, const char __user * buf, size_t count,
+ loff_t * ppos)
+{
+ int err = 0;
+
+ if ((err = unionfs_file_revalidate(file, 1)))
+ goto out;
+
+ err = __unionfs_write(file, buf, count, ppos);
+
+out:
+ return err;
+}
+
+static int unionfs_file_readdir(struct file *file, void *dirent,
+ filldir_t filldir)
+{
+ return -ENOTDIR;
+}
+
+static unsigned int unionfs_poll(struct file *file, poll_table * wait)
+{
+ unsigned int mask = DEFAULT_POLLMASK;
+ struct file *hidden_file = NULL;
+
+ if (unionfs_file_revalidate(file, 0)) {
+ /* We should pretend an error happend. */
+ mask = POLLERR | POLLIN | POLLOUT;
+ goto out;
+ }
+
+ hidden_file = unionfs_lower_file(file);
+
+ if (!hidden_file->f_op || !hidden_file->f_op->poll)
+ goto out;
+
+ mask = hidden_file->f_op->poll(hidden_file, wait);
+
+out:
+ return mask;
+}
+
+static int __do_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ int err;
+ struct file *hidden_file;
+
+ hidden_file = unionfs_lower_file(file);
+
+ err = -ENODEV;
+ if (!hidden_file->f_op || !hidden_file->f_op->mmap)
+ goto out;
+
+ vma->vm_file = hidden_file;
+ err = hidden_file->f_op->mmap(hidden_file, vma);
+ get_file(hidden_file); /* make sure it doesn't get freed on us */
+ fput(file); /* no need to keep extra ref on ours */
+out:
+ return err;
+}
+
+static int unionfs_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ int err = 0;
+ int willwrite;
+
+ /* This might could be deferred to mmap's writepage. */
+ willwrite = ((vma->vm_flags | VM_SHARED | VM_WRITE) == vma->vm_flags);
+ if ((err = unionfs_file_revalidate(file, willwrite)))
+ goto out;
+
+ err = __do_mmap(file, vma);
+
+out:
+ return err;
+}
+
+static int unionfs_fsync(struct file *file, struct dentry *dentry, int datasync)
+{
+ int err;
+ struct file *hidden_file = NULL;
+
+ if ((err = unionfs_file_revalidate(file, 1)))
+ goto out;
+
+ hidden_file = unionfs_lower_file(file);
+
+ err = -EINVAL;
+ if (!hidden_file->f_op || !hidden_file->f_op->fsync)
+ goto out;
+
+ mutex_lock(&hidden_file->f_dentry->d_inode->i_mutex);
+ err = hidden_file->f_op->fsync(hidden_file, hidden_file->f_dentry,
+ datasync);
+ mutex_unlock(&hidden_file->f_dentry->d_inode->i_mutex);
+
+out:
+ return err;
+}
+
+/* SP: disabled as none of the other in kernel fs's seem to use it */
+static int unionfs_fasync(int fd, struct file *file, int flag)
+{
+ int err = 0;
+ struct file *hidden_file = NULL;
+
+ if ((err = unionfs_file_revalidate(file, 1)))
+ goto out;
+
+ hidden_file = unionfs_lower_file(file);
+
+ if (hidden_file->f_op && hidden_file->f_op->fasync)
+ err = hidden_file->f_op->fasync(fd, hidden_file, flag);
+
+out:
+ return err;
+}
+
+struct file_operations unionfs_main_fops = {
+ .llseek = unionfs_llseek,
+ .read = unionfs_read,
+ .write = unionfs_write,
+ .readdir = unionfs_file_readdir,
+ .poll = unionfs_poll,
+ .unlocked_ioctl = unionfs_ioctl,
+ .mmap = unionfs_mmap,
+ .open = unionfs_open,
+ .flush = unionfs_flush,
+ .release = unionfs_file_release,
+ .fsync = unionfs_fsync,
+ .fasync = unionfs_fasync,
+};
+
--
1.4.3.3

2006-12-04 12:39:01

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 25/35] Unionfs: Rename

From: Josef "Jeff" Sipek <[email protected]>

This patch provides rename functionality for Unionfs.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
Signed-off-by: David Quigley <[email protected]>
Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/rename.c | 442 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 442 insertions(+), 0 deletions(-)

diff --git a/fs/unionfs/rename.c b/fs/unionfs/rename.c
new file mode 100644
index 0000000..f0c3461
--- /dev/null
+++ b/fs/unionfs/rename.c
@@ -0,0 +1,442 @@
+/*
+ * Copyright (c) 2003-2006 Erez Zadok
+ * Copyright (c) 2003-2006 Charles P. Wright
+ * Copyright (c) 2005-2006 Josef 'Jeff' Sipek
+ * Copyright (c) 2005-2006 Junjiro Okajima
+ * Copyright (c) 2005 Arun M. Krishnakumar
+ * Copyright (c) 2004-2006 David P. Quigley
+ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair
+ * Copyright (c) 2003 Puja Gupta
+ * Copyright (c) 2003 Harikesavan Krishnan
+ * Copyright (c) 2003-2006 Stony Brook University
+ * Copyright (c) 2003-2006 The Research Foundation of State University of New York
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "union.h"
+
+static int do_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry,
+ int bindex, struct dentry **wh_old)
+{
+ int err = 0;
+ struct dentry *hidden_old_dentry;
+ struct dentry *hidden_new_dentry;
+ struct dentry *hidden_old_dir_dentry;
+ struct dentry *hidden_new_dir_dentry;
+ struct dentry *hidden_wh_dentry;
+ struct dentry *hidden_wh_dir_dentry;
+ char *wh_name = NULL;
+
+ hidden_new_dentry = unionfs_lower_dentry_idx(new_dentry, bindex);
+ hidden_old_dentry = unionfs_lower_dentry_idx(old_dentry, bindex);
+
+ if (!hidden_new_dentry) {
+ hidden_new_dentry =
+ create_parents(new_dentry->d_parent->d_inode, new_dentry, bindex);
+ if (IS_ERR(hidden_new_dentry)) {
+ printk(KERN_DEBUG "error creating directory tree for"
+ " rename, bindex = %d, err = %ld\n",
+ bindex, PTR_ERR(hidden_new_dentry));
+ err = PTR_ERR(hidden_new_dentry);
+ goto out;
+ }
+ }
+
+ wh_name = alloc_whname(new_dentry->d_name.name, new_dentry->d_name.len);
+ if (IS_ERR(wh_name)) {
+ err = PTR_ERR(wh_name);
+ goto out;
+ }
+
+ hidden_wh_dentry = lookup_one_len(wh_name, hidden_new_dentry->d_parent,
+ new_dentry->d_name.len + UNIONFS_WHLEN);
+ if (IS_ERR(hidden_wh_dentry)) {
+ err = PTR_ERR(hidden_wh_dentry);
+ goto out;
+ }
+
+ if (hidden_wh_dentry->d_inode) {
+ /* get rid of the whiteout that is existing */
+ if (hidden_new_dentry->d_inode) {
+ printk(KERN_WARNING "Both a whiteout and a dentry"
+ " exist when doing a rename!\n");
+ err = -EIO;
+
+ dput(hidden_wh_dentry);
+ goto out;
+ }
+
+ hidden_wh_dir_dentry = lock_parent(hidden_wh_dentry);
+ if (!(err = is_robranch_super(old_dentry->d_sb, bindex)))
+ err = vfs_unlink(hidden_wh_dir_dentry->d_inode,
+ hidden_wh_dentry);
+
+ dput(hidden_wh_dentry);
+ unlock_dir(hidden_wh_dir_dentry);
+ if (err)
+ goto out;
+ } else
+ dput(hidden_wh_dentry);
+
+ dget(hidden_old_dentry);
+ hidden_old_dir_dentry = dget_parent(hidden_old_dentry);
+ hidden_new_dir_dentry = dget_parent(hidden_new_dentry);
+
+ lock_rename(hidden_old_dir_dentry, hidden_new_dir_dentry);
+
+ err = is_robranch_super(old_dentry->d_sb, bindex);
+ if (err)
+ goto out_unlock;
+
+ /* ready to whiteout for old_dentry. caller will create the actual
+ * whiteout, and must dput(*wh_old) */
+ if (wh_old) {
+ char *whname;
+ whname = alloc_whname(old_dentry->d_name.name,
+ old_dentry->d_name.len);
+ err = PTR_ERR(whname);
+ if (IS_ERR(whname))
+ goto out_unlock;
+ *wh_old = lookup_one_len(whname, hidden_old_dir_dentry,
+ old_dentry->d_name.len + UNIONFS_WHLEN);
+ kfree(whname);
+ err = PTR_ERR(*wh_old);
+ if (IS_ERR(*wh_old)) {
+ *wh_old = NULL;
+ goto out_unlock;
+ }
+ }
+
+ err = vfs_rename(hidden_old_dir_dentry->d_inode, hidden_old_dentry,
+ hidden_new_dir_dentry->d_inode, hidden_new_dentry);
+
+out_unlock:
+ unlock_rename(hidden_old_dir_dentry, hidden_new_dir_dentry);
+
+ dput(hidden_old_dir_dentry);
+ dput(hidden_new_dir_dentry);
+ dput(hidden_old_dentry);
+
+out:
+ if (!err) {
+ /* Fixup the newdentry. */
+ if (bindex < dbstart(new_dentry))
+ set_dbstart(new_dentry, bindex);
+ else if (bindex > dbend(new_dentry))
+ set_dbend(new_dentry, bindex);
+ }
+
+ kfree(wh_name);
+
+ return err;
+}
+
+static int do_unionfs_rename(struct inode *old_dir,
+ struct dentry *old_dentry,
+ struct inode *new_dir,
+ struct dentry *new_dentry)
+{
+ int err = 0;
+ int bindex, bwh_old;
+ int old_bstart, old_bend;
+ int new_bstart, new_bend;
+ int do_copyup = -1;
+ struct dentry *parent_dentry;
+ int local_err = 0;
+ int eio = 0;
+ int revert = 0;
+ struct dentry *wh_old = NULL;
+
+ old_bstart = dbstart(old_dentry);
+ bwh_old = old_bstart;
+ old_bend = dbend(old_dentry);
+ parent_dentry = old_dentry->d_parent;
+
+ new_bstart = dbstart(new_dentry);
+ new_bend = dbend(new_dentry);
+
+ /* Rename source to destination. */
+ err = do_rename(old_dir, old_dentry, new_dir, new_dentry, old_bstart,
+ &wh_old);
+ if (err) {
+ if (!IS_COPYUP_ERR(err))
+ goto out;
+ do_copyup = old_bstart - 1;
+ } else
+ revert = 1;
+
+ /* Unlink all instances of destination that exist to the left of
+ * bstart of source. On error, revert back, goto out.
+ */
+ for (bindex = old_bstart - 1; bindex >= new_bstart; bindex--) {
+ struct dentry *unlink_dentry;
+ struct dentry *unlink_dir_dentry;
+
+ unlink_dentry = unionfs_lower_dentry_idx(new_dentry, bindex);
+ if (!unlink_dentry)
+ continue;
+
+ unlink_dir_dentry = lock_parent(unlink_dentry);
+ if (!(err = is_robranch_super(old_dir->i_sb, bindex)))
+ err = vfs_unlink(unlink_dir_dentry->d_inode,
+ unlink_dentry);
+
+ fsstack_copy_attr_times(new_dentry->d_parent->d_inode,
+ unlink_dir_dentry->d_inode);
+ /* propagate number of hard-links */
+ new_dentry->d_parent->d_inode->i_nlink =
+ unionfs_get_nlinks(new_dentry->d_parent->d_inode);
+
+ unlock_dir(unlink_dir_dentry);
+ if (!err) {
+ if (bindex != new_bstart) {
+ dput(unlink_dentry);
+ unionfs_set_lower_dentry_idx(new_dentry, bindex, NULL);
+ }
+ } else if (IS_COPYUP_ERR(err)) {
+ do_copyup = bindex - 1;
+ } else if (revert) {
+ dput(wh_old);
+ goto revert;
+ }
+ }
+
+ if (do_copyup != -1) {
+ for (bindex = do_copyup; bindex >= 0; bindex--) {
+ /* copyup the file into some left directory, so that you can rename it */
+ err = copyup_dentry(old_dentry->d_parent->d_inode,
+ old_dentry, old_bstart, bindex, NULL,
+ old_dentry->d_inode->i_size);
+ if (!err) {
+ dput(wh_old);
+ bwh_old = bindex;
+ err = do_rename(old_dir, old_dentry, new_dir,
+ new_dentry, bindex, &wh_old);
+ break;
+ }
+ }
+ }
+
+ /* make it opaque */
+ if (S_ISDIR(old_dentry->d_inode->i_mode)) {
+ err = make_dir_opaque(old_dentry, dbstart(old_dentry));
+ if (err)
+ goto revert;
+ }
+
+ /* Create whiteout for source, only if:
+ * (1) There is more than one underlying instance of source.
+ * (2) We did a copy_up
+ */
+ if ((old_bstart != old_bend) || (do_copyup != -1)) {
+ struct dentry *hidden_parent;
+ BUG_ON(!wh_old || wh_old->d_inode || bwh_old < 0);
+ hidden_parent = lock_parent(wh_old);
+ local_err = vfs_create(hidden_parent->d_inode, wh_old, S_IRUGO,
+ NULL);
+ unlock_dir(hidden_parent);
+ if (!local_err)
+ set_dbopaque(old_dentry, bwh_old);
+ else {
+ /* We can't fix anything now, so we cop-out and use -EIO. */
+ printk(KERN_ERR "We can't create a whiteout for the "
+ "source in rename!\n");
+ err = -EIO;
+ }
+ }
+
+out:
+ dput(wh_old);
+ return err;
+
+revert:
+ /* Do revert here. */
+ local_err = unionfs_refresh_hidden_dentry(new_dentry, old_bstart);
+ if (local_err) {
+ printk(KERN_WARNING "Revert failed in rename: the new refresh "
+ "failed.\n");
+ eio = -EIO;
+ }
+
+ local_err = unionfs_refresh_hidden_dentry(old_dentry, old_bstart);
+ if (local_err) {
+ printk(KERN_WARNING "Revert failed in rename: the old refresh "
+ "failed.\n");
+ eio = -EIO;
+ goto revert_out;
+ }
+
+ if (!unionfs_lower_dentry_idx(new_dentry, bindex) ||
+ !unionfs_lower_dentry_idx(new_dentry, bindex)->d_inode) {
+ printk(KERN_WARNING "Revert failed in rename: the object "
+ "disappeared from under us!\n");
+ eio = -EIO;
+ goto revert_out;
+ }
+
+ if (unionfs_lower_dentry_idx(old_dentry, bindex) &&
+ unionfs_lower_dentry_idx(old_dentry, bindex)->d_inode) {
+ printk(KERN_WARNING "Revert failed in rename: the object was "
+ "created underneath us!\n");
+ eio = -EIO;
+ goto revert_out;
+ }
+
+ local_err = do_rename(new_dir, new_dentry, old_dir, old_dentry, old_bstart,
+ NULL);
+
+ /* If we can't fix it, then we cop-out with -EIO. */
+ if (local_err) {
+ printk(KERN_WARNING "Revert failed in rename!\n");
+ eio = -EIO;
+ }
+
+ local_err = unionfs_refresh_hidden_dentry(new_dentry, bindex);
+ if (local_err)
+ eio = -EIO;
+ local_err = unionfs_refresh_hidden_dentry(old_dentry, bindex);
+ if (local_err)
+ eio = -EIO;
+
+revert_out:
+ if (eio)
+ err = eio;
+ return err;
+}
+
+static struct dentry *lookup_whiteout(struct dentry *dentry)
+{
+ char *whname;
+ int bindex = -1, bstart = -1, bend = -1;
+ struct dentry *parent, *hidden_parent, *wh_dentry;
+
+ whname = alloc_whname(dentry->d_name.name, dentry->d_name.len);
+ if (IS_ERR(whname))
+ return (void *)whname;
+
+ parent = dget_parent(dentry);
+ lock_dentry(parent);
+ bstart = dbstart(parent);
+ bend = dbend(parent);
+ wh_dentry = ERR_PTR(-ENOENT);
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ hidden_parent = unionfs_lower_dentry_idx(parent, bindex);
+ if (!hidden_parent)
+ continue;
+ wh_dentry = lookup_one_len(whname, hidden_parent,
+ dentry->d_name.len + UNIONFS_WHLEN);
+ if (IS_ERR(wh_dentry))
+ continue;
+ if (wh_dentry->d_inode)
+ break;
+ dput(wh_dentry);
+ wh_dentry = ERR_PTR(-ENOENT);
+ }
+ unlock_dentry(parent);
+ dput(parent);
+ kfree(whname);
+ return wh_dentry;
+}
+
+/* We can't copyup a directory, because it may involve huge
+ * numbers of children, etc. Doing that in the kernel would
+ * be bad, so instead we let the userspace recurse and ask us
+ * to copy up each file separately
+ */
+static int may_rename_dir(struct dentry *dentry)
+{
+ int err, bstart;
+
+ err = check_empty(dentry, NULL);
+ if (err == -ENOTEMPTY) {
+ if (is_robranch(dentry))
+ return -EXDEV;
+ } else if (err)
+ return err;
+
+ bstart = dbstart(dentry);
+ if (dbend(dentry) == bstart || dbopaque(dentry) == bstart)
+ return 0;
+
+ set_dbstart(dentry, bstart + 1);
+ err = check_empty(dentry, NULL);
+ set_dbstart(dentry, bstart);
+ if (err == -ENOTEMPTY)
+ err = -EXDEV;
+ return err;
+}
+
+int unionfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ int err = 0;
+ struct dentry *wh_dentry;
+
+ double_lock_dentry(old_dentry, new_dentry);
+
+ if (!S_ISDIR(old_dentry->d_inode->i_mode))
+ err = unionfs_partial_lookup(old_dentry);
+ else
+ err = may_rename_dir(old_dentry);
+
+ if (err)
+ goto out;
+
+ err = unionfs_partial_lookup(new_dentry);
+ if (err)
+ goto out;
+
+ /*
+ * if new_dentry is already hidden because of whiteout,
+ * simply override it even if the whiteouted dir is not empty.
+ */
+ wh_dentry = lookup_whiteout(new_dentry);
+ if (!IS_ERR(wh_dentry))
+ dput(wh_dentry);
+ else if (new_dentry->d_inode) {
+ if (S_ISDIR(old_dentry->d_inode->i_mode) !=
+ S_ISDIR(new_dentry->d_inode->i_mode)) {
+ err = S_ISDIR(old_dentry->d_inode->i_mode) ?
+ -ENOTDIR : -EISDIR;
+ goto out;
+ }
+
+ if (S_ISDIR(new_dentry->d_inode->i_mode)) {
+ struct unionfs_dir_state *namelist;
+ /* check if this unionfs directory is empty or not */
+ err = check_empty(new_dentry, &namelist);
+ if (err)
+ goto out;
+
+ if (!is_robranch(new_dentry))
+ err = delete_whiteouts(new_dentry,
+ dbstart(new_dentry),
+ namelist);
+
+ free_rdstate(namelist);
+
+ if (err)
+ goto out;
+ }
+ }
+ err = do_unionfs_rename(old_dir, old_dentry, new_dir, new_dentry);
+
+out:
+ if (err)
+ /* clear the new_dentry stuff created */
+ d_drop(new_dentry);
+ else
+ /* force re-lookup since the dir on ro branch is not renamed,
+ and hidden dentries still indicate the un-renamed ones. */
+ if (S_ISDIR(old_dentry->d_inode->i_mode))
+ atomic_dec(&UNIONFS_D(old_dentry)->generation);
+
+ unlock_dentry(new_dentry);
+ unlock_dentry(old_dentry);
+ return err;
+}
+
--
1.4.3.3

2006-12-04 12:38:32

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 15/35] Unionfs: Common file operations

From: Josef "Jeff" Sipek <[email protected]>

This patch contains helper functions used through the rest of the code which
pertains to files.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
Signed-off-by: David Quigley <[email protected]>
Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/commonfops.c | 587 +++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 587 insertions(+), 0 deletions(-)

diff --git a/fs/unionfs/commonfops.c b/fs/unionfs/commonfops.c
new file mode 100644
index 0000000..92f9bbc
--- /dev/null
+++ b/fs/unionfs/commonfops.c
@@ -0,0 +1,587 @@
+/*
+ * Copyright (c) 2003-2006 Erez Zadok
+ * Copyright (c) 2003-2006 Charles P. Wright
+ * Copyright (c) 2005-2006 Josef 'Jeff' Sipek
+ * Copyright (c) 2005-2006 Junjiro Okajima
+ * Copyright (c) 2005 Arun M. Krishnakumar
+ * Copyright (c) 2004-2006 David P. Quigley
+ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair
+ * Copyright (c) 2003 Puja Gupta
+ * Copyright (c) 2003 Harikesavan Krishnan
+ * Copyright (c) 2003-2006 Stony Brook University
+ * Copyright (c) 2003-2006 The Research Foundation of State University of New York
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "union.h"
+
+/* 1) Copyup the file
+ * 2) Rename the file to '.unionfs<original inode#><counter>' - obviously
+ * stolen from NFS's silly rename
+ */
+static int copyup_deleted_file(struct file *file, struct dentry *dentry,
+ int bstart, int bindex)
+{
+ static unsigned int counter;
+ const int i_inosize = sizeof(dentry->d_inode->i_ino) * 2;
+ const int countersize = sizeof(counter) * 2;
+ const int nlen = sizeof(".unionfs") + i_inosize + countersize - 1;
+ char name[nlen + 1];
+
+ int err;
+ struct dentry *tmp_dentry = NULL;
+ struct dentry *hidden_dentry = NULL;
+ struct dentry *hidden_dir_dentry = NULL;
+
+ hidden_dentry = unionfs_lower_dentry_idx(dentry, bstart);
+
+ sprintf(name, ".unionfs%*.*lx",
+ i_inosize, i_inosize, hidden_dentry->d_inode->i_ino);
+
+ tmp_dentry = NULL;
+ do {
+ char *suffix = name + nlen - countersize;
+
+ dput(tmp_dentry);
+ counter++;
+ sprintf(suffix, "%*.*x", countersize, countersize, counter);
+
+ printk(KERN_DEBUG "unionfs: trying to rename %s to %s\n",
+ dentry->d_name.name, name);
+
+ tmp_dentry = lookup_one_len(name, hidden_dentry->d_parent,
+ UNIONFS_TMPNAM_LEN);
+ if (IS_ERR(tmp_dentry)) {
+ err = PTR_ERR(tmp_dentry);
+ goto out;
+ }
+ } while (tmp_dentry->d_inode != NULL); /* need negative dentry */
+
+ err = copyup_named_file(dentry->d_parent->d_inode, file, name, bstart,
+ bindex, file->f_dentry->d_inode->i_size);
+ if (err)
+ goto out;
+
+ /* bring it to the same state as an unlinked file */
+ hidden_dentry = unionfs_lower_dentry_idx(dentry, dbstart(dentry));
+ hidden_dir_dentry = lock_parent(hidden_dentry);
+ err = vfs_unlink(hidden_dir_dentry->d_inode, hidden_dentry);
+ unlock_dir(hidden_dir_dentry);
+
+out:
+ return err;
+}
+
+/* put all references held by upper struct file and free lower file pointer
+ * array */
+static void cleanup_file(struct file *file)
+{
+ int bindex, bstart, bend;
+ struct file **lf;
+
+ lf = UNIONFS_F(file)->lower_files;
+ bstart = fbstart(file);
+ bend = fbend(file);
+
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ if (unionfs_lower_file_idx(file, bindex)) {
+ branchput(file->f_dentry->d_sb, bindex);
+ fput(unionfs_lower_file_idx(file, bindex));
+ }
+ }
+
+ UNIONFS_F(file)->lower_files = NULL;
+ kfree(lf);
+}
+
+/* open all lower files for a given file */
+static int open_all_files(struct file *file)
+{
+ int bindex, bstart, bend, err = 0;
+ struct file *hidden_file;
+ struct dentry *hidden_dentry;
+ struct dentry *dentry = file->f_dentry;
+ struct super_block *sb = dentry->d_sb;
+
+ bstart = dbstart(dentry);
+ bend = dbend(dentry);
+
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ if (!hidden_dentry)
+ continue;
+
+ dget(hidden_dentry);
+ mntget(unionfs_lower_mnt_idx(dentry, bindex));
+ branchget(sb, bindex);
+
+ hidden_file = dentry_open(hidden_dentry,
+ unionfs_lower_mnt_idx(dentry, bindex),
+ file->f_flags);
+ if (IS_ERR(hidden_file)) {
+ err = PTR_ERR(hidden_file);
+ goto out;
+ } else
+ unionfs_set_lower_file_idx(file, bindex, hidden_file);
+ }
+out:
+ return err;
+}
+
+/* open the highest priority file for a given upper file */
+static int open_highest_file(struct file *file, int willwrite)
+{
+ int bindex, bstart, bend, err = 0;
+ struct file *hidden_file;
+ struct dentry *hidden_dentry;
+
+ struct dentry *dentry = file->f_dentry;
+ struct inode *parent_inode = dentry->d_parent->d_inode;
+ struct super_block *sb = dentry->d_sb;
+ size_t inode_size = dentry->d_inode->i_size;
+
+ bstart = dbstart(dentry);
+ bend = dbend(dentry);
+
+ hidden_dentry = unionfs_lower_dentry(dentry);
+ if (willwrite && IS_WRITE_FLAG(file->f_flags) && is_robranch(dentry)) {
+ for (bindex = bstart - 1; bindex >= 0; bindex--) {
+ err = copyup_file(parent_inode, file, bstart, bindex,
+ inode_size);
+ if (!err)
+ break;
+ }
+ atomic_set(&UNIONFS_F(file)->generation,
+ atomic_read(&UNIONFS_I(dentry->d_inode)->generation));
+ goto out;
+ }
+
+ dget(hidden_dentry);
+ mntget(unionfs_lower_mnt_idx(dentry, bstart));
+ branchget(sb, bstart);
+ hidden_file = dentry_open(hidden_dentry,
+ unionfs_lower_mnt_idx(dentry, bstart), file->f_flags);
+ if (IS_ERR(hidden_file)) {
+ err = PTR_ERR(hidden_file);
+ goto out;
+ }
+ unionfs_set_lower_file(file, hidden_file);
+ /* Fix up the position. */
+ hidden_file->f_pos = file->f_pos;
+
+ memcpy(&hidden_file->f_ra, &file->f_ra, sizeof(struct file_ra_state));
+out:
+ return err;
+}
+
+static int do_delayed_copyup(struct file *file, struct dentry *dentry)
+{
+ int bindex, bstart, bend, err = 0;
+ struct inode *parent_inode = dentry->d_parent->d_inode;
+ loff_t inode_size = file->f_dentry->d_inode->i_size;
+
+ bstart = fbstart(file);
+ bend = fbend(file);
+
+ BUG_ON(!S_ISREG(file->f_dentry->d_inode->i_mode));
+
+ for (bindex = bstart - 1; bindex >= 0; bindex--) {
+ if (!d_deleted(file->f_dentry))
+ err = copyup_file(parent_inode, file, bstart,
+ bindex, inode_size);
+ else
+ err = copyup_deleted_file(file, dentry, bstart, bindex);
+
+ if (!err)
+ break;
+ }
+ if (!err && (bstart > fbstart(file))) {
+ bend = fbend(file);
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ if (unionfs_lower_file_idx(file, bindex)) {
+ branchput(dentry->d_sb, bindex);
+ fput(unionfs_lower_file_idx(file, bindex));
+ unionfs_set_lower_file_idx(file, bindex, NULL);
+ }
+ }
+ fbend(file) = bend;
+ }
+ return err;
+}
+
+/* revalidate the stuct file */
+int unionfs_file_revalidate(struct file *file, int willwrite)
+{
+ struct super_block *sb;
+ struct dentry *dentry;
+ int sbgen, fgen, dgen;
+ int bstart, bend;
+ int size;
+
+ int err = 0;
+
+ dentry = file->f_dentry;
+ lock_dentry(dentry);
+ sb = dentry->d_sb;
+ unionfs_read_lock(sb);
+ if (!unionfs_d_revalidate(dentry, NULL) && !d_deleted(dentry)) {
+ err = -ESTALE;
+ goto out;
+ }
+
+ sbgen = atomic_read(&UNIONFS_SB(sb)->generation);
+ dgen = atomic_read(&UNIONFS_D(dentry)->generation);
+ fgen = atomic_read(&UNIONFS_F(file)->generation);
+
+ BUG_ON(sbgen > dgen);
+
+ /* There are two cases we are interested in. The first is if the
+ * generation is lower than the super-block. The second is if someone
+ * has copied up this file from underneath us, we also need to refresh
+ * things. */
+ if (!d_deleted(dentry) &&
+ (sbgen > fgen || dbstart(dentry) != fbstart(file))) {
+ /* First we throw out the existing files. */
+ cleanup_file(file);
+
+ /* Now we reopen the file(s) as in unionfs_open. */
+ bstart = fbstart(file) = dbstart(dentry);
+ bend = fbend(file) = dbend(dentry);
+
+ size = sizeof(struct file *) * sbmax(sb);
+ UNIONFS_F(file)->lower_files = kzalloc(size, GFP_KERNEL);
+ if (!UNIONFS_F(file)->lower_files) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ if (S_ISDIR(dentry->d_inode->i_mode)) {
+ /* We need to open all the files. */
+ err = open_all_files(file);
+ if (err)
+ goto out;
+ } else {
+ /* We only open the highest priority branch. */
+ err = open_highest_file(file, willwrite);
+ if (err)
+ goto out;
+ }
+ atomic_set(&UNIONFS_F(file)->generation,
+ atomic_read(&UNIONFS_I(dentry->d_inode)->
+ generation));
+ }
+
+ /* Copyup on the first write to a file on a readonly branch. */
+ if (willwrite && IS_WRITE_FLAG(file->f_flags) &&
+ !IS_WRITE_FLAG(unionfs_lower_file(file)->f_flags) &&
+ is_robranch(dentry)) {
+ printk(KERN_DEBUG "Doing delayed copyup of a read-write "
+ "file on a read-only branch.\n");
+ err = do_delayed_copyup(file, dentry);
+ }
+
+out:
+ unlock_dentry(dentry);
+ unionfs_read_unlock(dentry->d_sb);
+ return err;
+}
+
+/* unionfs_open helper function: open a directory */
+static inline int __open_dir(struct inode *inode, struct file *file)
+{
+ struct dentry *hidden_dentry;
+ struct file *hidden_file;
+ int bindex, bstart, bend;
+
+ bstart = fbstart(file) = dbstart(file->f_dentry);
+ bend = fbend(file) = dbend(file->f_dentry);
+
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ hidden_dentry = unionfs_lower_dentry_idx(file->f_dentry, bindex);
+ if (!hidden_dentry)
+ continue;
+
+ dget(hidden_dentry);
+ mntget(unionfs_lower_mnt_idx(file->f_dentry, bindex));
+ hidden_file = dentry_open(hidden_dentry,
+ unionfs_lower_mnt_idx(file->f_dentry, bindex),
+ file->f_flags);
+ if (IS_ERR(hidden_file))
+ return PTR_ERR(hidden_file);
+
+ unionfs_set_lower_file_idx(file, bindex, hidden_file);
+
+ /* The branchget goes after the open, because otherwise
+ * we would miss the reference on release. */
+ branchget(inode->i_sb, bindex);
+ }
+
+ return 0;
+}
+
+/* unionfs_open helper function: open a file */
+static inline int __open_file(struct inode *inode, struct file *file)
+{
+ struct dentry *hidden_dentry;
+ struct file *hidden_file;
+ int hidden_flags;
+ int bindex, bstart, bend;
+
+ hidden_dentry = unionfs_lower_dentry(file->f_dentry);
+ hidden_flags = file->f_flags;
+
+ bstart = fbstart(file) = dbstart(file->f_dentry);
+ bend = fbend(file) = dbend(file->f_dentry);
+
+ /* check for the permission for hidden file. If the error is COPYUP_ERR,
+ * copyup the file.
+ */
+ if (hidden_dentry->d_inode && is_robranch(file->f_dentry)) {
+ /* if the open will change the file, copy it up otherwise defer it. */
+ if (hidden_flags & O_TRUNC) {
+ int size = 0;
+ int err = -EROFS;
+
+ /* copyup the file */
+ for (bindex = bstart - 1; bindex >= 0; bindex--) {
+ err = copyup_file(file->f_dentry->d_parent->d_inode,
+ file, bstart, bindex, size);
+ if (!err)
+ break;
+ }
+ return err;
+ } else
+ hidden_flags &= ~(OPEN_WRITE_FLAGS);
+ }
+
+ dget(hidden_dentry);
+
+ /* dentry_open will decrement mnt refcnt if err.
+ * otherwise fput() will do an mntput() for us upon file close.
+ */
+ mntget(unionfs_lower_mnt_idx(file->f_dentry, bstart));
+ hidden_file = dentry_open(hidden_dentry,
+ unionfs_lower_mnt_idx(file->f_dentry, bstart),
+ hidden_flags);
+ if (IS_ERR(hidden_file))
+ return PTR_ERR(hidden_file);
+
+ unionfs_set_lower_file(file, hidden_file);
+ branchget(inode->i_sb, bstart);
+
+ return 0;
+}
+
+int unionfs_open(struct inode *inode, struct file *file)
+{
+ int err = 0;
+ struct file *hidden_file = NULL;
+ struct dentry *dentry = NULL;
+ int bindex = 0, bstart = 0, bend = 0;
+ int size;
+
+ file->private_data = kzalloc(sizeof(struct unionfs_file_info), GFP_KERNEL);
+ if (!UNIONFS_F(file)) {
+ err = -ENOMEM;
+ goto out;
+ }
+ fbstart(file) = -1;
+ fbend(file) = -1;
+ atomic_set(&UNIONFS_F(file)->generation,
+ atomic_read(&UNIONFS_I(inode)->generation));
+
+ size = sizeof(struct file *) * sbmax(inode->i_sb);
+ UNIONFS_F(file)->lower_files = kzalloc(size, GFP_KERNEL);
+ if (!UNIONFS_F(file)->lower_files) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ dentry = file->f_dentry;
+ lock_dentry(dentry);
+ unionfs_read_lock(inode->i_sb);
+
+ bstart = fbstart(file) = dbstart(dentry);
+ bend = fbend(file) = dbend(dentry);
+
+ /* increment, so that we can flush appropriately */
+ atomic_inc(&UNIONFS_I(dentry->d_inode)->totalopens);
+
+ /* open all directories and make the unionfs file struct point to
+ * these hidden file structs */
+ if (S_ISDIR(inode->i_mode))
+ err = __open_dir(inode, file); /* open a dir */
+ else
+ err = __open_file(inode, file); /* open a file */
+
+ /* freeing the allocated resources, and fput the opened files */
+ if (err) {
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ hidden_file = unionfs_lower_file_idx(file, bindex);
+ if (!hidden_file)
+ continue;
+
+ branchput(file->f_dentry->d_sb, bindex);
+ /* fput calls dput for hidden_dentry */
+ fput(hidden_file);
+ }
+ }
+
+ unlock_dentry(dentry);
+ unionfs_read_unlock(inode->i_sb);
+
+out:
+ if (err) {
+ kfree(UNIONFS_F(file)->lower_files);
+ kfree(UNIONFS_F(file));
+ }
+
+ return err;
+}
+
+/* release all lower object references & free the file info structure */
+int unionfs_file_release(struct inode *inode, struct file *file)
+{
+ struct file *hidden_file = NULL;
+ struct unionfs_file_info *fileinfo = UNIONFS_F(file);
+ struct unionfs_inode_info *inodeinfo = UNIONFS_I(inode);
+ int bindex, bstart, bend;
+ int fgen;
+
+ /* fput all the hidden files */
+ fgen = atomic_read(&fileinfo->generation);
+ bstart = fbstart(file);
+ bend = fbend(file);
+
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ hidden_file = unionfs_lower_file_idx(file, bindex);
+
+ if (hidden_file) {
+ fput(hidden_file);
+ unionfs_read_lock(inode->i_sb);
+ branchput(inode->i_sb, bindex);
+ unionfs_read_unlock(inode->i_sb);
+ }
+ }
+ kfree(fileinfo->lower_files);
+
+ if (fileinfo->rdstate) {
+ fileinfo->rdstate->access = jiffies;
+ printk(KERN_DEBUG "Saving rdstate with cookie %u [%d.%lld]\n",
+ fileinfo->rdstate->cookie,
+ fileinfo->rdstate->bindex,
+ (long long)fileinfo->rdstate->dirpos);
+ spin_lock(&inodeinfo->rdlock);
+ inodeinfo->rdcount++;
+ list_add_tail(&fileinfo->rdstate->cache,
+ &inodeinfo->readdircache);
+ mark_inode_dirty(inode);
+ spin_unlock(&inodeinfo->rdlock);
+ fileinfo->rdstate = NULL;
+ }
+ kfree(fileinfo);
+ return 0;
+}
+
+/* pass the ioctl to the lower fs */
+static inline long do_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct file *hidden_file;
+ int err;
+
+ hidden_file = unionfs_lower_file(file);
+
+ err = security_file_ioctl(hidden_file, cmd, arg);
+ if (err)
+ goto out;
+
+ err = -ENOTTY;
+ if (!hidden_file || !hidden_file->f_op)
+ goto out;
+ if (hidden_file->f_op->unlocked_ioctl) {
+ err = hidden_file->f_op->unlocked_ioctl(hidden_file, cmd, arg);
+ } else if (hidden_file->f_op->ioctl) {
+ lock_kernel();
+ err = hidden_file->f_op->ioctl(hidden_file->f_dentry->d_inode,
+ hidden_file, cmd, arg);
+ unlock_kernel();
+ }
+
+out:
+ return err;
+}
+
+long unionfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ long err;
+
+ if ((err = unionfs_file_revalidate(file, 1)))
+ goto out;
+
+ /* check if asked for local commands */
+ switch (cmd) {
+ case UNIONFS_IOCTL_INCGEN:
+ /* Increment the superblock generation count */
+ err = -EACCES;
+ if (!capable(CAP_SYS_ADMIN))
+ goto out;
+ err = unionfs_ioctl_incgen(file, cmd, arg);
+ break;
+
+ case UNIONFS_IOCTL_QUERYFILE:
+ /* Return list of branches containing the given file */
+ err = unionfs_ioctl_queryfile(file, cmd, arg);
+ break;
+
+ default:
+ /* pass the ioctl down */
+ err = do_ioctl(file, cmd, arg);
+ break;
+ }
+
+out:
+ return err;
+}
+
+int unionfs_flush(struct file *file, fl_owner_t id)
+{
+ int err = 0;
+ struct file *hidden_file = NULL;
+ struct dentry *dentry = file->f_dentry;
+ int bindex, bstart, bend;
+
+ if ((err = unionfs_file_revalidate(file, 1)))
+ goto out;
+ if (!atomic_dec_and_test(&UNIONFS_I(dentry->d_inode)->totalopens))
+ goto out;
+
+ lock_dentry(dentry);
+
+ bstart = fbstart(file);
+ bend = fbend(file);
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ hidden_file = unionfs_lower_file_idx(file, bindex);
+
+ if (hidden_file && hidden_file->f_op && hidden_file->f_op->flush) {
+ err = hidden_file->f_op->flush(hidden_file, id);
+ if (err)
+ goto out_lock;
+
+ /* if there are no more references to the dentry, dput it */
+ if (d_deleted(dentry)) {
+ dput(unionfs_lower_dentry_idx(dentry, bindex));
+ unionfs_set_lower_dentry_idx(dentry, bindex, NULL);
+ }
+ }
+
+ }
+
+out_lock:
+ unlock_dentry(dentry);
+out:
+ return err;
+}
+
--
1.4.3.3

2006-12-04 12:37:04

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 12/35] Unionfs: Documentation

From: Josef "Jeff" Sipek <[email protected]>

This patch contains documentation for Unionfs. You will find several files
outlining basic unification concepts and rename semantics.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
Signed-off-by: David Quigley <[email protected]>
Signed-off-by: Erez Zadok <[email protected]>
---
Documentation/filesystems/00-INDEX | 2 +
Documentation/filesystems/unionfs/00-INDEX | 8 +++
Documentation/filesystems/unionfs/concepts.txt | 68 ++++++++++++++++++++++++
Documentation/filesystems/unionfs/rename.txt | 31 +++++++++++
Documentation/filesystems/unionfs/usage.txt | 41 ++++++++++++++
5 files changed, 150 insertions(+), 0 deletions(-)

diff --git a/Documentation/filesystems/00-INDEX b/Documentation/filesystems/00-INDEX
index 4dc28cc..c737e3e 100644
--- a/Documentation/filesystems/00-INDEX
+++ b/Documentation/filesystems/00-INDEX
@@ -82,6 +82,8 @@ udf.txt
- info and mount options for the UDF filesystem.
ufs.txt
- info on the ufs filesystem.
+unionfs/
+ - info on the unionfs filesystem
v9fs.txt
- v9fs is a Unix implementation of the Plan 9 9p remote fs protocol.
vfat.txt
diff --git a/Documentation/filesystems/unionfs/00-INDEX b/Documentation/filesystems/unionfs/00-INDEX
new file mode 100644
index 0000000..fa87f83
--- /dev/null
+++ b/Documentation/filesystems/unionfs/00-INDEX
@@ -0,0 +1,8 @@
+00-INDEX
+ - this file.
+concepts.txt
+ - A brief introduction of concepts
+rename.txt
+ - Information regarding rename operations
+usage.txt
+ - Usage & known limitations
diff --git a/Documentation/filesystems/unionfs/concepts.txt b/Documentation/filesystems/unionfs/concepts.txt
new file mode 100644
index 0000000..0b9bcc9
--- /dev/null
+++ b/Documentation/filesystems/unionfs/concepts.txt
@@ -0,0 +1,68 @@
+This file describes the concepts needed by a namespace unification file system.
+
+Branch Priority:
+================
+
+Each branch is assigned a unique priority - starting from 0 (highest priority).
+No two branches can have the same priority.
+
+
+Branch Mode:
+============
+
+Each branch is assigned a mode - read-write or read-only. This allows
+directories on media mounted read-write to be used in a read-only manner.
+
+
+Whiteouts:
+==========
+
+A whiteout removes a file name from the namespace. Whiteouts are needed when
+one attempts to remove a file on a read-only branch.
+
+Suppose we have a two-branch union, where branch 0 is read-write and branch 1
+is read-only. And a file 'foo' on branch 1:
+
+./b0/
+./b1/
+./b1/foo
+
+The unified view would simply be:
+
+./union/
+./union/foo
+
+Since 'foo' is stored on a read-only branch, it cannot be removed. A whiteout
+is used to remove the name 'foo' from the unified namespace. Again, since
+branch 1 is read-only, the whiteout cannot be created there. So, we try on a
+higher priority (lower numerically) branch. And there we create the whiteout.
+
+./b0/
+./b0/.wh.foo
+./b1/
+./b1/foo
+
+Later, when Unionfs traverses branches (due to lookup or readdir), it eliminate
+'foo' from the namespace (as well as the whiteout itself.)
+
+
+Duplicate Elimination:
+======================
+
+It is possible for files on different branches to have the same name. Unionfs
+then has to select which instance of the file to show to the user. Given the
+fact that each branch has a priority associated with it, the simplest
+solution is to take the instance from the highest priority (lowest numerical
+value) and "hide" the others.
+
+
+Copyup:
+=======
+
+When a change is made to the contents of a file's data or meta-data, they
+have to be stored somewhere. The best way is to create a copy of the
+original file on a branch that is writable, and then redirect the write
+though to this copy. The copy must be made on a higher priority branch so
+that lookup & readdir return this newer "version" of the file rather than
+the original (see duplicate elimination).
+
diff --git a/Documentation/filesystems/unionfs/rename.txt b/Documentation/filesystems/unionfs/rename.txt
new file mode 100644
index 0000000..e20bb82
--- /dev/null
+++ b/Documentation/filesystems/unionfs/rename.txt
@@ -0,0 +1,31 @@
+Rename is a complex beast. The following table shows which rename(2) operations
+should succeed and which should fail.
+
+o: success
+E: error (either unionfs or vfs)
+X: EXDEV
+
+none = file does not exist
+file = file is a file
+dir = file is a empty directory
+child= file is a non-empty directory
+wh = file is a directory containing only whiteouts; this makes it logically
+ empty
+
+ none file dir child wh
+file o o E E E
+dir o E o E o
+child X E X E X
+wh o E o E o
+
+
+Renaming directories:
+=====================
+
+Whenever a empty (either physically or logically) directory is being renamed,
+the following sequence of events should take place:
+
+1) Remove whiteouts from both source and destination directory
+2) Rename source to destination
+3) Make destination opaque to prevent anything under it from showing up
+
diff --git a/Documentation/filesystems/unionfs/usage.txt b/Documentation/filesystems/unionfs/usage.txt
new file mode 100644
index 0000000..8db9158
--- /dev/null
+++ b/Documentation/filesystems/unionfs/usage.txt
@@ -0,0 +1,41 @@
+Unionfs is a stackable unification file system, which can appear to merge the
+contents of several directories (branches), while keeping their physical
+content separate. Unionfs is useful for unified source tree management, merged
+contents of split CD-ROM, merged separate software package directories, data
+grids, and more. Unionfs allows any mix of read-only and read-write branches,
+as well as insertion and deletion of branches anywhere in the fan-out. To
+maintain unix semantics, Unionfs handles elimination of duplicates,
+partial-error conditions, and more.
+
+mount -t unionfs -o branch-option[,union-options[,...]] none MOUNTPOINT
+
+The available branch-option for the mount command is:
+
+dirs=branch[=ro|=rw][:...]
+specifies a separated list of which directories compose the union. Directories
+that come earlier in the list have a higher precedence than those which come
+later. Additionally, read-only or read-write permissions of the branch can be
+specified by appending =ro or =rw (default) to each directory.
+
+Syntax:
+dirs=/branch1[=ro|=rw]:/branch2[=ro|=rw]:...:/branchN[=ro|=rw]
+
+Example:
+dirs=/writable_branch=rw:/read-only_branch=ro
+
+
+KNOWN ISSUES:
+=============
+
+The NFS server returns -EACCES for read-only exports, instead of -EROFS. This
+means we can't reliably detect a read-only NFS export.
+
+Modifying a Unionfs branch directly, while the union is mounted is currently
+unsupported. Any such change can cause Unionfs to oops, however it could even
+RESULT IN DATA LOSS.
+
+Unionfs shouldn't use lookup_one_len on the underlying fs as it confuses
+NFS. Currently, unionfs_lookup passes lookup intents to the lower
+filesystem, this eliminates part of the problem. The remaining calls to
+lookup_one_len may need to be changed to pass an intent.
+
--
1.4.3.3

2006-12-04 12:39:39

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 28/35] Unionfs: Miscellaneous helper functions

From: Josef "Jeff" Sipek <[email protected]>

This patch contains miscellaneous helper functions used thoughout Unionfs.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
Signed-off-by: David Quigley <[email protected]>
Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/subr.c | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 170 insertions(+), 0 deletions(-)

diff --git a/fs/unionfs/subr.c b/fs/unionfs/subr.c
new file mode 100644
index 0000000..f002f19
--- /dev/null
+++ b/fs/unionfs/subr.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2003-2006 Erez Zadok
+ * Copyright (c) 2003-2006 Charles P. Wright
+ * Copyright (c) 2005-2006 Josef 'Jeff' Sipek
+ * Copyright (c) 2005-2006 Junjiro Okajima
+ * Copyright (c) 2005 Arun M. Krishnakumar
+ * Copyright (c) 2004-2006 David P. Quigley
+ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair
+ * Copyright (c) 2003 Puja Gupta
+ * Copyright (c) 2003 Harikesavan Krishnan
+ * Copyright (c) 2003-2006 Stony Brook University
+ * Copyright (c) 2003-2006 The Research Foundation of State University of New York
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "union.h"
+
+/* Pass an unionfs dentry and an index. It will try to create a whiteout
+ * for the filename in dentry, and will try in branch 'index'. On error,
+ * it will proceed to a branch to the left.
+ */
+int create_whiteout(struct dentry *dentry, int start)
+{
+ int bstart, bend, bindex;
+ struct dentry *hidden_dir_dentry;
+ struct dentry *hidden_dentry;
+ struct dentry *hidden_wh_dentry;
+ char *name = NULL;
+ int err = -EINVAL;
+
+ verify_locked(dentry);
+
+ bstart = dbstart(dentry);
+ bend = dbend(dentry);
+
+ /* create dentry's whiteout equivalent */
+ name = alloc_whname(dentry->d_name.name, dentry->d_name.len);
+ if (IS_ERR(name)) {
+ err = PTR_ERR(name);
+ goto out;
+ }
+
+ for (bindex = start; bindex >= 0; bindex--) {
+ hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+
+ if (!hidden_dentry) {
+ /* if hidden dentry is not present, create the entire
+ * hidden dentry directory structure and go ahead.
+ * Since we want to just create whiteout, we only want
+ * the parent dentry, and hence get rid of this dentry.
+ */
+ hidden_dentry = create_parents(dentry->d_inode,
+ dentry, bindex);
+ if (!hidden_dentry || IS_ERR(hidden_dentry)) {
+ printk(KERN_DEBUG "create_parents failed for "
+ "bindex = %d\n", bindex);
+ continue;
+ }
+ }
+
+ hidden_wh_dentry = lookup_one_len(name, hidden_dentry->d_parent,
+ dentry->d_name.len + UNIONFS_WHLEN);
+ if (IS_ERR(hidden_wh_dentry))
+ continue;
+
+ /* The whiteout already exists. This used to be impossible, but
+ * now is possible because of opaqueness. */
+ if (hidden_wh_dentry->d_inode) {
+ dput(hidden_wh_dentry);
+ err = 0;
+ goto out;
+ }
+
+ hidden_dir_dentry = lock_parent(hidden_wh_dentry);
+ if (!(err = is_robranch_super(dentry->d_sb, bindex))) {
+ err = vfs_create(hidden_dir_dentry->d_inode,
+ hidden_wh_dentry,
+ ~current->fs->umask & S_IRWXUGO, NULL);
+
+ }
+ unlock_dir(hidden_dir_dentry);
+ dput(hidden_wh_dentry);
+
+ if (!err || !IS_COPYUP_ERR(err))
+ break;
+ }
+
+ /* set dbopaque so that lookup will not proceed after this branch */
+ if (!err)
+ set_dbopaque(dentry, bindex);
+
+out:
+ kfree(name);
+ return err;
+}
+
+/* This is a helper function for rename, which ends up with hosed over dentries
+ * when it needs to revert. */
+int unionfs_refresh_hidden_dentry(struct dentry *dentry, int bindex)
+{
+ struct dentry *hidden_dentry;
+ struct dentry *hidden_parent;
+ int err = 0;
+
+ verify_locked(dentry);
+
+ lock_dentry(dentry->d_parent);
+ hidden_parent = unionfs_lower_dentry_idx(dentry->d_parent, bindex);
+ unlock_dentry(dentry->d_parent);
+
+ BUG_ON(!S_ISDIR(hidden_parent->d_inode->i_mode));
+
+ hidden_dentry = lookup_one_len(dentry->d_name.name, hidden_parent,
+ dentry->d_name.len);
+ if (IS_ERR(hidden_dentry)) {
+ err = PTR_ERR(hidden_dentry);
+ goto out;
+ }
+
+ dput(unionfs_lower_dentry_idx(dentry, bindex));
+ iput(unionfs_lower_inode_idx(dentry->d_inode, bindex));
+ unionfs_set_lower_inode_idx(dentry->d_inode, bindex, NULL);
+
+ if (!hidden_dentry->d_inode) {
+ dput(hidden_dentry);
+ unionfs_set_lower_dentry_idx(dentry, bindex, NULL);
+ } else {
+ unionfs_set_lower_dentry_idx(dentry, bindex, hidden_dentry);
+ unionfs_set_lower_inode_idx(dentry->d_inode, bindex,
+ igrab(hidden_dentry->d_inode));
+ }
+
+out:
+ return err;
+}
+
+int make_dir_opaque(struct dentry *dentry, int bindex)
+{
+ int err = 0;
+ struct dentry *hidden_dentry, *diropq;
+ struct inode *hidden_dir;
+
+ hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ hidden_dir = hidden_dentry->d_inode;
+ BUG_ON(!S_ISDIR(dentry->d_inode->i_mode) ||
+ !S_ISDIR(hidden_dir->i_mode));
+
+ mutex_lock(&hidden_dir->i_mutex);
+ diropq = lookup_one_len(UNIONFS_DIR_OPAQUE, hidden_dentry,
+ sizeof(UNIONFS_DIR_OPAQUE) - 1);
+ if (IS_ERR(diropq)) {
+ err = PTR_ERR(diropq);
+ goto out;
+ }
+
+ if (!diropq->d_inode)
+ err = vfs_create(hidden_dir, diropq, S_IRUGO, NULL);
+ if (!err)
+ set_dbopaque(dentry, bindex);
+
+ dput(diropq);
+
+out:
+ mutex_unlock(&hidden_dir->i_mutex);
+ return err;
+}
+
--
1.4.3.3

2006-12-04 12:40:18

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 08/35] struct path: make eCryptfs a user of struct path

From: Josef "Jeff" Sipek <[email protected]>

Convert eCryptfs dentry-vfsmount pairs in dentry private data to struct
path.

Cc: Michael Halcrow <[email protected]>
Signed-off-by: Josef "Jeff" Sipek <[email protected]>
---
fs/ecryptfs/ecryptfs_kernel.h | 12 ++++++------
1 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
index f992533..870a65b 100644
--- a/fs/ecryptfs/ecryptfs_kernel.h
+++ b/fs/ecryptfs/ecryptfs_kernel.h
@@ -28,6 +28,7 @@

#include <keys/user-type.h>
#include <linux/fs.h>
+#include <linux/namei.h>
#include <linux/scatterlist.h>

/* Version verification for shared data structures w/ userspace */
@@ -227,8 +228,7 @@ struct ecryptfs_inode_info {
/* dentry private data. Each dentry must keep track of a lower
* vfsmount too. */
struct ecryptfs_dentry_info {
- struct dentry *wdi_dentry;
- struct vfsmount *lower_mnt;
+ struct path lower_path;
struct ecryptfs_crypt_stat *crypt_stat;
};

@@ -355,26 +355,26 @@ ecryptfs_set_dentry_private(struct dentr
static inline struct dentry *
ecryptfs_dentry_to_lower(struct dentry *dentry)
{
- return ((struct ecryptfs_dentry_info *)dentry->d_fsdata)->wdi_dentry;
+ return ((struct ecryptfs_dentry_info *)dentry->d_fsdata)->lower_path.dentry;
}

static inline void
ecryptfs_set_dentry_lower(struct dentry *dentry, struct dentry *lower_dentry)
{
- ((struct ecryptfs_dentry_info *)dentry->d_fsdata)->wdi_dentry =
+ ((struct ecryptfs_dentry_info *)dentry->d_fsdata)->lower_path.dentry =
lower_dentry;
}

static inline struct vfsmount *
ecryptfs_dentry_to_lower_mnt(struct dentry *dentry)
{
- return ((struct ecryptfs_dentry_info *)dentry->d_fsdata)->lower_mnt;
+ return ((struct ecryptfs_dentry_info *)dentry->d_fsdata)->lower_path.mnt;
}

static inline void
ecryptfs_set_dentry_lower_mnt(struct dentry *dentry, struct vfsmount *lower_mnt)
{
- ((struct ecryptfs_dentry_info *)dentry->d_fsdata)->lower_mnt =
+ ((struct ecryptfs_dentry_info *)dentry->d_fsdata)->lower_path.mnt =
lower_mnt;
}

--
1.4.3.3

2006-12-04 12:42:24

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 20/35] Unionfs: Directory manipulation helper functions

From: Josef "Jeff" Sipek <[email protected]>

This patch contains directory manipulation helper functions.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
Signed-off-by: David Quigley <[email protected]>
Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/dirhelper.c | 270 ++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 270 insertions(+), 0 deletions(-)

diff --git a/fs/unionfs/dirhelper.c b/fs/unionfs/dirhelper.c
new file mode 100644
index 0000000..714af2d
--- /dev/null
+++ b/fs/unionfs/dirhelper.c
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2003-2006 Erez Zadok
+ * Copyright (c) 2003-2006 Charles P. Wright
+ * Copyright (c) 2005-2006 Josef 'Jeff' Sipek
+ * Copyright (c) 2005-2006 Junjiro Okajima
+ * Copyright (c) 2005 Arun M. Krishnakumar
+ * Copyright (c) 2004-2006 David P. Quigley
+ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair
+ * Copyright (c) 2003 Puja Gupta
+ * Copyright (c) 2003 Harikesavan Krishnan
+ * Copyright (c) 2003-2006 Stony Brook University
+ * Copyright (c) 2003-2006 The Research Foundation of State University of New York
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "union.h"
+
+/* Delete all of the whiteouts in a given directory for rmdir.
+ *
+ * hidden directory inode should be locked
+ */
+int do_delete_whiteouts(struct dentry *dentry, int bindex,
+ struct unionfs_dir_state *namelist)
+{
+ int err = 0;
+ struct dentry *hidden_dir_dentry = NULL;
+ struct dentry *hidden_dentry;
+ char *name = NULL, *p;
+ struct inode *hidden_dir;
+
+ int i;
+ struct list_head *pos;
+ struct filldir_node *cursor;
+
+ /* Find out hidden parent dentry */
+ hidden_dir_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ BUG_ON(!S_ISDIR(hidden_dir_dentry->d_inode->i_mode));
+ hidden_dir = hidden_dir_dentry->d_inode;
+ BUG_ON(!S_ISDIR(hidden_dir->i_mode));
+
+ err = -ENOMEM;
+ name = __getname();
+ if (!name)
+ goto out;
+ strcpy(name, UNIONFS_WHPFX);
+ p = name + UNIONFS_WHLEN;
+
+ err = 0;
+ for (i = 0; !err && i < namelist->size; i++) {
+ list_for_each(pos, &namelist->list[i]) {
+ cursor =
+ list_entry(pos, struct filldir_node, file_list);
+ /* Only operate on whiteouts in this branch. */
+ if (cursor->bindex != bindex)
+ continue;
+ if (!cursor->whiteout)
+ continue;
+
+ strcpy(p, cursor->name);
+ hidden_dentry =
+ lookup_one_len(name, hidden_dir_dentry,
+ cursor->namelen + UNIONFS_WHLEN);
+ if (IS_ERR(hidden_dentry)) {
+ err = PTR_ERR(hidden_dentry);
+ break;
+ }
+ if (hidden_dentry->d_inode)
+ err = vfs_unlink(hidden_dir, hidden_dentry);
+ dput(hidden_dentry);
+ if (err)
+ break;
+ }
+ }
+
+ __putname(name);
+
+ /* After all of the removals, we should copy the attributes once. */
+ fsstack_copy_attr_times(dentry->d_inode, hidden_dir_dentry->d_inode);
+
+out:
+ return err;
+}
+
+/* delete whiteouts in a dir (for rmdir operation) using sioq if necessary */
+int delete_whiteouts(struct dentry *dentry, int bindex,
+ struct unionfs_dir_state *namelist)
+{
+ int err;
+ struct super_block *sb;
+ struct dentry *hidden_dir_dentry;
+ struct inode *hidden_dir;
+
+ struct sioq_args args;
+
+ sb = dentry->d_sb;
+ unionfs_read_lock(sb);
+
+ BUG_ON(!S_ISDIR(dentry->d_inode->i_mode));
+ BUG_ON(bindex < dbstart(dentry));
+ BUG_ON(bindex > dbend(dentry));
+ err = is_robranch_super(sb, bindex);
+ if (err)
+ goto out;
+
+ hidden_dir_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ BUG_ON(!S_ISDIR(hidden_dir_dentry->d_inode->i_mode));
+ hidden_dir = hidden_dir_dentry->d_inode;
+ BUG_ON(!S_ISDIR(hidden_dir->i_mode));
+
+ mutex_lock(&hidden_dir->i_mutex);
+ if (!permission(hidden_dir, MAY_WRITE | MAY_EXEC, NULL))
+ err = do_delete_whiteouts(dentry, bindex, namelist);
+ else {
+ args.deletewh.namelist = namelist;
+ args.deletewh.dentry = dentry;
+ args.deletewh.bindex = bindex;
+ run_sioq(__delete_whiteouts, &args);
+ err = args.err;
+ }
+ mutex_unlock(&hidden_dir->i_mutex);
+
+out:
+ unionfs_read_unlock(sb);
+ return err;
+}
+
+#define RD_NONE 0
+#define RD_CHECK_EMPTY 1
+/* The callback structure for check_empty. */
+struct unionfs_rdutil_callback {
+ int err;
+ int filldir_called;
+ struct unionfs_dir_state *rdstate;
+ int mode;
+};
+
+/* This filldir function makes sure only whiteouts exist within a directory. */
+static int readdir_util_callback(void *dirent, const char *name, int namelen,
+ loff_t offset, u64 ino, unsigned int d_type)
+{
+ int err = 0;
+ struct unionfs_rdutil_callback *buf = dirent;
+ int whiteout = 0;
+ struct filldir_node *found;
+
+ buf->filldir_called = 1;
+
+ if (name[0] == '.'
+ && (namelen == 1 || (name[1] == '.' && namelen == 2)))
+ goto out;
+
+ if (namelen > UNIONFS_WHLEN && !strncmp(name, UNIONFS_WHPFX, UNIONFS_WHLEN)) {
+ namelen -= UNIONFS_WHLEN;
+ name += UNIONFS_WHLEN;
+ whiteout = 1;
+ }
+
+ found = find_filldir_node(buf->rdstate, name, namelen);
+ /* If it was found in the table there was a previous whiteout. */
+ if (found)
+ goto out;
+
+ /* If it wasn't found and isn't a whiteout, the directory isn't empty. */
+ err = -ENOTEMPTY;
+ if ((buf->mode == RD_CHECK_EMPTY) && !whiteout)
+ goto out;
+
+ err = add_filldir_node(buf->rdstate, name, namelen,
+ buf->rdstate->bindex, whiteout);
+
+out:
+ buf->err = err;
+ return err;
+}
+
+/* Is a directory logically empty? */
+int check_empty(struct dentry *dentry, struct unionfs_dir_state **namelist)
+{
+ int err = 0;
+ struct dentry *hidden_dentry = NULL;
+ struct super_block *sb;
+ struct file *hidden_file;
+ struct unionfs_rdutil_callback *buf = NULL;
+ int bindex, bstart, bend, bopaque;
+
+ sb = dentry->d_sb;
+
+ unionfs_read_lock(sb);
+
+ BUG_ON(!S_ISDIR(dentry->d_inode->i_mode));
+
+ if ((err = unionfs_partial_lookup(dentry)))
+ goto out;
+
+ bstart = dbstart(dentry);
+ bend = dbend(dentry);
+ bopaque = dbopaque(dentry);
+ if (0 <= bopaque && bopaque < bend)
+ bend = bopaque;
+
+ buf = kmalloc(sizeof(struct unionfs_rdutil_callback), GFP_KERNEL);
+ if (!buf) {
+ err = -ENOMEM;
+ goto out;
+ }
+ buf->err = 0;
+ buf->mode = RD_CHECK_EMPTY;
+ buf->rdstate = alloc_rdstate(dentry->d_inode, bstart);
+ if (!buf->rdstate) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ /* Process the hidden directories with rdutil_callback as a filldir. */
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ if (!hidden_dentry)
+ continue;
+ if (!hidden_dentry->d_inode)
+ continue;
+ if (!S_ISDIR(hidden_dentry->d_inode->i_mode))
+ continue;
+
+ dget(hidden_dentry);
+ mntget(unionfs_lower_mnt_idx(dentry, bindex));
+ branchget(sb, bindex);
+ hidden_file =
+ dentry_open(hidden_dentry, unionfs_lower_mnt_idx(dentry, bindex),
+ O_RDONLY);
+ if (IS_ERR(hidden_file)) {
+ err = PTR_ERR(hidden_file);
+ dput(hidden_dentry);
+ branchput(sb, bindex);
+ goto out;
+ }
+
+ do {
+ buf->filldir_called = 0;
+ buf->rdstate->bindex = bindex;
+ err = vfs_readdir(hidden_file,
+ readdir_util_callback, buf);
+ if (buf->err)
+ err = buf->err;
+ } while ((err >= 0) && buf->filldir_called);
+
+ /* fput calls dput for hidden_dentry */
+ fput(hidden_file);
+ branchput(sb, bindex);
+
+ if (err < 0)
+ goto out;
+ }
+
+out:
+ if (buf) {
+ if (namelist && !err)
+ *namelist = buf->rdstate;
+ else if (buf->rdstate)
+ free_rdstate(buf->rdstate);
+ kfree(buf);
+ }
+
+ unionfs_read_unlock(sb);
+
+ return err;
+}
+
--
1.4.3.3

2006-12-04 12:41:15

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 26/35] Unionfs: Privileged operations workqueue

From: Josef "Jeff" Sipek <[email protected]>

Workqueue & helper functions used to perform privileged operations on
behalf of the user process.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
Signed-off-by: David Quigley <[email protected]>
Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/sioq.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++
fs/unionfs/sioq.h | 79 ++++++++++++++++++++++++++++++++++++
2 files changed, 194 insertions(+), 0 deletions(-)

diff --git a/fs/unionfs/sioq.c b/fs/unionfs/sioq.c
new file mode 100644
index 0000000..187ad87
--- /dev/null
+++ b/fs/unionfs/sioq.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2003-2006 Erez Zadok
+ * Copyright (c) 2003-2006 Charles P. Wright
+ * Copyright (c) 2005-2006 Josef 'Jeff' Sipek
+ * Copyright (c) 2005-2006 Junjiro Okajima
+ * Copyright (c) 2005 Arun M. Krishnakumar
+ * Copyright (c) 2004-2006 David P. Quigley
+ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair
+ * Copyright (c) 2003 Puja Gupta
+ * Copyright (c) 2003 Harikesavan Krishnan
+ * Copyright (c) 2003-2006 Stony Brook University
+ * Copyright (c) 2003-2006 The Research Foundation of State University of New York
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "union.h"
+
+struct workqueue_struct *sioq;
+
+int __init init_sioq(void)
+{
+ int err;
+
+ sioq = create_workqueue("unionfs_siod");
+ if (!IS_ERR(sioq))
+ return 0;
+
+ err = PTR_ERR(sioq);
+ printk(KERN_ERR "create_workqueue failed %d\n", err);
+ sioq = NULL;
+ return err;
+}
+
+void fin_sioq(void)
+{
+ if (sioq)
+ destroy_workqueue(sioq);
+}
+
+void run_sioq(void (*func)(void *arg), struct sioq_args *args)
+{
+ DECLARE_WORK(wk, func, &args->comp);
+
+ init_completion(&args->comp);
+ while (!queue_work(sioq, &wk)) {
+ /* TODO: do accounting if needed */
+ schedule();
+ }
+ wait_for_completion(&args->comp);
+}
+
+void __unionfs_create(void *data)
+{
+ struct sioq_args *args = data;
+ struct create_args *c = &args->create;
+
+ args->err = vfs_create(c->parent, c->dentry, c->mode, c->nd);
+ complete(&args->comp);
+}
+
+void __unionfs_mkdir(void *data)
+{
+ struct sioq_args *args = data;
+ struct mkdir_args *m = &args->mkdir;
+
+ args->err = vfs_mkdir(m->parent, m->dentry, m->mode);
+ complete(&args->comp);
+}
+
+void __unionfs_mknod(void *data)
+{
+ struct sioq_args *args = data;
+ struct mknod_args *m = &args->mknod;
+
+ args->err = vfs_mknod(m->parent, m->dentry, m->mode, m->dev);
+ complete(&args->comp);
+}
+void __unionfs_symlink(void *data)
+{
+ struct sioq_args *args = data;
+ struct symlink_args *s = &args->symlink;
+
+ args->err = vfs_symlink(s->parent, s->dentry, s->symbuf, s->mode);
+ complete(&args->comp);
+}
+
+void __unionfs_unlink(void *data)
+{
+ struct sioq_args *args = data;
+ struct unlink_args *u = &args->unlink;
+
+ args->err = vfs_unlink(u->parent, u->dentry);
+ complete(&args->comp);
+}
+
+void __delete_whiteouts(void *data) {
+ struct sioq_args *args = data;
+ struct deletewh_args *d = &args->deletewh;
+
+ args->err = do_delete_whiteouts(d->dentry, d->bindex, d->namelist);
+ complete(&args->comp);
+}
+
+void __is_opaque_dir(void *data)
+{
+ struct sioq_args *args = data;
+
+ args->ret = lookup_one_len(UNIONFS_DIR_OPAQUE, args->isopaque.dentry,
+ sizeof(UNIONFS_DIR_OPAQUE) - 1);
+ complete(&args->comp);
+}
+
diff --git a/fs/unionfs/sioq.h b/fs/unionfs/sioq.h
new file mode 100644
index 0000000..628d214
--- /dev/null
+++ b/fs/unionfs/sioq.h
@@ -0,0 +1,79 @@
+#ifndef _SIOQ_H
+#define _SIOQ_H
+
+struct deletewh_args {
+ struct unionfs_dir_state *namelist;
+ struct dentry *dentry;
+ int bindex;
+};
+
+struct isopaque_args {
+ struct dentry *dentry;
+};
+
+struct create_args {
+ struct inode *parent;
+ struct dentry *dentry;
+ umode_t mode;
+ struct nameidata *nd;
+};
+
+struct mkdir_args {
+ struct inode *parent;
+ struct dentry *dentry;
+ umode_t mode;
+};
+
+struct mknod_args {
+ struct inode *parent;
+ struct dentry *dentry;
+ umode_t mode;
+ dev_t dev;
+};
+
+struct symlink_args {
+ struct inode *parent;
+ struct dentry *dentry;
+ char *symbuf;
+ umode_t mode;
+};
+
+struct unlink_args {
+ struct inode *parent;
+ struct dentry *dentry;
+};
+
+
+struct sioq_args {
+
+ struct completion comp;
+ int err;
+ void *ret;
+
+ union {
+ struct deletewh_args deletewh;
+ struct isopaque_args isopaque;
+ struct create_args create;
+ struct mkdir_args mkdir;
+ struct mknod_args mknod;
+ struct symlink_args symlink;
+ struct unlink_args unlink;
+ };
+};
+
+extern struct workqueue_struct *sioq;
+int __init init_sioq(void);
+extern void fin_sioq(void);
+extern void run_sioq(void (*func)(void *arg), struct sioq_args *args);
+
+/* Extern definitions for our privledge escalation helpers */
+extern void __unionfs_create(void *data);
+extern void __unionfs_mkdir(void *data);
+extern void __unionfs_mknod(void *data);
+extern void __unionfs_symlink(void *data);
+extern void __unionfs_unlink(void *data);
+extern void __delete_whiteouts(void *data);
+extern void __is_opaque_dir(void *data);
+
+#endif /* _SIOQ_H */
+
--
1.4.3.3

2006-12-04 12:41:58

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 30/35] Unionfs: Helper macros/inlines

From: Josef "Jeff" Sipek <[email protected]>

This patch contains many macros and inline functions used thoughout Unionfs.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
Signed-off-by: David Quigley <[email protected]>
Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/fanout.h | 177 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 177 insertions(+), 0 deletions(-)

diff --git a/fs/unionfs/fanout.h b/fs/unionfs/fanout.h
new file mode 100644
index 0000000..309c881
--- /dev/null
+++ b/fs/unionfs/fanout.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2003-2006 Erez Zadok
+ * Copyright (c) 2003-2006 Charles P. Wright
+ * Copyright (c) 2005-2006 Josef Sipek
+ * Copyright (c) 2005 Arun M. Krishnakumar
+ * Copyright (c) 2004-2006 David P. Quigley
+ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair
+ * Copyright (c) 2003 Puja Gupta
+ * Copyright (c) 2003 Harikesavan Krishnan
+ * Copyright (c) 2003-2006 Stony Brook University
+ * Copyright (c) 2003-2006 The Research Foundation of State University of New York
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _FANOUT_H_
+#define _FANOUT_H_
+
+/* Inode to private data */
+static inline struct unionfs_inode_info *UNIONFS_I(const struct inode *inode)
+{
+ return container_of(inode, struct unionfs_inode_info, vfs_inode);
+}
+
+#define ibstart(ino) (UNIONFS_I(ino)->bstart)
+#define ibend(ino) (UNIONFS_I(ino)->bend)
+
+/* Superblock to private data */
+#define UNIONFS_SB(super) ((struct unionfs_sb_info *)(super)->s_fs_info)
+#define sbstart(sb) 0
+#define sbend(sb) UNIONFS_SB(sb)->bend
+#define sbmax(sb) (UNIONFS_SB(sb)->bend + 1)
+
+/* File to private Data */
+#define UNIONFS_F(file) ((struct unionfs_file_info *)((file)->private_data))
+#define fbstart(file) (UNIONFS_F(file)->bstart)
+#define fbend(file) (UNIONFS_F(file)->bend)
+
+/* File to lower file. */
+static inline struct file *unionfs_lower_file(struct file *f)
+{
+ return UNIONFS_F(f)->lower_files[fbstart(f)];
+}
+
+static inline struct file *unionfs_lower_file_idx(const struct file *f, int index)
+{
+ return UNIONFS_F(f)->lower_files[index];
+}
+
+static inline void unionfs_set_lower_file_idx(struct file *f, int index, struct file *val)
+{
+ UNIONFS_F(f)->lower_files[index] = val;
+}
+
+static inline void unionfs_set_lower_file(struct file *f, struct file *val)
+{
+ UNIONFS_F(f)->lower_files[fbstart(f)] = val;
+}
+
+/* Inode to lower inode. */
+static inline struct inode *unionfs_lower_inode(const struct inode *i)
+{
+ return UNIONFS_I(i)->lower_inodes[ibstart(i)];
+}
+
+static inline struct inode *unionfs_lower_inode_idx(const struct inode *i, int index)
+{
+ return UNIONFS_I(i)->lower_inodes[index];
+}
+
+static inline void unionfs_set_lower_inode_idx(struct inode *i, int index,
+ struct inode *val)
+{
+ UNIONFS_I(i)->lower_inodes[index] = val;
+}
+
+static inline void unionfs_set_lower_inode(struct inode *i, struct inode *val)
+{
+ UNIONFS_I(i)->lower_inodes[ibstart(i)] = val;
+}
+
+/* Superblock to lower superblock. */
+static inline struct super_block *unionfs_lower_super(const struct super_block *sb)
+{
+ return UNIONFS_SB(sb)->data[sbstart(sb)].sb;
+}
+
+static inline struct super_block *unionfs_lower_super_idx(const struct super_block *sb, int index)
+{
+ return UNIONFS_SB(sb)->data[index].sb;
+}
+
+static inline void unionfs_set_lower_super_idx(struct super_block *sb, int index,
+ struct super_block *val)
+{
+ UNIONFS_SB(sb)->data[index].sb = val;
+}
+
+static inline void unionfs_set_lower_super(struct super_block *sb, struct super_block *val)
+{
+ UNIONFS_SB(sb)->data[sbstart(sb)].sb = val;
+}
+
+/* Branch count macros. */
+static inline int branch_count(struct super_block *sb, int index)
+{
+ return atomic_read(&UNIONFS_SB(sb)->data[index].sbcount);
+}
+
+static inline void set_branch_count(struct super_block *sb, int index, int val)
+{
+ atomic_set(&UNIONFS_SB(sb)->data[index].sbcount, val);
+}
+
+static inline void branchget(struct super_block *sb, int index)
+{
+ atomic_inc(&UNIONFS_SB(sb)->data[index].sbcount);
+}
+
+static inline void branchput(struct super_block *sb, int index)
+{
+ atomic_dec(&UNIONFS_SB(sb)->data[index].sbcount);
+}
+
+/* Dentry macros */
+static inline struct unionfs_dentry_info *UNIONFS_D(const struct dentry *dent)
+{
+ return dent->d_fsdata;
+}
+
+#define dbstart(dent) (UNIONFS_D(dent)->bstart)
+#define set_dbstart(dent, val) do { UNIONFS_D(dent)->bstart = val; } while(0)
+#define dbend(dent) (UNIONFS_D(dent)->bend)
+#define set_dbend(dent, val) do { UNIONFS_D(dent)->bend = val; } while(0)
+#define dbopaque(dent) (UNIONFS_D(dent)->bopaque)
+#define set_dbopaque(dent, val) do { UNIONFS_D(dent)->bopaque = val; } while (0)
+
+static inline void unionfs_set_lower_dentry_idx(struct dentry *dent, int index,
+ struct dentry *val)
+{
+ UNIONFS_D(dent)->lower_paths[index].dentry = val;
+}
+
+static inline struct dentry *unionfs_lower_dentry_idx(const struct dentry *dent, int index)
+{
+ return UNIONFS_D(dent)->lower_paths[index].dentry;
+}
+
+static inline struct dentry *unionfs_lower_dentry(const struct dentry *dent)
+{
+ return unionfs_lower_dentry_idx(dent, dbstart(dent));
+}
+
+static inline void unionfs_set_lower_mnt_idx(struct dentry *dent, int index,
+ struct vfsmount *mnt)
+{
+ UNIONFS_D(dent)->lower_paths[index].mnt = mnt;
+}
+
+static inline struct vfsmount *unionfs_lower_mnt_idx(const struct dentry *dent, int index)
+{
+ return UNIONFS_D(dent)->lower_paths[index].mnt;
+}
+
+static inline struct vfsmount *unionfs_lower_mnt(const struct dentry *dent)
+{
+ return unionfs_lower_mnt_idx(dent,dbstart(dent));
+}
+
+/* Macros for locking a dentry. */
+#define lock_dentry(d) down(&UNIONFS_D(d)->sem)
+#define unlock_dentry(d) up(&UNIONFS_D(d)->sem)
+#define verify_locked(d)
+
+#endif /* _FANOUT_H */
--
1.4.3.3

2006-12-04 12:40:24

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 11/35] fsstack: Fix up ecryptfs's fsstack usage

From: Josef "Jeff" Sipek <[email protected]>

Fix up a stray ecryptfs_copy_attr_all call and remove prototypes for
ecryptfs_copy_* as they no longer exist.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
---
fs/ecryptfs/dentry.c | 2 +-
fs/ecryptfs/ecryptfs_kernel.h | 4 +---
2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/fs/ecryptfs/dentry.c b/fs/ecryptfs/dentry.c
index 52d1e36..b0352d8 100644
--- a/fs/ecryptfs/dentry.c
+++ b/fs/ecryptfs/dentry.c
@@ -61,7 +61,7 @@ static int ecryptfs_d_revalidate(struct
struct inode *lower_inode =
ecryptfs_inode_to_lower(dentry->d_inode);

- ecryptfs_copy_attr_all(dentry->d_inode, lower_inode);
+ fsstack_copy_attr_all(dentry->d_inode, lower_inode, NULL);
}
out:
return rc;
diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
index 870a65b..afb64bd 100644
--- a/fs/ecryptfs/ecryptfs_kernel.h
+++ b/fs/ecryptfs/ecryptfs_kernel.h
@@ -28,6 +28,7 @@

#include <keys/user-type.h>
#include <linux/fs.h>
+#include <linux/fs_stack.h>
#include <linux/namei.h>
#include <linux/scatterlist.h>

@@ -413,9 +414,6 @@ int ecryptfs_encode_filename(struct ecry
const char *name, int length,
char **encoded_name);
struct dentry *ecryptfs_lower_dentry(struct dentry *this_dentry);
-void ecryptfs_copy_attr_atime(struct inode *dest, const struct inode *src);
-void ecryptfs_copy_attr_all(struct inode *dest, const struct inode *src);
-void ecryptfs_copy_inode_size(struct inode *dst, const struct inode *src);
void ecryptfs_dump_hex(char *data, int bytes);
int virt_to_scatterlist(const void *addr, int size, struct scatterlist *sg,
int sg_size);
--
1.4.3.3

2006-12-04 12:40:17

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 32/35] Unionfs: Include file

From: Josef "Jeff" Sipek <[email protected]>

Global include file - can be included from userspace by utilities.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
Signed-off-by: David Quigley <[email protected]>
Signed-off-by: Erez Zadok <[email protected]>
---
include/linux/union_fs.h | 20 ++++++++++++++++++++
1 files changed, 20 insertions(+), 0 deletions(-)

diff --git a/include/linux/union_fs.h b/include/linux/union_fs.h
new file mode 100644
index 0000000..e76d3cf
--- /dev/null
+++ b/include/linux/union_fs.h
@@ -0,0 +1,20 @@
+#ifndef _LINUX_UNION_FS_H
+#define _LINUX_UNION_FS_H
+
+#define UNIONFS_VERSION "2.0"
+/*
+ * DEFINITIONS FOR USER AND KERNEL CODE:
+ * (Note: ioctl numbers 1--9 are reserved for fistgen, the rest
+ * are auto-generated automatically based on the user's .fist file.)
+ */
+# define UNIONFS_IOCTL_INCGEN _IOR(0x15, 11, int)
+# define UNIONFS_IOCTL_QUERYFILE _IOR(0x15, 15, int)
+
+/* We don't support normal remount, but unionctl uses it. */
+# define UNIONFS_REMOUNT_MAGIC 0x4a5a4380
+
+/* should be at least LAST_USED_UNIONFS_PERMISSION<<1 */
+#define MAY_NFSRO 16
+
+#endif /* _LINUX_UNIONFS_H */
+
--
1.4.3.3

2006-12-04 12:41:47

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 24/35] Unionfs: Readdir state

From: Josef "Jeff" Sipek <[email protected]>

This file contains the routines for maintaining readdir state.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
Signed-off-by: David Quigley <[email protected]>
Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/rdstate.c | 282 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 282 insertions(+), 0 deletions(-)

diff --git a/fs/unionfs/rdstate.c b/fs/unionfs/rdstate.c
new file mode 100644
index 0000000..5a19157
--- /dev/null
+++ b/fs/unionfs/rdstate.c
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2003-2006 Erez Zadok
+ * Copyright (c) 2003-2006 Charles P. Wright
+ * Copyright (c) 2005-2006 Josef 'Jeff' Sipek
+ * Copyright (c) 2005-2006 Junjiro Okajima
+ * Copyright (c) 2005 Arun M. Krishnakumar
+ * Copyright (c) 2004-2006 David P. Quigley
+ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair
+ * Copyright (c) 2003 Puja Gupta
+ * Copyright (c) 2003 Harikesavan Krishnan
+ * Copyright (c) 2003-2006 Stony Brook University
+ * Copyright (c) 2003-2006 The Research Foundation of State University of New York
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "union.h"
+
+/* This file contains the routines for maintaining readdir state. */
+/* There are two structures here, rdstate which is a hash table
+ * of the second structure which is a filldir_node. */
+
+/* This is a struct kmem_cache for filldir nodes, because we allocate a lot
+ * of them and they shouldn't waste memory. If the node has a small name
+ * (as defined by the dentry structure), then we use an inline name to
+ * preserve kmalloc space. */
+static struct kmem_cache *unionfs_filldir_cachep;
+int init_filldir_cache(void)
+{
+ unionfs_filldir_cachep =
+ kmem_cache_create("unionfs_filldir", sizeof(struct filldir_node), 0,
+ SLAB_RECLAIM_ACCOUNT, NULL, NULL);
+
+ return (unionfs_filldir_cachep ? 0 : -ENOMEM);
+}
+
+void destroy_filldir_cache(void)
+{
+ if (unionfs_filldir_cachep)
+ kmem_cache_destroy(unionfs_filldir_cachep);
+}
+
+/* This is a tuning parameter that tells us roughly how big to make the
+ * hash table in directory entries per page. This isn't perfect, but
+ * at least we get a hash table size that shouldn't be too overloaded.
+ * The following averages are based on my home directory.
+ * 14.44693 Overall
+ * 12.29 Single Page Directories
+ * 117.93 Multi-page directories
+ */
+#define DENTPAGE 4096
+#define DENTPERONEPAGE 12
+#define DENTPERPAGE 118
+#define MINHASHSIZE 1
+static int guesstimate_hash_size(struct inode *inode)
+{
+ struct inode *hidden_inode;
+ int bindex;
+ int hashsize = MINHASHSIZE;
+
+ if (UNIONFS_I(inode)->hashsize > 0)
+ return UNIONFS_I(inode)->hashsize;
+
+ for (bindex = ibstart(inode); bindex <= ibend(inode); bindex++) {
+ if (!(hidden_inode = unionfs_lower_inode_idx(inode, bindex)))
+ continue;
+
+ if (hidden_inode->i_size == DENTPAGE)
+ hashsize += DENTPERONEPAGE;
+ else
+ hashsize += (hidden_inode->i_size / DENTPAGE) * DENTPERPAGE;
+ }
+
+ return hashsize;
+}
+
+int init_rdstate(struct file *file)
+{
+ BUG_ON(sizeof(loff_t) != (sizeof(unsigned int) + sizeof(unsigned int)));
+ BUG_ON(UNIONFS_F(file)->rdstate != NULL);
+
+ UNIONFS_F(file)->rdstate = alloc_rdstate(file->f_dentry->d_inode,
+ fbstart(file));
+
+ return (UNIONFS_F(file)->rdstate ? 0 : -ENOMEM);
+}
+
+struct unionfs_dir_state *find_rdstate(struct inode *inode, loff_t fpos)
+{
+ struct unionfs_dir_state *rdstate = NULL;
+ struct list_head *pos;
+
+ spin_lock(&UNIONFS_I(inode)->rdlock);
+ list_for_each(pos, &UNIONFS_I(inode)->readdircache) {
+ struct unionfs_dir_state *r =
+ list_entry(pos, struct unionfs_dir_state, cache);
+ if (fpos == rdstate2offset(r)) {
+ UNIONFS_I(inode)->rdcount--;
+ list_del(&r->cache);
+ rdstate = r;
+ break;
+ }
+ }
+ spin_unlock(&UNIONFS_I(inode)->rdlock);
+ return rdstate;
+}
+
+struct unionfs_dir_state *alloc_rdstate(struct inode *inode, int bindex)
+{
+ int i = 0;
+ int hashsize;
+ int mallocsize = sizeof(struct unionfs_dir_state);
+ struct unionfs_dir_state *rdstate;
+
+ hashsize = guesstimate_hash_size(inode);
+ mallocsize += hashsize * sizeof(struct list_head);
+ /* Round it up to the next highest power of two. */
+ mallocsize--;
+ mallocsize |= mallocsize >> 1;
+ mallocsize |= mallocsize >> 2;
+ mallocsize |= mallocsize >> 4;
+ mallocsize |= mallocsize >> 8;
+ mallocsize |= mallocsize >> 16;
+ mallocsize++;
+
+ /* This should give us about 500 entries anyway. */
+ if (mallocsize > PAGE_SIZE)
+ mallocsize = PAGE_SIZE;
+
+ hashsize = (mallocsize -
+ sizeof(struct unionfs_dir_state)) / sizeof(struct list_head);
+
+ rdstate = kmalloc(mallocsize, GFP_KERNEL);
+ if (!rdstate)
+ return NULL;
+
+ spin_lock(&UNIONFS_I(inode)->rdlock);
+ if (UNIONFS_I(inode)->cookie >= (MAXRDCOOKIE - 1))
+ UNIONFS_I(inode)->cookie = 1;
+ else
+ UNIONFS_I(inode)->cookie++;
+
+ rdstate->cookie = UNIONFS_I(inode)->cookie;
+ spin_unlock(&UNIONFS_I(inode)->rdlock);
+ rdstate->offset = 1;
+ rdstate->access = jiffies;
+ rdstate->bindex = bindex;
+ rdstate->dirpos = 0;
+ rdstate->hashentries = 0;
+ rdstate->size = hashsize;
+ for (i = 0; i < rdstate->size; i++)
+ INIT_LIST_HEAD(&rdstate->list[i]);
+
+ return rdstate;
+}
+
+static void free_filldir_node(struct filldir_node *node)
+{
+ if (node->namelen >= DNAME_INLINE_LEN_MIN)
+ kfree(node->name);
+ kmem_cache_free(unionfs_filldir_cachep, node);
+}
+
+void free_rdstate(struct unionfs_dir_state *state)
+{
+ struct filldir_node *tmp;
+ int i;
+
+ for (i = 0; i < state->size; i++) {
+ struct list_head *head = &(state->list[i]);
+ struct list_head *pos, *n;
+
+ /* traverse the list and deallocate space */
+ list_for_each_safe(pos, n, head) {
+ tmp = list_entry(pos, struct filldir_node, file_list);
+ list_del(&tmp->file_list);
+ free_filldir_node(tmp);
+ }
+ }
+
+ kfree(state);
+}
+
+struct filldir_node *find_filldir_node(struct unionfs_dir_state *rdstate,
+ const char *name, int namelen)
+{
+ int index;
+ unsigned int hash;
+ struct list_head *head;
+ struct list_head *pos;
+ struct filldir_node *cursor = NULL;
+ int found = 0;
+
+ BUG_ON(namelen <= 0);
+
+ hash = full_name_hash(name, namelen);
+ index = hash % rdstate->size;
+
+ head = &(rdstate->list[index]);
+ list_for_each(pos, head) {
+ cursor = list_entry(pos, struct filldir_node, file_list);
+
+ if (cursor->namelen == namelen && cursor->hash == hash &&
+ !strncmp(cursor->name, name, namelen)) {
+ /* a duplicate exists, and hence no need to create
+ * entry to the list */
+ found = 1;
+
+ /* if the duplicate is in this branch, then the file
+ * system is corrupted. */
+ if (cursor->bindex == rdstate->bindex) {
+ printk(KERN_DEBUG "Possible I/O error "
+ "unionfs_filldir: a file is duplicated "
+ "in the same branch %d: %s\n",
+ rdstate->bindex, cursor->name);
+ }
+ break;
+ }
+ }
+
+ if (!found)
+ cursor = NULL;
+
+ return cursor;
+}
+
+inline struct filldir_node *alloc_filldir_node(const char *name, int namelen,
+ unsigned int hash, int bindex)
+{
+ return kmem_cache_alloc(unionfs_filldir_cachep, SLAB_KERNEL);
+}
+
+int add_filldir_node(struct unionfs_dir_state *rdstate, const char *name,
+ int namelen, int bindex, int whiteout)
+{
+ struct filldir_node *new;
+ unsigned int hash;
+ int index;
+ int err = 0;
+ struct list_head *head;
+
+ BUG_ON(namelen <= 0);
+
+ hash = full_name_hash(name, namelen);
+ index = hash % rdstate->size;
+ head = &(rdstate->list[index]);
+
+ new = alloc_filldir_node(name, namelen, hash, bindex);
+ if (!new) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ INIT_LIST_HEAD(&new->file_list);
+ new->namelen = namelen;
+ new->hash = hash;
+ new->bindex = bindex;
+ new->whiteout = whiteout;
+
+ if (namelen < DNAME_INLINE_LEN_MIN)
+ new->name = new->iname;
+ else {
+ new->name = kmalloc(namelen + 1, GFP_KERNEL);
+ if (!new->name) {
+ kmem_cache_free(unionfs_filldir_cachep, new);
+ new = NULL;
+ goto out;
+ }
+ }
+
+ memcpy(new->name, name, namelen);
+ new->name[namelen] = '\0';
+
+ rdstate->hashentries++;
+
+ list_add(&(new->file_list), head);
+out:
+ return err;
+}
+
--
1.4.3.3

2006-12-04 12:43:01

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 33/35] Unionfs: Unlink

From: Josef "Jeff" Sipek <[email protected]>

This patch provides unlink functionality for Unionfs.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
Signed-off-by: David Quigley <[email protected]>
Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/unlink.c | 162 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 162 insertions(+), 0 deletions(-)

diff --git a/fs/unionfs/unlink.c b/fs/unionfs/unlink.c
new file mode 100644
index 0000000..844835f
--- /dev/null
+++ b/fs/unionfs/unlink.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2003-2006 Erez Zadok
+ * Copyright (c) 2003-2006 Charles P. Wright
+ * Copyright (c) 2005-2006 Josef 'Jeff' Sipek
+ * Copyright (c) 2005-2006 Junjiro Okajima
+ * Copyright (c) 2005 Arun M. Krishnakumar
+ * Copyright (c) 2004-2006 David P. Quigley
+ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair
+ * Copyright (c) 2003 Puja Gupta
+ * Copyright (c) 2003 Harikesavan Krishnan
+ * Copyright (c) 2003-2006 Stony Brook University
+ * Copyright (c) 2003-2006 The Research Foundation of State University of New York
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "union.h"
+
+/* unlink a file by creating a whiteout */
+static int unionfs_unlink_whiteout(struct inode *dir, struct dentry *dentry)
+{
+ struct dentry *hidden_dentry;
+ struct dentry *hidden_dir_dentry;
+ int bindex;
+ int err = 0;
+
+ if ((err = unionfs_partial_lookup(dentry)))
+ goto out;
+
+ bindex = dbstart(dentry);
+
+ hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ if (!hidden_dentry)
+ goto out;
+
+ hidden_dir_dentry = lock_parent(hidden_dentry);
+
+ /* avoid destroying the hidden inode if the file is in use */
+ dget(hidden_dentry);
+ if (!(err = is_robranch_super(dentry->d_sb, bindex)))
+ err = vfs_unlink(hidden_dir_dentry->d_inode, hidden_dentry);
+ dput(hidden_dentry);
+ fsstack_copy_attr_times(dir, hidden_dir_dentry->d_inode);
+ unlock_dir(hidden_dir_dentry);
+
+ if (err && !IS_COPYUP_ERR(err))
+ goto out;
+
+ if (err) {
+ if (dbstart(dentry) == 0)
+ goto out;
+
+ err = create_whiteout(dentry, dbstart(dentry) - 1);
+ } else if (dbopaque(dentry) != -1)
+ /* There is a hidden lower-priority file with the same name. */
+ err = create_whiteout(dentry, dbopaque(dentry));
+ else
+ err = create_whiteout(dentry, dbstart(dentry));
+
+out:
+ if (!err)
+ dentry->d_inode->i_nlink--;
+
+ /* We don't want to leave negative leftover dentries for revalidate. */
+ if (!err && (dbopaque(dentry) != -1))
+ update_bstart(dentry);
+
+ return err;
+}
+
+int unionfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+ int err = 0;
+
+ lock_dentry(dentry);
+
+ err = unionfs_unlink_whiteout(dir, dentry);
+ /* call d_drop so the system "forgets" about us */
+ if (!err)
+ d_drop(dentry);
+
+ unlock_dentry(dentry);
+ return err;
+}
+
+static int unionfs_rmdir_first(struct inode *dir, struct dentry *dentry,
+ struct unionfs_dir_state *namelist)
+{
+ int err;
+ struct dentry *hidden_dentry;
+ struct dentry *hidden_dir_dentry = NULL;
+
+ /* Here we need to remove whiteout entries. */
+ err = delete_whiteouts(dentry, dbstart(dentry), namelist);
+ if (err)
+ goto out;
+
+ hidden_dentry = unionfs_lower_dentry(dentry);
+
+ hidden_dir_dentry = lock_parent(hidden_dentry);
+
+ /* avoid destroying the hidden inode if the file is in use */
+ dget(hidden_dentry);
+ if (!(err = is_robranch(dentry)))
+ err = vfs_rmdir(hidden_dir_dentry->d_inode, hidden_dentry);
+ dput(hidden_dentry);
+
+ fsstack_copy_attr_times(dir, hidden_dir_dentry->d_inode);
+ /* propagate number of hard-links */
+ dentry->d_inode->i_nlink = unionfs_get_nlinks(dentry->d_inode);
+
+out:
+ if (hidden_dir_dentry)
+ unlock_dir(hidden_dir_dentry);
+ return err;
+}
+
+int unionfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ int err = 0;
+ struct unionfs_dir_state *namelist = NULL;
+
+ lock_dentry(dentry);
+
+ /* check if this unionfs directory is empty or not */
+ err = check_empty(dentry, &namelist);
+ if (err)
+ goto out;
+
+ err = unionfs_rmdir_first(dir, dentry, namelist);
+ /* create whiteout */
+ if (!err)
+ err = create_whiteout(dentry, dbstart(dentry));
+ else {
+ int new_err;
+
+ if (dbstart(dentry) == 0)
+ goto out;
+
+ /* exit if the error returned was NOT -EROFS */
+ if (!IS_COPYUP_ERR(err))
+ goto out;
+
+ new_err = create_whiteout(dentry, dbstart(dentry) - 1);
+ if (new_err != -EEXIST)
+ err = new_err;
+ }
+
+out:
+ /* call d_drop so the system "forgets" about us */
+ if (!err)
+ d_drop(dentry);
+
+ if (namelist)
+ free_rdstate(namelist);
+
+ unlock_dentry(dentry);
+ return err;
+}
+
--
1.4.3.3

2006-12-04 12:45:14

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 13/35] lookup_one_len_nd - lookup_one_len with nameidata argument

From: Josef "Jeff" Sipek <[email protected]>

This patch renames lookup_one_len to lookup_one_len_nd, and adds a nameidata
argument. An inline function, lookup_one_len (which calls lookup_one_len_nd
with nd == NULL) preserves original behavior.

The following Unionfs patches depend on this one.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
---
fs/namei.c | 8 ++++----
include/linux/namei.h | 10 +++++++++-
2 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 8a7b923..76de391 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1290,8 +1290,8 @@ static struct dentry *lookup_hash(struct
return __lookup_hash(&nd->last, nd->dentry, nd);
}

-/* SMP-safe */
-struct dentry * lookup_one_len(const char * name, struct dentry * base, int len)
+struct dentry * lookup_one_len_nd(const char *name, struct dentry * base,
+ int len, struct nameidata *nd)
{
unsigned long hash;
struct qstr this;
@@ -1311,7 +1311,7 @@ struct dentry * lookup_one_len(const cha
}
this.hash = end_name_hash(hash);

- return __lookup_hash(&this, base, NULL);
+ return __lookup_hash(&this, base, nd);
access:
return ERR_PTR(-EACCES);
}
@@ -2756,7 +2756,7 @@ EXPORT_SYMBOL(follow_up);
EXPORT_SYMBOL(get_write_access); /* binfmt_aout */
EXPORT_SYMBOL(getname);
EXPORT_SYMBOL(lock_rename);
-EXPORT_SYMBOL(lookup_one_len);
+EXPORT_SYMBOL(lookup_one_len_nd);
EXPORT_SYMBOL(page_follow_link_light);
EXPORT_SYMBOL(page_put_link);
EXPORT_SYMBOL(page_readlink);
diff --git a/include/linux/namei.h b/include/linux/namei.h
index d39a5a6..8551806 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -81,7 +81,15 @@ extern struct file *lookup_instantiate_f
extern struct file *nameidata_to_filp(struct nameidata *nd, int flags);
extern void release_open_intent(struct nameidata *);

-extern struct dentry * lookup_one_len(const char *, struct dentry *, int);
+extern struct dentry * lookup_one_len_nd(const char *,
+ struct dentry *, int, struct nameidata *);
+
+/* SMP-safe */
+static inline struct dentry *lookup_one_len(const char *name,
+ struct dentry *dir, int len)
+{
+ return lookup_one_len_nd(name, dir, len, NULL);
+}

extern int follow_down(struct vfsmount **, struct dentry **);
extern int follow_up(struct vfsmount **, struct dentry **);
--
1.4.3.3

2006-12-04 12:46:37

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 03/35] eCryptfs: Use fsstack's generic copy inode attr functions

From: Josef "Jeff" Sipek <[email protected]>

Replace eCryptfs specific code & calls with the more generic fsstack
equivalents and remove the eCryptfs specific functions.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
Cc: Michael Halcrow <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
---
fs/ecryptfs/file.c | 3 +-
fs/ecryptfs/inode.c | 75 +++++++++++++--------------------------------------
fs/ecryptfs/main.c | 5 ++-
3 files changed, 24 insertions(+), 59 deletions(-)

diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c
index a92ef05..a961a0c 100644
--- a/fs/ecryptfs/file.c
+++ b/fs/ecryptfs/file.c
@@ -30,6 +30,7 @@
#include <linux/security.h>
#include <linux/smp_lock.h>
#include <linux/compat.h>
+#include <linux/fs_stack.h>
#include "ecryptfs_kernel.h"

/**
@@ -192,7 +193,7 @@ retry:
goto retry;
file->f_pos = lower_file->f_pos;
if (rc >= 0)
- ecryptfs_copy_attr_atime(inode, lower_file->f_dentry->d_inode);
+ fsstack_copy_attr_atime(inode, lower_file->f_dentry->d_inode);
return rc;
}

diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index dfcc684..3e2a786 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -30,6 +30,7 @@
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/crypto.h>
+#include <linux/fs_stack.h>
#include "ecryptfs_kernel.h"

static struct dentry *lock_parent(struct dentry *dentry)
@@ -53,48 +54,6 @@ static void unlock_dir(struct dentry *di
dput(dir);
}

-void ecryptfs_copy_inode_size(struct inode *dst, const struct inode *src)
-{
- i_size_write(dst, i_size_read((struct inode *)src));
- dst->i_blocks = src->i_blocks;
-}
-
-void ecryptfs_copy_attr_atime(struct inode *dest, const struct inode *src)
-{
- dest->i_atime = src->i_atime;
-}
-
-static void ecryptfs_copy_attr_times(struct inode *dest,
- const struct inode *src)
-{
- dest->i_atime = src->i_atime;
- dest->i_mtime = src->i_mtime;
- dest->i_ctime = src->i_ctime;
-}
-
-static void ecryptfs_copy_attr_timesizes(struct inode *dest,
- const struct inode *src)
-{
- dest->i_atime = src->i_atime;
- dest->i_mtime = src->i_mtime;
- dest->i_ctime = src->i_ctime;
- ecryptfs_copy_inode_size(dest, src);
-}
-
-void ecryptfs_copy_attr_all(struct inode *dest, const struct inode *src)
-{
- dest->i_mode = src->i_mode;
- dest->i_nlink = src->i_nlink;
- dest->i_uid = src->i_uid;
- dest->i_gid = src->i_gid;
- dest->i_rdev = src->i_rdev;
- dest->i_atime = src->i_atime;
- dest->i_mtime = src->i_mtime;
- dest->i_ctime = src->i_ctime;
- dest->i_blkbits = src->i_blkbits;
- dest->i_flags = src->i_flags;
-}
-
/**
* ecryptfs_create_underlying_file
* @lower_dir_inode: inode of the parent in the lower fs of the new file
@@ -171,8 +130,8 @@ ecryptfs_do_create(struct inode *directo
ecryptfs_printk(KERN_ERR, "Failure in ecryptfs_interpose\n");
goto out_lock;
}
- ecryptfs_copy_attr_timesizes(directory_inode,
- lower_dir_dentry->d_inode);
+ fsstack_copy_attr_times(directory_inode, lower_dir_dentry->d_inode);
+ fsstack_copy_inode_size(directory_inode, lower_dir_dentry->d_inode);
out_lock:
unlock_dir(lower_dir_dentry);
out:
@@ -365,7 +324,7 @@ static struct dentry *ecryptfs_lookup(st
"d_name.name = [%s]\n", lower_dentry,
lower_dentry->d_name.name);
lower_inode = lower_dentry->d_inode;
- ecryptfs_copy_attr_atime(dir, lower_dir_dentry->d_inode);
+ fsstack_copy_attr_atime(dir, lower_dir_dentry->d_inode);
BUG_ON(!atomic_read(&lower_dentry->d_count));
ecryptfs_set_dentry_private(dentry,
kmem_cache_alloc(ecryptfs_dentry_info_cache,
@@ -462,7 +421,8 @@ static int ecryptfs_link(struct dentry *
rc = ecryptfs_interpose(lower_new_dentry, new_dentry, dir->i_sb, 0);
if (rc)
goto out_lock;
- ecryptfs_copy_attr_timesizes(dir, lower_new_dentry->d_inode);
+ fsstack_copy_attr_times(dir, lower_new_dentry->d_inode);
+ fsstack_copy_inode_size(dir, lower_new_dentry->d_inode);
old_dentry->d_inode->i_nlink =
ecryptfs_inode_to_lower(old_dentry->d_inode)->i_nlink;
i_size_write(new_dentry->d_inode, file_size_save);
@@ -488,7 +448,7 @@ static int ecryptfs_unlink(struct inode
printk(KERN_ERR "Error in vfs_unlink; rc = [%d]\n", rc);
goto out_unlock;
}
- ecryptfs_copy_attr_times(dir, lower_dir_inode);
+ fsstack_copy_attr_times(dir, lower_dir_inode);
dentry->d_inode->i_nlink =
ecryptfs_inode_to_lower(dentry->d_inode)->i_nlink;
dentry->d_inode->i_ctime = dir->i_ctime;
@@ -527,7 +487,8 @@ static int ecryptfs_symlink(struct inode
rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0);
if (rc)
goto out_lock;
- ecryptfs_copy_attr_timesizes(dir, lower_dir_dentry->d_inode);
+ fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode);
+ fsstack_copy_inode_size(dir, lower_dir_dentry->d_inode);
out_lock:
unlock_dir(lower_dir_dentry);
dput(lower_dentry);
@@ -550,7 +511,8 @@ static int ecryptfs_mkdir(struct inode *
rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0);
if (rc)
goto out;
- ecryptfs_copy_attr_timesizes(dir, lower_dir_dentry->d_inode);
+ fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode);
+ fsstack_copy_inode_size(dir, lower_dir_dentry->d_inode);
dir->i_nlink = lower_dir_dentry->d_inode->i_nlink;
out:
unlock_dir(lower_dir_dentry);
@@ -573,7 +535,7 @@ static int ecryptfs_rmdir(struct inode *
dput(lower_dentry);
if (!rc)
d_delete(lower_dentry);
- ecryptfs_copy_attr_times(dir, lower_dir_dentry->d_inode);
+ fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode);
dir->i_nlink = lower_dir_dentry->d_inode->i_nlink;
unlock_dir(lower_dir_dentry);
if (!rc)
@@ -597,7 +559,8 @@ ecryptfs_mknod(struct inode *dir, struct
rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0);
if (rc)
goto out;
- ecryptfs_copy_attr_timesizes(dir, lower_dir_dentry->d_inode);
+ fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode);
+ fsstack_copy_inode_size(dir, lower_dir_dentry->d_inode);
out:
unlock_dir(lower_dir_dentry);
if (!dentry->d_inode)
@@ -626,9 +589,9 @@ ecryptfs_rename(struct inode *old_dir, s
lower_new_dir_dentry->d_inode, lower_new_dentry);
if (rc)
goto out_lock;
- ecryptfs_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode);
+ fsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode);
if (new_dir != old_dir)
- ecryptfs_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode);
+ fsstack_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode);
out_lock:
unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
dput(lower_new_dentry->d_parent);
@@ -684,8 +647,8 @@ ecryptfs_readlink(struct dentry *dentry,
rc = -EFAULT;
}
kfree(decoded_name);
- ecryptfs_copy_attr_atime(dentry->d_inode,
- lower_dentry->d_inode);
+ fsstack_copy_attr_atime(dentry->d_inode,
+ lower_dentry->d_inode);
}
out_free_lower_buf:
kfree(lower_buf);
@@ -915,7 +878,7 @@ static int ecryptfs_setattr(struct dentr
}
rc = notify_change(lower_dentry, ia);
out:
- ecryptfs_copy_attr_all(inode, lower_inode);
+ fsstack_copy_attr_all(inode, lower_inode);
return rc;
}

diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c
index a78d87d..5982931 100644
--- a/fs/ecryptfs/main.c
+++ b/fs/ecryptfs/main.c
@@ -35,6 +35,7 @@
#include <linux/pagemap.h>
#include <linux/key.h>
#include <linux/parser.h>
+#include <linux/fs_stack.h>
#include "ecryptfs_kernel.h"

/**
@@ -112,10 +113,10 @@ int ecryptfs_interpose(struct dentry *lo
d_add(dentry, inode);
else
d_instantiate(dentry, inode);
- ecryptfs_copy_attr_all(inode, lower_inode);
+ fsstack_copy_attr_all(inode, lower_inode);
/* This size will be overwritten for real files w/ headers and
* other metadata */
- ecryptfs_copy_inode_size(inode, lower_inode);
+ fsstack_copy_inode_size(inode, lower_inode);
out:
return rc;
}
--
1.4.3.3

2006-12-04 12:44:37

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 29/35] Unionfs: Superblock operations

From: Josef "Jeff" Sipek <[email protected]>

This patch contains the superblock operations for Unionfs.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
Signed-off-by: David Quigley <[email protected]>
Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/super.c | 342 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 342 insertions(+), 0 deletions(-)

diff --git a/fs/unionfs/super.c b/fs/unionfs/super.c
new file mode 100644
index 0000000..9920079
--- /dev/null
+++ b/fs/unionfs/super.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2003-2006 Erez Zadok
+ * Copyright (c) 2003-2006 Charles P. Wright
+ * Copyright (c) 2005-2006 Josef 'Jeff' Sipek
+ * Copyright (c) 2005-2006 Junjiro Okajima
+ * Copyright (c) 2005 Arun M. Krishnakumar
+ * Copyright (c) 2004-2006 David P. Quigley
+ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair
+ * Copyright (c) 2003 Puja Gupta
+ * Copyright (c) 2003 Harikesavan Krishnan
+ * Copyright (c) 2003-2006 Stony Brook University
+ * Copyright (c) 2003-2006 The Research Foundation of State University of New York
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "union.h"
+
+/* The inode cache is used with alloc_inode for both our inode info and the
+ * vfs inode. */
+static struct kmem_cache *unionfs_inode_cachep;
+
+static void unionfs_read_inode(struct inode *inode)
+{
+ static struct address_space_operations unionfs_empty_aops;
+ int size;
+ struct unionfs_inode_info *info = UNIONFS_I(inode);
+
+ if (!info) {
+ printk(KERN_ERR "No kernel memory when allocating inode "
+ "private data!\n");
+ BUG();
+ }
+
+ memset(info, 0, offsetof(struct unionfs_inode_info, vfs_inode));
+ info->bstart = -1;
+ info->bend = -1;
+ atomic_set(&info->generation,
+ atomic_read(&UNIONFS_SB(inode->i_sb)->generation));
+ spin_lock_init(&info->rdlock);
+ info->rdcount = 1;
+ info->hashsize = -1;
+ INIT_LIST_HEAD(&info->readdircache);
+
+ size = sbmax(inode->i_sb) * sizeof(struct inode *);
+ info->lower_inodes = kzalloc(size, GFP_KERNEL);
+ if (!info->lower_inodes) {
+ printk(KERN_ERR "No kernel memory when allocating lower-"
+ "pointer array!\n");
+ BUG();
+ }
+
+ inode->i_version++;
+ inode->i_op = &unionfs_main_iops;
+ inode->i_fop = &unionfs_main_fops;
+
+ /* I don't think ->a_ops is ever allowed to be NULL */
+ inode->i_mapping->a_ops = &unionfs_empty_aops;
+}
+
+static void unionfs_put_inode(struct inode *inode)
+{
+ /*
+ * This is really funky stuff:
+ * Basically, if i_count == 1, iput will then decrement it and this
+ * inode will be destroyed. It is currently holding a reference to the
+ * hidden inode. Therefore, it needs to release that reference by
+ * calling iput on the hidden inode. iput() _will_ do it for us (by
+ * calling our clear_inode), but _only_ if i_nlink == 0. The problem
+ * is, NFS keeps i_nlink == 1 for silly_rename'd files. So we must for
+ * our i_nlink to 0 here to trick iput() into calling our clear_inode.
+ */
+
+ if (atomic_read(&inode->i_count) == 1)
+ inode->i_nlink = 0;
+}
+
+/*
+ * we now define delete_inode, because there are two VFS paths that may
+ * destroy an inode: one of them calls clear inode before doing everything
+ * else that's needed, and the other is fine. This way we truncate the inode
+ * size (and its pages) and then clear our own inode, which will do an iput
+ * on our and the lower inode.
+ */
+static void unionfs_delete_inode(struct inode *inode)
+{
+ inode->i_size = 0; /* every f/s seems to do that */
+
+ clear_inode(inode);
+}
+
+/* final actions when unmounting a file system */
+static void unionfs_put_super(struct super_block *sb)
+{
+ int bindex, bstart, bend;
+ struct unionfs_sb_info *spd;
+
+ spd = UNIONFS_SB(sb);
+ if (!spd)
+ return;
+
+ bstart = sbstart(sb);
+ bend = sbend(sb);
+
+ /* Make sure we have no leaks of branchget/branchput. */
+ for (bindex = bstart; bindex <= bend; bindex++)
+ BUG_ON(branch_count(sb, bindex) != 0);
+
+ kfree(spd->data);
+ kfree(spd);
+ sb->s_fs_info = NULL;
+}
+
+/* Since people use this to answer the "How big of a file can I write?"
+ * question, we report the size of the highest priority branch as the size of
+ * the union. */
+static int unionfs_statfs(struct dentry *dentry, struct kstatfs *buf)
+{
+ int err = 0;
+ struct super_block *sb, *hidden_sb;
+
+ sb = dentry->d_sb;
+
+ hidden_sb = unionfs_lower_super_idx(sb, sbstart(sb));
+ err = vfs_statfs(hidden_sb->s_root, buf);
+
+ buf->f_type = UNIONFS_SUPER_MAGIC;
+ buf->f_namelen -= UNIONFS_WHLEN;
+
+ memset(&buf->f_fsid, 0, sizeof(__kernel_fsid_t));
+ memset(&buf->f_spare, 0, sizeof(buf->f_spare));
+
+ return err;
+}
+
+/* We don't support a standard text remount. Eventually it would be nice to
+ * support a full-on remount, so that you can have all of the directories
+ * change at once, but that would require some pretty complicated matching
+ * code. */
+static int unionfs_remount_fs(struct super_block *sb, int *flags, char *data)
+{
+ return -ENOSYS;
+}
+
+/*
+ * Called by iput() when the inode reference count reached zero
+ * and the inode is not hashed anywhere. Used to clear anything
+ * that needs to be, before the inode is completely destroyed and put
+ * on the inode free list.
+ */
+static void unionfs_clear_inode(struct inode *inode)
+{
+ int bindex, bstart, bend;
+ struct inode *hidden_inode;
+ struct list_head *pos, *n;
+ struct unionfs_dir_state *rdstate;
+
+ list_for_each_safe(pos, n, &UNIONFS_I(inode)->readdircache) {
+ rdstate = list_entry(pos, struct unionfs_dir_state, cache);
+ list_del(&rdstate->cache);
+ free_rdstate(rdstate);
+ }
+
+ /* Decrement a reference to a hidden_inode, which was incremented
+ * by our read_inode when it was created initially. */
+ bstart = ibstart(inode);
+ bend = ibend(inode);
+ if (bstart >= 0) {
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ hidden_inode = unionfs_lower_inode_idx(inode, bindex);
+ if (!hidden_inode)
+ continue;
+ iput(hidden_inode);
+ }
+ }
+
+ kfree(UNIONFS_I(inode)->lower_inodes);
+ UNIONFS_I(inode)->lower_inodes = NULL;
+}
+
+static struct inode *unionfs_alloc_inode(struct super_block *sb)
+{
+ struct unionfs_inode_info *i;
+
+ i = kmem_cache_alloc(unionfs_inode_cachep, SLAB_KERNEL);
+ if (!i)
+ return NULL;
+
+ /* memset everything up to the inode to 0 */
+ memset(i, 0, offsetof(struct unionfs_inode_info, vfs_inode));
+
+ i->vfs_inode.i_version = 1;
+ return &i->vfs_inode;
+}
+
+static void unionfs_destroy_inode(struct inode *inode)
+{
+ kmem_cache_free(unionfs_inode_cachep, UNIONFS_I(inode));
+}
+
+/* unionfs inode cache constructor */
+static void init_once(void *v, struct kmem_cache * cachep, unsigned long flags)
+{
+ struct unionfs_inode_info *i = v;
+
+ if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) ==
+ SLAB_CTOR_CONSTRUCTOR)
+ inode_init_once(&i->vfs_inode);
+}
+
+int init_inode_cache(void)
+{
+ int err = 0;
+
+ unionfs_inode_cachep =
+ kmem_cache_create("unionfs_inode_cache",
+ sizeof(struct unionfs_inode_info), 0,
+ SLAB_RECLAIM_ACCOUNT, init_once, NULL);
+ if (!unionfs_inode_cachep)
+ err = -ENOMEM;
+ return err;
+}
+
+void destroy_inode_cache(void)
+{
+ if (unionfs_inode_cachep)
+ kmem_cache_destroy(unionfs_inode_cachep);
+}
+
+/* Called when we have a dirty inode, right here we only throw out
+ * parts of our readdir list that are too old.
+ */
+static int unionfs_write_inode(struct inode *inode, int sync)
+{
+ struct list_head *pos, *n;
+ struct unionfs_dir_state *rdstate;
+
+ spin_lock(&UNIONFS_I(inode)->rdlock);
+ list_for_each_safe(pos, n, &UNIONFS_I(inode)->readdircache) {
+ rdstate = list_entry(pos, struct unionfs_dir_state, cache);
+ /* We keep this list in LRU order. */
+ if ((rdstate->access + RDCACHE_JIFFIES) > jiffies)
+ break;
+ UNIONFS_I(inode)->rdcount--;
+ list_del(&rdstate->cache);
+ free_rdstate(rdstate);
+ }
+ spin_unlock(&UNIONFS_I(inode)->rdlock);
+
+ return 0;
+}
+
+/*
+ * Used only in nfs, to kill any pending RPC tasks, so that subsequent
+ * code can actually succeed and won't leave tasks that need handling.
+ */
+static void unionfs_umount_begin(struct vfsmount *mnt, int flags)
+{
+ struct super_block *sb, *hidden_sb;
+ struct vfsmount *hidden_mnt;
+ int bindex, bstart, bend;
+
+ if (!(flags & MNT_FORCE))
+ /* we are not being MNT_FORCEd, therefore we should emulate
+ * old behaviour
+ */
+ return;
+
+ sb = mnt->mnt_sb;
+
+ bstart = sbstart(sb);
+ bend = sbend(sb);
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ hidden_mnt = unionfs_lower_mnt_idx(sb->s_root, bindex);
+ hidden_sb = unionfs_lower_super_idx(sb, bindex);
+
+ if (hidden_mnt && hidden_sb && hidden_sb->s_op &&
+ hidden_sb->s_op->umount_begin)
+ hidden_sb->s_op->umount_begin(hidden_mnt, flags);
+ }
+}
+
+static int unionfs_show_options(struct seq_file *m, struct vfsmount *mnt)
+{
+ struct super_block *sb = mnt->mnt_sb;
+ int ret = 0;
+ char *tmp_page;
+ char *path;
+ int bindex, bstart, bend;
+ int perms;
+
+ lock_dentry(sb->s_root);
+
+ tmp_page = (char*) __get_free_page(GFP_KERNEL);
+ if (!tmp_page) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ bstart = sbstart(sb);
+ bend = sbend(sb);
+
+ seq_printf(m, ",dirs=");
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ path = d_path(unionfs_lower_dentry_idx(sb->s_root, bindex),
+ unionfs_lower_mnt_idx(sb->s_root, bindex), tmp_page,
+ PAGE_SIZE);
+ perms = branchperms(sb, bindex);
+
+ seq_printf(m, "%s=%s", path,
+ perms & MAY_WRITE ? "rw" : "ro");
+ if (bindex != bend) {
+ seq_printf(m, ":");
+ }
+ }
+
+out:
+ if (tmp_page)
+ free_page((unsigned long) tmp_page);
+
+ unlock_dentry(sb->s_root);
+
+ return ret;
+}
+
+struct super_operations unionfs_sops = {
+ .read_inode = unionfs_read_inode,
+ .put_inode = unionfs_put_inode,
+ .delete_inode = unionfs_delete_inode,
+ .put_super = unionfs_put_super,
+ .statfs = unionfs_statfs,
+ .remount_fs = unionfs_remount_fs,
+ .clear_inode = unionfs_clear_inode,
+ .umount_begin = unionfs_umount_begin,
+ .show_options = unionfs_show_options,
+ .write_inode = unionfs_write_inode,
+ .alloc_inode = unionfs_alloc_inode,
+ .destroy_inode = unionfs_destroy_inode,
+};
+
--
1.4.3.3

2006-12-04 12:44:43

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 05/35] struct path: Rename Reiserfs's struct path

From: Josef "Jeff" Sipek <[email protected]>

Rename Reiserfs's struct path to struct treepath.

Cc: [email protected]
Signed-off-by: Josef "Jeff" Sipek <[email protected]>
---
fs/reiserfs/bitmap.c | 2 +-
fs/reiserfs/fix_node.c | 6 ++--
fs/reiserfs/inode.c | 12 +++++-----
fs/reiserfs/namei.c | 6 ++--
fs/reiserfs/stree.c | 42 +++++++++++++++++++-------------------
fs/reiserfs/tail_conversion.c | 4 +-
include/linux/reiserfs_fs.h | 44 ++++++++++++++++++++--------------------
7 files changed, 58 insertions(+), 58 deletions(-)

diff --git a/fs/reiserfs/bitmap.c b/fs/reiserfs/bitmap.c
index e3d466a..b286ccb 100644
--- a/fs/reiserfs/bitmap.c
+++ b/fs/reiserfs/bitmap.c
@@ -708,7 +708,7 @@ static void oid_groups(reiserfs_blocknr_
*/
static int get_left_neighbor(reiserfs_blocknr_hint_t * hint)
{
- struct path *path;
+ struct treepath *path;
struct buffer_head *bh;
struct item_head *ih;
int pos_in_item;
diff --git a/fs/reiserfs/fix_node.c b/fs/reiserfs/fix_node.c
index 6d0e554..0ee35c6 100644
--- a/fs/reiserfs/fix_node.c
+++ b/fs/reiserfs/fix_node.c
@@ -957,7 +957,7 @@ static int get_far_parent(struct tree_ba
{
struct buffer_head *p_s_parent;
INITIALIZE_PATH(s_path_to_neighbor_father);
- struct path *p_s_path = p_s_tb->tb_path;
+ struct treepath *p_s_path = p_s_tb->tb_path;
struct cpu_key s_lr_father_key;
int n_counter,
n_position = INT_MAX,
@@ -1074,7 +1074,7 @@ static int get_far_parent(struct tree_ba
*/
static int get_parents(struct tree_balance *p_s_tb, int n_h)
{
- struct path *p_s_path = p_s_tb->tb_path;
+ struct treepath *p_s_path = p_s_tb->tb_path;
int n_position,
n_ret_value,
n_path_offset = PATH_H_PATH_OFFSET(p_s_tb->tb_path, n_h);
@@ -1885,7 +1885,7 @@ static int check_balance(int mode,
static int get_direct_parent(struct tree_balance *p_s_tb, int n_h)
{
struct buffer_head *p_s_bh;
- struct path *p_s_path = p_s_tb->tb_path;
+ struct treepath *p_s_path = p_s_tb->tb_path;
int n_position,
n_path_offset = PATH_H_PATH_OFFSET(p_s_tb->tb_path, n_h);

diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
index 9c69bca..eda099e 100644
--- a/fs/reiserfs/inode.c
+++ b/fs/reiserfs/inode.c
@@ -207,7 +207,7 @@ static int file_capable(struct inode *in
}

/*static*/ int restart_transaction(struct reiserfs_transaction_handle *th,
- struct inode *inode, struct path *path)
+ struct inode *inode, struct treepath *path)
{
struct super_block *s = th->t_super;
int len = th->t_blocks_allocated;
@@ -569,7 +569,7 @@ static inline int _allocate_block(struct
long block,
struct inode *inode,
b_blocknr_t * allocated_block_nr,
- struct path *path, int flags)
+ struct treepath *path, int flags)
{
BUG_ON(!th->t_trans_id);

@@ -1109,7 +1109,7 @@ static inline ulong to_fake_used_blocks(
//

// called by read_locked_inode
-static void init_inode(struct inode *inode, struct path *path)
+static void init_inode(struct inode *inode, struct treepath *path)
{
struct buffer_head *bh;
struct item_head *ih;
@@ -1286,7 +1286,7 @@ static void inode2sd_v1(void *sd, struct
/* NOTE, you must prepare the buffer head before sending it here,
** and then log it after the call
*/
-static void update_stat_data(struct path *path, struct inode *inode,
+static void update_stat_data(struct treepath *path, struct inode *inode,
loff_t size)
{
struct buffer_head *bh;
@@ -1655,7 +1655,7 @@ int reiserfs_write_inode(struct inode *i
containing "." and ".." entries */
static int reiserfs_new_directory(struct reiserfs_transaction_handle *th,
struct inode *inode,
- struct item_head *ih, struct path *path,
+ struct item_head *ih, struct treepath *path,
struct inode *dir)
{
struct super_block *sb = th->t_super;
@@ -1714,7 +1714,7 @@ static int reiserfs_new_directory(struct
containing the body of symlink */
static int reiserfs_new_symlink(struct reiserfs_transaction_handle *th, struct inode *inode, /* Inode of symlink */
struct item_head *ih,
- struct path *path, const char *symname,
+ struct treepath *path, const char *symname,
int item_len)
{
struct super_block *sb = th->t_super;
diff --git a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c
index abde1ed..23f5cd5 100644
--- a/fs/reiserfs/namei.c
+++ b/fs/reiserfs/namei.c
@@ -54,7 +54,7 @@ static int bin_search_in_dir_item(struct

// comment? maybe something like set de to point to what the path points to?
static inline void set_de_item_location(struct reiserfs_dir_entry *de,
- struct path *path)
+ struct treepath *path)
{
de->de_bh = get_last_bh(path);
de->de_ih = get_ih(path);
@@ -113,7 +113,7 @@ entry position in the item

/* The function is NOT SCHEDULE-SAFE! */
int search_by_entry_key(struct super_block *sb, const struct cpu_key *key,
- struct path *path, struct reiserfs_dir_entry *de)
+ struct treepath *path, struct reiserfs_dir_entry *de)
{
int retval;

@@ -282,7 +282,7 @@ static int linear_search_in_dir_item(str
// may return NAME_FOUND, NAME_FOUND_INVISIBLE, NAME_NOT_FOUND
// FIXME: should add something like IOERROR
static int reiserfs_find_entry(struct inode *dir, const char *name, int namelen,
- struct path *path_to_entry,
+ struct treepath *path_to_entry,
struct reiserfs_dir_entry *de)
{
struct cpu_key key_to_search;
diff --git a/fs/reiserfs/stree.c b/fs/reiserfs/stree.c
index 5240abe..47e7027 100644
--- a/fs/reiserfs/stree.c
+++ b/fs/reiserfs/stree.c
@@ -244,7 +244,7 @@ static const struct reiserfs_key MAX_KEY
of the path, and going upwards. We must check the path's validity at each step. If the key is not in
the path, there is no delimiting key in the tree (buffer is first or last buffer in tree), and in this
case we return a special key, either MIN_KEY or MAX_KEY. */
-static inline const struct reiserfs_key *get_lkey(const struct path
+static inline const struct reiserfs_key *get_lkey(const struct treepath
*p_s_chk_path,
const struct super_block
*p_s_sb)
@@ -290,7 +290,7 @@ static inline const struct reiserfs_key
}

/* Get delimiting key of the buffer at the path and its right neighbor. */
-inline const struct reiserfs_key *get_rkey(const struct path *p_s_chk_path,
+inline const struct reiserfs_key *get_rkey(const struct treepath *p_s_chk_path,
const struct super_block *p_s_sb)
{
int n_position, n_path_offset = p_s_chk_path->path_length;
@@ -337,7 +337,7 @@ inline const struct reiserfs_key *get_rk
the path. These delimiting keys are stored at least one level above that buffer in the tree. If the
buffer is the first or last node in the tree order then one of the delimiting keys may be absent, and in
this case get_lkey and get_rkey return a special key which is MIN_KEY or MAX_KEY. */
-static inline int key_in_buffer(struct path *p_s_chk_path, /* Path which should be checked. */
+static inline int key_in_buffer(struct treepath *p_s_chk_path, /* Path which should be checked. */
const struct cpu_key *p_s_key, /* Key which should be checked. */
struct super_block *p_s_sb /* Super block pointer. */
)
@@ -374,7 +374,7 @@ inline void decrement_bcount(struct buff
}

/* Decrement b_count field of the all buffers in the path. */
-void decrement_counters_in_path(struct path *p_s_search_path)
+void decrement_counters_in_path(struct treepath *p_s_search_path)
{
int n_path_offset = p_s_search_path->path_length;

@@ -391,7 +391,7 @@ void decrement_counters_in_path(struct p
p_s_search_path->path_length = ILLEGAL_PATH_ELEMENT_OFFSET;
}

-int reiserfs_check_path(struct path *p)
+int reiserfs_check_path(struct treepath *p)
{
RFALSE(p->path_length != ILLEGAL_PATH_ELEMENT_OFFSET,
"path not properly relsed");
@@ -403,7 +403,7 @@ int reiserfs_check_path(struct path *p)
**
** only called from fix_nodes()
*/
-void pathrelse_and_restore(struct super_block *s, struct path *p_s_search_path)
+void pathrelse_and_restore(struct super_block *s, struct treepath *p_s_search_path)
{
int n_path_offset = p_s_search_path->path_length;

@@ -421,7 +421,7 @@ void pathrelse_and_restore(struct super_
}

/* Release all buffers in the path. */
-void pathrelse(struct path *p_s_search_path)
+void pathrelse(struct treepath *p_s_search_path)
{
int n_path_offset = p_s_search_path->path_length;

@@ -602,7 +602,7 @@ static void search_by_key_reada(struct s
correctness of the bottom of the path */
/* The function is NOT SCHEDULE-SAFE! */
int search_by_key(struct super_block *p_s_sb, const struct cpu_key *p_s_key, /* Key to search. */
- struct path *p_s_search_path, /* This structure was
+ struct treepath *p_s_search_path,/* This structure was
allocated and initialized
by the calling
function. It is filled up
@@ -813,7 +813,7 @@ int search_by_key(struct super_block *p_
/* The function is NOT SCHEDULE-SAFE! */
int search_for_position_by_key(struct super_block *p_s_sb, /* Pointer to the super block. */
const struct cpu_key *p_cpu_key, /* Key to search (cpu variable) */
- struct path *p_s_search_path /* Filled up by this function. */
+ struct treepath *p_s_search_path /* Filled up by this function. */
)
{
struct item_head *p_le_ih; /* pointer to on-disk structure */
@@ -884,7 +884,7 @@ int search_for_position_by_key(struct su
}

/* Compare given item and item pointed to by the path. */
-int comp_items(const struct item_head *stored_ih, const struct path *p_s_path)
+int comp_items(const struct item_head *stored_ih, const struct treepath *p_s_path)
{
struct buffer_head *p_s_bh;
struct item_head *ih;
@@ -911,7 +911,7 @@ int comp_items(const struct item_head *s
#define block_in_use(bh) (buffer_locked(bh) || (held_by_others(bh)))

// prepare for delete or cut of direct item
-static inline int prepare_for_direct_item(struct path *path,
+static inline int prepare_for_direct_item(struct treepath *path,
struct item_head *le_ih,
struct inode *inode,
loff_t new_file_length, int *cut_size)
@@ -952,7 +952,7 @@ static inline int prepare_for_direct_ite
return M_CUT; /* Cut from this item. */
}

-static inline int prepare_for_direntry_item(struct path *path,
+static inline int prepare_for_direntry_item(struct treepath *path,
struct item_head *le_ih,
struct inode *inode,
loff_t new_file_length,
@@ -987,7 +987,7 @@ static inline int prepare_for_direntry_i
In case of file truncate calculate whether this item must be deleted/truncated or last
unformatted node of this item will be converted to a direct item.
This function returns a determination of what balance mode the calling function should employ. */
-static char prepare_for_delete_or_cut(struct reiserfs_transaction_handle *th, struct inode *inode, struct path *p_s_path, const struct cpu_key *p_s_item_key, int *p_n_removed, /* Number of unformatted nodes which were removed
+static char prepare_for_delete_or_cut(struct reiserfs_transaction_handle *th, struct inode *inode, struct treepath *p_s_path, const struct cpu_key *p_s_item_key, int *p_n_removed, /* Number of unformatted nodes which were removed
from end of the file. */
int *p_n_cut_size, unsigned long long n_new_file_length /* MAX_KEY_OFFSET in case of delete. */
)
@@ -1125,7 +1125,7 @@ static int calc_deleted_bytes_number(str
static void init_tb_struct(struct reiserfs_transaction_handle *th,
struct tree_balance *p_s_tb,
struct super_block *p_s_sb,
- struct path *p_s_path, int n_size)
+ struct treepath *p_s_path, int n_size)
{

BUG_ON(!th->t_trans_id);
@@ -1176,7 +1176,7 @@ char head2type(struct item_head *ih)
#endif

/* Delete object item. */
-int reiserfs_delete_item(struct reiserfs_transaction_handle *th, struct path *p_s_path, /* Path to the deleted item. */
+int reiserfs_delete_item(struct reiserfs_transaction_handle *th, struct treepath *p_s_path, /* Path to the deleted item. */
const struct cpu_key *p_s_item_key, /* Key to search for the deleted item. */
struct inode *p_s_inode, /* inode is here just to update i_blocks and quotas */
struct buffer_head *p_s_un_bh)
@@ -1468,7 +1468,7 @@ static void unmap_buffers(struct page *p
static int maybe_indirect_to_direct(struct reiserfs_transaction_handle *th,
struct inode *p_s_inode,
struct page *page,
- struct path *p_s_path,
+ struct treepath *p_s_path,
const struct cpu_key *p_s_item_key,
loff_t n_new_file_size, char *p_c_mode)
{
@@ -1503,7 +1503,7 @@ static int maybe_indirect_to_direct(stru
pointer being converted. Therefore we have to delete inserted
direct item(s) */
static void indirect_to_direct_roll_back(struct reiserfs_transaction_handle *th,
- struct inode *inode, struct path *path)
+ struct inode *inode, struct treepath *path)
{
struct cpu_key tail_key;
int tail_len;
@@ -1545,7 +1545,7 @@ static void indirect_to_direct_roll_back

/* (Truncate or cut entry) or delete object item. Returns < 0 on failure */
int reiserfs_cut_from_item(struct reiserfs_transaction_handle *th,
- struct path *p_s_path,
+ struct treepath *p_s_path,
struct cpu_key *p_s_item_key,
struct inode *p_s_inode,
struct page *page, loff_t n_new_file_size)
@@ -1920,7 +1920,7 @@ int reiserfs_do_truncate(struct reiserfs

#ifdef CONFIG_REISERFS_CHECK
// this makes sure, that we __append__, not overwrite or add holes
-static void check_research_for_paste(struct path *path,
+static void check_research_for_paste(struct treepath *path,
const struct cpu_key *p_s_key)
{
struct item_head *found_ih = get_ih(path);
@@ -1954,7 +1954,7 @@ static void check_research_for_paste(str
#endif /* config reiserfs check */

/* Paste bytes to the existing item. Returns bytes number pasted into the item. */
-int reiserfs_paste_into_item(struct reiserfs_transaction_handle *th, struct path *p_s_search_path, /* Path to the pasted item. */
+int reiserfs_paste_into_item(struct reiserfs_transaction_handle *th, struct treepath *p_s_search_path, /* Path to the pasted item. */
const struct cpu_key *p_s_key, /* Key to search for the needed item. */
struct inode *inode, /* Inode item belongs to */
const char *p_c_body, /* Pointer to the bytes to paste. */
@@ -2036,7 +2036,7 @@ int reiserfs_paste_into_item(struct reis
}

/* Insert new item into the buffer at the path. */
-int reiserfs_insert_item(struct reiserfs_transaction_handle *th, struct path *p_s_path, /* Path to the inserteded item. */
+int reiserfs_insert_item(struct reiserfs_transaction_handle *th, struct treepath *p_s_path, /* Path to the inserteded item. */
const struct cpu_key *key, struct item_head *p_s_ih, /* Pointer to the item header to insert. */
struct inode *inode, const char *p_c_body)
{ /* Pointer to the bytes to insert. */
diff --git a/fs/reiserfs/tail_conversion.c b/fs/reiserfs/tail_conversion.c
index 36f108f..f8121a1 100644
--- a/fs/reiserfs/tail_conversion.c
+++ b/fs/reiserfs/tail_conversion.c
@@ -15,7 +15,7 @@
/* path points to first direct item of the file regarless of how many of
them are there */
int direct2indirect(struct reiserfs_transaction_handle *th, struct inode *inode,
- struct path *path, struct buffer_head *unbh,
+ struct treepath *path, struct buffer_head *unbh,
loff_t tail_offset)
{
struct super_block *sb = inode->i_sb;
@@ -171,7 +171,7 @@ void reiserfs_unmap_buffer(struct buffer
what we expect from it (number of cut bytes). But when tail remains
in the unformatted node, we set mode to SKIP_BALANCING and unlock
inode */
-int indirect2direct(struct reiserfs_transaction_handle *th, struct inode *p_s_inode, struct page *page, struct path *p_s_path, /* path to the indirect item. */
+int indirect2direct(struct reiserfs_transaction_handle *th, struct inode *p_s_inode, struct page *page, struct treepath *p_s_path, /* path to the indirect item. */
const struct cpu_key *p_s_item_key, /* Key to look for unformatted node pointer to be cut. */
loff_t n_new_file_size, /* New file size. */
char *p_c_mode)
diff --git a/include/linux/reiserfs_fs.h b/include/linux/reiserfs_fs.h
index 7bc6bfb..849f530 100644
--- a/include/linux/reiserfs_fs.h
+++ b/include/linux/reiserfs_fs.h
@@ -1159,7 +1159,7 @@ znodes are the way! */
#define PATH_READA 0x1 /* do read ahead */
#define PATH_READA_BACK 0x2 /* read backwards */

-struct path {
+struct treepath {
int path_length; /* Length of the array above. */
int reada;
struct path_element path_elements[EXTENDED_MAX_HEIGHT]; /* Array of the path elements. */
@@ -1169,7 +1169,7 @@ struct path {
#define pos_in_item(path) ((path)->pos_in_item)

#define INITIALIZE_PATH(var) \
-struct path var = {.path_length = ILLEGAL_PATH_ELEMENT_OFFSET, .reada = 0,}
+struct treepath var = {.path_length = ILLEGAL_PATH_ELEMENT_OFFSET, .reada = 0,}

/* Get path element by path and path position. */
#define PATH_OFFSET_PELEMENT(p_s_path,n_offset) ((p_s_path)->path_elements +(n_offset))
@@ -1327,7 +1327,7 @@ struct tree_balance {
int need_balance_dirty;
struct super_block *tb_sb;
struct reiserfs_transaction_handle *transaction_handle;
- struct path *tb_path;
+ struct treepath *tb_path;
struct buffer_head *L[MAX_HEIGHT]; /* array of left neighbors of nodes in the path */
struct buffer_head *R[MAX_HEIGHT]; /* array of right neighbors of nodes in the path */
struct buffer_head *FL[MAX_HEIGHT]; /* array of fathers of the left neighbors */
@@ -1793,41 +1793,41 @@ static inline void copy_key(struct reise
memcpy(to, from, KEY_SIZE);
}

-int comp_items(const struct item_head *stored_ih, const struct path *p_s_path);
-const struct reiserfs_key *get_rkey(const struct path *p_s_chk_path,
+int comp_items(const struct item_head *stored_ih, const struct treepath *p_s_path);
+const struct reiserfs_key *get_rkey(const struct treepath *p_s_chk_path,
const struct super_block *p_s_sb);
int search_by_key(struct super_block *, const struct cpu_key *,
- struct path *, int);
+ struct treepath *, int);
#define search_item(s,key,path) search_by_key (s, key, path, DISK_LEAF_NODE_LEVEL)
int search_for_position_by_key(struct super_block *p_s_sb,
const struct cpu_key *p_s_cpu_key,
- struct path *p_s_search_path);
+ struct treepath *p_s_search_path);
extern void decrement_bcount(struct buffer_head *p_s_bh);
-void decrement_counters_in_path(struct path *p_s_search_path);
-void pathrelse(struct path *p_s_search_path);
-int reiserfs_check_path(struct path *p);
-void pathrelse_and_restore(struct super_block *s, struct path *p_s_search_path);
+void decrement_counters_in_path(struct treepath *p_s_search_path);
+void pathrelse(struct treepath *p_s_search_path);
+int reiserfs_check_path(struct treepath *p);
+void pathrelse_and_restore(struct super_block *s, struct treepath *p_s_search_path);

int reiserfs_insert_item(struct reiserfs_transaction_handle *th,
- struct path *path,
+ struct treepath *path,
const struct cpu_key *key,
struct item_head *ih,
struct inode *inode, const char *body);

int reiserfs_paste_into_item(struct reiserfs_transaction_handle *th,
- struct path *path,
+ struct treepath *path,
const struct cpu_key *key,
struct inode *inode,
const char *body, int paste_size);

int reiserfs_cut_from_item(struct reiserfs_transaction_handle *th,
- struct path *path,
+ struct treepath *path,
struct cpu_key *key,
struct inode *inode,
struct page *page, loff_t new_file_size);

int reiserfs_delete_item(struct reiserfs_transaction_handle *th,
- struct path *path,
+ struct treepath *path,
const struct cpu_key *key,
struct inode *inode, struct buffer_head *p_s_un_bh);

@@ -1858,7 +1858,7 @@ void padd_item(char *item, int total_len
#define GET_BLOCK_NO_DANGLE 16 /* don't leave any transactions running */

int restart_transaction(struct reiserfs_transaction_handle *th,
- struct inode *inode, struct path *path);
+ struct inode *inode, struct treepath *path);
void reiserfs_read_locked_inode(struct inode *inode,
struct reiserfs_iget_args *args);
int reiserfs_find_actor(struct inode *inode, void *p);
@@ -1905,7 +1905,7 @@ int reiserfs_setattr(struct dentry *dent
/* namei.c */
void set_de_name_and_namelen(struct reiserfs_dir_entry *de);
int search_by_entry_key(struct super_block *sb, const struct cpu_key *key,
- struct path *path, struct reiserfs_dir_entry *de);
+ struct treepath *path, struct reiserfs_dir_entry *de);
struct dentry *reiserfs_get_parent(struct dentry *);
/* procfs.c */

@@ -1956,9 +1956,9 @@ extern const struct file_operations reis

/* tail_conversion.c */
int direct2indirect(struct reiserfs_transaction_handle *, struct inode *,
- struct path *, struct buffer_head *, loff_t);
+ struct treepath *, struct buffer_head *, loff_t);
int indirect2direct(struct reiserfs_transaction_handle *, struct inode *,
- struct page *, struct path *, const struct cpu_key *,
+ struct page *, struct treepath *, const struct cpu_key *,
loff_t, char *);
void reiserfs_unmap_buffer(struct buffer_head *);

@@ -2045,7 +2045,7 @@ struct __reiserfs_blocknr_hint {
struct inode *inode; /* inode passed to allocator, if we allocate unf. nodes */
long block; /* file offset, in blocks */
struct in_core_key key;
- struct path *path; /* search path, used by allocator to deternine search_start by
+ struct treepath *path; /* search path, used by allocator to deternine search_start by
* various ways */
struct reiserfs_transaction_handle *th; /* transaction handle is needed to log super blocks and
* bitmap blocks changes */
@@ -2101,7 +2101,7 @@ static inline int reiserfs_new_form_bloc
static inline int reiserfs_new_unf_blocknrs(struct reiserfs_transaction_handle
*th, struct inode *inode,
b_blocknr_t * new_blocknrs,
- struct path *path, long block)
+ struct treepath *path, long block)
{
reiserfs_blocknr_hint_t hint = {
.th = th,
@@ -2118,7 +2118,7 @@ static inline int reiserfs_new_unf_block
static inline int reiserfs_new_unf_blocknrs2(struct reiserfs_transaction_handle
*th, struct inode *inode,
b_blocknr_t * new_blocknrs,
- struct path *path, long block)
+ struct treepath *path, long block)
{
reiserfs_blocknr_hint_t hint = {
.th = th,
--
1.4.3.3

2006-12-04 12:43:34

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 34/35] Unionfs: Kconfig and Makefile

From: Josef "Jeff" Sipek <[email protected]>

This patch contains the changes to fs Kconfig file, Makefiles, and Maintainers
file for Unionfs.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
Signed-off-by: David Quigley <[email protected]>
Signed-off-by: Erez Zadok <[email protected]>
---
MAINTAINERS | 7 +++++++
fs/Kconfig | 10 ++++++++++
fs/Makefile | 1 +
fs/unionfs/Makefile | 5 +++++
4 files changed, 23 insertions(+), 0 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 8385a69..7d9ebb0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3051,6 +3051,13 @@ L: [email protected]
W: http://www.kernel.dk
S: Maintained

+UNIONFS
+P: Josef "Jeff" Sipek
+M: [email protected]
+L: [email protected]
+W: http://www.unionfs.org
+S: Maintained
+
USB ACM DRIVER
P: Oliver Neukum
M: [email protected]
diff --git a/fs/Kconfig b/fs/Kconfig
index b3b5aa0..4b31ea4 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -1557,6 +1557,16 @@ config UFS_DEBUG
Y here. This will result in _many_ additional debugging messages to be
written to the system log.

+config UNION_FS
+ tristate "Union file system (EXPERIMENTAL)"
+ depends on EXPERIMENTAL
+ help
+ Unionfs is a stackable unification file system, which appears to
+ merge the contents of several directories (branches), while keeping
+ their physical content separate.
+
+ See <http://www.unionfs.org> for details
+
endmenu

menu "Network File Systems"
diff --git a/fs/Makefile b/fs/Makefile
index b9ffa63..76c6acc 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -115,3 +115,4 @@ obj-$(CONFIG_HPPFS) += hppfs/
obj-$(CONFIG_DEBUG_FS) += debugfs/
obj-$(CONFIG_OCFS2_FS) += ocfs2/
obj-$(CONFIG_GFS2_FS) += gfs2/
+obj-$(CONFIG_UNION_FS) += unionfs/
diff --git a/fs/unionfs/Makefile b/fs/unionfs/Makefile
new file mode 100644
index 0000000..25dd78f
--- /dev/null
+++ b/fs/unionfs/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_UNION_FS) += unionfs.o
+
+unionfs-y := subr.o dentry.o file.o inode.o main.o super.o \
+ stale_inode.o branchman.o rdstate.o copyup.o dirhelper.o \
+ rename.o unlink.o lookup.o commonfops.o dirfops.o sioq.o
--
1.4.3.3

2006-12-04 12:43:35

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 10/35] fsstack: Make fsstack_copy_attr_all copy inode size

From: Josef "Jeff" Sipek <[email protected]>

fsstack_copy_attr_all should copy the inode size in addition to all the
other attributes.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
---
fs/stack.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/fs/stack.c b/fs/stack.c
index 8ffb880..5ddbc34 100644
--- a/fs/stack.c
+++ b/fs/stack.c
@@ -34,5 +34,7 @@ void fsstack_copy_attr_all(struct inode
dest->i_ctime = src->i_ctime;
dest->i_blkbits = src->i_blkbits;
dest->i_flags = src->i_flags;
+
+ fsstack_copy_inode_size(dest, src);
}
EXPORT_SYMBOL_GPL(fsstack_copy_attr_all);
--
1.4.3.3

2006-12-04 12:44:40

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 22/35] Unionfs: Lookup helper functions

From: Josef "Jeff" Sipek <[email protected]>

This patch provides helper functions for the lookup operations in Unionfs.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
Signed-off-by: David Quigley <[email protected]>
Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/lookup.c | 506 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 506 insertions(+), 0 deletions(-)

diff --git a/fs/unionfs/lookup.c b/fs/unionfs/lookup.c
new file mode 100644
index 0000000..7376e69
--- /dev/null
+++ b/fs/unionfs/lookup.c
@@ -0,0 +1,506 @@
+/*
+ * Copyright (c) 2003-2006 Erez Zadok
+ * Copyright (c) 2003-2006 Charles P. Wright
+ * Copyright (c) 2005-2006 Josef 'Jeff' Sipek
+ * Copyright (c) 2005-2006 Junjiro Okajima
+ * Copyright (c) 2005 Arun M. Krishnakumar
+ * Copyright (c) 2004-2006 David P. Quigley
+ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair
+ * Copyright (c) 2003 Puja Gupta
+ * Copyright (c) 2003 Harikesavan Krishnan
+ * Copyright (c) 2003-2006 Stony Brook University
+ * Copyright (c) 2003-2006 The Research Foundation of State University of New York
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "union.h"
+
+static int is_opaque_dir(struct dentry *dentry, int bindex);
+static int is_validname(const char *name);
+
+struct dentry *unionfs_lookup_backend(struct dentry *dentry, struct nameidata *nd,
+ int lookupmode)
+{
+ int err = 0;
+ struct dentry *hidden_dentry = NULL;
+ struct dentry *wh_hidden_dentry = NULL;
+ struct dentry *hidden_dir_dentry = NULL;
+ struct dentry *parent_dentry = NULL;
+ int bindex, bstart, bend, bopaque;
+ int dentry_count = 0; /* Number of positive dentries. */
+ int first_dentry_offset = -1;
+ struct dentry *first_hidden_dentry = NULL;
+ struct vfsmount *first_hidden_mnt = NULL;
+ int locked_parent = 0;
+ int locked_child = 0;
+
+ int opaque;
+ char *whname = NULL;
+ const char *name;
+ int namelen;
+
+ /* We should already have a lock on this dentry in the case of a
+ * partial lookup, or a revalidation. Otherwise it is returned from
+ * new_dentry_private_data already locked. */
+ if (lookupmode == INTERPOSE_PARTIAL || lookupmode == INTERPOSE_REVAL ||
+ lookupmode == INTERPOSE_REVAL_NEG)
+ verify_locked(dentry);
+ else {
+ BUG_ON(UNIONFS_D(dentry) != NULL);
+ locked_child = 1;
+ }
+ if (lookupmode != INTERPOSE_PARTIAL)
+ if ((err = new_dentry_private_data(dentry)))
+ goto out;
+ /* must initialize dentry operations */
+ dentry->d_op = &unionfs_dops;
+
+ parent_dentry = dget_parent(dentry);
+ /* We never partial lookup the root directory. */
+ if (parent_dentry != dentry) {
+ lock_dentry(parent_dentry);
+ locked_parent = 1;
+ } else {
+ dput(parent_dentry);
+ parent_dentry = NULL;
+ goto out;
+ }
+
+ name = dentry->d_name.name;
+ namelen = dentry->d_name.len;
+
+ /* No dentries should get created for possible whiteout names. */
+ if (!is_validname(name)) {
+ err = -EPERM;
+ goto out_free;
+ }
+
+ /* Now start the actual lookup procedure. */
+ bstart = dbstart(parent_dentry);
+ bend = dbend(parent_dentry);
+ bopaque = dbopaque(parent_dentry);
+ BUG_ON(bstart < 0);
+
+ /* It would be ideal if we could convert partial lookups to only have
+ * to do this work when they really need to. It could probably improve
+ * performance quite a bit, and maybe simplify the rest of the code. */
+ if (lookupmode == INTERPOSE_PARTIAL) {
+ bstart++;
+ if ((bopaque != -1) && (bopaque < bend))
+ bend = bopaque;
+ }
+
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ if (lookupmode == INTERPOSE_PARTIAL && hidden_dentry)
+ continue;
+ BUG_ON(hidden_dentry != NULL);
+
+ hidden_dir_dentry = unionfs_lower_dentry_idx(parent_dentry, bindex);
+
+ /* if the parent hidden dentry does not exist skip this */
+ if (!(hidden_dir_dentry && hidden_dir_dentry->d_inode))
+ continue;
+
+ /* also skip it if the parent isn't a directory. */
+ if (!S_ISDIR(hidden_dir_dentry->d_inode->i_mode))
+ continue;
+
+ /* Reuse the whiteout name because its value doesn't change. */
+ if (!whname) {
+ whname = alloc_whname(name, namelen);
+ if (IS_ERR(whname)) {
+ err = PTR_ERR(whname);
+ goto out_free;
+ }
+ }
+
+ /* check if whiteout exists in this branch: lookup .wh.foo */
+ wh_hidden_dentry = lookup_one_len(whname, hidden_dir_dentry,
+ namelen + UNIONFS_WHLEN);
+ if (IS_ERR(wh_hidden_dentry)) {
+ dput(first_hidden_dentry);
+ mntput(first_hidden_mnt);
+ err = PTR_ERR(wh_hidden_dentry);
+ goto out_free;
+ }
+
+ if (wh_hidden_dentry->d_inode) {
+ /* We found a whiteout so lets give up. */
+ if (S_ISREG(wh_hidden_dentry->d_inode->i_mode)) {
+ set_dbend(dentry, bindex);
+ set_dbopaque(dentry, bindex);
+ dput(wh_hidden_dentry);
+ break;
+ }
+ err = -EIO;
+ printk(KERN_NOTICE "EIO: Invalid whiteout entry type"
+ " %d.\n", wh_hidden_dentry->d_inode->i_mode);
+ dput(wh_hidden_dentry);
+ dput(first_hidden_dentry);
+ mntput(first_hidden_mnt);
+ goto out_free;
+ }
+
+ dput(wh_hidden_dentry);
+ wh_hidden_dentry = NULL;
+
+ /* Now do regular lookup; lookup foo */
+ nd->dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ /* FIXME: fix following line for mount point crossing */
+ nd->mnt = unionfs_lower_mnt_idx(parent_dentry, bindex);
+
+ hidden_dentry = lookup_one_len_nd(name, hidden_dir_dentry,
+ namelen, nd);
+ if (IS_ERR(hidden_dentry)) {
+ dput(first_hidden_dentry);
+ mntput(first_hidden_mnt);
+ err = PTR_ERR(hidden_dentry);
+ goto out_free;
+ }
+
+ /* Store the first negative dentry specially, because if they
+ * are all negative we need this for future creates. */
+ if (!hidden_dentry->d_inode) {
+ if (!first_hidden_dentry && (dbstart(dentry) == -1)) {
+ first_hidden_dentry = hidden_dentry;
+ /* FIXME: following line needs to be changed
+ * to allow mountpoint crossing */
+ first_hidden_mnt = mntget(unionfs_lower_mnt_idx(parent_dentry, bindex));
+ first_dentry_offset = bindex;
+ } else
+ dput(hidden_dentry);
+
+ continue;
+ }
+
+ /* number of positive dentries */
+ dentry_count++;
+
+ /* store underlying dentry */
+ if (dbstart(dentry) == -1)
+ set_dbstart(dentry, bindex);
+ unionfs_set_lower_dentry_idx(dentry, bindex, hidden_dentry);
+ /* FIXME: the following line needs to get fixed to allow
+ * mountpoint crossing */
+ unionfs_set_lower_mnt_idx(dentry, bindex,
+ mntget(unionfs_lower_mnt_idx(parent_dentry, bindex)));
+ set_dbend(dentry, bindex);
+
+ /* update parent directory's atime with the bindex */
+ fsstack_copy_attr_atime(parent_dentry->d_inode,
+ hidden_dir_dentry->d_inode);
+
+ /* We terminate file lookups here. */
+ if (!S_ISDIR(hidden_dentry->d_inode->i_mode)) {
+ if (lookupmode == INTERPOSE_PARTIAL)
+ continue;
+ if (dentry_count == 1)
+ goto out_positive;
+ /* This can only happen with mixed D-*-F-* */
+ BUG_ON(!S_ISDIR(unionfs_lower_dentry(dentry)->d_inode->i_mode));
+ continue;
+ }
+
+ opaque = is_opaque_dir(dentry, bindex);
+ if (opaque < 0) {
+ dput(first_hidden_dentry);
+ mntput(first_hidden_mnt);
+ err = opaque;
+ goto out_free;
+ } else if (opaque) {
+ set_dbend(dentry, bindex);
+ set_dbopaque(dentry, bindex);
+ break;
+ }
+ }
+
+ if (dentry_count)
+ goto out_positive;
+ else
+ goto out_negative;
+
+out_negative:
+ if (lookupmode == INTERPOSE_PARTIAL)
+ goto out;
+
+ /* If we've only got negative dentries, then use the leftmost one. */
+ if (lookupmode == INTERPOSE_REVAL) {
+ if (dentry->d_inode)
+ UNIONFS_I(dentry->d_inode)->stale = 1;
+
+ goto out;
+ }
+ /* This should only happen if we found a whiteout. */
+ if (first_dentry_offset == -1) {
+ nd->dentry = dentry;
+ /* FIXME: fix following line for mount point crossing */
+ nd->mnt = unionfs_lower_mnt_idx(parent_dentry, bindex);
+
+ first_hidden_dentry = lookup_one_len_nd(name, hidden_dir_dentry,
+ namelen, nd);
+ first_dentry_offset = bindex;
+ if (IS_ERR(first_hidden_dentry)) {
+ err = PTR_ERR(first_hidden_dentry);
+ goto out;
+ }
+
+ /* FIXME: the following line needs to be changed to allow
+ * mountpoint crossing */
+ first_hidden_mnt = mntget(unionfs_lower_mnt_idx(dentry, bindex));
+ }
+ unionfs_set_lower_dentry_idx(dentry, first_dentry_offset, first_hidden_dentry);
+ unionfs_set_lower_mnt_idx(dentry, first_dentry_offset, first_hidden_mnt);
+ set_dbstart(dentry, first_dentry_offset);
+ set_dbend(dentry, first_dentry_offset);
+
+ if (lookupmode == INTERPOSE_REVAL_NEG)
+ BUG_ON(dentry->d_inode != NULL);
+ else
+ d_add(dentry, NULL);
+ goto out;
+
+/* This part of the code is for positive dentries. */
+out_positive:
+ BUG_ON(dentry_count <= 0);
+
+ /* If we're holding onto the first negative dentry & corresponding
+ * vfsmount - throw it out. */
+ dput(first_hidden_dentry);
+ mntput(first_hidden_mnt);
+
+ /* Partial lookups need to reinterpose, or throw away older negs. */
+ if (lookupmode == INTERPOSE_PARTIAL) {
+ if (dentry->d_inode) {
+ unionfs_reinterpose(dentry);
+ goto out;
+ }
+
+ /* This somehow turned positive, so it is as if we had a
+ * negative revalidation. */
+ lookupmode = INTERPOSE_REVAL_NEG;
+
+ update_bstart(dentry);
+ bstart = dbstart(dentry);
+ bend = dbend(dentry);
+ }
+
+ err = unionfs_interpose(dentry, dentry->d_sb, lookupmode);
+ if (err)
+ goto out_drop;
+
+ goto out;
+
+out_drop:
+ d_drop(dentry);
+
+out_free:
+ /* should dput all the underlying dentries on error condition */
+ bstart = dbstart(dentry);
+ if (bstart >= 0) {
+ bend = dbend(dentry);
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ dput(unionfs_lower_dentry_idx(dentry, bindex));
+ mntput(unionfs_lower_mnt_idx(dentry, bindex));
+ }
+ }
+ kfree(UNIONFS_D(dentry)->lower_paths);
+ UNIONFS_D(dentry)->lower_paths = NULL;
+ set_dbstart(dentry, -1);
+ set_dbend(dentry, -1);
+
+out:
+ if (!err && UNIONFS_D(dentry)) {
+ BUG_ON(dbend(dentry) > UNIONFS_D(dentry)->bcount);
+ BUG_ON(dbend(dentry) > sbmax(dentry->d_sb));
+ BUG_ON(dbstart(dentry) < 0);
+ }
+ kfree(whname);
+ if (locked_parent)
+ unlock_dentry(parent_dentry);
+ dput(parent_dentry);
+ if (locked_child)
+ unlock_dentry(dentry);
+ return ERR_PTR(err);
+}
+
+/* This is a utility function that fills in a unionfs dentry.*/
+int unionfs_partial_lookup(struct dentry *dentry)
+{
+ struct dentry *tmp;
+ struct nameidata nd = { .flags = 0 };
+
+ tmp = unionfs_lookup_backend(dentry, &nd, INTERPOSE_PARTIAL);
+ if (!tmp)
+ return 0;
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ /* need to change the interface */
+ BUG_ON(tmp != dentry);
+ return -ENOSYS;
+}
+
+/* The rest of these are utility functions for lookup. */
+static int is_opaque_dir(struct dentry *dentry, int bindex)
+{
+ int err = 0;
+ struct dentry *hidden_dentry;
+ struct dentry *wh_hidden_dentry;
+ struct inode *hidden_inode;
+ struct sioq_args args;
+
+ hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ hidden_inode = hidden_dentry->d_inode;
+
+ BUG_ON(!S_ISDIR(hidden_inode->i_mode));
+
+ mutex_lock(&hidden_inode->i_mutex);
+
+ if (!permission(hidden_inode, MAY_EXEC, NULL))
+ wh_hidden_dentry = lookup_one_len(UNIONFS_DIR_OPAQUE, hidden_dentry,
+ sizeof(UNIONFS_DIR_OPAQUE) - 1);
+ else {
+ args.isopaque.dentry = hidden_dentry;
+ run_sioq(__is_opaque_dir, &args);
+ wh_hidden_dentry = args.ret;
+ }
+
+ mutex_unlock(&hidden_inode->i_mutex);
+
+ if (IS_ERR(wh_hidden_dentry)) {
+ err = PTR_ERR(wh_hidden_dentry);
+ goto out;
+ }
+
+ /* This is an opaque dir iff wh_hidden_dentry is positive */
+ err = !!wh_hidden_dentry->d_inode;
+
+ dput(wh_hidden_dentry);
+out:
+ return err;
+}
+
+/* is the filename valid == !(whiteout for a file or opaque dir marker) */
+static int is_validname(const char *name)
+{
+ if (!strncmp(name, UNIONFS_WHPFX, UNIONFS_WHLEN))
+ return 0;
+ if (!strncmp(name, UNIONFS_DIR_OPAQUE_NAME,
+ sizeof(UNIONFS_DIR_OPAQUE_NAME) - 1))
+ return 0;
+ return 1;
+}
+
+/* The dentry cache is just so we have properly sized dentries. */
+static struct kmem_cache *unionfs_dentry_cachep;
+int init_dentry_cache(void)
+{
+ unionfs_dentry_cachep = kmem_cache_create("unionfs_dentry",
+ sizeof(struct unionfs_dentry_info), 0,
+ SLAB_RECLAIM_ACCOUNT, NULL, NULL);
+
+ return (unionfs_dentry_cachep ? 0 : -ENOMEM);
+}
+
+void destroy_dentry_cache(void)
+{
+ if (unionfs_dentry_cachep)
+ kmem_cache_destroy(unionfs_dentry_cachep);
+}
+
+void free_dentry_private_data(struct unionfs_dentry_info *udi)
+{
+ if (!udi)
+ return;
+ kmem_cache_free(unionfs_dentry_cachep, udi);
+}
+
+/* allocate new dentry private data, free old one if necessary */
+int new_dentry_private_data(struct dentry *dentry)
+{
+ int newsize;
+ int oldsize = 0;
+ struct unionfs_dentry_info *info = UNIONFS_D(dentry);
+
+ spin_lock(&dentry->d_lock);
+ if (!info) {
+ dentry->d_fsdata = kmem_cache_alloc(unionfs_dentry_cachep,
+ SLAB_ATOMIC);
+ info = UNIONFS_D(dentry);
+
+ if (!info)
+ goto out;
+ init_MUTEX_LOCKED(&info->sem);
+
+ info->lower_paths = NULL;
+ } else
+ oldsize = sizeof(struct path) * info->bcount;
+
+ info->bstart = -1;
+ info->bend = -1;
+ info->bopaque = -1;
+ info->bcount = sbmax(dentry->d_sb);
+ atomic_set(&info->generation,
+ atomic_read(&UNIONFS_SB(dentry->d_sb)->generation));
+ newsize = sizeof(struct path) * sbmax(dentry->d_sb);
+
+ /* Don't reallocate when we already have enough space. */
+ /* It would be ideal if we could actually use the slab macros to
+ * determine what our object sizes is, but those are not exported.
+ */
+ if (oldsize) {
+ int minsize = malloc_sizes[0].cs_size;
+
+ if (!newsize || ((oldsize < newsize) && (newsize > minsize))) {
+ kfree(info->lower_paths);
+ info->lower_paths = NULL;
+ }
+ }
+
+ if (!info->lower_paths && newsize) {
+ info->lower_paths = kmalloc(newsize, GFP_ATOMIC);
+ if (!info->lower_paths)
+ goto out_free;
+ }
+
+ memset(info->lower_paths, 0, (oldsize > newsize ? oldsize : newsize));
+
+ spin_unlock(&dentry->d_lock);
+ return 0;
+ /* */
+
+out_free:
+ kfree(info->lower_paths);
+
+out:
+ free_dentry_private_data(info);
+ dentry->d_fsdata = NULL;
+ spin_unlock(&dentry->d_lock);
+ return -ENOMEM;
+}
+
+/* scan through the lower dentry objects, and set bstart to reflect the
+ * starting branch */
+void update_bstart(struct dentry *dentry)
+{
+ int bindex;
+ int bstart = dbstart(dentry);
+ int bend = dbend(dentry);
+ struct dentry *hidden_dentry;
+
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ if (!hidden_dentry)
+ continue;
+ if (hidden_dentry->d_inode) {
+ set_dbstart(dentry, bindex);
+ break;
+ }
+ dput(hidden_dentry);
+ unionfs_set_lower_dentry_idx(dentry, bindex, NULL);
+ }
+}
+
--
1.4.3.3

2006-12-04 12:44:05

by Josef 'Jeff' Sipek

[permalink] [raw]
Subject: [PATCH 21/35] Unionfs: Inode operations

From: Josef "Jeff" Sipek <[email protected]>

This patch provides the inode operations for Unionfs.

Signed-off-by: Josef "Jeff" Sipek <[email protected]>
Signed-off-by: David Quigley <[email protected]>
Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/inode.c | 926 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 926 insertions(+), 0 deletions(-)

diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c
new file mode 100644
index 0000000..c7806e9
--- /dev/null
+++ b/fs/unionfs/inode.c
@@ -0,0 +1,926 @@
+/*
+ * Copyright (c) 2003-2006 Erez Zadok
+ * Copyright (c) 2003-2006 Charles P. Wright
+ * Copyright (c) 2005-2006 Josef 'Jeff' Sipek
+ * Copyright (c) 2005-2006 Junjiro Okajima
+ * Copyright (c) 2005 Arun M. Krishnakumar
+ * Copyright (c) 2004-2006 David P. Quigley
+ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair
+ * Copyright (c) 2003 Puja Gupta
+ * Copyright (c) 2003 Harikesavan Krishnan
+ * Copyright (c) 2003-2006 Stony Brook University
+ * Copyright (c) 2003-2006 The Research Foundation of State University of New York
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "union.h"
+
+/* declarations added for "sparse" */
+extern struct dentry *unionfs_lookup(struct inode *, struct dentry *,
+ struct nameidata *);
+extern int unionfs_readlink(struct dentry *dentry, char __user * buf,
+ int bufsiz);
+extern void unionfs_put_link(struct dentry *dentry, struct nameidata *nd,
+ void *cookie);
+
+static int unionfs_create(struct inode *parent, struct dentry *dentry,
+ int mode, struct nameidata *nd)
+{
+ int err = 0;
+ struct dentry *hidden_dentry = NULL;
+ struct dentry *whiteout_dentry = NULL;
+ struct dentry *new_hidden_dentry;
+ struct dentry *hidden_parent_dentry = NULL;
+ int bindex = 0, bstart;
+ char *name = NULL;
+
+ lock_dentry(dentry);
+
+ /* We start out in the leftmost branch. */
+ bstart = dbstart(dentry);
+ hidden_dentry = unionfs_lower_dentry(dentry);
+
+ /* check if whiteout exists in this branch, i.e. lookup .wh.foo first */
+ name = alloc_whname(dentry->d_name.name, dentry->d_name.len);
+ if (IS_ERR(name)) {
+ err = PTR_ERR(name);
+ goto out;
+ }
+
+ whiteout_dentry =
+ lookup_one_len(name, hidden_dentry->d_parent,
+ dentry->d_name.len + UNIONFS_WHLEN);
+ if (IS_ERR(whiteout_dentry)) {
+ err = PTR_ERR(whiteout_dentry);
+ whiteout_dentry = NULL;
+ goto out;
+ }
+
+ if (whiteout_dentry->d_inode) {
+ /* .wh.foo has been found. */
+ /* First truncate it and then rename it to foo (hence having
+ * the same overall effect as a normal create.
+ */
+ struct dentry *hidden_dir_dentry;
+ struct iattr newattrs;
+
+ mutex_lock(&whiteout_dentry->d_inode->i_mutex);
+ newattrs.ia_valid = ATTR_CTIME | ATTR_MODE | ATTR_ATIME
+ | ATTR_MTIME | ATTR_UID | ATTR_GID | ATTR_FORCE
+ | ATTR_KILL_SUID | ATTR_KILL_SGID;
+
+ newattrs.ia_mode = mode & ~current->fs->umask;
+ newattrs.ia_uid = current->fsuid;
+ newattrs.ia_gid = current->fsgid;
+
+ if (whiteout_dentry->d_inode->i_size != 0) {
+ newattrs.ia_valid |= ATTR_SIZE;
+ newattrs.ia_size = 0;
+ }
+
+ err = notify_change(whiteout_dentry, &newattrs);
+
+ mutex_unlock(&whiteout_dentry->d_inode->i_mutex);
+
+ if (err)
+ printk(KERN_WARNING "unionfs: %s:%d: notify_change "
+ "failed: %d, ignoring..\n",
+ __FILE__, __LINE__, err);
+
+ new_hidden_dentry = unionfs_lower_dentry(dentry);
+ dget(new_hidden_dentry);
+
+ hidden_dir_dentry = dget_parent(whiteout_dentry);
+ lock_rename(hidden_dir_dentry, hidden_dir_dentry);
+
+ if (!(err = is_robranch_super(dentry->d_sb, bstart))) {
+ err =
+ vfs_rename(hidden_dir_dentry->d_inode,
+ whiteout_dentry,
+ hidden_dir_dentry->d_inode,
+ new_hidden_dentry);
+ }
+ if (!err) {
+ fsstack_copy_attr_times(parent,
+ new_hidden_dentry->d_parent->d_inode);
+ fsstack_copy_inode_size(parent,
+ new_hidden_dentry->d_parent->d_inode);
+ parent->i_nlink = unionfs_get_nlinks(parent);
+ }
+
+ unlock_rename(hidden_dir_dentry, hidden_dir_dentry);
+ dput(hidden_dir_dentry);
+
+ dput(new_hidden_dentry);
+
+ if (err) {
+ /* exit if the error returned was NOT -EROFS */
+ if (!IS_COPYUP_ERR(err))
+ goto out;
+ /* We were not able to create the file in this branch,
+ * so, we try to create it in one branch to left
+ */
+ bstart--;
+ } else {
+ /* reset the unionfs dentry to point to the .wh.foo entry. */
+
+ /* Discard any old reference. */
+ dput(unionfs_lower_dentry(dentry));
+
+ /* Trade one reference to another. */
+ unionfs_set_lower_dentry_idx(dentry, bstart, whiteout_dentry);
+ whiteout_dentry = NULL;
+
+ err = unionfs_interpose(dentry, parent->i_sb, 0);
+ goto out;
+ }
+ }
+
+ for (bindex = bstart; bindex >= 0; bindex--) {
+ hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ if (!hidden_dentry) {
+ /* if hidden_dentry is NULL, create the entire
+ * dentry directory structure in branch 'bindex'.
+ * hidden_dentry will NOT be null when bindex == bstart
+ * because lookup passed as a negative unionfs dentry
+ * pointing to a lone negative underlying dentry */
+ hidden_dentry = create_parents(parent, dentry, bindex);
+ if (!hidden_dentry || IS_ERR(hidden_dentry)) {
+ if (IS_ERR(hidden_dentry))
+ err = PTR_ERR(hidden_dentry);
+ continue;
+ }
+ }
+
+ hidden_parent_dentry = lock_parent(hidden_dentry);
+ if (IS_ERR(hidden_parent_dentry)) {
+ err = PTR_ERR(hidden_parent_dentry);
+ goto out;
+ }
+ /* We shouldn't create things in a read-only branch. */
+ if (!(err = is_robranch_super(dentry->d_sb, bindex)))
+ err = vfs_create(hidden_parent_dentry->d_inode,
+ hidden_dentry, mode, nd);
+
+ if (err || !hidden_dentry->d_inode) {
+ unlock_dir(hidden_parent_dentry);
+
+ /* break out of for loop if the error wasn't -EROFS */
+ if (!IS_COPYUP_ERR(err))
+ break;
+ } else {
+ err = unionfs_interpose(dentry, parent->i_sb, 0);
+ if (!err) {
+ fsstack_copy_attr_times(parent,
+ hidden_parent_dentry->d_inode);
+ fsstack_copy_inode_size(parent,
+ hidden_parent_dentry->d_inode);
+ /* update number of links on parent directory */
+ parent->i_nlink = unionfs_get_nlinks(parent);
+ }
+ unlock_dir(hidden_parent_dentry);
+ break;
+ }
+ }
+
+out:
+ dput(whiteout_dentry);
+ kfree(name);
+
+ unlock_dentry(dentry);
+ return err;
+}
+
+struct dentry *unionfs_lookup(struct inode *parent, struct dentry *dentry,
+ struct nameidata *nd)
+{
+ struct nameidata lowernd;
+
+ if (nd)
+ memcpy(&lowernd, nd, sizeof(struct nameidata));
+ else
+ memset(&lowernd, 0, sizeof(struct nameidata));
+
+ /* The locking is done by unionfs_lookup_backend. */
+ return unionfs_lookup_backend(dentry, &lowernd, INTERPOSE_LOOKUP);
+}
+
+static int unionfs_link(struct dentry *old_dentry, struct inode *dir,
+ struct dentry *new_dentry)
+{
+ int err = 0;
+ struct dentry *hidden_old_dentry = NULL;
+ struct dentry *hidden_new_dentry = NULL;
+ struct dentry *hidden_dir_dentry = NULL;
+ struct dentry *whiteout_dentry;
+ char *name = NULL;
+
+ double_lock_dentry(new_dentry, old_dentry);
+
+ hidden_new_dentry = unionfs_lower_dentry(new_dentry);
+
+ /* check if whiteout exists in the branch of new dentry, i.e. lookup
+ * .wh.foo first. If present, delete it */
+ name = alloc_whname(new_dentry->d_name.name, new_dentry->d_name.len);
+ if (IS_ERR(name)) {
+ err = PTR_ERR(name);
+ goto out;
+ }
+
+ whiteout_dentry = lookup_one_len(name, hidden_new_dentry->d_parent,
+ new_dentry->d_name.len + UNIONFS_WHLEN);
+ if (IS_ERR(whiteout_dentry)) {
+ err = PTR_ERR(whiteout_dentry);
+ goto out;
+ }
+
+ if (!whiteout_dentry->d_inode) {
+ dput(whiteout_dentry);
+ whiteout_dentry = NULL;
+ } else {
+ /* found a .wh.foo entry, unlink it and then call vfs_link() */
+ hidden_dir_dentry = lock_parent(whiteout_dentry);
+ err = is_robranch_super(new_dentry->d_sb, dbstart(new_dentry));
+ if (!err)
+ err = vfs_unlink(hidden_dir_dentry->d_inode,
+ whiteout_dentry);
+
+ fsstack_copy_attr_times(dir, hidden_dir_dentry->d_inode);
+ dir->i_nlink = unionfs_get_nlinks(dir);
+ unlock_dir(hidden_dir_dentry);
+ hidden_dir_dentry = NULL;
+ dput(whiteout_dentry);
+ if (err)
+ goto out;
+ }
+
+ if (dbstart(old_dentry) != dbstart(new_dentry)) {
+ hidden_new_dentry =
+ create_parents(dir, new_dentry, dbstart(old_dentry));
+ err = PTR_ERR(hidden_new_dentry);
+ if (IS_COPYUP_ERR(err))
+ goto docopyup;
+ if (!hidden_new_dentry || IS_ERR(hidden_new_dentry))
+ goto out;
+ }
+ hidden_new_dentry = unionfs_lower_dentry(new_dentry);
+ hidden_old_dentry = unionfs_lower_dentry(old_dentry);
+
+ BUG_ON(dbstart(old_dentry) != dbstart(new_dentry));
+ hidden_dir_dentry = lock_parent(hidden_new_dentry);
+ if (!(err = is_robranch(old_dentry)))
+ err = vfs_link(hidden_old_dentry, hidden_dir_dentry->d_inode,
+ hidden_new_dentry);
+ unlock_dir(hidden_dir_dentry);
+
+docopyup:
+ if (IS_COPYUP_ERR(err)) {
+ int old_bstart = dbstart(old_dentry);
+ int bindex;
+
+ for (bindex = old_bstart - 1; bindex >= 0; bindex--) {
+ err =
+ copyup_dentry(old_dentry->d_parent->
+ d_inode, old_dentry,
+ old_bstart, bindex, NULL,
+ old_dentry->d_inode->i_size);
+ if (!err) {
+ hidden_new_dentry =
+ create_parents(dir, new_dentry, bindex);
+ hidden_old_dentry = unionfs_lower_dentry(old_dentry);
+ hidden_dir_dentry =
+ lock_parent(hidden_new_dentry);
+ /* do vfs_link */
+ err = vfs_link(hidden_old_dentry,
+ hidden_dir_dentry->d_inode,
+ hidden_new_dentry);
+ unlock_dir(hidden_dir_dentry);
+ goto check_link;
+ }
+ }
+ goto out;
+ }
+
+check_link:
+ if (err || !hidden_new_dentry->d_inode)
+ goto out;
+
+ /* Its a hard link, so use the same inode */
+ new_dentry->d_inode = igrab(old_dentry->d_inode);
+ d_instantiate(new_dentry, new_dentry->d_inode);
+ fsstack_copy_attr_all(dir, hidden_new_dentry->d_parent->d_inode,
+ unionfs_get_nlinks);
+ /* propagate number of hard-links */
+ old_dentry->d_inode->i_nlink = unionfs_get_nlinks(old_dentry->d_inode);
+
+out:
+ if (!new_dentry->d_inode)
+ d_drop(new_dentry);
+
+ kfree(name);
+
+ unlock_dentry(new_dentry);
+ unlock_dentry(old_dentry);
+
+ return err;
+}
+
+static int unionfs_symlink(struct inode *dir, struct dentry *dentry,
+ const char *symname)
+{
+ int err = 0;
+ struct dentry *hidden_dentry = NULL;
+ struct dentry *whiteout_dentry = NULL;
+ struct dentry *hidden_dir_dentry = NULL;
+ umode_t mode;
+ int bindex = 0, bstart;
+ char *name = NULL;
+
+ lock_dentry(dentry);
+
+ /* We start out in the leftmost branch. */
+ bstart = dbstart(dentry);
+
+ hidden_dentry = unionfs_lower_dentry(dentry);
+
+ /* check if whiteout exists in this branch, i.e. lookup .wh.foo
+ * first. If present, delete it */
+ name = alloc_whname(dentry->d_name.name, dentry->d_name.len);
+ if (IS_ERR(name)) {
+ err = PTR_ERR(name);
+ goto out;
+ }
+
+ whiteout_dentry =
+ lookup_one_len(name, hidden_dentry->d_parent,
+ dentry->d_name.len + UNIONFS_WHLEN);
+ if (IS_ERR(whiteout_dentry)) {
+ err = PTR_ERR(whiteout_dentry);
+ goto out;
+ }
+
+ if (!whiteout_dentry->d_inode) {
+ dput(whiteout_dentry);
+ whiteout_dentry = NULL;
+ } else {
+ /* found a .wh.foo entry, unlink it and then call vfs_symlink() */
+ hidden_dir_dentry = lock_parent(whiteout_dentry);
+
+ if (!(err = is_robranch_super(dentry->d_sb, bstart)))
+ err = vfs_unlink(hidden_dir_dentry->d_inode,
+ whiteout_dentry);
+ dput(whiteout_dentry);
+
+ fsstack_copy_attr_times(dir, hidden_dir_dentry->d_inode);
+ /* propagate number of hard-links */
+ dir->i_nlink = unionfs_get_nlinks(dir);
+
+ unlock_dir(hidden_dir_dentry);
+
+ if (err) {
+ /* exit if the error returned was NOT -EROFS */
+ if (!IS_COPYUP_ERR(err))
+ goto out;
+ /* should now try to create symlink in the another branch */
+ bstart--;
+ }
+ }
+
+ /* deleted whiteout if it was present, now do a normal vfs_symlink()
+ * with possible recursive directory creation */
+ for (bindex = bstart; bindex >= 0; bindex--) {
+ hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ if (!hidden_dentry) {
+ /* if hidden_dentry is NULL, create the entire
+ * dentry directory structure in branch 'bindex'.
+ * hidden_dentry will NOT be null when bindex ==
+ * bstart because lookup passed as a negative
+ * unionfs dentry pointing to a lone negative
+ * underlying dentry */
+ hidden_dentry = create_parents(dir, dentry, bindex);
+ if (!hidden_dentry || IS_ERR(hidden_dentry)) {
+ if (IS_ERR(hidden_dentry))
+ err = PTR_ERR(hidden_dentry);
+
+ printk(KERN_DEBUG "hidden dentry NULL (or error)"
+ "for bindex = %d\n", bindex);
+ continue;
+ }
+ }
+
+ hidden_dir_dentry = lock_parent(hidden_dentry);
+
+ if (!(err = is_robranch_super(dentry->d_sb, bindex))) {
+ mode = S_IALLUGO;
+ err =
+ vfs_symlink(hidden_dir_dentry->d_inode,
+ hidden_dentry, symname, mode);
+ }
+ unlock_dir(hidden_dir_dentry);
+
+ if (err || !hidden_dentry->d_inode) {
+ /* break out of for loop if error returned was NOT -EROFS */
+ if (!IS_COPYUP_ERR(err))
+ break;
+ } else {
+ err = unionfs_interpose(dentry, dir->i_sb, 0);
+ if (!err) {
+ fsstack_copy_attr_times(dir,
+ hidden_dir_dentry->d_inode);
+ fsstack_copy_inode_size(dir,
+ hidden_dir_dentry->d_inode);
+ /* update number of links on parent directory */
+ dir->i_nlink = unionfs_get_nlinks(dir);
+ }
+ break;
+ }
+ }
+
+out:
+ if (!dentry->d_inode)
+ d_drop(dentry);
+
+ kfree(name);
+ unlock_dentry(dentry);
+ return err;
+}
+
+static int unionfs_mkdir(struct inode *parent, struct dentry *dentry, int mode)
+{
+ int err = 0;
+ struct dentry *hidden_dentry = NULL, *whiteout_dentry = NULL;
+ struct dentry *hidden_parent_dentry = NULL;
+ int bindex = 0, bstart;
+ char *name = NULL;
+ int whiteout_unlinked = 0;
+ struct sioq_args args;
+
+ lock_dentry(dentry);
+ bstart = dbstart(dentry);
+
+ hidden_dentry = unionfs_lower_dentry(dentry);
+
+ /* check if whiteout exists in this branch, i.e. lookup .wh.foo first */
+ name = alloc_whname(dentry->d_name.name, dentry->d_name.len);
+ if (IS_ERR(name)) {
+ err = PTR_ERR(name);
+ goto out;
+ }
+
+ whiteout_dentry = lookup_one_len(name, hidden_dentry->d_parent,
+ dentry->d_name.len + UNIONFS_WHLEN);
+ if (IS_ERR(whiteout_dentry)) {
+ err = PTR_ERR(whiteout_dentry);
+ goto out;
+ }
+
+ if (!whiteout_dentry->d_inode) {
+ dput(whiteout_dentry);
+ whiteout_dentry = NULL;
+ } else {
+ hidden_parent_dentry = lock_parent(whiteout_dentry);
+
+ /* found a.wh.foo entry, remove it then do vfs_mkdir */
+ if (!(err = is_robranch_super(dentry->d_sb, bstart))) {
+ args.unlink.parent = hidden_parent_dentry->d_inode;
+ args.unlink.dentry = whiteout_dentry;
+ run_sioq(__unionfs_unlink, &args);
+ err = args.err;
+ }
+ dput(whiteout_dentry);
+
+ unlock_dir(hidden_parent_dentry);
+
+ if (err) {
+ /* exit if the error returned was NOT -EROFS */
+ if (!IS_COPYUP_ERR(err))
+ goto out;
+ bstart--;
+ } else
+ whiteout_unlinked = 1;
+ }
+
+ for (bindex = bstart; bindex >= 0; bindex--) {
+ int i;
+ int bend = dbend(dentry);
+
+ if (is_robranch_super(dentry->d_sb, bindex))
+ continue;
+
+ hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ if (!hidden_dentry) {
+ hidden_dentry = create_parents(parent, dentry, bindex);
+ if (!hidden_dentry || IS_ERR(hidden_dentry)) {
+ printk(KERN_DEBUG "hidden dentry NULL for "
+ "bindex = %d\n", bindex);
+ continue;
+ }
+ }
+
+ hidden_parent_dentry = lock_parent(hidden_dentry);
+
+ if (IS_ERR(hidden_parent_dentry)) {
+ err = PTR_ERR(hidden_parent_dentry);
+ goto out;
+ }
+
+ err = vfs_mkdir(hidden_parent_dentry->d_inode, hidden_dentry, mode);
+
+ unlock_dir(hidden_parent_dentry);
+
+ /* did the mkdir suceed? */
+ if (err)
+ break;
+
+ for (i = bindex + 1; i < bend; i++) {
+ if (unionfs_lower_dentry_idx(dentry, i)) {
+ dput(unionfs_lower_dentry_idx(dentry, i));
+ unionfs_set_lower_dentry_idx(dentry, i, NULL);
+ }
+ }
+ set_dbend(dentry, bindex);
+
+ err = unionfs_interpose(dentry, parent->i_sb, 0);
+ if (!err) {
+ fsstack_copy_attr_times(parent,
+ hidden_parent_dentry->d_inode);
+ fsstack_copy_inode_size(parent,
+ hidden_parent_dentry->d_inode);
+
+ /* update number of links on parent directory */
+ parent->i_nlink = unionfs_get_nlinks(parent);
+ }
+
+ err = make_dir_opaque(dentry, dbstart(dentry));
+ if (err) {
+ printk(KERN_ERR "mkdir: error creating "
+ ".wh.__dir_opaque: %d\n", err);
+ goto out;
+ }
+
+ /* we are done! */
+ break;
+ }
+
+out:
+ if (!dentry->d_inode)
+ d_drop(dentry);
+
+ kfree(name);
+
+ unlock_dentry(dentry);
+ return err;
+}
+
+static int unionfs_mknod(struct inode *dir, struct dentry *dentry, int mode,
+ dev_t dev)
+{
+ int err = 0;
+ struct dentry *hidden_dentry = NULL, *whiteout_dentry = NULL;
+ struct dentry *hidden_parent_dentry = NULL;
+ int bindex = 0, bstart;
+ char *name = NULL;
+ int whiteout_unlinked = 0;
+
+ lock_dentry(dentry);
+ bstart = dbstart(dentry);
+
+ hidden_dentry = unionfs_lower_dentry(dentry);
+
+ /* check if whiteout exists in this branch, i.e. lookup .wh.foo first */
+ name = alloc_whname(dentry->d_name.name, dentry->d_name.len);
+ if (IS_ERR(name)) {
+ err = PTR_ERR(name);
+ goto out;
+ }
+
+ whiteout_dentry = lookup_one_len(name, hidden_dentry->d_parent,
+ dentry->d_name.len + UNIONFS_WHLEN);
+ if (IS_ERR(whiteout_dentry)) {
+ err = PTR_ERR(whiteout_dentry);
+ goto out;
+ }
+
+ if (!whiteout_dentry->d_inode) {
+ dput(whiteout_dentry);
+ whiteout_dentry = NULL;
+ } else {
+ /* found .wh.foo, unlink it */
+ hidden_parent_dentry = lock_parent(whiteout_dentry);
+
+ /* found a.wh.foo entry, remove it then do vfs_mkdir */
+ if (!(err = is_robranch_super(dentry->d_sb, bstart)))
+ err = vfs_unlink(hidden_parent_dentry->d_inode,
+ whiteout_dentry);
+ dput(whiteout_dentry);
+
+ unlock_dir(hidden_parent_dentry);
+
+ if (err) {
+ if (!IS_COPYUP_ERR(err))
+ goto out;
+
+ bstart--;
+ } else
+ whiteout_unlinked = 1;
+ }
+
+ for (bindex = bstart; bindex >= 0; bindex--) {
+ if (is_robranch_super(dentry->d_sb, bindex))
+ continue;
+
+ hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ if (!hidden_dentry) {
+ hidden_dentry = create_parents(dir, dentry, bindex);
+ if (IS_ERR(hidden_dentry)) {
+ printk(KERN_DEBUG
+ "failed to create parents on %d, err = %ld\n",
+ bindex, PTR_ERR(hidden_dentry));
+ continue;
+ }
+ }
+
+ hidden_parent_dentry = lock_parent(hidden_dentry);
+ if (IS_ERR(hidden_parent_dentry)) {
+ err = PTR_ERR(hidden_parent_dentry);
+ goto out;
+ }
+
+ err = vfs_mknod(hidden_parent_dentry->d_inode,
+ hidden_dentry, mode, dev);
+
+ if (err) {
+ unlock_dir(hidden_parent_dentry);
+ break;
+ }
+
+ err = unionfs_interpose(dentry, dir->i_sb, 0);
+ if (!err) {
+ fsstack_copy_attr_times(dir,
+ hidden_parent_dentry->d_inode);
+ fsstack_copy_inode_size(dir,
+ hidden_parent_dentry->d_inode);
+ /* update number of links on parent directory */
+ dir->i_nlink = unionfs_get_nlinks(dir);
+ }
+ unlock_dir(hidden_parent_dentry);
+
+ break;
+ }
+
+out:
+ if (!dentry->d_inode)
+ d_drop(dentry);
+
+ kfree(name);
+
+ unlock_dentry(dentry);
+ return err;
+}
+
+int unionfs_readlink(struct dentry *dentry, char __user * buf, int bufsiz)
+{
+ int err;
+ struct dentry *hidden_dentry;
+
+ lock_dentry(dentry);
+ hidden_dentry = unionfs_lower_dentry(dentry);
+
+ if (!hidden_dentry->d_inode->i_op ||
+ !hidden_dentry->d_inode->i_op->readlink) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ err = hidden_dentry->d_inode->i_op->readlink(hidden_dentry, buf, bufsiz);
+ if (err > 0)
+ fsstack_copy_attr_atime(dentry->d_inode, hidden_dentry->d_inode);
+
+out:
+ unlock_dentry(dentry);
+ return err;
+}
+
+/* We don't lock the dentry here, because readlink does the heavy lifting. */
+static void *unionfs_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ char *buf;
+ int len = PAGE_SIZE, err;
+ mm_segment_t old_fs;
+
+ /* This is freed by the put_link method assuming a successful call. */
+ buf = kmalloc(len, GFP_KERNEL);
+ if (!buf) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ /* read the symlink, and then we will follow it */
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ err = dentry->d_inode->i_op->readlink(dentry, (char __user *)buf, len);
+ set_fs(old_fs);
+ if (err < 0) {
+ kfree(buf);
+ buf = NULL;
+ goto out;
+ }
+ buf[err] = 0;
+ nd_set_link(nd, buf);
+ err = 0;
+
+out:
+ return ERR_PTR(err);
+}
+
+void unionfs_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
+{
+ kfree(nd_get_link(nd));
+}
+
+/* Basically copied from the kernel vfs permission(), but we've changed
+ * the following: (1) the IS_RDONLY check is skipped, and (2) if you set
+ * the mount option `nfsperms=insceure', we assume that -EACCES means that
+ * the export is read-only and we should check standard Unix permissions.
+ * This means that NFS ACL checks (or other advanced permission features)
+ * are bypassed.
+ */
+static int inode_permission(struct inode *inode, int mask, struct nameidata *nd,
+ int bindex)
+{
+ int retval, submask;
+
+ if (mask & MAY_WRITE) {
+ /* The first branch is allowed to be really readonly. */
+ if (bindex == 0) {
+ umode_t mode = inode->i_mode;
+ if (IS_RDONLY(inode) && (S_ISREG(mode) || S_ISDIR(mode)
+ || S_ISLNK(mode)))
+ return -EROFS;
+ }
+ /*
+ * Nobody gets write access to an immutable file.
+ */
+ if (IS_IMMUTABLE(inode))
+ return -EACCES;
+ }
+
+ /* Ordinary permission routines do not understand MAY_APPEND. */
+ submask = mask & ~MAY_APPEND;
+ if (inode->i_op && inode->i_op->permission) {
+ retval = inode->i_op->permission(inode, submask, nd);
+ if ((retval == -EACCES) && (submask & MAY_WRITE) &&
+ (!strcmp("nfs", (inode)->i_sb->s_type->name)) &&
+ (nd) && (nd->mnt) && (nd->mnt->mnt_sb) &&
+ (branchperms(nd->mnt->mnt_sb, bindex) & MAY_NFSRO)) {
+ retval = generic_permission(inode, submask, NULL);
+ }
+ } else
+ retval = generic_permission(inode, submask, NULL);
+
+ if (retval && retval != -EROFS) /* ignore EROFS */
+ return retval;
+
+ retval = security_inode_permission(inode, mask, nd);
+ return ((retval == -EROFS) ? 0 : retval); /* ignore EROFS */
+}
+
+static int unionfs_permission(struct inode *inode, int mask,
+ struct nameidata *nd)
+{
+ struct inode *hidden_inode = NULL;
+ int err = 0;
+ int bindex, bstart, bend;
+ const int is_file = !S_ISDIR(inode->i_mode);
+ const int write_mask = (mask & MAY_WRITE) && !(mask & MAY_READ);
+
+ bstart = ibstart(inode);
+ bend = ibend(inode);
+
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ hidden_inode = unionfs_lower_inode_idx(inode, bindex);
+ if (!hidden_inode)
+ continue;
+
+ /* check the condition for D-F-D underlying files/directories,
+ * we dont have to check for files, if we are checking for
+ * directories.
+ */
+ if (!is_file && !S_ISDIR(hidden_inode->i_mode))
+ continue;
+
+ /* We use our own special version of permission, such that
+ * only the first branch returns -EROFS. */
+ err = inode_permission(hidden_inode, mask, nd, bindex);
+
+ /* The permissions are an intersection of the overall directory
+ * permissions, so we fail if one fails. */
+ if (err)
+ goto out;
+
+ /* only the leftmost file matters. */
+ if (is_file || write_mask) {
+ if (is_file && write_mask) {
+ err = get_write_access(hidden_inode);
+ if (!err)
+ put_write_access(hidden_inode);
+ }
+ break;
+ }
+ }
+
+out:
+ return err;
+}
+
+static int unionfs_setattr(struct dentry *dentry, struct iattr *ia)
+{
+ int err = 0;
+ struct dentry *hidden_dentry;
+ struct inode *inode = NULL;
+ struct inode *hidden_inode = NULL;
+ int bstart, bend, bindex;
+ int i;
+ int copyup = 0;
+
+ lock_dentry(dentry);
+ bstart = dbstart(dentry);
+ bend = dbend(dentry);
+ inode = dentry->d_inode;
+
+ for (bindex = bstart; (bindex <= bend) || (bindex == bstart); bindex++) {
+ hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ if (!hidden_dentry)
+ continue;
+ BUG_ON(hidden_dentry->d_inode == NULL);
+
+ /* If the file is on a read only branch */
+ if (is_robranch_super(dentry->d_sb, bindex)
+ || IS_RDONLY(hidden_dentry->d_inode)) {
+ if (copyup || (bindex != bstart))
+ continue;
+ /* Only if its the leftmost file, copyup the file */
+ for (i = bstart - 1; i >= 0; i--) {
+ loff_t size = dentry->d_inode->i_size;
+ if (ia->ia_valid & ATTR_SIZE)
+ size = ia->ia_size;
+ err = copyup_dentry(dentry->d_parent->d_inode,
+ dentry, bstart, i, NULL,
+ size);
+
+ if (!err) {
+ copyup = 1;
+ hidden_dentry = unionfs_lower_dentry(dentry);
+ break;
+ }
+ /* if error is in the leftmost branch, pass it up */
+ if (i == 0)
+ goto out;
+ }
+
+ }
+ err = notify_change(hidden_dentry, ia);
+ if (err)
+ goto out;
+ break;
+ }
+
+ /* get the size from the first hidden inode */
+ hidden_inode = unionfs_lower_inode(dentry->d_inode);
+ fsstack_copy_attr_all(inode, hidden_inode, unionfs_get_nlinks);
+
+out:
+ unlock_dentry(dentry);
+ return err;
+}
+
+struct inode_operations unionfs_symlink_iops = {
+ .readlink = unionfs_readlink,
+ .permission = unionfs_permission,
+ .follow_link = unionfs_follow_link,
+ .setattr = unionfs_setattr,
+ .put_link = unionfs_put_link,
+};
+
+struct inode_operations unionfs_dir_iops = {
+ .create = unionfs_create,
+ .lookup = unionfs_lookup,
+ .link = unionfs_link,
+ .unlink = unionfs_unlink,
+ .symlink = unionfs_symlink,
+ .mkdir = unionfs_mkdir,
+ .rmdir = unionfs_rmdir,
+ .mknod = unionfs_mknod,
+ .rename = unionfs_rename,
+ .permission = unionfs_permission,
+ .setattr = unionfs_setattr,
+};
+
+struct inode_operations unionfs_main_iops = {
+ .permission = unionfs_permission,
+ .setattr = unionfs_setattr,
+};
+
--
1.4.3.3

2006-12-04 12:57:43

by bert hubert

[permalink] [raw]
Subject: Re: Unionfs: Stackable namespace unification filesystem

On Mon, Dec 04, 2006 at 07:30:33AM -0500, Josef 'Jeff' Sipek wrote:
> The following patches are in a git repo at:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/jsipek/unionfs.git

Jeff,

Do you have a pointer to a quick blurb on this work?

Thanks.

--
http://www.PowerDNS.com Open source, database driven DNS Software
http://netherlabs.nl Open and Closed source services

2006-12-04 13:04:43

by Josef Sipek

[permalink] [raw]
Subject: Re: Unionfs: Stackable namespace unification filesystem

On Mon, Dec 04, 2006 at 01:57:40PM +0100, bert hubert wrote:
> On Mon, Dec 04, 2006 at 07:30:33AM -0500, Josef 'Jeff' Sipek wrote:
> > The following patches are in a git repo at:
> >
> > git://git.kernel.org/pub/scm/linux/kernel/git/jsipek/unionfs.git
>
> Jeff,
>
> Do you have a pointer to a quick blurb on this work?

There's a lot of material on our website, http://www.unionfs.org.

The one sentence summary would be: Unionfs is a stackable unification
filesystem, which can appear to merge the contents of several directories
(branches), while keeping their physical content separate.

Hope that helps,

Josef "Jeff" Sipek.

2006-12-05 15:15:59

by Josef Sipek

[permalink] [raw]
Subject: Re: Unionfs: Stackable namespace unification filesystem

On Mon, Dec 04, 2006 at 07:30:33AM -0500, Josef 'Jeff' Sipek wrote:
> The following patches are in a git repo at:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/jsipek/unionfs.git
>
> (master.kernel.org:/pub/scm/linux/kernel/git/jsipek/unionfs.git)
>
> The repository contains the following 35 commits (also available as patches
> in replies to this email).

Wow, I completely forgot to say what changed since the previous posting...

- Added more comments (akpm)
- Cleaned up inode/dentry/file/sb private data structure member names
- Moved struct inode from unionfs_inode_container into unionfs_inode_info
(following ext2's example)
- Renamed {d,i,f,s}topd to UNIONFS_{D,I,F,S} (following almost any fs's
example)
- Removed *_ptr and *_lhs macros, open-coding the assignments (Pekka Enberg)
- Kill wrappers (e.g., unionfs_kill_block_super) (Pekka Enberg)
- Few tiny coding style fixes
- Removed some unnecessary complexity (no need to pass struct file and a
struct dentry for that file to a function - just pass a struct file
and dereference in the function, etc.)
- Removed some unnecessary checks (if (foo) kfree(foo); is completely
redundant as kfree has a null check)
- Changed C++-style comments to C-style comments (Pekka Enberg)
- Use struct kmem_cache instead of kmem_cache_t (Pekka Enberg)
- Use anonymous unions for sioq (Jan Engelhardt)
- Moved some functionality into fsstack (most of it is in -mm already)

All in all about 1/8th of the code was touched in one way or another.

Josef "Jeff" Sipek.

2006-12-05 19:35:13

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH 26/35] Unionfs: Privileged operations workqueue


On Dec 4 2006 07:30, Josef 'Jeff' Sipek wrote:
>+#include "union.h"
>+
>+struct workqueue_struct *sioq;
>+
>+int __init init_sioq(void)

Although it's just me, I'd prefer sioq_init(), sioq_exit(),
sioq_run(), etc. to go in hand with what most drivers use as naming
(i.e. <modulename> "_" <function>).

>+ sioq = create_workqueue("unionfs_siod");

Beat me: what does SIO stand for?

>+void fin_sioq(void)

No __exit tag?

>+void __unionfs_mknod(void *data)
>+{
>+ struct sioq_args *args = data;
>+ struct mknod_args *m = &args->mknod;

Care to make that: const struct mknod_args *m = &args->mknod;?
(Same for other places)

>+
>+ args->err = vfs_mknod(m->parent, m->dentry, m->mode, m->dev);
>+ complete(&args->comp);
>+}
>+void __unionfs_symlink(void *data)
>+{

Hah, missing free line :^)

>+ struct sioq_args *args = data;
>+ struct symlink_args *s = &args->symlink;
>+
>+ args->err = vfs_symlink(s->parent, s->dentry, s->symbuf, s->mode);
>+ complete(&args->comp);
>+}
>+

>+++ b/fs/unionfs/sioq.h
>+struct isopaque_args {
>+ struct dentry *dentry;
>+};

This name puzzled me at first. iso - paque - "same (o)paque"?
Try is_opaque_args.

>+/* Extern definitions for our privledge escalation helpers */

-> privilege


-`J'
--

2006-12-05 19:48:31

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH 31/35] Unionfs: Internal include file


On Dec 4 2006 07:31, Josef 'Jeff' Sipek wrote:
>+++ b/fs/unionfs/union.h

>+#include <asm/mman.h>
>+#include <asm/system.h>
>+#include <linux/dcache.h>
>+#include <linux/file.h>

I think someone once said asm/s should be after all the linux/s.
Well, as long as it compiles, I have no objection it being before.

>+/* unionfs file systems superblock magic */
>+#define UNIONFS_SUPER_MAGIC 0xf15f083d

Put this in include/linux/magic.h instead?

>+/* How long should an entry be allowed to persist */
>+#define RDCACHE_JIFFIES 5*HZ

Put () around it.

>+/* Cache creation/deletion routines. */
>+void destroy_filldir_cache(void);
>+int init_filldir_cache(void);
>+int init_inode_cache(void);
>+void destroy_inode_cache(void);
>+int init_dentry_cache(void);
>+void destroy_dentry_cache(void);

In sioq.h and below, "extern"s are present before function
prototypes. No bias, just make it even across all code somehow.

>+/* Initialize and free readdir-specific state. */
>+int init_rdstate(struct file *file);
>+struct unionfs_dir_state *alloc_rdstate(struct inode *inode, int bindex);
>+struct unionfs_dir_state *find_rdstate(struct inode *inode, loff_t fpos);
>+void free_rdstate(struct unionfs_dir_state *state);
>+int add_filldir_node(struct unionfs_dir_state *rdstate, const char *name,
>+ int namelen, int bindex, int whiteout);
>+struct filldir_node *find_filldir_node(struct unionfs_dir_state *rdstate,
>+ const char *name, int namelen);
>+
>+struct dentry **alloc_new_dentries(int objs);
>+struct unionfs_data *alloc_new_data(int objs);
>+
>+/* We can only use 32-bits of offset for rdstate --- blech! */
>+#define DIREOF (0xfffff)
>+#define RDOFFBITS 20 /* This is the number of bits in DIREOF. */
>+#define MAXRDCOOKIE (0xfff)
>+/* Turn an rdstate into an offset. */
>+static inline off_t rdstate2offset(struct unionfs_dir_state *buf)
>+{
>+ off_t tmp;
>+ tmp = ((buf->cookie & MAXRDCOOKIE) << RDOFFBITS)
>+ | (buf->offset & DIREOF);
>+ return tmp;
>+}
>+
>+#define unionfs_read_lock(sb) down_read(&UNIONFS_SB(sb)->rwsem)
>+#define unionfs_read_unlock(sb) up_read(&UNIONFS_SB(sb)->rwsem)
>+#define unionfs_write_lock(sb) down_write(&UNIONFS_SB(sb)->rwsem)
>+#define unionfs_write_unlock(sb) up_write(&UNIONFS_SB(sb)->rwsem)
>+
>+/* The double lock function needs to go after the debugmacros, so that
>+ * dtopd is defined. */
^^^^^

This was changed, was not it? :)

>+static inline void double_lock_dentry(struct dentry *d1, struct dentry *d2)
>+{
>+ if (d2 < d1) {

What is this supposed to do? Is it guaranteed that a
later-allocated dentry always has a lower/higher address than one
allocated before?

>+ struct dentry *tmp = d1;
>+ d1 = d2;
>+ d2 = tmp;
>+ }
>+ lock_dentry(d1);
>+ lock_dentry(d2);
>+}
>+
>+extern int new_dentry_private_data(struct dentry *dentry);
>+void free_dentry_private_data(struct unionfs_dentry_info *udi);
>+void update_bstart(struct dentry *dentry);
>+

[bigger snip]

>+/* The values for unionfs_interpose's flag. */
>+#define INTERPOSE_DEFAULT 0
>+#define INTERPOSE_LOOKUP 1
>+#define INTERPOSE_REVAL 2
>+#define INTERPOSE_REVAL_NEG 3
>+#define INTERPOSE_PARTIAL 4

I vote for enums! Who joins?


>+/* The root directory is unhashed, but isn't deleted. */
>+static inline int d_deleted(struct dentry *d)
>+{
>+ return d_unhashed(d) && (d != d->d_sb->s_root);
>+}

() around the != expr is redundant.

>+/* returns the sum of the n_link values of all the underlying inodes of the
>+ * passed inode */
>+static inline int unionfs_get_nlinks(struct inode *inode)
>+{
>...
>+ /* A broken directory (e.g., squashfs). */
>...

You're so mean :>
Though I can't recall right now, squashfs is not the only one doing
that.

>+#define IS_SET(sb, check_flag) (check_flag & MOUNT_FLAG(sb))

#define IS_SET(sb, check_flag) ((check_flag) & MOUNT_FLAG(sb))

>+#define IS_WRITE_FLAG(flag) ((flag) & (OPEN_WRITE_FLAGS))

#define IS_WRITE_FLAG(flag) ((flag) & OPEN_WRITE_FLAGS)

>+static inline int branchperms(struct super_block *sb, int index)
>+{
>+ BUG_ON(index < 0);
>+
>+ return UNIONFS_SB(sb)->data[index].branchperms;
>+}

This could have const struct super_block *sb;

>+
>+static inline int set_branchperms(struct super_block *sb, int index, int perms)
>+{
>+ BUG_ON(index < 0);
>+
>+ UNIONFS_SB(sb)->data[index].branchperms = perms;
>+
>+ return perms;
>+}

This one I don't think,

>+/* Is this file on a read-only branch? */
>+static inline int is_robranch_super(struct super_block *sb, int index)
>+{
>+ return (!(branchperms(sb, index) & MAY_WRITE)) ? -EROFS : 0;
>+}

But this one. Check others.

>+/* Is this file on a read-only branch? */
>+static inline int is_robranch_idx(struct dentry *dentry, int index)
>+{
>+ int err = 0;
>+
>+ BUG_ON(index < 0);
>+
>+ if ((!(branchperms(dentry->d_sb, index) & MAY_WRITE)) ||
>+ IS_RDONLY(unionfs_lower_dentry_idx(dentry, index)->d_inode))
>+ err = -EROFS;
>+
>+ return err;
>+}
>+
>+static inline int is_robranch(struct dentry *dentry)
>+{
>+ int index;
>+
>+ index = UNIONFS_D(dentry)->bstart;
>+ BUG_ON(index < 0);
>+
>+ return is_robranch_idx(dentry, index);
>+}
>+
>+/* What do we use for whiteouts. */
>+#define UNIONFS_WHPFX ".wh."
>+#define UNIONFS_WHLEN 4
>+/* If a directory contains this file, then it is opaque. We start with the
>+ * .wh. flag so that it is blocked by lookup.
>+ */
>+#define UNIONFS_DIR_OPAQUE_NAME "__dir_opaque"
>+#define UNIONFS_DIR_OPAQUE UNIONFS_WHPFX UNIONFS_DIR_OPAQUE_NAME
>+
>+/* construct whiteout filename */
>+static inline char *alloc_whname(const char *name, int len)
>+{
>+ char *buf;
>+
>+ buf = kmalloc(len + UNIONFS_WHLEN + 1, GFP_KERNEL);
>+ if (!buf)
>+ return ERR_PTR(-ENOMEM);
>+
>+ strcpy(buf, UNIONFS_WHPFX);
>+ strlcat(buf, name, len + UNIONFS_WHLEN + 1);
>+
>+ return buf;
>+}
>+
>+#define VALID_MOUNT_FLAGS (0)
>+
>+/*
>+ * MACROS:
>+ */
>+
>+#ifndef DEFAULT_POLLMASK
>+#define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)
>+#endif
>+
>+/*
>+ * EXTERNALS:
>+ */
>+
>+/* These two functions are here because it is kind of daft to copy and paste the
>+ * contents of the two functions to 32+ places in unionfs
>+ */
>+static inline struct dentry *lock_parent(struct dentry *dentry)
>+{
>+ struct dentry *dir = dget(dentry->d_parent);
>+
>+ mutex_lock(&dir->d_inode->i_mutex);
>+ return dir;
>+}
>+
>+static inline void unlock_dir(struct dentry *dir)
>+{
>+ mutex_unlock(&dir->d_inode->i_mutex);
>+ dput(dir);
>+}
>+
>+extern int make_dir_opaque(struct dentry *dir, int bindex);
>+
>+#endif /* not _UNION_H_ */
>+


-`J'
--

2006-12-05 19:51:36

by Josef Sipek

[permalink] [raw]
Subject: Re: [PATCH 26/35] Unionfs: Privileged operations workqueue

On Tue, Dec 05, 2006 at 08:27:51PM +0100, Jan Engelhardt wrote:
>
> On Dec 4 2006 07:30, Josef 'Jeff' Sipek wrote:
> >+#include "union.h"
> >+
> >+struct workqueue_struct *sioq;
> >+
> >+int __init init_sioq(void)
>
> Although it's just me, I'd prefer sioq_init(), sioq_exit(),
> sioq_run(), etc. to go in hand with what most drivers use as naming
> (i.e. <modulename> "_" <function>).

That makes sense.

> >+ sioq = create_workqueue("unionfs_siod");
>
> Beat me: what does SIO stand for?

Super-user IO - sometimes we need to perform actions which would fail due to
the unix permissions on the parent directory (e.g., rmdir a directory which
appears empty, but in reality contains whiteouts).

> >+void fin_sioq(void)
>
> No __exit tag?

Good catch.

> >+void __unionfs_mknod(void *data)
> >+{
> >+ struct sioq_args *args = data;
> >+ struct mknod_args *m = &args->mknod;
>
> Care to make that: const struct mknod_args *m = &args->mknod;?
> (Same for other places)

Right.

> >+
> >+ args->err = vfs_mknod(m->parent, m->dentry, m->mode, m->dev);
> >+ complete(&args->comp);
> >+}
> >+void __unionfs_symlink(void *data)
> >+{
>
> Hah, missing free line :^)

:)

> >+ struct sioq_args *args = data;
> >+ struct symlink_args *s = &args->symlink;
> >+
> >+ args->err = vfs_symlink(s->parent, s->dentry, s->symbuf, s->mode);
> >+ complete(&args->comp);
> >+}
> >+
>
> >+++ b/fs/unionfs/sioq.h
> >+struct isopaque_args {
> >+ struct dentry *dentry;
> >+};
>
> This name puzzled me at first. iso - paque - "same (o)paque"?
> Try is_opaque_args.

Will do.

> >+/* Extern definitions for our privledge escalation helpers */
>
> -> privilege

Why do I keep misspelling that?

Thanks for the comments.

Josef "Jeff" Sipek.

--
Bad pun of the week: The formula 1 control computer suffered from a race
condition

2006-12-05 20:59:18

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH 12/35] Unionfs: Documentation


>+++ b/Documentation/filesystems/unionfs/00-INDEX
>@@ -0,0 +1,8 @@
>+00-INDEX
>+ - this file.
>+concepts.txt
>+ - A brief introduction of concepts
>+rename.txt
>+ - Information regarding rename operations
>+usage.txt
>+ - Usage & known limitations

Try "and", & is so... 'lazy'.


>+Since 'foo' is stored on a read-only branch, it cannot be removed. A whiteout
>+is used to remove the name 'foo' from the unified namespace. Again, since
>+branch 1 is read-only, the whiteout cannot be created there. So, we try on a
>+higher priority (lower numerically) branch. And there we create the whiteout.

higher priority (numerically lower) branch and create the whiteout there.
(Starting a sentence with 'and' is like telling fairytales^W stories.)

>+solution is to take the instance from the highest priority (lowest numerical
>+value) and "hide" the others.

(numerically lowest value)

>+When a change is made to the contents of a file's data or meta-data, they
>+have to be stored somewhere. The best way is to create a copy of the
>+original file on a branch that is writable, and then redirect the write
>+though to this copy. The copy must be made on a higher priority branch so
>+that lookup & readdir return this newer "version" of the file rather than
>+the original (see duplicate elimination).

s/&/and/g;

>+Modifying a Unionfs branch directly, while the union is mounted is currently
>+unsupported.

Either:
Modifying a Unionfs branch directly while the union
is mounted is currently unsupported.
Or:
Modifying a Unionfs branch directly, while the union
is mounted, is currently unsupported.

> Any such change can cause Unionfs to oops, however it could even
>+RESULT IN DATA LOSS.

Or stay silent (-> silent data corruption / loss)

>+Unionfs shouldn't use lookup_one_len on the underlying fs as it confuses

For written text, non-shortened forms (should not) are preferred. At least
that's (<- that's texified speech not documentation) what we were told back in
scool :p

>+NFS. Currently, unionfs_lookup passes lookup intents to the lower

should not use lookup_one_len() [...] Currently, unionfs_lookup()

most doc add () to clarify it is a function.

>+filesystem, this eliminates part of the problem. The remaining calls to
>+lookup_one_len may need to be changed to pass an intent.

~

-`J'
--

2006-12-05 21:00:42

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH 13/35] lookup_one_len_nd - lookup_one_len with nameidata argument


>+++ b/fs/namei.c
>@@ -1290,8 +1290,8 @@ static struct dentry *lookup_hash(struct
> return __lookup_hash(&nd->last, nd->dentry, nd);
> }
>
>-/* SMP-safe */
>-struct dentry * lookup_one_len(const char * name, struct dentry * base, int len)
>+struct dentry * lookup_one_len_nd(const char *name, struct dentry * base,
>+ int len, struct nameidata *nd)

Your style varies between <TYPE> *<NAME> and <TYPE> * <NAME>.
Unify it some time.



-`J'
--

2006-12-05 21:09:20

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH 15/35] Unionfs: Common file operations



On Dec 4 2006 07:30, Josef 'Jeff' Sipek wrote:

>+long unionfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>+{
>+ long err;
>+
>+ if ((err = unionfs_file_revalidate(file, 1)))
>+ goto out;
>+
>+ /* check if asked for local commands */
>+ switch (cmd) {
>+ case UNIONFS_IOCTL_INCGEN:
>+ /* Increment the superblock generation count */
>+ err = -EACCES;
>+ if (!capable(CAP_SYS_ADMIN))
>+ goto out;
>+ err = unionfs_ioctl_incgen(file, cmd, arg);
>+ break;
>+
>+ case UNIONFS_IOCTL_QUERYFILE:
>+ /* Return list of branches containing the given file */
>+ err = unionfs_ioctl_queryfile(file, cmd, arg);
>+ break;
>+
>+ default:
>+ /* pass the ioctl down */
>+ err = do_ioctl(file, cmd, arg);
>+ break;
>+ }
>+
>+out:
>+ return err;
>+}


I think there was an ioctl for files to find out where a particular
file lives on disk. Do you think unionfs should handle it and return
something more or less meaningful?


-`J'
--

2006-12-05 21:16:28

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH 16/35] Unionfs: Copyup Functionality


On Dec 4 2006 07:30, Josef 'Jeff' Sipek wrote:
>+/* Determine the mode based on the copyup flags, and the existing dentry. */
>+static int copyup_permissions(struct super_block *sb,
>+ struct dentry *old_hidden_dentry,
>+ struct dentry *new_hidden_dentry)
>+{
>+ struct inode *i = old_hidden_dentry->d_inode;

Screams for constification. (Or rather I do.)

>+{
>+ int err = 0;
>+ umode_t old_mode = old_hidden_dentry->d_inode->i_mode;

Generel question for everybody: Why do we have two same (at least on i386
that's the case) types, umode_t and mode_t?

>+ } else if (S_ISBLK(old_mode)
>+ || S_ISCHR(old_mode)
>+ || S_ISFIFO(old_mode)
>+ || S_ISSOCK(old_mode)) {
>+ args.mknod.parent = new_hidden_parent_dentry->d_inode;
>+ args.mknod.dentry = new_hidden_dentry;
>+ args.mknod.mode = old_mode;

I'd say the indent got screwed up, and it's not a mailer thing.

>+ } else if (S_ISBLK(old_mode)
>+ || S_ISCHR(old_mode)
>+ || S_ISFIFO(old_mode)
>+ || S_ISSOCK(old_mode)) {
>+ args.mknod.parent = new_hidden_parent_dentry->d_inode;
>+ args.mknod.dentry = new_hidden_dentry;
>+ args.mknod.mode = old_mode;

Try this ^^^. Or even this vvv:

>+ } else if (S_ISBLK(old_mode) || S_ISCHR(old_mode) ||
>+ S_ISFIFO(old_mode) || S_ISSOCK(old_mode)) {
>+ args.mknod.parent = new_hidden_parent_dentry->d_inode;
>+ args.mknod.dentry = new_hidden_dentry;
>+ args.mknod.mode = old_mode;


>+static inline int __copyup_reg_data(struct dentry *dentry,
>+ struct dentry *new_hidden_dentry,
>+ int new_bindex,
>+ struct dentry *old_hidden_dentry,
>+ int old_bindex,
>+ struct file **copyup_file,
>+ loff_t len)
>+{
>+ struct super_block *sb = dentry->d_sb;
>+ struct file *input_file;
>+ struct file *output_file;
>+ mm_segment_t old_fs;
>+ char *buf = NULL;
>+ ssize_t read_bytes, write_bytes;
>+ loff_t size;
>+ int err = 0;
>+
>+ /* open old file */
>+ mntget(unionfs_lower_mnt_idx(dentry, old_bindex));
>+ branchget(sb, old_bindex);
>+ input_file = dentry_open(old_hidden_dentry,
>+ unionfs_lower_mnt_idx(dentry, old_bindex), O_RDONLY | O_LARGEFILE);
>+ if (IS_ERR(input_file)) {
>+ dput(old_hidden_dentry);
>+ err = PTR_ERR(input_file);
>+ goto out;
>+ }
>+ if (!input_file->f_op || !input_file->f_op->read) {
>+ err = -EINVAL;
>+ goto out_close_in;
>+ }
>+
>+ /* open new file */
>+ dget(new_hidden_dentry);
>+ mntget(unionfs_lower_mnt_idx(dentry, new_bindex));
>+ branchget(sb, new_bindex);
>+ output_file = dentry_open(new_hidden_dentry,
>+ unionfs_lower_mnt_idx(dentry, new_bindex), O_WRONLY | O_LARGEFILE);

Here we got an 80-column buster.

>+ if (IS_ERR(output_file)) {
>+ err = PTR_ERR(output_file);
>+ goto out_close_in2;
>+ }
>+ if (!output_file->f_op || !output_file->f_op->write) {
>+ err = -EINVAL;
>+ goto out_close_out;
>+ }
>+
>+ /* allocating a buffer */
>+ buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
>+ if (!buf) {
>+ err = -ENOMEM;
>+ goto out_close_out;
>+ }
>+
>+ input_file->f_pos = 0;
>+ output_file->f_pos = 0;
>+
>+ old_fs = get_fs();
>+ set_fs(KERNEL_DS);
>+
>+ size = len;
>+ err = 0;
>+ do {
>+ if (len >= PAGE_SIZE)
>+ size = PAGE_SIZE;
>+ else if ((len < PAGE_SIZE) && (len > 0))
>+ size = len;

Some redundant () here.

>+
>+ len -= PAGE_SIZE;
>+
>+ read_bytes =
>+ input_file->f_op->read(input_file,
>+ (char __user *)buf, size,
>+ &input_file->f_pos);
>+ if (read_bytes <= 0) {
>+ err = read_bytes;
>+ break;
>+ }
>+
>+ write_bytes =
>+ output_file->f_op->write(output_file,
>+ (char __user *)buf,
>+ read_bytes,
>+ &output_file->f_pos);
>+ if (write_bytes < 0 || (write_bytes < read_bytes)) {

dit(t)o

>+ err = write_bytes;
>+ break;
>+ }
>+ } while ((read_bytes > 0) && (len > 0));

~

>+
>+ set_fs(old_fs);
>+
>+ kfree(buf);
>+
>+ if (err)
>+ goto out_close_out;
>+ if (copyup_file) {
>+ *copyup_file = output_file;
>+ goto out_close_in;
>+ }
>+
>+out_close_out:
>+ fput(output_file);
>+
>+out_close_in2:
>+ branchput(sb, new_bindex);
>+
>+out_close_in:
>+ fput(input_file);
>+
>+out:
>+ branchput(sb, old_bindex);
>+
>+ return err;
>+}
>+

>+ /* TODO: should we reset the error to something like -EIO? */

Handle it :) - if it does not take a paper.




-`J'
--

2006-12-05 21:24:24

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH 19/35] Unionfs: Directory file operations



>+++ b/fs/unionfs/dirfops.c

>+/* This is not meant to be a generic repositioning function. If you do
>+ * things that aren't supported, then we return EINVAL.
>+ *
>+ * What is allowed:
>+ * (1) seeking to the same position that you are currently at
>+ * This really has no effect, but returns where you are.
>+ * (2) seeking to the end of the file, if you've read everything
>+ * This really has no effect, but returns where you are.
>+ * (3) seeking to the beginning of the file
>+ * This throws out all state, and lets you begin again.
>+ */
>+static loff_t unionfs_dir_llseek(struct file *file, loff_t offset, int origin)
>+{
[...]
>+ /* We let users seek to their current position, but not anywhere else. */
>+ if (!offset) {
[...]
>+ case SEEK_END:
>+ /* Unsupported, because we would break everything. */
>+ err = -EINVAL;
>+ break;
[...]

This SEEK_END implementation clashes with (2).

>+ } else {
[...]
>+ }
>+
>+out:
>+ return err;
>+}
>+
>+/* Trimmed directory options, we shouldn't pass everything down since
>+ * we don't want to operate on partial directories.
>+ */
>+struct file_operations unionfs_dir_fops = {
>+ .llseek = unionfs_dir_llseek,
>+ .read = generic_read_dir,
>+ .readdir = unionfs_readdir,
>+ .unlocked_ioctl = unionfs_ioctl,
>+ .open = unionfs_open,
>+ .release = unionfs_file_release,
>+ .flush = unionfs_flush,
>+};

Prefers

+struct file_operations unionfs_dir_fops = {
+ .llseek = unionfs_dir_llseek,
+ .read = generic_read_dir,
+ .readdir = unionfs_readdir,
+ .unlocked_ioctl = unionfs_ioctl,
+ .open = unionfs_open,
+ .release = unionfs_file_release,
+ .flush = unionfs_flush,
+};

BTW, you could line up other structs too! :)



-`J'
--

2006-12-05 21:28:22

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH 20/35] Unionfs: Directory manipulation helper functions


>+++ b/fs/unionfs/dirhelper.c

>+/* This filldir function makes sure only whiteouts exist within a directory. */
>+static int readdir_util_callback(void *dirent, const char *name, int namelen,
>+ loff_t offset, u64 ino, unsigned int d_type)
>+{
>+ int err = 0;
>+ struct unionfs_rdutil_callback *buf = dirent;
>+ int whiteout = 0;
>+ struct filldir_node *found;
>+
>+ buf->filldir_called = 1;
>+
>+ if (name[0] == '.'
>+ && (namelen == 1 || (name[1] == '.' && namelen == 2)))
>+ goto out;
>+
>+ if (namelen > UNIONFS_WHLEN && !strncmp(name, UNIONFS_WHPFX, UNIONFS_WHLEN)) {

When I see this if, I feel like asking (unrelated to dirhelper.c):
what do we do when

* strlen(UNIONFS_WHPFX filename) is greater than the maximum file
name length supported by a filesystem?

* strlen(absolute_path(UNIONFS_WHPFX filename)) is greater than PATH_MAX?



-`J'
--

2006-12-05 21:34:21

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH 21/35] Unionfs: Inode operations


On Dec 4 2006 07:30, Josef 'Jeff' Sipek wrote:
>+ if (!hidden_dentry) {
>+ /* if hidden_dentry is NULL, create the entire
>+ * dentry directory structure in branch 'bindex'.
>+ * hidden_dentry will NOT be null when bindex == bstart
>+ * because lookup passed as a negative unionfs dentry
>+ * pointing to a lone negative underlying dentry */
>+ hidden_dentry = create_parents(parent, dentry, bindex);

Someone refresh me: what's the correct[preferred] kdoc style?

(A)
/* Lorem ipsum dolor sit amet, consectetur
* adipisicing elit, sed do eiusmod tempor
* incididunt ut labore et dolore magna aliqua. */

(B)
/* Lorem ipsum dolor sit amet, consectetur
adipisicing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. */

(C)
/* Lorem ipsum dolor sit amet, consectetur
adipisicing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. */


-`J'
--

2006-12-05 21:37:05

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH 22/35] Unionfs: Lookup helper functions


>+/* allocate new dentry private data, free old one if necessary */
>+int new_dentry_private_data(struct dentry *dentry)
>+{
>[...]
>+
>+ spin_unlock(&dentry->d_lock);
>+ return 0;
>+ /* */
>+
>+out_free:
>+ kfree(info->lower_paths);
>+
>+out:
>+ free_dentry_private_data(info);
>+ dentry->d_fsdata = NULL;
>+ spin_unlock(&dentry->d_lock);
>+ return -ENOMEM;
>+}

Whereever I go, ... care to explain the /* */? If it was just a marker
logically belonging to the syntactic sugar, it's missing in the other source
files too.


-`J'
--

2006-12-05 21:39:55

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH 23/35] Unionfs: Main module functions


>+++ b/fs/unionfs/main.c
>+static int init_debug = 0;
>+module_param_named(debug, init_debug, int, S_IRUGO);
>+MODULE_PARM_DESC(debug, "Initial Unionfs debug value.");

I think there is not anything that forbids it being S_IRUGO | S_IWUSR.


>+
>+static int __init init_unionfs_fs(void)
>+{
>+ int err;
>+ printk("Registering unionfs " UNIONFS_VERSION "\n");
>+
>+ if ((err = init_filldir_cache()))
>+ goto out;
>+ if ((err = init_inode_cache()))
>+ goto out;
>+ if ((err = init_dentry_cache()))
>+ goto out;
>+ if ((err = init_sioq()))
>+ goto out;
>+ err = register_filesystem(&unionfs_fs_type);
>+out:
>+ if (err) {
>+ fin_sioq();
>+ destroy_filldir_cache();
>+ destroy_inode_cache();
>+ destroy_dentry_cache();
>+ }
>+ return err;
>+}
>+static void __exit exit_unionfs_fs(void)
>+{

There's that missing white line again :^)

>+ fin_sioq();
>+ destroy_filldir_cache();
>+ destroy_inode_cache();
>+ destroy_dentry_cache();
>+ unregister_filesystem(&unionfs_fs_type);
>+ printk("Completed unionfs module unload.\n");
>+}
>+
>+MODULE_AUTHOR("Erez Zadok, Filesystems and Storage Lab, Stony Brook University"
>+ " (http://www.fsl.cs.sunysb.edu)");
>+MODULE_DESCRIPTION("Unionfs " UNIONFS_VERSION
>+ " (http://www.unionfs.org)");
>+MODULE_LICENSE("GPL");
>+
>+module_init(init_unionfs_fs);
>+module_exit(exit_unionfs_fs);

Like with sioq, unionfs_init/unionfs_exit should suffice.


-`J'
--

2006-12-05 21:47:48

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH 24/35] Unionfs: Readdir state



>+ /* Round it up to the next highest power of two. */
>+ mallocsize--;
>+ mallocsize |= mallocsize >> 1;
>+ mallocsize |= mallocsize >> 2;
>+ mallocsize |= mallocsize >> 4;
>+ mallocsize |= mallocsize >> 8;
>+ mallocsize |= mallocsize >> 16;
>+ mallocsize++;

Interesting idea how to do it. This could be made a kernel-wide function and
be used by other subsystems (they sure do have find-next-power-of-two too). But
that another time another patch.


-`J'
--

2006-12-05 21:51:30

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH 21/35] Unionfs: Inode operations

On Tue, 5 Dec 2006 22:27:10 +0100 (MET)
Jan Engelhardt <[email protected]> wrote:

>
> On Dec 4 2006 07:30, Josef 'Jeff' Sipek wrote:
> >+ if (!hidden_dentry) {
> >+ /* if hidden_dentry is NULL, create the entire
> >+ * dentry directory structure in branch 'bindex'.
> >+ * hidden_dentry will NOT be null when bindex == bstart
> >+ * because lookup passed as a negative unionfs dentry
> >+ * pointing to a lone negative underlying dentry */
> >+ hidden_dentry = create_parents(parent, dentry, bindex);
>
> Someone refresh me: what's the correct[preferred] kdoc style?

This isn't part of kernel-doc, if that's what you mean.

> (A)
> /* Lorem ipsum dolor sit amet, consectetur
> * adipisicing elit, sed do eiusmod tempor
> * incididunt ut labore et dolore magna aliqua. */
>
> (B)
> /* Lorem ipsum dolor sit amet, consectetur
> adipisicing elit, sed do eiusmod tempor
> incididunt ut labore et dolore magna aliqua. */
>
> (C)
> /* Lorem ipsum dolor sit amet, consectetur
> adipisicing elit, sed do eiusmod tempor incididunt
> ut labore et dolore magna aliqua. */

You forgot (D), (E), (F), (G) and a whole lot more besides.

It doesn't matter a lot what we do, but we should do it one way and not 38
ways.

Documentation/CodingStyle doesn't mention commenting at all (eyes roll
heavenwards).

This

/*
* Lorem ipsum dolor sit amet, consectetur
* adipisicing elit, sed do eiusmod tempor
* incididunt ut labore et dolore magna aliqua.
*/

is probably the most common, and is what I use when forced to descrog
comments.

2006-12-05 21:53:26

by David Miller

[permalink] [raw]
Subject: Re: [PATCH 21/35] Unionfs: Inode operations

From: Andrew Morton <[email protected]>
Date: Tue, 5 Dec 2006 13:50:17 -0800

> This
>
> /*
> * Lorem ipsum dolor sit amet, consectetur
> * adipisicing elit, sed do eiusmod tempor
> * incididunt ut labore et dolore magna aliqua.
> */
>
> is probably the most common, and is what I use when forced to descrog
> comments.

This is the style I try to use and enforce too.

2006-12-06 04:13:04

by Randy Dunlap

[permalink] [raw]
Subject: Re: [PATCH 21/35] Unionfs: Inode operations

On Tue, 5 Dec 2006 13:50:17 -0800 Andrew Morton wrote:

> On Tue, 5 Dec 2006 22:27:10 +0100 (MET)
> Jan Engelhardt <[email protected]> wrote:
>
> > Someone refresh me: what's the correct[preferred] kdoc style?
>
> This isn't part of kernel-doc, if that's what you mean.
>
> > (A)
> > /* Lorem ipsum dolor sit amet, consectetur
> > * adipisicing elit, sed do eiusmod tempor
> > * incididunt ut labore et dolore magna aliqua. */
> >
> > (B)
> > /* Lorem ipsum dolor sit amet, consectetur
> > adipisicing elit, sed do eiusmod tempor
> > incididunt ut labore et dolore magna aliqua. */
> >
> > (C)
> > /* Lorem ipsum dolor sit amet, consectetur
> > adipisicing elit, sed do eiusmod tempor incididunt
> > ut labore et dolore magna aliqua. */
>
> You forgot (D), (E), (F), (G) and a whole lot more besides.
>
> It doesn't matter a lot what we do, but we should do it one way and not 38
> ways.
>
> Documentation/CodingStyle doesn't mention commenting at all (eyes roll
> heavenwards).

I have several (probably 5-6) Doc/CodingStyle changes in my WIP
(work-in-progress) folder that I will do in the next few days.



> This
>
> /*
> * Lorem ipsum dolor sit amet, consectetur
> * adipisicing elit, sed do eiusmod tempor
> * incididunt ut labore et dolore magna aliqua.
> */
>
> is probably the most common, and is what I use when forced to descrog
> comments.

---
~Randy

2006-12-06 17:36:08

by Josef Sipek

[permalink] [raw]
Subject: Re: [PATCH 26/35] Unionfs: Privileged operations workqueue

On Tue, Dec 05, 2006 at 02:50:13PM -0500, Josef Sipek wrote:
> On Tue, Dec 05, 2006 at 08:27:51PM +0100, Jan Engelhardt wrote:
> >
> > On Dec 4 2006 07:30, Josef 'Jeff' Sipek wrote:
> > >+#include "union.h"
> > >+
> > >+struct workqueue_struct *sioq;
> > >+
> > >+int __init init_sioq(void)
> >
> > Although it's just me, I'd prefer sioq_init(), sioq_exit(),
> > sioq_run(), etc. to go in hand with what most drivers use as naming
> > (i.e. <modulename> "_" <function>).
>
> That makes sense.

Hrm. Looking at the code, I noticed that the opposite is true:

destroy_filldir_cache();
destroy_inode_cache();
destroy_dentry_cache();
unregister_filesystem(&unionfs_fs_type);

The last one in particular...

> > >+void __unionfs_mknod(void *data)
> > >+{
> > >+ struct sioq_args *args = data;
> > >+ struct mknod_args *m = &args->mknod;
> >
> > Care to make that: const struct mknod_args *m = &args->mknod;?
> > (Same for other places)
>
> Right.

If I make the *args = data line const, then gcc (4.1) yells about modifying
a const variable 3 lines down..

args->err = vfs_mknod(m->parent, m->dentry, m->mode, m->dev);

Sure, I could cast, but that seems like adding cruft for no good reason.

Josef "Jeff" Sipek.

--
Only two things are infinite, the universe and human stupidity, and I'm not
sure about the former.
- Albert Einstein

2006-12-06 18:56:17

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH 26/35] Unionfs: Privileged operations workqueue


On Dec 6 2006 12:32, Josef Sipek wrote:
>> > >+int __init init_sioq(void)
>> >
>> > Although it's just me, I'd prefer sioq_init(), sioq_exit(),
>> > sioq_run(), etc. to go in hand with what most drivers use as naming
>> > (i.e. <modulename> "_" <function>).
>>
>> That makes sense.
>
>Hrm. Looking at the code, I noticed that the opposite is true:
>
>destroy_filldir_cache();
>destroy_inode_cache();
>destroy_dentry_cache();
>unregister_filesystem(&unionfs_fs_type);
>
>The last one in particular...

I smell a big conspiracy! So yet again it's mixed mixed

fs$ grep __init */*.c | grep -v ' init_'
sysfs/mount.c:int __init sysfs_init(void)
sysv/inode.c:int __init sysv_init_icache(void)
proc/vmcore.c:static int __init vmcore_init(void)
proc/nommu.c:static int __init proc_nommu_init(void)
proc/proc_misc.c:void __init proc_misc_init(void)
proc/proc_tty.c:void __init proc_tty_init(void)
proc/root.c:void __init proc_root_init(void)


>
>> > >+void __unionfs_mknod(void *data)
>> > >+{
>> > >+ struct sioq_args *args = data;
>> > >+ struct mknod_args *m = &args->mknod;
>> >
>> > Care to make that: const struct mknod_args *m = &args->mknod;?
>> > (Same for other places)
>>
>> Right.
>
>If I make the *args = data line const, then gcc (4.1) yells about modifying
>a const variable 3 lines down..
>
>args->err = vfs_mknod(m->parent, m->dentry, m->mode, m->dev);
>
>Sure, I could cast, but that seems like adding cruft for no good reason.

No I despise casts more than missing consts. Why would gcc throw a warning?
Let's take this super simple program

<<<
struct inode;
struct dentry;

struct mknod_args {
struct inode *parent;
struct dentry *dentry;
int mode;
int dev;
};

extern int vfs_mknod(struct inode *, struct dentry *, int, int /*dev_t*/);

int main(void) {
const struct mknod_args *m;
vfs_mknod(m->parent, m->dentry, m->mode, m->dev);
return 0;
}
>>>

As undefined-behavior as it looks, it's got the const and vfs_mknod, as well as
an approximation of dev_t. It throws no warnings when compiled with `gcc -Wall
-c test.c`. Did I miss something?



-`J'
--

2006-12-07 11:06:34

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH 29/35] Unionfs: Superblock operations


On Dec 4 2006 07:31, Josef 'Jeff' Sipek wrote:
>+int init_inode_cache(void)
>+{
>+ int err = 0;
>+
>+ unionfs_inode_cachep =
>+ kmem_cache_create("unionfs_inode_cache",
>+ sizeof(struct unionfs_inode_info), 0,
>+ SLAB_RECLAIM_ACCOUNT, init_once, NULL);
>+ if (!unionfs_inode_cachep)
>+ err = -ENOMEM;
>+ return err;
>+}
>+
>+void destroy_inode_cache(void)
>+{
>+ if (unionfs_inode_cachep)
>+ kmem_cache_destroy(unionfs_inode_cachep);
>+}

These function names could possibly clash with same-named functions
elsewhere, because they are not marked static. (This shows when two
features with same function names are compiled as y.) I grepped
through a 2.6.18 tree with unionfs, and unionfs was the only one
having this function name at this time. I would suggest prefixing it
somehow.


-`J'
--

2006-12-07 11:08:42

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH 32/35] Unionfs: Include file


On Dec 4 2006 07:31, Josef 'Jeff' Sipek wrote:

>Date: Mon, 4 Dec 2006 07:31:05 -0500
>From: Josef 'Jeff' Sipek <[email protected]>
>To: [email protected]
>Cc: [email protected], [email protected], [email protected], [email protected],
> [email protected], [email protected],
> Josef Jeff Sipek <[email protected]>
>Subject: [PATCH 32/35] Unionfs: Include file
>
>From: Josef "Jeff" Sipek <[email protected]>
>
>Global include file - can be included from userspace by utilities.
>
>Signed-off-by: Josef "Jeff" Sipek <[email protected]>
>Signed-off-by: David Quigley <[email protected]>
>Signed-off-by: Erez Zadok <[email protected]>
>---
> include/linux/union_fs.h | 20 ++++++++++++++++++++
> 1 files changed, 20 insertions(+), 0 deletions(-)
>
>diff --git a/include/linux/union_fs.h b/include/linux/union_fs.h
>new file mode 100644
>index 0000000..e76d3cf
>--- /dev/null
>+++ b/include/linux/union_fs.h
>@@ -0,0 +1,20 @@
>+#ifndef _LINUX_UNION_FS_H
>+#define _LINUX_UNION_FS_H
>+
>+#define UNIONFS_VERSION "2.0"
>+/*
>+ * DEFINITIONS FOR USER AND KERNEL CODE:
>+ * (Note: ioctl numbers 1--9 are reserved for fistgen, the rest
>+ * are auto-generated automatically based on the user's .fist file.)
>+ */

FIST is not used here so is this comment still appropriate?


>+# define UNIONFS_IOCTL_INCGEN _IOR(0x15, 11, int)
>+# define UNIONFS_IOCTL_QUERYFILE _IOR(0x15, 15, int)
>+
>+/* We don't support normal remount, but unionctl uses it. */
>+# define UNIONFS_REMOUNT_MAGIC 0x4a5a4380
>+
>+/* should be at least LAST_USED_UNIONFS_PERMISSION<<1 */
>+#define MAY_NFSRO 16
>+
>+#endif /* _LINUX_UNIONFS_H */
>+

-`J'
--

2006-12-07 11:07:57

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH 30/35] Unionfs: Helper macros/inlines


On Dec 4 2006 07:31, Josef 'Jeff' Sipek wrote:
>+#define ibstart(ino) (UNIONFS_I(ino)->bstart)
>+#define ibend(ino) (UNIONFS_I(ino)->bend)
>+
>+#define sbend(sb) UNIONFS_SB(sb)->bend

If you () ibstart/ibend, you may want to do the same for sbend.


-`J'
--

2006-12-07 11:11:49

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH 35/35] Unionfs: Extended Attributes support


On Dec 4 2006 07:31, Josef 'Jeff' Sipek wrote:

If the makefile contains

>--- a/fs/unionfs/Makefile
>+++ b/fs/unionfs/Makefile
>@@ -3,3 +3,5 @@ obj-$(CONFIG_UNION_FS) += unionfs.o
> unionfs-y := subr.o dentry.o file.o inode.o main.o super.o \
> stale_inode.o branchman.o rdstate.o copyup.o dirhelper.o \
> rename.o unlink.o lookup.o commonfops.o dirfops.o sioq.o
>+
>+unionfs-$(CONFIG_UNION_FS_XATTR) += xattr.o

then you don't need

>--- a/fs/unionfs/copyup.c
>+++ b/fs/unionfs/copyup.c
>@@ -18,6 +18,75 @@
>
> #include "union.h"
>
>+#ifdef CONFIG_UNION_FS_XATTR

^^ this, do you?.


>+#include "union.h"
>+
>+/* This is lifted from fs/xattr.c */
>+void *xattr_alloc(size_t size, size_t limit)

Same as for init_inode_cache, possibly prefix with unionfs.


-`J'
--

2006-12-07 14:05:23

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [PATCH 21/35] Unionfs: Inode operations

On Tue, Dec 05, 2006 at 01:50:17PM -0800, Andrew Morton wrote:
> This
>
> /*
> * Lorem ipsum dolor sit amet, consectetur
> * adipisicing elit, sed do eiusmod tempor
> * incididunt ut labore et dolore magna aliqua.
> */
>
> is probably the most common, and is what I use when forced to descrog
> comments.

This what I normally do by default, unless it's a one-line comment, in
which case my preference is usually for this:

/* Lorem ipsum dolor sit amet, consectetur */

I'm not convinced we really do _need_ to standardize on comment styles
(I can foresee thousands and thousands of trivial patches being
submitted and we'd probably be better off encouraging people to spend
time actually improving the documentation instead of reformatting it :-),
but if were going to standardize, that would be my vote.

- Ted


2006-12-08 16:01:32

by Josef Sipek

[permalink] [raw]
Subject: Re: [PATCH 26/35] Unionfs: Privileged operations workqueue

On Fri, Dec 08, 2006 at 11:38:13AM +0100, Jan Engelhardt wrote:
>
> On Dec 7 2006 21:17, Josef Sipek wrote:
> >> >> > >+void __unionfs_mknod(void *data)
> >> >> > >+{
> >> >> > >+ struct sioq_args *args = data;
> >> >> > >+ struct mknod_args *m = &args->mknod;
>
> ...
> ||||| vfs_mknod(m->parent, m->dentry, m->mode, m->dev);
>
> >> >If I make the *args = data line const, then gcc (4.1) yells about modifying
> >> >a const variable 3 lines down..
> >> >
> >> >args->err = vfs_mknod(m->parent, m->dentry, m->mode, m->dev);
> >> >
> >> >Sure, I could cast, but that seems like adding cruft for no good reason.
> >>
> >> No I despise casts more than missing consts. Why would gcc throw a warning?
> >> Let's take this super simple program
> >
> >No, this program doesn't tickle the problem.. Try to compile this one:
>
> The members of m (i.e. m->*) are not modified as for as __unionfs_mknod goes.
> vfs_mknod may only modify the members of m->parent (i.e. m->parent->*)

Yes they are. The return value is passed through a member of m.

Josef "Jeff" Sipek.

--
The box said "Windows XP or better required". So I installed Linux.

2006-12-08 17:11:50

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH 26/35] Unionfs: Privileged operations workqueue


On Dec 8 2006 11:00, Josef Sipek wrote:

+void __unionfs_mkdir(void *data)
+{
+ struct sioq_args *args = data;
+ struct mkdir_args *m = &args->mkdir;
+
+ args->err = vfs_mkdir(m->parent, m->dentry, m->mode);
+ complete(&args->comp);
+}

>> The members of m (i.e. m->*) are not modified as for as __unionfs_mknod goes.
>> vfs_mknod may only modify the members of m->parent (i.e. m->parent->*)
>
>Yes they are. The return value is passed through a member of m.

Where - where do you see that m->parent, m->dentry or m->mode are modified?
(The original submission is above.)


-`J'
--

2006-12-08 11:02:21

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH 35/35] Unionfs: Extended Attributes support



On Dec 8 2006 00:35, Josef Sipek wrote:
>>
>> >--- a/fs/unionfs/copyup.c
>> >+++ b/fs/unionfs/copyup.c
>> >@@ -18,6 +18,75 @@
>> >
>> > #include "union.h"
>> >
>> >+#ifdef CONFIG_UNION_FS_XATTR
>>
>> ^^ this, do you?.
>
>Beware, copyup.c gets compiled all the time even when you don't have xattrs
>enabled.

Oops, I thought this was xattr.c. Sorry for the noise.

-`J'
--

2006-12-07 22:42:04

by Randy Dunlap

[permalink] [raw]
Subject: Re: [PATCH 21/35] Unionfs: Inode operations

On Thu, 7 Dec 2006 09:07:15 -0800 Andrew Morton wrote:

> On Thu, 7 Dec 2006 09:04:27 -0500
> Theodore Tso <[email protected]> wrote:
>
> > On Tue, Dec 05, 2006 at 01:50:17PM -0800, Andrew Morton wrote:
> > > This
> > >
> > > /*
> > > * Lorem ipsum dolor sit amet, consectetur
> > > * adipisicing elit, sed do eiusmod tempor
> > > * incididunt ut labore et dolore magna aliqua.
> > > */
> > >
> > > is probably the most common, and is what I use when forced to descrog
> > > comments.
> >
> > This what I normally do by default, unless it's a one-line comment, in
> > which case my preference is usually for this:
> >
> > /* Lorem ipsum dolor sit amet, consectetur */
>
> yup.
>
> > I'm not convinced we really do _need_ to standardize on comment styles
> > (I can foresee thousands and thousands of trivial patches being
> > submitted and we'd probably be better off encouraging people to spend
> > time actually improving the documentation instead of reformatting it :-),
> > but if were going to standardize, that would be my vote.
>
> Sure. comment-relaying-out patches would not be particularly welcome ;)

I third that. Not a janitors (or anyone's) task/busy-work.

---
~Randy

2006-12-07 17:22:57

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH 21/35] Unionfs: Inode operations

On Thu, 7 Dec 2006 09:04:27 -0500
Theodore Tso <[email protected]> wrote:

> On Tue, Dec 05, 2006 at 01:50:17PM -0800, Andrew Morton wrote:
> > This
> >
> > /*
> > * Lorem ipsum dolor sit amet, consectetur
> > * adipisicing elit, sed do eiusmod tempor
> > * incididunt ut labore et dolore magna aliqua.
> > */
> >
> > is probably the most common, and is what I use when forced to descrog
> > comments.
>
> This what I normally do by default, unless it's a one-line comment, in
> which case my preference is usually for this:
>
> /* Lorem ipsum dolor sit amet, consectetur */

yup.

> I'm not convinced we really do _need_ to standardize on comment styles
> (I can foresee thousands and thousands of trivial patches being
> submitted and we'd probably be better off encouraging people to spend
> time actually improving the documentation instead of reformatting it :-),
> but if were going to standardize, that would be my vote.

Sure. comment-relaying-out patches would not be particularly welcome ;)

2006-12-08 10:47:26

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH 26/35] Unionfs: Privileged operations workqueue


On Dec 7 2006 21:17, Josef Sipek wrote:
>> >> > >+void __unionfs_mknod(void *data)
>> >> > >+{
>> >> > >+ struct sioq_args *args = data;
>> >> > >+ struct mknod_args *m = &args->mknod;

...
||||| vfs_mknod(m->parent, m->dentry, m->mode, m->dev);

>> >If I make the *args = data line const, then gcc (4.1) yells about modifying
>> >a const variable 3 lines down..
>> >
>> >args->err = vfs_mknod(m->parent, m->dentry, m->mode, m->dev);
>> >
>> >Sure, I could cast, but that seems like adding cruft for no good reason.
>>
>> No I despise casts more than missing consts. Why would gcc throw a warning?
>> Let's take this super simple program
>
>No, this program doesn't tickle the problem.. Try to compile this one:

The members of m (i.e. m->*) are not modified as for as __unionfs_mknod goes.
vfs_mknod may only modify the members of m->parent (i.e. m->parent->*)

>
><<<
>struct mknod_args {
> int mode;
> int dev;
>};
>
>void __mknod(const void *data)
>{
> const struct mknod_args *args = data;
> args->mode = 0;
>}
>
>int main(void) {
> const struct mknod_args *m;
> __mknod(m);
> return 0;
>}
>>>>
>
>$ gcc -Wall -c test.c
>test.c: In function âmknodâtest.c:10: error: assignment of read-only location
>
>
>Josef "Jeff" Sipek.
>
>--
>Reality is merely an illusion, albeit a very persistent one.
> - Albert Einstein
>

-`J'
--

2006-12-08 04:56:03

by Josef Sipek

[permalink] [raw]
Subject: Re: [PATCH 16/35] Unionfs: Copyup Functionality

On Tue, Dec 05, 2006 at 10:09:19PM +0100, Jan Engelhardt wrote:
>
> On Dec 4 2006 07:30, Josef 'Jeff' Sipek wrote:
> >+/* Determine the mode based on the copyup flags, and the existing dentry. */
> >+static int copyup_permissions(struct super_block *sb,
> >+ struct dentry *old_hidden_dentry,
> >+ struct dentry *new_hidden_dentry)
> >+{
> >+ struct inode *i = old_hidden_dentry->d_inode;
>
> Screams for constification. (Or rather I do.)

I'm not following.

> >+{
> >+ int err = 0;
> >+ umode_t old_mode = old_hidden_dentry->d_inode->i_mode;
>
> Generel question for everybody: Why do we have two same (at least on i386
> that's the case) types, umode_t and mode_t?

No idea.

> >+ } else if (S_ISBLK(old_mode)
> >+ || S_ISCHR(old_mode)
> >+ || S_ISFIFO(old_mode)
> >+ || S_ISSOCK(old_mode)) {
> >+ args.mknod.parent = new_hidden_parent_dentry->d_inode;
> >+ args.mknod.dentry = new_hidden_dentry;
> >+ args.mknod.mode = old_mode;
>
> I'd say the indent got screwed up, and it's not a mailer thing.

Right.

> >+ input_file = dentry_open(old_hidden_dentry,
> >+ unionfs_lower_mnt_idx(dentry, old_bindex), O_RDONLY | O_LARGEFILE);
> >+ if (IS_ERR(input_file)) {
...
> >+ output_file = dentry_open(new_hidden_dentry,
> >+ unionfs_lower_mnt_idx(dentry, new_bindex), O_WRONLY | O_LARGEFILE);
>
> Here we got an 80-column buster.

/me whistles innocently

> >+ /* TODO: should we reset the error to something like -EIO? */
>
> Handle it :) - if it does not take a paper.

I'm not even sure if we want to reset it to begin with.

If we don't reset, the user may get some non-sensical errors, but on the
other hand, if we reset to EIO, we guarantee that the user will get a
"confusing" error message.

Josef "Jeff" Sipek.

--
All science is either physics or stamp collecting.
- Ernest Rutherford

2006-12-08 04:17:31

by Josef Sipek

[permalink] [raw]
Subject: Re: [PATCH 15/35] Unionfs: Common file operations

On Tue, Dec 05, 2006 at 10:02:10PM +0100, Jan Engelhardt wrote:
> On Dec 4 2006 07:30, Josef 'Jeff' Sipek wrote:
> >+long unionfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> >+{
> >+ long err;
> >+
> >+ if ((err = unionfs_file_revalidate(file, 1)))
> >+ goto out;
> >+
> >+ /* check if asked for local commands */
> >+ switch (cmd) {
> >+ case UNIONFS_IOCTL_INCGEN:
> >+ /* Increment the superblock generation count */
> >+ err = -EACCES;
> >+ if (!capable(CAP_SYS_ADMIN))
> >+ goto out;
> >+ err = unionfs_ioctl_incgen(file, cmd, arg);
> >+ break;
> >+
> >+ case UNIONFS_IOCTL_QUERYFILE:
> >+ /* Return list of branches containing the given file */
> >+ err = unionfs_ioctl_queryfile(file, cmd, arg);
> >+ break;
> >+
> >+ default:
> >+ /* pass the ioctl down */
> >+ err = do_ioctl(file, cmd, arg);
> >+ break;
> >+ }
> >+
> >+out:
> >+ return err;
> >+}
>
>
> I think there was an ioctl for files to find out where a particular
> file lives on disk.

That's the UNIONFS_IOCTL_QUERYFILE case.

> Do you think unionfs should handle it and return
> something more or less meaningful?

It is a very useful functionality for any stackable filesystem which may
store files on one of several branches. Do I think that it should be
factored out of Unionfs into fsstack or similar? Not at the moment. If there
seems to be a need for it, then I'd gladly move it out of Unionfs, but until
then I don't see a reason to do anything special.

Josef "Jeff" Sipek.

--
Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are, by
definition, not smart enough to debug it.
- Brian W. Kernighan

2006-12-08 11:01:05

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH 15/35] Unionfs: Common file operations


On Dec 7 2006 23:16, Josef Sipek wrote:
>> I think there was an ioctl for files to find out where a particular
>> file lives on disk.
>
>That's the UNIONFS_IOCTL_QUERYFILE case.

No I meant something that works on all filesystems, something generic, not
unionfs-based.


-`J'
--

2006-12-08 02:17:44

by Josef Sipek

[permalink] [raw]
Subject: Re: [PATCH 26/35] Unionfs: Privileged operations workqueue

On Wed, Dec 06, 2006 at 07:46:50PM +0100, Jan Engelhardt wrote:
> I smell a big conspiracy! So yet again it's mixed mixed
>
> fs$ grep __init */*.c | grep -v ' init_'
> sysfs/mount.c:int __init sysfs_init(void)
> sysv/inode.c:int __init sysv_init_icache(void)
> proc/vmcore.c:static int __init vmcore_init(void)
> proc/nommu.c:static int __init proc_nommu_init(void)
> proc/proc_misc.c:void __init proc_misc_init(void)
> proc/proc_tty.c:void __init proc_tty_init(void)
> proc/root.c:void __init proc_root_init(void)

Yep.

> >> > >+void __unionfs_mknod(void *data)
> >> > >+{
> >> > >+ struct sioq_args *args = data;
> >> > >+ struct mknod_args *m = &args->mknod;
> >> >
> >> > Care to make that: const struct mknod_args *m = &args->mknod;?
> >> > (Same for other places)
> >>
> >> Right.
> >
> >If I make the *args = data line const, then gcc (4.1) yells about modifying
> >a const variable 3 lines down..
> >
> >args->err = vfs_mknod(m->parent, m->dentry, m->mode, m->dev);
> >
> >Sure, I could cast, but that seems like adding cruft for no good reason.
>
> No I despise casts more than missing consts. Why would gcc throw a warning?
> Let's take this super simple program

No, this program doesn't tickle the problem.. Try to compile this one:

<<<
struct mknod_args {
int mode;
int dev;
};

void __mknod(const void *data)
{
const struct mknod_args *args = data;
args->mode = 0;
}

int main(void) {
const struct mknod_args *m;
__mknod(m);
return 0;
}
>>>

$ gcc -Wall -c test.c
test.c: In function ?mknod?test.c:10: error: assignment of read-only location


Josef "Jeff" Sipek.

--
Reality is merely an illusion, albeit a very persistent one.
- Albert Einstein

2006-12-08 05:36:37

by Josef Sipek

[permalink] [raw]
Subject: Re: [PATCH 35/35] Unionfs: Extended Attributes support

On Thu, Dec 07, 2006 at 12:04:43PM +0100, Jan Engelhardt wrote:
>
> On Dec 4 2006 07:31, Josef 'Jeff' Sipek wrote:
>
> If the makefile contains
>
> >--- a/fs/unionfs/Makefile
> >+++ b/fs/unionfs/Makefile
> >@@ -3,3 +3,5 @@ obj-$(CONFIG_UNION_FS) += unionfs.o
> > unionfs-y := subr.o dentry.o file.o inode.o main.o super.o \
> > stale_inode.o branchman.o rdstate.o copyup.o dirhelper.o \
> > rename.o unlink.o lookup.o commonfops.o dirfops.o sioq.o
> >+
> >+unionfs-$(CONFIG_UNION_FS_XATTR) += xattr.o
>
> then you don't need
>
> >--- a/fs/unionfs/copyup.c
> >+++ b/fs/unionfs/copyup.c
> >@@ -18,6 +18,75 @@
> >
> > #include "union.h"
> >
> >+#ifdef CONFIG_UNION_FS_XATTR
>
> ^^ this, do you?.

Beware, copyup.c gets compiled all the time even when you don't have xattrs
enabled.

> >+#include "union.h"
> >+
> >+/* This is lifted from fs/xattr.c */
> >+void *xattr_alloc(size_t size, size_t limit)
>
> Same as for init_inode_cache, possibly prefix with unionfs.

Right.

Josef "Jeff" Sipek.

--
A computer without Microsoft is like chocolate cake without mustard.

2006-12-08 17:43:49

by Josef Sipek

[permalink] [raw]
Subject: Re: [PATCH 26/35] Unionfs: Privileged operations workqueue

On Fri, Dec 08, 2006 at 06:02:28PM +0100, Jan Engelhardt wrote:
>
> On Dec 8 2006 11:00, Josef Sipek wrote:
>
> +void __unionfs_mkdir(void *data)
> +{
> + struct sioq_args *args = data;
> + struct mkdir_args *m = &args->mkdir;
> +
> + args->err = vfs_mkdir(m->parent, m->dentry, m->mode);
> + complete(&args->comp);
> +}
>
> >> The members of m (i.e. m->*) are not modified as for as __unionfs_mknod goes.
> >> vfs_mknod may only modify the members of m->parent (i.e. m->parent->*)
> >
> >Yes they are. The return value is passed through a member of m.
>
> Where - where do you see that m->parent, m->dentry or m->mode are modified?
> (The original submission is above.)

args->err is modified. If args is declared const, gcc complains. Sorry, the
example code I sent was a bit mis-leading (different from the actual code
above), but it triggered the same check in gcc.

Josef "Jeff" Sipek.

--
It used to be said [...] that AIX looks like one space alien discovered
Unix, and described it to another different space alien who then implemented
AIX. But their universal translators were broken and they'd had to gesture a
lot.
- Paul Tomblin

2006-12-08 18:13:48

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH 26/35] Unionfs: Privileged operations workqueue


On Dec 8 2006 12:43, Josef Sipek wrote:
>> On Dec 8 2006 11:00, Josef Sipek wrote:
>>
>> +void __unionfs_mkdir(void *data)
>> +{
>> + struct sioq_args *args = data;
>> + struct mkdir_args *m = &args->mkdir;
>> +
>> + args->err = vfs_mkdir(m->parent, m->dentry, m->mode);
>> + complete(&args->comp);
>> +}
>>
>> >> The members of m (i.e. m->*) are not modified as for as __unionfs_mknod goes.
>> >> vfs_mknod may only modify the members of m->parent (i.e. m->parent->*)
>> >
>> >Yes they are. The return value is passed through a member of m.
>>
>> Where - where do you see that m->parent, m->dentry or m->mode are modified?
>> (The original submission is above.)
>
>args->err is modified. If args is declared const, gcc complains.

I never said making "args" const, but
rather [-> http://lkml.org/lkml/2006/12/5/210 ] I said:

"Care to make that: const struct mknod_args *m = &args->mknod;?"


-`J'
--

2006-12-08 18:25:42

by Josef Sipek

[permalink] [raw]
Subject: Re: [PATCH 26/35] Unionfs: Privileged operations workqueue

On Fri, Dec 08, 2006 at 07:03:00PM +0100, Jan Engelhardt wrote:
> On Dec 8 2006 12:43, Josef Sipek wrote:
...
> >args->err is modified. If args is declared const, gcc complains.
>
> I never said making "args" const, but
> rather [-> http://lkml.org/lkml/2006/12/5/210 ] I said:
>
> "Care to make that: const struct mknod_args *m = &args->mknod;?"

Eeek. Sorry.

Josef "Jeff" Sipek.

--
NT is to UNIX what a doughnut is to a particle accelerator.