From 0c6a0c8d9d81a40eefa4dbfb45c17fc5f4722ebe Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Tue, 2 Aug 2011 19:23:10 -0400 Subject: [PATCH] Add fix from 3.1 for mounting HFSPlus filesystems --- ...io-requests-are-not-smaller-than-the.patch | 315 ++++++++++++++++++ kernel.spec | 7 +- 2 files changed, 321 insertions(+), 1 deletion(-) create mode 100644 hfsplus-ensure-bio-requests-are-not-smaller-than-the.patch 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 000000000..c81bf91d1 --- /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 +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 +Signed-off-by: Christoph Hellwig +--- + 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 + #include + #include ++#include + #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 dfe7cdf7a..c2d30b1d5 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 +- Add patch to fix HFSPlus filesystem mounting (rhbz 720771) + * Tue Aug 02 2011 Dave Jones - Change USB_SERIAL_OPTION back to modular. (rhbz 727680)