1 /* 2 * linux/fs/hfsplus/wrapper.c 3 * 4 * Copyright (C) 2001 5 * Brad Boyer (flar@allandria.com) 6 * (C) 2003 Ardis Technologies <roman@ardistech.com> 7 * 8 * Handling of HFS wrappers around HFS+ volumes 9 */ 10 11 #include <linux/fs.h> 12 #include <linux/blkdev.h> 13 #include <linux/cdrom.h> 14 #include <linux/genhd.h> 15 #include <asm/unaligned.h> 16 17 #include "hfsplus_fs.h" 18 #include "hfsplus_raw.h" 19 20 struct hfsplus_wd { 21 u32 ablk_size; 22 u16 ablk_start; 23 u16 embed_start; 24 u16 embed_count; 25 }; 26 27 static void hfsplus_end_io_sync(struct bio *bio, int err) 28 { 29 if (err) 30 clear_bit(BIO_UPTODATE, &bio->bi_flags); 31 complete(bio->bi_private); 32 } 33 34 int hfsplus_submit_bio(struct block_device *bdev, sector_t sector, 35 void *data, int rw) 36 { 37 DECLARE_COMPLETION_ONSTACK(wait); 38 struct bio *bio; 39 40 bio = bio_alloc(GFP_NOIO, 1); 41 bio->bi_sector = sector; 42 bio->bi_bdev = bdev; 43 bio->bi_end_io = hfsplus_end_io_sync; 44 bio->bi_private = &wait; 45 46 /* 47 * We always submit one sector at a time, so bio_add_page must not fail. 48 */ 49 if (bio_add_page(bio, virt_to_page(data), HFSPLUS_SECTOR_SIZE, 50 offset_in_page(data)) != HFSPLUS_SECTOR_SIZE) 51 BUG(); 52 53 submit_bio(rw, bio); 54 wait_for_completion(&wait); 55 56 if (!bio_flagged(bio, BIO_UPTODATE)) 57 return -EIO; 58 return 0; 59 } 60 61 static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd) 62 { 63 u32 extent; 64 u16 attrib; 65 __be16 sig; 66 67 sig = *(__be16 *)(bufptr + HFSP_WRAPOFF_EMBEDSIG); 68 if (sig != cpu_to_be16(HFSPLUS_VOLHEAD_SIG) && 69 sig != cpu_to_be16(HFSPLUS_VOLHEAD_SIGX)) 70 return 0; 71 72 attrib = be16_to_cpu(*(__be16 *)(bufptr + HFSP_WRAPOFF_ATTRIB)); 73 if (!(attrib & HFSP_WRAP_ATTRIB_SLOCK) || 74 !(attrib & HFSP_WRAP_ATTRIB_SPARED)) 75 return 0; 76 77 wd->ablk_size = 78 be32_to_cpu(*(__be32 *)(bufptr + HFSP_WRAPOFF_ABLKSIZE)); 79 if (wd->ablk_size < HFSPLUS_SECTOR_SIZE) 80 return 0; 81 if (wd->ablk_size % HFSPLUS_SECTOR_SIZE) 82 return 0; 83 wd->ablk_start = 84 be16_to_cpu(*(__be16 *)(bufptr + HFSP_WRAPOFF_ABLKSTART)); 85 86 extent = get_unaligned_be32(bufptr + HFSP_WRAPOFF_EMBEDEXT); 87 wd->embed_start = (extent >> 16) & 0xFFFF; 88 wd->embed_count = extent & 0xFFFF; 89 90 return 1; 91 } 92 93 static int hfsplus_get_last_session(struct super_block *sb, 94 sector_t *start, sector_t *size) 95 { 96 struct cdrom_multisession ms_info; 97 struct cdrom_tocentry te; 98 int res; 99 100 /* default values */ 101 *start = 0; 102 *size = sb->s_bdev->bd_inode->i_size >> 9; 103 104 if (HFSPLUS_SB(sb)->session >= 0) { 105 te.cdte_track = HFSPLUS_SB(sb)->session; 106 te.cdte_format = CDROM_LBA; 107 res = ioctl_by_bdev(sb->s_bdev, 108 CDROMREADTOCENTRY, (unsigned long)&te); 109 if (!res && (te.cdte_ctrl & CDROM_DATA_TRACK) == 4) { 110 *start = (sector_t)te.cdte_addr.lba << 2; 111 return 0; 112 } 113 printk(KERN_ERR "hfs: invalid session number or type of track\n"); 114 return -EINVAL; 115 } 116 ms_info.addr_format = CDROM_LBA; 117 res = ioctl_by_bdev(sb->s_bdev, CDROMMULTISESSION, 118 (unsigned long)&ms_info); 119 if (!res && ms_info.xa_flag) 120 *start = (sector_t)ms_info.addr.lba << 2; 121 return 0; 122 } 123 124 /* Find the volume header and fill in some minimum bits in superblock */ 125 /* Takes in super block, returns true if good data read */ 126 int hfsplus_read_wrapper(struct super_block *sb) 127 { 128 struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); 129 struct hfsplus_wd wd; 130 sector_t part_start, part_size; 131 u32 blocksize; 132 int error = 0; 133 134 error = -EINVAL; 135 blocksize = sb_min_blocksize(sb, HFSPLUS_SECTOR_SIZE); 136 if (!blocksize) 137 goto out; 138 139 if (hfsplus_get_last_session(sb, &part_start, &part_size)) 140 goto out; 141 if ((u64)part_start + part_size > 0x100000000ULL) { 142 pr_err("hfs: volumes larger than 2TB are not supported yet\n"); 143 goto out; 144 } 145 146 error = -ENOMEM; 147 sbi->s_vhdr = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL); 148 if (!sbi->s_vhdr) 149 goto out; 150 sbi->s_backup_vhdr = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL); 151 if (!sbi->s_backup_vhdr) 152 goto out_free_vhdr; 153 154 reread: 155 error = hfsplus_submit_bio(sb->s_bdev, 156 part_start + HFSPLUS_VOLHEAD_SECTOR, 157 sbi->s_vhdr, READ); 158 if (error) 159 goto out_free_backup_vhdr; 160 161 error = -EINVAL; 162 switch (sbi->s_vhdr->signature) { 163 case cpu_to_be16(HFSPLUS_VOLHEAD_SIGX): 164 set_bit(HFSPLUS_SB_HFSX, &sbi->flags); 165 /*FALLTHRU*/ 166 case cpu_to_be16(HFSPLUS_VOLHEAD_SIG): 167 break; 168 case cpu_to_be16(HFSP_WRAP_MAGIC): 169 if (!hfsplus_read_mdb(sbi->s_vhdr, &wd)) 170 goto out_free_backup_vhdr; 171 wd.ablk_size >>= HFSPLUS_SECTOR_SHIFT; 172 part_start += wd.ablk_start + wd.embed_start * wd.ablk_size; 173 part_size = wd.embed_count * wd.ablk_size; 174 goto reread; 175 default: 176 /* 177 * Check for a partition block. 178 * 179 * (should do this only for cdrom/loop though) 180 */ 181 if (hfs_part_find(sb, &part_start, &part_size)) 182 goto out_free_backup_vhdr; 183 goto reread; 184 } 185 186 error = hfsplus_submit_bio(sb->s_bdev, 187 part_start + part_size - 2, 188 sbi->s_backup_vhdr, READ); 189 if (error) 190 goto out_free_backup_vhdr; 191 192 error = -EINVAL; 193 if (sbi->s_backup_vhdr->signature != sbi->s_vhdr->signature) { 194 printk(KERN_WARNING 195 "hfs: invalid secondary volume header\n"); 196 goto out_free_backup_vhdr; 197 } 198 199 blocksize = be32_to_cpu(sbi->s_vhdr->blocksize); 200 201 /* 202 * Block size must be at least as large as a sector and a multiple of 2. 203 */ 204 if (blocksize < HFSPLUS_SECTOR_SIZE || ((blocksize - 1) & blocksize)) 205 goto out_free_backup_vhdr; 206 sbi->alloc_blksz = blocksize; 207 sbi->alloc_blksz_shift = 0; 208 while ((blocksize >>= 1) != 0) 209 sbi->alloc_blksz_shift++; 210 blocksize = min(sbi->alloc_blksz, (u32)PAGE_SIZE); 211 212 /* 213 * Align block size to block offset. 214 */ 215 while (part_start & ((blocksize >> HFSPLUS_SECTOR_SHIFT) - 1)) 216 blocksize >>= 1; 217 218 if (sb_set_blocksize(sb, blocksize) != blocksize) { 219 printk(KERN_ERR "hfs: unable to set blocksize to %u!\n", 220 blocksize); 221 goto out_free_backup_vhdr; 222 } 223 224 sbi->blockoffset = 225 part_start >> (sb->s_blocksize_bits - HFSPLUS_SECTOR_SHIFT); 226 sbi->part_start = part_start; 227 sbi->sect_count = part_size; 228 sbi->fs_shift = sbi->alloc_blksz_shift - sb->s_blocksize_bits; 229 return 0; 230 231 out_free_backup_vhdr: 232 kfree(sbi->s_backup_vhdr); 233 out_free_vhdr: 234 kfree(sbi->s_vhdr); 235 out: 236 return error; 237 } 238