diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/hfsplus/wrapper.c | |
download | lwn-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.gz lwn-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.zip |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'fs/hfsplus/wrapper.c')
-rw-r--r-- | fs/hfsplus/wrapper.c | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/fs/hfsplus/wrapper.c b/fs/hfsplus/wrapper.c new file mode 100644 index 000000000000..0c51d6338b0b --- /dev/null +++ b/fs/hfsplus/wrapper.c @@ -0,0 +1,171 @@ +/* + * 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> +#include <linux/version.h> +#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; +}; + +static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd) +{ + u32 extent; + u16 attrib; + + if (be16_to_cpu(*(__be16 *)(bufptr + HFSP_WRAPOFF_EMBEDSIG)) != HFSPLUS_VOLHEAD_SIG) + return 0; + + attrib = be16_to_cpu(*(__be16 *)(bufptr + HFSP_WRAPOFF_ATTRIB)); + if (!(attrib & HFSP_WRAP_ATTRIB_SLOCK) || + !(attrib & HFSP_WRAP_ATTRIB_SPARED)) + return 0; + + wd->ablk_size = be32_to_cpu(*(__be32 *)(bufptr + HFSP_WRAPOFF_ABLKSIZE)); + if (wd->ablk_size < HFSPLUS_SECTOR_SIZE) + return 0; + if (wd->ablk_size % HFSPLUS_SECTOR_SIZE) + return 0; + wd->ablk_start = be16_to_cpu(*(__be16 *)(bufptr + HFSP_WRAPOFF_ABLKSTART)); + + extent = be32_to_cpu(get_unaligned((__be32 *)(bufptr + HFSP_WRAPOFF_EMBEDEXT))); + 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; + + if (HFSPLUS_SB(sb).session >= 0) { + te.cdte_track = HFSPLUS_SB(sb).session; + te.cdte_format = CDROM_LBA; + res = ioctl_by_bdev(sb->s_bdev, CDROMREADTOCENTRY, (unsigned long)&te); + if (!res && (te.cdte_ctrl & CDROM_DATA_TRACK) == 4) { + *start = (sector_t)te.cdte_addr.lba << 2; + return 0; + } + printk(KERN_ERR "HFS: Invalid session number or type of track\n"); + return -EINVAL; + } + ms_info.addr_format = CDROM_LBA; + res = ioctl_by_bdev(sb->s_bdev, CDROMMULTISESSION, (unsigned long)&ms_info); + 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) +{ + struct buffer_head *bh; + struct hfsplus_vh *vhdr; + struct hfsplus_wd wd; + sector_t part_start, part_size; + u32 blocksize; + + blocksize = sb_min_blocksize(sb, HFSPLUS_SECTOR_SIZE); + if (!blocksize) + return -EINVAL; + + if (hfsplus_get_last_session(sb, &part_start, &part_size)) + return -EINVAL; + while (1) { + bh = sb_bread512(sb, part_start + HFSPLUS_VOLHEAD_SECTOR, vhdr); + if (!bh) + return -EIO; + + if (vhdr->signature == cpu_to_be16(HFSP_WRAP_MAGIC)) { + if (!hfsplus_read_mdb(vhdr, &wd)) + goto error; + wd.ablk_size >>= HFSPLUS_SECTOR_SHIFT; + part_start += wd.ablk_start + wd.embed_start * wd.ablk_size; + part_size = wd.embed_count * wd.ablk_size; + brelse(bh); + bh = sb_bread512(sb, part_start + HFSPLUS_VOLHEAD_SECTOR, vhdr); + if (!bh) + return -EIO; + } + if (vhdr->signature == cpu_to_be16(HFSPLUS_VOLHEAD_SIG)) + break; + brelse(bh); + + /* check for a partition block + * (should do this only for cdrom/loop though) + */ + if (hfs_part_find(sb, &part_start, &part_size)) + return -EINVAL; + } + + blocksize = be32_to_cpu(vhdr->blocksize); + brelse(bh); + + /* block size must be at least as large as a sector + * and a multiple of 2 + */ + if (blocksize < HFSPLUS_SECTOR_SIZE || + ((blocksize - 1) & blocksize)) + return -EINVAL; + HFSPLUS_SB(sb).alloc_blksz = blocksize; + HFSPLUS_SB(sb).alloc_blksz_shift = 0; + while ((blocksize >>= 1) != 0) + HFSPLUS_SB(sb).alloc_blksz_shift++; + blocksize = min(HFSPLUS_SB(sb).alloc_blksz, (u32)PAGE_SIZE); + + /* align block size to block offset */ + while (part_start & ((blocksize >> HFSPLUS_SECTOR_SHIFT) - 1)) + blocksize >>= 1; + + if (sb_set_blocksize(sb, blocksize) != blocksize) { + printk("HFS+: unable to blocksize to %u!\n", blocksize); + return -EINVAL; + } + + HFSPLUS_SB(sb).blockoffset = part_start >> + (sb->s_blocksize_bits - HFSPLUS_SECTOR_SHIFT); + HFSPLUS_SB(sb).sect_count = part_size; + HFSPLUS_SB(sb).fs_shift = HFSPLUS_SB(sb).alloc_blksz_shift - + sb->s_blocksize_bits; + + bh = sb_bread512(sb, part_start + HFSPLUS_VOLHEAD_SECTOR, vhdr); + if (!bh) + return -EIO; + + /* should still be the same... */ + if (be16_to_cpu(vhdr->signature) != HFSPLUS_VOLHEAD_SIG) + goto error; + HFSPLUS_SB(sb).s_vhbh = bh; + HFSPLUS_SB(sb).s_vhdr = vhdr; + + return 0; + error: + brelse(bh); + return -EINVAL; +} |