aboutsummaryrefslogtreecommitdiffstats
path: root/fs/hfsplus
diff options
context:
space:
mode:
authorSeth Forshee <seth.forshee@canonical.com>2011-07-18 11:06:23 -0400
committerChristoph Hellwig <hch@lst.de>2011-07-22 10:37:44 -0400
commit6596528e391ad978a6a120142cba97a1d7324cb6 (patch)
tree9f9ad6f52b9665074545639c54399e2680e6c889 /fs/hfsplus
parentaac4e4198eff7f9551d586c55342403d49249d95 (diff)
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>
Diffstat (limited to 'fs/hfsplus')
-rw-r--r--fs/hfsplus/hfsplus_fs.h16
-rw-r--r--fs/hfsplus/part_tbl.c32
-rw-r--r--fs/hfsplus/super.c12
-rw-r--r--fs/hfsplus/wrapper.c83
4 files changed, 101 insertions, 42 deletions
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h
index 0bebf7488feb..81dfd1e495e3 100644
--- a/fs/hfsplus/hfsplus_fs.h
+++ b/fs/hfsplus/hfsplus_fs.h
@@ -13,6 +13,7 @@
13#include <linux/fs.h> 13#include <linux/fs.h>
14#include <linux/mutex.h> 14#include <linux/mutex.h>
15#include <linux/buffer_head.h> 15#include <linux/buffer_head.h>
16#include <linux/blkdev.h>
16#include "hfsplus_raw.h" 17#include "hfsplus_raw.h"
17 18
18#define DBG_BNODE_REFS 0x00000001 19#define DBG_BNODE_REFS 0x00000001
@@ -110,7 +111,9 @@ struct hfsplus_vh;
110struct hfs_btree; 111struct hfs_btree;
111 112
112struct hfsplus_sb_info { 113struct hfsplus_sb_info {
114 void *s_vhdr_buf;
113 struct hfsplus_vh *s_vhdr; 115 struct hfsplus_vh *s_vhdr;
116 void *s_backup_vhdr_buf;
114 struct hfsplus_vh *s_backup_vhdr; 117 struct hfsplus_vh *s_backup_vhdr;
115 struct hfs_btree *ext_tree; 118 struct hfs_btree *ext_tree;
116 struct hfs_btree *cat_tree; 119 struct hfs_btree *cat_tree;
@@ -258,6 +261,15 @@ struct hfsplus_readdir_data {
258 struct hfsplus_cat_key key; 261 struct hfsplus_cat_key key;
259}; 262};
260 263
264/*
265 * Find minimum acceptible I/O size for an hfsplus sb.
266 */
267static 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#define hfs_btree_open hfsplus_btree_open 273#define hfs_btree_open hfsplus_btree_open
262#define hfs_btree_close hfsplus_btree_close 274#define hfs_btree_close hfsplus_btree_close
263#define hfs_btree_write hfsplus_btree_write 275#define hfs_btree_write hfsplus_btree_write
@@ -436,8 +448,8 @@ int hfsplus_compare_dentry(const struct dentry *parent,
436/* wrapper.c */ 448/* wrapper.c */
437int hfsplus_read_wrapper(struct super_block *); 449int hfsplus_read_wrapper(struct super_block *);
438int hfs_part_find(struct super_block *, sector_t *, sector_t *); 450int hfs_part_find(struct super_block *, sector_t *, sector_t *);
439int hfsplus_submit_bio(struct block_device *bdev, sector_t sector, 451int hfsplus_submit_bio(struct super_block *sb, sector_t sector,
440 void *data, int rw); 452 void *buf, void **data, int rw);
441 453
442/* time macros */ 454/* time macros */
443#define __hfsp_mt2ut(t) (be32_to_cpu(t) - 2082844800U) 455#define __hfsp_mt2ut(t) (be32_to_cpu(t) - 2082844800U)
diff --git a/fs/hfsplus/part_tbl.c b/fs/hfsplus/part_tbl.c
index 40ad88c12c64..eb355d81e279 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,
88 return -ENOENT; 88 return -ENOENT;
89} 89}
90 90
91static int hfs_parse_new_pmap(struct super_block *sb, struct new_pmap *pm, 91static int hfs_parse_new_pmap(struct super_block *sb, void *buf,
92 sector_t *part_start, sector_t *part_size) 92 struct new_pmap *pm, sector_t *part_start, sector_t *part_size)
93{ 93{
94 struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); 94 struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
95 int size = be32_to_cpu(pm->pmMapBlkCnt); 95 int size = be32_to_cpu(pm->pmMapBlkCnt);
96 int buf_size = hfsplus_min_io_size(sb);
96 int res; 97 int res;
97 int i = 0; 98 int i = 0;
98 99
@@ -107,11 +108,14 @@ static int hfs_parse_new_pmap(struct super_block *sb, struct new_pmap *pm,
107 if (++i >= size) 108 if (++i >= size)
108 return -ENOENT; 109 return -ENOENT;
109 110
110 res = hfsplus_submit_bio(sb->s_bdev, 111 pm = (struct new_pmap *)((u8 *)pm + HFSPLUS_SECTOR_SIZE);
111 *part_start + HFS_PMAP_BLK + i, 112 if ((u8 *)pm - (u8 *)buf >= buf_size) {
112 pm, READ); 113 res = hfsplus_submit_bio(sb,
113 if (res) 114 *part_start + HFS_PMAP_BLK + i,
114 return res; 115 buf, (void **)&pm, READ);
116 if (res)
117 return res;
118 }
115 } while (pm->pmSig == cpu_to_be16(HFS_NEW_PMAP_MAGIC)); 119 } while (pm->pmSig == cpu_to_be16(HFS_NEW_PMAP_MAGIC));
116 120
117 return -ENOENT; 121 return -ENOENT;
@@ -124,15 +128,15 @@ static int hfs_parse_new_pmap(struct super_block *sb, struct new_pmap *pm,
124int hfs_part_find(struct super_block *sb, 128int hfs_part_find(struct super_block *sb,
125 sector_t *part_start, sector_t *part_size) 129 sector_t *part_start, sector_t *part_size)
126{ 130{
127 void *data; 131 void *buf, *data;
128 int res; 132 int res;
129 133
130 data = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL); 134 buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL);
131 if (!data) 135 if (!buf)
132 return -ENOMEM; 136 return -ENOMEM;
133 137
134 res = hfsplus_submit_bio(sb->s_bdev, *part_start + HFS_PMAP_BLK, 138 res = hfsplus_submit_bio(sb, *part_start + HFS_PMAP_BLK,
135 data, READ); 139 buf, &data, READ);
136 if (res) 140 if (res)
137 goto out; 141 goto out;
138 142
@@ -141,13 +145,13 @@ int hfs_part_find(struct super_block *sb,
141 res = hfs_parse_old_pmap(sb, data, part_start, part_size); 145 res = hfs_parse_old_pmap(sb, data, part_start, part_size);
142 break; 146 break;
143 case HFS_NEW_PMAP_MAGIC: 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 break; 149 break;
146 default: 150 default:
147 res = -ENOENT; 151 res = -ENOENT;
148 break; 152 break;
149 } 153 }
150out: 154out:
151 kfree(data); 155 kfree(buf);
152 return res; 156 return res;
153} 157}
diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c
index 84f56e1f6dac..c106ca22e812 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)
203 write_backup = 1; 203 write_backup = 1;
204 } 204 }
205 205
206 error2 = hfsplus_submit_bio(sb->s_bdev, 206 error2 = hfsplus_submit_bio(sb,
207 sbi->part_start + HFSPLUS_VOLHEAD_SECTOR, 207 sbi->part_start + HFSPLUS_VOLHEAD_SECTOR,
208 sbi->s_vhdr, WRITE_SYNC); 208 sbi->s_vhdr_buf, NULL, WRITE_SYNC);
209 if (!error) 209 if (!error)
210 error = error2; 210 error = error2;
211 if (!write_backup) 211 if (!write_backup)
212 goto out; 212 goto out;
213 213
214 error2 = hfsplus_submit_bio(sb->s_bdev, 214 error2 = hfsplus_submit_bio(sb,
215 sbi->part_start + sbi->sect_count - 2, 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 if (!error) 217 if (!error)
218 error2 = error; 218 error2 = error;
219out: 219out:
@@ -257,8 +257,8 @@ static void hfsplus_put_super(struct super_block *sb)
257 hfs_btree_close(sbi->ext_tree); 257 hfs_btree_close(sbi->ext_tree);
258 iput(sbi->alloc_file); 258 iput(sbi->alloc_file);
259 iput(sbi->hidden_dir); 259 iput(sbi->hidden_dir);
260 kfree(sbi->s_vhdr); 260 kfree(sbi->s_vhdr_buf);
261 kfree(sbi->s_backup_vhdr); 261 kfree(sbi->s_backup_vhdr_buf);
262 unload_nls(sbi->nls); 262 unload_nls(sbi->nls);
263 kfree(sb->s_fs_info); 263 kfree(sb->s_fs_info);
264 sb->s_fs_info = NULL; 264 sb->s_fs_info = NULL;
diff --git a/fs/hfsplus/wrapper.c b/fs/hfsplus/wrapper.c
index 2f933e83f5c5..10e515a0d452 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)
31 complete(bio->bi_private); 31 complete(bio->bi_private);
32} 32}
33 33
34int hfsplus_submit_bio(struct block_device *bdev, sector_t sector, 34/*
35 void *data, int rw) 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 */
53int hfsplus_submit_bio(struct super_block *sb, sector_t sector,
54 void *buf, void **data, int rw)
36{ 55{
37 DECLARE_COMPLETION_ONSTACK(wait); 56 DECLARE_COMPLETION_ONSTACK(wait);
38 struct bio *bio; 57 struct bio *bio;
39 int ret = 0; 58 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);
40 72
41 bio = bio_alloc(GFP_NOIO, 1); 73 bio = bio_alloc(GFP_NOIO, 1);
42 bio->bi_sector = sector; 74 bio->bi_sector = sector;
43 bio->bi_bdev = bdev; 75 bio->bi_bdev = sb->s_bdev;
44 bio->bi_end_io = hfsplus_end_io_sync; 76 bio->bi_end_io = hfsplus_end_io_sync;
45 bio->bi_private = &wait; 77 bio->bi_private = &wait;
46 78
47 /* 79 if (!(rw & WRITE) && data)
48 * We always submit one sector at a time, so bio_add_page must not fail. 80 *data = (u8 *)buf + offset;
49 */ 81
50 if (bio_add_page(bio, virt_to_page(data), HFSPLUS_SECTOR_SIZE, 82 while (io_size > 0) {
51 offset_in_page(data)) != HFSPLUS_SECTOR_SIZE) 83 unsigned int page_offset = offset_in_page(buf);
52 BUG(); 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 }
53 95
54 submit_bio(rw, bio); 96 submit_bio(rw, bio);
55 wait_for_completion(&wait); 97 wait_for_completion(&wait);
@@ -57,8 +99,9 @@ int hfsplus_submit_bio(struct block_device *bdev, sector_t sector,
57 if (!bio_flagged(bio, BIO_UPTODATE)) 99 if (!bio_flagged(bio, BIO_UPTODATE))
58 ret = -EIO; 100 ret = -EIO;
59 101
102out:
60 bio_put(bio); 103 bio_put(bio);
61 return ret; 104 return ret < 0 ? ret : 0;
62} 105}
63 106
64static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd) 107static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd)
@@ -143,17 +186,17 @@ int hfsplus_read_wrapper(struct super_block *sb)
143 goto out; 186 goto out;
144 187
145 error = -ENOMEM; 188 error = -ENOMEM;
146 sbi->s_vhdr = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL); 189 sbi->s_vhdr_buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL);
147 if (!sbi->s_vhdr) 190 if (!sbi->s_vhdr_buf)
148 goto out; 191 goto out;
149 sbi->s_backup_vhdr = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL); 192 sbi->s_backup_vhdr_buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL);
150 if (!sbi->s_backup_vhdr) 193 if (!sbi->s_backup_vhdr_buf)
151 goto out_free_vhdr; 194 goto out_free_vhdr;
152 195
153reread: 196reread:
154 error = hfsplus_submit_bio(sb->s_bdev, 197 error = hfsplus_submit_bio(sb, part_start + HFSPLUS_VOLHEAD_SECTOR,
155 part_start + HFSPLUS_VOLHEAD_SECTOR, 198 sbi->s_vhdr_buf, (void **)&sbi->s_vhdr,
156 sbi->s_vhdr, READ); 199 READ);
157 if (error) 200 if (error)
158 goto out_free_backup_vhdr; 201 goto out_free_backup_vhdr;
159 202
@@ -183,9 +226,9 @@ reread:
183 goto reread; 226 goto reread;
184 } 227 }
185 228
186 error = hfsplus_submit_bio(sb->s_bdev, 229 error = hfsplus_submit_bio(sb, part_start + part_size - 2,
187 part_start + part_size - 2, 230 sbi->s_backup_vhdr_buf,
188 sbi->s_backup_vhdr, READ); 231 (void **)&sbi->s_backup_vhdr, READ);
189 if (error) 232 if (error)
190 goto out_free_backup_vhdr; 233 goto out_free_backup_vhdr;
191 234