Commit c53c89aba3ebdfc3e9acdb18bb5ee9d2f8a328d0

Authored by Seth Forshee
Committed by Greg Kroah-Hartman
1 parent 16b2b5c638
Exists in imx_3.0.35_4.1.0

hfsplus: ensure bio requests are not smaller than the hardware sectors

commit 6596528e391ad978a6a120142cba97a1d7324cb6 upstream.

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@canonical.com>
Signed-off-by: Christoph Hellwig <hch@lst.de>
Cc: Josh Boyer <jwboyer@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

Showing 4 changed files with 101 additions and 42 deletions Side-by-side Diff

fs/hfsplus/hfsplus_fs.h
... ... @@ -13,6 +13,7 @@
13 13 #include <linux/fs.h>
14 14 #include <linux/mutex.h>
15 15 #include <linux/buffer_head.h>
  16 +#include <linux/blkdev.h>
16 17 #include "hfsplus_raw.h"
17 18  
18 19 #define DBG_BNODE_REFS 0x00000001
19 20  
... ... @@ -110,7 +111,9 @@
110 111 struct hfs_btree;
111 112  
112 113 struct hfsplus_sb_info {
  114 + void *s_vhdr_buf;
113 115 struct hfsplus_vh *s_vhdr;
  116 + void *s_backup_vhdr_buf;
114 117 struct hfsplus_vh *s_backup_vhdr;
115 118 struct hfs_btree *ext_tree;
116 119 struct hfs_btree *cat_tree;
... ... @@ -258,6 +261,15 @@
258 261 struct hfsplus_cat_key key;
259 262 };
260 263  
  264 +/*
  265 + * Find minimum acceptible I/O size for an hfsplus sb.
  266 + */
  267 +static inline unsigned short hfsplus_min_io_size(struct super_block *sb)
  268 +{
  269 + return max_t(unsigned short, bdev_logical_block_size(sb->s_bdev),
  270 + HFSPLUS_SECTOR_SIZE);
  271 +}
  272 +
261 273 #define hfs_btree_open hfsplus_btree_open
262 274 #define hfs_btree_close hfsplus_btree_close
263 275 #define hfs_btree_write hfsplus_btree_write
... ... @@ -436,8 +448,8 @@
436 448 /* wrapper.c */
437 449 int hfsplus_read_wrapper(struct super_block *);
438 450 int hfs_part_find(struct super_block *, sector_t *, sector_t *);
439   -int hfsplus_submit_bio(struct block_device *bdev, sector_t sector,
440   - void *data, int rw);
  451 +int hfsplus_submit_bio(struct super_block *sb, sector_t sector,
  452 + void *buf, void **data, int rw);
441 453  
442 454 /* time macros */
443 455 #define __hfsp_mt2ut(t) (be32_to_cpu(t) - 2082844800U)
fs/hfsplus/part_tbl.c
... ... @@ -88,11 +88,12 @@
88 88 return -ENOENT;
89 89 }
90 90  
91   -static int hfs_parse_new_pmap(struct super_block *sb, struct new_pmap *pm,
92   - sector_t *part_start, sector_t *part_size)
  91 +static int hfs_parse_new_pmap(struct super_block *sb, void *buf,
  92 + struct new_pmap *pm, sector_t *part_start, sector_t *part_size)
93 93 {
94 94 struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
95 95 int size = be32_to_cpu(pm->pmMapBlkCnt);
  96 + int buf_size = hfsplus_min_io_size(sb);
96 97 int res;
97 98 int i = 0;
98 99  
... ... @@ -107,11 +108,14 @@
107 108 if (++i >= size)
108 109 return -ENOENT;
109 110  
110   - res = hfsplus_submit_bio(sb->s_bdev,
111   - *part_start + HFS_PMAP_BLK + i,
112   - pm, READ);
113   - if (res)
114   - return res;
  111 + pm = (struct new_pmap *)((u8 *)pm + HFSPLUS_SECTOR_SIZE);
  112 + if ((u8 *)pm - (u8 *)buf >= buf_size) {
  113 + res = hfsplus_submit_bio(sb,
  114 + *part_start + HFS_PMAP_BLK + i,
  115 + buf, (void **)&pm, READ);
  116 + if (res)
  117 + return res;
  118 + }
115 119 } while (pm->pmSig == cpu_to_be16(HFS_NEW_PMAP_MAGIC));
116 120  
117 121 return -ENOENT;
118 122  
119 123  
... ... @@ -124,15 +128,15 @@
124 128 int hfs_part_find(struct super_block *sb,
125 129 sector_t *part_start, sector_t *part_size)
126 130 {
127   - void *data;
  131 + void *buf, *data;
128 132 int res;
129 133  
130   - data = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL);
131   - if (!data)
  134 + buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL);
  135 + if (!buf)
