Commit 6596528e391ad978a6a120142cba97a1d7324cb6
Committed by
Christoph Hellwig
1 parent
aac4e4198e
Exists in
master
and in
38 other branches
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@canonical.com> Signed-off-by: Christoph Hellwig <hch@lst.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 | } |
fs/hfsplus/super.c
... | ... | @@ -203,17 +203,17 @@ |
203 | 203 | write_backup = 1; |
204 | 204 | } |
205 | 205 | |
206 | - error2 = hfsplus_submit_bio(sb->s_bdev, | |
206 | + error2 = hfsplus_submit_bio(sb, | |
207 | 207 | sbi->part_start + HFSPLUS_VOLHEAD_SECTOR, |
208 | - sbi->s_vhdr, WRITE_SYNC); | |
208 | + sbi->s_vhdr_buf, NULL, WRITE_SYNC); | |
209 | 209 | if (!error) |
210 | 210 | error = error2; |
211 | 211 | if (!write_backup) |
212 | 212 | goto out; |
213 | 213 | |
214 | - error2 = hfsplus_submit_bio(sb->s_bdev, | |
214 | + error2 = hfsplus_submit_bio(sb, | |
215 | 215 | sbi->part_start + sbi->sect_count - 2, |
216 | - sbi->s_backup_vhdr, WRITE_SYNC); | |
216 | + sbi->s_backup_vhdr_buf, NULL, WRITE_SYNC); | |
217 | 217 | if (!error) |
218 | 218 | error2 = error; |
219 | 219 | out: |
... | ... | @@ -257,8 +257,8 @@ |
257 | 257 | hfs_btree_close(sbi->ext_tree); |
258 | 258 | iput(sbi->alloc_file); |
259 | 259 | iput(sbi->hidden_dir); |
260 | - kfree(sbi->s_vhdr); | |
261 | - kfree(sbi->s_backup_vhdr); | |
260 | + kfree(sbi->s_vhdr_buf); | |
261 | + kfree(sbi->s_backup_vhdr_buf); | |
262 | 262 | unload_nls(sbi->nls); |
263 | 263 | kfree(sb->s_fs_info); |
264 | 264 | 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 | |
... | ... | @@ -143,17 +186,17 @@ |
143 | 186 | goto out; |
144 | 187 | |
145 | 188 | error = -ENOMEM; |
146 | - sbi->s_vhdr = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL); | |
147 | - if (!sbi->s_vhdr) | |
189 | + sbi->s_vhdr_buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL); | |
190 | + if (!sbi->s_vhdr_buf) | |
148 | 191 | goto out; |
149 | - sbi->s_backup_vhdr = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL); | |
150 | - if (!sbi->s_backup_vhdr) | |
192 | + sbi->s_backup_vhdr_buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL); | |
193 | + if (!sbi->s_backup_vhdr_buf) | |
151 | 194 | goto out_free_vhdr; |
152 | 195 | |
153 | 196 | reread: |
154 | - error = hfsplus_submit_bio(sb->s_bdev, | |
155 | - part_start + HFSPLUS_VOLHEAD_SECTOR, | |
156 | - sbi->s_vhdr, READ); | |
197 | + error = hfsplus_submit_bio(sb, part_start + HFSPLUS_VOLHEAD_SECTOR, | |
198 | + sbi->s_vhdr_buf, (void **)&sbi->s_vhdr, | |
199 | + READ); | |
157 | 200 | if (error) |
158 | 201 | goto out_free_backup_vhdr; |
159 | 202 | |
... | ... | @@ -183,9 +226,9 @@ |
183 | 226 | goto reread; |
184 | 227 | } |
185 | 228 | |
186 | - error = hfsplus_submit_bio(sb->s_bdev, | |
187 | - part_start + part_size - 2, | |
188 | - sbi->s_backup_vhdr, READ); | |
229 | + error = hfsplus_submit_bio(sb, part_start + part_size - 2, | |
230 | + sbi->s_backup_vhdr_buf, | |
231 | + (void **)&sbi->s_backup_vhdr, READ); | |
189 | 232 | if (error) |
190 | 233 | goto out_free_backup_vhdr; |
191 | 234 |