Exemplo n.º 1
0
/**
 * parse_vblk - parse a LDM database vblk record
 * @buffer:	vblk record loaded from the LDM database
 * @buf_size:	size of @buffer in bytes
 * @vb:		in memory vblk structure to return parsed information in
 *
 * This parses the LDM database vblk record supplied in @buffer and sets up
 * the in memory vblk structure @vb with the obtained information.
 *
 * Return 1 on success, 0 if successful but record not in use, and -1 on error.
 * If the return value is 0 or -1, @vb is undefined.
 *
 * NOTE: Currently the only record type we handle is VBLK_PART, i.e. records
 * describing a partition. For all others, we just set @vb->vblk_type to 0 and
 * return success. This of course means that if @vb->vblk_type is zero, all
 * other fields in @vb are undefined.
 */
static int parse_vblk(const u8 *buffer, const int buf_size, struct vblk *vb)
{
	int err = 1;

	if (buf_size < 0x14)
		return -1;
	if (MAGIC_VBLK != BE32(buffer)) {
		printk(LDM_CRIT "Cannot find VBLK, database may be corrupt.\n");
		return -1;
	}
	if ((BE16(buffer + 0x0E) == 0) ||       /* Record is not in use. */
	    (BE16(buffer + 0x0C) != 0))         /* Part 2 of an ext. record */
		return 0;
	/* FIXME: What about extended VBLKs? */
	switch (buffer[0x13]) {
	case VBLK_PART:
		err = parse_vblk_part(buffer, buf_size, vb);
		break;
	default:
		vb->vblk_type = 0;
	}
	if (err != -1)
		ldm_debug("Parsed VBLK successfully.\n");
	return err;
}
Exemplo n.º 2
0
/**
 * ldm_parse_vmdb - Read the LDM Database VMDB structure
 * @data:  Raw database VMDB structure loaded from the device
 * @vm:    In-memory vmdb structure in which to return parsed information
 *
 * This parses the LDM Database VMDB structure supplied in @data and sets up
 * the in-memory vmdb structure @vm with the obtained information.
 *
 * N.B.  The *_start, *_size and *_seq values will be range-checked later.
 *
 * Return:  'true'   @vm contains VMDB info
 *          'false'  @vm contents are undefined
 */
static bool ldm_parse_vmdb (const u8 *data, struct vmdb *vm)
{
	BUG_ON (!data || !vm);

	if (MAGIC_VMDB != get_unaligned_be32(data)) {
		ldm_crit ("Cannot find the VMDB, database may be corrupt.");
		return false;
	}

	vm->ver_major = get_unaligned_be16(data + 0x12);
	vm->ver_minor = get_unaligned_be16(data + 0x14);
	if ((vm->ver_major != 4) || (vm->ver_minor != 10)) {
		ldm_error ("Expected VMDB version %d.%d, got %d.%d. "
			"Aborting.", 4, 10, vm->ver_major, vm->ver_minor);
		return false;
	}

	vm->vblk_size     = get_unaligned_be32(data + 0x08);
	if (vm->vblk_size == 0) {
		ldm_error ("Illegal VBLK size");
		return false;
	}

	vm->vblk_offset   = get_unaligned_be32(data + 0x0C);
	vm->last_vblk_seq = get_unaligned_be32(data + 0x04);

	ldm_debug ("Parsed VMDB successfully.");
	return true;
}
Exemplo n.º 3
0
/**
 * ldm_parse_tocblock - Read the LDM Database TOCBLOCK structure
 * @data:  Raw database TOCBLOCK structure loaded from the device
 * @toc:   In-memory toc structure in which to return parsed information
 *
 * This parses the LDM Database TOCBLOCK (table of contents) structure supplied
 * in @data and sets up the in-memory tocblock structure @toc with the obtained
 * information.
 *
 * N.B.  The *_start and *_size values returned in @toc are not range-checked.
 *
 * Return:  'true'   @toc contains the TOCBLOCK data
 *          'false'  @toc contents are undefined
 */
