Blame view
fs/hfsplus/wrapper.c
7.23 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/* * 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 Linux-2.6.12-rc2 |
15 16 17 18 19 20 21 22 23 24 25 |
#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; }; |
52399b171 hfsplus: use raw ... |
26 27 28 29 30 31 |
static void hfsplus_end_io_sync(struct bio *bio, int err) { if (err) clear_bit(BIO_UPTODATE, &bio->bi_flags); complete(bio->bi_private); } |
6596528e3 hfsplus: ensure b... |
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
/* * hfsplus_submit_bio - Perfrom block I/O * @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 * @rw: direction of I/O * * 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, void *buf, void **data, int rw) |
52399b171 hfsplus: use raw ... |
53 54 55 |
{ DECLARE_COMPLETION_ONSTACK(wait); struct bio *bio; |
50176ddef hfsplus: add miss... |
56 |
int ret = 0; |
6596528e3 hfsplus: ensure b... |
57 58 59 60 61 62 63 64 65 66 67 68 69 |
unsigned int io_size; 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 hfsplus: use raw ... |
70 71 72 |
bio = bio_alloc(GFP_NOIO, 1); bio->bi_sector = sector; |
6596528e3 hfsplus: ensure b... |
73 |
bio->bi_bdev = sb->s_bdev; |
52399b171 hfsplus: use raw ... |
74 75 |
bio->bi_end_io = hfsplus_end_io_sync; bio->bi_private = &wait; |
6596528e3 hfsplus: ensure b... |
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
if (!(rw & WRITE) && data) *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 hfsplus: use raw ... |
92 93 94 95 96 |
submit_bio(rw, bio); wait_for_completion(&wait); if (!bio_flagged(bio, BIO_UPTODATE)) |
50176ddef hfsplus: add miss... |
97 |
ret = -EIO; |
6596528e3 hfsplus: ensure b... |
98 |
out: |
50176ddef hfsplus: add miss... |
99 |
bio_put(bio); |
6596528e3 hfsplus: ensure b... |
100 |
return ret < 0 ? ret : 0; |
52399b171 hfsplus: use raw ... |
101 |
} |
1da177e4c Linux-2.6.12-rc2 |
102 103 104 105 |
static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd) { u32 extent; u16 attrib; |
2179d372d [PATCH] hfs: add ... |
106 |
__be16 sig; |
1da177e4c Linux-2.6.12-rc2 |
107 |
|
2179d372d [PATCH] hfs: add ... |
108 109 110 |
sig = *(__be16 *)(bufptr + HFSP_WRAPOFF_EMBEDSIG); if (sig != cpu_to_be16(HFSPLUS_VOLHEAD_SIG) && sig != cpu_to_be16(HFSPLUS_VOLHEAD_SIGX)) |
1da177e4c Linux-2.6.12-rc2 |
111 112 113 114 115 116 |
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 hfsplus: over 80 ... |
117 118 |
wd->ablk_size = be32_to_cpu(*(__be32 *)(bufptr + HFSP_WRAPOFF_ABLKSIZE)); |
1da177e4c Linux-2.6.12-rc2 |
119 120 121 122 |
if (wd->ablk_size < HFSPLUS_SECTOR_SIZE) return 0; if (wd->ablk_size % HFSPLUS_SECTOR_SIZE) return 0; |
2753cc281 hfsplus: over 80 ... |
123 124 |
wd->ablk_start = be16_to_cpu(*(__be16 *)(bufptr + HFSP_WRAPOFF_ABLKSTART)); |
1da177e4c Linux-2.6.12-rc2 |
125 |
|
8b3789e5d hfsplus: use get/... |
126 |
extent = get_unaligned_be32(bufptr + HFSP_WRAPOFF_EMBEDEXT); |
1da177e4c Linux-2.6.12-rc2 |
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
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) { struct cdrom_multisession ms_info; struct cdrom_tocentry te; int res; /* default values */ *start = 0; *size = sb->s_bdev->bd_inode->i_size >> 9; |
dd73a01a3 hfsplus: fix HFSP... |
143 144 |
if (HFSPLUS_SB(sb)->session >= 0) { te.cdte_track = HFSPLUS_SB(sb)->session; |
1da177e4c Linux-2.6.12-rc2 |
145 |
te.cdte_format = CDROM_LBA; |
2753cc281 hfsplus: over 80 ... |
146 147 |
res = ioctl_by_bdev(sb->s_bdev, CDROMREADTOCENTRY, (unsigned long)&te); |
1da177e4c Linux-2.6.12-rc2 |
148 149 150 151 |
if (!res && (te.cdte_ctrl & CDROM_DATA_TRACK) == 4) { *start = (sector_t)te.cdte_addr.lba << 2; return 0; } |
634725a92 [PATCH] hfs: clea... |
152 153 |
printk(KERN_ERR "hfs: invalid session number or type of track "); |
1da177e4c Linux-2.6.12-rc2 |
154 155 156 |
return -EINVAL; } ms_info.addr_format = CDROM_LBA; |
2753cc281 hfsplus: over 80 ... |
157 158 |
res = ioctl_by_bdev(sb->s_bdev, CDROMMULTISESSION, (unsigned long)&ms_info); |
1da177e4c Linux-2.6.12-rc2 |
159 160 161 162 163 164 165 166 167 |
if (!res && ms_info.xa_flag) *start = (sector_t)ms_info.addr.lba << 2; 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 hfsplus: fix HFSP... |
168 |
struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); |
1da177e4c Linux-2.6.12-rc2 |
169 170 171 |
struct hfsplus_wd wd; sector_t part_start, part_size; u32 blocksize; |
52399b171 hfsplus: use raw ... |
172 |
int error = 0; |
1da177e4c Linux-2.6.12-rc2 |
173 |
|
52399b171 hfsplus: use raw ... |
174 |
error = -EINVAL; |
1da177e4c Linux-2.6.12-rc2 |
175 176 |
blocksize = sb_min_blocksize(sb, HFSPLUS_SECTOR_SIZE); if (!blocksize) |
52399b171 hfsplus: use raw ... |
177 |
goto out; |
1da177e4c Linux-2.6.12-rc2 |
178 179 |
if (hfsplus_get_last_session(sb, &part_start, &part_size)) |
52399b171 hfsplus: use raw ... |
180 |
goto out; |
1da177e4c Linux-2.6.12-rc2 |
181 |
|
52399b171 hfsplus: use raw ... |
182 |
error = -ENOMEM; |
6596528e3 hfsplus: ensure b... |
183 184 |
sbi->s_vhdr_buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL); if (!sbi->s_vhdr_buf) |
52399b171 hfsplus: use raw ... |
185 |
goto out; |
6596528e3 hfsplus: ensure b... |
186 187 |
sbi->s_backup_vhdr_buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL); if (!sbi->s_backup_vhdr_buf) |
52399b171 hfsplus: use raw ... |
188 189 190 |
goto out_free_vhdr; reread: |
6596528e3 hfsplus: ensure b... |
191 192 193 |
error = hfsplus_submit_bio(sb, part_start + HFSPLUS_VOLHEAD_SECTOR, sbi->s_vhdr_buf, (void **)&sbi->s_vhdr, READ); |
52399b171 hfsplus: use raw ... |
194 195 196 197 198 199 200 201 202 203 204 205 |
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 hfsplus: fix two ... |
206 |
goto out_free_backup_vhdr; |
52399b171 hfsplus: use raw ... |
207 |
wd.ablk_size >>= HFSPLUS_SECTOR_SHIFT; |
4ba2d5fdc hfsplus: fix over... |
208 209 210 |
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 hfsplus: use raw ... |
211 212 213 214 215 |
goto reread; default: /* * Check for a partition block. * |
1da177e4c Linux-2.6.12-rc2 |
216 217 218 |
* (should do this only for cdrom/loop though) */ if (hfs_part_find(sb, &part_start, &part_size)) |
a1dbcef01 hfsplus: fix two ... |
219 |
goto out_free_backup_vhdr; |
52399b171 hfsplus: use raw ... |
220 221 |
goto reread; } |
6596528e3 hfsplus: ensure b... |
222 223 224 |
error = hfsplus_submit_bio(sb, part_start + part_size - 2, sbi->s_backup_vhdr_buf, (void **)&sbi->s_backup_vhdr, READ); |
52399b171 hfsplus: use raw ... |
225 226 227 228 229 230 231 232 233 |
if (error) goto out_free_backup_vhdr; error = -EINVAL; if (sbi->s_backup_vhdr->signature != sbi->s_vhdr->signature) { printk(KERN_WARNING "hfs: invalid secondary volume header "); goto out_free_backup_vhdr; |
1da177e4c Linux-2.6.12-rc2 |
234 |
} |
52399b171 hfsplus: use raw ... |
235 |
blocksize = be32_to_cpu(sbi->s_vhdr->blocksize); |
1da177e4c Linux-2.6.12-rc2 |
236 |
|
52399b171 hfsplus: use raw ... |
237 238 |
/* * Block size must be at least as large as a sector and a multiple of 2. |
1da177e4c Linux-2.6.12-rc2 |
239 |
*/ |
52399b171 hfsplus: use raw ... |
240 241 |
if (blocksize < HFSPLUS_SECTOR_SIZE || ((blocksize - 1) & blocksize)) goto out_free_backup_vhdr; |
dd73a01a3 hfsplus: fix HFSP... |
242 243 |
sbi->alloc_blksz = blocksize; sbi->alloc_blksz_shift = 0; |
1da177e4c Linux-2.6.12-rc2 |
244 |
while ((blocksize >>= 1) != 0) |
dd73a01a3 hfsplus: fix HFSP... |
245 246 |
sbi->alloc_blksz_shift++; blocksize = min(sbi->alloc_blksz, (u32)PAGE_SIZE); |
1da177e4c Linux-2.6.12-rc2 |
247 |
|
52399b171 hfsplus: use raw ... |
248 249 250 |
/* * Align block size to block offset. */ |
1da177e4c Linux-2.6.12-rc2 |
251 252 253 254 |
while (part_start & ((blocksize >> HFSPLUS_SECTOR_SHIFT) - 1)) blocksize >>= 1; if (sb_set_blocksize(sb, blocksize) != blocksize) { |
2753cc281 hfsplus: over 80 ... |
255 256 257 |
printk(KERN_ERR "hfs: unable to set blocksize to %u! ", blocksize); |
52399b171 hfsplus: use raw ... |
258 |
goto out_free_backup_vhdr; |
1da177e4c Linux-2.6.12-rc2 |
259 |
} |
dd73a01a3 hfsplus: fix HFSP... |
260 261 |
sbi->blockoffset = part_start >> (sb->s_blocksize_bits - HFSPLUS_SECTOR_SHIFT); |
52399b171 hfsplus: use raw ... |
262 |
sbi->part_start = part_start; |
dd73a01a3 hfsplus: fix HFSP... |
263 264 |
sbi->sect_count = part_size; sbi->fs_shift = sbi->alloc_blksz_shift - sb->s_blocksize_bits; |
1da177e4c Linux-2.6.12-rc2 |
265 |
return 0; |
52399b171 hfsplus: use raw ... |
266 267 |
out_free_backup_vhdr: |
f588c960f hfsplus: Fix kfre... |
268 |
kfree(sbi->s_backup_vhdr_buf); |
52399b171 hfsplus: use raw ... |
269 |
out_free_vhdr: |
f588c960f hfsplus: Fix kfre... |
270 |
kfree(sbi->s_vhdr_buf); |
52399b171 hfsplus: use raw ... |
271 272 |
out: return error; |
1da177e4c Linux-2.6.12-rc2 |
273 |
} |