2 * linux/fs/hfsplus/wrapper.c
5 * Brad Boyer (flar@allandria.com)
6 * (C) 2003 Ardis Technologies <roman@ardistech.com>
8 * Handling of HFS wrappers around HFS+ volumes
12 #include <linux/blkdev.h>
13 #include <linux/cdrom.h>
14 #include <linux/genhd.h>
15 #include <asm/unaligned.h>
17 #include "hfsplus_fs.h"
18 #include "hfsplus_raw.h"
27 static void hfsplus_end_io_sync(struct bio
*bio
, int err
)
30 clear_bit(BIO_UPTODATE
, &bio
->bi_flags
);
31 complete(bio
->bi_private
);
34 int hfsplus_submit_bio(struct block_device
*bdev
, sector_t sector
,
37 DECLARE_COMPLETION_ONSTACK(wait
);
40 bio
= bio_alloc(GFP_NOIO
, 1);
41 bio
->bi_sector
= sector
;
43 bio
->bi_end_io
= hfsplus_end_io_sync
;
44 bio
->bi_private
= &wait
;
47 * We always submit one sector at a time, so bio_add_page must not fail.
49 if (bio_add_page(bio
, virt_to_page(data
), HFSPLUS_SECTOR_SIZE
,
50 offset_in_page(data
)) != HFSPLUS_SECTOR_SIZE
)
54 wait_for_completion(&wait
);
56 if (!bio_flagged(bio
, BIO_UPTODATE
))
61 static int hfsplus_read_mdb(void *bufptr
, struct hfsplus_wd
*wd
)
67 sig
= *(__be16
*)(bufptr
+ HFSP_WRAPOFF_EMBEDSIG
);
68 if (sig
!= cpu_to_be16(HFSPLUS_VOLHEAD_SIG
) &&
69 sig
!= cpu_to_be16(HFSPLUS_VOLHEAD_SIGX
))
72 attrib
= be16_to_cpu(*(__be16
*)(bufptr
+ HFSP_WRAPOFF_ATTRIB
));
73 if (!(attrib
& HFSP_WRAP_ATTRIB_SLOCK
) ||
74 !(attrib
& HFSP_WRAP_ATTRIB_SPARED
))
78 be32_to_cpu(*(__be32
*)(bufptr
+ HFSP_WRAPOFF_ABLKSIZE
));
79 if (wd
->ablk_size
< HFSPLUS_SECTOR_SIZE
)
81 if (wd
->ablk_size
% HFSPLUS_SECTOR_SIZE
)
84 be16_to_cpu(*(__be16
*)(bufptr
+ HFSP_WRAPOFF_ABLKSTART
));
86 extent
= get_unaligned_be32(bufptr
+ HFSP_WRAPOFF_EMBEDEXT
);
87 wd
->embed_start
= (extent
>> 16) & 0xFFFF;
88 wd
->embed_count
= extent
& 0xFFFF;
93 static int hfsplus_get_last_session(struct super_block
*sb
,
94 sector_t
*start
, sector_t
*size
)
96 struct cdrom_multisession ms_info
;
97 struct cdrom_tocentry te
;
102 *size
= sb
->s_bdev
->bd_inode
->i_size
>> 9;
104 if (HFSPLUS_SB(sb
)->session
>= 0) {
105 te
.cdte_track
= HFSPLUS_SB(sb
)->session
;
106 te
.cdte_format
= CDROM_LBA
;
107 res
= ioctl_by_bdev(sb
->s_bdev
,
108 CDROMREADTOCENTRY
, (unsigned long)&te
);
109 if (!res
&& (te
.cdte_ctrl
& CDROM_DATA_TRACK
) == 4) {
110 *start
= (sector_t
)te
.cdte_addr
.lba
<< 2;
113 printk(KERN_ERR
"hfs: invalid session number or type of track\n");
116 ms_info
.addr_format
= CDROM_LBA
;
117 res
= ioctl_by_bdev(sb
->s_bdev
, CDROMMULTISESSION
,
118 (unsigned long)&ms_info
);
119 if (!res
&& ms_info
.xa_flag
)
120 *start
= (sector_t
)ms_info
.addr
.lba
<< 2;
124 /* Find the volume header and fill in some minimum bits in superblock */
125 /* Takes in super block, returns true if good data read */
126 int hfsplus_read_wrapper(struct super_block
*sb
)
128 struct hfsplus_sb_info
*sbi
= HFSPLUS_SB(sb
);
129 struct hfsplus_wd wd
;
130 sector_t part_start
, part_size
;
135 blocksize
= sb_min_blocksize(sb
, HFSPLUS_SECTOR_SIZE
);
139 if (hfsplus_get_last_session(sb
, &part_start
, &part_size
))
141 if ((u64
)part_start
+ part_size
> 0x100000000ULL
) {
142 pr_err("hfs: volumes larger than 2TB are not supported yet\n");
147 sbi
->s_vhdr
= kmalloc(HFSPLUS_SECTOR_SIZE
, GFP_KERNEL
);
150 sbi
->s_backup_vhdr
= kmalloc(HFSPLUS_SECTOR_SIZE
, GFP_KERNEL
);
151 if (!sbi
->s_backup_vhdr
)
155 error
= hfsplus_submit_bio(sb
->s_bdev
,
156 part_start
+ HFSPLUS_VOLHEAD_SECTOR
,
159 goto out_free_backup_vhdr
;
162 switch (sbi
->s_vhdr
->signature
) {
163 case cpu_to_be16(HFSPLUS_VOLHEAD_SIGX
):
164 set_bit(HFSPLUS_SB_HFSX
, &sbi
->flags
);
166 case cpu_to_be16(HFSPLUS_VOLHEAD_SIG
):
168 case cpu_to_be16(HFSP_WRAP_MAGIC
):
169 if (!hfsplus_read_mdb(sbi
->s_vhdr
, &wd
))
171 wd
.ablk_size
>>= HFSPLUS_SECTOR_SHIFT
;
172 part_start
+= wd
.ablk_start
+ wd
.embed_start
* wd
.ablk_size
;
173 part_size
= wd
.embed_count
* wd
.ablk_size
;
177 * Check for a partition block.
179 * (should do this only for cdrom/loop though)
181 if (hfs_part_find(sb
, &part_start
, &part_size
))
186 error
= hfsplus_submit_bio(sb
->s_bdev
,
187 part_start
+ part_size
- 2,
188 sbi
->s_backup_vhdr
, READ
);
190 goto out_free_backup_vhdr
;
193 if (sbi
->s_backup_vhdr
->signature
!= sbi
->s_vhdr
->signature
) {
195 "hfs: invalid secondary volume header\n");
196 goto out_free_backup_vhdr
;
199 blocksize
= be32_to_cpu(sbi
->s_vhdr
->blocksize
);
202 * Block size must be at least as large as a sector and a multiple of 2.
204 if (blocksize
< HFSPLUS_SECTOR_SIZE
|| ((blocksize
- 1) & blocksize
))
205 goto out_free_backup_vhdr
;
206 sbi
->alloc_blksz
= blocksize
;
207 sbi
->alloc_blksz_shift
= 0;
208 while ((blocksize
>>= 1) != 0)
209 sbi
->alloc_blksz_shift
++;
210 blocksize
= min(sbi
->alloc_blksz
, (u32
)PAGE_SIZE
);
213 * Align block size to block offset.
215 while (part_start
& ((blocksize
>> HFSPLUS_SECTOR_SHIFT
) - 1))
218 if (sb_set_blocksize(sb
, blocksize
) != blocksize
) {
219 printk(KERN_ERR
"hfs: unable to set blocksize to %u!\n",
221 goto out_free_backup_vhdr
;
225 part_start
>> (sb
->s_blocksize_bits
- HFSPLUS_SECTOR_SHIFT
);
226 sbi
->part_start
= part_start
;
227 sbi
->sect_count
= part_size
;
228 sbi
->fs_shift
= sbi
->alloc_blksz_shift
- sb
->s_blocksize_bits
;
231 out_free_backup_vhdr
:
232 kfree(sbi
->s_backup_vhdr
);