Chuck Ebbert 64ca523
From: Christoph Hellwig <hch@tuxera.com>
Chuck Ebbert 64ca523
Subject: hfsplus: fix failed mount handling
Chuck Ebbert 64ca523
Chuck Ebbert 64ca523
Currently the error handling in hfsplus_fill_super is a mess, and can
Chuck Ebbert 64ca523
lead to accessing fields in the superblock that haven't been even set
Chuck Ebbert 64ca523
up yet.  Fix this by making sure we do not set up sb->s_root until we
Chuck Ebbert 64ca523
have the mount fully set up, and before that do proper step by step
Chuck Ebbert 64ca523
unwinding instead of using hfsplus_put_super as a big hammer.
Chuck Ebbert 64ca523
Chuck Ebbert 64ca523
Reported-by: Dan Williams <dcbw@redhat.com>
Chuck Ebbert 64ca523
Signed-off-by: Christoph Hellwig <hch@tuxera.com>
Chuck Ebbert 64ca523
Chuck Ebbert 64ca523
Index: linux-2.6/fs/hfsplus/super.c
Chuck Ebbert 64ca523
===================================================================
Chuck Ebbert 64ca523
--- linux-2.6.orig/fs/hfsplus/super.c	2011-02-01 12:48:09.984663687 -0700
Chuck Ebbert 64ca523
+++ linux-2.6/fs/hfsplus/super.c	2011-02-01 13:17:41.803164619 -0700
Chuck Ebbert 64ca523
@@ -338,20 +338,28 @@ static int hfsplus_fill_super(struct sup
Chuck Ebbert 64ca523
 	struct inode *root, *inode;
Chuck Ebbert 64ca523
 	struct qstr str;
Chuck Ebbert 64ca523
 	struct nls_table *nls = NULL;
Chuck Ebbert 64ca523
-	int err = -EINVAL;
Chuck Ebbert 64ca523
+	int err;
Chuck Ebbert 64ca523
 
Chuck Ebbert 64ca523
+	err = -EINVAL;
Chuck Ebbert 64ca523
 	sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
Chuck Ebbert 64ca523
 	if (!sbi)
Chuck Ebbert 64ca523
-		return -ENOMEM;
Chuck Ebbert 64ca523
+		goto out;
Chuck Ebbert 64ca523
 
Chuck Ebbert 64ca523
 	sb->s_fs_info = sbi;
Chuck Ebbert 64ca523
 	mutex_init(&sbi->alloc_mutex);
Chuck Ebbert 64ca523
 	mutex_init(&sbi->vh_mutex);
Chuck Ebbert 64ca523
 	hfsplus_fill_defaults(sbi);
Chuck Ebbert 64ca523
+
Chuck Ebbert 64ca523
+	err = -EINVAL;
Chuck Ebbert 64ca523
 	if (!hfsplus_parse_options(data, sbi)) {
Chuck Ebbert 64ca523
 		printk(KERN_ERR "hfs: unable to parse mount options\n");
Chuck Ebbert 64ca523
-		err = -EINVAL;
Chuck Ebbert 64ca523
-		goto cleanup;
Chuck Ebbert 64ca523
+
Chuck Ebbert 64ca523
+		/*
Chuck Ebbert 64ca523
+		 * hfsplus_parse_options sets sbi->nls, but we are going
Chuck Ebbert 64ca523
+		 * to free the localy cached nls in the cleanup path.
Chuck Ebbert 64ca523
+		 */
Chuck Ebbert 64ca523
+		nls = sbi->nls;
Chuck Ebbert 64ca523
+		goto out_unload_nls;
Chuck Ebbert 64ca523
 	}
Chuck Ebbert 64ca523
 
Chuck Ebbert 64ca523
 	/* temporarily use utf8 to correctly find the hidden dir below */
Chuck Ebbert 64ca523
@@ -359,16 +367,14 @@ static int hfsplus_fill_super(struct sup
Chuck Ebbert 64ca523
 	sbi->nls = load_nls("utf8");
Chuck Ebbert 64ca523
 	if (!sbi->nls) {
Chuck Ebbert 64ca523
 		printk(KERN_ERR "hfs: unable to load nls for utf8\n");
Chuck Ebbert 64ca523
-		err = -EINVAL;
Chuck Ebbert 64ca523
-		goto cleanup;
Chuck Ebbert 64ca523
+		goto out_unload_nls;
Chuck Ebbert 64ca523
 	}
Chuck Ebbert 64ca523
 
Chuck Ebbert 64ca523
 	/* Grab the volume header */
Chuck Ebbert 64ca523
 	if (hfsplus_read_wrapper(sb)) {
Chuck Ebbert 64ca523
 		if (!silent)
Chuck Ebbert 64ca523
 			printk(KERN_WARNING "hfs: unable to find HFS+ superblock\n");
Chuck Ebbert 64ca523
-		err = -EINVAL;
Chuck Ebbert 64ca523
-		goto cleanup;
Chuck Ebbert 64ca523
+		goto out_free_vhdr;
Chuck Ebbert 64ca523
 	}
Chuck Ebbert 64ca523
 	vhdr = sbi->s_vhdr;
Chuck Ebbert 64ca523
 
Chuck Ebbert 64ca523
@@ -377,7 +383,7 @@ static int hfsplus_fill_super(struct sup
Chuck Ebbert 64ca523
 	if (be16_to_cpu(vhdr->version) < HFSPLUS_MIN_VERSION ||
Chuck Ebbert 64ca523
 	    be16_to_cpu(vhdr->version) > HFSPLUS_CURRENT_VERSION) {
Chuck Ebbert 64ca523
 		printk(KERN_ERR "hfs: wrong filesystem version\n");
Chuck Ebbert 64ca523
-		goto cleanup;
Chuck Ebbert 64ca523
+		goto out_free_vhdr;
Chuck Ebbert 64ca523
 	}
Chuck Ebbert 64ca523
 	sbi->total_blocks = be32_to_cpu(vhdr->total_blocks);
Chuck Ebbert 64ca523
 	sbi->free_blocks = be32_to_cpu(vhdr->free_blocks);
Chuck Ebbert 64ca523
@@ -421,19 +427,19 @@ static int hfsplus_fill_super(struct sup
Chuck Ebbert 64ca523
 	sbi->ext_tree = hfs_btree_open(sb, HFSPLUS_EXT_CNID);
Chuck Ebbert 64ca523
 	if (!sbi->ext_tree) {
Chuck Ebbert 64ca523
 		printk(KERN_ERR "hfs: failed to load extents file\n");
Chuck Ebbert 64ca523
-		goto cleanup;
Chuck Ebbert 64ca523
+		goto out_free_vhdr;
Chuck Ebbert 64ca523
 	}
Chuck Ebbert 64ca523
 	sbi->cat_tree = hfs_btree_open(sb, HFSPLUS_CAT_CNID);
Chuck Ebbert 64ca523
 	if (!sbi->cat_tree) {
Chuck Ebbert 64ca523
 		printk(KERN_ERR "hfs: failed to load catalog file\n");
Chuck Ebbert 64ca523
-		goto cleanup;
Chuck Ebbert 64ca523
+		goto out_close_ext_tree;
Chuck Ebbert 64ca523
 	}
Chuck Ebbert 64ca523
 
Chuck Ebbert 64ca523
 	inode = hfsplus_iget(sb, HFSPLUS_ALLOC_CNID);
Chuck Ebbert 64ca523
 	if (IS_ERR(inode)) {
Chuck Ebbert 64ca523
 		printk(KERN_ERR "hfs: failed to load allocation file\n");
Chuck Ebbert 64ca523
 		err = PTR_ERR(inode);
Chuck Ebbert 64ca523
-		goto cleanup;
Chuck Ebbert 64ca523
+		goto out_close_cat_tree;
Chuck Ebbert 64ca523
 	}
Chuck Ebbert 64ca523
 	sbi->alloc_file = inode;
Chuck Ebbert 64ca523
 
Chuck Ebbert 64ca523
@@ -442,14 +448,7 @@ static int hfsplus_fill_super(struct sup
Chuck Ebbert 64ca523
 	if (IS_ERR(root)) {
Chuck Ebbert 64ca523
 		printk(KERN_ERR "hfs: failed to load root directory\n");
Chuck Ebbert 64ca523
 		err = PTR_ERR(root);
Chuck Ebbert 64ca523
-		goto cleanup;
Chuck Ebbert 64ca523
-	}
Chuck Ebbert 64ca523
-	sb->s_d_op = &hfsplus_dentry_operations;
Chuck Ebbert 64ca523
-	sb->s_root = d_alloc_root(root);
Chuck Ebbert 64ca523
-	if (!sb->s_root) {
Chuck Ebbert 64ca523
-		iput(root);
Chuck Ebbert 64ca523
-		err = -ENOMEM;
Chuck Ebbert 64ca523
-		goto cleanup;
Chuck Ebbert 64ca523
+		goto out_put_alloc_file;
Chuck Ebbert 64ca523
 	}
Chuck Ebbert 64ca523
 
Chuck Ebbert 64ca523
 	str.len = sizeof(HFSP_HIDDENDIR_NAME) - 1;
Chuck Ebbert 64ca523
@@ -459,46 +458,68 @@ static int hfsplus_fill_super(struct sup
Chuck Ebbert 64ca523
 	if (!hfs_brec_read(&fd, &entry, sizeof(entry))) {
Chuck Ebbert 64ca523
 		hfs_find_exit(&fd;;
Chuck Ebbert 64ca523
 		if (entry.type != cpu_to_be16(HFSPLUS_FOLDER))
Chuck Ebbert 64ca523
-			goto cleanup;
Chuck Ebbert 64ca523
+			goto out_put_root;
Chuck Ebbert 64ca523
 		inode = hfsplus_iget(sb, be32_to_cpu(entry.folder.id));
Chuck Ebbert 64ca523
 		if (IS_ERR(inode)) {
Chuck Ebbert 64ca523
 			err = PTR_ERR(inode);
Chuck Ebbert 64ca523
-			goto cleanup;
Chuck Ebbert 64ca523
+			goto out_put_root;
Chuck Ebbert 64ca523
 		}
Chuck Ebbert 64ca523
 		sbi->hidden_dir = inode;
Chuck Ebbert 64ca523
 	} else
Chuck Ebbert 64ca523
 		hfs_find_exit(&fd;;
Chuck Ebbert 64ca523
 
Chuck Ebbert 64ca523
-	if (sb->s_flags & MS_RDONLY)
Chuck Ebbert 64ca523
-		goto out;
Chuck Ebbert 64ca523
+	if (!(sb->s_flags & MS_RDONLY)) {
Chuck Ebbert 64ca523
+		/*
Chuck Ebbert 64ca523
+		 * H+LX == hfsplusutils, H+Lx == this driver, H+lx is unused
Chuck Ebbert 64ca523
+		 * all three are registered with Apple for our use
Chuck Ebbert 64ca523
+		 */
Chuck Ebbert 64ca523
+		vhdr->last_mount_vers = cpu_to_be32(HFSP_MOUNT_VERSION);
Chuck Ebbert 64ca523
+		vhdr->modify_date = hfsp_now2mt();
Chuck Ebbert 64ca523
+		be32_add_cpu(&vhdr->write_count, 1);
Chuck Ebbert 64ca523
+		vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_UNMNT);
Chuck Ebbert 64ca523
+		vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT);
Chuck Ebbert 64ca523
+		hfsplus_sync_fs(sb, 1);
Chuck Ebbert 64ca523
+
Chuck Ebbert 64ca523
+		if (!sbi->hidden_dir) {
Chuck Ebbert 64ca523
+			mutex_lock(&sbi->vh_mutex);
Chuck Ebbert 64ca523
+			sbi->hidden_dir = hfsplus_new_inode(sb, S_IFDIR);
Chuck Ebbert 64ca523
+			hfsplus_create_cat(sbi->hidden_dir->i_ino, root, &str,
Chuck Ebbert 64ca523
+					   sbi->hidden_dir);
Chuck Ebbert 64ca523
+			mutex_unlock(&sbi->vh_mutex);
Chuck Ebbert 64ca523
 
Chuck Ebbert 64ca523
-	/* H+LX == hfsplusutils, H+Lx == this driver, H+lx is unused
Chuck Ebbert 64ca523
-	 * all three are registered with Apple for our use
Chuck Ebbert 64ca523
-	 */
Chuck Ebbert 64ca523
-	vhdr->last_mount_vers = cpu_to_be32(HFSP_MOUNT_VERSION);
Chuck Ebbert 64ca523
-	vhdr->modify_date = hfsp_now2mt();
Chuck Ebbert 64ca523
-	be32_add_cpu(&vhdr->write_count, 1);
Chuck Ebbert 64ca523
-	vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_UNMNT);
Chuck Ebbert 64ca523
-	vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT);
Chuck Ebbert 64ca523
-	hfsplus_sync_fs(sb, 1);
Chuck Ebbert 64ca523
-
Chuck Ebbert 64ca523
-	if (!sbi->hidden_dir) {
Chuck Ebbert 64ca523
-		mutex_lock(&sbi->vh_mutex);
Chuck Ebbert 64ca523
-		sbi->hidden_dir = hfsplus_new_inode(sb, S_IFDIR);
Chuck Ebbert 64ca523
-		hfsplus_create_cat(sbi->hidden_dir->i_ino, sb->s_root->d_inode,
Chuck Ebbert 64ca523
-				   &str, sbi->hidden_dir);
Chuck Ebbert 64ca523
-		mutex_unlock(&sbi->vh_mutex);
Chuck Ebbert 64ca523
+			hfsplus_mark_inode_dirty(sbi->hidden_dir,
Chuck Ebbert 64ca523
+						 HFSPLUS_I_CAT_DIRTY);
Chuck Ebbert 64ca523
+		}
Chuck Ebbert 64ca523
+	}
Chuck Ebbert 64ca523
 
Chuck Ebbert 64ca523
-		hfsplus_mark_inode_dirty(sbi->hidden_dir, HFSPLUS_I_CAT_DIRTY);
Chuck Ebbert 64ca523
+	sb->s_d_op = &hfsplus_dentry_operations;
Chuck Ebbert 64ca523
+	sb->s_root = d_alloc_root(root);
Chuck Ebbert 64ca523
+	if (!sb->s_root) {
Chuck Ebbert 64ca523
+		err = -ENOMEM;
Chuck Ebbert 64ca523
+		goto out_put_hidden_dir;
Chuck Ebbert 64ca523
 	}
Chuck Ebbert 64ca523
-out:
Chuck Ebbert 64ca523
+
Chuck Ebbert 64ca523
 	unload_nls(sbi->nls);
Chuck Ebbert 64ca523
 	sbi->nls = nls;
Chuck Ebbert 64ca523
 	return 0;
Chuck Ebbert 64ca523
 
Chuck Ebbert 64ca523
-cleanup:
Chuck Ebbert 64ca523
-	hfsplus_put_super(sb);
Chuck Ebbert 64ca523
+out_put_hidden_dir:
Chuck Ebbert 64ca523
+	iput(sbi->hidden_dir);
Chuck Ebbert 64ca523
+out_put_root:
Chuck Ebbert 64ca523
+	iput(sbi->alloc_file);
Chuck Ebbert 64ca523
+out_put_alloc_file:
Chuck Ebbert 64ca523
+	iput(sbi->alloc_file);
Chuck Ebbert 64ca523
+out_close_cat_tree:
Chuck Ebbert 64ca523
+	hfs_btree_close(sbi->cat_tree);
Chuck Ebbert 64ca523
+out_close_ext_tree:
Chuck Ebbert 64ca523
+	hfs_btree_close(sbi->ext_tree);
Chuck Ebbert 64ca523
+out_free_vhdr:
Chuck Ebbert 64ca523
+	kfree(sbi->s_vhdr);
Chuck Ebbert 64ca523
+	kfree(sbi->s_backup_vhdr);
Chuck Ebbert 64ca523
+out_unload_nls:
Chuck Ebbert 64ca523
 	unload_nls(nls);
Chuck Ebbert 64ca523
+	kfree(sbi);
Chuck Ebbert 64ca523
+out:
Chuck Ebbert 64ca523
 	return err;
Chuck Ebbert 64ca523
 }
Chuck Ebbert 64ca523