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 §); 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], §); 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, §); 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, §); 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, §); 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