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 int hfsplus_read_mdb(void *bufptr
, struct hfsplus_wd
*wd
)
33 sig
= *(__be16
*)(bufptr
+ HFSP_WRAPOFF_EMBEDSIG
);
34 if (sig
!= cpu_to_be16(HFSPLUS_VOLHEAD_SIG
) &&
35 sig
!= cpu_to_be16(HFSPLUS_VOLHEAD_SIGX
))
38 attrib
= be16_to_cpu(*(__be16
*)(bufptr
+ HFSP_WRAPOFF_ATTRIB
));
39 if (!(attrib
& HFSP_WRAP_ATTRIB_SLOCK
) ||
40 !(attrib
& HFSP_WRAP_ATTRIB_SPARED
))
43 wd
->ablk_size
= be32_to_cpu(*(__be32
*)(bufptr
+ HFSP_WRAPOFF_ABLKSIZE
));
44 if (wd
->ablk_size
< HFSPLUS_SECTOR_SIZE
)
46 if (wd
->ablk_size
% HFSPLUS_SECTOR_SIZE
)
48 wd
->ablk_start
= be16_to_cpu(*(__be16
*)(bufptr
+ HFSP_WRAPOFF_ABLKSTART
));
50 extent
= be32_to_cpu(get_unaligned((__be32
*)(bufptr
+ HFSP_WRAPOFF_EMBEDEXT
)));
51 wd
->embed_start
= (extent
>> 16) & 0xFFFF;
52 wd
->embed_count
= extent
& 0xFFFF;
57 static int hfsplus_get_last_session(struct super_block
*sb
,
58 sector_t
*start
, sector_t
*size
)
60 struct cdrom_multisession ms_info
;
61 struct cdrom_tocentry te
;
66 *size
= sb
->s_bdev
->bd_inode
->i_size
>> 9;
68 if (HFSPLUS_SB(sb
).session
>= 0) {
69 te
.cdte_track
= HFSPLUS_SB(sb
).session
;
70 te
.cdte_format
= CDROM_LBA
;
71 res
= ioctl_by_bdev(sb
->s_bdev
, CDROMREADTOCENTRY
, (unsigned long)&te
);
72 if (!res
&& (te
.cdte_ctrl
& CDROM_DATA_TRACK
) == 4) {
73 *start
= (sector_t
)te
.cdte_addr
.lba
<< 2;
76 printk(KERN_ERR
"hfs: invalid session number or type of track\n");
79 ms_info
.addr_format
= CDROM_LBA
;
80 res
= ioctl_by_bdev(sb
->s_bdev
, CDROMMULTISESSION
, (unsigned long)&ms_info
);
81 if (!res
&& ms_info
.xa_flag
)
82 *start
= (sector_t
)ms_info
.addr
.lba
<< 2;
86 /* Find the volume header and fill in some minimum bits in superblock */
87 /* Takes in super block, returns true if good data read */
88 int hfsplus_read_wrapper(struct super_block
*sb
)
90 struct buffer_head
*bh
;
91 struct hfsplus_vh
*vhdr
;
93 sector_t part_start
, part_size
;
96 blocksize
= sb_min_blocksize(sb
, HFSPLUS_SECTOR_SIZE
);
100 if (hfsplus_get_last_session(sb
, &part_start
, &part_size
))
103 bh
= sb_bread512(sb
, part_start
+ HFSPLUS_VOLHEAD_SECTOR
, vhdr
);
107 if (vhdr
->signature
== cpu_to_be16(HFSP_WRAP_MAGIC
)) {
108 if (!hfsplus_read_mdb(vhdr
, &wd
))
110 wd
.ablk_size
>>= HFSPLUS_SECTOR_SHIFT
;
111 part_start
+= wd
.ablk_start
+ wd
.embed_start
* wd
.ablk_size
;
112 part_size
= wd
.embed_count
* wd
.ablk_size
;
114 bh
= sb_bread512(sb
, part_start
+ HFSPLUS_VOLHEAD_SECTOR
, vhdr
);
118 if (vhdr
->signature
== cpu_to_be16(HFSPLUS_VOLHEAD_SIG
))
120 if (vhdr
->signature
== cpu_to_be16(HFSPLUS_VOLHEAD_SIGX
)) {
121 HFSPLUS_SB(sb
).flags
|= HFSPLUS_SB_HFSX
;
126 /* check for a partition block
127 * (should do this only for cdrom/loop though)
129 if (hfs_part_find(sb
, &part_start
, &part_size
))
133 blocksize
= be32_to_cpu(vhdr
->blocksize
);
136 /* block size must be at least as large as a sector
137 * and a multiple of 2
139 if (blocksize
< HFSPLUS_SECTOR_SIZE
||
140 ((blocksize
- 1) & blocksize
))
142 HFSPLUS_SB(sb
).alloc_blksz
= blocksize
;
143 HFSPLUS_SB(sb
).alloc_blksz_shift
= 0;
144 while ((blocksize
>>= 1) != 0)
145 HFSPLUS_SB(sb
).alloc_blksz_shift
++;
146 blocksize
= min(HFSPLUS_SB(sb
).alloc_blksz
, (u32
)PAGE_SIZE
);
148 /* align block size to block offset */
149 while (part_start
& ((blocksize
>> HFSPLUS_SECTOR_SHIFT
) - 1))
152 if (sb_set_blocksize(sb
, blocksize
) != blocksize
) {
153 printk(KERN_ERR
"hfs: unable to set blocksize to %u!\n", blocksize
);
157 HFSPLUS_SB(sb
).blockoffset
= part_start
>>
158 (sb
->s_blocksize_bits
- HFSPLUS_SECTOR_SHIFT
);
159 HFSPLUS_SB(sb
).sect_count
= part_size
;
160 HFSPLUS_SB(sb
).fs_shift
= HFSPLUS_SB(sb
).alloc_blksz_shift
-
161 sb
->s_blocksize_bits
;
163 bh
= sb_bread512(sb
, part_start
+ HFSPLUS_VOLHEAD_SECTOR
, vhdr
);
167 /* should still be the same... */
168 if (vhdr
->signature
!= (HFSPLUS_SB(sb
).flags
& HFSPLUS_SB_HFSX
?
169 cpu_to_be16(HFSPLUS_VOLHEAD_SIGX
) :
170 cpu_to_be16(HFSPLUS_VOLHEAD_SIG
)))
172 HFSPLUS_SB(sb
).s_vhbh
= bh
;
173 HFSPLUS_SB(sb
).s_vhdr
= vhdr
;