diff options
Diffstat (limited to 'fs/hfsplus/wrapper.c')
-rw-r--r-- | fs/hfsplus/wrapper.c | 83 |
1 files changed, 63 insertions, 20 deletions
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 | ||
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) |
@@ -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 | ||
153 | reread: | 196 | reread: |
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 | ||