Blame view
fs/hfsplus/wrapper.c
6.99 KB
b24413180
|
1 |
// SPDX-License-Identifier: GPL-2.0 |
1da177e4c
|
2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/* * linux/fs/hfsplus/wrapper.c * * Copyright (C) 2001 * Brad Boyer (flar@allandria.com) * (C) 2003 Ardis Technologies <roman@ardistech.com> * * Handling of HFS wrappers around HFS+ volumes */ #include <linux/fs.h> #include <linux/blkdev.h> #include <linux/cdrom.h> #include <linux/genhd.h> |
1da177e4c
|
16 17 18 19 20 21 22 23 24 25 26 |
#include <asm/unaligned.h> #include "hfsplus_fs.h" #include "hfsplus_raw.h" struct hfsplus_wd { u32 ablk_size; u16 ablk_start; u16 embed_start; u16 embed_count; }; |
915ab236d
|
27 28 |
/** * hfsplus_submit_bio - Perform block I/O |
6596528e3
|
29 30 31 32 |
* @sb: super block of volume for I/O * @sector: block to read or write, for blocks of HFSPLUS_SECTOR_SIZE bytes * @buf: buffer for I/O * @data: output pointer for location of requested data |
67ed25961
|
33 34 |
* @op: direction of I/O * @op_flags: request op flags |
6596528e3
|
35 36 37 38 39 40 41 42 43 44 45 46 47 |
* * The unit of I/O is hfsplus_min_io_size(sb), which may be bigger than * HFSPLUS_SECTOR_SIZE, and @buf must be sized accordingly. On reads * @data will return a pointer to the start of the requested sector, * which may not be the same location as @buf. * * If @sector is not aligned to the bdev logical block size it will * be rounded down. For writes this means that @buf should contain data * that starts at the rounded-down address. As long as the data was * read using hfsplus_submit_bio() and the same buffer is used things * will work correctly. */ int hfsplus_submit_bio(struct super_block *sb, sector_t sector, |
67ed25961
|
48 |
void *buf, void **data, int op, int op_flags) |
52399b171
|
49 |
{ |
52399b171
|
50 |
struct bio *bio; |
50176ddef
|
51 |
int ret = 0; |
a6dc8c042
|
52 |
u64 io_size; |
6596528e3
|
53 54 55 56 57 58 59 60 61 62 63 64 |
loff_t start; int offset; /* * Align sector to hardware sector size and find offset. We * assume that io_size is a power of two, which _should_ * be true. */ io_size = hfsplus_min_io_size(sb); start = (loff_t)sector << HFSPLUS_SECTOR_SHIFT; offset = start & (io_size - 1); sector &= ~((io_size >> HFSPLUS_SECTOR_SHIFT) - 1); |
52399b171
|
65 66 |
bio = bio_alloc(GFP_NOIO, 1); |
4f024f379
|
67 |
bio->bi_iter.bi_sector = sector; |
74d46992e
|
68 |
bio_set_dev(bio, sb->s_bdev); |
67ed25961
|
69 |
bio_set_op_attrs(bio, op, op_flags); |
52399b171
|
70 |
|
67ed25961
|
71 |
if (op != WRITE && data) |
6596528e3
|
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
*data = (u8 *)buf + offset; while (io_size > 0) { unsigned int page_offset = offset_in_page(buf); unsigned int len = min_t(unsigned int, PAGE_SIZE - page_offset, io_size); ret = bio_add_page(bio, virt_to_page(buf), len, page_offset); if (ret != len) { ret = -EIO; goto out; } io_size -= len; buf = (u8 *)buf + len; } |
52399b171
|
87 |
|
4e49ea4a3
|
88 |
ret = submit_bio_wait(bio); |
6596528e3
|
89 |
out: |
50176ddef
|
90 |
bio_put(bio); |
6596528e3
|
91 |
return ret < 0 ? ret : 0; |
52399b171
|
92 |
} |
1da177e4c
|
93 94 95 96 |
static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd) { u32 extent; u16 attrib; |
2179d372d
|
97 |
__be16 sig; |
1da177e4c
|
98 |
|
2179d372d
|
99 100 101 |
sig = *(__be16 *)(bufptr + HFSP_WRAPOFF_EMBEDSIG); if (sig != cpu_to_be16(HFSPLUS_VOLHEAD_SIG) && sig != cpu_to_be16(HFSPLUS_VOLHEAD_SIGX)) |
1da177e4c
|
102 103 104 105 106 107 |
return 0; attrib = be16_to_cpu(*(__be16 *)(bufptr + HFSP_WRAPOFF_ATTRIB)); if (!(attrib & HFSP_WRAP_ATTRIB_SLOCK) || !(attrib & HFSP_WRAP_ATTRIB_SPARED)) return 0; |
2753cc281
|
108 109 |
wd->ablk_size = be32_to_cpu(*(__be32 *)(bufptr + HFSP_WRAPOFF_ABLKSIZE)); |
1da177e4c
|
110 111 112 113 |
if (wd->ablk_size < HFSPLUS_SECTOR_SIZE) return 0; if (wd->ablk_size % HFSPLUS_SECTOR_SIZE) return 0; |
2753cc281
|
114 115 |
wd->ablk_start = be16_to_cpu(*(__be16 *)(bufptr + HFSP_WRAPOFF_ABLKSTART)); |
1da177e4c
|
116 |
|
8b3789e5d
|
117 |
extent = get_unaligned_be32(bufptr + HFSP_WRAPOFF_EMBEDEXT); |
1da177e4c
|
118 119 120 121 122 123 124 125 126 |
wd->embed_start = (extent >> 16) & 0xFFFF; wd->embed_count = extent & 0xFFFF; return 1; } static int hfsplus_get_last_session(struct super_block *sb, sector_t *start, sector_t *size) { |
f252fa33d
|
127 |
struct cdrom_device_info *cdi = disk_to_cdi(sb->s_bdev->bd_disk); |
1da177e4c
|
128 129 130 |
/* default values */ *start = 0; |
a3f223501
|
131 |
*size = i_size_read(sb->s_bdev->bd_inode) >> 9; |
1da177e4c
|
132 |
|
dd73a01a3
|
133 |
if (HFSPLUS_SB(sb)->session >= 0) { |
f252fa33d
|
134 135 136 137 |
struct cdrom_tocentry te; if (!cdi) return -EINVAL; |
dd73a01a3
|
138 |
te.cdte_track = HFSPLUS_SB(sb)->session; |
1da177e4c
|
139 |
te.cdte_format = CDROM_LBA; |
f252fa33d
|
140 141 142 143 144 |
if (cdrom_read_tocentry(cdi, &te) || (te.cdte_ctrl & CDROM_DATA_TRACK) != 4) { pr_err("invalid session number or type of track "); return -EINVAL; |
1da177e4c
|
145 |
} |
f252fa33d
|
146 147 148 149 150 151 152 |
*start = (sector_t)te.cdte_addr.lba << 2; } else if (cdi) { struct cdrom_multisession ms_info; ms_info.addr_format = CDROM_LBA; if (cdrom_multisession(cdi, &ms_info) == 0 && ms_info.xa_flag) *start = (sector_t)ms_info.addr.lba << 2; |
1da177e4c
|
153 |
} |
f252fa33d
|
154 |
|
1da177e4c
|
155 156 157 158 159 160 161 |
return 0; } /* Find the volume header and fill in some minimum bits in superblock */ /* Takes in super block, returns true if good data read */ int hfsplus_read_wrapper(struct super_block *sb) { |
dd73a01a3
|
162 |
struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); |
1da177e4c
|
163 164 165 |
struct hfsplus_wd wd; sector_t part_start, part_size; u32 blocksize; |
52399b171
|
166 |
int error = 0; |
1da177e4c
|
167 |
|
52399b171
|
168 |
error = -EINVAL; |
1da177e4c
|
169 170 |
blocksize = sb_min_blocksize(sb, HFSPLUS_SECTOR_SIZE); if (!blocksize) |
52399b171
|
171 |
goto out; |
1da177e4c
|
172 173 |
if (hfsplus_get_last_session(sb, &part_start, &part_size)) |
52399b171
|
174 |
goto out; |
1da177e4c
|
175 |
|
52399b171
|
176 |
error = -ENOMEM; |
6596528e3
|
177 178 |
sbi->s_vhdr_buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL); if (!sbi->s_vhdr_buf) |
52399b171
|
179 |
goto out; |
6596528e3
|
180 181 |
sbi->s_backup_vhdr_buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL); if (!sbi->s_backup_vhdr_buf) |
52399b171
|
182 183 184 |
goto out_free_vhdr; reread: |
6596528e3
|
185 186 |
error = hfsplus_submit_bio(sb, part_start + HFSPLUS_VOLHEAD_SECTOR, sbi->s_vhdr_buf, (void **)&sbi->s_vhdr, |
67ed25961
|
187 |
REQ_OP_READ, 0); |
52399b171
|
188 189 190 191 192 193 194 195 196 197 198 199 |
if (error) goto out_free_backup_vhdr; error = -EINVAL; switch (sbi->s_vhdr->signature) { case cpu_to_be16(HFSPLUS_VOLHEAD_SIGX): set_bit(HFSPLUS_SB_HFSX, &sbi->flags); /*FALLTHRU*/ case cpu_to_be16(HFSPLUS_VOLHEAD_SIG): break; case cpu_to_be16(HFSP_WRAP_MAGIC): if (!hfsplus_read_mdb(sbi->s_vhdr, &wd)) |
a1dbcef01
|
200 |
goto out_free_backup_vhdr; |
52399b171
|
201 |
wd.ablk_size >>= HFSPLUS_SECTOR_SHIFT; |
4ba2d5fdc
|
202 203 204 |
part_start += (sector_t)wd.ablk_start + (sector_t)wd.embed_start * wd.ablk_size; part_size = (sector_t)wd.embed_count * wd.ablk_size; |
52399b171
|
205 206 207 208 209 |
goto reread; default: /* * Check for a partition block. * |
1da177e4c
|
210 211 212 |
* (should do this only for cdrom/loop though) */ if (hfs_part_find(sb, &part_start, &part_size)) |
a1dbcef01
|
213 |
goto out_free_backup_vhdr; |
52399b171
|
214 215 |
goto reread; } |
6596528e3
|
216 217 |
error = hfsplus_submit_bio(sb, part_start + part_size - 2, sbi->s_backup_vhdr_buf, |
67ed25961
|
218 219 |
(void **)&sbi->s_backup_vhdr, REQ_OP_READ, 0); |
52399b171
|
220 221 222 223 224 |
if (error) goto out_free_backup_vhdr; error = -EINVAL; if (sbi->s_backup_vhdr->signature != sbi->s_vhdr->signature) { |
d61426732
|
225 226 |
pr_warn("invalid secondary volume header "); |
52399b171
|
227 |
goto out_free_backup_vhdr; |
1da177e4c
|
228 |
} |
52399b171
|
229 |
blocksize = be32_to_cpu(sbi->s_vhdr->blocksize); |
1da177e4c
|
230 |
|
52399b171
|
231 232 |
/* * Block size must be at least as large as a sector and a multiple of 2. |
1da177e4c
|
233 |
*/ |
52399b171
|
234 235 |
if (blocksize < HFSPLUS_SECTOR_SIZE || ((blocksize - 1) & blocksize)) goto out_free_backup_vhdr; |
dd73a01a3
|
236 |
sbi->alloc_blksz = blocksize; |
297cc2720
|
237 |
sbi->alloc_blksz_shift = ilog2(blocksize); |
915ab236d
|
238 |
blocksize = min_t(u32, sbi->alloc_blksz, PAGE_SIZE); |
1da177e4c
|
239 |
|
52399b171
|
240 241 242 |
/* * Align block size to block offset. */ |
1da177e4c
|
243 244 245 246 |
while (part_start & ((blocksize >> HFSPLUS_SECTOR_SHIFT) - 1)) blocksize >>= 1; if (sb_set_blocksize(sb, blocksize) != blocksize) { |
d61426732
|
247 248 |
pr_err("unable to set blocksize to %u! ", blocksize); |
52399b171
|
249 |
goto out_free_backup_vhdr; |
1da177e4c
|
250 |
} |
dd73a01a3
|
251 252 |
sbi->blockoffset = part_start >> (sb->s_blocksize_bits - HFSPLUS_SECTOR_SHIFT); |
52399b171
|
253 |
sbi->part_start = part_start; |
dd73a01a3
|
254 255 |
sbi->sect_count = part_size; sbi->fs_shift = sbi->alloc_blksz_shift - sb->s_blocksize_bits; |
1da177e4c
|
256 |
return 0; |
52399b171
|
257 258 |
out_free_backup_vhdr: |
f588c960f
|
259 |
kfree(sbi->s_backup_vhdr_buf); |
52399b171
|
260 |
out_free_vhdr: |
f588c960f
|
261 |
kfree(sbi->s_vhdr_buf); |
52399b171
|
262 263 |
out: return error; |
1da177e4c
|
264 |
} |