[funini.com] -> [kei@sodan] -> Kernel Reading

root/fs/hfsplus/wrapper.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. hfsplus_read_mdb
  2. hfsplus_get_last_session
  3. hfsplus_read_wrapper

/*
 *  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 <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;
        __be16 sig;

        sig = *(__be16 *)(bufptr + HFSP_WRAPOFF_EMBEDSIG);
        if (sig != cpu_to_be16(HFSPLUS_VOLHEAD_SIG) &&
            sig != cpu_to_be16(HFSPLUS_VOLHEAD_SIGX))
                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 = 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;
                if (vhdr->signature == cpu_to_be16(HFSPLUS_VOLHEAD_SIGX)) {
                        HFSPLUS_SB(sb).flags |= HFSPLUS_SB_HFSX;
                        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(KERN_ERR "hfs: unable to set 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 (vhdr->signature != (HFSPLUS_SB(sb).flags & HFSPLUS_SB_HFSX ?
                                cpu_to_be16(HFSPLUS_VOLHEAD_SIGX) :
                                cpu_to_be16(HFSPLUS_VOLHEAD_SIG)))
                goto error;
        HFSPLUS_SB(sb).s_vhbh = bh;
        HFSPLUS_SB(sb).s_vhdr = vhdr;

        return 0;
 error:
        brelse(bh);
        return -EINVAL;
}

/* [<][>][^][v][top][bottom][index][help] */

[funini.com] -> [kei@sodan] -> Kernel Reading