132 136 return -ENOMEM;
133 137  
134   - res = hfsplus_submit_bio(sb->s_bdev, *part_start + HFS_PMAP_BLK,
135   - data, READ);
  138 + res = hfsplus_submit_bio(sb, *part_start + HFS_PMAP_BLK,
  139 + buf, &data, READ);
136 140 if (res)
137 141 goto out;
138 142  
139 143  
... ... @@ -141,14 +145,14 @@
141 145 res = hfs_parse_old_pmap(sb, data, part_start, part_size);
142 146 break;
143 147 case HFS_NEW_PMAP_MAGIC:
144   - res = hfs_parse_new_pmap(sb, data, part_start, part_size);
  148 + res = hfs_parse_new_pmap(sb, buf, data, part_start, part_size);
145 149 break;
146 150 default:
147 151 res = -ENOENT;
148 152 break;
149 153 }
150 154 out:
151   - kfree(data);
  155 + kfree(buf);
152 156 return res;
153 157 }
... ... @@ -197,17 +197,17 @@
197 197 write_backup = 1;
198 198 }
199 199  
200   - error2 = hfsplus_submit_bio(sb->s_bdev,
  200 + error2 = hfsplus_submit_bio(sb,
201 201 sbi->part_start + HFSPLUS_VOLHEAD_SECTOR,
202   - sbi->s_vhdr, WRITE_SYNC);
  202 + sbi->s_vhdr_buf, NULL, WRITE_SYNC);
203 203 if (!error)
204 204 error = error2;
205 205 if (!write_backup)
206 206 goto out;
207 207  
208   - error2 = hfsplus_submit_bio(sb->s_bdev,
  208 + error2 = hfsplus_submit_bio(sb,
209 209 sbi->part_start + sbi->sect_count - 2,
210   - sbi->s_backup_vhdr, WRITE_SYNC);
  210 + sbi->s_backup_vhdr_buf, NULL, WRITE_SYNC);
211 211 if (!error)
212 212 error2 = error;
213 213 out:
... ... @@ -251,8 +251,8 @@
251 251 hfs_btree_close(sbi->ext_tree);
252 252 iput(sbi->alloc_file);
253 253 iput(sbi->hidden_dir);
254   - kfree(sbi->s_vhdr);
255   - kfree(sbi->s_backup_vhdr);
  254 + kfree(sbi->s_vhdr_buf);
  255 + kfree(sbi->s_backup_vhdr_buf);
256 256 unload_nls(sbi->nls);
257 257 kfree(sb->s_fs_info);
258 258 sb->s_fs_info = NULL;
fs/hfsplus/wrapper.c
... ... @@ -31,34 +31,77 @@
31 31 complete(bio->bi_private);
32 32 }
33 33  
34   -int hfsplus_submit_bio(struct block_device *bdev, sector_t sector,
35   - void *data, int rw)
  34 +/*
  35 + * hfsplus_submit_bio - Perfrom block I/O
  36 + * @sb: super block of volume for I/O
  37 + * @sector: block to read or write, for blocks of HFSPLUS_SECTOR_SIZE bytes
  38 + * @buf: buffer for I/O
  39 + * @data: output pointer for location of requested data
  40 + * @rw: direction of I/O
  41 + *
  42 + * The unit of I/O is hfsplus_min_io_size(sb), which may be bigger than
  43 + * HFSPLUS_SECTOR_SIZE, and @buf must be sized accordingly. On reads
  44 + * @data will return a pointer to the start of the requested sector,
  45 + * which may not be the same location as @buf.
  46 + *
  47 + * If @sector is not aligned to the bdev logical block size it will
  48 + * be rounded down. For writes this means that @buf should contain data
  49 + * that starts at the rounded-down address. As long as the data was
  50 + * read using hfsplus_submit_bio() and the same buffer is used things
  51 + * will work correctly.
  52 + */
  53 +int hfsplus_submit_bio(struct super_block *sb, sector_t sector,
  54 + void *buf, void **data, int rw)
