2008-07-30 02:47:18

by Erez Zadok

[permalink] [raw]
Subject: [GIT PULL -mm] 00/19 fsstack+Unionfs updates/fixes/cleanups


The following is a series of patchsets related to Unionfs, and one for
fsstack. This series includes four types of changes:

1. Fold in Hugh Dickins's fsstack_copy_inode_size fix. Since it depends on
code in unionfs, I'm temporarily folding this patch into my tree. I'll
extract it into a separate patch which can be applied cleanly against
2.6.27-rc in the next few days.

2. Assorted fixes to adderess VFS API changes in 2.6.26/2.6.27-rc1:
->symlink, ->permission, nameidata intent flags changed, and
kmem_cache_create prototype changed.

3. Extracted all whiteout related code into a separate file, and cleaned it
up a lot. Now the main unionfs code calls somewhat generic functions
such as "check if there's a whiteout" or "create a whiteout" and the
like. Once Bharata whiteout code makes it in, it would be easy to make
unionfs use filesystem-native whiteout support.

4. Reworked much of the lookup code in unionfs to ensure that we have a
vfsmount at every lookup stage, esp. when crossing directories. In
particular, started using vfs_path_lookup instead of lookup_one_len.
These changes are a first but important step towards getting rid of all
vfs_* calls within unionfs in preparation for having Miklos's path_*
calls.

There were also a few smaller bug fixes: memleak (also from Hugh Dickins),
some cache coherency fixes, and a fix to an LTP/rename13 regression.

These patches were tested (where appropriate) on v2.6.27-rc1, MM, as well as
the backports to 2.6.{26,25,24,23,22,21,20,19,18,9} on ext2/3/4, xfs,
reiserfs, nfs2/3/4, jffs2, ramfs, tmpfs, cramfs, and squashfs (where
available). Also tested with LTP-full-20080630 and with a continuous
parallel kernel compile (while forcing cache flushing, manipulating lower
branches, etc.). See http://unionfs.filesystems.org/ to download
back-ported unionfs code.

Please pull from the 'master' branch of
git://git.kernel.org/pub/scm/linux/kernel/git/ezk/unionfs.git

to receive the following:

Erez Zadok (18):
Unionfs: simplify the macros used to get/set the dentry start/end branches
Unionfs: move a rename helper closer to rename code
Unionfs: create and consolidate helpers to iput lower objects
Unionfs: create and consolidate helpers to path-put lower objects
Unionfs: simplify stale-inode detection code
Unionfs: overhaul whiteout code
Unionfs: lookup overhaul using vfs_path_lookup
Unionfs: free lower paths array when destroying dentry's private data
Unionfs: cache coherency fixes
Unionfs: remove old lookup code
Unionfs: update maintainers
Unionfs: update copyrights
Unionfs: minor checkpatch fixes
Unionfs: properly hash newly created inodes
Unionfs: symlink no longer takes a mode parameter
Unionfs: permission no longer takes a nameidata parameter
Unionfs: LOOKUP_ACCESS intent no longer exists
Unionfs: use new kmem_cache_create constructor prototype

Hugh Dickins (1):
LTP's iogen01 doio tests used to hang nicely on 32-bit SMP when /tmp was a

MAINTAINERS | 3
fs/stack.c | 58 ++-
fs/unionfs/Makefile | 2
fs/unionfs/commonfops.c | 32 -
fs/unionfs/copyup.c | 45 +-
fs/unionfs/debug.c | 8
fs/unionfs/dentry.c | 123 +++----
fs/unionfs/dirfops.c | 28 -
fs/unionfs/dirhelper.c | 130 -------
fs/unionfs/fanout.h | 158 ++++++---
fs/unionfs/file.c | 6
fs/unionfs/inode.c | 225 ++-----------
fs/unionfs/lookup.c | 769 ++++++++++++++++++++---------------------------
fs/unionfs/main.c | 40 --
fs/unionfs/mmap.c | 6
fs/unionfs/rdstate.c | 6
fs/unionfs/rename.c | 206 ++++--------
fs/unionfs/sioq.c | 26 -
fs/unionfs/sioq.h | 7
fs/unionfs/subr.c | 209 ------------
fs/unionfs/super.c | 22 -
fs/unionfs/union.h | 58 +--
fs/unionfs/unlink.c | 28 -
fs/unionfs/whiteout.c | 577 +++++++++++++++++++++++++++++++++++
fs/unionfs/xattr.c | 6
include/linux/fs_stack.h | 3
include/linux/union_fs.h | 6
27 files changed, 1384 insertions(+), 1403 deletions(-)

Thanks.
---
Erez Zadok
[email protected]


2008-07-30 02:44:37

by Erez Zadok

[permalink] [raw]
Subject: [PATCH 14/19] Unionfs: minor checkpatch fixes

Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/main.c | 12 ------------
fs/unionfs/super.c | 4 ++--
2 files changed, 2 insertions(+), 14 deletions(-)

