[kernel/f16] Add fix from 3.1 for mounting HFSPlus filesystems

Josh Boyer jwboyer at fedoraproject.org
Tue Aug 2 23:34:25 UTC 2011


commit 0c6a0c8d9d81a40eefa4dbfb45c17fc5f4722ebe
Author: Josh Boyer <jwboyer at redhat.com>
Date:   Tue Aug 2 19:23:10 2011 -0400

    Add fix from 3.1 for mounting HFSPlus filesystems

 ...ure-bio-requests-are-not-smaller-than-the.patch |  315 ++++++++++++++++++++
 kernel.spec                                        |    7 +-
 2 files changed, 321 insertions(+), 1 deletions(-)
---
diff --git a/hfsplus-ensure-bio-requests-are-not-smaller-than-the.patch b/hfsplus-ensure-bio-requests-are-not-smaller-than-the.patch
new file mode 100644
index 0000000..c81bf91
--- /dev/null
+++ b/hfsplus-ensure-bio-requests-are-not-smaller-than-the.patch
@@ -0,0 +1,315 @@
+From 6596528e391ad978a6a120142cba97a1d7324cb6 Mon Sep 17 00:00:00 2001
+From: Seth Forshee <seth.forshee at canonical.com>
+Date: Mon, 18 Jul 2011 08:06:23 -0700
+Subject: [PATCH] hfsplus: ensure bio requests are not smaller than the
+ hardware sectors
+
+Currently all bio requests are 512 bytes, which may fail for media
+whose physical sector size is larger than this. Ensure these
+requests are not smaller than the block device logical block size.
+
+BugLink: http://bugs.launchpad.net/bugs/734883
+Signed-off-by: Seth Forshee <seth.forshee at canonical.com>
+Signed-off-by: Christoph Hellwig <hch at lst.de>
+---
+ fs/hfsplus/hfsplus_fs.h |   16 ++++++++-
+ fs/hfsplus/part_tbl.c   |   32 ++++++++++--------
+ fs/hfsplus/super.c      |   12 +++---
+ fs/hfsplus/wrapper.c    |   83 +++++++++++++++++++++++++++++++++++-----------
+ 4 files changed, 101 insertions(+), 42 deletions(-)
+
+diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h
+index 0bebf74..81dfd1e 100644
+--- a/fs/hfsplus/hfsplus_fs.h
++++ b/fs/hfsplus/hfsplus_fs.h
+@@ -13,6 +13,7 @@
+ #include <linux/fs.h>
+ #include <linux/mutex.h>
+ #include <linux/buffer_head.h>
++#include <linux/blkdev.h>
+ #include "hfsplus_raw.h"
+ 
+ #define DBG_BNODE_REFS	0x00000001
+@@ -110,7 +111,9 @@ struct hfsplus_vh;
+ struct hfs_btree;
+ 
+ struct hfsplus_sb_info {
++	void *s_vhdr_buf;
+ 	struct hfsplus_vh *s_vhdr;
++	void *s_backup_vhdr_buf;
+ 	struct hfsplus_vh *s_backup_vhdr;
+ 	struct hfs_btree *ext_tree;
+ 	struct hfs_btree *cat_tree;
+@@ -258,6 +261,15 @@ struct hfsplus_readdir_data {
+ 	struct hfsplus_cat_key key;
+ };
+ 
++/*
++ * Find minimum acceptible I/O size for an hfsplus sb.
++ */
++static inline unsigned short hfsplus_min_io_size(struct super_block *sb)
++{
++	return max_t(unsigned short, bdev_logical_block_size(sb->s_bdev),
++		     HFSPLUS_SECTOR_SIZE);
++}
++
+ #define hfs_btree_open hfsplus_btree_open
+ #define hfs_btree_close hfsplus_btree_close
+ #define hfs_btree_write hfsplus_btree_write
+@@ -436,8 +448,8 @@ int hfsplus_compare_dentry(const struct dentry *parent,
+ /* wrapper.c */
+ int hfsplus_read_wrapper(struct super_block *);
+ int hfs_part_find(struct super_block *, sector_t *, sector_t *);
+-int hfsplus_submit_bio(struct block_device *bdev, sector_t sector,
+-		void *data, int rw);
++int hfsplus_submit_bio(struct super_block *sb, sector_t sector,
++		void *buf, void **data, int rw);
+ 
+ /* time macros */
+ #define __hfsp_mt2ut(t)		(be32_to_cpu(t) - 2082844800U)
+diff --git a/fs/hfsplus/part_tbl.c b/fs/hfsplus/part_tbl.c
+index 40ad88c..eb355d8 100644
+--- a/fs/hfsplus/part_tbl.c
++++ b/fs/hfsplus/part_tbl.c
+@@ -88,11 +88,12 @@ static int hfs_parse_old_pmap(struct super_block *sb, struct old_pmap *pm,
+ 	return -ENOENT;
+ }
+ 
+-static int hfs_parse_new_pmap(struct super_block *sb, struct new_pmap *pm,
+-		sector_t *part_start, sector_t *part_size)
++static int hfs_parse_new_pmap(struct super_block *sb, void *buf,
++		struct new_pmap *pm, sector_t *part_start, sector_t *part_size)
+ {
+ 	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
+ 	int size = be32_to_cpu(pm->pmMapBlkCnt);
++	int buf_size = hfsplus_min_io_size(sb);
+ 	int res;
+ 	int i = 0;
+ 
+@@ -107,11 +108,14 @@ static int hfs_parse_new_pmap(struct super_block *sb, struct new_pmap *pm,
+ 		if (++i >= size)
+ 			return -ENOENT;
+ 
+-		res = hfsplus_submit_bio(sb->s_bdev,
+-					 *part_start + HFS_PMAP_BLK + i,
+-					 pm, READ);
+-		if (res)
+-			return res;
++		pm = (struct new_pmap *)((u8 *)pm + HFSPLUS_SECTOR_SIZE);
++		if ((u8 *)pm - (u8 *)buf >= buf_size) {
++			res = hfsplus_submit_bio(sb,
++						 *part_start + HFS_PMAP_BLK + i,
++						 buf, (void **)&pm, READ);
++			if (res)
++				return res;
++		}
+ 	} while (pm->pmSig == cpu_to_be16(HFS_NEW_PMAP_MAGIC));
+ 
+ 	return -ENOENT;
+@@ -124,15 +128,15 @@ static int hfs_parse_new_pmap(struct super_block *sb, struct new_pmap *pm,
+ int hfs_part_find(struct super_block *sb,
+ 		sector_t *part_start, sector_t *part_size)
+ {
+-	void *data;
++	void *buf, *data;
+ 	int res;
+ 
+-	data = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL);
+-	if (!data)
++	buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL);
++	if (!buf)
+ 		return -ENOMEM;
+ 
+-	res = hfsplus_submit_bio(sb->s_bdev, *part_start + HFS_PMAP_BLK,
+-				 data, READ);
++	res = hfsplus_submit_bio(sb, *part_start + HFS_PMAP_BLK,
++				 buf, &data, READ);
+ 	if (res)
+ 		goto out;
+ 
+@@ -141,13 +145,13 @@ int hfs_part_find(struct super_block *sb,
+ 		res = hfs_parse_old_pmap(sb, data, part_start, part_size);
+ 		break;
+ 	case HFS_NEW_PMAP_MAGIC:
+-		res = hfs_parse_new_pmap(sb, data, part_start, part_size);
++		res = hfs_parse_new_pmap(sb, buf, data, part_start, part_size);
+ 		break;
+ 	default:
+ 		res = -ENOENT;
+ 		break;
+ 	}
+ out:
+-	kfree(data);
++	kfree(buf);
+ 	return res;
+ }
+diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c
+index 84f56e1..c106ca2 100644
+--- a/fs/hfsplus/super.c
++++ b/fs/hfsplus/super.c
+@@ -203,17 +203,17 @@ int hfsplus_sync_fs(struct super_block *sb, int wait)
+ 		write_backup = 1;
+ 	}
+ 
+-	error2 = hfsplus_submit_bio(sb->s_bdev,
++	error2 = hfsplus_submit_bio(sb,
+ 				   sbi->part_start + HFSPLUS_VOLHEAD_SECTOR,
+-				   sbi->s_vhdr, WRITE_SYNC);
++				   sbi->s_vhdr_buf, NULL, WRITE_SYNC);
+ 	if (!error)
+ 		error = error2;
+ 	if (!write_backup)
+ 		goto out;
+ 
+-	error2 = hfsplus_submit_bio(sb->s_bdev,
++	error2 = hfsplus_submit_bio(sb,
+ 				  sbi->part_start + sbi->sect_count - 2,
+-				  sbi->s_backup_vhdr, WRITE_SYNC);
++				  sbi->s_backup_vhdr_buf, NULL, WRITE_SYNC);
+ 	if (!error)
+ 		error2 = error;
+ out:
+@@ -257,8 +257,8 @@ static void hfsplus_put_super(struct super_block *sb)
+ 	hfs_btree_close(sbi->ext_tree);
+ 	iput(sbi->alloc_file);
+ 	iput(sbi->hidden_dir);
+-	kfree(sbi->s_vhdr);
+-	kfree(sbi->s_backup_vhdr);
++	kfree(sbi->s_vhdr_buf);
++	kfree(sbi->s_backup_vhdr_buf);
+ 	unload_nls(sbi->nls);
+ 	kfree(sb->s_fs_info);
+ 	sb->s_fs_info = NULL;
+diff --git a/fs/hfsplus/wrapper.c b/fs/hfsplus/wrapper.c
+index 2f933e8..10e515a 100644
+--- a/fs/hfsplus/wrapper.c
++++ b/fs/hfsplus/wrapper.c
+@@ -31,25 +31,67 @@ static void hfsplus_end_io_sync(struct bio *bio, int err)
+ 	complete(bio->bi_private);
+ }
+ 
+-int hfsplus_submit_bio(struct block_device *bdev, sector_t sector,
+-		void *data, int rw)
++/*
++ * hfsplus_submit_bio - Perfrom block I/O
++ * @sb: super block of volume for I/O
++ * @sector: block to read or write, for blocks of HFSPLUS_SECTOR_SIZE bytes
++ * @buf: buffer for I/O
++ * @data: output pointer for location of requested data
++ * @rw: direction of I/O
++ *
++ * The unit of I/O is hfsplus_min_io_size(sb), which may be bigger than
++ * HFSPLUS_SECTOR_SIZE, and @buf must be sized accordingly. On reads
++ * @data will return a pointer to the start of the requested sector,
++ * which may not be the same location as @buf.
++ *
++ * If @sector is not aligned to the bdev logical block size it will
++ * be rounded down. For writes this means that @buf should contain data
++ * that starts at the rounded-down address. As long as the data was
++ * read using hfsplus_submit_bio() and the same buffer is used things
++ * will work correctly.
++ */
++int hfsplus_submit_bio(struct super_block *sb, sector_t sector,
++		void *buf, void **data, int rw)
+ {
+ 	DECLARE_COMPLETION_ONSTACK(wait);
+ 	struct bio *bio;
+ 	int ret = 0;
++	unsigned int io_size;
++	loff_t start;
++	int offset;
++
++	/*
++	 * Align sector to hardware sector size and find offset. We
++	 * assume that io_size is a power of two, which _should_
++	 * be true.
++	 */
++	io_size = hfsplus_min_io_size(sb);
++	start = (loff_t)sector << HFSPLUS_SECTOR_SHIFT;
++	offset = start & (io_size - 1);
++	sector &= ~((io_size >> HFSPLUS_SECTOR_SHIFT) - 1);
+ 
+ 	bio = bio_alloc(GFP_NOIO, 1);
+ 	bio->bi_sector = sector;
+-	bio->bi_bdev = bdev;
++	bio->bi_bdev = sb->s_bdev;
+ 	bio->bi_end_io = hfsplus_end_io_sync;
+ 	bio->bi_private = &wait;
+ 
+-	/*
+-	 * We always submit one sector at a time, so bio_add_page must not fail.
+-	 */
+-	if (bio_add_page(bio, virt_to_page(data), HFSPLUS_SECTOR_SIZE,
+-			 offset_in_page(data)) != HFSPLUS_SECTOR_SIZE)
+-		BUG();
++	if (!(rw & WRITE) && data)
++		*data = (u8 *)buf + offset;
++
++	while (io_size > 0) {
++		unsigned int page_offset = offset_in_page(buf);
++		unsigned int len = min_t(unsigned int, PAGE_SIZE - page_offset,
++					 io_size);
++
++		ret = bio_add_page(bio, virt_to_page(buf), len, page_offset);
++		if (ret != len) {
++			ret = -EIO;
++			goto out;
++		}
++		io_size -= len;
++		buf = (u8 *)buf + len;
++	}
+ 
+ 	submit_bio(rw, bio);
+ 	wait_for_completion(&wait);
+@@ -57,8 +99,9 @@ int hfsplus_submit_bio(struct block_device *bdev, sector_t sector,
+ 	if (!bio_flagged(bio, BIO_UPTODATE))
+ 		ret = -EIO;
+ 
++out:
+ 	bio_put(bio);
+-	return ret;
++	return ret < 0 ? ret : 0;
+ }
+ 
+ static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd)
+@@ -143,17 +186,17 @@ int hfsplus_read_wrapper(struct super_block *sb)
+ 		goto out;
+ 
+ 	error = -ENOMEM;
+-	sbi->s_vhdr = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL);
+-	if (!sbi->s_vhdr)
++	sbi->s_vhdr_buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL);
++	if (!sbi->s_vhdr_buf)
+ 		goto out;
+-	sbi->s_backup_vhdr = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL);
+-	if (!sbi->s_backup_vhdr)
++	sbi->s_backup_vhdr_buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL);
++	if (!sbi->s_backup_vhdr_buf)
+ 		goto out_free_vhdr;
+ 
+ reread:
+-	error = hfsplus_submit_bio(sb->s_bdev,
+-				   part_start + HFSPLUS_VOLHEAD_SECTOR,
+-				   sbi->s_vhdr, READ);
++	error = hfsplus_submit_bio(sb, part_start + HFSPLUS_VOLHEAD_SECTOR,
++				   sbi->s_vhdr_buf, (void **)&sbi->s_vhdr,
++				   READ);
+ 	if (error)
+ 		goto out_free_backup_vhdr;
+ 
+@@ -183,9 +226,9 @@ reread:
+ 		goto reread;
+ 	}
+ 
+-	error = hfsplus_submit_bio(sb->s_bdev,
+-				   part_start + part_size - 2,
+-				   sbi->s_backup_vhdr, READ);
++	error = hfsplus_submit_bio(sb, part_start + part_size - 2,
++				   sbi->s_backup_vhdr_buf,
++				   (void **)&sbi->s_backup_vhdr, READ);
+ 	if (error)
+ 		goto out_free_backup_vhdr;
+ 
+-- 
+1.7.6
+
diff --git a/kernel.spec b/kernel.spec
index dfe7cdf..c2d30b1 100644
--- a/kernel.spec
+++ b/kernel.spec
@@ -51,7 +51,7 @@ Summary: The Linux kernel
 # For non-released -rc kernels, this will be prepended with "0.", so
 # for example a 3 here will become 0.3
 #