static bool ldm_parse_tocblock (const u8 *data, struct tocblock *toc)
{
	BUG_ON (!data || !toc);

	if (MAGIC_TOCBLOCK != get_unaligned_be64(data)) {
		ldm_crit ("Cannot find TOCBLOCK, database may be corrupt.");
		return false;
	}
	strncpy (toc->bitmap1_name, data + 0x24, sizeof (toc->bitmap1_name));
	toc->bitmap1_name[sizeof (toc->bitmap1_name) - 1] = 0;
	toc->bitmap1_start = get_unaligned_be64(data + 0x2E);
	toc->bitmap1_size  = get_unaligned_be64(data + 0x36);

	if (strncmp (toc->bitmap1_name, TOC_BITMAP1,
			sizeof (toc->bitmap1_name)) != 0) {
		ldm_crit ("TOCBLOCK's first bitmap is '%s', should be '%s'.",
				TOC_BITMAP1, toc->bitmap1_name);
		return false;
	}
	strncpy (toc->bitmap2_name, data + 0x46, sizeof (toc->bitmap2_name));
	toc->bitmap2_name[sizeof (toc->bitmap2_name) - 1] = 0;
	toc->bitmap2_start = get_unaligned_be64(data + 0x50);
	toc->bitmap2_size  = get_unaligned_be64(data + 0x58);
	if (strncmp (toc->bitmap2_name, TOC_BITMAP2,
			sizeof (toc->bitmap2_name)) != 0) {
		ldm_crit ("TOCBLOCK's second bitmap is '%s', should be '%s'.",
				TOC_BITMAP2, toc->bitmap2_name);
		return false;
	}
	ldm_debug ("Parsed TOCBLOCK successfully.");
	return true;
}
Exemplo n.º 4
0
/**
 * ldm_parse_privhead - Read the LDM Database PRIVHEAD structure
 * @data:  Raw database PRIVHEAD structure loaded from the device
 * @ph:    In-memory privhead structure in which to return parsed information
 *
 * This parses the LDM database PRIVHEAD structure supplied in @data and
 * sets up the in-memory privhead structure @ph with the obtained information.
 *
 * Return:  'true'   @ph contains the PRIVHEAD data
 *          'false'  @ph contents are undefined
 */
static bool ldm_parse_privhead(const u8 *data, struct privhead *ph)
{
	bool is_vista = false;

	BUG_ON(!data || !ph);
	if (MAGIC_PRIVHEAD != get_unaligned_be64(data)) {
		ldm_error("Cannot find PRIVHEAD structure. LDM database is"
			" corrupt. Aborting.");
		return false;
	}
	ph->ver_major = get_unaligned_be16(data + 0x000C);
	ph->ver_minor = get_unaligned_be16(data + 0x000E);
	ph->logical_disk_start = get_unaligned_be64(data + 0x011B);
	ph->logical_disk_size = get_unaligned_be64(data + 0x0123);
	ph->config_start = get_unaligned_be64(data + 0x012B);
	ph->config_size = get_unaligned_be64(data + 0x0133);
	/* Version 2.11 is Win2k/XP and version 2.12 is Vista. */
	if (ph->ver_major == 2 && ph->ver_minor == 12)
		is_vista = true;
	if (!is_vista && (ph->ver_major != 2 || ph->ver_minor != 11)) {
		ldm_error("Expected PRIVHEAD version 2.11 or 2.12, got %d.%d."
			" Aborting.", ph->ver_major, ph->ver_minor);
		return false;
	}
	ldm_debug("PRIVHEAD version %d.%d (Windows %s).", ph->ver_major,
			ph->ver_minor, is_vista ? "Vista" : "2000/XP");
	if (ph->config_size != LDM_DB_SIZE) {	/* 1 MiB in sectors. */
		/* Warn the user and continue, carefully. */
		ldm_info("Database is normally %u bytes, it claims to "
			"be %llu bytes.", LDM_DB_SIZE,
			(unsigned long long)ph->config_size);
	}
	if ((ph->logical_disk_size == 0) || (ph->logical_disk_start +
			ph->logical_disk_size > ph->config_start)) {
		ldm_error("PRIVHEAD disk size doesn't match real disk size");
		return false;
	}
	if (!ldm_parse_guid(data + 0x0030, ph->disk_id)) {
		ldm_error("PRIVHEAD contains an invalid GUID.");
		return false;
	}
	ldm_debug("Parsed PRIVHEAD successfully.");
	return true;
}
Exemplo n.º 5
0
/**
 * parse_vblk_part - parse a LDM database vblk partition record
 * @buffer:	vblk partition record loaded from the LDM database
 * @buf_size:	size of @buffer in bytes
 * @vb:		in memory vblk structure to return parsed information in
 *
 * This parses the LDM database vblk record of type VBLK_PART, i.e. a partition
 * record, supplied in @buffer and sets up the in memory vblk structure @vb
 * with the obtained information.
 *
 * Return 1 on success and -1 on error, in which case @vb is undefined.
 */