diff --git a/fs/unionfs/main.c b/fs/unionfs/main.c
index 6484e2b..0cdd50b 100644
--- a/fs/unionfs/main.c
+++ b/fs/unionfs/main.c
@@ -459,8 +459,6 @@ static struct unionfs_dentry_info *unionfs_parse_options(

while ((optname = strsep(&options, ",")) != NULL) {
char *optarg;
- char *endptr;
- int intval;

if (!optname || !*optname)
continue;
@@ -493,16 +491,6 @@ static struct unionfs_dentry_info *unionfs_parse_options(
continue;
}

- /* All of these options require an integer argument. */
- intval = simple_strtoul(optarg, &endptr, 0);
- if (*endptr) {
- printk(KERN_ERR
- "unionfs: invalid %s option '%s'\n",
- optname, optarg);
- err = -EINVAL;
- goto out_error;
- }
-
err = -EINVAL;
printk(KERN_ERR
"unionfs: unrecognized option '%s'\n", optname);
diff --git a/fs/unionfs/super.c b/fs/unionfs/super.c
index f163c08..1f4b3f4 100644
--- a/fs/unionfs/super.c
+++ b/fs/unionfs/super.c
@@ -33,8 +33,8 @@ struct inode *unionfs_iget(struct super_block *sb, unsigned long ino)
inode = iget_locked(sb, ino);
if (!inode)
return ERR_PTR(-ENOMEM);
- if (!(inode->i_state & I_NEW))
- return inode;
+ if (!(inode->i_state & I_NEW))
+ return inode;

info = UNIONFS_I(inode);
memset(info, 0, offsetof(struct unionfs_inode_info, vfs_inode));
--
1.5.2.2

2008-07-30 02:44:57

by Erez Zadok

[permalink] [raw]
Subject: [PATCH 10/19] Unionfs: cache coherency fixes

Handle unhashed or silly-renamed lower dentries properly.

Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/commonfops.c | 3 +-
fs/unionfs/debug.c | 2 +-
fs/unionfs/dentry.c | 49 +++++++++++++++++++++++++++++++++++++++++-----
fs/unionfs/main.c | 3 --
4 files changed, 46 insertions(+), 11 deletions(-)

diff --git a/fs/unionfs/commonfops.c b/fs/unionfs/commonfops.c
index df002d5..d64272b 100644
--- a/fs/unionfs/commonfops.c
+++ b/fs/unionfs/commonfops.c
@@ -466,7 +466,8 @@ reval_dentry:
dgen = atomic_read(&UNIONFS_D(dentry)->generation);

if (unlikely(sbgen > dgen)) {
- pr_debug("unionfs: retry (locked) dentry revalidation\n");
+ pr_debug("unionfs: retry (locked) dentry %s revalidation\n",
+ dentry->d_name.name);
schedule();
goto reval_dentry;
}
diff --git a/fs/unionfs/debug.c b/fs/unionfs/debug.c
index d154c32..4da4f0b 100644
--- a/fs/unionfs/debug.c
+++ b/fs/unionfs/debug.c
@@ -313,7 +313,7 @@ check_inode:
}
}
/* check if lower inode is newer than upper one (it shouldn't) */
- if (unlikely(is_newer_lower(dentry))) {
+ if (unlikely(is_newer_lower(dentry) && !is_negative_lower(dentry))) {
PRINT_CALLER(fname, fxn, line);
for (bindex = ibstart(inode); bindex <= ibend(inode);
bindex++) {
diff --git a/fs/unionfs/dentry.c b/fs/unionfs/dentry.c
index cd4611b..dfa4755 100644
--- a/fs/unionfs/dentry.c
+++ b/fs/unionfs/dentry.c
@@ -23,11 +23,16 @@ bool is_negative_lower(const struct dentry *dentry)
int bindex;
struct dentry *lower_dentry;

- BUG_ON(!dentry || dbstart(dentry) < 0);
+ BUG_ON(!dentry);
+ /* cache coherency: check if file was deleted on lower branch */
+ if (dbstart(dentry) < 0)
+ return true;
for (bindex = dbstart(dentry); bindex <= dbend(dentry); bindex++) {
lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
- /* XXX: what if lower_dentry is NULL? */
- if (lower_dentry && lower_dentry->d_inode)
+ /* unhashed (i.e., unlinked) lower dentries don't count */
+ if (lower_dentry && lower_dentry->d_inode &&
+ !d_deleted(lower_dentry) &&
+ !(lower_dentry->d_flags & DCACHE_NFSFS_RENAMED))
return false;
}
return true;
@@ -75,6 +80,7 @@ static bool __unionfs_d_revalidate_one(struct dentry *dentry,
verify_locked(dentry);
verify_locked(dentry->d_parent);

+ sbgen = atomic_read(&UNIONFS_SB(dentry->d_sb)->generation);
/* if the dentry is unhashed, do NOT revalidate */
if (d_deleted(dentry))
goto out;
@@ -83,7 +89,6 @@ static bool __unionfs_d_revalidate_one(struct dentry *dentry,
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
@@ -103,6 +108,21 @@ static bool __unionfs_d_revalidate_one(struct dentry *dentry,
/* Free the pointers for our inodes and this dentry. */
bstart = dbstart(dentry);
bend = dbend(dentry);
+
+ /*
+ * mntput unhashed lower dentries, because those files got
+ * deleted or rmdir'ed.
+ */
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ if (!lower_dentry)
+ continue;
+ if (!d_deleted(lower_dentry) &&
+ !(lower_dentry->d_flags & DCACHE_NFSFS_RENAMED))
+ continue;
+ unionfs_mntput(dentry, bindex);
+ }
+
__dput_lowers(dentry, bstart, bend);
dbstart(dentry) = dbend(dentry) = -1;

@@ -112,8 +132,12 @@ static bool __unionfs_d_revalidate_one(struct dentry *dentry,
iput_lowers_all(dentry->d_inode, true);
}

- result = unionfs_lookup_backend(dentry, &lowernd,
- interpose_flag);
+ if (realloc_dentry_private_data(dentry) != 0) {
+ valid = false;
+ goto out;
+ }
+
+ result = unionfs_lookup_full(dentry, &lowernd, interpose_flag);
if (result) {
if (IS_ERR(result)) {
valid = false;
@@ -179,6 +203,9 @@ static bool __unionfs_d_revalidate_one(struct dentry *dentry,
}

out:
+ if (valid)
+ atomic_set(&UNIONFS_D(dentry)->generation, sbgen);
+
return valid;
}

@@ -241,6 +268,16 @@ bool is_newer_lower(const struct dentry *dentry)
return true;
}
}
+
+ /*
+ * Last check: if this is a positive dentry, but somehow all lower
+ * dentries are negative or unhashed, then this dentry needs to be
+ * revalidated, because someone probably deleted the objects from
+ * the lower branches directly.
+ */
+ if (is_negative_lower(dentry))
+ return true;
+
return false; /* default: lower is not newer */
}

diff --git a/fs/unionfs/main.c b/fs/unionfs/main.c
index a9d2cb6..1fd67c6 100644
--- a/fs/unionfs/main.c
+++ b/fs/unionfs/main.c
@@ -88,9 +88,6 @@ struct dentry *unionfs_interpose(struct dentry *dentry, struct super_block *sb,

verify_locked(dentry);

- /* Make sure that we didn't get a negative dentry. */
- BUG_ON(is_negative_lower(dentry));
-
/*
* We allocate our new inode below by calling unionfs_iget,
* which will initialize some of the new inode's fields
--
1.5.2.2

2008-07-30 02:45:34

by Erez Zadok

[permalink] [raw]
Subject: [PATCH 11/19] Unionfs: remove old lookup code

Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/lookup.c | 337 ---------------------------------------------------
fs/unionfs/union.h | 3 -
2 files changed, 0 insertions(+), 340 deletions(-)

diff --git a/fs/unionfs/lookup.c b/fs/unionfs/lookup.c
index b5f64c9..93c3a14 100644
--- a/fs/unionfs/lookup.c
+++ b/fs/unionfs/lookup.c
@@ -63,343 +63,6 @@ struct dentry *__lookup_one(struct dentry *base, struct vfsmount *mnt,
}

/*
- * Main (and complex) driver function for Unionfs's lookup
- *
- * Returns: NULL (ok), ERR_PTR if an error occurred, or a non-null non-error
- * PTR if d_splice returned a different dentry.
- *
- * If lookupmode is INTERPOSE_PARTIAL/REVAL/REVAL_NEG, the passed dentry's
- * inode info must be locked. If lookupmode is INTERPOSE_LOOKUP (i.e., a
- * newly looked-up dentry), then unionfs_lookup_backend will return a locked
- * dentry's info, which the caller must unlock.
- */
-struct dentry *unionfs_lookup_backend(struct dentry *dentry,
- struct nameidata *nd_unused,
- int lookupmode)
-{
- int err = 0;
- struct dentry *lower_dentry = NULL;
- struct dentry *wh_lower_dentry = NULL;
- struct dentry *lower_dir_dentry = NULL;
- struct dentry *parent_dentry = NULL;
- struct dentry *d_interposed = NULL;
- int bindex, bstart = -1, bend, bopaque;
- int dentry_count = 0; /* Number of positive dentries. */
- int first_dentry_offset = -1; /* -1 is uninitialized */
- struct dentry *first_dentry = NULL;
- struct dentry *first_lower_dentry = NULL;
- struct vfsmount *first_lower_mnt = NULL;
- int opaque;
- 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 /* this could only be INTERPOSE_LOOKUP */
- BUG_ON(UNIONFS_D(dentry) != NULL);
-
- switch (lookupmode) {
- case INTERPOSE_PARTIAL:
- break;
- case INTERPOSE_LOOKUP:
- err = new_dentry_private_data(dentry, UNIONFS_DMUTEX_CHILD);
- if (unlikely(err))
- goto out;
- break;
- default:
- /* default: can only be INTERPOSE_REVAL/REVAL_NEG */
- err = realloc_dentry_private_data(dentry);
- if (unlikely(err))
- goto out;
- break;
- }
-
- /* 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) {
- 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++) {
- lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
- if (lookupmode == INTERPOSE_PARTIAL && lower_dentry)
- continue;
- BUG_ON(lower_dentry != NULL);
-
- lower_dir_dentry =
- unionfs_lower_dentry_idx(parent_dentry, bindex);
-
- /* if the parent lower dentry does not exist skip this */
- if (!(lower_dir_dentry && lower_dir_dentry->d_inode))
- continue;
-
- /* also skip it if the parent isn't a directory. */
- if (!S_ISDIR(lower_dir_dentry->d_inode->i_mode))
- continue;
-
- /* check for whiteouts: stop lookup if found */
- wh_lower_dentry = lookup_whiteout(name, lower_dir_dentry);
- if (IS_ERR(wh_lower_dentry)) {
- dput(first_lower_dentry);
- unionfs_mntput(first_dentry, first_dentry_offset);
- err = PTR_ERR(wh_lower_dentry);
- goto out_free;
- }
- if (wh_lower_dentry->d_inode) {
- dbend(dentry) = dbopaque(dentry) = bindex;
- dput(wh_lower_dentry);
- break;
- }
- dput(wh_lower_dentry);
- wh_lower_dentry = NULL;
-
- /* Now do regular lookup; lookup foo */
- BUG_ON(!lower_dir_dentry);
- lower_dentry = lookup_one_len(name, lower_dir_dentry, namelen);
- if (IS_ERR(lower_dentry)) {
- dput(first_lower_dentry);
- unionfs_mntput(first_dentry, first_dentry_offset);
- err = PTR_ERR(lower_dentry);
- goto out_free;
- }
-
- /*
- * Store the first negative dentry specially, because if they
- * are all negative we need this for future creates.
- */
- if (!lower_dentry->d_inode) {
- if (!first_lower_dentry && (dbstart(dentry) == -1)) {
- first_lower_dentry = lower_dentry;
- /*
- * FIXME: following line needs to be changed
- * to allow mount-point crossing
- */
- first_dentry = parent_dentry;
- first_lower_mnt =
- unionfs_mntget(parent_dentry, bindex);
- first_dentry_offset = bindex;
- } else {
- dput(lower_dentry);
- }
-
- continue;
- }
-
- /*
- * If we already found at least one positive dentry
- * (dentry_count is non-zero), then we skip all remaining
- * positive dentries if their type is a non-dir. This is
- * because only directories are allowed to stack on multiple
- * branches, but we have to skip non-dirs (to avoid, say,
- * calling readdir on a regular file).
- */
- if ((lookupmode != INTERPOSE_PARTIAL) &&
- !S_ISDIR(lower_dentry->d_inode->i_mode) &&
- dentry_count) {
- dput(lower_dentry);
- continue;
- }
-
- /* number of positive dentries */
- dentry_count++;
-
- /* store underlying dentry */
- if (dbstart(dentry) == -1)
- dbstart(dentry) = bindex;
- unionfs_set_lower_dentry_idx(dentry, bindex, lower_dentry);
- /*
- * FIXME: the following line needs to get fixed to allow
- * mount-point crossing
- */
- unionfs_set_lower_mnt_idx(dentry, bindex,
- unionfs_mntget(parent_dentry,
- bindex));
- dbend(dentry) = bindex;
-
- /* update parent directory's atime with the bindex */
- fsstack_copy_attr_atime(parent_dentry->d_inode,
- lower_dir_dentry->d_inode);
-
- /* We terminate file lookups here. */
- if (!S_ISDIR(lower_dentry->d_inode->i_mode)) {
- if (lookupmode == INTERPOSE_PARTIAL)
- continue;
- if (dentry_count == 1)
- goto out_positive;
- }
-
- opaque = is_opaque_dir(dentry, bindex);
- if (opaque < 0) {
- dput(first_lower_dentry);
- unionfs_mntput(first_dentry, first_dentry_offset);
- err = opaque;
- goto out_free;
- } else if (opaque) {
- dbend(dentry) = 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)
- goto out;
-
- if (!lower_dir_dentry) {
- err = -ENOENT;
- goto out;
- }
- /* This should only happen if we found a whiteout. */
- if (first_dentry_offset == -1) {
- first_lower_dentry = lookup_one_len(name, lower_dir_dentry,
- namelen);
- first_dentry_offset = bindex;
- if (IS_ERR(first_lower_dentry)) {
- err = PTR_ERR(first_lower_dentry);
- goto out;
- }
-
- /*
- * FIXME: the following line needs to be changed to allow
- * mount-point crossing
- */
- first_dentry = dentry;
- first_lower_mnt = unionfs_mntget(dentry->d_sb->s_root,
- bindex);
- }
- unionfs_set_lower_dentry_idx(dentry, first_dentry_offset,
- first_lower_dentry);
- unionfs_set_lower_mnt_idx(dentry, first_dentry_offset,
- first_lower_mnt);
- dbstart(dentry) = 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_lower_dentry);
- unionfs_mntput(first_dentry, first_dentry_offset);
-
- /* Partial lookups need to re-interpose, 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);
- }
-
- /*
- * Interpose can return a dentry if d_splice returned a different
- * dentry.
- */
- d_interposed = unionfs_interpose(dentry, dentry->d_sb, lookupmode);
- if (IS_ERR(d_interposed))
- err = PTR_ERR(d_interposed);
- else if (d_interposed)
- dentry = d_interposed;
-
- if (err)
- goto out_drop;
-
- goto out;
-
-out_drop:
- d_drop(dentry);
-
-out_free:
- /* should dput/mntput all the underlying dentries on error condition */
- if (dbstart(dentry) >= 0)
- path_put_lowers_all(dentry, false);
- /* free lower_paths unconditionally */
- kfree(UNIONFS_D(dentry)->lower_paths);
- UNIONFS_D(dentry)->lower_paths = NULL;
-
-out:
- if (!err && UNIONFS_D(dentry)) {
- BUG_ON(dbend(dentry) > UNIONFS_D(dentry)->bcount);
- BUG_ON(dbend(dentry) > sbmax(dentry->d_sb));
- if (dbstart(dentry) < 0 &&
- dentry->d_inode && bstart >= 0 &&
- (!UNIONFS_I(dentry->d_inode) ||
- !UNIONFS_I(dentry->d_inode)->lower_inodes)) {
- unionfs_mntput(dentry->d_sb->s_root, bstart);
- dput(first_lower_dentry);
- }
- }
- dput(parent_dentry);
- if (err && (lookupmode == INTERPOSE_LOOKUP))
- unionfs_unlock_dentry(dentry);
- if (!err && d_interposed)
- return d_interposed;
- return ERR_PTR(err);
-}
-
-/*
* This is a utility function that fills in a unionfs dentry.
* Caller must lock this dentry with unionfs_lock_dentry.
*
diff --git a/fs/unionfs/union.h b/fs/unionfs/union.h
index 81f34c4..356a146 100644
--- a/fs/unionfs/union.h
+++ b/fs/unionfs/union.h
@@ -415,9 +415,6 @@ static inline int d_deleted(struct dentry *d)
return d_unhashed(d) && (d != d->d_sb->s_root);
}

-struct dentry *unionfs_lookup_backend(struct dentry *dentry,
- struct nameidata *nd, int lookupmode);
-
/* unionfs_permission, check if we should bypass error to facilitate copyup */
#define IS_COPYUP_ERR(err) ((err) == -EROFS)

--
1.5.2.2

2008-07-30 02:46:00

by Erez Zadok

[permalink] [raw]
Subject: [PATCH 15/19] Unionfs: properly hash newly created inodes

This fixes LTP's syscalls/rename13 test.

Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/inode.c | 2 +-
fs/unionfs/main.c | 3 +++
2 files changed, 4 insertions(+), 1 deletions(-)

diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c
index fa3c518..a05b412 100644
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -339,7 +339,7 @@ check_link:

/* 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);
+ d_add(new_dentry, new_dentry->d_inode);
unionfs_copy_attr_all(dir, lower_new_dentry->d_parent->d_inode);
fsstack_copy_inode_size(dir, lower_new_dentry->d_parent->d_inode);

diff --git a/fs/unionfs/main.c b/fs/unionfs/main.c
index 0cdd50b..fea670b 100644
--- a/fs/unionfs/main.c
+++ b/fs/unionfs/main.c
@@ -128,6 +128,9 @@ skip:
/* only (our) lookup wants to do a d_add */
switch (flag) {
case INTERPOSE_DEFAULT:
+ /* for operations which create new inodes */
+ d_add(dentry, inode);
+ break;
case INTERPOSE_REVAL_NEG:
d_instantiate(dentry, inode);
break;
--
1.5.2.2

2008-07-30 02:46:23

by Erez Zadok

[permalink] [raw]
Subject: [PATCH 19/19] Unionfs: use new kmem_cache_create constructor prototype

Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/super.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/fs/unionfs/super.c b/fs/unionfs/super.c
index 1f4b3f4..e774ef3 100644
--- a/fs/unionfs/super.c
+++ b/fs/unionfs/super.c
@@ -900,7 +900,7 @@ static void unionfs_destroy_inode(struct inode *inode)
}

/* unionfs inode cache constructor */
-static void init_once(struct kmem_cache *cachep, void *obj)
+static void init_once(void *obj)
{
struct unionfs_inode_info *i = obj;

--
1.5.2.2

2008-07-30 02:46:39

by Erez Zadok

[permalink] [raw]
Subject: [PATCH 17/19] Unionfs: permission no longer takes a nameidata parameter

Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/inode.c | 11 ++---------
fs/unionfs/whiteout.c | 4 ++--
2 files changed, 4 insertions(+), 11 deletions(-)

diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c
index 1a95e3b..0bd9fab 100644
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -750,8 +750,7 @@ static void unionfs_put_link(struct dentry *dentry, struct nameidata *nd,
* unionfs_permission, or anything it calls, will use stale branch
* information.
*/
-static int unionfs_permission(struct inode *inode, int mask,
- struct nameidata *nd)
+static int unionfs_permission(struct inode *inode, int mask)
{
struct inode *lower_inode = NULL;
int err = 0;
@@ -759,9 +758,6 @@ static int unionfs_permission(struct inode *inode, int mask,
const int is_file = !S_ISDIR(inode->i_mode);
const int write_mask = (mask & MAY_WRITE) && !(mask & MAY_READ);

- if (nd)
- unionfs_lock_dentry(nd->path.dentry, UNIONFS_DMUTEX_CHILD);
-
if (!UNIONFS_I(inode)->lower_inodes) {
if (is_file) /* dirs can be unlinked but chdir'ed to */
err = -ESTALE; /* force revalidate */
@@ -801,7 +797,7 @@ static int unionfs_permission(struct inode *inode, int mask,
* readonly, because those conditions should lead to a
* copyup taking place later on.
*/
- err = permission(lower_inode, mask, nd);
+ err = inode_permission(lower_inode, mask);
if (err && bindex > 0) {
umode_t mode = lower_inode->i_mode;
if (is_robranch_super(inode->i_sb, bindex) &&
@@ -833,9 +829,6 @@ static int unionfs_permission(struct inode *inode, int mask,

out:
unionfs_check_inode(inode);
- unionfs_check_nd(nd);
- if (nd)
- unionfs_unlock_dentry(nd->path.dentry);
return err;
}

diff --git a/fs/unionfs/whiteout.c b/fs/unionfs/whiteout.c
index 94b5241..db7a21e 100644
--- a/fs/unionfs/whiteout.c
+++ b/fs/unionfs/whiteout.c
@@ -455,7 +455,7 @@ int delete_whiteouts(struct dentry *dentry, int bindex,
lower_dir = lower_dir_dentry->d_inode;
BUG_ON(!S_ISDIR(lower_dir->i_mode));

- if (!permission(lower_dir, MAY_WRITE | MAY_EXEC, NULL)) {
+ if (!inode_permission(lower_dir, MAY_WRITE | MAY_EXEC)) {
err = do_delete_whiteouts(dentry, bindex, namelist);
} else {
args.deletewh.namelist = namelist;
@@ -492,7 +492,7 @@ int is_opaque_dir(struct dentry *dentry, int bindex)

mutex_lock(&lower_inode->i_mutex);

- if (!permission(lower_inode, MAY_EXEC, NULL)) {
+ if (!inode_permission(lower_inode, MAY_EXEC)) {
wh_lower_dentry =
lookup_one_len(UNIONFS_DIR_OPAQUE, lower_dentry,
sizeof(UNIONFS_DIR_OPAQUE) - 1);
--
1.5.2.2

2008-07-30 02:46:54

by Erez Zadok

[permalink] [raw]
Subject: [PATCH 13/19] Unionfs: update copyrights

Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/commonfops.c | 6 +++---
fs/unionfs/copyup.c | 6 +++---
fs/unionfs/debug.c | 6 +++---
fs/unionfs/dentry.c | 6 +++---
fs/unionfs/dirfops.c | 6 +++---
fs/unionfs/dirhelper.c | 6 +++---
fs/unionfs/fanout.h | 6 +++---
fs/unionfs/file.c | 6 +++---
fs/unionfs/inode.c | 6 +++---
fs/unionfs/lookup.c | 6 +++---
fs/unionfs/main.c | 6 +++---
fs/unionfs/mmap.c | 6 +++---
fs/unionfs/rdstate.c | 6 +++---
fs/unionfs/rename.c | 6 +++---
fs/unionfs/sioq.c | 6 +++---
fs/unionfs/sioq.h | 6 +++---
fs/unionfs/subr.c | 6 +++---
fs/unionfs/super.c | 6 +++---
fs/unionfs/union.h | 6 +++---
fs/unionfs/unlink.c | 6 +++---
fs/unionfs/whiteout.c | 6 +++---
fs/unionfs/xattr.c | 6 +++---
include/linux/union_fs.h | 6 +++---
23 files changed, 69 insertions(+), 69 deletions(-)

diff --git a/fs/unionfs/commonfops.c b/fs/unionfs/commonfops.c
index d64272b..5861970 100644
--- a/fs/unionfs/commonfops.c
+++ b/fs/unionfs/commonfops.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2007 Erez Zadok
+ * Copyright (c) 2003-2008 Erez Zadok
* Copyright (c) 2003-2006 Charles P. Wright
* Copyright (c) 2005-2007 Josef 'Jeff' Sipek
* Copyright (c) 2005-2006 Junjiro Okajima
@@ -8,8 +8,8 @@
* Copyright (c) 2003-2004 Mohammad Nayyer Zubair
* Copyright (c) 2003 Puja Gupta
* Copyright (c) 2003 Harikesavan Krishnan
- * Copyright (c) 2003-2007 Stony Brook University
- * Copyright (c) 2003-2007 The Research Foundation of SUNY
+ * Copyright (c) 2003-2008 Stony Brook University
+ * Copyright (c) 2003-2008 The Research Foundation of SUNY
*
* 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
diff --git a/fs/unionfs/copyup.c b/fs/unionfs/copyup.c
index 55d48aa..bbd49c8 100644
--- a/fs/unionfs/copyup.c
+++ b/fs/unionfs/copyup.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2007 Erez Zadok
+ * Copyright (c) 2003-2008 Erez Zadok
* Copyright (c) 2003-2006 Charles P. Wright
* Copyright (c) 2005-2007 Josef 'Jeff' Sipek
* Copyright (c) 2005-2006 Junjiro Okajima
@@ -8,8 +8,8 @@
* Copyright (c) 2003-2004 Mohammad Nayyer Zubair
* Copyright (c) 2003 Puja Gupta
* Copyright (c) 2003 Harikesavan Krishnan
- * Copyright (c) 2003-2007 Stony Brook University
- * Copyright (c) 2003-2007 The Research Foundation of SUNY
+ * Copyright (c) 2003-2008 Stony Brook University
+ * Copyright (c) 2003-2008 The Research Foundation of SUNY
*
* 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
diff --git a/fs/unionfs/debug.c b/fs/unionfs/debug.c
index 4da4f0b..db62d22 100644
--- a/fs/unionfs/debug.c
+++ b/fs/unionfs/debug.c
@@ -1,8 +1,8 @@
/*
- * Copyright (c) 2003-2007 Erez Zadok
+ * Copyright (c) 2003-2008 Erez Zadok
* Copyright (c) 2005-2007 Josef 'Jeff' Sipek
- * Copyright (c) 2003-2007 Stony Brook University
- * Copyright (c) 2003-2007 The Research Foundation of SUNY
+ * Copyright (c) 2003-2008 Stony Brook University
+ * Copyright (c) 2003-2008 The Research Foundation of SUNY
*
* 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
diff --git a/fs/unionfs/dentry.c b/fs/unionfs/dentry.c
index dfa4755..7f0c7f7 100644
--- a/fs/unionfs/dentry.c
+++ b/fs/unionfs/dentry.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2007 Erez Zadok
+ * Copyright (c) 2003-2008 Erez Zadok
* Copyright (c) 2003-2006 Charles P. Wright
* Copyright (c) 2005-2007 Josef 'Jeff' Sipek
* Copyright (c) 2005-2006 Junjiro Okajima
@@ -8,8 +8,8 @@
* Copyright (c) 2003-2004 Mohammad Nayyer Zubair
* Copyright (c) 2003 Puja Gupta
* Copyright (c) 2003 Harikesavan Krishnan
- * Copyright (c) 2003-2007 Stony Brook University
- * Copyright (c) 2003-2007 The Research Foundation of SUNY
+ * Copyright (c) 2003-2008 Stony Brook University
+ * Copyright (c) 2003-2008 The Research Foundation of SUNY
*
* 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
diff --git a/fs/unionfs/dirfops.c b/fs/unionfs/dirfops.c
index e35afa4..14ca7d3 100644
--- a/fs/unionfs/dirfops.c
+++ b/fs/unionfs/dirfops.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2007 Erez Zadok
+ * Copyright (c) 2003-2008 Erez Zadok
* Copyright (c) 2003-2006 Charles P. Wright
* Copyright (c) 2005-2007 Josef 'Jeff' Sipek
* Copyright (c) 2005-2006 Junjiro Okajima
@@ -8,8 +8,8 @@
* Copyright (c) 2003-2004 Mohammad Nayyer Zubair
* Copyright (c) 2003 Puja Gupta
* Copyright (c) 2003 Harikesavan Krishnan
- * Copyright (c) 2003-2007 Stony Brook University
- * Copyright (c) 2003-2007 The Research Foundation of SUNY
+ * Copyright (c) 2003-2008 Stony Brook University
+ * Copyright (c) 2003-2008 The Research Foundation of SUNY
*
* 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
diff --git a/fs/unionfs/dirhelper.c b/fs/unionfs/dirhelper.c
index 302a4a1..d936f03 100644
--- a/fs/unionfs/dirhelper.c
+++ b/fs/unionfs/dirhelper.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2007 Erez Zadok
+ * Copyright (c) 2003-2008 Erez Zadok
* Copyright (c) 2003-2006 Charles P. Wright
* Copyright (c) 2005-2007 Josef 'Jeff' Sipek
* Copyright (c) 2005-2006 Junjiro Okajima
@@ -8,8 +8,8 @@
* Copyright (c) 2003-2004 Mohammad Nayyer Zubair
* Copyright (c) 2003 Puja Gupta
* Copyright (c) 2003 Harikesavan Krishnan
- * Copyright (c) 2003-2007 Stony Brook University
- * Copyright (c) 2003-2007 The Research Foundation of SUNY
+ * Copyright (c) 2003-2008 Stony Brook University
+ * Copyright (c) 2003-2008 The Research Foundation of SUNY
*
* 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
diff --git a/fs/unionfs/fanout.h b/fs/unionfs/fanout.h
index d8977bd..4f264de 100644
--- a/fs/unionfs/fanout.h
+++ b/fs/unionfs/fanout.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2007 Erez Zadok
+ * Copyright (c) 2003-2008 Erez Zadok
* Copyright (c) 2003-2006 Charles P. Wright
* Copyright (c) 2005-2007 Josef 'Jeff' Sipek
* Copyright (c) 2005 Arun M. Krishnakumar
@@ -7,8 +7,8 @@
* Copyright (c) 2003-2004 Mohammad Nayyer Zubair
* Copyright (c) 2003 Puja Gupta
* Copyright (c) 2003 Harikesavan Krishnan
- * Copyright (c) 2003-2007 Stony Brook University
- * Copyright (c) 2003-2007 The Research Foundation of SUNY
+ * Copyright (c) 2003-2008 Stony Brook University
+ * Copyright (c) 2003-2008 The Research Foundation of SUNY
*
* 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
diff --git a/fs/unionfs/file.c b/fs/unionfs/file.c
index 9b5fc58..965d071 100644
--- a/fs/unionfs/file.c
+++ b/fs/unionfs/file.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2007 Erez Zadok
+ * Copyright (c) 2003-2008 Erez Zadok
* Copyright (c) 2003-2006 Charles P. Wright
* Copyright (c) 2005-2007 Josef 'Jeff' Sipek
* Copyright (c) 2005-2006 Junjiro Okajima
@@ -8,8 +8,8 @@
* Copyright (c) 2003-2004 Mohammad Nayyer Zubair
* Copyright (c) 2003 Puja Gupta
* Copyright (c) 2003 Harikesavan Krishnan
- * Copyright (c) 2003-2007 Stony Brook University
- * Copyright (c) 2003-2007 The Research Foundation of SUNY
+ * Copyright (c) 2003-2008 Stony Brook University
+ * Copyright (c) 2003-2008 The Research Foundation of SUNY
*
* 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
diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c
index f81ebb6..fa3c518 100644
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2007 Erez Zadok
+ * Copyright (c) 2003-2008 Erez Zadok
* Copyright (c) 2003-2006 Charles P. Wright
* Copyright (c) 2005-2007 Josef 'Jeff' Sipek
* Copyright (c) 2005-2006 Junjiro Okajima
@@ -8,8 +8,8 @@
* Copyright (c) 2003-2004 Mohammad Nayyer Zubair
* Copyright (c) 2003 Puja Gupta
* Copyright (c) 2003 Harikesavan Krishnan
- * Copyright (c) 2003-2007 Stony Brook University
- * Copyright (c) 2003-2007 The Research Foundation of SUNY
+ * Copyright (c) 2003-2008 Stony Brook University
+ * Copyright (c) 2003-2008 The Research Foundation of SUNY
*
* 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
diff --git a/fs/unionfs/lookup.c b/fs/unionfs/lookup.c
index 93c3a14..0ae7f3a 100644
--- a/fs/unionfs/lookup.c
+++ b/fs/unionfs/lookup.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2007 Erez Zadok
+ * Copyright (c) 2003-2008 Erez Zadok
* Copyright (c) 2003-2006 Charles P. Wright
* Copyright (c) 2005-2007 Josef 'Jeff' Sipek
* Copyright (c) 2005-2006 Junjiro Okajima
@@ -8,8 +8,8 @@
* Copyright (c) 2003-2004 Mohammad Nayyer Zubair
* Copyright (c) 2003 Puja Gupta
* Copyright (c) 2003 Harikesavan Krishnan
- * Copyright (c) 2003-2007 Stony Brook University
- * Copyright (c) 2003-2007 The Research Foundation of SUNY
+ * Copyright (c) 2003-2008 Stony Brook University
+ * Copyright (c) 2003-2008 The Research Foundation of SUNY
*
* 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
diff --git a/fs/unionfs/main.c b/fs/unionfs/main.c
index 1fd67c6..6484e2b 100644
--- a/fs/unionfs/main.c
+++ b/fs/unionfs/main.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2007 Erez Zadok
+ * Copyright (c) 2003-2008 Erez Zadok
* Copyright (c) 2003-2006 Charles P. Wright
* Copyright (c) 2005-2007 Josef 'Jeff' Sipek
* Copyright (c) 2005-2006 Junjiro Okajima
@@ -8,8 +8,8 @@
* Copyright (c) 2003-2004 Mohammad Nayyer Zubair
* Copyright (c) 2003 Puja Gupta
* Copyright (c) 2003 Harikesavan Krishnan
- * Copyright (c) 2003-2007 Stony Brook University
- * Copyright (c) 2003-2007 The Research Foundation of SUNY
+ * Copyright (c) 2003-2008 Stony Brook University
+ * Copyright (c) 2003-2008 The Research Foundation of SUNY
*
* 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
diff --git a/fs/unionfs/mmap.c b/fs/unionfs/mmap.c
index febde7c..b7d4713 100644
--- a/fs/unionfs/mmap.c
+++ b/fs/unionfs/mmap.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2007 Erez Zadok
+ * Copyright (c) 2003-2008 Erez Zadok
* Copyright (c) 2003-2006 Charles P. Wright
* Copyright (c) 2005-2007 Josef 'Jeff' Sipek
* Copyright (c) 2005-2006 Junjiro Okajima
@@ -9,8 +9,8 @@
* Copyright (c) 2003-2004 Mohammad Nayyer Zubair
* Copyright (c) 2003 Puja Gupta
* Copyright (c) 2003 Harikesavan Krishnan
- * Copyright (c) 2003-2007 Stony Brook University
- * Copyright (c) 2003-2007 The Research Foundation of SUNY
+ * Copyright (c) 2003-2008 Stony Brook University
+ * Copyright (c) 2003-2008 The Research Foundation of SUNY
*
* 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
diff --git a/fs/unionfs/rdstate.c b/fs/unionfs/rdstate.c
index 7ba1e1a..06d5374 100644
--- a/fs/unionfs/rdstate.c
+++ b/fs/unionfs/rdstate.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2007 Erez Zadok
+ * Copyright (c) 2003-2008 Erez Zadok
* Copyright (c) 2003-2006 Charles P. Wright
* Copyright (c) 2005-2007 Josef 'Jeff' Sipek
* Copyright (c) 2005-2006 Junjiro Okajima
@@ -8,8 +8,8 @@
* Copyright (c) 2003-2004 Mohammad Nayyer Zubair
* Copyright (c) 2003 Puja Gupta
* Copyright (c) 2003 Harikesavan Krishnan
- * Copyright (c) 2003-2007 Stony Brook University
- * Copyright (c) 2003-2007 The Research Foundation of SUNY
+ * Copyright (c) 2003-2008 Stony Brook University
+ * Copyright (c) 2003-2008 The Research Foundation of SUNY
*
* 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
diff --git a/fs/unionfs/rename.c b/fs/unionfs/rename.c
index ecce9da..da7d589 100644
--- a/fs/unionfs/rename.c
+++ b/fs/unionfs/rename.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2007 Erez Zadok
+ * Copyright (c) 2003-2008 Erez Zadok
* Copyright (c) 2003-2006 Charles P. Wright
* Copyright (c) 2005-2007 Josef 'Jeff' Sipek
* Copyright (c) 2005-2006 Junjiro Okajima
@@ -8,8 +8,8 @@
* Copyright (c) 2003-2004 Mohammad Nayyer Zubair
* Copyright (c) 2003 Puja Gupta
* Copyright (c) 2003 Harikesavan Krishnan
- * Copyright (c) 2003-2007 Stony Brook University
- * Copyright (c) 2003-2007 The Research Foundation of SUNY
+ * Copyright (c) 2003-2008 Stony Brook University
+ * Copyright (c) 2003-2008 The Research Foundation of SUNY
*
* 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
diff --git a/fs/unionfs/sioq.c b/fs/unionfs/sioq.c
index 0ea8436..e6f15a0 100644
--- a/fs/unionfs/sioq.c
+++ b/fs/unionfs/sioq.c
@@ -1,11 +1,11 @@
/*
- * Copyright (c) 2006-2007 Erez Zadok
+ * Copyright (c) 2006-2008 Erez Zadok
* Copyright (c) 2006 Charles P. Wright
* Copyright (c) 2006-2007 Josef 'Jeff' Sipek
* Copyright (c) 2006 Junjiro Okajima
* Copyright (c) 2006 David P. Quigley
- * Copyright (c) 2006-2007 Stony Brook University
- * Copyright (c) 2006-2007 The Research Foundation of SUNY
+ * Copyright (c) 2006-2008 Stony Brook University
+ * Copyright (c) 2006-2008 The Research Foundation of SUNY
*
* 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
diff --git a/fs/unionfs/sioq.h b/fs/unionfs/sioq.h
index afb71ee..e072bf7 100644
--- a/fs/unionfs/sioq.h
+++ b/fs/unionfs/sioq.h
@@ -1,11 +1,11 @@
/*
- * Copyright (c) 2006-2007 Erez Zadok
+ * Copyright (c) 2006-2008 Erez Zadok
* Copyright (c) 2006 Charles P. Wright
* Copyright (c) 2006-2007 Josef 'Jeff' Sipek
* Copyright (c) 2006 Junjiro Okajima
* Copyright (c) 2006 David P. Quigley
- * Copyright (c) 2006-2007 Stony Brook University
- * Copyright (c) 2006-2007 The Research Foundation of SUNY
+ * Copyright (c) 2006-2008 Stony Brook University
+ * Copyright (c) 2006-2008 The Research Foundation of SUNY
*
* 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
diff --git a/fs/unionfs/subr.c b/fs/unionfs/subr.c
index dda2745..8747d20 100644
--- a/fs/unionfs/subr.c
+++ b/fs/unionfs/subr.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2007 Erez Zadok
+ * Copyright (c) 2003-2008 Erez Zadok
* Copyright (c) 2003-2006 Charles P. Wright
* Copyright (c) 2005-2007 Josef 'Jeff' Sipek
* Copyright (c) 2005-2006 Junjiro Okajima
@@ -8,8 +8,8 @@
* Copyright (c) 2003-2004 Mohammad Nayyer Zubair
* Copyright (c) 2003 Puja Gupta
* Copyright (c) 2003 Harikesavan Krishnan
- * Copyright (c) 2003-2007 Stony Brook University
- * Copyright (c) 2003-2007 The Research Foundation of SUNY
+ * Copyright (c) 2003-2008 Stony Brook University
+ * Copyright (c) 2003-2008 The Research Foundation of SUNY
*
* 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
diff --git a/fs/unionfs/super.c b/fs/unionfs/super.c
index 3b6b65a..f163c08 100644
--- a/fs/unionfs/super.c
+++ b/fs/unionfs/super.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2007 Erez Zadok
+ * Copyright (c) 2003-2008 Erez Zadok
* Copyright (c) 2003-2006 Charles P. Wright
* Copyright (c) 2005-2007 Josef 'Jeff' Sipek
* Copyright (c) 2005-2006 Junjiro Okajima
@@ -8,8 +8,8 @@
* Copyright (c) 2003-2004 Mohammad Nayyer Zubair
* Copyright (c) 2003 Puja Gupta
* Copyright (c) 2003 Harikesavan Krishnan
- * Copyright (c) 2003-2007 Stony Brook University
- * Copyright (c) 2003-2007 The Research Foundation of SUNY
+ * Copyright (c) 2003-2008 Stony Brook University
+ * Copyright (c) 2003-2008 The Research Foundation of SUNY
*
* 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
diff --git a/fs/unionfs/union.h b/fs/unionfs/union.h
index 356a146..4c7b213 100644
--- a/fs/unionfs/union.h
+++ b/fs/unionfs/union.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2007 Erez Zadok
+ * Copyright (c) 2003-2008 Erez Zadok
* Copyright (c) 2003-2006 Charles P. Wright
* Copyright (c) 2005-2007 Josef 'Jeff' Sipek
* Copyright (c) 2005 Arun M. Krishnakumar
@@ -7,8 +7,8 @@
* Copyright (c) 2003-2004 Mohammad Nayyer Zubair
* Copyright (c) 2003 Puja Gupta
* Copyright (c) 2003 Harikesavan Krishnan
- * Copyright (c) 2003-2007 Stony Brook University
- * Copyright (c) 2003-2007 The Research Foundation of SUNY
+ * Copyright (c) 2003-2008 Stony Brook University
+ * Copyright (c) 2003-2008 The Research Foundation of SUNY
*
* 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
diff --git a/fs/unionfs/unlink.c b/fs/unionfs/unlink.c
index 3d6ca5f..623f68d 100644
--- a/fs/unionfs/unlink.c
+++ b/fs/unionfs/unlink.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2007 Erez Zadok
+ * Copyright (c) 2003-2008 Erez Zadok
* Copyright (c) 2003-2006 Charles P. Wright
* Copyright (c) 2005-2007 Josef 'Jeff' Sipek
* Copyright (c) 2005-2006 Junjiro Okajima
@@ -8,8 +8,8 @@
* Copyright (c) 2003-2004 Mohammad Nayyer Zubair
* Copyright (c) 2003 Puja Gupta
* Copyright (c) 2003 Harikesavan Krishnan
- * Copyright (c) 2003-2007 Stony Brook University
- * Copyright (c) 2003-2007 The Research Foundation of SUNY
+ * Copyright (c) 2003-2008 Stony Brook University
+ * Copyright (c) 2003-2008 The Research Foundation of SUNY
*
* 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
diff --git a/fs/unionfs/whiteout.c b/fs/unionfs/whiteout.c
index b1768ed..94b5241 100644
--- a/fs/unionfs/whiteout.c
+++ b/fs/unionfs/whiteout.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2007 Erez Zadok
+ * Copyright (c) 2003-2008 Erez Zadok
* Copyright (c) 2003-2006 Charles P. Wright
* Copyright (c) 2005-2007 Josef 'Jeff' Sipek
* Copyright (c) 2005-2006 Junjiro Okajima
@@ -8,8 +8,8 @@
* Copyright (c) 2003-2004 Mohammad Nayyer Zubair
* Copyright (c) 2003 Puja Gupta
* Copyright (c) 2003 Harikesavan Krishnan
- * Copyright (c) 2003-2007 Stony Brook University
- * Copyright (c) 2003-2007 The Research Foundation of SUNY
+ * Copyright (c) 2003-2008 Stony Brook University
+ * Copyright (c) 2003-2008 The Research Foundation of SUNY
*
* 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
diff --git a/fs/unionfs/xattr.c b/fs/unionfs/xattr.c
index 8001c65..93a8fce 100644
--- a/fs/unionfs/xattr.c
+++ b/fs/unionfs/xattr.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2007 Erez Zadok
+ * Copyright (c) 2003-2008 Erez Zadok
* Copyright (c) 2003-2006 Charles P. Wright
* Copyright (c) 2005-2007 Josef 'Jeff' Sipek
* Copyright (c) 2005-2006 Junjiro Okajima
@@ -8,8 +8,8 @@
* Copyright (c) 2003-2004 Mohammad Nayyer Zubair
* Copyright (c) 2003 Puja Gupta
* Copyright (c) 2003 Harikesavan Krishnan
- * Copyright (c) 2003-2007 Stony Brook University
- * Copyright (c) 2003-2007 The Research Foundation of SUNY
+ * Copyright (c) 2003-2008 Stony Brook University
+ * Copyright (c) 2003-2008 The Research Foundation of SUNY
*
* 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
diff --git a/include/linux/union_fs.h b/include/linux/union_fs.h
index 9d601d2..7199458 100644
--- a/include/linux/union_fs.h
+++ b/include/linux/union_fs.h
@@ -1,8 +1,8 @@
/*
- * Copyright (c) 2003-2007 Erez Zadok
+ * Copyright (c) 2003-2008 Erez Zadok
* Copyright (c) 2005-2007 Josef 'Jeff' Sipek
- * Copyright (c) 2003-2007 Stony Brook University
- * Copyright (c) 2003-2007 The Research Foundation of SUNY
+ * Copyright (c) 2003-2008 Stony Brook University
+ * Copyright (c) 2003-2008 The Research Foundation of SUNY
*
* 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
--
1.5.2.2

2008-07-30 02:47:51

by Erez Zadok

[permalink] [raw]
Subject: [PATCH 03/19] Unionfs: move a rename helper closer to rename code

Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/rename.c | 42 ++++++++++++++++++++++++++++++++++++++++++
fs/unionfs/subr.c | 42 ------------------------------------------
fs/unionfs/union.h | 3 ---
3 files changed, 42 insertions(+), 45 deletions(-)

diff --git a/fs/unionfs/rename.c b/fs/unionfs/rename.c
index fe8d877..5b3f1a3 100644
--- a/fs/unionfs/rename.c
+++ b/fs/unionfs/rename.c
@@ -18,6 +18,48 @@

#include "union.h"

+/*
+ * This is a helper function for rename, used when rename ends up with hosed
+ * over dentries and we need to revert.
+ */
+static int unionfs_refresh_lower_dentry(struct dentry *dentry, int bindex)
+{
+ struct dentry *lower_dentry;
+ struct dentry *lower_parent;
+ int err = 0;
+
+ verify_locked(dentry);
+
+ unionfs_lock_dentry(dentry->d_parent, UNIONFS_DMUTEX_CHILD);
+ lower_parent = unionfs_lower_dentry_idx(dentry->d_parent, bindex);
+ unionfs_unlock_dentry(dentry->d_parent);
+
+ BUG_ON(!S_ISDIR(lower_parent->d_inode->i_mode));
+
+ lower_dentry = lookup_one_len(dentry->d_name.name, lower_parent,
+ dentry->d_name.len);
+ if (IS_ERR(lower_dentry)) {
+ err = PTR_ERR(lower_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 (!lower_dentry->d_inode) {
+ dput(lower_dentry);
+ unionfs_set_lower_dentry_idx(dentry, bindex, NULL);
+ } else {
+ unionfs_set_lower_dentry_idx(dentry, bindex, lower_dentry);
+ unionfs_set_lower_inode_idx(dentry->d_inode, bindex,
+ igrab(lower_dentry->d_inode));
+ }
+
+out:
+ return err;
+}
+
static int __unionfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry,
int bindex, struct dentry **wh_old)
diff --git a/fs/unionfs/subr.c b/fs/unionfs/subr.c
index b76fa7a..1f1db3d 100644
--- a/fs/unionfs/subr.c
+++ b/fs/unionfs/subr.c
@@ -114,48 +114,6 @@ out:
return err;
}

-/*
- * This is a helper function for rename, which ends up with hosed over
- * dentries when it needs to revert.
- */
-int unionfs_refresh_lower_dentry(struct dentry *dentry, int bindex)
-{
- struct dentry *lower_dentry;
- struct dentry *lower_parent;
- int err = 0;
-
- verify_locked(dentry);
-
- unionfs_lock_dentry(dentry->d_parent, UNIONFS_DMUTEX_CHILD);
- lower_parent = unionfs_lower_dentry_idx(dentry->d_parent, bindex);
- unionfs_unlock_dentry(dentry->d_parent);
-
- BUG_ON(!S_ISDIR(lower_parent->d_inode->i_mode));
-
- lower_dentry = lookup_one_len(dentry->d_name.name, lower_parent,
- dentry->d_name.len);
- if (IS_ERR(lower_dentry)) {
- err = PTR_ERR(lower_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 (!lower_dentry->d_inode) {
- dput(lower_dentry);
- unionfs_set_lower_dentry_idx(dentry, bindex, NULL);
- } else {
- unionfs_set_lower_dentry_idx(dentry, bindex, lower_dentry);
- unionfs_set_lower_inode_idx(dentry->d_inode, bindex,
- igrab(lower_dentry->d_inode));
- }
-
-out:
- return err;
-}
-
int make_dir_opaque(struct dentry *dentry, int bindex)
{
int err = 0;
diff --git a/fs/unionfs/union.h b/fs/unionfs/union.h
index edd5685..7ddbad1 100644
--- a/fs/unionfs/union.h
+++ b/fs/unionfs/union.h
@@ -346,9 +346,6 @@ extern int check_empty(struct dentry *dentry,
extern int delete_whiteouts(struct dentry *dentry, int bindex,
struct unionfs_dir_state *namelist);

-/* Re-lookup a lower dentry. */
-extern int unionfs_refresh_lower_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);

--
1.5.2.2

2008-07-30 02:47:34

by Erez Zadok

[permalink] [raw]
Subject: [PATCH 02/19] Unionfs: simplify the macros used to get/set the dentry start/end branches

Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/commonfops.c | 6 ++--
fs/unionfs/copyup.c | 14 ++++++------
fs/unionfs/dentry.c | 6 +---
fs/unionfs/fanout.h | 48 +++++-----------------------------------------
fs/unionfs/inode.c | 2 +-
fs/unionfs/lookup.c | 18 ++++++----------
fs/unionfs/main.c | 4 +-
fs/unionfs/rename.c | 10 ++++----
fs/unionfs/subr.c | 4 +-
fs/unionfs/super.c | 2 +-
10 files changed, 36 insertions(+), 78 deletions(-)

diff --git a/fs/unionfs/commonfops.c b/fs/unionfs/commonfops.c
index 631e081..6f61fb0 100644
--- a/fs/unionfs/commonfops.c
+++ b/fs/unionfs/commonfops.c
@@ -290,7 +290,7 @@ static int do_delayed_copyup(struct file *file)
}
/* for reg file, we only open it "once" */
fbend(file) = fbstart(file);
- set_dbend(dentry, dbstart(dentry));
+ dbend(dentry) = dbstart(dentry);
ibend(dentry->d_inode) = ibstart(dentry->d_inode);

out:
@@ -823,8 +823,8 @@ static int unionfs_ioctl_queryfile(struct file *file, unsigned int cmd,
}
}
/* restore original dentry's offsets */
- set_dbstart(dentry, orig_bstart);
- set_dbend(dentry, orig_bend);
+ dbstart(dentry) = orig_bstart;
+ dbend(dentry) = orig_bend;
ibstart(dentry->d_inode) = orig_bstart;
ibend(dentry->d_inode) = orig_bend;

diff --git a/fs/unionfs/copyup.c b/fs/unionfs/copyup.c
index 6d1e461..b918897 100644
--- a/fs/unionfs/copyup.c
+++ b/fs/unionfs/copyup.c
@@ -354,8 +354,8 @@ static void __clear(struct dentry *dentry, struct dentry *old_lower_dentry,
{
/* get rid of the lower dentry and all its traces */
unionfs_set_lower_dentry_idx(dentry, new_bindex, NULL);
- set_dbstart(dentry, old_bstart);
- set_dbend(dentry, old_bend);
+ dbstart(dentry) = old_bstart;
+ dbend(dentry) = old_bend;

dput(new_lower_dentry);
dput(old_lower_dentry);
@@ -633,8 +633,8 @@ static void __cleanup_dentry(struct dentry *dentry, int bindex,
new_bstart = bindex;
if (new_bend < 0)
new_bend = bindex;
- set_dbstart(dentry, new_bstart);
- set_dbend(dentry, new_bend);
+ dbstart(dentry) = new_bstart;
+ dbend(dentry) = new_bend;

}

@@ -657,9 +657,9 @@ static void __set_dentry(struct dentry *upper, struct dentry *lower,
{
unionfs_set_lower_dentry_idx(upper, bindex, lower);
if (likely(dbstart(upper) > bindex))
- set_dbstart(upper, bindex);
+ dbstart(upper) = bindex;
if (likely(dbend(upper) < bindex))
- set_dbend(upper, bindex);
+ dbend(upper) = bindex;
}

/*
@@ -883,6 +883,6 @@ void unionfs_postcopyup_release(struct dentry *dentry)
}
}
bindex = dbstart(dentry);
- set_dbend(dentry, bindex);
+ dbend(dentry) = bindex;
ibend(dentry->d_inode) = ibstart(dentry->d_inode) = bindex;
}
diff --git a/fs/unionfs/dentry.c b/fs/unionfs/dentry.c
index e5f894c..44b17ce 100644
--- a/fs/unionfs/dentry.c
+++ b/fs/unionfs/dentry.c
@@ -106,8 +106,7 @@ static bool __unionfs_d_revalidate_one(struct dentry *dentry,
bstart = dbstart(dentry);
bend = dbend(dentry);
__dput_lowers(dentry, bstart, bend);
- set_dbstart(dentry, -1);
- set_dbend(dentry, -1);
+ dbstart(dentry) = dbend(dentry) = -1;

interpose_flag = INTERPOSE_REVAL_NEG;
if (positive) {
@@ -542,8 +541,7 @@ static void unionfs_d_iput(struct dentry *dentry, struct inode *inode)
unionfs_set_lower_dentry_idx(dentry, bindex, NULL);
}
}
- set_dbstart(dentry, -1);
- set_dbend(dentry, -1);
+ dbstart(dentry) = dbend(dentry) = -1;

drop_lower_inodes:
rc = atomic_read(&inode->i_count);
diff --git a/fs/unionfs/fanout.h b/fs/unionfs/fanout.h
index 29d42fb..69a8e8f 100644
--- a/fs/unionfs/fanout.h
+++ b/fs/unionfs/fanout.h
@@ -33,6 +33,12 @@ static inline struct unionfs_inode_info *UNIONFS_I(const struct inode *inode)
#define ibstart(ino) (UNIONFS_I(ino)->bstart)
#define ibend(ino) (UNIONFS_I(ino)->bend)

+/* Dentry to private data */
+#define UNIONFS_D(dent) ((struct unionfs_dentry_info *)(dent)->d_fsdata)
+#define dbstart(dent) (UNIONFS_D(dent)->bstart)
+#define dbend(dent) (UNIONFS_D(dent)->bend)
+#define dbopaque(dent) (UNIONFS_D(dent)->bopaque)
+
/* Superblock to private data */
#define UNIONFS_SB(super) ((struct unionfs_sb_info *)(super)->s_fs_info)
#define sbstart(sb) 0
@@ -199,48 +205,6 @@ static inline void branchput(struct super_block *sb, int index)
}

/* Dentry macros */
-static inline struct unionfs_dentry_info *UNIONFS_D(const struct dentry *dent)
-{
- BUG_ON(!dent);
- return dent->d_fsdata;
-}
-
-static inline int dbstart(const struct dentry *dent)
-{
- BUG_ON(!dent);
- return UNIONFS_D(dent)->bstart;
-}
-
-static inline void set_dbstart(struct dentry *dent, int val)
-{
- BUG_ON(!dent);
- UNIONFS_D(dent)->bstart = val;
-}
-
-static inline int dbend(const struct dentry *dent)
-{
- BUG_ON(!dent);
- return UNIONFS_D(dent)->bend;
-}
-
-static inline void set_dbend(struct dentry *dent, int val)
-{
- BUG_ON(!dent);
- UNIONFS_D(dent)->bend = val;
-}
-
-static inline int dbopaque(const struct dentry *dent)
-{
- BUG_ON(!dent);
- return UNIONFS_D(dent)->bopaque;
-}
-
-static inline void set_dbopaque(struct dentry *dent, int val)
-{
- BUG_ON(!dent);
- UNIONFS_D(dent)->bopaque = val;
-}
-
static inline void unionfs_set_lower_dentry_idx(struct dentry *dent, int index,
struct dentry *val)
{
diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c
index a1d7aaf..8b4da54 100644
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -644,7 +644,7 @@ static int unionfs_mkdir(struct inode *parent, struct dentry *dentry, int mode)
unionfs_set_lower_dentry_idx(dentry, i, NULL);
}
}
- set_dbend(dentry, bindex);
+ dbend(dentry) = bindex;

/*
* Only INTERPOSE_LOOKUP can return a value other than 0 on
diff --git a/fs/unionfs/lookup.c b/fs/unionfs/lookup.c
index 7f512c2..33be53c 100644
--- a/fs/unionfs/lookup.c
+++ b/fs/unionfs/lookup.c
@@ -206,8 +206,7 @@ struct dentry *unionfs_lookup_backend(struct dentry *dentry,
if (wh_lower_dentry->d_inode) {
/* We found a whiteout so let's give up. */
if (S_ISREG(wh_lower_dentry->d_inode->i_mode)) {
- set_dbend(dentry, bindex);
- set_dbopaque(dentry, bindex);
+ dbend(dentry) = dbopaque(dentry) = bindex;
dput(wh_lower_dentry);
break;
}
@@ -276,7 +275,7 @@ struct dentry *unionfs_lookup_backend(struct dentry *dentry,

/* store underlying dentry */
if (dbstart(dentry) == -1)
- set_dbstart(dentry, bindex);
+ dbstart(dentry) = bindex;
unionfs_set_lower_dentry_idx(dentry, bindex, lower_dentry);
/*
* FIXME: the following line needs to get fixed to allow
@@ -285,7 +284,7 @@ struct dentry *unionfs_lookup_backend(struct dentry *dentry,
unionfs_set_lower_mnt_idx(dentry, bindex,
unionfs_mntget(parent_dentry,
bindex));
- set_dbend(dentry, bindex);
+ dbend(dentry) = bindex;

/* update parent directory's atime with the bindex */
fsstack_copy_attr_atime(parent_dentry->d_inode,
@@ -306,8 +305,7 @@ struct dentry *unionfs_lookup_backend(struct dentry *dentry,
err = opaque;
goto out_free;
} else if (opaque) {
- set_dbend(dentry, bindex);
- set_dbopaque(dentry, bindex);
+ dbend(dentry) = dbopaque(dentry) = bindex;
break;
}
}
@@ -353,8 +351,7 @@ out_negative:
first_lower_dentry);
unionfs_set_lower_mnt_idx(dentry, first_dentry_offset,
first_lower_mnt);
- set_dbstart(dentry, first_dentry_offset);
- set_dbend(dentry, first_dentry_offset);
+ dbstart(dentry) = dbend(dentry) = first_dentry_offset;

if (lookupmode == INTERPOSE_REVAL_NEG)
BUG_ON(dentry->d_inode != NULL);
@@ -421,8 +418,7 @@ out_free:
}
kfree(UNIONFS_D(dentry)->lower_paths);
UNIONFS_D(dentry)->lower_paths = NULL;
- set_dbstart(dentry, -1);
- set_dbend(dentry, -1);
+ dbstart(dentry) = dbend(dentry) = -1;

out:
if (!err && UNIONFS_D(dentry)) {
@@ -582,7 +578,7 @@ void update_bstart(struct dentry *dentry)
if (!lower_dentry)
continue;
if (lower_dentry->d_inode) {
- set_dbstart(dentry, bindex);
+ dbstart(dentry) = bindex;
break;
}
dput(lower_dentry);
diff --git a/fs/unionfs/main.c b/fs/unionfs/main.c
index b76264a..b8f84bb 100644
--- a/fs/unionfs/main.c
+++ b/fs/unionfs/main.c
@@ -675,8 +675,8 @@ static int unionfs_read_super(struct super_block *sb, void *raw_data,
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);
+ dbstart(sb->s_root) = bstart;
+ 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);
diff --git a/fs/unionfs/rename.c b/fs/unionfs/rename.c
index cc16eb2..fe8d877 100644
--- a/fs/unionfs/rename.c
+++ b/fs/unionfs/rename.c
@@ -156,9 +156,9 @@ out:
if (!err) {
/* Fixup the new_dentry. */
if (bindex < dbstart(new_dentry))
- set_dbstart(new_dentry, bindex);
+ dbstart(new_dentry) = bindex;
else if (bindex > dbend(new_dentry))
- set_dbend(new_dentry, bindex);
+ dbend(new_dentry) = bindex;
}

kfree(wh_name);
@@ -298,7 +298,7 @@ static int do_unionfs_rename(struct inode *old_dir,
&nd);
unlock_dir(lower_parent);
if (!local_err) {
- set_dbopaque(old_dentry, bwh_old);
+ dbopaque(old_dentry) = bwh_old;
} else {
/*
* we can't fix anything now, so we cop-out and use
@@ -426,9 +426,9 @@ static int may_rename_dir(struct dentry *dentry)
if (dbend(dentry) == bstart || dbopaque(dentry) == bstart)
return 0;

- set_dbstart(dentry, bstart + 1);
+ dbstart(dentry) = bstart + 1;
err = check_empty(dentry, NULL);
- set_dbstart(dentry, bstart);
+ dbstart(dentry) = bstart;
if (err == -ENOTEMPTY)
err = -EXDEV;
return err;
diff --git a/fs/unionfs/subr.c b/fs/unionfs/subr.c
index 1a40f63..b76fa7a 100644
--- a/fs/unionfs/subr.c
+++ b/fs/unionfs/subr.c
@@ -107,7 +107,7 @@ int create_whiteout(struct dentry *dentry, int start)

/* set dbopaque so that lookup will not proceed after this branch */
if (!err)
- set_dbopaque(dentry, bindex);
+ dbopaque(dentry) = bindex;

out:
kfree(name);
@@ -195,7 +195,7 @@ int make_dir_opaque(struct dentry *dentry, int bindex)
if (!diropq->d_inode)
err = vfs_create(lower_dir, diropq, S_IRUGO, &nd);
if (!err)
- set_dbopaque(dentry, bindex);
+ dbopaque(dentry) = bindex;
release_lower_nd(&nd, err);

dput(diropq);
diff --git a/fs/unionfs/super.c b/fs/unionfs/super.c
index b110760..1a172af 100644
--- a/fs/unionfs/super.c
+++ b/fs/unionfs/super.c
@@ -771,7 +771,7 @@ out_no_change:
/* update our unionfs_sb_info and root dentry index of last branch */
i = sbmax(sb); /* save no. of branches to release at end */
sbend(sb) = new_branches - 1;
- set_dbend(sb->s_root, new_branches - 1);
+ dbend(sb->s_root) = new_branches - 1;
old_ibstart = ibstart(sb->s_root->d_inode);
old_ibend = ibend(sb->s_root->d_inode);
ibend(sb->s_root->d_inode) = new_branches - 1;
--
1.5.2.2

2008-07-30 02:48:16

by Erez Zadok

[permalink] [raw]
Subject: [PATCH 06/19] Unionfs: simplify stale-inode detection code

Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/dentry.c | 16 +++++++++++++++-
fs/unionfs/lookup.c | 10 ++--------
fs/unionfs/main.c | 14 +-------------
fs/unionfs/union.h | 2 +-
4 files changed, 19 insertions(+), 23 deletions(-)

diff --git a/fs/unionfs/dentry.c b/fs/unionfs/dentry.c
index 51c0baf..cd4611b 100644
--- a/fs/unionfs/dentry.c
+++ b/fs/unionfs/dentry.c
@@ -18,6 +18,20 @@

#include "union.h"

+bool is_negative_lower(const struct dentry *dentry)
+{
+ int bindex;
+ struct dentry *lower_dentry;
+
+ BUG_ON(!dentry || dbstart(dentry) < 0);
+ for (bindex = dbstart(dentry); bindex <= dbend(dentry); bindex++) {
+ lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ /* XXX: what if lower_dentry is NULL? */
+ if (lower_dentry && lower_dentry->d_inode)
+ return false;
+ }
+ return true;
+}

static inline void __dput_lowers(struct dentry *dentry, int start, int end)
{
@@ -113,7 +127,7 @@ static bool __unionfs_d_revalidate_one(struct dentry *dentry,
dentry = result;
}

- if (unlikely(positive && UNIONFS_I(dentry->d_inode)->stale)) {
+ if (unlikely(positive && is_negative_lower(dentry))) {
make_bad_inode(dentry->d_inode);
d_drop(dentry);
valid = false;
diff --git a/fs/unionfs/lookup.c b/fs/unionfs/lookup.c
index f6bb748..1ba7103 100644
--- a/fs/unionfs/lookup.c
+++ b/fs/unionfs/lookup.c
@@ -320,11 +320,9 @@ out_negative:
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;
+ if (lookupmode == INTERPOSE_REVAL)
goto out;
- }
+
if (!lower_dir_dentry) {
err = -ENOENT;
goto out;
@@ -424,7 +422,6 @@ out:
!UNIONFS_I(dentry->d_inode)->lower_inodes)) {
unionfs_mntput(dentry->d_sb->s_root, bstart);
dput(first_lower_dentry);
- UNIONFS_I(dentry->d_inode)->stale = 1;
}
}
kfree(whname);
@@ -433,9 +430,6 @@ out:
unionfs_unlock_dentry(dentry);
if (!err && d_interposed)
return d_interposed;
- if (dentry->d_inode && UNIONFS_I(dentry->d_inode)->stale &&
- first_dentry_offset >= 0)
- unionfs_mntput(dentry->d_sb->s_root, first_dentry_offset);
return ERR_PTR(err);
}

diff --git a/fs/unionfs/main.c b/fs/unionfs/main.c
index b8f84bb..a9d2cb6 100644
--- a/fs/unionfs/main.c
+++ b/fs/unionfs/main.c
@@ -83,25 +83,13 @@ struct dentry *unionfs_interpose(struct dentry *dentry, struct super_block *sb,
{
int err = 0;
struct inode *inode;
- int is_negative_dentry = 1;
- int bindex, bstart, bend;
int need_fill_inode = 1;
struct dentry *spliced = NULL;

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);
+ BUG_ON(is_negative_lower(dentry));

/*
* We allocate our new inode below by calling unionfs_iget,
diff --git a/fs/unionfs/union.h b/fs/unionfs/union.h
index 7ddbad1..f5cf09b 100644
--- a/fs/unionfs/union.h
+++ b/fs/unionfs/union.h
@@ -99,7 +99,6 @@ 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;
@@ -377,6 +376,7 @@ extern bool __unionfs_d_revalidate_one_locked(struct dentry *dentry,
bool willwrite);
extern bool __unionfs_d_revalidate_chain(struct dentry *dentry,
struct nameidata *nd, bool willwrite);
+extern bool is_negative_lower(const struct dentry *dentry);
extern bool is_newer_lower(const struct dentry *dentry);
extern void purge_sb_data(struct super_block *sb);

--
1.5.2.2

2008-07-30 02:48:36

by Erez Zadok

[permalink] [raw]
Subject: [PATCH 05/19] Unionfs: create and consolidate helpers to path-put lower objects

Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/commonfops.c | 9 +------
fs/unionfs/copyup.c | 23 ++++++------------
fs/unionfs/dentry.c | 31 ++------------------------
fs/unionfs/fanout.h | 55 +++++++++++++++++++++++++++++++++++++++++++++++
fs/unionfs/lookup.c | 14 +++--------
5 files changed, 71 insertions(+), 61 deletions(-)

diff --git a/fs/unionfs/commonfops.c b/fs/unionfs/commonfops.c
index 585c5c9..5816d41 100644
--- a/fs/unionfs/commonfops.c
+++ b/fs/unionfs/commonfops.c
@@ -275,15 +275,8 @@ static int do_delayed_copyup(struct file *file)
fput(unionfs_lower_file_idx(file, bindex));
unionfs_set_lower_file_idx(file, bindex, NULL);
}
- if (unionfs_lower_mnt_idx(dentry, bindex)) {
- unionfs_mntput(dentry, bindex);
- unionfs_set_lower_mnt_idx(dentry, bindex, NULL);
- }
- if (unionfs_lower_dentry_idx(dentry, bindex)) {
- dput(unionfs_lower_dentry_idx(dentry, bindex));
- unionfs_set_lower_dentry_idx(dentry, bindex, NULL);
- }
}
+ path_put_lowers(dentry, bstart, bend, false);
iput_lowers(dentry->d_inode, bstart, bend, false);
/* for reg file, we only open it "once" */
fbend(file) = fbstart(file);
diff --git a/fs/unionfs/copyup.c b/fs/unionfs/copyup.c
index f20b984..55d48aa 100644
--- a/fs/unionfs/copyup.c
+++ b/fs/unionfs/copyup.c
@@ -866,22 +866,15 @@ void unionfs_postcopyup_setmnt(struct dentry *dentry)
*/
void unionfs_postcopyup_release(struct dentry *dentry)
{
- int bindex;
+ int bstart, bend;

BUG_ON(S_ISDIR(dentry->d_inode->i_mode));
- for (bindex = dbstart(dentry)+1; bindex <= dbend(dentry); bindex++) {
- if (unionfs_lower_mnt_idx(dentry, bindex)) {
- unionfs_mntput(dentry, bindex);
- unionfs_set_lower_mnt_idx(dentry, bindex, NULL);
- }
- if (unionfs_lower_dentry_idx(dentry, bindex)) {
- dput(unionfs_lower_dentry_idx(dentry, bindex));
- unionfs_set_lower_dentry_idx(dentry, bindex, NULL);
- }
- }
- iput_lowers(dentry->d_inode, dbstart(dentry)+1, dbend(dentry), false);
+ bstart = dbstart(dentry);
+ bend = dbend(dentry);
+
+ path_put_lowers(dentry, bstart + 1, bend, false);
+ iput_lowers(dentry->d_inode, bstart + 1, bend, false);

- bindex = dbstart(dentry);
- dbend(dentry) = bindex;
- ibend(dentry->d_inode) = ibstart(dentry->d_inode) = bindex;
+ dbend(dentry) = bstart;
+ ibend(dentry->d_inode) = ibstart(dentry->d_inode) = bstart;
}
diff --git a/fs/unionfs/dentry.c b/fs/unionfs/dentry.c
index a7960ac..51c0baf 100644
--- a/fs/unionfs/dentry.c
+++ b/fs/unionfs/dentry.c
@@ -455,8 +455,6 @@ static int unionfs_d_revalidate(struct dentry *dentry, struct nameidata *nd)

static void unionfs_d_release(struct dentry *dentry)
{
- int bindex, bstart, bend;
-
unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
if (unlikely(!UNIONFS_D(dentry)))
goto out; /* skip if no lower branches */
@@ -471,20 +469,7 @@ static void unionfs_d_release(struct dentry *dentry)
}

/* Release all the lower dentries */
- bstart = dbstart(dentry);
- bend = dbend(dentry);
- for (bindex = bstart; bindex <= bend; bindex++) {
- dput(unionfs_lower_dentry_idx(dentry, bindex));
- unionfs_set_lower_dentry_idx(dentry, bindex, NULL);
- /* NULL lower mnt is ok if this is a negative dentry */
- if (!dentry->d_inode && !unionfs_lower_mnt_idx(dentry, bindex))
- continue;
- unionfs_mntput(dentry, bindex);
- 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;
+ path_put_lowers_all(dentry, true);

unionfs_unlock_dentry(dentry);

@@ -500,7 +485,7 @@ out:
*/
static void unionfs_d_iput(struct dentry *dentry, struct inode *inode)
{
- int bindex, rc;
+ int rc;

BUG_ON(!dentry);
unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
@@ -508,17 +493,7 @@ static void unionfs_d_iput(struct dentry *dentry, struct inode *inode)

if (!UNIONFS_D(dentry) || dbstart(dentry) < 0)
goto drop_lower_inodes;
- for (bindex = dbstart(dentry); bindex <= dbend(dentry); bindex++) {
- if (unionfs_lower_mnt_idx(dentry, bindex)) {
- unionfs_mntput(dentry, bindex);
- unionfs_set_lower_mnt_idx(dentry, bindex, NULL);
- }
- if (unionfs_lower_dentry_idx(dentry, bindex)) {
- dput(unionfs_lower_dentry_idx(dentry, bindex));
- unionfs_set_lower_dentry_idx(dentry, bindex, NULL);
- }
- }
- dbstart(dentry) = dbend(dentry) = -1;
+ path_put_lowers_all(dentry, false);

drop_lower_inodes:
rc = atomic_read(&inode->i_count);
diff --git a/fs/unionfs/fanout.h b/fs/unionfs/fanout.h
index 94421f8..d8977bd 100644
--- a/fs/unionfs/fanout.h
+++ b/fs/unionfs/fanout.h
@@ -326,4 +326,59 @@ static inline void iput_lowers_all(struct inode *inode, bool free_lower)
ibstart(inode) = ibend(inode) = -1;
}

+/*
+ * dput/mntput all lower dentries and vfsmounts of an unionfs dentry, from
+ * bstart to bend. If @free_lower is true, then also kfree the memory used
+ * to hold the lower object pointers.
+ *
+ * XXX: implement using path_put VFS macros
+ */
+static inline void path_put_lowers(struct dentry *dentry,
+ int bstart, int bend, bool free_lower)
+{
+ struct dentry *lower_dentry;
+ struct vfsmount *lower_mnt;
+ int bindex;
+
+ BUG_ON(!dentry);
+ BUG_ON(!UNIONFS_D(dentry));
+ BUG_ON(bstart < 0);
+
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ if (lower_dentry) {
+ unionfs_set_lower_dentry_idx(dentry, bindex, NULL);
+ dput(lower_dentry);
+ }
+ lower_mnt = unionfs_lower_mnt_idx(dentry, bindex);
+ if (lower_mnt) {
+ unionfs_set_lower_mnt_idx(dentry, bindex, NULL);
+ mntput(lower_mnt);
+ }
+ }
+
+ if (free_lower) {
+ kfree(UNIONFS_D(dentry)->lower_paths);
+ UNIONFS_D(dentry)->lower_paths = NULL;
+ }
+}
+
+/*
+ * dput/mntput all lower dentries and vfsmounts, and reset start/end branch
+ * indices to -1.
+ */
+static inline void path_put_lowers_all(struct dentry *dentry, bool free_lower)
+{
+ int bstart, bend;
+
+ BUG_ON(!dentry);
+ BUG_ON(!UNIONFS_D(dentry));
+ bstart = dbstart(dentry);
+ bend = dbend(dentry);
+ BUG_ON(bstart < 0);
+
+ path_put_lowers(dentry, bstart, bend, free_lower);
+ dbstart(dentry) = dbend(dentry) = -1;
+}
+
#endif /* not _FANOUT_H */
diff --git a/fs/unionfs/lookup.c b/fs/unionfs/lookup.c
index 33be53c..f6bb748 100644
--- a/fs/unionfs/lookup.c
+++ b/fs/unionfs/lookup.c
@@ -407,18 +407,12 @@ 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));
- unionfs_mntput(dentry, bindex);
- }
- }
+ /* should dput/mntput all the underlying dentries on error condition */
+ if (dbstart(dentry) >= 0)
+ path_put_lowers_all(dentry, false);
+ /* free lower_paths unconditionally */
kfree(UNIONFS_D(dentry)->lower_paths);
UNIONFS_D(dentry)->lower_paths = NULL;
- dbstart(dentry) = dbend(dentry) = -1;

out:
if (!err && UNIONFS_D(dentry)) {
--
1.5.2.2

2008-07-30 02:48:52

by Erez Zadok

[permalink] [raw]
Subject: [PATCH 04/19] Unionfs: create and consolidate helpers to iput lower objects

Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/commonfops.c | 5 +---
fs/unionfs/copyup.c | 5 +--
fs/unionfs/dentry.c | 25 +-----------------------
fs/unionfs/fanout.h | 49 +++++++++++++++++++++++++++++++++++++++++++++++
fs/unionfs/super.c | 6 +----
fs/unionfs/unlink.c | 22 ++------------------
6 files changed, 57 insertions(+), 55 deletions(-)

diff --git a/fs/unionfs/commonfops.c b/fs/unionfs/commonfops.c
index 6f61fb0..585c5c9 100644
--- a/fs/unionfs/commonfops.c
+++ b/fs/unionfs/commonfops.c
@@ -280,14 +280,11 @@ static int do_delayed_copyup(struct file *file)
unionfs_set_lower_mnt_idx(dentry, bindex, NULL);
}
if (unionfs_lower_dentry_idx(dentry, bindex)) {
- BUG_ON(!dentry->d_inode);
- iput(unionfs_lower_inode_idx(dentry->d_inode, bindex));
- unionfs_set_lower_inode_idx(dentry->d_inode, bindex,
- NULL);
dput(unionfs_lower_dentry_idx(dentry, bindex));
unionfs_set_lower_dentry_idx(dentry, bindex, NULL);
}
}
+ iput_lowers(dentry->d_inode, bstart, bend, false);
/* for reg file, we only open it "once" */
fbend(file) = fbstart(file);
dbend(dentry) = dbstart(dentry);
diff --git a/fs/unionfs/copyup.c b/fs/unionfs/copyup.c
index b918897..f20b984 100644
--- a/fs/unionfs/copyup.c
+++ b/fs/unionfs/copyup.c
@@ -877,11 +877,10 @@ void unionfs_postcopyup_release(struct dentry *dentry)
if (unionfs_lower_dentry_idx(dentry, bindex)) {
dput(unionfs_lower_dentry_idx(dentry, bindex));
unionfs_set_lower_dentry_idx(dentry, bindex, NULL);
- iput(unionfs_lower_inode_idx(dentry->d_inode, bindex));
- unionfs_set_lower_inode_idx(dentry->d_inode, bindex,
- NULL);
}
}
+ iput_lowers(dentry->d_inode, dbstart(dentry)+1, dbend(dentry), false);
+
bindex = dbstart(dentry);
dbend(dentry) = bindex;
ibend(dentry->d_inode) = ibstart(dentry->d_inode) = bindex;
diff --git a/fs/unionfs/dentry.c b/fs/unionfs/dentry.c
index 44b17ce..a7960ac 100644
--- a/fs/unionfs/dentry.c
+++ b/fs/unionfs/dentry.c
@@ -35,22 +35,6 @@ static inline void __dput_lowers(struct dentry *dentry, int start, int end)
}
}

-static inline void __iput_lowers(struct inode *inode, int start, int end)
-{
- struct inode *lower_inode;
- int bindex;
-
- if (start < 0)
- return;
- for (bindex = start; bindex <= end; bindex++) {
- lower_inode = unionfs_lower_inode_idx(inode, bindex);
- if (!lower_inode)
- continue;
- unionfs_set_lower_inode_idx(inode, bindex, NULL);
- iput(lower_inode);
- }
-}
-
/*
* Revalidate a single dentry.
* Assume that dentry's info node is locked.
@@ -111,14 +95,7 @@ static bool __unionfs_d_revalidate_one(struct dentry *dentry,
interpose_flag = INTERPOSE_REVAL_NEG;
if (positive) {
interpose_flag = INTERPOSE_REVAL;
-
- bstart = ibstart(dentry->d_inode);
- bend = ibend(dentry->d_inode);
- __iput_lowers(dentry->d_inode, bstart, bend);
- 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;
+ iput_lowers_all(dentry->d_inode, true);
}

result = unionfs_lookup_backend(dentry, &lowernd,
diff --git a/fs/unionfs/fanout.h b/fs/unionfs/fanout.h
index 69a8e8f..94421f8 100644
--- a/fs/unionfs/fanout.h
+++ b/fs/unionfs/fanout.h
@@ -277,4 +277,53 @@ static inline void verify_locked(struct dentry *d)
BUG_ON(!mutex_is_locked(&UNIONFS_D(d)->lock));
}

+/* macros to put lower objects */
+
+/*
+ * iput lower inodes of an unionfs dentry, from bstart to bend. If
+ * @free_lower is true, then also kfree the memory used to hold the lower
+ * object pointers.
+ */
+static inline void iput_lowers(struct inode *inode,
+ int bstart, int bend, bool free_lower)
+{
+ struct inode *lower_inode;
+ int bindex;
+
+ BUG_ON(!inode);
+ BUG_ON(!UNIONFS_I(inode));
+ BUG_ON(bstart < 0);
+
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ lower_inode = unionfs_lower_inode_idx(inode, bindex);
+ if (lower_inode) {
+ unionfs_set_lower_inode_idx(inode, bindex, NULL);
+ /* see Documentation/filesystems/unionfs/issues.txt */
+ lockdep_off();
+ iput(lower_inode);
+ lockdep_on();
+ }
+ }
+
+ if (free_lower) {
+ kfree(UNIONFS_I(inode)->lower_inodes);
+ UNIONFS_I(inode)->lower_inodes = NULL;
+ }
+}
+
+/* iput all lower inodes, and reset start/end branch indices to -1 */
+static inline void iput_lowers_all(struct inode *inode, bool free_lower)
+{
+ int bstart, bend;
+
+ BUG_ON(!inode);
+ BUG_ON(!UNIONFS_I(inode));
+ bstart = ibstart(inode);
+ bend = ibend(inode);
+ BUG_ON(bstart < 0);
+
+ iput_lowers(inode, bstart, bend, free_lower);
+ ibstart(inode) = ibend(inode) = -1;
+}
+
#endif /* not _FANOUT_H */
diff --git a/fs/unionfs/super.c b/fs/unionfs/super.c
index 1a172af..9715529 100644
--- a/fs/unionfs/super.c
+++ b/fs/unionfs/super.c
@@ -789,11 +789,7 @@ out_no_change:
new_lower_inodes[i] = lower_dentry->d_inode;
}
/* 2. release reference on all older lower inodes */
- for (i = old_ibstart; i <= old_ibend; i++) {
- iput(unionfs_lower_inode_idx(sb->s_root->d_inode, i));
- unionfs_set_lower_inode_idx(sb->s_root->d_inode, i, NULL);
- }
- kfree(UNIONFS_I(sb->s_root->d_inode)->lower_inodes);
+ iput_lowers(sb->s_root->d_inode, old_ibstart, old_ibend, true);
/* 3. update root dentry's inode to new lower_inodes array */
UNIONFS_I(sb->s_root->d_inode)->lower_inodes = new_lower_inodes;
new_lower_inodes = NULL;
diff --git a/fs/unionfs/unlink.c b/fs/unionfs/unlink.c
index cad0386..3d6ca5f 100644
--- a/fs/unionfs/unlink.c
+++ b/fs/unionfs/unlink.c
@@ -146,12 +146,8 @@ int unionfs_unlink(struct inode *dir, struct dentry *dentry)
/* call d_drop so the system "forgets" about us */
if (!err) {
unionfs_postcopyup_release(dentry);
- if (inode->i_nlink == 0) {
- /* drop lower inodes */
- iput(unionfs_lower_inode(inode));
- unionfs_set_lower_inode(inode, NULL);
- ibstart(inode) = ibend(inode) = -1;
- }
+ if (inode->i_nlink == 0) /* drop lower inodes */
+ iput_lowers_all(inode, false);
d_drop(dentry);
/*
* if unlink/whiteout succeeded, parent dir mtime has
@@ -264,21 +260,9 @@ out:
* about us.
*/
if (!err) {
- struct inode *inode = dentry->d_inode;
- BUG_ON(!inode);
- iput(unionfs_lower_inode_idx(inode, dstart));
- unionfs_set_lower_inode_idx(inode, dstart, NULL);
+ iput_lowers_all(dentry->d_inode, false);
dput(unionfs_lower_dentry_idx(dentry, dstart));
unionfs_set_lower_dentry_idx(dentry, dstart, NULL);
- /*
- * If the last directory is unlinked, then mark istart/end
- * as -1, (to maintain the invariant that if there are no
- * lower objects, then branch index start and end are set to
- * -1).
- */
- if (!unionfs_lower_inode_idx(inode, dstart) &&
- !unionfs_lower_inode_idx(inode, dend))
- ibstart(inode) = ibend(inode) = -1;
d_drop(dentry);
/* update our lower vfsmnts, in case a copyup took place */
unionfs_postcopyup_setmnt(dentry);
--
1.5.2.2

2008-07-30 02:49:17

by Erez Zadok

[permalink] [raw]
Subject: [PATCH 08/19] Unionfs: lookup overhaul using vfs_path_lookup

Rework the lookup code to use vfs_path_lookup as much as possible, to ensure
that we have a vfsmount at this critical stage. This is necessary for the
upcoming VFS API change from vfs_* to path_* methods. This change also
allows unionfs to cross bind mounts and other mounts on lower branches.

Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/commonfops.c | 3 +-
fs/unionfs/inode.c | 18 ++-
fs/unionfs/lookup.c | 348 ++++++++++++++++++++++++++++++++++++++++++++++-
fs/unionfs/rename.c | 1 +
fs/unionfs/union.h | 4 +
5 files changed, 366 insertions(+), 8 deletions(-)

diff --git a/fs/unionfs/commonfops.c b/fs/unionfs/commonfops.c
index 5816d41..df002d5 100644
--- a/fs/unionfs/commonfops.c
+++ b/fs/unionfs/commonfops.c
@@ -422,7 +422,8 @@ reval_dentry:
dgen = atomic_read(&UNIONFS_D(dentry)->generation);

if (unlikely(sbgen > dgen)) {
- pr_debug("unionfs: retry dentry revalidation\n");
+ pr_debug("unionfs: retry dentry %s revalidation\n",
+ dentry->d_name.name);
schedule();
goto reval_dentry;
}
diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c
index bfebc0c..f81ebb6 100644
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -178,6 +178,7 @@ static struct dentry *unionfs_lookup(struct inode *parent,
{
struct path path_save = {NULL, NULL};
struct dentry *ret;
+ int err = 0;

unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
if (dentry != dentry->d_parent)
@@ -193,7 +194,14 @@ static struct dentry *unionfs_lookup(struct inode *parent,
* unionfs_lookup_backend returns a locked dentry upon success,
* so we'll have to unlock it below.
*/
- ret = unionfs_lookup_backend(dentry, nd, INTERPOSE_LOOKUP);
+
+ /* allocate dentry private data. We free it in ->d_release */
+ err = new_dentry_private_data(dentry, UNIONFS_DMUTEX_CHILD);
+ if (unlikely(err)) {
+ ret = ERR_PTR(err);
+ goto out;
+ }
+ ret = unionfs_lookup_full(dentry, nd, INTERPOSE_LOOKUP);

/* restore the dentry & vfsmnt in namei */
if (nd) {
@@ -203,6 +211,11 @@ static struct dentry *unionfs_lookup(struct inode *parent,
if (!IS_ERR(ret)) {
if (ret)
dentry = ret;
+ /* lookup_full can return multiple positive dentries */
+ if (dentry->d_inode && !S_ISDIR(dentry->d_inode->i_mode)) {
+ BUG_ON(dbstart(dentry) < 0);
+ unionfs_postcopyup_release(dentry);
+ }
unionfs_copy_attr_times(dentry->d_inode);
/* parent times may have changed */
unionfs_copy_attr_times(dentry->d_parent->d_inode);
@@ -212,9 +225,10 @@ static struct dentry *unionfs_lookup(struct inode *parent,
if (!IS_ERR(ret)) {
unionfs_check_dentry(dentry);
unionfs_check_nd(nd);
- unionfs_unlock_dentry(dentry);
}
+ unionfs_unlock_dentry(dentry);

+out:
if (dentry != dentry->d_parent) {
unionfs_check_dentry(dentry->d_parent);
unionfs_unlock_dentry(dentry->d_parent);
diff --git a/fs/unionfs/lookup.c b/fs/unionfs/lookup.c
index 0f62087..37adf66 100644
--- a/fs/unionfs/lookup.c
+++ b/fs/unionfs/lookup.c
@@ -18,7 +18,49 @@

#include "union.h"

-static int realloc_dentry_private_data(struct dentry *dentry);
+/*
+ * Lookup one path component @name relative to a <base,mnt> path pair.
+ * Behaves nearly the same as lookup_one_len (i.e., return negative dentry
+ * on ENOENT), but uses the @mnt passed, so it can cross bind mounts and
+ * other lower mounts properly. If @new_mnt is non-null, will fill in the
+ * new mnt there. Caller is responsible to dput/mntput/path_put returned
+ * @dentry and @new_mnt.
+ */
+struct dentry *__lookup_one(struct dentry *base, struct vfsmount *mnt,
+ const char *name, struct vfsmount **new_mnt)
+{
+ struct dentry *dentry = NULL;
+ struct nameidata lower_nd;
+ int err;
+
+ /* we use flags=0 to get basic lookup */
+ err = vfs_path_lookup(base, mnt, name, 0, &lower_nd);
+
+ switch (err) {
+ case 0: /* no error */
+ dentry = lower_nd.path.dentry;
+ if (new_mnt)
+ *new_mnt = lower_nd.path.mnt; /* rc already inc'ed */
+ break;
+ case -ENOENT:
+ /*
+ * We don't consider ENOENT an error, and we want to return
+ * a negative dentry (ala lookup_one_len). As we know
+ * there was no inode for this name before (-ENOENT), then
+ * it's safe to call lookup_one_len (which doesn't take a
+ * vfsmount).
+ */
+ dentry = lookup_one_len(name, base, strlen(name));
+ if (new_mnt)
+ *new_mnt = mntget(lower_nd.path.mnt);
+ break;
+ default: /* all other real errors */
+ dentry = ERR_PTR(err);
+ break;
+ }
+
+ return dentry;
+}

/*
* Main (and complex) driver function for Unionfs's lookup
@@ -32,7 +74,8 @@ static int realloc_dentry_private_data(struct dentry *dentry);
* dentry's info, which the caller must unlock.
*/
struct dentry *unionfs_lookup_backend(struct dentry *dentry,
- struct nameidata *nd, int lookupmode)
+ struct nameidata *nd_unused,
+ int lookupmode)
{
int err = 0;
struct dentry *lower_dentry = NULL;
@@ -361,6 +404,7 @@ out:
* Caller must lock this dentry with unionfs_lock_dentry.
*
* Returns: 0 (ok), or -ERRNO if an error occurred.
+ * XXX: get rid of _partial_lookup and make callers call _lookup_full directly
*/
int unionfs_partial_lookup(struct dentry *dentry)
{
@@ -368,7 +412,8 @@ int unionfs_partial_lookup(struct dentry *dentry)
struct nameidata nd = { .flags = 0 };
int err = -ENOSYS;

- tmp = unionfs_lookup_backend(dentry, &nd, INTERPOSE_PARTIAL);
+ tmp = unionfs_lookup_full(dentry, &nd, INTERPOSE_PARTIAL);
+
if (!tmp) {
err = 0;
goto out;
@@ -377,7 +422,7 @@ int unionfs_partial_lookup(struct dentry *dentry)
err = PTR_ERR(tmp);
goto out;
}
- /* need to change the interface */
+ /* XXX: need to change the interface */
BUG_ON(tmp != dentry);
out:
return err;
@@ -437,7 +482,7 @@ static inline int __realloc_dentry_private_data(struct dentry *dentry)
}

/* UNIONFS_D(dentry)->lock must be locked */
-static int realloc_dentry_private_data(struct dentry *dentry)
+int realloc_dentry_private_data(struct dentry *dentry)
{
if (!__realloc_dentry_private_data(dentry))
return 0;
@@ -568,3 +613,296 @@ void release_lower_nd(struct nameidata *nd, int err)
kfree(nd->intent.open.file);
#endif /* ALLOC_LOWER_ND_FILE */
}
+
+/*
+ * Main (and complex) driver function for Unionfs's lookup
+ *
+ * Returns: NULL (ok), ERR_PTR if an error occurred, or a non-null non-error
+ * PTR if d_splice returned a different dentry.
+ *
+ * If lookupmode is INTERPOSE_PARTIAL/REVAL/REVAL_NEG, the passed dentry's
+ * inode info must be locked. If lookupmode is INTERPOSE_LOOKUP (i.e., a
+ * newly looked-up dentry), then unionfs_lookup_backend will return a locked
+ * dentry's info, which the caller must unlock.
+ */
+struct dentry *unionfs_lookup_full(struct dentry *dentry,
+ struct nameidata *nd_unused, int lookupmode)
+{
+ int err = 0;
+ struct dentry *lower_dentry = NULL;
+ struct vfsmount *lower_mnt;
+ struct vfsmount *lower_dir_mnt;
+ struct dentry *wh_lower_dentry = NULL;
+ struct dentry *lower_dir_dentry = NULL;
+ struct dentry *parent_dentry = NULL;
+ struct dentry *d_interposed = NULL;
+ int bindex, bstart, bend, bopaque;
+ int opaque, num_positive = 0;
+ const char *name;
+ int namelen;
+ int pos_start, pos_end;
+
+ /*
+ * 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.
+ */
+ verify_locked(dentry);
+
+ /* must initialize dentry operations */
+ dentry->d_op = &unionfs_dops;
+
+ /* We never partial lookup the root directory. */
+ if (IS_ROOT(dentry))
+ goto out;
+ parent_dentry = dget_parent(dentry);
+
+ 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);
+
+ /* adjust bend to bopaque if needed */
+ if ((bopaque >= 0) && (bopaque < bend))
+ bend = bopaque;
+
+ /* lookup all possible dentries */
+ for (bindex = bstart; bindex <= bend; bindex++) {
+
+ lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ lower_mnt = unionfs_lower_mnt_idx(dentry, bindex);
+
+ /* skip if we already have a positive lower dentry */
+ if (lower_dentry) {
+ if (dbstart(dentry) < 0)
+ dbstart(dentry) = bindex;
+ if (bindex > dbend(dentry))
+ dbend(dentry) = bindex;
+ if (lower_dentry->d_inode)
+ num_positive++;
+ continue;
+ }
+
+ lower_dir_dentry =
+ unionfs_lower_dentry_idx(parent_dentry, bindex);
+ /* if the lower dentry's parent does not exist, skip this */
+ if (!lower_dir_dentry || !lower_dir_dentry->d_inode)
+ continue;
+
+ /* also skip it if the parent isn't a directory. */
+ if (!S_ISDIR(lower_dir_dentry->d_inode->i_mode))
+ continue; /* XXX: should be BUG_ON */
+
+ /* check for whiteouts: stop lookup if found */
+ wh_lower_dentry = lookup_whiteout(name, lower_dir_dentry);
+ if (IS_ERR(wh_lower_dentry)) {
+ err = PTR_ERR(wh_lower_dentry);
+ goto out_free;
+ }
+ if (wh_lower_dentry->d_inode) {
+ dbend(dentry) = dbopaque(dentry) = bindex;
+ if (dbstart(dentry) < 0)
+ dbstart(dentry) = bindex;
+ dput(wh_lower_dentry);
+ break;
+ }
+ dput(wh_lower_dentry);
+
+ /* Now do regular lookup; lookup @name */
+ lower_dir_mnt = unionfs_lower_mnt_idx(parent_dentry, bindex);
+ lower_mnt = NULL; /* XXX: needed? */
+
+ lower_dentry = __lookup_one(lower_dir_dentry, lower_dir_mnt,
+ name, &lower_mnt);
+
+ if (IS_ERR(lower_dentry)) {
+ err = PTR_ERR(lower_dentry);
+ goto out_free;
+ }
+ unionfs_set_lower_dentry_idx(dentry, bindex, lower_dentry);
+ BUG_ON(!lower_mnt);
+ unionfs_set_lower_mnt_idx(dentry, bindex, lower_mnt);
+
+ /* adjust dbstart/end */
+ if (dbstart(dentry) < 0)
+ dbstart(dentry) = bindex;
+ if (bindex > dbend(dentry))
+ dbend(dentry) = bindex;
+ /*
+ * We always store the lower dentries above, and update
+ * dbstart/dbend, even if the whole unionfs dentry is
+ * negative (i.e., no lower inodes).
+ */
+ if (!lower_dentry->d_inode)
+ continue;
+ num_positive++;
+
+ /*
+ * check if we just found an opaque directory, if so, stop
+ * lookups here.
+ */
+ if (!S_ISDIR(lower_dentry->d_inode->i_mode))
+ continue;
+ opaque = is_opaque_dir(dentry, bindex);
+ if (opaque < 0) {
+ err = opaque;
+ goto out_free;
+ } else if (opaque) {
+ dbend(dentry) = dbopaque(dentry) = bindex;
+ break;
+ }
+ dbend(dentry) = bindex;
+
+ /* update parent directory's atime with the bindex */
+ fsstack_copy_attr_atime(parent_dentry->d_inode,
+ lower_dir_dentry->d_inode);
+ }
+
+ /* sanity checks, then decide if to process a negative dentry */
+ BUG_ON(dbstart(dentry) < 0 && dbend(dentry) >= 0);
+ BUG_ON(dbstart(dentry) >= 0 && dbend(dentry) < 0);
+
+ if (num_positive > 0)
+ goto out_positive;
+
+ /*** handle NEGATIVE dentries ***/
+
+ /*
+ * If negative, keep only first lower negative dentry, to save on
+ * memory.
+ */
+ if (dbstart(dentry) < dbend(dentry)) {
+ path_put_lowers(dentry, dbstart(dentry) + 1,
+ dbend(dentry), false);
+ dbend(dentry) = dbstart(dentry);
+ }
+ if (lookupmode == INTERPOSE_PARTIAL)
+ goto out;
+ if (lookupmode == INTERPOSE_LOOKUP) {
+ /*
+ * If all we found was a whiteout in the first available
+ * branch, then create a negative dentry for a possibly new
+ * file to be created.
+ */
+ if (dbopaque(dentry) < 0)
+ goto out;
+ /* XXX: need to get mnt here */
+ bindex = dbstart(dentry);
+ if (unionfs_lower_dentry_idx(dentry, bindex))
+ goto out;
+ lower_dir_dentry =
+ unionfs_lower_dentry_idx(parent_dentry, bindex);
+ if (!lower_dir_dentry || !lower_dir_dentry->d_inode)
+ goto out;
+ if (!S_ISDIR(lower_dir_dentry->d_inode->i_mode))
+ goto out; /* XXX: should be BUG_ON */
+ /* XXX: do we need to cross bind mounts here? */
+ lower_dentry = lookup_one_len(name, lower_dir_dentry, namelen);
+ if (IS_ERR(lower_dentry)) {
+ err = PTR_ERR(lower_dentry);
+ goto out;
+ }
+ /* XXX: need to mntget/mntput as needed too! */
+ unionfs_set_lower_dentry_idx(dentry, bindex, lower_dentry);
+ /* XXX: wrong mnt for crossing bind mounts! */
+ lower_mnt = unionfs_mntget(dentry->d_sb->s_root, bindex);
+ unionfs_set_lower_mnt_idx(dentry, bindex, lower_mnt);
+
+ goto out;
+ }
+
+ /* if we're revalidating a positive dentry, don't make it negative */
+ if (lookupmode != INTERPOSE_REVAL)
+ d_add(dentry, NULL);
+
+ goto out;
+
+out_positive:
+ /*** handle POSITIVE dentries ***/
+
+ /*
+ * This unionfs dentry is positive (at least one lower inode
+ * exists), so scan entire dentry from beginning to end, and remove
+ * any negative lower dentries, if any. Then, update dbstart/dbend
+ * to reflect the start/end of positive dentries.
+ */
+ pos_start = pos_end = -1;
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ lower_dentry = unionfs_lower_dentry_idx(dentry,
+ bindex);
+ if (lower_dentry && lower_dentry->d_inode) {
+ if (pos_start < 0)
+ pos_start = bindex;
+ if (bindex > pos_end)
+ pos_end = bindex;
+ continue;
+ }
+ path_put_lowers(dentry, bindex, bindex, false);
+ }
+ if (pos_start >= 0)
+ dbstart(dentry) = pos_start;
+ if (pos_end >= 0)
+ dbend(dentry) = pos_end;
+
+ /* Partial lookups need to re-interpose, or throw away older negs. */
+ if (lookupmode == INTERPOSE_PARTIAL) {
+ if (dentry->d_inode) {
+ unionfs_reinterpose(dentry);
+ goto out;
+ }
+
+ /*
+ * This dentry was positive, so it is as if we had a
+ * negative revalidation.
+ */
+ lookupmode = INTERPOSE_REVAL_NEG;
+ update_bstart(dentry);
+ }
+
+ /*
+ * Interpose can return a dentry if d_splice returned a different
+ * dentry.
+ */
+ d_interposed = unionfs_interpose(dentry, dentry->d_sb, lookupmode);
+ if (IS_ERR(d_interposed))
+ err = PTR_ERR(d_interposed);
+ else if (d_interposed)
+ dentry = d_interposed;
+
+ if (!err)
+ goto out;
+ d_drop(dentry);
+
+out_free:
+ /* should dput/mntput all the underlying dentries on error condition */
+ if (dbstart(dentry) >= 0)
+ path_put_lowers_all(dentry, false);
+ /* free lower_paths unconditionally */
+ kfree(UNIONFS_D(dentry)->lower_paths);
+ UNIONFS_D(dentry)->lower_paths = NULL;
+
+out:
+ if (dentry && UNIONFS_D(dentry)) {
+ BUG_ON(dbstart(dentry) < 0 && dbend(dentry) >= 0);
+ BUG_ON(dbstart(dentry) >= 0 && dbend(dentry) < 0);
+ }
+ if (d_interposed && UNIONFS_D(d_interposed)) {
+ BUG_ON(dbstart(d_interposed) < 0 && dbend(d_interposed) >= 0);
+ BUG_ON(dbstart(d_interposed) >= 0 && dbend(d_interposed) < 0);
+ }
+
+ dput(parent_dentry);
+ if (!err && d_interposed)
+ return d_interposed;
+ return ERR_PTR(err);
+}
diff --git a/fs/unionfs/rename.c b/fs/unionfs/rename.c
index 85f96ee..ecce9da 100644
--- a/fs/unionfs/rename.c
+++ b/fs/unionfs/rename.c
@@ -195,6 +195,7 @@ static int do_unionfs_rename(struct inode *old_dir,
struct dentry *unlink_dentry;
struct dentry *unlink_dir_dentry;

+ BUG_ON(bindex < 0);
unlink_dentry = unionfs_lower_dentry_idx(new_dentry, bindex);
if (!unlink_dentry)
continue;
diff --git a/fs/unionfs/union.h b/fs/unionfs/union.h
index 9271b3b..81f34c4 100644
--- a/fs/unionfs/union.h
+++ b/fs/unionfs/union.h
@@ -294,6 +294,7 @@ static inline void unionfs_double_lock_dentry(struct dentry *d1,
}

extern int new_dentry_private_data(struct dentry *dentry, int subclass);
+extern int realloc_dentry_private_data(struct dentry *dentry);
extern void free_dentry_private_data(struct dentry *dentry);
extern void update_bstart(struct dentry *dentry);
extern int init_lower_nd(struct nameidata *nd, unsigned int flags);
@@ -309,6 +310,9 @@ extern struct dentry *create_parents(struct inode *dir, struct dentry *dentry,

/* partial lookup */
extern int unionfs_partial_lookup(struct dentry *dentry);
+extern struct dentry *unionfs_lookup_full(struct dentry *dentry,
+ struct nameidata *nd_unused,
+ int lookupmode);

/* copies a file from dbstart to newbindex branch */
extern int copyup_file(struct inode *dir, struct file *file, int bstart,
--
1.5.2.2

2008-07-30 02:49:35

by Erez Zadok

[permalink] [raw]
Subject: [PATCH 09/19] Unionfs: free lower paths array when destroying dentry's private data

CC: Hugh Dickins <[email protected]>

Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/lookup.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/fs/unionfs/lookup.c b/fs/unionfs/lookup.c
index 37adf66..b5f64c9 100644
--- a/fs/unionfs/lookup.c
+++ b/fs/unionfs/lookup.c
@@ -450,6 +450,8 @@ void free_dentry_private_data(struct dentry *dentry)
{
if (!dentry || !dentry->d_fsdata)
return;
+ kfree(UNIONFS_D(dentry)->lower_paths);
+ UNIONFS_D(dentry)->lower_paths = NULL;
kmem_cache_free(unionfs_dentry_cachep, dentry->d_fsdata);
dentry->d_fsdata = NULL;
}
--
1.5.2.2

2008-07-30 02:50:48

by Erez Zadok

[permalink] [raw]
Subject: [PATCH 18/19] Unionfs: LOOKUP_ACCESS intent no longer exists

Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/lookup.c | 3 ---
1 files changed, 0 insertions(+), 3 deletions(-)

diff --git a/fs/unionfs/lookup.c b/fs/unionfs/lookup.c
index 0ae7f3a..0a9602a 100644
--- a/fs/unionfs/lookup.c
+++ b/fs/unionfs/lookup.c
@@ -252,9 +252,6 @@ int init_lower_nd(struct nameidata *nd, unsigned int flags)
nd->intent.open.file = file;
#endif /* ALLOC_LOWER_ND_FILE */
break;
- case LOOKUP_ACCESS:
- nd->flags = flags;
- break;
default:
/*
* We should never get here, for now.
--
1.5.2.2

2008-07-30 02:51:07

by Erez Zadok

[permalink] [raw]
Subject: [PATCH 07/19] Unionfs: overhaul whiteout code

Move all whiteout functions and helpers into a separate file, replace all
embedded whiteout code with calls to helpers. Cleanup and consolidate the
code. This will make it easier to replace the whiteout code with a
Linux-native whiteout implementation (once available).

Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/Makefile | 2 +-
fs/unionfs/dirfops.c | 22 +-
fs/unionfs/dirhelper.c | 124 +----------
fs/unionfs/inode.c | 183 ++-------------
fs/unionfs/lookup.c | 85 +-------
fs/unionfs/rename.c | 149 ++-----------
fs/unionfs/sioq.c | 18 --
fs/unionfs/subr.c | 161 --------------
fs/unionfs/super.c | 2 +-
fs/unionfs/union.h | 40 ++---
fs/unionfs/whiteout.c | 577 ++++++++++++++++++++++++++++++++++++++++++++++++
11 files changed, 655 insertions(+), 708 deletions(-)
create mode 100644 fs/unionfs/whiteout.c

diff --git a/fs/unionfs/Makefile b/fs/unionfs/Makefile
index 17ca4a7..0dc28c1 100644
--- a/fs/unionfs/Makefile
+++ b/fs/unionfs/Makefile
@@ -2,7 +2,7 @@ obj-$(CONFIG_UNION_FS) += unionfs.o

unionfs-y := subr.o dentry.o file.o inode.o main.o super.o \
rdstate.o copyup.o dirhelper.o rename.o unlink.o \
- lookup.o commonfops.o dirfops.o sioq.o mmap.o
+ lookup.o commonfops.o dirfops.o sioq.o mmap.o whiteout.o

unionfs-$(CONFIG_UNION_FS_XATTR) += xattr.o

diff --git a/fs/unionfs/dirfops.c b/fs/unionfs/dirfops.c
index 8272fb6..e35afa4 100644
--- a/fs/unionfs/dirfops.c
+++ b/fs/unionfs/dirfops.c
@@ -36,37 +36,33 @@ struct unionfs_getdents_callback {
};

/* based on generic filldir in fs/readir.c */
-static int unionfs_filldir(void *dirent, const char *name, int namelen,
+static int unionfs_filldir(void *dirent, const char *oname, 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;
+ int is_whiteout;
+ char *name = (char *) oname;

buf->filldir_called++;

- if ((namelen > UNIONFS_WHLEN) &&
- !strncmp(name, UNIONFS_WHPFX, UNIONFS_WHLEN)) {
- name += UNIONFS_WHLEN;
- namelen -= UNIONFS_WHLEN;
- is_wh_entry = 1;
- }
+ is_whiteout = is_whiteout_name(&name, &namelen);

- found = find_filldir_node(buf->rdstate, name, namelen, is_wh_entry);
+ found = find_filldir_node(buf->rdstate, name, namelen, is_whiteout);

if (found) {
/*
* If we had non-whiteout entry in dir cache, then mark it
* as a whiteout and but leave it in the dir cache.
*/
- if (is_wh_entry && !found->whiteout)
- found->whiteout = is_wh_entry;
+ if (is_whiteout && !found->whiteout)
+ found->whiteout = is_whiteout;
goto out;
}

/* if 'name' isn't a whiteout, filldir it. */
- if (!is_wh_entry) {
+ if (!is_whiteout) {
off_t pos = rdstate2offset(buf->rdstate);
u64 unionfs_ino = ino;

@@ -85,7 +81,7 @@ static int unionfs_filldir(void *dirent, const char *name, int namelen,
}
buf->entries_written++;
err = add_filldir_node(buf->rdstate, name, namelen,
- buf->rdstate->bindex, is_wh_entry);
+ buf->rdstate->bindex, is_whiteout);
if (err)
buf->filldir_error = err;

diff --git a/fs/unionfs/dirhelper.c b/fs/unionfs/dirhelper.c
index 4b73bb6..302a4a1 100644
--- a/fs/unionfs/dirhelper.c
+++ b/fs/unionfs/dirhelper.c
@@ -18,112 +18,6 @@

#include "union.h"

-/*
- * Delete all of the whiteouts in a given directory for rmdir.
- *
- * lower directory inode should be locked
- */
-int do_delete_whiteouts(struct dentry *dentry, int bindex,
- struct unionfs_dir_state *namelist)
-{
- int err = 0;
- struct dentry *lower_dir_dentry = NULL;
- struct dentry *lower_dentry;
- char *name = NULL, *p;
- struct inode *lower_dir;
- int i;
- struct list_head *pos;
- struct filldir_node *cursor;
-
- /* Find out lower parent dentry */
- lower_dir_dentry = unionfs_lower_dentry_idx(dentry, bindex);
- BUG_ON(!S_ISDIR(lower_dir_dentry->d_inode->i_mode));
- lower_dir = lower_dir_dentry->d_inode;
- BUG_ON(!S_ISDIR(lower_dir->i_mode));
-
- err = -ENOMEM;
- name = __getname();
- if (unlikely(!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);
- lower_dentry =
- lookup_one_len(name, lower_dir_dentry,
- cursor->namelen +
- UNIONFS_WHLEN);
- if (IS_ERR(lower_dentry)) {
- err = PTR_ERR(lower_dentry);
- break;
- }
- if (lower_dentry->d_inode)
- err = vfs_unlink(lower_dir, lower_dentry);
- dput(lower_dentry);
- if (err)
- break;
- }
- }
-
- __putname(name);
-
- /* After all of the removals, we should copy the attributes once. */
- fsstack_copy_attr_times(dentry->d_inode, lower_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 *lower_dir_dentry;
- struct inode *lower_dir;
- struct sioq_args args;
-
- sb = dentry->d_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;
-
- lower_dir_dentry = unionfs_lower_dentry_idx(dentry, bindex);
- BUG_ON(!S_ISDIR(lower_dir_dentry->d_inode->i_mode));
- lower_dir = lower_dir_dentry->d_inode;
- BUG_ON(!S_ISDIR(lower_dir->i_mode));
-
- if (!permission(lower_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;
- }
-
-out:
- return err;
-}
-
#define RD_NONE 0
#define RD_CHECK_EMPTY 1
/* The callback structure for check_empty. */
@@ -135,13 +29,14 @@ struct unionfs_rdutil_callback {
};

/* This filldir function makes sure only whiteouts exist within a directory. */
-static int readdir_util_callback(void *dirent, const char *name, int namelen,
+static int readdir_util_callback(void *dirent, const char *oname, int namelen,
loff_t offset, u64 ino, unsigned int d_type)
{
int err = 0;
struct unionfs_rdutil_callback *buf = dirent;
- int whiteout = 0;
+ int is_whiteout;
struct filldir_node *found;
+ char *name = (char *) oname;

buf->filldir_called = 1;

@@ -149,14 +44,9 @@ static int readdir_util_callback(void *dirent, const char *name, int namelen,
(name[1] == '.' && namelen == 2)))
goto out;

- if (namelen > UNIONFS_WHLEN &&
- !strncmp(name, UNIONFS_WHPFX, UNIONFS_WHLEN)) {
- namelen -= UNIONFS_WHLEN;
- name += UNIONFS_WHLEN;
- whiteout = 1;
- }
+ is_whiteout = is_whiteout_name(&name, &namelen);

- found = find_filldir_node(buf->rdstate, name, namelen, whiteout);
+ found = find_filldir_node(buf->rdstate, name, namelen, is_whiteout);
/* If it was found in the table there was a previous whiteout. */
if (found)
goto out;
@@ -166,11 +56,11 @@ static int readdir_util_callback(void *dirent, const char *name, int namelen,
* empty.
*/
err = -ENOTEMPTY;
- if ((buf->mode == RD_CHECK_EMPTY) && !whiteout)
+ if ((buf->mode == RD_CHECK_EMPTY) && !is_whiteout)
goto out;

err = add_filldir_node(buf->rdstate, name, namelen,
- buf->rdstate->bindex, whiteout);
+ buf->rdstate->bindex, is_whiteout);

out:
buf->err = err;
diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c
index 8b4da54..bfebc0c 100644
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -19,79 +19,6 @@
#include "union.h"

/*
- * Helper function when creating new objects (create, symlink, and mknod).
- * Checks to see if there's a whiteout in @lower_dentry's parent directory,
- * whose name is taken from @dentry. Then tries to remove that whiteout, if
- * found.
- *
- * Return 0 if no whiteout was found, or if one was found and successfully
- * removed (a zero tells the caller that @lower_dentry belongs to a good
- * branch to create the new object in). Return -ERRNO if an error occurred
- * during whiteout lookup or in trying to unlink the whiteout.
- */
-static int check_for_whiteout(struct dentry *dentry,
- struct dentry *lower_dentry)
-{
- int err = 0;
- struct dentry *wh_dentry = NULL;
- struct dentry *lower_dir_dentry;
- char *name = NULL;
-
- /*
- * 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 (unlikely(IS_ERR(name))) {
- err = PTR_ERR(name);
- goto out;
- }
-
- wh_dentry = lookup_one_len(name, lower_dentry->d_parent,
- dentry->d_name.len + UNIONFS_WHLEN);
- if (IS_ERR(wh_dentry)) {
- err = PTR_ERR(wh_dentry);
- wh_dentry = NULL;
- goto out;
- }
-
- if (!wh_dentry->d_inode) /* no whiteout exists */
- goto out;
-
- /* .wh.foo has been found, so let's unlink it */
- lower_dir_dentry = lock_parent_wh(wh_dentry);
- /* see Documentation/filesystems/unionfs/issues.txt */
- lockdep_off();
- err = vfs_unlink(lower_dir_dentry->d_inode, wh_dentry);
- lockdep_on();
- unlock_dir(lower_dir_dentry);
-
- /*
- * Whiteouts are special files and should be deleted no matter what
- * (as if they never existed), in order to allow this create
- * operation to succeed. This is especially important in sticky
- * directories: a whiteout may have been created by one user, but
- * the newly created file may be created by another user.
- * Therefore, in order to maintain Unix semantics, if the vfs_unlink
- * above failed, then we have to try to directly unlink the
- * whiteout. Note: in the ODF version of unionfs, whiteout are
- * handled much more cleanly.
- */
- if (err == -EPERM) {
- struct inode *inode = lower_dir_dentry->d_inode;
- err = inode->i_op->unlink(inode, wh_dentry);
- }
- if (err)
- printk(KERN_ERR "unionfs: could not "
- "unlink whiteout, err = %d\n", err);
-
-out:
- dput(wh_dentry);
- kfree(name);
- return err;
-}
-
-/*
* Find a writeable branch to create new object in. Checks all writeble
* branches of the parent inode, from istart to iend order; if none are
* suitable, also tries branch 0 (which may require a copyup).
@@ -125,7 +52,9 @@ begin:
* check for whiteouts in writeable branch, and remove them
* if necessary.
*/
- err = check_for_whiteout(dentry, lower_dentry);
+ err = check_unlink_whiteout(dentry, lower_dentry, bindex);
+ if (err > 0) /* ignore if whiteout found and removed */
+ err = 0;
if (err)
continue;
/* if get here, we can write to the branch */
@@ -302,7 +231,6 @@ static int unionfs_link(struct dentry *old_dentry, struct inode *dir,
struct dentry *lower_old_dentry = NULL;
struct dentry *lower_new_dentry = NULL;
struct dentry *lower_dir_dentry = NULL;
- struct dentry *whiteout_dentry;
char *name = NULL;

unionfs_read_lock(old_dentry->d_sb, UNIONFS_SMUTEX_CHILD);
@@ -320,48 +248,20 @@ static int unionfs_link(struct dentry *old_dentry, struct inode *dir,

lower_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 (unlikely(IS_ERR(name))) {
- err = PTR_ERR(name);
- goto out;
- }
-
- whiteout_dentry = lookup_one_len(name, lower_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() */
- lower_dir_dentry = lock_parent_wh(whiteout_dentry);
- err = is_robranch_super(new_dentry->d_sb, dbstart(new_dentry));
- if (!err) {
- /* see Documentation/filesystems/unionfs/issues.txt */
- lockdep_off();
- err = vfs_unlink(lower_dir_dentry->d_inode,
- whiteout_dentry);
- lockdep_on();
- }
-
+ /* check for a whiteout in new dentry branch, and delete it */
+ err = check_unlink_whiteout(new_dentry, lower_new_dentry,
+ dbstart(new_dentry));
+ if (err > 0) { /* whiteout found and removed successfully */
+ lower_dir_dentry = dget_parent(lower_new_dentry);
fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode);
+ dput(lower_dir_dentry);
dir->i_nlink = unionfs_get_nlinks(dir);
- unlock_dir(lower_dir_dentry);
- lower_dir_dentry = NULL;
- dput(whiteout_dentry);
- if (err)
- goto out;
+ err = 0;
}
+ if (err)
+ goto out;

+ /* check if parent hierachy is needed, then link in same branch */
if (dbstart(old_dentry) != dbstart(new_dentry)) {
lower_new_dentry = create_parents(dir, new_dentry,
new_dentry->d_name.name,
@@ -531,12 +431,10 @@ out:
static int unionfs_mkdir(struct inode *parent, struct dentry *dentry, int mode)
{
int err = 0;
- struct dentry *lower_dentry = NULL, *whiteout_dentry = NULL;
+ struct dentry *lower_dentry = NULL;
struct dentry *lower_parent_dentry = NULL;
int bindex = 0, bstart;
char *name = NULL;
- int whiteout_unlinked = 0;
- struct sioq_args args;
int valid;

unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
@@ -558,51 +456,18 @@ static int unionfs_mkdir(struct inode *parent, struct dentry *dentry, int mode)

lower_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 (unlikely(IS_ERR(name))) {
- err = PTR_ERR(name);
- goto out;
- }
-
- whiteout_dentry = lookup_one_len(name, lower_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 {
- lower_parent_dentry = lock_parent_wh(whiteout_dentry);
-
- /* found a.wh.foo entry, remove it then do vfs_mkdir */
- err = is_robranch_super(dentry->d_sb, bstart);
- if (!err) {
- args.unlink.parent = lower_parent_dentry->d_inode;
- args.unlink.dentry = whiteout_dentry;
- run_sioq(__unionfs_unlink, &args);
- err = args.err;
- }
- dput(whiteout_dentry);
-
- unlock_dir(lower_parent_dentry);
-
- if (err) {
- /* exit if the error returned was NOT -EROFS */
- if (!IS_COPYUP_ERR(err))
- goto out;
- bstart--;
- } else {
- whiteout_unlinked = 1;
- }
+ /* check for a whiteout in new dentry branch, and delete it */
+ err = check_unlink_whiteout(dentry, lower_dentry, bstart);
+ if (err > 0) /* whiteout found and removed successfully */
+ err = 0;
+ if (err) {
+ /* exit if the error returned was NOT -EROFS */
+ if (!IS_COPYUP_ERR(err))
+ goto out;
+ bstart--;
}

+ /* check if copyup's needed, and mkdir */
for (bindex = bstart; bindex >= 0; bindex--) {
int i;
int bend = dbend(dentry);
diff --git a/fs/unionfs/lookup.c b/fs/unionfs/lookup.c
index 1ba7103..0f62087 100644
--- a/fs/unionfs/lookup.c
+++ b/fs/unionfs/lookup.c
@@ -20,58 +20,6 @@

static int realloc_dentry_private_data(struct dentry *dentry);

-/* 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 rest of these are utility functions for lookup. */
-static noinline_for_stack int is_opaque_dir(struct dentry *dentry, int bindex)
-{
- int err = 0;
- struct dentry *lower_dentry;
- struct dentry *wh_lower_dentry;
- struct inode *lower_inode;
- struct sioq_args args;
-
- lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
- lower_inode = lower_dentry->d_inode;
-
- BUG_ON(!S_ISDIR(lower_inode->i_mode));
-
- mutex_lock(&lower_inode->i_mutex);
-
- if (!permission(lower_inode, MAY_EXEC, NULL)) {
- wh_lower_dentry =
- lookup_one_len(UNIONFS_DIR_OPAQUE, lower_dentry,
- sizeof(UNIONFS_DIR_OPAQUE) - 1);
- } else {
- args.is_opaque.dentry = lower_dentry;
- run_sioq(__is_opaque_dir, &args);
- wh_lower_dentry = args.ret;
- }
-
- mutex_unlock(&lower_inode->i_mutex);
-
- if (IS_ERR(wh_lower_dentry)) {
- err = PTR_ERR(wh_lower_dentry);
- goto out;
- }
-
- /* This is an opaque dir iff wh_lower_dentry is positive */
- err = !!wh_lower_dentry->d_inode;
-
- dput(wh_lower_dentry);
-out:
- return err;
-}
-
/*
* Main (and complex) driver function for Unionfs's lookup
*
@@ -99,7 +47,6 @@ struct dentry *unionfs_lookup_backend(struct dentry *dentry,
struct dentry *first_lower_dentry = NULL;
struct vfsmount *first_lower_mnt = NULL;
int opaque;
- char *whname = NULL;
const char *name;
int namelen;

@@ -184,42 +131,19 @@ struct dentry *unionfs_lookup_backend(struct dentry *dentry,
if (!S_ISDIR(lower_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 (unlikely(IS_ERR(whname))) {
- err = PTR_ERR(whname);
- goto out_free;
- }
- }
-
- /* check if whiteout exists in this branch: lookup .wh.foo */
- wh_lower_dentry = lookup_one_len(whname, lower_dir_dentry,
- namelen + UNIONFS_WHLEN);
+ /* check for whiteouts: stop lookup if found */
+ wh_lower_dentry = lookup_whiteout(name, lower_dir_dentry);
if (IS_ERR(wh_lower_dentry)) {
dput(first_lower_dentry);
unionfs_mntput(first_dentry, first_dentry_offset);
err = PTR_ERR(wh_lower_dentry);
goto out_free;
}
-
if (wh_lower_dentry->d_inode) {
- /* We found a whiteout so let's give up. */
- if (S_ISREG(wh_lower_dentry->d_inode->i_mode)) {
- dbend(dentry) = dbopaque(dentry) = bindex;
- dput(wh_lower_dentry);
- break;
- }
- err = -EIO;
- printk(KERN_ERR "unionfs: EIO: invalid whiteout "
- "entry type %d\n",
- wh_lower_dentry->d_inode->i_mode);
+ dbend(dentry) = dbopaque(dentry) = bindex;
dput(wh_lower_dentry);
- dput(first_lower_dentry);
- unionfs_mntput(first_dentry, first_dentry_offset);
- goto out_free;
+ break;
}
-
dput(wh_lower_dentry);
wh_lower_dentry = NULL;

@@ -424,7 +348,6 @@ out:
dput(first_lower_dentry);
}
}
- kfree(whname);
dput(parent_dentry);
if (err && (lookupmode == INTERPOSE_LOOKUP))
unionfs_unlock_dentry(dentry);
diff --git a/fs/unionfs/rename.c b/fs/unionfs/rename.c
index 5b3f1a3..85f96ee 100644
--- a/fs/unionfs/rename.c
+++ b/fs/unionfs/rename.c
@@ -62,17 +62,14 @@ out:

static int __unionfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry,
- int bindex, struct dentry **wh_old)
+ int bindex)
{
int err = 0;
struct dentry *lower_old_dentry;
struct dentry *lower_new_dentry;
struct dentry *lower_old_dir_dentry;
struct dentry *lower_new_dir_dentry;
- struct dentry *lower_wh_dentry;
- struct dentry *lower_wh_dir_dentry;
struct dentry *trap;
- char *wh_name = NULL;

lower_new_dentry = unionfs_lower_dentry_idx(new_dentry, bindex);
lower_old_dentry = unionfs_lower_dentry_idx(old_dentry, bindex);
@@ -93,46 +90,14 @@ static int __unionfs_rename(struct inode *old_dir, struct dentry *old_dentry,
}
}

- wh_name = alloc_whname(new_dentry->d_name.name,
- new_dentry->d_name.len);
- if (unlikely(IS_ERR(wh_name))) {
- err = PTR_ERR(wh_name);
- goto out;
- }
-
- lower_wh_dentry = lookup_one_len(wh_name, lower_new_dentry->d_parent,
- new_dentry->d_name.len +
- UNIONFS_WHLEN);
- if (IS_ERR(lower_wh_dentry)) {
- err = PTR_ERR(lower_wh_dentry);
+ /* check for and remove whiteout, if any */
+ err = check_unlink_whiteout(new_dentry, lower_new_dentry, bindex);
+ if (err > 0) /* ignore if whiteout found and successfully removed */
+ err = 0;
+ if (err)
goto out;
- }
-
- if (lower_wh_dentry->d_inode) {
- /* get rid of the whiteout that is existing */
- if (lower_new_dentry->d_inode) {
- printk(KERN_ERR "unionfs: both a whiteout and a "
- "dentry exist when doing a rename!\n");
- err = -EIO;
-
- dput(lower_wh_dentry);
- goto out;
- }
-
- lower_wh_dir_dentry = lock_parent_wh(lower_wh_dentry);
- err = is_robranch_super(old_dentry->d_sb, bindex);
- if (!err)
- err = vfs_unlink(lower_wh_dir_dentry->d_inode,
- lower_wh_dentry);
-
- dput(lower_wh_dentry);
- unlock_dir(lower_wh_dir_dentry);
- if (err)
- goto out;
- } else {
- dput(lower_wh_dentry);
- }

+ /* check of old_dentry branch is writable */
err = is_robranch_super(old_dentry->d_sb, bindex);
if (err)
goto out;
@@ -142,28 +107,6 @@ static int __unionfs_rename(struct inode *old_dir, struct dentry *old_dentry,
lower_old_dir_dentry = dget_parent(lower_old_dentry);
lower_new_dir_dentry = dget_parent(lower_new_dentry);

- /*
- * 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 (unlikely(IS_ERR(whname)))
- goto out_dput;
- *wh_old = lookup_one_len(whname, lower_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_dput;
- }
- }
-
/* see Documentation/filesystems/unionfs/issues.txt */
lockdep_off();
trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
@@ -188,7 +131,6 @@ out_err_unlock:
unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
lockdep_on();

-out_dput:
dput(lower_old_dir_dentry);
dput(lower_new_dir_dentry);
dput(lower_old_dentry);
@@ -203,8 +145,6 @@ out:
dbend(new_dentry) = bindex;
}

- kfree(wh_name);
-
return err;
}

@@ -227,7 +167,6 @@ static int do_unionfs_rename(struct inode *old_dir,
int local_err = 0;
int eio = 0;
int revert = 0;
- struct dentry *wh_old = NULL;

old_bstart = dbstart(old_dentry);
bwh_old = old_bstart;
@@ -239,7 +178,7 @@ static int do_unionfs_rename(struct inode *old_dir,

/* Rename source to destination. */
err = __unionfs_rename(old_dir, old_dentry, new_dir, new_dentry,
- old_bstart, &wh_old);
+ old_bstart);
if (err) {
if (!IS_COPYUP_ERR(err))
goto out;
@@ -282,7 +221,6 @@ static int do_unionfs_rename(struct inode *old_dir,
} else if (IS_COPYUP_ERR(err)) {
do_copyup = bindex - 1;
} else if (revert) {
- dput(wh_old);
goto revert;
}
}
@@ -301,11 +239,10 @@ static int do_unionfs_rename(struct inode *old_dir,
/* if copyup failed, try next branch to the left */
if (err)
continue;
- dput(wh_old);
bwh_old = bindex;
err = __unionfs_rename(old_dir, old_dentry,
new_dir, new_dentry,
- bindex, &wh_old);
+ bindex);
break;
}
}
@@ -323,38 +260,22 @@ static int do_unionfs_rename(struct inode *old_dir,
* (2) We did a copy_up
*/
if ((old_bstart != old_bend) || (do_copyup != -1)) {
- struct dentry *lower_parent;
- struct nameidata nd;
- if (!wh_old || wh_old->d_inode || bwh_old < 0) {
- printk(KERN_ERR "unionfs: rename error "
- "(wh_old=%p/%p bwh_old=%d)\n", wh_old,
- (wh_old ? wh_old->d_inode : NULL), bwh_old);
+ if (bwh_old < 0) {
+ printk(KERN_ERR "unionfs: rename error (bwh_old=%d)\n",
+ bwh_old);
err = -EIO;
goto out;
}
- err = init_lower_nd(&nd, LOOKUP_CREATE);
- if (unlikely(err < 0))
- goto out;
- lower_parent = lock_parent_wh(wh_old);
- local_err = vfs_create(lower_parent->d_inode, wh_old, S_IRUGO,
- &nd);
- unlock_dir(lower_parent);
- if (!local_err) {
- dbopaque(old_dentry) = bwh_old;
- } else {
- /*
- * we can't fix anything now, so we cop-out and use
- * -EIO.
- */
+ err = create_whiteout(old_dentry, bwh_old);
+ if (err) {
+ /* can't fix anything now, so we exit with -EIO */
printk(KERN_ERR "unionfs: can't create a whiteout for "
- "the source in rename!\n");
+ "%s in rename!\n", old_dentry->d_name.name);
err = -EIO;
}
- release_lower_nd(&nd, local_err);
}

out:
- dput(wh_old);
return err;

revert:
@@ -391,7 +312,7 @@ revert:
}

local_err = __unionfs_rename(new_dir, new_dentry,
- old_dir, old_dentry, old_bstart, NULL);
+ old_dir, old_dentry, old_bstart);

/* If we can't fix it, then we cop-out with -EIO. */
if (local_err) {
@@ -412,40 +333,6 @@ revert_out:
return err;
}

-static struct dentry *lookup_whiteout(struct dentry *dentry)
-{
- char *whname;
- int bindex = -1, bstart = -1, bend = -1;
- struct dentry *parent, *lower_parent, *wh_dentry;
-
- whname = alloc_whname(dentry->d_name.name, dentry->d_name.len);
- if (unlikely(IS_ERR(whname)))
- return (void *)whname;
-
- parent = dget_parent(dentry);
- unionfs_lock_dentry(parent, UNIONFS_DMUTEX_WHITEOUT);
- bstart = dbstart(parent);
- bend = dbend(parent);
- wh_dentry = ERR_PTR(-ENOENT);
- for (bindex = bstart; bindex <= bend; bindex++) {
- lower_parent = unionfs_lower_dentry_idx(parent, bindex);
- if (!lower_parent)
- continue;
- wh_dentry = lookup_one_len(whname, lower_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);
- }
- unionfs_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
@@ -511,7 +398,7 @@ int unionfs_rename(struct inode *old_dir, struct dentry *old_dentry,
* if new_dentry is already lower because of whiteout,
* simply override it even if the whited-out dir is not empty.
*/
- wh_dentry = lookup_whiteout(new_dentry);
+ wh_dentry = find_first_whiteout(new_dentry);
if (!IS_ERR(wh_dentry)) {
dput(wh_dentry);
} else if (new_dentry->d_inode) {
diff --git a/fs/unionfs/sioq.c b/fs/unionfs/sioq.c
index 2a8c88e..0ea8436 100644
--- a/fs/unionfs/sioq.c
+++ b/fs/unionfs/sioq.c
@@ -99,21 +99,3 @@ void __unionfs_unlink(struct work_struct *work)
args->err = vfs_unlink(u->parent, u->dentry);
complete(&args->comp);
}
-
-void __delete_whiteouts(struct work_struct *work)
-{
- struct sioq_args *args = container_of(work, struct sioq_args, work);
- struct deletewh_args *d = &args->deletewh;
-
- args->err = do_delete_whiteouts(d->dentry, d->bindex, d->namelist);
- complete(&args->comp);
-}
-
-void __is_opaque_dir(struct work_struct *work)
-{
- struct sioq_args *args = container_of(work, struct sioq_args, work);
-
- args->ret = lookup_one_len(UNIONFS_DIR_OPAQUE, args->is_opaque.dentry,
- sizeof(UNIONFS_DIR_OPAQUE) - 1);
- complete(&args->comp);
-}
diff --git a/fs/unionfs/subr.c b/fs/unionfs/subr.c
index 1f1db3d..dda2745 100644
--- a/fs/unionfs/subr.c
+++ b/fs/unionfs/subr.c
@@ -19,152 +19,6 @@
#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 *lower_dir_dentry;
- struct dentry *lower_dentry;
- struct dentry *lower_wh_dentry;
- struct nameidata nd;
- 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 (unlikely(IS_ERR(name))) {
- err = PTR_ERR(name);
- goto out;
- }
-
- for (bindex = start; bindex >= 0; bindex--) {
- lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
-
- if (!lower_dentry) {
- /*
- * if lower dentry is not present, create the
- * entire lower 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.
- */
- lower_dentry = create_parents(dentry->d_inode,
- dentry,
- dentry->d_name.name,
- bindex);
- if (!lower_dentry || IS_ERR(lower_dentry)) {
- int ret = PTR_ERR(lower_dentry);
- if (!IS_COPYUP_ERR(ret))
- printk(KERN_ERR
- "unionfs: create_parents for "
- "whiteout failed: bindex=%d "
- "err=%d\n", bindex, ret);
- continue;
- }
- }
-
- lower_wh_dentry =
- lookup_one_len(name, lower_dentry->d_parent,
- dentry->d_name.len + UNIONFS_WHLEN);
- if (IS_ERR(lower_wh_dentry))
- continue;
-
- /*
- * The whiteout already exists. This used to be impossible,
- * but now is possible because of opaqueness.
- */
- if (lower_wh_dentry->d_inode) {
- dput(lower_wh_dentry);
- err = 0;
- goto out;
- }
-
- err = init_lower_nd(&nd, LOOKUP_CREATE);
- if (unlikely(err < 0))
- goto out;
- lower_dir_dentry = lock_parent_wh(lower_wh_dentry);
- err = is_robranch_super(dentry->d_sb, bindex);
- if (!err)
- err = vfs_create(lower_dir_dentry->d_inode,
- lower_wh_dentry,
- ~current->fs->umask & S_IRWXUGO,
- &nd);
- unlock_dir(lower_dir_dentry);
- dput(lower_wh_dentry);
- release_lower_nd(&nd, err);
-
- if (!err || !IS_COPYUP_ERR(err))
- break;
- }
-
- /* set dbopaque so that lookup will not proceed after this branch */
- if (!err)
- dbopaque(dentry) = bindex;
-
-out:
- kfree(name);
- return err;
-}
-
-int make_dir_opaque(struct dentry *dentry, int bindex)
-{
- int err = 0;
- struct dentry *lower_dentry, *diropq;
- struct inode *lower_dir;
- struct nameidata nd;
- kernel_cap_t orig_cap;
-
- /*
- * Opaque directory whiteout markers are special files (like regular
- * whiteouts), and should appear to the users as if they don't
- * exist. They should be created/deleted regardless of directory
- * search/create permissions, but only for the duration of this
- * creation of the .wh.__dir_opaque: file. Note, this does not
- * circumvent normal ->permission).
- */
- orig_cap = current->cap_effective;
- cap_raise(current->cap_effective, CAP_DAC_READ_SEARCH);
- cap_raise(current->cap_effective, CAP_DAC_OVERRIDE);
-
- lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
- lower_dir = lower_dentry->d_inode;
- BUG_ON(!S_ISDIR(dentry->d_inode->i_mode) ||
- !S_ISDIR(lower_dir->i_mode));
-
- mutex_lock(&lower_dir->i_mutex);
- diropq = lookup_one_len(UNIONFS_DIR_OPAQUE, lower_dentry,
- sizeof(UNIONFS_DIR_OPAQUE) - 1);
- if (IS_ERR(diropq)) {
- err = PTR_ERR(diropq);
- goto out;
- }
-
- err = init_lower_nd(&nd, LOOKUP_CREATE);
- if (unlikely(err < 0))
- goto out;
- if (!diropq->d_inode)
- err = vfs_create(lower_dir, diropq, S_IRUGO, &nd);
- if (!err)
- dbopaque(dentry) = bindex;
- release_lower_nd(&nd, err);
-
- dput(diropq);
-
-out:
- mutex_unlock(&lower_dir->i_mutex);
- current->cap_effective = orig_cap;
- return err;
-}
-
-/*
* returns the right n_link value based on the inode type
*/
int unionfs_get_nlinks(const struct inode *inode)
@@ -184,21 +38,6 @@ int unionfs_get_nlinks(const struct inode *inode)
return 1;
}

-/* construct whiteout filename */
-char *alloc_whname(const char *name, int len)
-{
- char *buf;
-
- buf = kmalloc(len + UNIONFS_WHLEN + 1, GFP_KERNEL);
- if (unlikely(!buf))
- return ERR_PTR(-ENOMEM);
-
- strcpy(buf, UNIONFS_WHPFX);
- strlcat(buf, name, len + UNIONFS_WHLEN + 1);
-
- return buf;
-}
-
/* copy a/m/ctime from the lower branch with the newest times */
void unionfs_copy_attr_times(struct inode *upper)
{
diff --git a/fs/unionfs/super.c b/fs/unionfs/super.c
index 9715529..3b6b65a 100644
--- a/fs/unionfs/super.c
+++ b/fs/unionfs/super.c
@@ -173,7 +173,7 @@ static int unionfs_statfs(struct dentry *dentry, struct kstatfs *buf)
*
* XXX: this restriction goes away with ODF.
*/
- buf->f_namelen -= UNIONFS_WHLEN;
+ unionfs_set_max_namelen(&buf->f_namelen);

/*
* reset two fields to avoid confusing user-land.
diff --git a/fs/unionfs/union.h b/fs/unionfs/union.h
index f5cf09b..9271b3b 100644
--- a/fs/unionfs/union.h
+++ b/fs/unionfs/union.h
@@ -306,18 +306,10 @@ extern void release_lower_nd(struct nameidata *nd, int err);
/* replicates the directory structure up to given dentry in given branch */
extern struct dentry *create_parents(struct inode *dir, struct dentry *dentry,
const char *name, int bindex);
-extern int make_dir_opaque(struct dentry *dir, int bindex);

/* 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);
@@ -332,18 +324,25 @@ extern int copyup_dentry(struct inode *dir, struct dentry *dentry,
extern void unionfs_postcopyup_setmnt(struct dentry *dentry);
extern void unionfs_postcopyup_release(struct dentry *dentry);

-extern int remove_whiteouts(struct dentry *dentry,
- struct dentry *lower_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. */
+/* whiteout and opaque directory helpers */
+extern char *alloc_whname(const char *name, int len);
+extern bool is_whiteout_name(char **namep, int *namelenp);
+extern bool is_validname(const char *name);
+extern struct dentry *lookup_whiteout(const char *name,
+ struct dentry *lower_parent);
+extern struct dentry *find_first_whiteout(struct dentry *dentry);
+extern int unlink_whiteout(struct dentry *wh_dentry);
+extern int check_unlink_whiteout(struct dentry *dentry,
+ struct dentry *lower_dentry, int bindex);
+extern int create_whiteout(struct dentry *dentry, int start);
extern int delete_whiteouts(struct dentry *dentry, int bindex,
struct unionfs_dir_state *namelist);
+extern int is_opaque_dir(struct dentry *dentry, int bindex);
+extern int make_dir_opaque(struct dentry *dir, int bindex);
+extern void unionfs_set_max_namelen(long *namelen);

extern void unionfs_reinterpose(struct dentry *this_dentry);
extern struct super_block *unionfs_duplicate_super(struct super_block *sb);
@@ -476,20 +475,9 @@ static inline int is_robranch(const struct dentry *dentry)
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
-
/*
* EXTERNALS:
*/
-extern char *alloc_whname(const char *name, int len);
extern int check_branch(struct nameidata *nd);
extern int parse_branch_mode(const char *name, int *perms);

diff --git a/fs/unionfs/whiteout.c b/fs/unionfs/whiteout.c
new file mode 100644
index 0000000..b1768ed
--- /dev/null
+++ b/fs/unionfs/whiteout.c
@@ -0,0 +1,577 @@
+/*
+ * Copyright (c) 2003-2007 Erez Zadok
+ * Copyright (c) 2003-2006 Charles P. Wright
+ * Copyright (c) 2005-2007 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-2007 Stony Brook University
+ * Copyright (c) 2003-2007 The Research Foundation of SUNY
+ *
+ * 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"
+
+/*
+ * whiteout and opaque directory helpers
+ */
+
+/* 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 */
+char *alloc_whname(const char *name, int len)
+{
+ char *buf;
+
+ buf = kmalloc(len + UNIONFS_WHLEN + 1, GFP_KERNEL);
+ if (unlikely(!buf))
+ return ERR_PTR(-ENOMEM);
+
+ strcpy(buf, UNIONFS_WHPFX);
+ strlcat(buf, name, len + UNIONFS_WHLEN + 1);
+
+ return buf;
+}
+
+/*
+ * XXX: this can be inline or CPP macro, but is here to keep all whiteout
+ * code in one place.
+ */
+void unionfs_set_max_namelen(long *namelen)
+{
+ *namelen -= UNIONFS_WHLEN;
+}
+
+/* check if @namep is a whiteout, update @namep and @namelenp accordingly */
+bool is_whiteout_name(char **namep, int *namelenp)
+{
+ if (*namelenp > UNIONFS_WHLEN &&
+ !strncmp(*namep, UNIONFS_WHPFX, UNIONFS_WHLEN)) {
+ *namep += UNIONFS_WHLEN;
+ *namelenp -= UNIONFS_WHLEN;
+ return true;
+ }
+ return false;
+}
+
+/* is the filename valid == !(whiteout for a file or opaque dir marker) */
+bool is_validname(const char *name)
+{
+ if (!strncmp(name, UNIONFS_WHPFX, UNIONFS_WHLEN))
+ return false;
+ if (!strncmp(name, UNIONFS_DIR_OPAQUE_NAME,
+ sizeof(UNIONFS_DIR_OPAQUE_NAME) - 1))
+ return false;
+ return true;
+}
+
+/*
+ * Look for a whiteout @name in @lower_parent directory. If error, return
+ * ERR_PTR. Caller must dput() the returned dentry if not an error.
+ *
+ * XXX: some callers can reuse the whname allocated buffer to avoid repeated
+ * free then re-malloc calls. Need to provide a different API for those
+ * callers.
+ */
+struct dentry *lookup_whiteout(const char *name, struct dentry *lower_parent)
+{
+ char *whname = NULL;
+ int err = 0, namelen;
+ struct dentry *wh_dentry = NULL;
+
+ namelen = strlen(name);
+ whname = alloc_whname(name, namelen);
+ if (unlikely(IS_ERR(whname))) {
+ err = PTR_ERR(whname);
+ goto out;
+ }
+
+ /* check if whiteout exists in this branch: lookup .wh.foo */
+ wh_dentry = lookup_one_len(whname, lower_parent, strlen(whname));
+ if (IS_ERR(wh_dentry)) {
+ err = PTR_ERR(wh_dentry);
+ goto out;
+ }
+
+ /* check if negative dentry (ENOENT) */
+ if (!wh_dentry->d_inode)
+ goto out;
+
+ /* whiteout found: check if valid type */
+ if (!S_ISREG(wh_dentry->d_inode->i_mode)) {
+ printk(KERN_ERR "unionfs: invalid whiteout %s entry type %d\n",
+ whname, wh_dentry->d_inode->i_mode);
+ dput(wh_dentry);
+ err = -EIO;
+ goto out;
+ }
+
+out:
+ kfree(whname);
+ if (err)
+ wh_dentry = ERR_PTR(err);
+ return wh_dentry;
+}
+
+/* find and return first whiteout in parent directory, else ENOENT */
+struct dentry *find_first_whiteout(struct dentry *dentry)
+{
+ int bindex, bstart, bend;
+ struct dentry *parent, *lower_parent, *wh_dentry;
+
+ parent = dget_parent(dentry);
+ unionfs_lock_dentry(parent, UNIONFS_DMUTEX_WHITEOUT);
+ bstart = dbstart(parent);
+ bend = dbend(parent);
+ wh_dentry = ERR_PTR(-ENOENT);
+
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ lower_parent = unionfs_lower_dentry_idx(parent, bindex);
+ if (!lower_parent)
+ continue;
+ wh_dentry = lookup_whiteout(dentry->d_name.name, lower_parent);
+ if (IS_ERR(wh_dentry))
+ continue;
+ if (wh_dentry->d_inode)
+ break;
+ dput(wh_dentry);
+ wh_dentry = ERR_PTR(-ENOENT);
+ }
+ unionfs_unlock_dentry(parent);
+ dput(parent);
+
+ return wh_dentry;
+}
+
+/*
+ * Unlink a whiteout dentry. Returns 0 or -errno. Caller must hold and
+ * release dentry reference.
+ */
+int unlink_whiteout(struct dentry *wh_dentry)
+{
+ int err;
+ struct dentry *lower_dir_dentry;
+
+ /* dget and lock parent dentry */
+ lower_dir_dentry = lock_parent_wh(wh_dentry);
+
+ /* see Documentation/filesystems/unionfs/issues.txt */
+ lockdep_off();
+ err = vfs_unlink(lower_dir_dentry->d_inode, wh_dentry);
+ lockdep_on();
+ unlock_dir(lower_dir_dentry);
+
+ /*
+ * Whiteouts are special files and should be deleted no matter what
+ * (as if they never existed), in order to allow this create
+ * operation to succeed. This is especially important in sticky
+ * directories: a whiteout may have been created by one user, but
+ * the newly created file may be created by another user.
+ * Therefore, in order to maintain Unix semantics, if the vfs_unlink
+ * above failed, then we have to try to directly unlink the
+ * whiteout. Note: in the ODF version of unionfs, whiteout are
+ * handled much more cleanly.
+ */
+ if (err == -EPERM) {
+ struct inode *inode = lower_dir_dentry->d_inode;
+ err = inode->i_op->unlink(inode, wh_dentry);
+ }
+ if (err)
+ printk(KERN_ERR "unionfs: could not unlink whiteout %s, "
+ "err = %d\n", wh_dentry->d_name.name, err);
+
+ return err;
+
+}
+
+/*
+ * Helper function when creating new objects (create, symlink, mknod, etc.).
+ * Checks to see if there's a whiteout in @lower_dentry's parent directory,
+ * whose name is taken from @dentry. Then tries to remove that whiteout, if
+ * found. If <dentry,bindex> is a branch marked readonly, return -EROFS.
+ * If it finds both a regular file and a whiteout, return -EIO (this should
+ * never happen).
+ *
+ * Return 0 if no whiteout was found. Return 1 if one was found and
+ * successfully removed. Therefore a value >= 0 tells the caller that
+ * @lower_dentry belongs to a good branch to create the new object in).
+ * Return -ERRNO if an error occurred during whiteout lookup or in trying to
+ * unlink the whiteout.
+ */
+int check_unlink_whiteout(struct dentry *dentry, struct dentry *lower_dentry,
+ int bindex)
+{
+ int err;
+ struct dentry *wh_dentry = NULL;
+ struct dentry *lower_dir_dentry = NULL;
+
+ /* look for whiteout dentry first */
+ lower_dir_dentry = dget_parent(lower_dentry);
+ wh_dentry = lookup_whiteout(dentry->d_name.name, lower_dir_dentry);
+ dput(lower_dir_dentry);
+ if (IS_ERR(wh_dentry)) {
+ err = PTR_ERR(wh_dentry);
+ goto out;
+ }
+
+ if (!wh_dentry->d_inode) { /* no whiteout exists*/
+ err = 0;
+ goto out_dput;
+ }
+
+ /* check if regular file and whiteout were both found */
+ if (unlikely(lower_dentry->d_inode)) {
+ err = -EIO;
+ printk(KERN_ERR "unionfs: found both whiteout and regular "
+ "file in directory %s (branch %d)\n",
+ lower_dentry->d_parent->d_name.name, bindex);
+ goto out_dput;
+ }
+
+ /* check if branch is writeable */
+ err = is_robranch_super(dentry->d_sb, bindex);
+ if (err)
+ goto out_dput;
+
+ /* .wh.foo has been found, so let's unlink it */
+ err = unlink_whiteout(wh_dentry);
+ if (!err)
+ err = 1; /* a whiteout was found and successfully removed */
+out_dput:
+ dput(wh_dentry);
+out:
+ return err;
+}
+
+/*
+ * 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 *lower_dir_dentry;
+ struct dentry *lower_dentry;
+ struct dentry *lower_wh_dentry;
+ struct nameidata nd;
+ 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 (unlikely(IS_ERR(name))) {
+ err = PTR_ERR(name);
+ goto out;
+ }
+
+ for (bindex = start; bindex >= 0; bindex--) {
+ lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+
+ if (!lower_dentry) {
+ /*
+ * if lower dentry is not present, create the
+ * entire lower 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.
+ */
+ lower_dentry = create_parents(dentry->d_inode,
+ dentry,
+ dentry->d_name.name,
+ bindex);
+ if (!lower_dentry || IS_ERR(lower_dentry)) {
+ int ret = PTR_ERR(lower_dentry);
+ if (!IS_COPYUP_ERR(ret))
+ printk(KERN_ERR
+ "unionfs: create_parents for "
+ "whiteout failed: bindex=%d "
+ "err=%d\n", bindex, ret);
+ continue;
+ }
+ }
+
+ lower_wh_dentry =
+ lookup_one_len(name, lower_dentry->d_parent,
+ dentry->d_name.len + UNIONFS_WHLEN);
+ if (IS_ERR(lower_wh_dentry))
+ continue;
+
+ /*
+ * The whiteout already exists. This used to be impossible,
+ * but now is possible because of opaqueness.
+ */
+ if (lower_wh_dentry->d_inode) {
+ dput(lower_wh_dentry);
+ err = 0;
+ goto out;
+ }
+
+ err = init_lower_nd(&nd, LOOKUP_CREATE);
+ if (unlikely(err < 0))
+ goto out;
+ lower_dir_dentry = lock_parent_wh(lower_wh_dentry);
+ err = is_robranch_super(dentry->d_sb, bindex);
+ if (!err)
+ err = vfs_create(lower_dir_dentry->d_inode,
+ lower_wh_dentry,
+ ~current->fs->umask & S_IRUGO,
+ &nd);
+ unlock_dir(lower_dir_dentry);
+ dput(lower_wh_dentry);
+ release_lower_nd(&nd, err);
+
+ if (!err || !IS_COPYUP_ERR(err))
+ break;
+ }
+
+ /* set dbopaque so that lookup will not proceed after this branch */
+ if (!err)
+ dbopaque(dentry) = bindex;
+
+out:
+ kfree(name);
+ return err;
+}
+
+/*
+ * Delete all of the whiteouts in a given directory for rmdir.
+ *
+ * lower directory inode should be locked
+ */
+static int do_delete_whiteouts(struct dentry *dentry, int bindex,
+ struct unionfs_dir_state *namelist)
+{
+ int err = 0;
+ struct dentry *lower_dir_dentry = NULL;
+ struct dentry *lower_dentry;
+ char *name = NULL, *p;
+ struct inode *lower_dir;
+ int i;
+ struct list_head *pos;
+ struct filldir_node *cursor;
+
+ /* Find out lower parent dentry */
+ lower_dir_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ BUG_ON(!S_ISDIR(lower_dir_dentry->d_inode->i_mode));
+ lower_dir = lower_dir_dentry->d_inode;
+ BUG_ON(!S_ISDIR(lower_dir->i_mode));
+
+ err = -ENOMEM;
+ name = __getname();
+ if (unlikely(!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;
+
+ strlcpy(p, cursor->name, PATH_MAX - UNIONFS_WHLEN);
+ lower_dentry =
+ lookup_one_len(name, lower_dir_dentry,
+ cursor->namelen +
+ UNIONFS_WHLEN);
+ if (IS_ERR(lower_dentry)) {
+ err = PTR_ERR(lower_dentry);
+ break;
+ }
+ if (lower_dentry->d_inode)
+ err = vfs_unlink(lower_dir, lower_dentry);
+ dput(lower_dentry);
+ if (err)
+ break;
+ }
+ }
+
+ __putname(name);
+
+ /* After all of the removals, we should copy the attributes once. */
+ fsstack_copy_attr_times(dentry->d_inode, lower_dir_dentry->d_inode);
+
+out:
+ return err;
+}
+
+
+void __delete_whiteouts(struct work_struct *work)
+{
+ struct sioq_args *args = container_of(work, struct sioq_args, work);
+ struct deletewh_args *d = &args->deletewh;
+
+ args->err = do_delete_whiteouts(d->dentry, d->bindex, d->namelist);
+ complete(&args->comp);
+}
+
+/* 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 *lower_dir_dentry;
+ struct inode *lower_dir;
+ struct sioq_args args;
+
+ sb = dentry->d_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;
+
+ lower_dir_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ BUG_ON(!S_ISDIR(lower_dir_dentry->d_inode->i_mode));
+ lower_dir = lower_dir_dentry->d_inode;
+ BUG_ON(!S_ISDIR(lower_dir->i_mode));
+
+ if (!permission(lower_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;
+ }
+
+out:
+ return err;
+}
+
+/****************************************************************************
+ * Opaque directory helpers *
+ ****************************************************************************/
+
+/*
+ * is_opaque_dir: returns 0 if it is NOT an opaque dir, 1 if it is, and
+ * -errno if an error occurred trying to figure this out.
+ */
+int is_opaque_dir(struct dentry *dentry, int bindex)
+{
+ int err = 0;
+ struct dentry *lower_dentry;
+ struct dentry *wh_lower_dentry;
+ struct inode *lower_inode;
+ struct sioq_args args;
+
+ lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ lower_inode = lower_dentry->d_inode;
+
+ BUG_ON(!S_ISDIR(lower_inode->i_mode));
+
+ mutex_lock(&lower_inode->i_mutex);
+
+ if (!permission(lower_inode, MAY_EXEC, NULL)) {
+ wh_lower_dentry =
+ lookup_one_len(UNIONFS_DIR_OPAQUE, lower_dentry,
+ sizeof(UNIONFS_DIR_OPAQUE) - 1);
+ } else {
+ args.is_opaque.dentry = lower_dentry;
+ run_sioq(__is_opaque_dir, &args);
+ wh_lower_dentry = args.ret;
+ }
+
+ mutex_unlock(&lower_inode->i_mutex);
+
+ if (IS_ERR(wh_lower_dentry)) {
+ err = PTR_ERR(wh_lower_dentry);
+ goto out;
+ }
+
+ /* This is an opaque dir iff wh_lower_dentry is positive */
+ err = !!wh_lower_dentry->d_inode;
+
+ dput(wh_lower_dentry);
+out:
+ return err;
+}
+
+void __is_opaque_dir(struct work_struct *work)
+{
+ struct sioq_args *args = container_of(work, struct sioq_args, work);
+
+ args->ret = lookup_one_len(UNIONFS_DIR_OPAQUE, args->is_opaque.dentry,
+ sizeof(UNIONFS_DIR_OPAQUE) - 1);
+ complete(&args->comp);
+}
+
+int make_dir_opaque(struct dentry *dentry, int bindex)
+{
+ int err = 0;
+ struct dentry *lower_dentry, *diropq;
+ struct inode *lower_dir;
+ struct nameidata nd;
+ kernel_cap_t orig_cap;
+
+ /*
+ * Opaque directory whiteout markers are special files (like regular
+ * whiteouts), and should appear to the users as if they don't
+ * exist. They should be created/deleted regardless of directory
+ * search/create permissions, but only for the duration of this
+ * creation of the .wh.__dir_opaque: file. Note, this does not
+ * circumvent normal ->permission).
+ */
+ orig_cap = current->cap_effective;
+ cap_raise(current->cap_effective, CAP_DAC_READ_SEARCH);
+ cap_raise(current->cap_effective, CAP_DAC_OVERRIDE);
+
+ lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ lower_dir = lower_dentry->d_inode;
+ BUG_ON(!S_ISDIR(dentry->d_inode->i_mode) ||
+ !S_ISDIR(lower_dir->i_mode));
+
+ mutex_lock(&lower_dir->i_mutex);
+ diropq = lookup_one_len(UNIONFS_DIR_OPAQUE, lower_dentry,
+ sizeof(UNIONFS_DIR_OPAQUE) - 1);
+ if (IS_ERR(diropq)) {
+ err = PTR_ERR(diropq);
+ goto out;
+ }
+
+ err = init_lower_nd(&nd, LOOKUP_CREATE);
+ if (unlikely(err < 0))
+ goto out;
+ if (!diropq->d_inode)
+ err = vfs_create(lower_dir, diropq, S_IRUGO, &nd);
+ if (!err)
+ dbopaque(dentry) = bindex;
+ release_lower_nd(&nd, err);
+
+ dput(diropq);
+
+out:
+ mutex_unlock(&lower_dir->i_mutex);
+ current->cap_effective = orig_cap;
+ return err;
+}
--
1.5.2.2

2008-07-30 02:51:32

by Erez Zadok

[permalink] [raw]
Subject: [PATCH 16/19] Unionfs: symlink no longer takes a mode parameter

Signed-off-by: Erez Zadok <[email protected]>
---
fs/unionfs/copyup.c | 1 -
fs/unionfs/inode.c | 3 +--
fs/unionfs/sioq.c | 2 +-
fs/unionfs/sioq.h | 1 -
4 files changed, 2 insertions(+), 5 deletions(-)

diff --git a/fs/unionfs/copyup.c b/fs/unionfs/copyup.c
index bbd49c8..ae6ea2b 100644
--- a/fs/unionfs/copyup.c
+++ b/fs/unionfs/copyup.c
@@ -184,7 +184,6 @@ static int __copyup_ndentry(struct dentry *old_lower_dentry,
args.symlink.parent = new_lower_parent_dentry->d_inode;
args.symlink.dentry = new_lower_dentry;
args.symlink.symbuf = symbuf;
- args.symlink.mode = old_mode;

run_sioq(__unionfs_symlink, &args);
err = args.err;
diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c
index a05b412..1a95e3b 100644
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -412,8 +412,7 @@ static int unionfs_symlink(struct inode *parent, struct dentry *dentry,
}

mode = S_IALLUGO;
- err = vfs_symlink(lower_parent_dentry->d_inode, lower_dentry,
- symname, mode);
+ err = vfs_symlink(lower_parent_dentry->d_inode, lower_dentry, symname);
if (!err) {
err = PTR_ERR(unionfs_interpose(dentry, parent->i_sb, 0));
if (!err) {
diff --git a/fs/unionfs/sioq.c b/fs/unionfs/sioq.c
index e6f15a0..dd45e39 100644
--- a/fs/unionfs/sioq.c
+++ b/fs/unionfs/sioq.c
@@ -87,7 +87,7 @@ void __unionfs_symlink(struct work_struct *work)
struct sioq_args *args = container_of(work, struct sioq_args, work);
struct symlink_args *s = &args->symlink;

- args->err = vfs_symlink(s->parent, s->dentry, s->symbuf, s->mode);
+ args->err = vfs_symlink(s->parent, s->dentry, s->symbuf);
complete(&args->comp);
}

diff --git a/fs/unionfs/sioq.h b/fs/unionfs/sioq.h
index e072bf7..679a0df 100644
--- a/fs/unionfs/sioq.h
+++ b/fs/unionfs/sioq.h
@@ -49,7 +49,6 @@ struct symlink_args {
struct inode *parent;
struct dentry *dentry;
char *symbuf;
- umode_t mode;
};

struct unlink_args {
--
1.5.2.2

2008-07-30 02:51:48

by Erez Zadok

[permalink] [raw]
Subject: [PATCH 12/19] Unionfs: update maintainers

Signed-off-by: Erez Zadok <[email protected]>
---
MAINTAINERS | 3 +--
1 files changed, 1 insertions(+), 2 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 797e7b9..c722f8e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4176,10 +4176,9 @@ S: Maintained
UNIONFS
P: Erez Zadok
M: [email protected]
-P: Josef "Jeff" Sipek
-M: [email protected]
L: [email protected]
W: http://unionfs.filesystems.org
+T: git git.kernel.org/pub/scm/linux/kernel/git/ezk/unionfs.git
S: Maintained

USB ACM DRIVER
--
1.5.2.2

2008-07-30 02:53:13

by Erez Zadok

[permalink] [raw]
Subject: [PATCH 01/19] LTP's iogen01 doio tests used to hang nicely on 32-bit SMP when /tmp was a

From: Hugh Dickins <[email protected]>

But akpm was dissatisfied with the resulting patch: its lack of
commentary, the #ifs, the nesting around i_size_read, the lack of
attention to i_blocks. I promised to redo it with the general
spin_lock_32bit() he proposed; but disliked the result, partly because
"32bit" obscures the real constraints, which are best commented within
fsstack_copy_inode_size itself.

This version adds those comments, and uses sizeof comparisons which the
compiler can optimize out, instead of CONFIG_SMP, CONFIG_LSF.
BITS_PER_LONG.

Signed-off-by: Hugh Dickins <[email protected]>
Cc: Erez Zadok <[email protected]>
Cc: Michael Halcrow <[email protected]>
Cc: <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Erez Zadok <[email protected]>
---
fs/stack.c | 58 ++++++++++++++++++++++++++++++++++++++-------
include/linux/fs_stack.h | 3 +-
2 files changed, 50 insertions(+), 11 deletions(-)

diff --git a/fs/stack.c b/fs/stack.c
index 4336f2b..a66ff6c 100644
--- a/fs/stack.c
+++ b/fs/stack.c
@@ -19,16 +19,56 @@
* 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)
+void fsstack_copy_inode_size(struct inode *dst, struct inode *src)
{
-#if BITS_PER_LONG == 32 && defined(CONFIG_SMP)
- spin_lock(&dst->i_lock);
-#endif
- i_size_write(dst, i_size_read(src));
- dst->i_blocks = src->i_blocks;
-#if BITS_PER_LONG == 32 && defined(CONFIG_SMP)
- spin_unlock(&dst->i_lock);
-#endif
+ loff_t i_size;
+ blkcnt_t i_blocks;
+
+ /*
+ * i_size_read() includes its own seqlocking and protection from
+ * preemption (see include/linux/fs.h): we need nothing extra for
+ * that here, and prefer to avoid nesting locks than attempt to
+ * keep i_size and i_blocks in synch together.
+ */
+ i_size = i_size_read(src);
+
+ /*
+ * But if CONFIG_LSF (on 32-bit), we ought to make an effort to keep
+ * the two halves of i_blocks in synch despite SMP or PREEMPT - though
+ * stat's generic_fillattr() doesn't bother, and we won't be applying
+ * quotas (where i_blocks does become important) at the upper level.
+ *
+ * We don't actually know what locking is used at the lower level; but
+ * if it's a filesystem that supports quotas, it will be using i_lock
+ * as in inode_add_bytes(). tmpfs uses other locking, and its 32-bit
+ * is (just) able to exceed 2TB i_size with the aid of holes; but its
+ * i_blocks cannot carry into the upper long without almost 2TB swap -
+ * let's ignore that case.
+ */
+ if (sizeof(i_blocks) > sizeof(long))
+ spin_lock(&src->i_lock);
+ i_blocks = src->i_blocks;
+ if (sizeof(i_blocks) > sizeof(long))
+ spin_unlock(&src->i_lock);
+
+ /*
+ * If CONFIG_SMP on 32-bit, it's vital for fsstack_copy_inode_size()
+ * to hold some lock around i_size_write(), otherwise i_size_read()
+ * may spin forever (see include/linux/fs.h). We don't necessarily
+ * hold i_mutex when this is called, so take i_lock for that case.
+ *
+ * And if CONFIG_LSF (on 32-bit), continue our effort to keep the
+ * two halves of i_blocks in synch despite SMP or PREEMPT: use i_lock
+ * for that case too, and do both at once by combining the tests.
+ *
+ * There is none of this locking overhead in the 64-bit case.
+ */
+ if (sizeof(i_size) > sizeof(long) || sizeof(i_blocks) > sizeof(long))
+ spin_lock(&dst->i_lock);
+ i_size_write(dst, i_size);
+ dst->i_blocks = i_blocks;
+ if (sizeof(i_size) > sizeof(long) || sizeof(i_blocks) > sizeof(long))
+ spin_unlock(&dst->i_lock);
}
EXPORT_SYMBOL_GPL(fsstack_copy_inode_size);

diff --git a/include/linux/fs_stack.h b/include/linux/fs_stack.h
index 6b52faf..6615a52 100644
--- a/include/linux/fs_stack.h
+++ b/include/linux/fs_stack.h
@@ -21,8 +21,7 @@

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

/* inlines */
static inline void fsstack_copy_attr_atime(struct inode *dest,
--
1.5.2.2