aboutsummaryrefslogtreecommitdiffstats
path: root/fs/hfsplus/wrapper.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/hfsplus/wrapper.c')
-rw-r--r--fs/hfsplus/wrapper.c163
1 files changed, 104 insertions, 59 deletions
diff --git a/fs/hfsplus/wrapper.c b/fs/hfsplus/wrapper.c
index 8972c20b3216..15e0eabb489e 100644
--- a/fs/hfsplus/wrapper.c
+++ b/fs/hfsplus/wrapper.c
@@ -24,6 +24,40 @@ struct hfsplus_wd {
24 u16 embed_count; 24 u16 embed_count;
25}; 25};
26 26
27static void hfsplus_end_io_sync(struct bio *bio, int err)
28{
29 if (err)
30 clear_bit(BIO_UPTODATE, &bio->bi_flags);
31 complete(bio->bi_private);
32}
33
34int hfsplus_submit_bio(struct block_device *bdev, sector_t sector,
35 void *data, int rw)
36{
37 DECLARE_COMPLETION_ONSTACK(wait);
38 struct bio *bio;
39
40 bio = bio_alloc(GFP_NOIO, 1);
41 bio->bi_sector = sector;
42 bio->bi_bdev = bdev;
43 bio->bi_end_io = hfsplus_end_io_sync;
44 bio->bi_private = &wait;
45
46 /*
47 * We always submit one sector at a time, so bio_add_page must not fail.
48 */
49 if (bio_add_page(bio, virt_to_page(data), HFSPLUS_SECTOR_SIZE,
50 offset_in_page(data)) != HFSPLUS_SECTOR_SIZE)
51 BUG();
52
53 submit_bio(rw, bio);
54 wait_for_completion(&wait);
55
56 if (!bio_flagged(bio, BIO_UPTODATE))
57 return -EIO;
58 return 0;
59}
60
27static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd) 61static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd)
28{ 62{
29 u32 extent; 63 u32 extent;
@@ -88,100 +122,111 @@ static int hfsplus_get_last_session(struct super_block *sb,
88int hfsplus_read_wrapper(struct super_block *sb) 122int hfsplus_read_wrapper(struct super_block *sb)
89{ 123{
90 struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); 124 struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
91 struct buffer_head *bh;
92 struct hfsplus_vh *vhdr;
93 struct hfsplus_wd wd; 125 struct hfsplus_wd wd;
94 sector_t part_start, part_size; 126 sector_t part_start, part_size;
95 u32 blocksize; 127 u32 blocksize;
128 int error = 0;
96 129
130 error = -EINVAL;
97 blocksize = sb_min_blocksize(sb, HFSPLUS_SECTOR_SIZE); 131 blocksize = sb_min_blocksize(sb, HFSPLUS_SECTOR_SIZE);
98 if (!blocksize) 132 if (!blocksize)
99 return -EINVAL; 133 goto out;
100 134
101 if (hfsplus_get_last_session(sb, &part_start, &part_size)) 135 if (hfsplus_get_last_session(sb, &part_start, &part_size))
102 return -EINVAL; 136 goto out;
103 if ((u64)part_start + part_size > 0x100000000ULL) { 137 if ((u64)part_start + part_size > 0x100000000ULL) {
104 pr_err("hfs: volumes larger than 2TB are not supported yet\n"); 138 pr_err("hfs: volumes larger than 2TB are not supported yet\n");
105 return -EINVAL; 139 goto out;
106 } 140 }
107 while (1) {
108 bh = sb_bread512(sb, part_start + HFSPLUS_VOLHEAD_SECTOR, vhdr);
109 if (!bh)
110 return -EIO;
111
112 if (vhdr->signature == cpu_to_be16(HFSP_WRAP_MAGIC)) {
113 if (!hfsplus_read_mdb(vhdr, &wd))
114 goto error;
115 wd.ablk_size >>= HFSPLUS_SECTOR_SHIFT;
116 part_start += wd.ablk_start + wd.embed_start * wd.ablk_size;
117 part_size = wd.embed_count * wd.ablk_size;
118 brelse(bh);
119 bh = sb_bread512(sb, part_start + HFSPLUS_VOLHEAD_SECTOR, vhdr);
120 if (!bh)
121 return -EIO;
122 }
123 if (vhdr->signature == cpu_to_be16(HFSPLUS_VOLHEAD_SIG))
124 break;
125 if (vhdr->signature == cpu_to_be16(HFSPLUS_VOLHEAD_SIGX)) {
126 set_bit(HFSPLUS_SB_HFSX, &sbi->flags);
127 break;
128 }
129 brelse(bh);
130 141
131 /* check for a partition block 142 error = -ENOMEM;
143 sbi->s_vhdr = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL);
144 if (!sbi->s_vhdr)
145 goto out;
146 sbi->s_backup_vhdr = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL);
147 if (!sbi->s_backup_vhdr)
148 goto out_free_vhdr;
149
150reread:
151 error = hfsplus_submit_bio(sb->s_bdev,
152 part_start + HFSPLUS_VOLHEAD_SECTOR,
153 sbi->s_vhdr, READ);
154 if (error)
155 goto out_free_backup_vhdr;
156
157 error = -EINVAL;
158 switch (sbi->s_vhdr->signature) {
159 case cpu_to_be16(HFSPLUS_VOLHEAD_SIGX):
160 set_bit(HFSPLUS_SB_HFSX, &sbi->flags);
161 /*FALLTHRU*/
162 case cpu_to_be16(HFSPLUS_VOLHEAD_SIG):
163 break;
164 case cpu_to_be16(HFSP_WRAP_MAGIC):
165 if (!hfsplus_read_mdb(sbi->s_vhdr, &wd))
166 goto out;
167 wd.ablk_size >>= HFSPLUS_SECTOR_SHIFT;
168 part_start += wd.ablk_start + wd.embed_start * wd.ablk_size;
169 part_size = wd.embed_count * wd.ablk_size;
170 goto reread;
171 default:
172 /*
173 * Check for a partition block.
174 *
132 * (should do this only for cdrom/loop though) 175 * (should do this only for cdrom/loop though)
133 */ 176 */
134 if (hfs_part_find(sb, &part_start, &part_size)) 177 if (hfs_part_find(sb, &part_start, &part_size))
135 return -EINVAL; 178 goto out;
179 goto reread;
180 }
181
182 error = hfsplus_submit_bio(sb->s_bdev,
183 part_start + part_size - 2,
184 sbi->s_backup_vhdr, READ);
185 if (error)
186 goto out_free_backup_vhdr;
187
188 error = -EINVAL;
189 if (sbi->s_backup_vhdr->signature != sbi->s_vhdr->signature) {
190 printk(KERN_WARNING
191 "hfs: invalid secondary volume header\n");
192 goto out_free_backup_vhdr;
136 } 193 }
137 194
138 blocksize = be32_to_cpu(vhdr->blocksize); 195 blocksize = be32_to_cpu(sbi->s_vhdr->blocksize);
139 brelse(bh);
140 196
141 /* block size must be at least as large as a sector 197 /*
142 * and a multiple of 2 198 * Block size must be at least as large as a sector and a multiple of 2.
143 */ 199 */
144 if (blocksize < HFSPLUS_SECTOR_SIZE || 200 if (blocksize < HFSPLUS_SECTOR_SIZE || ((blocksize - 1) & blocksize))
145 ((blocksize - 1) & blocksize)) 201 goto out_free_backup_vhdr;
146 return -EINVAL;
147 sbi->alloc_blksz = blocksize; 202 sbi->alloc_blksz = blocksize;
148 sbi->alloc_blksz_shift = 0; 203 sbi->alloc_blksz_shift = 0;
149 while ((blocksize >>= 1) != 0) 204 while ((blocksize >>= 1) != 0)
150 sbi->alloc_blksz_shift++; 205 sbi->alloc_blksz_shift++;
151 blocksize = min(sbi->alloc_blksz, (u32)PAGE_SIZE); 206 blocksize = min(sbi->alloc_blksz, (u32)PAGE_SIZE);
152 207
153 /* align block size to block offset */ 208 /*
209 * Align block size to block offset.
210 */
154 while (part_start & ((blocksize >> HFSPLUS_SECTOR_SHIFT) - 1)) 211 while (part_start & ((blocksize >> HFSPLUS_SECTOR_SHIFT) - 1))
155 blocksize >>= 1; 212 blocksize >>= 1;
156 213
157 if (sb_set_blocksize(sb, blocksize) != blocksize) { 214 if (sb_set_blocksize(sb, blocksize) != blocksize) {
158 printk(KERN_ERR "hfs: unable to set blocksize to %u!\n", blocksize); 215 printk(KERN_ERR "hfs: unable to set blocksize to %u!\n", blocksize);
159 return -EINVAL; 216 goto out_free_backup_vhdr;
160 } 217 }
161 218
162 sbi->blockoffset = 219 sbi->blockoffset =
163 part_start >> (sb->s_blocksize_bits - HFSPLUS_SECTOR_SHIFT); 220 part_start >> (sb->s_blocksize_bits - HFSPLUS_SECTOR_SHIFT);
221 sbi->part_start = part_start;
164 sbi->sect_count = part_size; 222 sbi->sect_count = part_size;
165 sbi->fs_shift = sbi->alloc_blksz_shift - sb->s_blocksize_bits; 223 sbi->fs_shift = sbi->alloc_blksz_shift - sb->s_blocksize_bits;
166
167 bh = sb_bread512(sb, part_start + HFSPLUS_VOLHEAD_SECTOR, vhdr);
168 if (!bh)
169 return -EIO;
170
171 /* should still be the same... */
172 if (test_bit(HFSPLUS_SB_HFSX, &sbi->flags)) {
173 if (vhdr->signature != cpu_to_be16(HFSPLUS_VOLHEAD_SIGX))
174 goto error;
175 } else {
176 if (vhdr->signature != cpu_to_be16(HFSPLUS_VOLHEAD_SIG))
177 goto error;
178 }
179
180 sbi->s_vhbh = bh;
181 sbi->s_vhdr = vhdr;
182
183 return 0; 224 return 0;
184 error: 225
185 brelse(bh); 226out_free_backup_vhdr:
186 return -EINVAL; 227 kfree(sbi->s_backup_vhdr);
228out_free_vhdr:
229 kfree(sbi->s_vhdr);
230out:
231 return error;
187} 232}