static int parse_vblk_part(const u8 *buffer, const int buf_size,
		struct vblk *vb)
{
	int err, rel_objid, rel_name, rel_size, rel_parent;

	if (0x34 >= buf_size)
		return -1;
	/* Calculate relative offsets. */
	rel_objid  = 1 + buffer[0x18];
	if (0x18 + rel_objid >= buf_size)
		return -1;
	rel_name   = 1 + buffer[0x18 + rel_objid] + rel_objid;
	if (0x34 + rel_name >= buf_size)
		return -1;
	rel_size   = 1 + buffer[0x34 + rel_name] + rel_name;
	if (0x34 + rel_size >= buf_size)
		return -1;
	rel_parent = 1 + buffer[0x34 + rel_size] + rel_size;
	if (0x34 + rel_parent >= buf_size)
		return -1;
	/* Setup @vb. */
	vb->vblk_type    = VBLK_PART;
	vb->obj_id       = get_vnum(buffer + 0x18, &err);
	if (err || 0x34 + rel_parent + buffer[0x34 + rel_parent] >= buf_size)
		return -1;
	vb->disk_id      = get_vnum(buffer + 0x34 + rel_parent, &err);
	if (err || 0x24 + rel_name + 8 > buf_size)
		return -1;
	vb->start_sector = BE64(buffer + 0x24 + rel_name);
	if (0x34 + rel_name + buffer[0x34 + rel_name] >= buf_size)
		return -1;
	vb->num_sectors  = get_vnum(buffer + 0x34 + rel_name, &err);
	if (err || 0x18 + rel_objid + buffer[0x18 + rel_objid] >= buf_size)
		return -1;
	err = get_vstr(buffer + 0x18 + rel_objid, vb->name, sizeof(vb->name));
	if (err == -1)
		return err;
	ldm_debug("Parsed Partition VBLK successfully.\n");
	return 1;
}
Exemplo n.º 6
0
/**
 * ldm_validate_tocblocks - Validate the table of contents and its backups
 * @state: Partition check state including device holding the LDM Database
 * @base:  Offset, into @state->bdev, of the database
 * @ldb:   Cache of the database structures
 *
 * Find and compare the four tables of contents of the LDM Database stored on
 * @state->bdev and return the parsed information into @toc1.
 *
 * The offsets and sizes of the configs are range-checked against a privhead.
 *
 * Return:  'true'   @toc1 contains validated TOCBLOCK info
 *          'false'  @toc1 contents are undefined
 */
