1 // SPDX-License-Identifier: GPL-2.0
3 * linux/fs/hfsplus/wrapper.c
6 * Brad Boyer (flar@allandria.com)
7 * (C) 2003 Ardis Technologies <roman@ardistech.com>
9 * Handling of HFS wrappers around HFS+ volumes
13 #include <linux/blkdev.h>
14 #include <linux/cdrom.h>
15 #include <linux/genhd.h>
16 #include <asm/unaligned.h>
18 #include "hfsplus_fs.h"
19 #include "hfsplus_raw.h"
29 * hfsplus_submit_bio - Perform block I/O
30 * @sb: super block of volume for I/O
31 * @sector: block to read or write, for blocks of HFSPLUS_SECTOR_SIZE bytes
32 * @buf: buffer for I/O
33 * @data: output pointer for location of requested data
34 * @op: direction of I/O
35 * @op_flags: request op flags
37 * The unit of I/O is hfsplus_min_io_size(sb), which may be bigger than
38 * HFSPLUS_SECTOR_SIZE, and @buf must be sized accordingly. On reads
39 * @data will return a pointer to the start of the requested sector,
40 * which may not be the same location as @buf.
42 * If @sector is not aligned to the bdev logical block size it will
43 * be rounded down. For writes this means that @buf should contain data
44 * that starts at the rounded-down address. As long as the data was
45 * read using hfsplus_submit_bio() and the same buffer is used things
46 * will work correctly.
48 int hfsplus_submit_bio(struct super_block
*sb
, sector_t sector
,
49 void *buf
, void **data
, int op
, int op_flags
)
58 * Align sector to hardware sector size and find offset. We
59 * assume that io_size is a power of two, which _should_
62 io_size
= hfsplus_min_io_size(sb
);
63 start
= (loff_t
)sector
<< HFSPLUS_SECTOR_SHIFT
;
64 offset
= start
& (io_size
- 1);
65 sector
&= ~((io_size
>> HFSPLUS_SECTOR_SHIFT
) - 1);
67 bio
= bio_alloc(GFP_NOIO
, 1);
68 bio
->bi_iter
.bi_sector
= sector
;
69 bio_set_dev(bio
, sb
->s_bdev
);
70 bio_set_op_attrs(bio
, op
, op_flags
);
72 if (op
!= WRITE
&& data
)
73 *data
= (u8
*)buf
+ offset
;
76 unsigned int page_offset
= offset_in_page(buf
);
77 unsigned int len
= min_t(unsigned int, PAGE_SIZE
- page_offset
,
80 ret
= bio_add_page(bio
, virt_to_page(buf
), len
, page_offset
);
86 buf
= (u8
*)buf
+ len
;
89 ret
= submit_bio_wait(bio
);
92 return ret
< 0 ? ret
: 0;
95 static int hfsplus_read_mdb(void *bufptr
, struct hfsplus_wd
*wd
)
101 sig
= *(__be16
*)(bufptr
+ HFSP_WRAPOFF_EMBEDSIG
);
102 if (sig
!= cpu_to_be16(HFSPLUS_VOLHEAD_SIG
) &&
103 sig
!= cpu_to_be16(HFSPLUS_VOLHEAD_SIGX
))
106 attrib
= be16_to_cpu(*(__be16
*)(bufptr
+ HFSP_WRAPOFF_ATTRIB
));
107 if (!(attrib
& HFSP_WRAP_ATTRIB_SLOCK
) ||
108 !(attrib
& HFSP_WRAP_ATTRIB_SPARED
))
112 be32_to_cpu(*(__be32
*)(bufptr
+ HFSP_WRAPOFF_ABLKSIZE
));
113 if (wd
->ablk_size
< HFSPLUS_SECTOR_SIZE
)
115 if (wd
->ablk_size
% HFSPLUS_SECTOR_SIZE
)
118 be16_to_cpu(*(__be16
*)(bufptr
+ HFSP_WRAPOFF_ABLKSTART
));
120 extent
= get_unaligned_be32(bufptr
+ HFSP_WRAPOFF_EMBEDEXT
);
121 wd
->embed_start
= (extent
>> 16) & 0xFFFF;
122 wd
->embed_count
= extent
& 0xFFFF;
127 static int hfsplus_get_last_session(struct super_block
*sb
,
128 sector_t
*start
, sector_t
*size
)
130 struct cdrom_multisession ms_info
;
131 struct cdrom_tocentry te
;
136 *size
= i_size_read(sb
->s_bdev
->bd_inode
) >> 9;
138 if (HFSPLUS_SB(sb
)->session
>= 0) {
139 te
.cdte_track
= HFSPLUS_SB(sb
)->session
;
140 te
.cdte_format
= CDROM_LBA
;
141 res
= ioctl_by_bdev(sb
->s_bdev
,
142 CDROMREADTOCENTRY
, (unsigned long)&te
);
143 if (!res
&& (te
.cdte_ctrl
& CDROM_DATA_TRACK
) == 4) {
144 *start
= (sector_t
)te
.cdte_addr
.lba
<< 2;
147 pr_err("invalid session number or type of track\n");
150 ms_info
.addr_format
= CDROM_LBA
;
151 res
= ioctl_by_bdev(sb
->s_bdev
, CDROMMULTISESSION
,
152 (unsigned long)&ms_info
);
153 if (!res
&& ms_info
.xa_flag
)
154 *start
= (sector_t
)ms_info
.addr
.lba
<< 2;
158 /* Find the volume header and fill in some minimum bits in superblock */
159 /* Takes in super block, returns true if good data read */
160 int hfsplus_read_wrapper(struct super_block
*sb
)
162 struct hfsplus_sb_info
*sbi
= HFSPLUS_SB(sb
);
163 struct hfsplus_wd wd
;
164 sector_t part_start
, part_size
;
169 blocksize
= sb_min_blocksize(sb
, HFSPLUS_SECTOR_SIZE
);
173 if (hfsplus_get_last_session(sb
, &part_start
, &part_size
))
177 sbi
->s_vhdr_buf
= kmalloc(hfsplus_min_io_size(sb
), GFP_KERNEL
);
178 if (!sbi
->s_vhdr_buf
)
180 sbi
->s_backup_vhdr_buf
= kmalloc(hfsplus_min_io_size(sb
), GFP_KERNEL
);
181 if (!sbi
->s_backup_vhdr_buf
)
185 error
= hfsplus_submit_bio(sb
, part_start
+ HFSPLUS_VOLHEAD_SECTOR
,
186 sbi
->s_vhdr_buf
, (void **)&sbi
->s_vhdr
,
189 goto out_free_backup_vhdr
;
192 switch (sbi
->s_vhdr
->signature
) {
193 case cpu_to_be16(HFSPLUS_VOLHEAD_SIGX
):
194 set_bit(HFSPLUS_SB_HFSX
, &sbi
->flags
);
196 case cpu_to_be16(HFSPLUS_VOLHEAD_SIG
):
198 case cpu_to_be16(HFSP_WRAP_MAGIC
):
199 if (!hfsplus_read_mdb(sbi
->s_vhdr
, &wd
))
200 goto out_free_backup_vhdr
;
201 wd
.ablk_size
>>= HFSPLUS_SECTOR_SHIFT
;
202 part_start
+= (sector_t
)wd
.ablk_start
+
203 (sector_t
)wd
.embed_start
* wd
.ablk_size
;
204 part_size
= (sector_t
)wd
.embed_count
* wd
.ablk_size
;
208 * Check for a partition block.
210 * (should do this only for cdrom/loop though)
212 if (hfs_part_find(sb
, &part_start
, &part_size
))
213 goto out_free_backup_vhdr
;
217 error
= hfsplus_submit_bio(sb
, part_start
+ part_size
- 2,
218 sbi
->s_backup_vhdr_buf
,
219 (void **)&sbi
->s_backup_vhdr
, REQ_OP_READ
,
222 goto out_free_backup_vhdr
;
225 if (sbi
->s_backup_vhdr
->signature
!= sbi
->s_vhdr
->signature
) {
226 pr_warn("invalid secondary volume header\n");
227 goto out_free_backup_vhdr
;
230 blocksize
= be32_to_cpu(sbi
->s_vhdr
->blocksize
);
233 * Block size must be at least as large as a sector and a multiple of 2.
235 if (blocksize
< HFSPLUS_SECTOR_SIZE
|| ((blocksize
- 1) & blocksize
))
236 goto out_free_backup_vhdr
;
237 sbi
->alloc_blksz
= blocksize
;
238 sbi
->alloc_blksz_shift
= ilog2(blocksize
);
239 blocksize
= min_t(u32
, sbi
->alloc_blksz
, PAGE_SIZE
);
242 * Align block size to block offset.
244 while (part_start
& ((blocksize
>> HFSPLUS_SECTOR_SHIFT
) - 1))
247 if (sb_set_blocksize(sb
, blocksize
) != blocksize
) {
248 pr_err("unable to set blocksize to %u!\n", blocksize
);
249 goto out_free_backup_vhdr
;
253 part_start
>> (sb
->s_blocksize_bits
- HFSPLUS_SECTOR_SHIFT
);
254 sbi
->part_start
= part_start
;
255 sbi
->sect_count
= part_size
;
256 sbi
->fs_shift
= sbi
->alloc_blksz_shift
- sb
->s_blocksize_bits
;
259 out_free_backup_vhdr
:
260 kfree(sbi
->s_backup_vhdr_buf
);
262 kfree(sbi
->s_vhdr_buf
);