/* * linux/fs/hfsplus/wrapper.c * * Copyright (C) 2001 * Brad Boyer (flar@allandria.com) * (C) 2003 Ardis Technologies * * Handling of HFS wrappers around HFS+ volumes */ #include #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) #include #endif #include #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(unsigned char *bufptr, struct hfsplus_wd *wd) { u32 extent; u16 attrib; if (be16_to_cpu(*(u16 *)(bufptr + HFSP_WRAPOFF_EMBEDSIG)) != HFSPLUS_VOLHEAD_SIG) return 0; attrib = be16_to_cpu(*(u16 *)(bufptr + HFSP_WRAPOFF_ATTRIB)); if (!(attrib & HFSP_WRAP_ATTRIB_SLOCK) || !(attrib & HFSP_WRAP_ATTRIB_SPARED)) return 0; wd->ablk_size = be32_to_cpu(*(u32 *)(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(*(u16 *)(bufptr + HFSP_WRAPOFF_ABLKSTART)); extent = be32_to_cpu(get_unaligned((u32 *)(bufptr + HFSP_WRAPOFF_EMBEDEXT))); wd->embed_start = (extent >> 16) & 0xFFFF; wd->embed_count = extent & 0xFFFF; return 1; } /* 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; char *bufptr; unsigned long block, offset, vhsect; struct hfsplus_wd wd; u32 blocksize, blockoffset; u16 sig; blocksize = sb_min_blocksize(sb, HFSPLUS_SECTOR_SIZE); if (!blocksize) { printk("HFS+-fs: unable to configure block size\n"); return -EINVAL; } block = (HFSPLUS_VOLHEAD_SECTOR * HFSPLUS_SECTOR_SIZE) / blocksize; offset = (HFSPLUS_VOLHEAD_SECTOR * HFSPLUS_SECTOR_SIZE) % blocksize; bh = sb_bread(sb, block); if (!bh) { printk("HFS+-fs: unable to read VHDR or MDB\n"); return -EIO; } bufptr = bh->b_data + offset; sig = be16_to_cpu(*(u16 *)(bufptr + HFSP_WRAPOFF_SIG)); if (sig == HFSP_WRAP_MAGIC) { if (!hfsplus_read_mdb(bufptr, &wd)) goto error; vhsect = (wd.ablk_start + wd.embed_start * (wd.ablk_size >> 9)) + HFSPLUS_VOLHEAD_SECTOR; block = (vhsect * HFSPLUS_SECTOR_SIZE) / blocksize; offset = (vhsect * HFSPLUS_SECTOR_SIZE) % blocksize; brelse(bh); bh = sb_bread(sb, block); if (!bh) { printk("HFS+-fs: unable to read VHDR\n"); return -EIO; } HFSPLUS_SB(sb).sect_count = wd.embed_count * (wd.ablk_size >> 9); } else { wd.ablk_start = 0; wd.ablk_size = blocksize; wd.embed_start = 0; HFSPLUS_SB(sb).sect_count = sb->s_bdev->bd_inode->i_size >> 9; } vhdr = (struct hfsplus_vh *)(bh->b_data + offset); if (be16_to_cpu(vhdr->signature) != HFSPLUS_VOLHEAD_SIG) goto error; 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; /* block offset must be a multiple of the block size */ blockoffset = wd.ablk_start + wd.embed_start * (wd.ablk_size >> 9); if (blockoffset % (blocksize / HFSPLUS_SECTOR_SIZE)) { printk("HFS+-fs: embedded blocks not aligned with wrapper\n"); return -EINVAL; } blockoffset /= blocksize / HFSPLUS_SECTOR_SIZE; HFSPLUS_SB(sb).blockoffset = blockoffset; if (sb_set_blocksize(sb, blocksize) != blocksize) return -EINVAL; block = blockoffset + HFSPLUS_VOLHEAD_SECTOR / (blocksize / HFSPLUS_SECTOR_SIZE); offset = (HFSPLUS_VOLHEAD_SECTOR * HFSPLUS_SECTOR_SIZE) % blocksize; bh = sb_bread(sb, block); if (!bh) { printk("HFS+-fs: unable to read VHDR or MDB\n"); return -EIO; } vhdr = (struct hfsplus_vh *)(bh->b_data + offset); /* 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; }