36 55 {
37 56 DECLARE_COMPLETION_ONSTACK(wait);
38 57 struct bio *bio;
39 58 int ret = 0;
  59 + unsigned int io_size;
  60 + loff_t start;
  61 + int offset;
40 62  
  63 + /*
  64 + * Align sector to hardware sector size and find offset. We
  65 + * assume that io_size is a power of two, which _should_
  66 + * be true.
  67 + */
  68 + io_size = hfsplus_min_io_size(sb);
  69 + start = (loff_t)sector << HFSPLUS_SECTOR_SHIFT;
  70 + offset = start & (io_size - 1);
  71 + sector &= ~((io_size >> HFSPLUS_SECTOR_SHIFT) - 1);
  72 +
41 73 bio = bio_alloc(GFP_NOIO, 1);
42 74 bio->bi_sector = sector;
43   - bio->bi_bdev = bdev;
  75 + bio->bi_bdev = sb->s_bdev;
44 76 bio->bi_end_io = hfsplus_end_io_sync;
45 77 bio->bi_private = &wait;
46 78  
47   - /*
48   - * We always submit one sector at a time, so bio_add_page must not fail.
49   - */
50   - if (bio_add_page(bio, virt_to_page(data), HFSPLUS_SECTOR_SIZE,
51   - offset_in_page(data)) != HFSPLUS_SECTOR_SIZE)
52   - BUG();
  79 + if (!(rw & WRITE) && data)
  80 + *data = (u8 *)buf + offset;
53 81  
  82 + while (io_size > 0) {
  83 + unsigned int page_offset = offset_in_page(buf);
  84 + unsigned int len = min_t(unsigned int, PAGE_SIZE - page_offset,
  85 + io_size);
  86 +
  87 + ret = bio_add_page(bio, virt_to_page(buf), len, page_offset);
  88 + if (ret != len) {
  89 + ret = -EIO;
  90 + goto out;
  91 + }
  92 + io_size -= len;
  93 + buf = (u8 *)buf + len;
  94 + }
  95 +
54 96 submit_bio(rw, bio);
55 97 wait_for_completion(&wait);
56 98  
57 99 if (!bio_flagged(bio, BIO_UPTODATE))
58 100 ret = -EIO;
59 101  
  102 +out:
60 103 bio_put(bio);
61   - return ret;
  104 + return ret < 0 ? ret : 0;
62 105 }
63 106  
64 107 static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd)
65 108  
66 109  
... ... @@ -147,17 +190,17 @@
147 190 }
148 191  
149 192 error = -ENOMEM;
150   - sbi->s_vhdr = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL);
151   - if (!sbi->s_vhdr)
  193 + sbi->s_vhdr_buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL);
  194 + if (!sbi->s_vhdr_buf)
152 195 goto out;
153   - sbi->s_backup_vhdr = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL);
154   - if (!sbi->s_backup_vhdr)
  196 + sbi->s_backup_vhdr_buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL);
  197 + if (!sbi->s_backup_vhdr_buf)
155 198 goto out_free_vhdr;
156 199  
157 200 reread:
158   - error = hfsplus_submit_bio(sb->s_bdev,
159   - part_start + HFSPLUS_VOLHEAD_SECTOR,
160   - sbi->s_vhdr, READ);
  201 + error = hfsplus_submit_bio(sb, part_start + HFSPLUS_VOLHEAD_SECTOR,
  202 + sbi->s_vhdr_buf, (void **)&sbi->s_vhdr,
  203 + READ);
161 204 if (error)
162 205 goto out_free_backup_vhdr;
163 206  
... ... @@ -186,9 +229,9 @@
186 229 goto reread;
187 230 }
188 231  
189   - error = hfsplus_submit_bio(sb->s_bdev,
190   - part_start + part_size - 2,
191   - sbi->s_backup_vhdr, READ);
  232 + error = hfsplus_submit_bio(sb, part_start + part_size - 2,
  233 + sbi->s_backup_vhdr_buf,
  234 + (void **)&sbi->s_backup_vhdr, READ);
192 235 if (error)
193 236 goto out_free_backup_vhdr;
194 237