Skip to content

Commit 6596528

Browse files
Seth ForsheeChristoph Hellwig
Seth Forshee
authored and
Christoph Hellwig
committed
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 <[email protected]> Signed-off-by: Christoph Hellwig <[email protected]>
1 parent aac4e41 commit 6596528

File tree

4 files changed

+101
-42
lines changed

4 files changed

+101
-42
lines changed

fs/hfsplus/hfsplus_fs.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/fs.h>
1414
#include <linux/mutex.h>
1515
#include <linux/buffer_head.h>
16+
#include <linux/blkdev.h>
1617
#include "hfsplus_raw.h"
1718

1819
#define DBG_BNODE_REFS 0x00000001
@@ -110,7 +111,9 @@ struct hfsplus_vh;
110111
struct hfs_btree;
111112

112113
struct hfsplus_sb_info {
114+
void *s_vhdr_buf;
113115
struct hfsplus_vh *s_vhdr;
116+
void *s_backup_vhdr_buf;
114117
struct hfsplus_vh *s_backup_vhdr;
115118
struct hfs_btree *ext_tree;
116119
struct hfs_btree *cat_tree;
@@ -258,6 +261,15 @@ struct hfsplus_readdir_data {
258261
struct hfsplus_cat_key key;
259262
};
260263

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+
261273
#define hfs_btree_open hfsplus_btree_open
262274
#define hfs_btree_close hfsplus_btree_close
263275
#define hfs_btree_write hfsplus_btree_write
@@ -436,8 +448,8 @@ int hfsplus_compare_dentry(const struct dentry *parent,
436448
/* wrapper.c */
437449
int hfsplus_read_wrapper(struct super_block *);
438450
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);
441453

442454
/* time macros */
443455
#define __hfsp_mt2ut(t) (be32_to_cpu(t) - 2082844800U)

fs/hfsplus/part_tbl.c

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,12 @@ static int hfs_parse_old_pmap(struct super_block *sb, struct old_pmap *pm,
8888
return -ENOENT;
8989
}
9090

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)
9393
{
9494
struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
9595
int size = be32_to_cpu(pm->pmMapBlkCnt);
96+
int buf_size = hfsplus_min_io_size(sb);
9697
int res;
9798
int i = 0;
9899

@@ -107,11 +108,14 @@ static int hfs_parse_new_pmap(struct super_block *sb, struct new_pmap *pm,
107108
if (++i >= size)
108109
return -ENOENT;
109110

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+
}
115119
} while (pm->pmSig == cpu_to_be16(HFS_NEW_PMAP_MAGIC));
116120

117121
return -ENOENT;
@@ -124,15 +128,15 @@ static int hfs_parse_new_pmap(struct super_block *sb, struct new_pmap *pm,
124128
int hfs_part_find(struct super_block *sb,
125129
sector_t *part_start, sector_t *part_size)
126130
{
127-
void *data;
131+
void *buf, *data;
128132
int res;
129133

130-
data = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL);
131-
if (!data)
134+
buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL);
135+
if (!buf)
132136
return -ENOMEM;
133137

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);
136140
if (res)
137141
goto out;
138142

@@ -141,13 +145,13 @@ int hfs_part_find(struct super_block *sb,
141145
res = hfs_parse_old_pmap(sb, data, part_start, part_size);
142146
break;
143147
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);
145149
break;
146150
default:
147151
res = -ENOENT;
148152
break;
149153
}
150154
out:
151-
kfree(data);
155+
kfree(buf);
152156
return res;
153157
}

fs/hfsplus/super.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -203,17 +203,17 @@ int hfsplus_sync_fs(struct super_block *sb, int wait)
203203
write_backup = 1;
204204
}
205205

206-
error2 = hfsplus_submit_bio(sb->s_bdev,
206+
error2 = hfsplus_submit_bio(sb,
207207
sbi->part_start + HFSPLUS_VOLHEAD_SECTOR,
208-
sbi->s_vhdr, WRITE_SYNC);
208+
sbi->s_vhdr_buf, NULL, WRITE_SYNC);
209209
if (!error)
210210
error = error2;
211211
if (!write_backup)
212212
goto out;
213213

214-
error2 = hfsplus_submit_bio(sb->s_bdev,
214+
error2 = hfsplus_submit_bio(sb,
215215
sbi->part_start + sbi->sect_count - 2,
216-
sbi->s_backup_vhdr, WRITE_SYNC);
216+
sbi->s_backup_vhdr_buf, NULL, WRITE_SYNC);
217217
if (!error)
218218
error2 = error;
219219
out:
@@ -257,8 +257,8 @@ static void hfsplus_put_super(struct super_block *sb)
257257
hfs_btree_close(sbi->ext_tree);
258258
iput(sbi->alloc_file);
259259
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);
262262
unload_nls(sbi->nls);
263263
kfree(sb->s_fs_info);
264264
sb->s_fs_info = NULL;

fs/hfsplus/wrapper.c

Lines changed: 63 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -31,34 +31,77 @@ static void hfsplus_end_io_sync(struct bio *bio, int err)
3131
complete(bio->bi_private);
3232
}
3333

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)
3655
{
3756
DECLARE_COMPLETION_ONSTACK(wait);
3857
struct bio *bio;
3958
int ret = 0;
59+
unsigned int io_size;
60+
loff_t start;
61+
int offset;
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);
4072

4173
bio = bio_alloc(GFP_NOIO, 1);
4274
bio->bi_sector = sector;
43-
bio->bi_bdev = bdev;
75+
bio->bi_bdev = sb->s_bdev;
4476
bio->bi_end_io = hfsplus_end_io_sync;
4577
bio->bi_private = &wait;
4678

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;
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+
}
5395

5496
submit_bio(rw, bio);
5597
wait_for_completion(&wait);
5698

5799
if (!bio_flagged(bio, BIO_UPTODATE))
58100
ret = -EIO;
59101

102+
out:
60103
bio_put(bio);
61-
return ret;
104+
return ret < 0 ? ret : 0;
62105
}
63106

64107
static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd)
@@ -143,17 +186,17 @@ int hfsplus_read_wrapper(struct super_block *sb)
143186
goto out;
144187

145188
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)
148191
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)
151194
goto out_free_vhdr;
152195

153196
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);
157200
if (error)
158201
goto out_free_backup_vhdr;
159202

@@ -183,9 +226,9 @@ int hfsplus_read_wrapper(struct super_block *sb)
183226
goto reread;
184227
}
185228

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);
189232
if (error)
190233
goto out_free_backup_vhdr;
191234

0 commit comments

Comments
 (0)