Beispiel #1
0
static int hfs_parse_new_pmap(struct super_block *sb, void *buf,
		struct new_pmap *pm, sector_t *part_start, sector_t *part_size)
{
	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
	int size = be32_to_cpu(pm->pmMapBlkCnt);
	int buf_size = hfsplus_min_io_size(sb);
	int res;
	int i = 0;

	do {
		if (!memcmp(pm->pmPartType, "Apple_HFS", 9) &&
		    (sbi->part < 0 || sbi->part == i)) {
			*part_start += be32_to_cpu(pm->pmPyPartStart);
			*part_size = be32_to_cpu(pm->pmPartBlkCnt);
			return 0;
		}

		if (++i >= size)
			return -ENOENT;

		pm = (struct new_pmap *)((u8 *)pm + HFSPLUS_SECTOR_SIZE);
		if ((u8 *)pm - (u8 *)buf >= buf_size) {
			res = hfsplus_submit_bio(sb,
						 *part_start + HFS_PMAP_BLK + i,
						 buf, (void **)&pm, REQ_OP_READ,
						 0);
			if (res)
				return res;
		}
	} while (pm->pmSig == cpu_to_be16(HFS_NEW_PMAP_MAGIC));

	return -ENOENT;
}
Beispiel #2
0
/*
 * Parse the partition map looking for the start and length of a
 * HFS/HFS+ partition.
 */
int hfs_part_find(struct super_block *sb,
		sector_t *part_start, sector_t *part_size)
{
	void *buf, *data;
	int res;

	buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL);
	if (!buf)
		return -ENOMEM;

	res = hfsplus_submit_bio(sb, *part_start + HFS_PMAP_BLK,
				 buf, &data, REQ_OP_READ, 0);
	if (res)
		goto out;

	switch (be16_to_cpu(*((__be16 *)data))) {
	case HFS_OLD_PMAP_MAGIC:
		res = hfs_parse_old_pmap(sb, data, part_start, part_size);
		break;
	case HFS_NEW_PMAP_MAGIC:
		res = hfs_parse_new_pmap(sb, buf, data, part_start, part_size);
		break;
	default:
		res = -ENOENT;
		break;
	}
out:
	kfree(buf);
	return res;
}
Beispiel #3
0
/*
 * 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)
{
    DECLARE_COMPLETION_ONSTACK(wait);
    struct bio *bio;
    int ret = 0;
    u64 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);

    bio = bio_alloc(GFP_NOIO, 1);
    bio->bi_sector = sector;
    bio->bi_bdev = sb->s_bdev;
    bio->bi_end_io = hfsplus_end_io_sync;
    bio->bi_private = &wait;

    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;
    }

    submit_bio(rw, bio);
    wait_for_completion(&wait);

    if (!bio_flagged(bio, BIO_UPTODATE))
        ret = -EIO;

out:
    bio_put(bio);
    return ret < 0 ? ret : 0;
}
Beispiel #4
0
/* Takes in super block, returns true if good data read */
int hfsplus_read_wrapper(struct super_block *sb)
{
    struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
    struct hfsplus_wd wd;
    sector_t part_start, part_size;
    u32 blocksize;
    int error = 0;

    error = -EINVAL;
    blocksize = sb_min_blocksize(sb, HFSPLUS_SECTOR_SIZE);
    if (!blocksize)
        goto out;

    if (hfsplus_get_last_session(sb, &part_start, &part_size))
        goto out;

    error = -ENOMEM;
    sbi->s_vhdr_buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL);
    if (!sbi->s_vhdr_buf)
        goto out;
    sbi->s_backup_vhdr_buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL);
    if (!sbi->s_backup_vhdr_buf)
        goto out_free_vhdr;

reread:
    error = hfsplus_submit_bio(sb, part_start + HFSPLUS_VOLHEAD_SECTOR,
                               sbi->s_vhdr_buf, (void **)&sbi->s_vhdr,
                               READ);
    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))
            goto out_free_backup_vhdr;
        wd.ablk_size >>= HFSPLUS_SECTOR_SHIFT;
        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;
        goto reread;
    default:
        /*
         * Check for a partition block.
         *
         * (should do this only for cdrom/loop though)
         */
        if (hfs_part_find(sb, &part_start, &part_size))
            goto out_free_backup_vhdr;
        goto reread;
    }

    error = hfsplus_submit_bio(sb, part_start + part_size - 2,
                               sbi->s_backup_vhdr_buf,
                               (void **)&sbi->s_backup_vhdr, READ);
    if (error)
        goto out_free_backup_vhdr;

    error = -EINVAL;
    if (sbi->s_backup_vhdr->signature != sbi->s_vhdr->signature) {
        pr_warn("invalid secondary volume header\n");
        goto out_free_backup_vhdr;
    }

    blocksize = be32_to_cpu(sbi->s_vhdr->blocksize);

    /*
     * Block size must be at least as large as a sector and a multiple of 2.
     */
    if (blocksize < HFSPLUS_SECTOR_SIZE || ((blocksize - 1) & blocksize))
        goto out_free_backup_vhdr;
    sbi->alloc_blksz = blocksize;
    sbi->alloc_blksz_shift = 0;
    while ((blocksize >>= 1) != 0)
        sbi->alloc_blksz_shift++;
    blocksize = min(sbi->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) {
        pr_err("unable to set blocksize to %u!\n", blocksize);
        goto out_free_backup_vhdr;
    }

    sbi->blockoffset =
        part_start >> (sb->s_blocksize_bits - HFSPLUS_SECTOR_SHIFT);
    sbi->part_start = part_start;
    sbi->sect_count = part_size;
    sbi->fs_shift = sbi->alloc_blksz_shift - sb->s_blocksize_bits;
    return 0;

out_free_backup_vhdr:
    kfree(sbi->s_backup_vhdr_buf);
out_free_vhdr:
    kfree(sbi->s_vhdr_buf);
out:
    return error;
}