diff options
-rw-r--r-- | fs/hfsplus/hfsplus_fs.h | 16 | ||||
-rw-r--r-- | fs/hfsplus/part_tbl.c | 32 | ||||
-rw-r--r-- | fs/hfsplus/super.c | 12 | ||||
-rw-r--r-- | 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 d6857523336..4e7f64b705d 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; | |||
110 | struct hfs_btree; | 111 | struct hfs_btree; |
111 | 112 | ||
112 | struct hfsplus_sb_info { | 113 | struct 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 | */ | ||
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 | #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 */ |
437 | int hfsplus_read_wrapper(struct super_block *); | 449 | int hfsplus_read_wrapper(struct super_block *); |
438 | int hfs_part_find(struct super_block *, sector_t *, sector_t *); | 450 | int hfs_part_find(struct super_block *, sector_t *, sector_t *); |
439 | int hfsplus_submit_bio(struct block_device *bdev, sector_t sector, | 451 | int 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 40ad88c12c6..eb355d81e27 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 | ||
91 | static int hfs_parse_new_pmap(struct super_block *sb, struct new_pmap *pm, | 91 | static 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, | |||
124 | int hfs_part_find(struct super_block *sb, | 128 | int 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 | } |
150 | out: | 154 | out: |
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 84a47b709f5..ab4857b81af 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c | |||
@@ -197,17 +197,17 @@ int hfsplus_sync_fs(struct super_block *sb, int wait) | |||
197 | write_backup = 1; | 197 | write_backup = 1; |
198 | } | 198 | } |
199 | 199 | ||
200 | error2 = hfsplus_submit_bio(sb->s_bdev, | 200 | error2 = hfsplus_submit_bio(sb, |
201 | sbi->part_start + HFSPLUS_VOLHEAD_SECTOR, | 201 | sbi->part_start + HFSPLUS_VOLHEAD_SECTOR, |
202 | sbi->s_vhdr, WRITE_SYNC); | 202 | sbi->s_vhdr_buf, NULL, WRITE_SYNC); |
203 | if (!error) | 203 | if (!error) |
204 | error = error2; | 204 | error = error2; |
205 | if (!write_backup) | 205 | if (!write_backup) |
206 | goto out; | 206 | goto out; |
207 | 207 | ||
208 | error2 = hfsplus_submit_bio(sb->s_bdev, | 208 | error2 = hfsplus_submit_bio(sb, |
209 | sbi->part_start + sbi->sect_count - 2, | 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 | if (!error) | 211 | if (!error) |
212 | error2 = error; | 212 | error2 = error; |
213 | out: | 213 | out: |
@@ -251,8 +251,8 @@ static void hfsplus_put_super(struct super_block *sb) | |||
251 | hfs_btree_close(sbi->ext_tree); | 251 | hfs_btree_close(sbi->ext_tree); |
252 | iput(sbi->alloc_file); | 252 | iput(sbi->alloc_file); |
253 | iput(sbi->hidden_dir); | 253 | iput(sbi->hidden_dir); |
254 | kfree(sbi->s_vhdr); | 254 | kfree(sbi->s_vhdr_buf); |
255 | kfree(sbi->s_backup_vhdr); | 255 | kfree(sbi->s_backup_vhdr_buf); |
256 | unload_nls(sbi->nls); | 256 | unload_nls(sbi->nls); |
257 | kfree(sb->s_fs_info); | 257 | kfree(sb->s_fs_info); |
258 | sb->s_fs_info = NULL; | 258 | sb->s_fs_info = NULL; |
diff --git a/fs/hfsplus/wrapper.c b/fs/hfsplus/wrapper.c index 4ac88ff79aa..e3881a17a5a 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 | ||
34 | int 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 | */ | ||
53 | int 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 | ||
102 | out: | ||
60 | bio_put(bio); | 103 | bio_put(bio); |
61 | return ret; | 104 | return ret < 0 ? ret : 0; |
62 | } | 105 | } |
63 | 106 | ||
64 | static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd) | 107 | static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd) |
@@ -147,17 +190,17 @@ int hfsplus_read_wrapper(struct super_block *sb) | |||
147 | } | 190 | } |
148 | 191 | ||
149 | error = -ENOMEM; | 192 | error = -ENOMEM; |
150 | sbi->s_vhdr = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL); | 193 | sbi->s_vhdr_buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL); |
151 | if (!sbi->s_vhdr) | 194 | if (!sbi->s_vhdr_buf) |
152 | goto out; | 195 | goto out; |
153 | sbi->s_backup_vhdr = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL); | 196 | sbi->s_backup_vhdr_buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL); |
154 | if (!sbi->s_backup_vhdr) | 197 | if (!sbi->s_backup_vhdr_buf) |
155 | goto out_free_vhdr; | 198 | goto out_free_vhdr; |
156 | 199 | ||
157 | reread: | 200 | reread: |
158 | error = hfsplus_submit_bio(sb->s_bdev, | 201 | error = hfsplus_submit_bio(sb, part_start + HFSPLUS_VOLHEAD_SECTOR, |
159 | part_start + HFSPLUS_VOLHEAD_SECTOR, | 202 | sbi->s_vhdr_buf, (void **)&sbi->s_vhdr, |
160 | sbi->s_vhdr, READ); | 203 | READ); |
161 | if (error) | 204 | if (error) |
162 | goto out_free_backup_vhdr; | 205 | goto out_free_backup_vhdr; |
163 | 206 | ||
@@ -186,9 +229,9 @@ reread: | |||
186 | goto reread; | 229 | goto reread; |
187 | } | 230 | } |
188 | 231 | ||
189 | error = hfsplus_submit_bio(sb->s_bdev, | 232 | error = hfsplus_submit_bio(sb, part_start + part_size - 2, |
190 | part_start + part_size - 2, | 233 | sbi->s_backup_vhdr_buf, |
191 | sbi->s_backup_vhdr, READ); | 234 | (void **)&sbi->s_backup_vhdr, READ); |
192 | if (error) | 235 | if (error) |
193 | goto out_free_backup_vhdr; | 236 | goto out_free_backup_vhdr; |
194 | 237 | ||