1
0
mirror of git://projects.qi-hardware.com/openwrt-xburst.git synced 2024-11-27 23:29:22 +02:00

remove incomplete union mounts / jffs2 patches. will be replaced by updated versions once they're ready - should fix #6375

git-svn-id: svn://svn.openwrt.org/openwrt/trunk@18822 3c298f89-4303-0410-b956-a3cf2f4a3e73
This commit is contained in:
nbd 2009-12-18 15:43:20 +00:00
parent e9b1e2efdf
commit 210b6ad13c
12 changed files with 0 additions and 11116 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +0,0 @@
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1550,7 +1550,7 @@ static int do_loopback(struct path *path
if (!mnt)
goto out;
- err = check_union_mnt(&old_path, mnt, mnt_flags);
+ err = check_union_mnt(path, mnt, mnt_flags);
if (err)
goto out;

View File

@ -1,11 +0,0 @@
--- a/include/linux/union.h
+++ b/include/linux/union.h
@@ -77,7 +77,7 @@ extern int union_permission(struct path
#define __union_copyup(x, y, z) ({ BUG(); (0); })
#define union_copyup(x, y) ({ (0); })
#define union_copyup_dir(x) ({ BUG(); (0); })
-#define union_permission(x, y) inode_permission(x->dentry->d_inode, y)
+#define union_permission(x, y) inode_permission((x)->dentry->d_inode, y)
#endif /* CONFIG_UNION_MOUNT */
#endif /* __KERNEL__ */

View File

@ -1,186 +0,0 @@
--- a/fs/jffs2/dir.c
+++ b/fs/jffs2/dir.c
@@ -34,6 +34,9 @@ static int jffs2_mknod (struct inode *,s
static int jffs2_rename (struct inode *, struct dentry *,
struct inode *, struct dentry *);
+static int jffs2_whiteout (struct inode *, struct dentry *, struct dentry *);
+static int jffs2_fallthru (struct inode *, struct dentry *);
+
const struct file_operations jffs2_dir_operations =
{
.read = generic_read_dir,
@@ -55,6 +58,8 @@ const struct inode_operations jffs2_dir_
.rmdir = jffs2_rmdir,
.mknod = jffs2_mknod,
.rename = jffs2_rename,
+ .fallthru = jffs2_fallthru,
+ .whiteout = jffs2_whiteout,
.permission = jffs2_permission,
.setattr = jffs2_setattr,
.setxattr = jffs2_setxattr,
@@ -98,8 +103,21 @@ static struct dentry *jffs2_lookup(struc
fd = fd_list;
}
}
- if (fd)
- ino = fd->ino;
+ if (fd) {
+ spin_lock(&target->d_lock);
+ switch(fd->type) {
+ case DT_WHT:
+ target->d_flags |= DCACHE_WHITEOUT;
+ break;
+ case DT_UNKNOWN:
+ target->d_flags |= DCACHE_FALLTHRU;
+ break;
+ default:
+ ino = fd->ino;
+ break;
+ }
+ spin_unlock(&target->d_lock);
+ }
mutex_unlock(&dir_f->sem);
if (ino) {
inode = jffs2_iget(dir_i->i_sb, ino);
@@ -155,7 +173,9 @@ static int jffs2_readdir(struct file *fi
fd->name, fd->ino, fd->type, curofs, offset));
continue;
}
- if (!fd->ino) {
+ if (fd->type == DT_UNKNOWN)
+ fd->ino = 100; /* XXX: arbitrary */
+ else if (!fd->ino && (fd->type != DT_WHT)) {
D2(printk(KERN_DEBUG "Skipping deletion dirent \"%s\"\n", fd->name));
offset++;
continue;
@@ -498,6 +518,11 @@ static int jffs2_mkdir (struct inode *di
return PTR_ERR(inode);
}
+ if (dentry->d_flags & DCACHE_WHITEOUT) {
+ inode->i_flags |= S_OPAQUE;
+ ri->flags = cpu_to_je16(JFFS2_INO_FLAG_OPAQUE);
+ }
+
inode->i_op = &jffs2_dir_inode_operations;
inode->i_fop = &jffs2_dir_operations;
@@ -779,6 +804,82 @@ static int jffs2_mknod (struct inode *di
return 0;
}
+static int jffs2_fallthru (struct inode *dir, struct dentry *dentry)
+{
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(dir->i_sb);
+ uint32_t now;
+ int ret;
+
+ now = get_seconds();
+ ret = jffs2_do_link(c, JFFS2_INODE_INFO(dir), 0, DT_UNKNOWN,
+ dentry->d_name.name, dentry->d_name.len, now);
+ if (ret)
+ return ret;
+
+ d_instantiate(dentry, NULL);
+ spin_lock(&dentry->d_lock);
+ dentry->d_flags |= DCACHE_FALLTHRU;
+ spin_unlock(&dentry->d_lock);
+
+ return 0;
+}
+
+static int jffs2_whiteout (struct inode *dir, struct dentry *old_dentry,
+ struct dentry *new_dentry)
+{
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(dir->i_sb);
+ struct jffs2_inode_info *victim_f = NULL;
+ uint32_t now;
+ int ret;
+
+ /* If it's a directory, then check whether it is really empty
+ */
+ if (new_dentry->d_inode) {
+ victim_f = JFFS2_INODE_INFO(old_dentry->d_inode);
+ if (S_ISDIR(old_dentry->d_inode->i_mode)) {
+ struct jffs2_full_dirent *fd;
+
+ mutex_lock(&victim_f->sem);
+ for (fd = victim_f->dents; fd; fd = fd->next) {
+ if (fd->ino) {
+ mutex_unlock(&victim_f->sem);
+ return -ENOTEMPTY;
+ }
+ }
+ mutex_unlock(&victim_f->sem);
+ }
+ }
+
+ now = get_seconds();
+ ret = jffs2_do_link(c, JFFS2_INODE_INFO(dir), 0, DT_WHT,
+ new_dentry->d_name.name, new_dentry->d_name.len, now);
+ if (ret)
+ return ret;
+
+ spin_lock(&new_dentry->d_lock);
+ new_dentry->d_flags &= ~DCACHE_FALLTHRU;
+ new_dentry->d_flags |= DCACHE_WHITEOUT;
+ spin_unlock(&new_dentry->d_lock);
+ d_add(new_dentry, NULL);
+
+ if (victim_f) {
+ /* There was a victim. Kill it off nicely */
+ drop_nlink(old_dentry->d_inode);
+ /* Don't oops if the victim was a dirent pointing to an
+ inode which didn't exist. */
+ if (victim_f->inocache) {
+ mutex_lock(&victim_f->sem);
+ if (S_ISDIR(old_dentry->d_inode->i_mode))
+ victim_f->inocache->pino_nlink = 0;
+ else
+ victim_f->inocache->pino_nlink--;
+ mutex_unlock(&victim_f->sem);
+ }
+ }
+
+ return 0;
+}
+
static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry,
struct inode *new_dir_i, struct dentry *new_dentry)
{
--- a/fs/jffs2/fs.c
+++ b/fs/jffs2/fs.c
@@ -301,6 +301,10 @@ struct inode *jffs2_iget(struct super_bl
inode->i_op = &jffs2_dir_inode_operations;
inode->i_fop = &jffs2_dir_operations;
+
+ if (je16_to_cpu(latest_node.flags) & JFFS2_INO_FLAG_OPAQUE)
+ inode->i_flags |= S_OPAQUE;
+
break;
}
case S_IFREG:
--- a/fs/jffs2/super.c
+++ b/fs/jffs2/super.c
@@ -172,7 +172,7 @@ static int jffs2_fill_super(struct super
sb->s_op = &jffs2_super_operations;
sb->s_export_op = &jffs2_export_ops;
- sb->s_flags = sb->s_flags | MS_NOATIME;
+ sb->s_flags = sb->s_flags | MS_NOATIME | MS_WHITEOUT;
sb->s_xattr = jffs2_xattr_handlers;
#ifdef CONFIG_JFFS2_FS_POSIX_ACL
sb->s_flags |= MS_POSIXACL;
--- a/include/linux/jffs2.h
+++ b/include/linux/jffs2.h
@@ -87,6 +87,8 @@
#define JFFS2_INO_FLAG_USERCOMPR 2 /* User has requested a specific
compression type */
+#define JFFS2_INO_FLAG_OPAQUE 4 /* Directory is opaque (for union mounts) */
+
/* These can go once we've made sure we've caught all uses without
byteswapping */

View File

@ -1,30 +0,0 @@
--- a/fs/union.c
+++ b/fs/union.c
@@ -842,10 +842,8 @@ static int union_copyup_dir_one(void *bu
/* Lookup this entry in the topmost directory */
dentry = lookup_one_len(name, topmost_dentry, namlen);
- if (IS_ERR(dentry)) {
- printk(KERN_INFO "error looking up %s\n", dentry->d_name.name);
+ if (IS_ERR(dentry))
goto out;
- }
/*
* If the entry already exists, one of the following is true:
@@ -857,7 +855,6 @@ static int union_copyup_dir_one(void *bu
*/
if (dentry->d_inode || d_is_whiteout(dentry) ||
d_is_fallthru(dentry)) {
- printk(KERN_INFO "skipping copy of %s\n", dentry->d_name.name);
goto out_dput;
}
@@ -867,7 +864,6 @@ static int union_copyup_dir_one(void *bu
* used, so each file system must implement its own way of
* storing a fallthru entry.
*/
- printk(KERN_INFO "creating fallthru for %s\n", dentry->d_name.name);
err = topmost_dentry->d_inode->i_op->fallthru(topmost_dentry->d_inode,
dentry);
/* FIXME */

View File

@ -1,117 +0,0 @@
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1656,8 +1656,10 @@ static int do_move_mount(struct path *pa
/* moving to or from a union mount is not supported */
err = -EINVAL;
+#if 0
if (IS_MNT_UNION(path->mnt))
goto exit;
+#endif
if (IS_MNT_UNION(old_path.mnt))
goto exit;
--- a/fs/union.c
+++ b/fs/union.c
@@ -260,8 +260,6 @@ int append_to_union(struct vfsmount *mnt
spin_lock(&union_lock);
um = union_lookup(dentry, mnt);
if (um) {
- BUG_ON((um->u_next.dentry != dest_dentry) ||
- (um->u_next.mnt != dest_mnt));
spin_unlock(&union_lock);
union_put(this);
return 0;
@@ -274,6 +272,23 @@ int append_to_union(struct vfsmount *mnt
return 0;
}
+int follow_union_mountpoint(struct path *path)
+{
+ struct path new_path = *path;
+
+ path_get(&new_path);
+ while (follow_union_down(&new_path)) {
+ if (new_path.dentry != new_path.mnt->mnt_root)
+ continue;
+
+ path_put(path);
+ *path = new_path;
+ return 1;
+ }
+ path_put(&new_path);
+ return 0;
+}
+
/*
* follow_union_down - follow the union stack one layer down
*
--- a/include/linux/union.h
+++ b/include/linux/union.h
@@ -47,6 +47,7 @@ extern int append_to_union(struct vfsmou
struct vfsmount *, struct dentry *);
extern int follow_union_down(struct path *);
extern int follow_union_mount(struct path *);
+extern int follow_union_mountpoint(struct path *path);
extern void __d_drop_unions(struct dentry *);
extern void shrink_d_unions(struct dentry *);
extern void __shrink_d_unions(struct dentry *, struct list_head *);
@@ -68,6 +69,7 @@ extern int union_permission(struct path
#define append_to_union(x1, y1, x2, y2) ({ BUG(); (0); })
#define follow_union_down(x) ({ (0); })
#define follow_union_mount(x) ({ (0); })
+#define follow_union_mountpoint(x) ({ (0); })
#define __d_drop_unions(x) do { } while (0)
#define shrink_d_unions(x) do { } while (0)
#define __shrink_d_unions(x,y) do { } while (0)
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -613,6 +613,9 @@ static int cache_lookup_union(struct nam
!S_ISDIR(path->dentry->d_inode->i_mode))
goto out;
+ if (follow_union_mountpoint(path))
+ goto out;
+
/* Build the union stack for this part */
res = __cache_lookup_build_union(nd, name, path);
if (res) {
@@ -886,6 +889,9 @@ static int real_lookup_union(struct name
!S_ISDIR(path->dentry->d_inode->i_mode))
goto out;
+ if (follow_union_mountpoint(path))
+ goto out;
+
/* Build the union stack for this part */
res = __real_lookup_build_union(nd, name, path);
if (res) {
@@ -1813,6 +1819,9 @@ int hash_lookup_union(struct nameidata *
!S_ISDIR(path->dentry->d_inode->i_mode))
goto out;
+ if (follow_union_mountpoint(path))
+ goto out;
+
/* Build the union stack for this part */
res = __hash_lookup_build_union(nd, name, path);
if (res) {
--- a/fs/readdir.c
+++ b/fs/readdir.c
@@ -17,6 +17,7 @@
#include <linux/syscalls.h>
#include <linux/unistd.h>
#include <linux/union.h>
+#include <linux/mount.h>
#include <asm/uaccess.h>
@@ -45,7 +46,7 @@ int vfs_readdir(struct file *file, filld
* below this one in the union stack.
*/
if (is_unionized(file->f_path.dentry, file->f_path.mnt) &&
- !IS_OPAQUE(inode)) {
+ !IS_OPAQUE(inode) && IS_MNT_UNION(file->f_path.mnt)) {
res = union_copyup_dir(&file->f_path);
if (res)
goto out_unlock;

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +0,0 @@
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1550,7 +1550,7 @@ static int do_loopback(struct path *path
if (!mnt)
goto out;
- err = check_union_mnt(&old_path, mnt, mnt_flags);
+ err = check_union_mnt(path, mnt, mnt_flags);
if (err)
goto out;

View File

@ -1,11 +0,0 @@
--- a/include/linux/union.h
+++ b/include/linux/union.h
@@ -77,7 +77,7 @@ extern int union_permission(struct path
#define __union_copyup(x, y, z) ({ BUG(); (0); })
#define union_copyup(x, y) ({ (0); })
#define union_copyup_dir(x) ({ BUG(); (0); })
-#define union_permission(x, y) inode_permission(x->dentry->d_inode, y)
+#define union_permission(x, y) inode_permission((x)->dentry->d_inode, y)
#endif /* CONFIG_UNION_MOUNT */
#endif /* __KERNEL__ */

View File

@ -1,186 +0,0 @@
--- a/fs/jffs2/dir.c
+++ b/fs/jffs2/dir.c
@@ -34,6 +34,9 @@ static int jffs2_mknod (struct inode *,s
static int jffs2_rename (struct inode *, struct dentry *,
struct inode *, struct dentry *);
+static int jffs2_whiteout (struct inode *, struct dentry *, struct dentry *);
+static int jffs2_fallthru (struct inode *, struct dentry *);
+
const struct file_operations jffs2_dir_operations =
{
.read = generic_read_dir,
@@ -55,6 +58,8 @@ const struct inode_operations jffs2_dir_
.rmdir = jffs2_rmdir,
.mknod = jffs2_mknod,
.rename = jffs2_rename,
+ .fallthru = jffs2_fallthru,
+ .whiteout = jffs2_whiteout,
.check_acl = jffs2_check_acl,
.setattr = jffs2_setattr,
.setxattr = jffs2_setxattr,
@@ -98,8 +103,21 @@ static struct dentry *jffs2_lookup(struc
fd = fd_list;
}
}
- if (fd)
- ino = fd->ino;
+ if (fd) {
+ spin_lock(&target->d_lock);
+ switch(fd->type) {
+ case DT_WHT:
+ target->d_flags |= DCACHE_WHITEOUT;
+ break;
+ case DT_UNKNOWN:
+ target->d_flags |= DCACHE_FALLTHRU;
+ break;
+ default:
+ ino = fd->ino;
+ break;
+ }
+ spin_unlock(&target->d_lock);
+ }
mutex_unlock(&dir_f->sem);
if (ino) {
inode = jffs2_iget(dir_i->i_sb, ino);
@@ -155,7 +173,9 @@ static int jffs2_readdir(struct file *fi
fd->name, fd->ino, fd->type, curofs, offset));
continue;
}
- if (!fd->ino) {
+ if (fd->type == DT_UNKNOWN)
+ fd->ino = 100; /* XXX: arbitrary */
+ else if (!fd->ino && (fd->type != DT_WHT)) {
D2(printk(KERN_DEBUG "Skipping deletion dirent \"%s\"\n", fd->name));
offset++;
continue;
@@ -498,6 +518,11 @@ static int jffs2_mkdir (struct inode *di
return PTR_ERR(inode);
}
+ if (dentry->d_flags & DCACHE_WHITEOUT) {
+ inode->i_flags |= S_OPAQUE;
+ ri->flags = cpu_to_je16(JFFS2_INO_FLAG_OPAQUE);
+ }
+
inode->i_op = &jffs2_dir_inode_operations;
inode->i_fop = &jffs2_dir_operations;
@@ -779,6 +804,82 @@ static int jffs2_mknod (struct inode *di
return 0;
}
+static int jffs2_fallthru (struct inode *dir, struct dentry *dentry)
+{
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(dir->i_sb);
+ uint32_t now;
+ int ret;
+
+ now = get_seconds();
+ ret = jffs2_do_link(c, JFFS2_INODE_INFO(dir), 0, DT_UNKNOWN,
+ dentry->d_name.name, dentry->d_name.len, now);
+ if (ret)
+ return ret;
+
+ d_instantiate(dentry, NULL);
+ spin_lock(&dentry->d_lock);
+ dentry->d_flags |= DCACHE_FALLTHRU;
+ spin_unlock(&dentry->d_lock);
+
+ return 0;
+}
+
+static int jffs2_whiteout (struct inode *dir, struct dentry *old_dentry,
+ struct dentry *new_dentry)
+{
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(dir->i_sb);
+ struct jffs2_inode_info *victim_f = NULL;
+ uint32_t now;
+ int ret;
+
+ /* If it's a directory, then check whether it is really empty
+ */
+ if (new_dentry->d_inode) {
+ victim_f = JFFS2_INODE_INFO(old_dentry->d_inode);
+ if (S_ISDIR(old_dentry->d_inode->i_mode)) {
+ struct jffs2_full_dirent *fd;
+
+ mutex_lock(&victim_f->sem);
+ for (fd = victim_f->dents; fd; fd = fd->next) {
+ if (fd->ino) {
+ mutex_unlock(&victim_f->sem);
+ return -ENOTEMPTY;
+ }
+ }
+ mutex_unlock(&victim_f->sem);
+ }
+ }
+
+ now = get_seconds();
+ ret = jffs2_do_link(c, JFFS2_INODE_INFO(dir), 0, DT_WHT,
+ new_dentry->d_name.name, new_dentry->d_name.len, now);
+ if (ret)
+ return ret;
+
+ spin_lock(&new_dentry->d_lock);
+ new_dentry->d_flags &= ~DCACHE_FALLTHRU;
+ new_dentry->d_flags |= DCACHE_WHITEOUT;
+ spin_unlock(&new_dentry->d_lock);
+ d_add(new_dentry, NULL);
+
+ if (victim_f) {
+ /* There was a victim. Kill it off nicely */
+ drop_nlink(old_dentry->d_inode);
+ /* Don't oops if the victim was a dirent pointing to an
+ inode which didn't exist. */
+ if (victim_f->inocache) {
+ mutex_lock(&victim_f->sem);
+ if (S_ISDIR(old_dentry->d_inode->i_mode))
+ victim_f->inocache->pino_nlink = 0;
+ else
+ victim_f->inocache->pino_nlink--;
+ mutex_unlock(&victim_f->sem);
+ }
+ }
+
+ return 0;
+}
+
static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry,
struct inode *new_dir_i, struct dentry *new_dentry)
{
--- a/fs/jffs2/fs.c
+++ b/fs/jffs2/fs.c
@@ -301,6 +301,10 @@ struct inode *jffs2_iget(struct super_bl
inode->i_op = &jffs2_dir_inode_operations;
inode->i_fop = &jffs2_dir_operations;
+
+ if (je16_to_cpu(latest_node.flags) & JFFS2_INO_FLAG_OPAQUE)
+ inode->i_flags |= S_OPAQUE;
+
break;
}
case S_IFREG:
--- a/fs/jffs2/super.c
+++ b/fs/jffs2/super.c
@@ -172,7 +172,7 @@ static int jffs2_fill_super(struct super
sb->s_op = &jffs2_super_operations;
sb->s_export_op = &jffs2_export_ops;
- sb->s_flags = sb->s_flags | MS_NOATIME;
+ sb->s_flags = sb->s_flags | MS_NOATIME | MS_WHITEOUT;
sb->s_xattr = jffs2_xattr_handlers;
#ifdef CONFIG_JFFS2_FS_POSIX_ACL
sb->s_flags |= MS_POSIXACL;
--- a/include/linux/jffs2.h
+++ b/include/linux/jffs2.h
@@ -87,6 +87,8 @@
#define JFFS2_INO_FLAG_USERCOMPR 2 /* User has requested a specific
compression type */
+#define JFFS2_INO_FLAG_OPAQUE 4 /* Directory is opaque (for union mounts) */
+
/* These can go once we've made sure we've caught all uses without
byteswapping */

View File

@ -1,30 +0,0 @@
--- a/fs/union.c
+++ b/fs/union.c
@@ -842,10 +842,8 @@ static int union_copyup_dir_one(void *bu
/* Lookup this entry in the topmost directory */
dentry = lookup_one_len(name, topmost_dentry, namlen);
- if (IS_ERR(dentry)) {
- printk(KERN_INFO "error looking up %s\n", dentry->d_name.name);
+ if (IS_ERR(dentry))
goto out;
- }
/*
* If the entry already exists, one of the following is true:
@@ -857,7 +855,6 @@ static int union_copyup_dir_one(void *bu
*/
if (dentry->d_inode || d_is_whiteout(dentry) ||
d_is_fallthru(dentry)) {
- printk(KERN_INFO "skipping copy of %s\n", dentry->d_name.name);
goto out_dput;
}
@@ -867,7 +864,6 @@ static int union_copyup_dir_one(void *bu
* used, so each file system must implement its own way of
* storing a fallthru entry.
*/
- printk(KERN_INFO "creating fallthru for %s\n", dentry->d_name.name);
err = topmost_dentry->d_inode->i_op->fallthru(topmost_dentry->d_inode,
dentry);
/* FIXME */

View File

@ -1,117 +0,0 @@
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1656,8 +1656,10 @@ static int do_move_mount(struct path *pa
/* moving to or from a union mount is not supported */
err = -EINVAL;
+#if 0
if (IS_MNT_UNION(path->mnt))
goto exit;
+#endif
if (IS_MNT_UNION(old_path.mnt))
goto exit;
--- a/fs/union.c
+++ b/fs/union.c
@@ -260,8 +260,6 @@ int append_to_union(struct vfsmount *mnt
spin_lock(&union_lock);
um = union_lookup(dentry, mnt);
if (um) {
- BUG_ON((um->u_next.dentry != dest_dentry) ||
- (um->u_next.mnt != dest_mnt));
spin_unlock(&union_lock);
union_put(this);
return 0;
@@ -274,6 +272,23 @@ int append_to_union(struct vfsmount *mnt
return 0;
}
+int follow_union_mountpoint(struct path *path)
+{
+ struct path new_path = *path;
+
+ path_get(&new_path);
+ while (follow_union_down(&new_path)) {
+ if (new_path.dentry != new_path.mnt->mnt_root)
+ continue;
+
+ path_put(path);
+ *path = new_path;
+ return 1;
+ }
+ path_put(&new_path);
+ return 0;
+}
+
/*
* follow_union_down - follow the union stack one layer down
*
--- a/include/linux/union.h
+++ b/include/linux/union.h
@@ -47,6 +47,7 @@ extern int append_to_union(struct vfsmou
struct vfsmount *, struct dentry *);
extern int follow_union_down(struct path *);
extern int follow_union_mount(struct path *);
+extern int follow_union_mountpoint(struct path *path);
extern void __d_drop_unions(struct dentry *);
extern void shrink_d_unions(struct dentry *);
extern void __shrink_d_unions(struct dentry *, struct list_head *);
@@ -68,6 +69,7 @@ extern int union_permission(struct path
#define append_to_union(x1, y1, x2, y2) ({ BUG(); (0); })
#define follow_union_down(x) ({ (0); })
#define follow_union_mount(x) ({ (0); })
+#define follow_union_mountpoint(x) ({ (0); })
#define __d_drop_unions(x) do { } while (0)
#define shrink_d_unions(x) do { } while (0)
#define __shrink_d_unions(x,y) do { } while (0)
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -626,6 +626,9 @@ static int cache_lookup_union(struct nam
!S_ISDIR(path->dentry->d_inode->i_mode))
goto out;
+ if (follow_union_mountpoint(path))
+ goto out;
+
/* Build the union stack for this part */
res = __cache_lookup_build_union(nd, name, path);
if (res) {
@@ -892,6 +895,9 @@ static int real_lookup_union(struct name
!S_ISDIR(path->dentry->d_inode->i_mode))
goto out;
+ if (follow_union_mountpoint(path))
+ goto out;
+
/* Build the union stack for this part */
res = __real_lookup_build_union(nd, name, path);
if (res) {
@@ -1813,6 +1819,9 @@ int hash_lookup_union(struct nameidata *
!S_ISDIR(path->dentry->d_inode->i_mode))
goto out;
+ if (follow_union_mountpoint(path))
+ goto out;
+
/* Build the union stack for this part */
res = __hash_lookup_build_union(nd, name, path);
if (res) {
--- a/fs/readdir.c
+++ b/fs/readdir.c
@@ -17,6 +17,7 @@
#include <linux/syscalls.h>
#include <linux/unistd.h>
#include <linux/union.h>
+#include <linux/mount.h>
#include <asm/uaccess.h>
@@ -45,7 +46,7 @@ int vfs_readdir(struct file *file, filld
* below this one in the union stack.
*/
if (is_unionized(file->f_path.dentry, file->f_path.mnt) &&
- !IS_OPAQUE(inode)) {
+ !IS_OPAQUE(inode) && IS_MNT_UNION(file->f_path.mnt)) {
res = union_copyup_dir(&file->f_path);
if (res)
goto out_unlock;