162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ldm - Support for Windows Logical Disk Manager (Dynamic Disks)
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org>
662306a36Sopenharmony_ci * Copyright (c) 2001-2012 Anton Altaparmakov
762306a36Sopenharmony_ci * Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Documentation is available at http://www.linux-ntfs.org/doku.php?id=downloads
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/pagemap.h>
1462306a36Sopenharmony_ci#include <linux/stringify.h>
1562306a36Sopenharmony_ci#include <linux/kernel.h>
1662306a36Sopenharmony_ci#include <linux/uuid.h>
1762306a36Sopenharmony_ci#include <linux/msdos_partition.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "ldm.h"
2062306a36Sopenharmony_ci#include "check.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/*
2362306a36Sopenharmony_ci * ldm_debug/info/error/crit - Output an error message
2462306a36Sopenharmony_ci * @f:    A printf format string containing the message
2562306a36Sopenharmony_ci * @...:  Variables to substitute into @f
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci * ldm_debug() writes a DEBUG level message to the syslog but only if the
2862306a36Sopenharmony_ci * driver was compiled with debug enabled. Otherwise, the call turns into a NOP.
2962306a36Sopenharmony_ci */
3062306a36Sopenharmony_ci#ifndef CONFIG_LDM_DEBUG
3162306a36Sopenharmony_ci#define ldm_debug(...)	do {} while (0)
3262306a36Sopenharmony_ci#else
3362306a36Sopenharmony_ci#define ldm_debug(f, a...) _ldm_printk (KERN_DEBUG, __func__, f, ##a)
3462306a36Sopenharmony_ci#endif
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define ldm_crit(f, a...)  _ldm_printk (KERN_CRIT,  __func__, f, ##a)
3762306a36Sopenharmony_ci#define ldm_error(f, a...) _ldm_printk (KERN_ERR,   __func__, f, ##a)
3862306a36Sopenharmony_ci#define ldm_info(f, a...)  _ldm_printk (KERN_INFO,  __func__, f, ##a)
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic __printf(3, 4)
4162306a36Sopenharmony_civoid _ldm_printk(const char *level, const char *function, const char *fmt, ...)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	struct va_format vaf;
4462306a36Sopenharmony_ci	va_list args;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	va_start (args, fmt);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	vaf.fmt = fmt;
4962306a36Sopenharmony_ci	vaf.va = &args;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	printk("%s%s(): %pV\n", level, function, &vaf);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	va_end(args);
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/**
5762306a36Sopenharmony_ci * ldm_parse_privhead - Read the LDM Database PRIVHEAD structure
5862306a36Sopenharmony_ci * @data:  Raw database PRIVHEAD structure loaded from the device
5962306a36Sopenharmony_ci * @ph:    In-memory privhead structure in which to return parsed information
6062306a36Sopenharmony_ci *
6162306a36Sopenharmony_ci * This parses the LDM database PRIVHEAD structure supplied in @data and
6262306a36Sopenharmony_ci * sets up the in-memory privhead structure @ph with the obtained information.
6362306a36Sopenharmony_ci *
6462306a36Sopenharmony_ci * Return:  'true'   @ph contains the PRIVHEAD data
6562306a36Sopenharmony_ci *          'false'  @ph contents are undefined
6662306a36Sopenharmony_ci */
6762306a36Sopenharmony_cistatic bool ldm_parse_privhead(const u8 *data, struct privhead *ph)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	bool is_vista = false;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	BUG_ON(!data || !ph);
7262306a36Sopenharmony_ci	if (MAGIC_PRIVHEAD != get_unaligned_be64(data)) {
7362306a36Sopenharmony_ci		ldm_error("Cannot find PRIVHEAD structure. LDM database is"
7462306a36Sopenharmony_ci			" corrupt. Aborting.");
7562306a36Sopenharmony_ci		return false;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci	ph->ver_major = get_unaligned_be16(data + 0x000C);
7862306a36Sopenharmony_ci	ph->ver_minor = get_unaligned_be16(data + 0x000E);
7962306a36Sopenharmony_ci	ph->logical_disk_start = get_unaligned_be64(data + 0x011B);
8062306a36Sopenharmony_ci	ph->logical_disk_size = get_unaligned_be64(data + 0x0123);
8162306a36Sopenharmony_ci	ph->config_start = get_unaligned_be64(data + 0x012B);
8262306a36Sopenharmony_ci	ph->config_size = get_unaligned_be64(data + 0x0133);
8362306a36Sopenharmony_ci	/* Version 2.11 is Win2k/XP and version 2.12 is Vista. */
8462306a36Sopenharmony_ci	if (ph->ver_major == 2 && ph->ver_minor == 12)
8562306a36Sopenharmony_ci		is_vista = true;
8662306a36Sopenharmony_ci	if (!is_vista && (ph->ver_major != 2 || ph->ver_minor != 11)) {
8762306a36Sopenharmony_ci		ldm_error("Expected PRIVHEAD version 2.11 or 2.12, got %d.%d."
8862306a36Sopenharmony_ci			" Aborting.", ph->ver_major, ph->ver_minor);
8962306a36Sopenharmony_ci		return false;
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci	ldm_debug("PRIVHEAD version %d.%d (Windows %s).", ph->ver_major,
9262306a36Sopenharmony_ci			ph->ver_minor, is_vista ? "Vista" : "2000/XP");
9362306a36Sopenharmony_ci	if (ph->config_size != LDM_DB_SIZE) {	/* 1 MiB in sectors. */
9462306a36Sopenharmony_ci		/* Warn the user and continue, carefully. */
9562306a36Sopenharmony_ci		ldm_info("Database is normally %u bytes, it claims to "
9662306a36Sopenharmony_ci			"be %llu bytes.", LDM_DB_SIZE,
9762306a36Sopenharmony_ci			(unsigned long long)ph->config_size);
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci	if ((ph->logical_disk_size == 0) || (ph->logical_disk_start +
10062306a36Sopenharmony_ci			ph->logical_disk_size > ph->config_start)) {
10162306a36Sopenharmony_ci		ldm_error("PRIVHEAD disk size doesn't match real disk size");
10262306a36Sopenharmony_ci		return false;
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci	if (uuid_parse(data + 0x0030, &ph->disk_id)) {
10562306a36Sopenharmony_ci		ldm_error("PRIVHEAD contains an invalid GUID.");
10662306a36Sopenharmony_ci		return false;
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci	ldm_debug("Parsed PRIVHEAD successfully.");
10962306a36Sopenharmony_ci	return true;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci/**
11362306a36Sopenharmony_ci * ldm_parse_tocblock - Read the LDM Database TOCBLOCK structure
11462306a36Sopenharmony_ci * @data:  Raw database TOCBLOCK structure loaded from the device
11562306a36Sopenharmony_ci * @toc:   In-memory toc structure in which to return parsed information
11662306a36Sopenharmony_ci *
11762306a36Sopenharmony_ci * This parses the LDM Database TOCBLOCK (table of contents) structure supplied
11862306a36Sopenharmony_ci * in @data and sets up the in-memory tocblock structure @toc with the obtained
11962306a36Sopenharmony_ci * information.
12062306a36Sopenharmony_ci *
12162306a36Sopenharmony_ci * N.B.  The *_start and *_size values returned in @toc are not range-checked.
12262306a36Sopenharmony_ci *
12362306a36Sopenharmony_ci * Return:  'true'   @toc contains the TOCBLOCK data
12462306a36Sopenharmony_ci *          'false'  @toc contents are undefined
12562306a36Sopenharmony_ci */
12662306a36Sopenharmony_cistatic bool ldm_parse_tocblock (const u8 *data, struct tocblock *toc)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	BUG_ON (!data || !toc);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (MAGIC_TOCBLOCK != get_unaligned_be64(data)) {
13162306a36Sopenharmony_ci		ldm_crit ("Cannot find TOCBLOCK, database may be corrupt.");
13262306a36Sopenharmony_ci		return false;
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci	strncpy (toc->bitmap1_name, data + 0x24, sizeof (toc->bitmap1_name));
13562306a36Sopenharmony_ci	toc->bitmap1_name[sizeof (toc->bitmap1_name) - 1] = 0;
13662306a36Sopenharmony_ci	toc->bitmap1_start = get_unaligned_be64(data + 0x2E);
13762306a36Sopenharmony_ci	toc->bitmap1_size  = get_unaligned_be64(data + 0x36);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	if (strncmp (toc->bitmap1_name, TOC_BITMAP1,
14062306a36Sopenharmony_ci			sizeof (toc->bitmap1_name)) != 0) {
14162306a36Sopenharmony_ci		ldm_crit ("TOCBLOCK's first bitmap is '%s', should be '%s'.",
14262306a36Sopenharmony_ci				TOC_BITMAP1, toc->bitmap1_name);
14362306a36Sopenharmony_ci		return false;
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci	strncpy (toc->bitmap2_name, data + 0x46, sizeof (toc->bitmap2_name));
14662306a36Sopenharmony_ci	toc->bitmap2_name[sizeof (toc->bitmap2_name) - 1] = 0;
14762306a36Sopenharmony_ci	toc->bitmap2_start = get_unaligned_be64(data + 0x50);
14862306a36Sopenharmony_ci	toc->bitmap2_size  = get_unaligned_be64(data + 0x58);
14962306a36Sopenharmony_ci	if (strncmp (toc->bitmap2_name, TOC_BITMAP2,
15062306a36Sopenharmony_ci			sizeof (toc->bitmap2_name)) != 0) {
15162306a36Sopenharmony_ci		ldm_crit ("TOCBLOCK's second bitmap is '%s', should be '%s'.",
15262306a36Sopenharmony_ci				TOC_BITMAP2, toc->bitmap2_name);
15362306a36Sopenharmony_ci		return false;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci	ldm_debug ("Parsed TOCBLOCK successfully.");
15662306a36Sopenharmony_ci	return true;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci/**
16062306a36Sopenharmony_ci * ldm_parse_vmdb - Read the LDM Database VMDB structure
16162306a36Sopenharmony_ci * @data:  Raw database VMDB structure loaded from the device
16262306a36Sopenharmony_ci * @vm:    In-memory vmdb structure in which to return parsed information
16362306a36Sopenharmony_ci *
16462306a36Sopenharmony_ci * This parses the LDM Database VMDB structure supplied in @data and sets up
16562306a36Sopenharmony_ci * the in-memory vmdb structure @vm with the obtained information.
16662306a36Sopenharmony_ci *
16762306a36Sopenharmony_ci * N.B.  The *_start, *_size and *_seq values will be range-checked later.
16862306a36Sopenharmony_ci *
16962306a36Sopenharmony_ci * Return:  'true'   @vm contains VMDB info
17062306a36Sopenharmony_ci *          'false'  @vm contents are undefined
17162306a36Sopenharmony_ci */
17262306a36Sopenharmony_cistatic bool ldm_parse_vmdb (const u8 *data, struct vmdb *vm)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	BUG_ON (!data || !vm);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	if (MAGIC_VMDB != get_unaligned_be32(data)) {
17762306a36Sopenharmony_ci		ldm_crit ("Cannot find the VMDB, database may be corrupt.");
17862306a36Sopenharmony_ci		return false;
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	vm->ver_major = get_unaligned_be16(data + 0x12);
18262306a36Sopenharmony_ci	vm->ver_minor = get_unaligned_be16(data + 0x14);
18362306a36Sopenharmony_ci	if ((vm->ver_major != 4) || (vm->ver_minor != 10)) {
18462306a36Sopenharmony_ci		ldm_error ("Expected VMDB version %d.%d, got %d.%d. "
18562306a36Sopenharmony_ci			"Aborting.", 4, 10, vm->ver_major, vm->ver_minor);
18662306a36Sopenharmony_ci		return false;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	vm->vblk_size     = get_unaligned_be32(data + 0x08);
19062306a36Sopenharmony_ci	if (vm->vblk_size == 0) {
19162306a36Sopenharmony_ci		ldm_error ("Illegal VBLK size");
19262306a36Sopenharmony_ci		return false;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	vm->vblk_offset   = get_unaligned_be32(data + 0x0C);
19662306a36Sopenharmony_ci	vm->last_vblk_seq = get_unaligned_be32(data + 0x04);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	ldm_debug ("Parsed VMDB successfully.");
19962306a36Sopenharmony_ci	return true;
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci/**
20362306a36Sopenharmony_ci * ldm_compare_privheads - Compare two privhead objects
20462306a36Sopenharmony_ci * @ph1:  First privhead
20562306a36Sopenharmony_ci * @ph2:  Second privhead
20662306a36Sopenharmony_ci *
20762306a36Sopenharmony_ci * This compares the two privhead structures @ph1 and @ph2.
20862306a36Sopenharmony_ci *
20962306a36Sopenharmony_ci * Return:  'true'   Identical
21062306a36Sopenharmony_ci *          'false'  Different
21162306a36Sopenharmony_ci */
21262306a36Sopenharmony_cistatic bool ldm_compare_privheads (const struct privhead *ph1,
21362306a36Sopenharmony_ci				   const struct privhead *ph2)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	BUG_ON (!ph1 || !ph2);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	return ((ph1->ver_major          == ph2->ver_major)		&&
21862306a36Sopenharmony_ci		(ph1->ver_minor          == ph2->ver_minor)		&&
21962306a36Sopenharmony_ci		(ph1->logical_disk_start == ph2->logical_disk_start)	&&
22062306a36Sopenharmony_ci		(ph1->logical_disk_size  == ph2->logical_disk_size)	&&
22162306a36Sopenharmony_ci		(ph1->config_start       == ph2->config_start)		&&
22262306a36Sopenharmony_ci		(ph1->config_size        == ph2->config_size)		&&
22362306a36Sopenharmony_ci		uuid_equal(&ph1->disk_id, &ph2->disk_id));
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci/**
22762306a36Sopenharmony_ci * ldm_compare_tocblocks - Compare two tocblock objects
22862306a36Sopenharmony_ci * @toc1:  First toc
22962306a36Sopenharmony_ci * @toc2:  Second toc
23062306a36Sopenharmony_ci *
23162306a36Sopenharmony_ci * This compares the two tocblock structures @toc1 and @toc2.
23262306a36Sopenharmony_ci *
23362306a36Sopenharmony_ci * Return:  'true'   Identical
23462306a36Sopenharmony_ci *          'false'  Different
23562306a36Sopenharmony_ci */
23662306a36Sopenharmony_cistatic bool ldm_compare_tocblocks (const struct tocblock *toc1,
23762306a36Sopenharmony_ci				   const struct tocblock *toc2)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	BUG_ON (!toc1 || !toc2);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	return ((toc1->bitmap1_start == toc2->bitmap1_start)	&&
24262306a36Sopenharmony_ci		(toc1->bitmap1_size  == toc2->bitmap1_size)	&&
24362306a36Sopenharmony_ci		(toc1->bitmap2_start == toc2->bitmap2_start)	&&
24462306a36Sopenharmony_ci		(toc1->bitmap2_size  == toc2->bitmap2_size)	&&
24562306a36Sopenharmony_ci		!strncmp (toc1->bitmap1_name, toc2->bitmap1_name,
24662306a36Sopenharmony_ci			sizeof (toc1->bitmap1_name))		&&
24762306a36Sopenharmony_ci		!strncmp (toc1->bitmap2_name, toc2->bitmap2_name,
24862306a36Sopenharmony_ci			sizeof (toc1->bitmap2_name)));
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci/**
25262306a36Sopenharmony_ci * ldm_validate_privheads - Compare the primary privhead with its backups
25362306a36Sopenharmony_ci * @state: Partition check state including device holding the LDM Database
25462306a36Sopenharmony_ci * @ph1:   Memory struct to fill with ph contents
25562306a36Sopenharmony_ci *
25662306a36Sopenharmony_ci * Read and compare all three privheads from disk.
25762306a36Sopenharmony_ci *
25862306a36Sopenharmony_ci * The privheads on disk show the size and location of the main disk area and
25962306a36Sopenharmony_ci * the configuration area (the database).  The values are range-checked against
26062306a36Sopenharmony_ci * @hd, which contains the real size of the disk.
26162306a36Sopenharmony_ci *
26262306a36Sopenharmony_ci * Return:  'true'   Success
26362306a36Sopenharmony_ci *          'false'  Error
26462306a36Sopenharmony_ci */
26562306a36Sopenharmony_cistatic bool ldm_validate_privheads(struct parsed_partitions *state,
26662306a36Sopenharmony_ci				   struct privhead *ph1)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	static const int off[3] = { OFF_PRIV1, OFF_PRIV2, OFF_PRIV3 };
26962306a36Sopenharmony_ci	struct privhead *ph[3] = { ph1 };
27062306a36Sopenharmony_ci	Sector sect;
27162306a36Sopenharmony_ci	u8 *data;
27262306a36Sopenharmony_ci	bool result = false;
27362306a36Sopenharmony_ci	long num_sects;
27462306a36Sopenharmony_ci	int i;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	BUG_ON (!state || !ph1);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	ph[1] = kmalloc (sizeof (*ph[1]), GFP_KERNEL);
27962306a36Sopenharmony_ci	ph[2] = kmalloc (sizeof (*ph[2]), GFP_KERNEL);
28062306a36Sopenharmony_ci	if (!ph[1] || !ph[2]) {
28162306a36Sopenharmony_ci		ldm_crit ("Out of memory.");
28262306a36Sopenharmony_ci		goto out;
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	/* off[1 & 2] are relative to ph[0]->config_start */
28662306a36Sopenharmony_ci	ph[0]->config_start = 0;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	/* Read and parse privheads */
28962306a36Sopenharmony_ci	for (i = 0; i < 3; i++) {
29062306a36Sopenharmony_ci		data = read_part_sector(state, ph[0]->config_start + off[i],
29162306a36Sopenharmony_ci					&sect);
29262306a36Sopenharmony_ci		if (!data) {
29362306a36Sopenharmony_ci			ldm_crit ("Disk read failed.");
29462306a36Sopenharmony_ci			goto out;
29562306a36Sopenharmony_ci		}
29662306a36Sopenharmony_ci		result = ldm_parse_privhead (data, ph[i]);
29762306a36Sopenharmony_ci		put_dev_sector (sect);
29862306a36Sopenharmony_ci		if (!result) {
29962306a36Sopenharmony_ci			ldm_error ("Cannot find PRIVHEAD %d.", i+1); /* Log again */
30062306a36Sopenharmony_ci			if (i < 2)
30162306a36Sopenharmony_ci				goto out;	/* Already logged */
30262306a36Sopenharmony_ci			else
30362306a36Sopenharmony_ci				break;	/* FIXME ignore for now, 3rd PH can fail on odd-sized disks */
30462306a36Sopenharmony_ci		}
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	num_sects = get_capacity(state->disk);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	if ((ph[0]->config_start > num_sects) ||
31062306a36Sopenharmony_ci	   ((ph[0]->config_start + ph[0]->config_size) > num_sects)) {
31162306a36Sopenharmony_ci		ldm_crit ("Database extends beyond the end of the disk.");
31262306a36Sopenharmony_ci		goto out;
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	if ((ph[0]->logical_disk_start > ph[0]->config_start) ||
31662306a36Sopenharmony_ci	   ((ph[0]->logical_disk_start + ph[0]->logical_disk_size)
31762306a36Sopenharmony_ci		    > ph[0]->config_start)) {
31862306a36Sopenharmony_ci		ldm_crit ("Disk and database overlap.");
31962306a36Sopenharmony_ci		goto out;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	if (!ldm_compare_privheads (ph[0], ph[1])) {
32362306a36Sopenharmony_ci		ldm_crit ("Primary and backup PRIVHEADs don't match.");
32462306a36Sopenharmony_ci		goto out;
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci	/* FIXME ignore this for now
32762306a36Sopenharmony_ci	if (!ldm_compare_privheads (ph[0], ph[2])) {
32862306a36Sopenharmony_ci		ldm_crit ("Primary and backup PRIVHEADs don't match.");
32962306a36Sopenharmony_ci		goto out;
33062306a36Sopenharmony_ci	}*/
33162306a36Sopenharmony_ci	ldm_debug ("Validated PRIVHEADs successfully.");
33262306a36Sopenharmony_ci	result = true;
33362306a36Sopenharmony_ciout:
33462306a36Sopenharmony_ci	kfree (ph[1]);
33562306a36Sopenharmony_ci	kfree (ph[2]);
33662306a36Sopenharmony_ci	return result;
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci/**
34062306a36Sopenharmony_ci * ldm_validate_tocblocks - Validate the table of contents and its backups
34162306a36Sopenharmony_ci * @state: Partition check state including device holding the LDM Database
34262306a36Sopenharmony_ci * @base:  Offset, into @state->disk, of the database
34362306a36Sopenharmony_ci * @ldb:   Cache of the database structures
34462306a36Sopenharmony_ci *
34562306a36Sopenharmony_ci * Find and compare the four tables of contents of the LDM Database stored on
34662306a36Sopenharmony_ci * @state->disk and return the parsed information into @toc1.
34762306a36Sopenharmony_ci *
34862306a36Sopenharmony_ci * The offsets and sizes of the configs are range-checked against a privhead.
34962306a36Sopenharmony_ci *
35062306a36Sopenharmony_ci * Return:  'true'   @toc1 contains validated TOCBLOCK info
35162306a36Sopenharmony_ci *          'false'  @toc1 contents are undefined
35262306a36Sopenharmony_ci */
35362306a36Sopenharmony_cistatic bool ldm_validate_tocblocks(struct parsed_partitions *state,
35462306a36Sopenharmony_ci				   unsigned long base, struct ldmdb *ldb)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	static const int off[4] = { OFF_TOCB1, OFF_TOCB2, OFF_TOCB3, OFF_TOCB4};
35762306a36Sopenharmony_ci	struct tocblock *tb[4];
35862306a36Sopenharmony_ci	struct privhead *ph;
35962306a36Sopenharmony_ci	Sector sect;
36062306a36Sopenharmony_ci	u8 *data;
36162306a36Sopenharmony_ci	int i, nr_tbs;
36262306a36Sopenharmony_ci	bool result = false;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	BUG_ON(!state || !ldb);
36562306a36Sopenharmony_ci	ph = &ldb->ph;
36662306a36Sopenharmony_ci	tb[0] = &ldb->toc;
36762306a36Sopenharmony_ci	tb[1] = kmalloc_array(3, sizeof(*tb[1]), GFP_KERNEL);
36862306a36Sopenharmony_ci	if (!tb[1]) {
36962306a36Sopenharmony_ci		ldm_crit("Out of memory.");
37062306a36Sopenharmony_ci		goto err;
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci	tb[2] = (struct tocblock*)((u8*)tb[1] + sizeof(*tb[1]));
37362306a36Sopenharmony_ci	tb[3] = (struct tocblock*)((u8*)tb[2] + sizeof(*tb[2]));
37462306a36Sopenharmony_ci	/*
37562306a36Sopenharmony_ci	 * Try to read and parse all four TOCBLOCKs.
37662306a36Sopenharmony_ci	 *
37762306a36Sopenharmony_ci	 * Windows Vista LDM v2.12 does not always have all four TOCBLOCKs so
37862306a36Sopenharmony_ci	 * skip any that fail as long as we get at least one valid TOCBLOCK.
37962306a36Sopenharmony_ci	 */
38062306a36Sopenharmony_ci	for (nr_tbs = i = 0; i < 4; i++) {
38162306a36Sopenharmony_ci		data = read_part_sector(state, base + off[i], &sect);
38262306a36Sopenharmony_ci		if (!data) {
38362306a36Sopenharmony_ci			ldm_error("Disk read failed for TOCBLOCK %d.", i);
38462306a36Sopenharmony_ci			continue;
38562306a36Sopenharmony_ci		}
38662306a36Sopenharmony_ci		if (ldm_parse_tocblock(data, tb[nr_tbs]))
38762306a36Sopenharmony_ci			nr_tbs++;
38862306a36Sopenharmony_ci		put_dev_sector(sect);
38962306a36Sopenharmony_ci	}
39062306a36Sopenharmony_ci	if (!nr_tbs) {
39162306a36Sopenharmony_ci		ldm_crit("Failed to find a valid TOCBLOCK.");
39262306a36Sopenharmony_ci		goto err;
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci	/* Range check the TOCBLOCK against a privhead. */
39562306a36Sopenharmony_ci	if (((tb[0]->bitmap1_start + tb[0]->bitmap1_size) > ph->config_size) ||
39662306a36Sopenharmony_ci			((tb[0]->bitmap2_start + tb[0]->bitmap2_size) >
39762306a36Sopenharmony_ci			ph->config_size)) {
39862306a36Sopenharmony_ci		ldm_crit("The bitmaps are out of range.  Giving up.");
39962306a36Sopenharmony_ci		goto err;
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci	/* Compare all loaded TOCBLOCKs. */
40262306a36Sopenharmony_ci	for (i = 1; i < nr_tbs; i++) {
40362306a36Sopenharmony_ci		if (!ldm_compare_tocblocks(tb[0], tb[i])) {
40462306a36Sopenharmony_ci			ldm_crit("TOCBLOCKs 0 and %d do not match.", i);
40562306a36Sopenharmony_ci			goto err;
40662306a36Sopenharmony_ci		}
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci	ldm_debug("Validated %d TOCBLOCKs successfully.", nr_tbs);
40962306a36Sopenharmony_ci	result = true;
41062306a36Sopenharmony_cierr:
41162306a36Sopenharmony_ci	kfree(tb[1]);
41262306a36Sopenharmony_ci	return result;
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci/**
41662306a36Sopenharmony_ci * ldm_validate_vmdb - Read the VMDB and validate it
41762306a36Sopenharmony_ci * @state: Partition check state including device holding the LDM Database
41862306a36Sopenharmony_ci * @base:  Offset, into @bdev, of the database
41962306a36Sopenharmony_ci * @ldb:   Cache of the database structures
42062306a36Sopenharmony_ci *
42162306a36Sopenharmony_ci * Find the vmdb of the LDM Database stored on @bdev and return the parsed
42262306a36Sopenharmony_ci * information in @ldb.
42362306a36Sopenharmony_ci *
42462306a36Sopenharmony_ci * Return:  'true'   @ldb contains validated VBDB info
42562306a36Sopenharmony_ci *          'false'  @ldb contents are undefined
42662306a36Sopenharmony_ci */
42762306a36Sopenharmony_cistatic bool ldm_validate_vmdb(struct parsed_partitions *state,
42862306a36Sopenharmony_ci			      unsigned long base, struct ldmdb *ldb)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	Sector sect;
43162306a36Sopenharmony_ci	u8 *data;
43262306a36Sopenharmony_ci	bool result = false;
43362306a36Sopenharmony_ci	struct vmdb *vm;
43462306a36Sopenharmony_ci	struct tocblock *toc;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	BUG_ON (!state || !ldb);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	vm  = &ldb->vm;
43962306a36Sopenharmony_ci	toc = &ldb->toc;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	data = read_part_sector(state, base + OFF_VMDB, &sect);
44262306a36Sopenharmony_ci	if (!data) {
44362306a36Sopenharmony_ci		ldm_crit ("Disk read failed.");
44462306a36Sopenharmony_ci		return false;
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	if (!ldm_parse_vmdb (data, vm))
44862306a36Sopenharmony_ci		goto out;				/* Already logged */
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	/* Are there uncommitted transactions? */
45162306a36Sopenharmony_ci	if (get_unaligned_be16(data + 0x10) != 0x01) {
45262306a36Sopenharmony_ci		ldm_crit ("Database is not in a consistent state.  Aborting.");
45362306a36Sopenharmony_ci		goto out;
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	if (vm->vblk_offset != 512)
45762306a36Sopenharmony_ci		ldm_info ("VBLKs start at offset 0x%04x.", vm->vblk_offset);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	/*
46062306a36Sopenharmony_ci	 * The last_vblkd_seq can be before the end of the vmdb, just make sure
46162306a36Sopenharmony_ci	 * it is not out of bounds.
46262306a36Sopenharmony_ci	 */
46362306a36Sopenharmony_ci	if ((vm->vblk_size * vm->last_vblk_seq) > (toc->bitmap1_size << 9)) {
46462306a36Sopenharmony_ci		ldm_crit ("VMDB exceeds allowed size specified by TOCBLOCK.  "
46562306a36Sopenharmony_ci				"Database is corrupt.  Aborting.");
46662306a36Sopenharmony_ci		goto out;
46762306a36Sopenharmony_ci	}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	result = true;
47062306a36Sopenharmony_ciout:
47162306a36Sopenharmony_ci	put_dev_sector (sect);
47262306a36Sopenharmony_ci	return result;
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci/**
47762306a36Sopenharmony_ci * ldm_validate_partition_table - Determine whether bdev might be a dynamic disk
47862306a36Sopenharmony_ci * @state: Partition check state including device holding the LDM Database
47962306a36Sopenharmony_ci *
48062306a36Sopenharmony_ci * This function provides a weak test to decide whether the device is a dynamic
48162306a36Sopenharmony_ci * disk or not.  It looks for an MS-DOS-style partition table containing at
48262306a36Sopenharmony_ci * least one partition of type 0x42 (formerly SFS, now used by Windows for
48362306a36Sopenharmony_ci * dynamic disks).
48462306a36Sopenharmony_ci *
48562306a36Sopenharmony_ci * N.B.  The only possible error can come from the read_part_sector and that is
48662306a36Sopenharmony_ci *       only likely to happen if the underlying device is strange.  If that IS
48762306a36Sopenharmony_ci *       the case we should return zero to let someone else try.
48862306a36Sopenharmony_ci *
48962306a36Sopenharmony_ci * Return:  'true'   @state->disk is a dynamic disk
49062306a36Sopenharmony_ci *          'false'  @state->disk is not a dynamic disk, or an error occurred
49162306a36Sopenharmony_ci */
49262306a36Sopenharmony_cistatic bool ldm_validate_partition_table(struct parsed_partitions *state)
49362306a36Sopenharmony_ci{
49462306a36Sopenharmony_ci	Sector sect;
49562306a36Sopenharmony_ci	u8 *data;
49662306a36Sopenharmony_ci	struct msdos_partition *p;
49762306a36Sopenharmony_ci	int i;
49862306a36Sopenharmony_ci	bool result = false;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	BUG_ON(!state);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	data = read_part_sector(state, 0, &sect);
50362306a36Sopenharmony_ci	if (!data) {
50462306a36Sopenharmony_ci		ldm_info ("Disk read failed.");
50562306a36Sopenharmony_ci		return false;
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	if (*(__le16*) (data + 0x01FE) != cpu_to_le16 (MSDOS_LABEL_MAGIC))
50962306a36Sopenharmony_ci		goto out;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	p = (struct msdos_partition *)(data + 0x01BE);
51262306a36Sopenharmony_ci	for (i = 0; i < 4; i++, p++)
51362306a36Sopenharmony_ci		if (p->sys_ind == LDM_PARTITION) {
51462306a36Sopenharmony_ci			result = true;
51562306a36Sopenharmony_ci			break;
51662306a36Sopenharmony_ci		}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	if (result)
51962306a36Sopenharmony_ci		ldm_debug ("Found W2K dynamic disk partition type.");
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ciout:
52262306a36Sopenharmony_ci	put_dev_sector (sect);
52362306a36Sopenharmony_ci	return result;
52462306a36Sopenharmony_ci}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci/**
52762306a36Sopenharmony_ci * ldm_get_disk_objid - Search a linked list of vblk's for a given Disk Id
52862306a36Sopenharmony_ci * @ldb:  Cache of the database structures
52962306a36Sopenharmony_ci *
53062306a36Sopenharmony_ci * The LDM Database contains a list of all partitions on all dynamic disks.
53162306a36Sopenharmony_ci * The primary PRIVHEAD, at the beginning of the physical disk, tells us
53262306a36Sopenharmony_ci * the GUID of this disk.  This function searches for the GUID in a linked
53362306a36Sopenharmony_ci * list of vblk's.
53462306a36Sopenharmony_ci *
53562306a36Sopenharmony_ci * Return:  Pointer, A matching vblk was found
53662306a36Sopenharmony_ci *          NULL,    No match, or an error
53762306a36Sopenharmony_ci */
53862306a36Sopenharmony_cistatic struct vblk * ldm_get_disk_objid (const struct ldmdb *ldb)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	struct list_head *item;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	BUG_ON (!ldb);
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	list_for_each (item, &ldb->v_disk) {
54562306a36Sopenharmony_ci		struct vblk *v = list_entry (item, struct vblk, list);
54662306a36Sopenharmony_ci		if (uuid_equal(&v->vblk.disk.disk_id, &ldb->ph.disk_id))
54762306a36Sopenharmony_ci			return v;
54862306a36Sopenharmony_ci	}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	return NULL;
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci/**
55462306a36Sopenharmony_ci * ldm_create_data_partitions - Create data partitions for this device
55562306a36Sopenharmony_ci * @pp:   List of the partitions parsed so far
55662306a36Sopenharmony_ci * @ldb:  Cache of the database structures
55762306a36Sopenharmony_ci *
55862306a36Sopenharmony_ci * The database contains ALL the partitions for ALL disk groups, so we need to
55962306a36Sopenharmony_ci * filter out this specific disk. Using the disk's object id, we can find all
56062306a36Sopenharmony_ci * the partitions in the database that belong to this disk.
56162306a36Sopenharmony_ci *
56262306a36Sopenharmony_ci * Add each partition in our database, to the parsed_partitions structure.
56362306a36Sopenharmony_ci *
56462306a36Sopenharmony_ci * N.B.  This function creates the partitions in the order it finds partition
56562306a36Sopenharmony_ci *       objects in the linked list.
56662306a36Sopenharmony_ci *
56762306a36Sopenharmony_ci * Return:  'true'   Partition created
56862306a36Sopenharmony_ci *          'false'  Error, probably a range checking problem
56962306a36Sopenharmony_ci */
57062306a36Sopenharmony_cistatic bool ldm_create_data_partitions (struct parsed_partitions *pp,
57162306a36Sopenharmony_ci					const struct ldmdb *ldb)
57262306a36Sopenharmony_ci{
57362306a36Sopenharmony_ci	struct list_head *item;
57462306a36Sopenharmony_ci	struct vblk *vb;
57562306a36Sopenharmony_ci	struct vblk *disk;
57662306a36Sopenharmony_ci	struct vblk_part *part;
57762306a36Sopenharmony_ci	int part_num = 1;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	BUG_ON (!pp || !ldb);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	disk = ldm_get_disk_objid (ldb);
58262306a36Sopenharmony_ci	if (!disk) {
58362306a36Sopenharmony_ci		ldm_crit ("Can't find the ID of this disk in the database.");
58462306a36Sopenharmony_ci		return false;
58562306a36Sopenharmony_ci	}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	strlcat(pp->pp_buf, " [LDM]", PAGE_SIZE);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	/* Create the data partitions */
59062306a36Sopenharmony_ci	list_for_each (item, &ldb->v_part) {
59162306a36Sopenharmony_ci		vb = list_entry (item, struct vblk, list);
59262306a36Sopenharmony_ci		part = &vb->vblk.part;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci		if (part->disk_id != disk->obj_id)
59562306a36Sopenharmony_ci			continue;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci		put_partition (pp, part_num, ldb->ph.logical_disk_start +
59862306a36Sopenharmony_ci				part->start, part->size);
59962306a36Sopenharmony_ci		part_num++;
60062306a36Sopenharmony_ci	}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	strlcat(pp->pp_buf, "\n", PAGE_SIZE);
60362306a36Sopenharmony_ci	return true;
60462306a36Sopenharmony_ci}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci/**
60862306a36Sopenharmony_ci * ldm_relative - Calculate the next relative offset
60962306a36Sopenharmony_ci * @buffer:  Block of data being worked on
61062306a36Sopenharmony_ci * @buflen:  Size of the block of data
61162306a36Sopenharmony_ci * @base:    Size of the previous fixed width fields
61262306a36Sopenharmony_ci * @offset:  Cumulative size of the previous variable-width fields
61362306a36Sopenharmony_ci *
61462306a36Sopenharmony_ci * Because many of the VBLK fields are variable-width, it's necessary
61562306a36Sopenharmony_ci * to calculate each offset based on the previous one and the length
61662306a36Sopenharmony_ci * of the field it pointed to.
61762306a36Sopenharmony_ci *
61862306a36Sopenharmony_ci * Return:  -1 Error, the calculated offset exceeded the size of the buffer
61962306a36Sopenharmony_ci *           n OK, a range-checked offset into buffer
62062306a36Sopenharmony_ci */
62162306a36Sopenharmony_cistatic int ldm_relative(const u8 *buffer, int buflen, int base, int offset)
62262306a36Sopenharmony_ci{
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	base += offset;
62562306a36Sopenharmony_ci	if (!buffer || offset < 0 || base > buflen) {
62662306a36Sopenharmony_ci		if (!buffer)
62762306a36Sopenharmony_ci			ldm_error("!buffer");
62862306a36Sopenharmony_ci		if (offset < 0)
62962306a36Sopenharmony_ci			ldm_error("offset (%d) < 0", offset);
63062306a36Sopenharmony_ci		if (base > buflen)
63162306a36Sopenharmony_ci			ldm_error("base (%d) > buflen (%d)", base, buflen);
63262306a36Sopenharmony_ci		return -1;
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci	if (base + buffer[base] >= buflen) {
63562306a36Sopenharmony_ci		ldm_error("base (%d) + buffer[base] (%d) >= buflen (%d)", base,
63662306a36Sopenharmony_ci				buffer[base], buflen);
63762306a36Sopenharmony_ci		return -1;
63862306a36Sopenharmony_ci	}
63962306a36Sopenharmony_ci	return buffer[base] + offset + 1;
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci/**
64362306a36Sopenharmony_ci * ldm_get_vnum - Convert a variable-width, big endian number, into cpu order
64462306a36Sopenharmony_ci * @block:  Pointer to the variable-width number to convert
64562306a36Sopenharmony_ci *
64662306a36Sopenharmony_ci * Large numbers in the LDM Database are often stored in a packed format.  Each
64762306a36Sopenharmony_ci * number is prefixed by a one byte width marker.  All numbers in the database
64862306a36Sopenharmony_ci * are stored in big-endian byte order.  This function reads one of these
64962306a36Sopenharmony_ci * numbers and returns the result
65062306a36Sopenharmony_ci *
65162306a36Sopenharmony_ci * N.B.  This function DOES NOT perform any range checking, though the most
65262306a36Sopenharmony_ci *       it will read is eight bytes.
65362306a36Sopenharmony_ci *
65462306a36Sopenharmony_ci * Return:  n A number
65562306a36Sopenharmony_ci *          0 Zero, or an error occurred
65662306a36Sopenharmony_ci */
65762306a36Sopenharmony_cistatic u64 ldm_get_vnum (const u8 *block)
65862306a36Sopenharmony_ci{
65962306a36Sopenharmony_ci	u64 tmp = 0;
66062306a36Sopenharmony_ci	u8 length;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	BUG_ON (!block);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	length = *block++;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	if (length && length <= 8)
66762306a36Sopenharmony_ci		while (length--)
66862306a36Sopenharmony_ci			tmp = (tmp << 8) | *block++;
66962306a36Sopenharmony_ci	else
67062306a36Sopenharmony_ci		ldm_error ("Illegal length %d.", length);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	return tmp;
67362306a36Sopenharmony_ci}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci/**
67662306a36Sopenharmony_ci * ldm_get_vstr - Read a length-prefixed string into a buffer
67762306a36Sopenharmony_ci * @block:   Pointer to the length marker
67862306a36Sopenharmony_ci * @buffer:  Location to copy string to
67962306a36Sopenharmony_ci * @buflen:  Size of the output buffer
68062306a36Sopenharmony_ci *
68162306a36Sopenharmony_ci * Many of the strings in the LDM Database are not NULL terminated.  Instead
68262306a36Sopenharmony_ci * they are prefixed by a one byte length marker.  This function copies one of
68362306a36Sopenharmony_ci * these strings into a buffer.
68462306a36Sopenharmony_ci *
68562306a36Sopenharmony_ci * N.B.  This function DOES NOT perform any range checking on the input.
68662306a36Sopenharmony_ci *       If the buffer is too small, the output will be truncated.
68762306a36Sopenharmony_ci *
68862306a36Sopenharmony_ci * Return:  0, Error and @buffer contents are undefined
68962306a36Sopenharmony_ci *          n, String length in characters (excluding NULL)
69062306a36Sopenharmony_ci *          buflen-1, String was truncated.
69162306a36Sopenharmony_ci */
69262306a36Sopenharmony_cistatic int ldm_get_vstr (const u8 *block, u8 *buffer, int buflen)
69362306a36Sopenharmony_ci{
69462306a36Sopenharmony_ci	int length;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	BUG_ON (!block || !buffer);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	length = block[0];
69962306a36Sopenharmony_ci	if (length >= buflen) {
70062306a36Sopenharmony_ci		ldm_error ("Truncating string %d -> %d.", length, buflen);
70162306a36Sopenharmony_ci		length = buflen - 1;
70262306a36Sopenharmony_ci	}
70362306a36Sopenharmony_ci	memcpy (buffer, block + 1, length);
70462306a36Sopenharmony_ci	buffer[length] = 0;
70562306a36Sopenharmony_ci	return length;
70662306a36Sopenharmony_ci}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci/**
71062306a36Sopenharmony_ci * ldm_parse_cmp3 - Read a raw VBLK Component object into a vblk structure
71162306a36Sopenharmony_ci * @buffer:  Block of data being worked on
71262306a36Sopenharmony_ci * @buflen:  Size of the block of data
71362306a36Sopenharmony_ci * @vb:      In-memory vblk in which to return information
71462306a36Sopenharmony_ci *
71562306a36Sopenharmony_ci * Read a raw VBLK Component object (version 3) into a vblk structure.
71662306a36Sopenharmony_ci *
71762306a36Sopenharmony_ci * Return:  'true'   @vb contains a Component VBLK
71862306a36Sopenharmony_ci *          'false'  @vb contents are not defined
71962306a36Sopenharmony_ci */
72062306a36Sopenharmony_cistatic bool ldm_parse_cmp3 (const u8 *buffer, int buflen, struct vblk *vb)
72162306a36Sopenharmony_ci{
72262306a36Sopenharmony_ci	int r_objid, r_name, r_vstate, r_child, r_parent, r_stripe, r_cols, len;
72362306a36Sopenharmony_ci	struct vblk_comp *comp;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	BUG_ON (!buffer || !vb);
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	r_objid  = ldm_relative (buffer, buflen, 0x18, 0);
72862306a36Sopenharmony_ci	r_name   = ldm_relative (buffer, buflen, 0x18, r_objid);
72962306a36Sopenharmony_ci	r_vstate = ldm_relative (buffer, buflen, 0x18, r_name);
73062306a36Sopenharmony_ci	r_child  = ldm_relative (buffer, buflen, 0x1D, r_vstate);
73162306a36Sopenharmony_ci	r_parent = ldm_relative (buffer, buflen, 0x2D, r_child);
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	if (buffer[0x12] & VBLK_FLAG_COMP_STRIPE) {
73462306a36Sopenharmony_ci		r_stripe = ldm_relative (buffer, buflen, 0x2E, r_parent);
73562306a36Sopenharmony_ci		r_cols   = ldm_relative (buffer, buflen, 0x2E, r_stripe);
73662306a36Sopenharmony_ci		len = r_cols;
73762306a36Sopenharmony_ci	} else {
73862306a36Sopenharmony_ci		r_stripe = 0;
73962306a36Sopenharmony_ci		len = r_parent;
74062306a36Sopenharmony_ci	}
74162306a36Sopenharmony_ci	if (len < 0)
74262306a36Sopenharmony_ci		return false;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	len += VBLK_SIZE_CMP3;
74562306a36Sopenharmony_ci	if (len != get_unaligned_be32(buffer + 0x14))
74662306a36Sopenharmony_ci		return false;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	comp = &vb->vblk.comp;
74962306a36Sopenharmony_ci	ldm_get_vstr (buffer + 0x18 + r_name, comp->state,
75062306a36Sopenharmony_ci		sizeof (comp->state));
75162306a36Sopenharmony_ci	comp->type      = buffer[0x18 + r_vstate];
75262306a36Sopenharmony_ci	comp->children  = ldm_get_vnum (buffer + 0x1D + r_vstate);
75362306a36Sopenharmony_ci	comp->parent_id = ldm_get_vnum (buffer + 0x2D + r_child);
75462306a36Sopenharmony_ci	comp->chunksize = r_stripe ? ldm_get_vnum (buffer+r_parent+0x2E) : 0;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	return true;
75762306a36Sopenharmony_ci}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci/**
76062306a36Sopenharmony_ci * ldm_parse_dgr3 - Read a raw VBLK Disk Group object into a vblk structure
76162306a36Sopenharmony_ci * @buffer:  Block of data being worked on
76262306a36Sopenharmony_ci * @buflen:  Size of the block of data
76362306a36Sopenharmony_ci * @vb:      In-memory vblk in which to return information
76462306a36Sopenharmony_ci *
76562306a36Sopenharmony_ci * Read a raw VBLK Disk Group object (version 3) into a vblk structure.
76662306a36Sopenharmony_ci *
76762306a36Sopenharmony_ci * Return:  'true'   @vb contains a Disk Group VBLK
76862306a36Sopenharmony_ci *          'false'  @vb contents are not defined
76962306a36Sopenharmony_ci */
77062306a36Sopenharmony_cistatic int ldm_parse_dgr3 (const u8 *buffer, int buflen, struct vblk *vb)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	int r_objid, r_name, r_diskid, r_id1, r_id2, len;
77362306a36Sopenharmony_ci	struct vblk_dgrp *dgrp;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	BUG_ON (!buffer || !vb);
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	r_objid  = ldm_relative (buffer, buflen, 0x18, 0);
77862306a36Sopenharmony_ci	r_name   = ldm_relative (buffer, buflen, 0x18, r_objid);
77962306a36Sopenharmony_ci	r_diskid = ldm_relative (buffer, buflen, 0x18, r_name);
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	if (buffer[0x12] & VBLK_FLAG_DGR3_IDS) {
78262306a36Sopenharmony_ci		r_id1 = ldm_relative (buffer, buflen, 0x24, r_diskid);
78362306a36Sopenharmony_ci		r_id2 = ldm_relative (buffer, buflen, 0x24, r_id1);
78462306a36Sopenharmony_ci		len = r_id2;
78562306a36Sopenharmony_ci	} else
78662306a36Sopenharmony_ci		len = r_diskid;
78762306a36Sopenharmony_ci	if (len < 0)
78862306a36Sopenharmony_ci		return false;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	len += VBLK_SIZE_DGR3;
79162306a36Sopenharmony_ci	if (len != get_unaligned_be32(buffer + 0x14))
79262306a36Sopenharmony_ci		return false;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	dgrp = &vb->vblk.dgrp;
79562306a36Sopenharmony_ci	ldm_get_vstr (buffer + 0x18 + r_name, dgrp->disk_id,
79662306a36Sopenharmony_ci		sizeof (dgrp->disk_id));
79762306a36Sopenharmony_ci	return true;
79862306a36Sopenharmony_ci}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci/**
80162306a36Sopenharmony_ci * ldm_parse_dgr4 - Read a raw VBLK Disk Group object into a vblk structure
80262306a36Sopenharmony_ci * @buffer:  Block of data being worked on
80362306a36Sopenharmony_ci * @buflen:  Size of the block of data
80462306a36Sopenharmony_ci * @vb:      In-memory vblk in which to return information
80562306a36Sopenharmony_ci *
80662306a36Sopenharmony_ci * Read a raw VBLK Disk Group object (version 4) into a vblk structure.
80762306a36Sopenharmony_ci *
80862306a36Sopenharmony_ci * Return:  'true'   @vb contains a Disk Group VBLK
80962306a36Sopenharmony_ci *          'false'  @vb contents are not defined
81062306a36Sopenharmony_ci */
81162306a36Sopenharmony_cistatic bool ldm_parse_dgr4 (const u8 *buffer, int buflen, struct vblk *vb)
81262306a36Sopenharmony_ci{
81362306a36Sopenharmony_ci	char buf[64];
81462306a36Sopenharmony_ci	int r_objid, r_name, r_id1, r_id2, len;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	BUG_ON (!buffer || !vb);
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	r_objid  = ldm_relative (buffer, buflen, 0x18, 0);
81962306a36Sopenharmony_ci	r_name   = ldm_relative (buffer, buflen, 0x18, r_objid);
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	if (buffer[0x12] & VBLK_FLAG_DGR4_IDS) {
82262306a36Sopenharmony_ci		r_id1 = ldm_relative (buffer, buflen, 0x44, r_name);
82362306a36Sopenharmony_ci		r_id2 = ldm_relative (buffer, buflen, 0x44, r_id1);
82462306a36Sopenharmony_ci		len = r_id2;
82562306a36Sopenharmony_ci	} else
82662306a36Sopenharmony_ci		len = r_name;
82762306a36Sopenharmony_ci	if (len < 0)
82862306a36Sopenharmony_ci		return false;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	len += VBLK_SIZE_DGR4;
83162306a36Sopenharmony_ci	if (len != get_unaligned_be32(buffer + 0x14))
83262306a36Sopenharmony_ci		return false;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	ldm_get_vstr (buffer + 0x18 + r_objid, buf, sizeof (buf));
83562306a36Sopenharmony_ci	return true;
83662306a36Sopenharmony_ci}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci/**
83962306a36Sopenharmony_ci * ldm_parse_dsk3 - Read a raw VBLK Disk object into a vblk structure
84062306a36Sopenharmony_ci * @buffer:  Block of data being worked on
84162306a36Sopenharmony_ci * @buflen:  Size of the block of data
84262306a36Sopenharmony_ci * @vb:      In-memory vblk in which to return information
84362306a36Sopenharmony_ci *
84462306a36Sopenharmony_ci * Read a raw VBLK Disk object (version 3) into a vblk structure.
84562306a36Sopenharmony_ci *
84662306a36Sopenharmony_ci * Return:  'true'   @vb contains a Disk VBLK
84762306a36Sopenharmony_ci *          'false'  @vb contents are not defined
84862306a36Sopenharmony_ci */
84962306a36Sopenharmony_cistatic bool ldm_parse_dsk3 (const u8 *buffer, int buflen, struct vblk *vb)
85062306a36Sopenharmony_ci{
85162306a36Sopenharmony_ci	int r_objid, r_name, r_diskid, r_altname, len;
85262306a36Sopenharmony_ci	struct vblk_disk *disk;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	BUG_ON (!buffer || !vb);
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	r_objid   = ldm_relative (buffer, buflen, 0x18, 0);
85762306a36Sopenharmony_ci	r_name    = ldm_relative (buffer, buflen, 0x18, r_objid);
85862306a36Sopenharmony_ci	r_diskid  = ldm_relative (buffer, buflen, 0x18, r_name);
85962306a36Sopenharmony_ci	r_altname = ldm_relative (buffer, buflen, 0x18, r_diskid);
86062306a36Sopenharmony_ci	len = r_altname;
86162306a36Sopenharmony_ci	if (len < 0)
86262306a36Sopenharmony_ci		return false;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	len += VBLK_SIZE_DSK3;
86562306a36Sopenharmony_ci	if (len != get_unaligned_be32(buffer + 0x14))
86662306a36Sopenharmony_ci		return false;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	disk = &vb->vblk.disk;
86962306a36Sopenharmony_ci	ldm_get_vstr (buffer + 0x18 + r_diskid, disk->alt_name,
87062306a36Sopenharmony_ci		sizeof (disk->alt_name));
87162306a36Sopenharmony_ci	if (uuid_parse(buffer + 0x19 + r_name, &disk->disk_id))
87262306a36Sopenharmony_ci		return false;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	return true;
87562306a36Sopenharmony_ci}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci/**
87862306a36Sopenharmony_ci * ldm_parse_dsk4 - Read a raw VBLK Disk object into a vblk structure
87962306a36Sopenharmony_ci * @buffer:  Block of data being worked on
88062306a36Sopenharmony_ci * @buflen:  Size of the block of data
88162306a36Sopenharmony_ci * @vb:      In-memory vblk in which to return information
88262306a36Sopenharmony_ci *
88362306a36Sopenharmony_ci * Read a raw VBLK Disk object (version 4) into a vblk structure.
88462306a36Sopenharmony_ci *
88562306a36Sopenharmony_ci * Return:  'true'   @vb contains a Disk VBLK
88662306a36Sopenharmony_ci *          'false'  @vb contents are not defined
88762306a36Sopenharmony_ci */
88862306a36Sopenharmony_cistatic bool ldm_parse_dsk4 (const u8 *buffer, int buflen, struct vblk *vb)
88962306a36Sopenharmony_ci{
89062306a36Sopenharmony_ci	int r_objid, r_name, len;
89162306a36Sopenharmony_ci	struct vblk_disk *disk;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	BUG_ON (!buffer || !vb);
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	r_objid = ldm_relative (buffer, buflen, 0x18, 0);
89662306a36Sopenharmony_ci	r_name  = ldm_relative (buffer, buflen, 0x18, r_objid);
89762306a36Sopenharmony_ci	len     = r_name;
89862306a36Sopenharmony_ci	if (len < 0)
89962306a36Sopenharmony_ci		return false;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	len += VBLK_SIZE_DSK4;
90262306a36Sopenharmony_ci	if (len != get_unaligned_be32(buffer + 0x14))
90362306a36Sopenharmony_ci		return false;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	disk = &vb->vblk.disk;
90662306a36Sopenharmony_ci	import_uuid(&disk->disk_id, buffer + 0x18 + r_name);
90762306a36Sopenharmony_ci	return true;
90862306a36Sopenharmony_ci}
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci/**
91162306a36Sopenharmony_ci * ldm_parse_prt3 - Read a raw VBLK Partition object into a vblk structure
91262306a36Sopenharmony_ci * @buffer:  Block of data being worked on
91362306a36Sopenharmony_ci * @buflen:  Size of the block of data
91462306a36Sopenharmony_ci * @vb:      In-memory vblk in which to return information
91562306a36Sopenharmony_ci *
91662306a36Sopenharmony_ci * Read a raw VBLK Partition object (version 3) into a vblk structure.
91762306a36Sopenharmony_ci *
91862306a36Sopenharmony_ci * Return:  'true'   @vb contains a Partition VBLK
91962306a36Sopenharmony_ci *          'false'  @vb contents are not defined
92062306a36Sopenharmony_ci */
92162306a36Sopenharmony_cistatic bool ldm_parse_prt3(const u8 *buffer, int buflen, struct vblk *vb)
92262306a36Sopenharmony_ci{
92362306a36Sopenharmony_ci	int r_objid, r_name, r_size, r_parent, r_diskid, r_index, len;
92462306a36Sopenharmony_ci	struct vblk_part *part;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	BUG_ON(!buffer || !vb);
92762306a36Sopenharmony_ci	r_objid = ldm_relative(buffer, buflen, 0x18, 0);
92862306a36Sopenharmony_ci	if (r_objid < 0) {
92962306a36Sopenharmony_ci		ldm_error("r_objid %d < 0", r_objid);
93062306a36Sopenharmony_ci		return false;
93162306a36Sopenharmony_ci	}
93262306a36Sopenharmony_ci	r_name = ldm_relative(buffer, buflen, 0x18, r_objid);
93362306a36Sopenharmony_ci	if (r_name < 0) {
93462306a36Sopenharmony_ci		ldm_error("r_name %d < 0", r_name);
93562306a36Sopenharmony_ci		return false;
93662306a36Sopenharmony_ci	}
93762306a36Sopenharmony_ci	r_size = ldm_relative(buffer, buflen, 0x34, r_name);
93862306a36Sopenharmony_ci	if (r_size < 0) {
93962306a36Sopenharmony_ci		ldm_error("r_size %d < 0", r_size);
94062306a36Sopenharmony_ci		return false;
94162306a36Sopenharmony_ci	}
94262306a36Sopenharmony_ci	r_parent = ldm_relative(buffer, buflen, 0x34, r_size);
94362306a36Sopenharmony_ci	if (r_parent < 0) {
94462306a36Sopenharmony_ci		ldm_error("r_parent %d < 0", r_parent);
94562306a36Sopenharmony_ci		return false;
94662306a36Sopenharmony_ci	}
94762306a36Sopenharmony_ci	r_diskid = ldm_relative(buffer, buflen, 0x34, r_parent);
94862306a36Sopenharmony_ci	if (r_diskid < 0) {
94962306a36Sopenharmony_ci		ldm_error("r_diskid %d < 0", r_diskid);
95062306a36Sopenharmony_ci		return false;
95162306a36Sopenharmony_ci	}
95262306a36Sopenharmony_ci	if (buffer[0x12] & VBLK_FLAG_PART_INDEX) {
95362306a36Sopenharmony_ci		r_index = ldm_relative(buffer, buflen, 0x34, r_diskid);
95462306a36Sopenharmony_ci		if (r_index < 0) {
95562306a36Sopenharmony_ci			ldm_error("r_index %d < 0", r_index);
95662306a36Sopenharmony_ci			return false;
95762306a36Sopenharmony_ci		}
95862306a36Sopenharmony_ci		len = r_index;
95962306a36Sopenharmony_ci	} else
96062306a36Sopenharmony_ci		len = r_diskid;
96162306a36Sopenharmony_ci	if (len < 0) {
96262306a36Sopenharmony_ci		ldm_error("len %d < 0", len);
96362306a36Sopenharmony_ci		return false;
96462306a36Sopenharmony_ci	}
96562306a36Sopenharmony_ci	len += VBLK_SIZE_PRT3;
96662306a36Sopenharmony_ci	if (len > get_unaligned_be32(buffer + 0x14)) {
96762306a36Sopenharmony_ci		ldm_error("len %d > BE32(buffer + 0x14) %d", len,
96862306a36Sopenharmony_ci				get_unaligned_be32(buffer + 0x14));
96962306a36Sopenharmony_ci		return false;
97062306a36Sopenharmony_ci	}
97162306a36Sopenharmony_ci	part = &vb->vblk.part;
97262306a36Sopenharmony_ci	part->start = get_unaligned_be64(buffer + 0x24 + r_name);
97362306a36Sopenharmony_ci	part->volume_offset = get_unaligned_be64(buffer + 0x2C + r_name);
97462306a36Sopenharmony_ci	part->size = ldm_get_vnum(buffer + 0x34 + r_name);
97562306a36Sopenharmony_ci	part->parent_id = ldm_get_vnum(buffer + 0x34 + r_size);
97662306a36Sopenharmony_ci	part->disk_id = ldm_get_vnum(buffer + 0x34 + r_parent);
97762306a36Sopenharmony_ci	if (vb->flags & VBLK_FLAG_PART_INDEX)
97862306a36Sopenharmony_ci		part->partnum = buffer[0x35 + r_diskid];
97962306a36Sopenharmony_ci	else
98062306a36Sopenharmony_ci		part->partnum = 0;
98162306a36Sopenharmony_ci	return true;
98262306a36Sopenharmony_ci}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci/**
98562306a36Sopenharmony_ci * ldm_parse_vol5 - Read a raw VBLK Volume object into a vblk structure
98662306a36Sopenharmony_ci * @buffer:  Block of data being worked on
98762306a36Sopenharmony_ci * @buflen:  Size of the block of data
98862306a36Sopenharmony_ci * @vb:      In-memory vblk in which to return information
98962306a36Sopenharmony_ci *
99062306a36Sopenharmony_ci * Read a raw VBLK Volume object (version 5) into a vblk structure.
99162306a36Sopenharmony_ci *
99262306a36Sopenharmony_ci * Return:  'true'   @vb contains a Volume VBLK
99362306a36Sopenharmony_ci *          'false'  @vb contents are not defined
99462306a36Sopenharmony_ci */
99562306a36Sopenharmony_cistatic bool ldm_parse_vol5(const u8 *buffer, int buflen, struct vblk *vb)
99662306a36Sopenharmony_ci{
99762306a36Sopenharmony_ci	int r_objid, r_name, r_vtype, r_disable_drive_letter, r_child, r_size;
99862306a36Sopenharmony_ci	int r_id1, r_id2, r_size2, r_drive, len;
99962306a36Sopenharmony_ci	struct vblk_volu *volu;
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	BUG_ON(!buffer || !vb);
100262306a36Sopenharmony_ci	r_objid = ldm_relative(buffer, buflen, 0x18, 0);
100362306a36Sopenharmony_ci	if (r_objid < 0) {
100462306a36Sopenharmony_ci		ldm_error("r_objid %d < 0", r_objid);
100562306a36Sopenharmony_ci		return false;
100662306a36Sopenharmony_ci	}
100762306a36Sopenharmony_ci	r_name = ldm_relative(buffer, buflen, 0x18, r_objid);
100862306a36Sopenharmony_ci	if (r_name < 0) {
100962306a36Sopenharmony_ci		ldm_error("r_name %d < 0", r_name);
101062306a36Sopenharmony_ci		return false;
101162306a36Sopenharmony_ci	}
101262306a36Sopenharmony_ci	r_vtype = ldm_relative(buffer, buflen, 0x18, r_name);
101362306a36Sopenharmony_ci	if (r_vtype < 0) {
101462306a36Sopenharmony_ci		ldm_error("r_vtype %d < 0", r_vtype);
101562306a36Sopenharmony_ci		return false;
101662306a36Sopenharmony_ci	}
101762306a36Sopenharmony_ci	r_disable_drive_letter = ldm_relative(buffer, buflen, 0x18, r_vtype);
101862306a36Sopenharmony_ci	if (r_disable_drive_letter < 0) {
101962306a36Sopenharmony_ci		ldm_error("r_disable_drive_letter %d < 0",
102062306a36Sopenharmony_ci				r_disable_drive_letter);
102162306a36Sopenharmony_ci		return false;
102262306a36Sopenharmony_ci	}
102362306a36Sopenharmony_ci	r_child = ldm_relative(buffer, buflen, 0x2D, r_disable_drive_letter);
102462306a36Sopenharmony_ci	if (r_child < 0) {
102562306a36Sopenharmony_ci		ldm_error("r_child %d < 0", r_child);
102662306a36Sopenharmony_ci		return false;
102762306a36Sopenharmony_ci	}
102862306a36Sopenharmony_ci	r_size = ldm_relative(buffer, buflen, 0x3D, r_child);
102962306a36Sopenharmony_ci	if (r_size < 0) {
103062306a36Sopenharmony_ci		ldm_error("r_size %d < 0", r_size);
103162306a36Sopenharmony_ci		return false;
103262306a36Sopenharmony_ci	}
103362306a36Sopenharmony_ci	if (buffer[0x12] & VBLK_FLAG_VOLU_ID1) {
103462306a36Sopenharmony_ci		r_id1 = ldm_relative(buffer, buflen, 0x52, r_size);
103562306a36Sopenharmony_ci		if (r_id1 < 0) {
103662306a36Sopenharmony_ci			ldm_error("r_id1 %d < 0", r_id1);
103762306a36Sopenharmony_ci			return false;
103862306a36Sopenharmony_ci		}
103962306a36Sopenharmony_ci	} else
104062306a36Sopenharmony_ci		r_id1 = r_size;
104162306a36Sopenharmony_ci	if (buffer[0x12] & VBLK_FLAG_VOLU_ID2) {
104262306a36Sopenharmony_ci		r_id2 = ldm_relative(buffer, buflen, 0x52, r_id1);
104362306a36Sopenharmony_ci		if (r_id2 < 0) {
104462306a36Sopenharmony_ci			ldm_error("r_id2 %d < 0", r_id2);
104562306a36Sopenharmony_ci			return false;
104662306a36Sopenharmony_ci		}
104762306a36Sopenharmony_ci	} else
104862306a36Sopenharmony_ci		r_id2 = r_id1;
104962306a36Sopenharmony_ci	if (buffer[0x12] & VBLK_FLAG_VOLU_SIZE) {
105062306a36Sopenharmony_ci		r_size2 = ldm_relative(buffer, buflen, 0x52, r_id2);
105162306a36Sopenharmony_ci		if (r_size2 < 0) {
105262306a36Sopenharmony_ci			ldm_error("r_size2 %d < 0", r_size2);
105362306a36Sopenharmony_ci			return false;
105462306a36Sopenharmony_ci		}
105562306a36Sopenharmony_ci	} else
105662306a36Sopenharmony_ci		r_size2 = r_id2;
105762306a36Sopenharmony_ci	if (buffer[0x12] & VBLK_FLAG_VOLU_DRIVE) {
105862306a36Sopenharmony_ci		r_drive = ldm_relative(buffer, buflen, 0x52, r_size2);
105962306a36Sopenharmony_ci		if (r_drive < 0) {
106062306a36Sopenharmony_ci			ldm_error("r_drive %d < 0", r_drive);
106162306a36Sopenharmony_ci			return false;
106262306a36Sopenharmony_ci		}
106362306a36Sopenharmony_ci	} else
106462306a36Sopenharmony_ci		r_drive = r_size2;
106562306a36Sopenharmony_ci	len = r_drive;
106662306a36Sopenharmony_ci	if (len < 0) {
106762306a36Sopenharmony_ci		ldm_error("len %d < 0", len);
106862306a36Sopenharmony_ci		return false;
106962306a36Sopenharmony_ci	}
107062306a36Sopenharmony_ci	len += VBLK_SIZE_VOL5;
107162306a36Sopenharmony_ci	if (len > get_unaligned_be32(buffer + 0x14)) {
107262306a36Sopenharmony_ci		ldm_error("len %d > BE32(buffer + 0x14) %d", len,
107362306a36Sopenharmony_ci				get_unaligned_be32(buffer + 0x14));
107462306a36Sopenharmony_ci		return false;
107562306a36Sopenharmony_ci	}
107662306a36Sopenharmony_ci	volu = &vb->vblk.volu;
107762306a36Sopenharmony_ci	ldm_get_vstr(buffer + 0x18 + r_name, volu->volume_type,
107862306a36Sopenharmony_ci			sizeof(volu->volume_type));
107962306a36Sopenharmony_ci	memcpy(volu->volume_state, buffer + 0x18 + r_disable_drive_letter,
108062306a36Sopenharmony_ci			sizeof(volu->volume_state));
108162306a36Sopenharmony_ci	volu->size = ldm_get_vnum(buffer + 0x3D + r_child);
108262306a36Sopenharmony_ci	volu->partition_type = buffer[0x41 + r_size];
108362306a36Sopenharmony_ci	memcpy(volu->guid, buffer + 0x42 + r_size, sizeof(volu->guid));
108462306a36Sopenharmony_ci	if (buffer[0x12] & VBLK_FLAG_VOLU_DRIVE) {
108562306a36Sopenharmony_ci		ldm_get_vstr(buffer + 0x52 + r_size, volu->drive_hint,
108662306a36Sopenharmony_ci				sizeof(volu->drive_hint));
108762306a36Sopenharmony_ci	}
108862306a36Sopenharmony_ci	return true;
108962306a36Sopenharmony_ci}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci/**
109262306a36Sopenharmony_ci * ldm_parse_vblk - Read a raw VBLK object into a vblk structure
109362306a36Sopenharmony_ci * @buf:  Block of data being worked on
109462306a36Sopenharmony_ci * @len:  Size of the block of data
109562306a36Sopenharmony_ci * @vb:   In-memory vblk in which to return information
109662306a36Sopenharmony_ci *
109762306a36Sopenharmony_ci * Read a raw VBLK object into a vblk structure.  This function just reads the
109862306a36Sopenharmony_ci * information common to all VBLK types, then delegates the rest of the work to
109962306a36Sopenharmony_ci * helper functions: ldm_parse_*.
110062306a36Sopenharmony_ci *
110162306a36Sopenharmony_ci * Return:  'true'   @vb contains a VBLK
110262306a36Sopenharmony_ci *          'false'  @vb contents are not defined
110362306a36Sopenharmony_ci */
110462306a36Sopenharmony_cistatic bool ldm_parse_vblk (const u8 *buf, int len, struct vblk *vb)
110562306a36Sopenharmony_ci{
110662306a36Sopenharmony_ci	bool result = false;
110762306a36Sopenharmony_ci	int r_objid;
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	BUG_ON (!buf || !vb);
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	r_objid = ldm_relative (buf, len, 0x18, 0);
111262306a36Sopenharmony_ci	if (r_objid < 0) {
111362306a36Sopenharmony_ci		ldm_error ("VBLK header is corrupt.");
111462306a36Sopenharmony_ci		return false;
111562306a36Sopenharmony_ci	}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	vb->flags  = buf[0x12];
111862306a36Sopenharmony_ci	vb->type   = buf[0x13];
111962306a36Sopenharmony_ci	vb->obj_id = ldm_get_vnum (buf + 0x18);
112062306a36Sopenharmony_ci	ldm_get_vstr (buf+0x18+r_objid, vb->name, sizeof (vb->name));
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	switch (vb->type) {
112362306a36Sopenharmony_ci		case VBLK_CMP3:  result = ldm_parse_cmp3 (buf, len, vb); break;
112462306a36Sopenharmony_ci		case VBLK_DSK3:  result = ldm_parse_dsk3 (buf, len, vb); break;
112562306a36Sopenharmony_ci		case VBLK_DSK4:  result = ldm_parse_dsk4 (buf, len, vb); break;
112662306a36Sopenharmony_ci		case VBLK_DGR3:  result = ldm_parse_dgr3 (buf, len, vb); break;
112762306a36Sopenharmony_ci		case VBLK_DGR4:  result = ldm_parse_dgr4 (buf, len, vb); break;
112862306a36Sopenharmony_ci		case VBLK_PRT3:  result = ldm_parse_prt3 (buf, len, vb); break;
112962306a36Sopenharmony_ci		case VBLK_VOL5:  result = ldm_parse_vol5 (buf, len, vb); break;
113062306a36Sopenharmony_ci	}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	if (result)
113362306a36Sopenharmony_ci		ldm_debug ("Parsed VBLK 0x%llx (type: 0x%02x) ok.",
113462306a36Sopenharmony_ci			 (unsigned long long) vb->obj_id, vb->type);
113562306a36Sopenharmony_ci	else
113662306a36Sopenharmony_ci		ldm_error ("Failed to parse VBLK 0x%llx (type: 0x%02x).",
113762306a36Sopenharmony_ci			(unsigned long long) vb->obj_id, vb->type);
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	return result;
114062306a36Sopenharmony_ci}
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci/**
114462306a36Sopenharmony_ci * ldm_ldmdb_add - Adds a raw VBLK entry to the ldmdb database
114562306a36Sopenharmony_ci * @data:  Raw VBLK to add to the database
114662306a36Sopenharmony_ci * @len:   Size of the raw VBLK
114762306a36Sopenharmony_ci * @ldb:   Cache of the database structures
114862306a36Sopenharmony_ci *
114962306a36Sopenharmony_ci * The VBLKs are sorted into categories.  Partitions are also sorted by offset.
115062306a36Sopenharmony_ci *
115162306a36Sopenharmony_ci * N.B.  This function does not check the validity of the VBLKs.
115262306a36Sopenharmony_ci *
115362306a36Sopenharmony_ci * Return:  'true'   The VBLK was added
115462306a36Sopenharmony_ci *          'false'  An error occurred
115562306a36Sopenharmony_ci */
115662306a36Sopenharmony_cistatic bool ldm_ldmdb_add (u8 *data, int len, struct ldmdb *ldb)
115762306a36Sopenharmony_ci{
115862306a36Sopenharmony_ci	struct vblk *vb;
115962306a36Sopenharmony_ci	struct list_head *item;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	BUG_ON (!data || !ldb);
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	vb = kmalloc (sizeof (*vb), GFP_KERNEL);
116462306a36Sopenharmony_ci	if (!vb) {
116562306a36Sopenharmony_ci		ldm_crit ("Out of memory.");
116662306a36Sopenharmony_ci		return false;
116762306a36Sopenharmony_ci	}
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	if (!ldm_parse_vblk (data, len, vb)) {
117062306a36Sopenharmony_ci		kfree(vb);
117162306a36Sopenharmony_ci		return false;			/* Already logged */
117262306a36Sopenharmony_ci	}
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	/* Put vblk into the correct list. */
117562306a36Sopenharmony_ci	switch (vb->type) {
117662306a36Sopenharmony_ci	case VBLK_DGR3:
117762306a36Sopenharmony_ci	case VBLK_DGR4:
117862306a36Sopenharmony_ci		list_add (&vb->list, &ldb->v_dgrp);
117962306a36Sopenharmony_ci		break;
118062306a36Sopenharmony_ci	case VBLK_DSK3:
118162306a36Sopenharmony_ci	case VBLK_DSK4:
118262306a36Sopenharmony_ci		list_add (&vb->list, &ldb->v_disk);
118362306a36Sopenharmony_ci		break;
118462306a36Sopenharmony_ci	case VBLK_VOL5:
118562306a36Sopenharmony_ci		list_add (&vb->list, &ldb->v_volu);
118662306a36Sopenharmony_ci		break;
118762306a36Sopenharmony_ci	case VBLK_CMP3:
118862306a36Sopenharmony_ci		list_add (&vb->list, &ldb->v_comp);
118962306a36Sopenharmony_ci		break;
119062306a36Sopenharmony_ci	case VBLK_PRT3:
119162306a36Sopenharmony_ci		/* Sort by the partition's start sector. */
119262306a36Sopenharmony_ci		list_for_each (item, &ldb->v_part) {
119362306a36Sopenharmony_ci			struct vblk *v = list_entry (item, struct vblk, list);
119462306a36Sopenharmony_ci			if ((v->vblk.part.disk_id == vb->vblk.part.disk_id) &&
119562306a36Sopenharmony_ci			    (v->vblk.part.start > vb->vblk.part.start)) {
119662306a36Sopenharmony_ci				list_add_tail (&vb->list, &v->list);
119762306a36Sopenharmony_ci				return true;
119862306a36Sopenharmony_ci			}
119962306a36Sopenharmony_ci		}
120062306a36Sopenharmony_ci		list_add_tail (&vb->list, &ldb->v_part);
120162306a36Sopenharmony_ci		break;
120262306a36Sopenharmony_ci	}
120362306a36Sopenharmony_ci	return true;
120462306a36Sopenharmony_ci}
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci/**
120762306a36Sopenharmony_ci * ldm_frag_add - Add a VBLK fragment to a list
120862306a36Sopenharmony_ci * @data:   Raw fragment to be added to the list
120962306a36Sopenharmony_ci * @size:   Size of the raw fragment
121062306a36Sopenharmony_ci * @frags:  Linked list of VBLK fragments
121162306a36Sopenharmony_ci *
121262306a36Sopenharmony_ci * Fragmented VBLKs may not be consecutive in the database, so they are placed
121362306a36Sopenharmony_ci * in a list so they can be pieced together later.
121462306a36Sopenharmony_ci *
121562306a36Sopenharmony_ci * Return:  'true'   Success, the VBLK was added to the list
121662306a36Sopenharmony_ci *          'false'  Error, a problem occurred
121762306a36Sopenharmony_ci */
121862306a36Sopenharmony_cistatic bool ldm_frag_add (const u8 *data, int size, struct list_head *frags)
121962306a36Sopenharmony_ci{
122062306a36Sopenharmony_ci	struct frag *f;
122162306a36Sopenharmony_ci	struct list_head *item;
122262306a36Sopenharmony_ci	int rec, num, group;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	BUG_ON (!data || !frags);
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	if (size < 2 * VBLK_SIZE_HEAD) {
122762306a36Sopenharmony_ci		ldm_error("Value of size is too small.");
122862306a36Sopenharmony_ci		return false;
122962306a36Sopenharmony_ci	}
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	group = get_unaligned_be32(data + 0x08);
123262306a36Sopenharmony_ci	rec   = get_unaligned_be16(data + 0x0C);
123362306a36Sopenharmony_ci	num   = get_unaligned_be16(data + 0x0E);
123462306a36Sopenharmony_ci	if ((num < 1) || (num > 4)) {
123562306a36Sopenharmony_ci		ldm_error ("A VBLK claims to have %d parts.", num);
123662306a36Sopenharmony_ci		return false;
123762306a36Sopenharmony_ci	}
123862306a36Sopenharmony_ci	if (rec >= num) {
123962306a36Sopenharmony_ci		ldm_error("REC value (%d) exceeds NUM value (%d)", rec, num);
124062306a36Sopenharmony_ci		return false;
124162306a36Sopenharmony_ci	}
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	list_for_each (item, frags) {
124462306a36Sopenharmony_ci		f = list_entry (item, struct frag, list);
124562306a36Sopenharmony_ci		if (f->group == group)
124662306a36Sopenharmony_ci			goto found;
124762306a36Sopenharmony_ci	}
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	f = kmalloc (sizeof (*f) + size*num, GFP_KERNEL);
125062306a36Sopenharmony_ci	if (!f) {
125162306a36Sopenharmony_ci		ldm_crit ("Out of memory.");
125262306a36Sopenharmony_ci		return false;
125362306a36Sopenharmony_ci	}
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	f->group = group;
125662306a36Sopenharmony_ci	f->num   = num;
125762306a36Sopenharmony_ci	f->rec   = rec;
125862306a36Sopenharmony_ci	f->map   = 0xFF << num;
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	list_add_tail (&f->list, frags);
126162306a36Sopenharmony_cifound:
126262306a36Sopenharmony_ci	if (rec >= f->num) {
126362306a36Sopenharmony_ci		ldm_error("REC value (%d) exceeds NUM value (%d)", rec, f->num);
126462306a36Sopenharmony_ci		return false;
126562306a36Sopenharmony_ci	}
126662306a36Sopenharmony_ci	if (f->map & (1 << rec)) {
126762306a36Sopenharmony_ci		ldm_error ("Duplicate VBLK, part %d.", rec);
126862306a36Sopenharmony_ci		f->map &= 0x7F;			/* Mark the group as broken */
126962306a36Sopenharmony_ci		return false;
127062306a36Sopenharmony_ci	}
127162306a36Sopenharmony_ci	f->map |= (1 << rec);
127262306a36Sopenharmony_ci	if (!rec)
127362306a36Sopenharmony_ci		memcpy(f->data, data, VBLK_SIZE_HEAD);
127462306a36Sopenharmony_ci	data += VBLK_SIZE_HEAD;
127562306a36Sopenharmony_ci	size -= VBLK_SIZE_HEAD;
127662306a36Sopenharmony_ci	memcpy(f->data + VBLK_SIZE_HEAD + rec * size, data, size);
127762306a36Sopenharmony_ci	return true;
127862306a36Sopenharmony_ci}
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci/**
128162306a36Sopenharmony_ci * ldm_frag_free - Free a linked list of VBLK fragments
128262306a36Sopenharmony_ci * @list:  Linked list of fragments
128362306a36Sopenharmony_ci *
128462306a36Sopenharmony_ci * Free a linked list of VBLK fragments
128562306a36Sopenharmony_ci *
128662306a36Sopenharmony_ci * Return:  none
128762306a36Sopenharmony_ci */
128862306a36Sopenharmony_cistatic void ldm_frag_free (struct list_head *list)
128962306a36Sopenharmony_ci{
129062306a36Sopenharmony_ci	struct list_head *item, *tmp;
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	BUG_ON (!list);
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	list_for_each_safe (item, tmp, list)
129562306a36Sopenharmony_ci		kfree (list_entry (item, struct frag, list));
129662306a36Sopenharmony_ci}
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci/**
129962306a36Sopenharmony_ci * ldm_frag_commit - Validate fragmented VBLKs and add them to the database
130062306a36Sopenharmony_ci * @frags:  Linked list of VBLK fragments
130162306a36Sopenharmony_ci * @ldb:    Cache of the database structures
130262306a36Sopenharmony_ci *
130362306a36Sopenharmony_ci * Now that all the fragmented VBLKs have been collected, they must be added to
130462306a36Sopenharmony_ci * the database for later use.
130562306a36Sopenharmony_ci *
130662306a36Sopenharmony_ci * Return:  'true'   All the fragments we added successfully
130762306a36Sopenharmony_ci *          'false'  One or more of the fragments we invalid
130862306a36Sopenharmony_ci */
130962306a36Sopenharmony_cistatic bool ldm_frag_commit (struct list_head *frags, struct ldmdb *ldb)
131062306a36Sopenharmony_ci{
131162306a36Sopenharmony_ci	struct frag *f;
131262306a36Sopenharmony_ci	struct list_head *item;
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	BUG_ON (!frags || !ldb);
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	list_for_each (item, frags) {
131762306a36Sopenharmony_ci		f = list_entry (item, struct frag, list);
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci		if (f->map != 0xFF) {
132062306a36Sopenharmony_ci			ldm_error ("VBLK group %d is incomplete (0x%02x).",
132162306a36Sopenharmony_ci				f->group, f->map);
132262306a36Sopenharmony_ci			return false;
132362306a36Sopenharmony_ci		}
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci		if (!ldm_ldmdb_add (f->data, f->num*ldb->vm.vblk_size, ldb))
132662306a36Sopenharmony_ci			return false;		/* Already logged */
132762306a36Sopenharmony_ci	}
132862306a36Sopenharmony_ci	return true;
132962306a36Sopenharmony_ci}
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci/**
133262306a36Sopenharmony_ci * ldm_get_vblks - Read the on-disk database of VBLKs into memory
133362306a36Sopenharmony_ci * @state: Partition check state including device holding the LDM Database
133462306a36Sopenharmony_ci * @base:  Offset, into @state->disk, of the database
133562306a36Sopenharmony_ci * @ldb:   Cache of the database structures
133662306a36Sopenharmony_ci *
133762306a36Sopenharmony_ci * To use the information from the VBLKs, they need to be read from the disk,
133862306a36Sopenharmony_ci * unpacked and validated.  We cache them in @ldb according to their type.
133962306a36Sopenharmony_ci *
134062306a36Sopenharmony_ci * Return:  'true'   All the VBLKs were read successfully
134162306a36Sopenharmony_ci *          'false'  An error occurred
134262306a36Sopenharmony_ci */
134362306a36Sopenharmony_cistatic bool ldm_get_vblks(struct parsed_partitions *state, unsigned long base,
134462306a36Sopenharmony_ci			  struct ldmdb *ldb)
134562306a36Sopenharmony_ci{
134662306a36Sopenharmony_ci	int size, perbuf, skip, finish, s, v, recs;
134762306a36Sopenharmony_ci	u8 *data = NULL;
134862306a36Sopenharmony_ci	Sector sect;
134962306a36Sopenharmony_ci	bool result = false;
135062306a36Sopenharmony_ci	LIST_HEAD (frags);
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	BUG_ON(!state || !ldb);
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	size   = ldb->vm.vblk_size;
135562306a36Sopenharmony_ci	perbuf = 512 / size;
135662306a36Sopenharmony_ci	skip   = ldb->vm.vblk_offset >> 9;		/* Bytes to sectors */
135762306a36Sopenharmony_ci	finish = (size * ldb->vm.last_vblk_seq) >> 9;
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	for (s = skip; s < finish; s++) {		/* For each sector */
136062306a36Sopenharmony_ci		data = read_part_sector(state, base + OFF_VMDB + s, &sect);
136162306a36Sopenharmony_ci		if (!data) {
136262306a36Sopenharmony_ci			ldm_crit ("Disk read failed.");
136362306a36Sopenharmony_ci			goto out;
136462306a36Sopenharmony_ci		}
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci		for (v = 0; v < perbuf; v++, data+=size) {  /* For each vblk */
136762306a36Sopenharmony_ci			if (MAGIC_VBLK != get_unaligned_be32(data)) {
136862306a36Sopenharmony_ci				ldm_error ("Expected to find a VBLK.");
136962306a36Sopenharmony_ci				goto out;
137062306a36Sopenharmony_ci			}
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci			recs = get_unaligned_be16(data + 0x0E);	/* Number of records */
137362306a36Sopenharmony_ci			if (recs == 1) {
137462306a36Sopenharmony_ci				if (!ldm_ldmdb_add (data, size, ldb))
137562306a36Sopenharmony_ci					goto out;	/* Already logged */
137662306a36Sopenharmony_ci			} else if (recs > 1) {
137762306a36Sopenharmony_ci				if (!ldm_frag_add (data, size, &frags))
137862306a36Sopenharmony_ci					goto out;	/* Already logged */
137962306a36Sopenharmony_ci			}
138062306a36Sopenharmony_ci			/* else Record is not in use, ignore it. */
138162306a36Sopenharmony_ci		}
138262306a36Sopenharmony_ci		put_dev_sector (sect);
138362306a36Sopenharmony_ci		data = NULL;
138462306a36Sopenharmony_ci	}
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	result = ldm_frag_commit (&frags, ldb);	/* Failures, already logged */
138762306a36Sopenharmony_ciout:
138862306a36Sopenharmony_ci	if (data)
138962306a36Sopenharmony_ci		put_dev_sector (sect);
139062306a36Sopenharmony_ci	ldm_frag_free (&frags);
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	return result;
139362306a36Sopenharmony_ci}
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci/**
139662306a36Sopenharmony_ci * ldm_free_vblks - Free a linked list of vblk's
139762306a36Sopenharmony_ci * @lh:  Head of a linked list of struct vblk
139862306a36Sopenharmony_ci *
139962306a36Sopenharmony_ci * Free a list of vblk's and free the memory used to maintain the list.
140062306a36Sopenharmony_ci *
140162306a36Sopenharmony_ci * Return:  none
140262306a36Sopenharmony_ci */
140362306a36Sopenharmony_cistatic void ldm_free_vblks (struct list_head *lh)
140462306a36Sopenharmony_ci{
140562306a36Sopenharmony_ci	struct list_head *item, *tmp;
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	BUG_ON (!lh);
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	list_for_each_safe (item, tmp, lh)
141062306a36Sopenharmony_ci		kfree (list_entry (item, struct vblk, list));
141162306a36Sopenharmony_ci}
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci/**
141562306a36Sopenharmony_ci * ldm_partition - Find out whether a device is a dynamic disk and handle it
141662306a36Sopenharmony_ci * @state: Partition check state including device holding the LDM Database
141762306a36Sopenharmony_ci *
141862306a36Sopenharmony_ci * This determines whether the device @bdev is a dynamic disk and if so creates
141962306a36Sopenharmony_ci * the partitions necessary in the gendisk structure pointed to by @hd.
142062306a36Sopenharmony_ci *
142162306a36Sopenharmony_ci * We create a dummy device 1, which contains the LDM database, and then create
142262306a36Sopenharmony_ci * each partition described by the LDM database in sequence as devices 2+. For
142362306a36Sopenharmony_ci * example, if the device is hda, we would have: hda1: LDM database, hda2, hda3,
142462306a36Sopenharmony_ci * and so on: the actual data containing partitions.
142562306a36Sopenharmony_ci *
142662306a36Sopenharmony_ci * Return:  1 Success, @state->disk is a dynamic disk and we handled it
142762306a36Sopenharmony_ci *          0 Success, @state->disk is not a dynamic disk
142862306a36Sopenharmony_ci *         -1 An error occurred before enough information had been read
142962306a36Sopenharmony_ci *            Or @state->disk is a dynamic disk, but it may be corrupted
143062306a36Sopenharmony_ci */
143162306a36Sopenharmony_ciint ldm_partition(struct parsed_partitions *state)
143262306a36Sopenharmony_ci{
143362306a36Sopenharmony_ci	struct ldmdb  *ldb;
143462306a36Sopenharmony_ci	unsigned long base;
143562306a36Sopenharmony_ci	int result = -1;
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	BUG_ON(!state);
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci	/* Look for signs of a Dynamic Disk */
144062306a36Sopenharmony_ci	if (!ldm_validate_partition_table(state))
144162306a36Sopenharmony_ci		return 0;
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	ldb = kmalloc (sizeof (*ldb), GFP_KERNEL);
144462306a36Sopenharmony_ci	if (!ldb) {
144562306a36Sopenharmony_ci		ldm_crit ("Out of memory.");
144662306a36Sopenharmony_ci		goto out;
144762306a36Sopenharmony_ci	}
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	/* Parse and check privheads. */
145062306a36Sopenharmony_ci	if (!ldm_validate_privheads(state, &ldb->ph))
145162306a36Sopenharmony_ci		goto out;		/* Already logged */
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	/* All further references are relative to base (database start). */
145462306a36Sopenharmony_ci	base = ldb->ph.config_start;
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	/* Parse and check tocs and vmdb. */
145762306a36Sopenharmony_ci	if (!ldm_validate_tocblocks(state, base, ldb) ||
145862306a36Sopenharmony_ci	    !ldm_validate_vmdb(state, base, ldb))
145962306a36Sopenharmony_ci	    	goto out;		/* Already logged */
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	/* Initialize vblk lists in ldmdb struct */
146262306a36Sopenharmony_ci	INIT_LIST_HEAD (&ldb->v_dgrp);
146362306a36Sopenharmony_ci	INIT_LIST_HEAD (&ldb->v_disk);
146462306a36Sopenharmony_ci	INIT_LIST_HEAD (&ldb->v_volu);
146562306a36Sopenharmony_ci	INIT_LIST_HEAD (&ldb->v_comp);
146662306a36Sopenharmony_ci	INIT_LIST_HEAD (&ldb->v_part);
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	if (!ldm_get_vblks(state, base, ldb)) {
146962306a36Sopenharmony_ci		ldm_crit ("Failed to read the VBLKs from the database.");
147062306a36Sopenharmony_ci		goto cleanup;
147162306a36Sopenharmony_ci	}
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci	/* Finally, create the data partition devices. */
147462306a36Sopenharmony_ci	if (ldm_create_data_partitions(state, ldb)) {
147562306a36Sopenharmony_ci		ldm_debug ("Parsed LDM database successfully.");
147662306a36Sopenharmony_ci		result = 1;
147762306a36Sopenharmony_ci	}
147862306a36Sopenharmony_ci	/* else Already logged */
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_cicleanup:
148162306a36Sopenharmony_ci	ldm_free_vblks (&ldb->v_dgrp);
148262306a36Sopenharmony_ci	ldm_free_vblks (&ldb->v_disk);
148362306a36Sopenharmony_ci	ldm_free_vblks (&ldb->v_volu);
148462306a36Sopenharmony_ci	ldm_free_vblks (&ldb->v_comp);
148562306a36Sopenharmony_ci	ldm_free_vblks (&ldb->v_part);
148662306a36Sopenharmony_ciout:
148762306a36Sopenharmony_ci	kfree (ldb);
148862306a36Sopenharmony_ci	return result;
148962306a36Sopenharmony_ci}
1490