Commit 13c9d416 authored by Alexander Viro's avatar Alexander Viro Committed by Linus Torvalds

[PATCH] jffs2_get_sb() fixes

Fixes races in jffs2_get_sb() - current code has a window when two
mounts of the same mtd device can miss each other, resulting in two
active instances of jffs2 fighting over the same device.
parent c03285bf
...@@ -94,12 +94,12 @@ static struct super_operations jffs2_super_operations = ...@@ -94,12 +94,12 @@ static struct super_operations jffs2_super_operations =
static int jffs2_sb_compare(struct super_block *sb, void *data) static int jffs2_sb_compare(struct super_block *sb, void *data)
{ {
struct mtd_info *mtd = data; struct jffs2_sb_info *p = data;
struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
/* The superblocks are considered to be equivalent if the underlying MTD /* The superblocks are considered to be equivalent if the underlying MTD
device is the same one */ device is the same one */
if (c->mtd == mtd) { if (c->mtd == p->mtd) {
D1(printk(KERN_DEBUG "jffs2_sb_compare: match on device %d (\"%s\")\n", mtd->index, mtd->name)); D1(printk(KERN_DEBUG "jffs2_sb_compare: match on device %d (\"%s\")\n", mtd->index, mtd->name));
return 1; return 1;
} else { } else {
...@@ -111,12 +111,14 @@ static int jffs2_sb_compare(struct super_block *sb, void *data) ...@@ -111,12 +111,14 @@ static int jffs2_sb_compare(struct super_block *sb, void *data)
static int jffs2_sb_set(struct super_block *sb, void *data) static int jffs2_sb_set(struct super_block *sb, void *data)
{ {
struct mtd_info *mtd = data; struct jffs2_sb_info *p = data;
/* For persistence of NFS exports etc. we use the same s_dev /* For persistence of NFS exports etc. we use the same s_dev
each time we mount the device, don't just use an anonymous each time we mount the device, don't just use an anonymous
device */ device */
sb->s_dev = mk_kdev(MTD_BLOCK_MAJOR, mtd->index); sb->u.generic_sbp = p;
p->os_priv = sb;
sb->s_dev = mk_kdev(MTD_BLOCK_MAJOR, p->mtd->index);
return 0; return 0;
} }
...@@ -129,7 +131,13 @@ static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type, ...@@ -129,7 +131,13 @@ static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type,
struct jffs2_sb_info *c; struct jffs2_sb_info *c;
int ret; int ret;
sb = sget(fs_type, jffs2_sb_compare, jffs2_sb_set, mtd); c = kmalloc(sizeof(*c), GFP_KERNEL);
if (!c)
return ERR_PTR(-ENOMEM);
memset(c, 0, sizeof(*c));
c->mtd = mtd;
sb = sget(fs_type, jffs2_sb_compare, jffs2_sb_set, c);
if (IS_ERR(sb)) if (IS_ERR(sb))
goto out_put; goto out_put;
...@@ -144,19 +152,8 @@ static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type, ...@@ -144,19 +152,8 @@ static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type,
D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): New superblock for device %d (\"%s\")\n", D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): New superblock for device %d (\"%s\")\n",
mtd->index, mtd->name)); mtd->index, mtd->name));
c = kmalloc(sizeof(*c), GFP_KERNEL);
if (!c) {
sb = ERR_PTR(-ENOMEM);
goto out_put;
}
sb->u.generic_sbp = c;
sb->s_op = &jffs2_super_operations; sb->s_op = &jffs2_super_operations;
memset(c, 0, sizeof(*c));
c->os_priv = sb;
c->mtd = mtd;
ret = jffs2_do_fill_super(sb, data, (flags&MS_VERBOSE)?1:0); ret = jffs2_do_fill_super(sb, data, (flags&MS_VERBOSE)?1:0);
if (ret) { if (ret) {
...@@ -164,13 +161,15 @@ static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type, ...@@ -164,13 +161,15 @@ static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type,
up_write(&sb->s_umount); up_write(&sb->s_umount);
deactivate_super(sb); deactivate_super(sb);
sb = ERR_PTR(ret); sb = ERR_PTR(ret);
goto out_put; goto out_put1;
} }
sb->s_flags |= MS_ACTIVE; sb->s_flags |= MS_ACTIVE;
return sb; return sb;
out_put: out_put:
kfree(c);
out_put1:
put_mtd_device(mtd); put_mtd_device(mtd);
return sb; return sb;
...@@ -288,18 +287,23 @@ void jffs2_put_super (struct super_block *sb) ...@@ -288,18 +287,23 @@ void jffs2_put_super (struct super_block *sb)
kfree(c->blocks); kfree(c->blocks);
if (c->mtd->sync) if (c->mtd->sync)
c->mtd->sync(c->mtd); c->mtd->sync(c->mtd);
put_mtd_device(c->mtd);
kfree(c);
D1(printk(KERN_DEBUG "jffs2_put_super returning\n")); D1(printk(KERN_DEBUG "jffs2_put_super returning\n"));
} }
static void jffs2_kill_sb(struct super_block *sb)
{
struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
generic_shutdown_super(sb);
put_mtd_device(c->mtd);
kfree(c);
}
static struct file_system_type jffs2_fs_type = { static struct file_system_type jffs2_fs_type = {
owner: THIS_MODULE, owner: THIS_MODULE,
name: "jffs2", name: "jffs2",
get_sb: jffs2_get_sb, get_sb: jffs2_get_sb,
kill_sb: generic_shutdown_super kill_sb: jffs2_kill_sb,
}; };
......
...@@ -184,7 +184,7 @@ static void remove_super(struct super_block *s) ...@@ -184,7 +184,7 @@ static void remove_super(struct super_block *s)
up_write(&s->s_umount); up_write(&s->s_umount);
} }
static void generic_shutdown_super(struct super_block *sb) void generic_shutdown_super(struct super_block *sb)
{ {
struct dentry *root = sb->s_root; struct dentry *root = sb->s_root;
struct super_operations *sop = sb->s_op; struct super_operations *sop = sb->s_op;
......
...@@ -956,6 +956,7 @@ struct super_block *get_sb_single(struct file_system_type *fs_type, ...@@ -956,6 +956,7 @@ struct super_block *get_sb_single(struct file_system_type *fs_type,
struct super_block *get_sb_nodev(struct file_system_type *fs_type, struct super_block *get_sb_nodev(struct file_system_type *fs_type,
int flags, void *data, int flags, void *data,
int (*fill_super)(struct super_block *, void *, int)); int (*fill_super)(struct super_block *, void *, int));
void generic_shutdown_super(struct super_block *sb);
void kill_block_super(struct super_block *sb); void kill_block_super(struct super_block *sb);
void kill_anon_super(struct super_block *sb); void kill_anon_super(struct super_block *sb);
void kill_litter_super(struct super_block *sb); void kill_litter_super(struct super_block *sb);
......
...@@ -293,6 +293,7 @@ EXPORT_SYMBOL(get_sb_nodev); ...@@ -293,6 +293,7 @@ EXPORT_SYMBOL(get_sb_nodev);
EXPORT_SYMBOL(get_sb_single); EXPORT_SYMBOL(get_sb_single);
EXPORT_SYMBOL(kill_anon_super); EXPORT_SYMBOL(kill_anon_super);
EXPORT_SYMBOL(kill_litter_super); EXPORT_SYMBOL(kill_litter_super);
EXPORT_SYMBOL(generic_shutdown_super);
EXPORT_SYMBOL(deactivate_super); EXPORT_SYMBOL(deactivate_super);
EXPORT_SYMBOL(sget); EXPORT_SYMBOL(sget);
EXPORT_SYMBOL(set_anon_super); EXPORT_SYMBOL(set_anon_super);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment