0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #include <linux/fs.h>
0013 #include <linux/blkdev.h>
0014 #include <linux/cdrom.h>
0015 #include <asm/unaligned.h>
0016
0017 #include "hfsplus_fs.h"
0018 #include "hfsplus_raw.h"
0019
0020 struct hfsplus_wd {
0021 u32 ablk_size;
0022 u16 ablk_start;
0023 u16 embed_start;
0024 u16 embed_count;
0025 };
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047 int hfsplus_submit_bio(struct super_block *sb, sector_t sector,
0048 void *buf, void **data, blk_opf_t opf)
0049 {
0050 const enum req_op op = opf & REQ_OP_MASK;
0051 struct bio *bio;
0052 int ret = 0;
0053 u64 io_size;
0054 loff_t start;
0055 int offset;
0056
0057
0058
0059
0060
0061
0062 io_size = hfsplus_min_io_size(sb);
0063 start = (loff_t)sector << HFSPLUS_SECTOR_SHIFT;
0064 offset = start & (io_size - 1);
0065 sector &= ~((io_size >> HFSPLUS_SECTOR_SHIFT) - 1);
0066
0067 bio = bio_alloc(sb->s_bdev, 1, opf, GFP_NOIO);
0068 bio->bi_iter.bi_sector = sector;
0069
0070 if (op != REQ_OP_WRITE && data)
0071 *data = (u8 *)buf + offset;
0072
0073 while (io_size > 0) {
0074 unsigned int page_offset = offset_in_page(buf);
0075 unsigned int len = min_t(unsigned int, PAGE_SIZE - page_offset,
0076 io_size);
0077
0078 ret = bio_add_page(bio, virt_to_page(buf), len, page_offset);
0079 if (ret != len) {
0080 ret = -EIO;
0081 goto out;
0082 }
0083 io_size -= len;
0084 buf = (u8 *)buf + len;
0085 }
0086
0087 ret = submit_bio_wait(bio);
0088 out:
0089 bio_put(bio);
0090 return ret < 0 ? ret : 0;
0091 }
0092
0093 static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd)
0094 {
0095 u32 extent;
0096 u16 attrib;
0097 __be16 sig;
0098
0099 sig = *(__be16 *)(bufptr + HFSP_WRAPOFF_EMBEDSIG);
0100 if (sig != cpu_to_be16(HFSPLUS_VOLHEAD_SIG) &&
0101 sig != cpu_to_be16(HFSPLUS_VOLHEAD_SIGX))
0102 return 0;
0103
0104 attrib = be16_to_cpu(*(__be16 *)(bufptr + HFSP_WRAPOFF_ATTRIB));
0105 if (!(attrib & HFSP_WRAP_ATTRIB_SLOCK) ||
0106 !(attrib & HFSP_WRAP_ATTRIB_SPARED))
0107 return 0;
0108
0109 wd->ablk_size =
0110 be32_to_cpu(*(__be32 *)(bufptr + HFSP_WRAPOFF_ABLKSIZE));
0111 if (wd->ablk_size < HFSPLUS_SECTOR_SIZE)
0112 return 0;
0113 if (wd->ablk_size % HFSPLUS_SECTOR_SIZE)
0114 return 0;
0115 wd->ablk_start =
0116 be16_to_cpu(*(__be16 *)(bufptr + HFSP_WRAPOFF_ABLKSTART));
0117
0118 extent = get_unaligned_be32(bufptr + HFSP_WRAPOFF_EMBEDEXT);
0119 wd->embed_start = (extent >> 16) & 0xFFFF;
0120 wd->embed_count = extent & 0xFFFF;
0121
0122 return 1;
0123 }
0124
0125 static int hfsplus_get_last_session(struct super_block *sb,
0126 sector_t *start, sector_t *size)
0127 {
0128 struct cdrom_device_info *cdi = disk_to_cdi(sb->s_bdev->bd_disk);
0129
0130
0131 *start = 0;
0132 *size = bdev_nr_sectors(sb->s_bdev);
0133
0134 if (HFSPLUS_SB(sb)->session >= 0) {
0135 struct cdrom_tocentry te;
0136
0137 if (!cdi)
0138 return -EINVAL;
0139
0140 te.cdte_track = HFSPLUS_SB(sb)->session;
0141 te.cdte_format = CDROM_LBA;
0142 if (cdrom_read_tocentry(cdi, &te) ||
0143 (te.cdte_ctrl & CDROM_DATA_TRACK) != 4) {
0144 pr_err("invalid session number or type of track\n");
0145 return -EINVAL;
0146 }
0147 *start = (sector_t)te.cdte_addr.lba << 2;
0148 } else if (cdi) {
0149 struct cdrom_multisession ms_info;
0150
0151 ms_info.addr_format = CDROM_LBA;
0152 if (cdrom_multisession(cdi, &ms_info) == 0 && ms_info.xa_flag)
0153 *start = (sector_t)ms_info.addr.lba << 2;
0154 }
0155
0156 return 0;
0157 }
0158
0159
0160
0161 int hfsplus_read_wrapper(struct super_block *sb)
0162 {
0163 struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
0164 struct hfsplus_wd wd;
0165 sector_t part_start, part_size;
0166 u32 blocksize;
0167 int error = 0;
0168
0169 error = -EINVAL;
0170 blocksize = sb_min_blocksize(sb, HFSPLUS_SECTOR_SIZE);
0171 if (!blocksize)
0172 goto out;
0173
0174 if (hfsplus_get_last_session(sb, &part_start, &part_size))
0175 goto out;
0176
0177 error = -ENOMEM;
0178 sbi->s_vhdr_buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL);
0179 if (!sbi->s_vhdr_buf)
0180 goto out;
0181 sbi->s_backup_vhdr_buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL);
0182 if (!sbi->s_backup_vhdr_buf)
0183 goto out_free_vhdr;
0184
0185 reread:
0186 error = hfsplus_submit_bio(sb, part_start + HFSPLUS_VOLHEAD_SECTOR,
0187 sbi->s_vhdr_buf, (void **)&sbi->s_vhdr,
0188 REQ_OP_READ);
0189 if (error)
0190 goto out_free_backup_vhdr;
0191
0192 error = -EINVAL;
0193 switch (sbi->s_vhdr->signature) {
0194 case cpu_to_be16(HFSPLUS_VOLHEAD_SIGX):
0195 set_bit(HFSPLUS_SB_HFSX, &sbi->flags);
0196 fallthrough;
0197 case cpu_to_be16(HFSPLUS_VOLHEAD_SIG):
0198 break;
0199 case cpu_to_be16(HFSP_WRAP_MAGIC):
0200 if (!hfsplus_read_mdb(sbi->s_vhdr, &wd))
0201 goto out_free_backup_vhdr;
0202 wd.ablk_size >>= HFSPLUS_SECTOR_SHIFT;
0203 part_start += (sector_t)wd.ablk_start +
0204 (sector_t)wd.embed_start * wd.ablk_size;
0205 part_size = (sector_t)wd.embed_count * wd.ablk_size;
0206 goto reread;
0207 default:
0208
0209
0210
0211
0212
0213 if (hfs_part_find(sb, &part_start, &part_size))
0214 goto out_free_backup_vhdr;
0215 goto reread;
0216 }
0217
0218 error = hfsplus_submit_bio(sb, part_start + part_size - 2,
0219 sbi->s_backup_vhdr_buf,
0220 (void **)&sbi->s_backup_vhdr, REQ_OP_READ);
0221 if (error)
0222 goto out_free_backup_vhdr;
0223
0224 error = -EINVAL;
0225 if (sbi->s_backup_vhdr->signature != sbi->s_vhdr->signature) {
0226 pr_warn("invalid secondary volume header\n");
0227 goto out_free_backup_vhdr;
0228 }
0229
0230 blocksize = be32_to_cpu(sbi->s_vhdr->blocksize);
0231
0232
0233
0234
0235 if (blocksize < HFSPLUS_SECTOR_SIZE || ((blocksize - 1) & blocksize))
0236 goto out_free_backup_vhdr;
0237 sbi->alloc_blksz = blocksize;
0238 sbi->alloc_blksz_shift = ilog2(blocksize);
0239 blocksize = min_t(u32, sbi->alloc_blksz, PAGE_SIZE);
0240
0241
0242
0243
0244 while (part_start & ((blocksize >> HFSPLUS_SECTOR_SHIFT) - 1))
0245 blocksize >>= 1;
0246
0247 if (sb_set_blocksize(sb, blocksize) != blocksize) {
0248 pr_err("unable to set blocksize to %u!\n", blocksize);
0249 goto out_free_backup_vhdr;
0250 }
0251
0252 sbi->blockoffset =
0253 part_start >> (sb->s_blocksize_bits - HFSPLUS_SECTOR_SHIFT);
0254 sbi->part_start = part_start;
0255 sbi->sect_count = part_size;
0256 sbi->fs_shift = sbi->alloc_blksz_shift - sb->s_blocksize_bits;
0257 return 0;
0258
0259 out_free_backup_vhdr:
0260 kfree(sbi->s_backup_vhdr_buf);
0261 out_free_vhdr:
0262 kfree(sbi->s_vhdr_buf);
0263 out:
0264 return error;
0265 }