static bool ldm_validate_tocblocks(struct parsed_partitions *state,
				   unsigned long base, struct ldmdb *ldb)
{
	static const int off[4] = { OFF_TOCB1, OFF_TOCB2, OFF_TOCB3, OFF_TOCB4};
	struct tocblock *tb[4];
	struct privhead *ph;
	Sector sect;
	u8 *data;
	int i, nr_tbs;
	bool result = false;

	BUG_ON(!state || !ldb);
	ph = &ldb->ph;
	tb[0] = &ldb->toc;
	tb[1] = kmalloc(sizeof(*tb[1]) * 3, GFP_KERNEL);
	if (!tb[1]) {
		ldm_crit("Out of memory.");
		goto err;
	}
	tb[2] = (struct tocblock*)((u8*)tb[1] + sizeof(*tb[1]));
	tb[3] = (struct tocblock*)((u8*)tb[2] + sizeof(*tb[2]));
	/*
	 * Try to read and parse all four TOCBLOCKs.
	 *
	 * Windows Vista LDM v2.12 does not always have all four TOCBLOCKs so
	 * skip any that fail as long as we get at least one valid TOCBLOCK.
	 */
	for (nr_tbs = i = 0; i < 4; i++) {
		data = read_part_sector(state, base + off[i], &sect);
		if (!data) {
			ldm_error("Disk read failed for TOCBLOCK %d.", i);
			continue;
		}
		if (ldm_parse_tocblock(data, tb[nr_tbs]))
			nr_tbs++;
		put_dev_sector(sect);
	}
	if (!nr_tbs) {
		ldm_crit("Failed to find a valid TOCBLOCK.");
		goto err;
	}
	/* Range check the TOCBLOCK against a privhead. */
	if (((tb[0]->bitmap1_start + tb[0]->bitmap1_size) > ph->config_size) ||
			((tb[0]->bitmap2_start + tb[0]->bitmap2_size) >
			ph->config_size)) {
		ldm_crit("The bitmaps are out of range.  Giving up.");
		goto err;
	}
	/* Compare all loaded TOCBLOCKs. */
	for (i = 1; i < nr_tbs; i++) {
		if (!ldm_compare_tocblocks(tb[0], tb[i])) {
			ldm_crit("TOCBLOCKs 0 and %d do not match.", i);
			goto err;
		}
	}
	ldm_debug("Validated %d TOCBLOCKs successfully.", nr_tbs);
	result = true;
err:
	kfree(tb[1]);
	return result;
}
Exemplo n.º 7
0
/**
 * ldm_validate_privheads - Compare the primary privhead with its backups
 * @state: Partition check state including device holding the LDM Database
 * @ph1:   Memory struct to fill with ph contents
 *
 * Read and compare all three privheads from disk.
 *
 * The privheads on disk show the size and location of the main disk area and
 * the configuration area (the database).  The values are range-checked against
 * @hd, which contains the real size of the disk.
 *
 * Return:  'true'   Success
 *          'false'  Error
 */
static bool ldm_validate_privheads(struct parsed_partitions *state,
				   struct privhead *ph1)
{
	static const int off[3] = { OFF_PRIV1, OFF_PRIV2, OFF_PRIV3 };
	struct privhead *ph[3] = { ph1 };
	Sector sect;
	u8 *data;
	bool result = false;
	long num_sects;
	int i;

	BUG_ON (!state || !ph1);

	ph[1] = kmalloc (sizeof (*ph[1]), GFP_KERNEL);
	ph[2] = kmalloc (sizeof (*ph[2]), GFP_KERNEL);
	if (!ph[1] || !ph[2]) {
		ldm_crit ("Out of memory.");
		goto out;
	}

	/* off[1 & 2] are relative to ph[0]->config_start */
	ph[0]->config_start = 0;

	/* Read and parse privheads */
	for (i = 0; i < 3; i++) {
		data = read_part_sector(state, ph[0]->config_start + off[i],
					&sect);
		if (!data) {
			ldm_crit ("Disk read failed.");
			goto out;
		}
		result = ldm_parse_privhead (data, ph[i]);
		put_dev_sector (sect);
		if (!result) {
			ldm_error ("Cannot find PRIVHEAD %d.", i+1); /* Log again */
			if (i < 2)
				goto out;	/* Already logged */
			else
				break;	/* FIXME ignore for now, 3rd PH can fail on odd-sized disks */
		}
	}

	num_sects = state->bdev->bd_inode->i_size >> 9;

	if ((ph[0]->config_start > num_sects) ||
	   ((ph[0]->config_start + ph[0]->config_size) > num_sects)) {
		ldm_crit ("Database extends beyond the end of the disk.");
		goto out;
	}

	if ((ph[0]->logical_disk_start > ph[0]->config_start) ||
	   ((ph[0]->logical_disk_start + ph[0]->logical_disk_size)
		    > ph[0]->config_start)) {
		ldm_crit ("Disk and database overlap.");
		goto out;
	}

	if (!ldm_compare_privheads (ph[0], ph[1])) {
		ldm_crit ("Primary and backup PRIVHEADs don't match.");
		goto out;
	}
	/* FIXME ignore this for now
	if (!ldm_compare_privheads (ph[0], ph[2])) {
		ldm_crit ("Primary and backup PRIVHEADs don't match.");
		goto out;
	}*/
	ldm_debug ("Validated PRIVHEADs successfully.");
	result = true;
out:
	kfree (ph[1]);
	kfree (ph[2]);
	return result;
}