aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 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;
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 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
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 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;
213out: 213out:
@@ -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
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)
@@ -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
157reread: 200reread:
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