-%global baserelease 3
+%global baserelease 4
 %global fedora_build %{baserelease}
 
 # base_sublevel is the kernel version we're starting with and patching
@@ -697,6 +697,7 @@ Patch12303: dmar-disable-when-ricoh-multifunction.patch
 
 Patch13000: fix-scsi_dispatch_cmd.patch
 Patch13001: epoll-fix-spurious-lockdep-warnings.patch
+Patch13002: hfsplus-ensure-bio-requests-are-not-smaller-than-the.patch
 
 Patch20000: utrace.patch
 
@@ -1282,6 +1283,7 @@ ApplyPatch dmar-disable-when-ricoh-multifunction.patch
 
 ApplyPatch fix-scsi_dispatch_cmd.patch
 ApplyPatch epoll-fix-spurious-lockdep-warnings.patch
+ApplyPatch hfsplus-ensure-bio-requests-are-not-smaller-than-the.patch
 
 # utrace.
 ApplyPatch utrace.patch
@@ -1895,6 +1897,9 @@ fi
 # and build.
 
 %changelog
+* Tue Aug 02 2011 Josh Boyer <jwboyer at redhat.com>
+- Add patch to fix HFSPlus filesystem mounting (rhbz 720771)
+
 * Tue Aug 02 2011 Dave Jones <davej at redhat.com>
 - Change USB_SERIAL_OPTION back to modular. (rhbz 727680)
 


More information about the scm-commits mailing list