18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/** 38c2ecf20Sopenharmony_ci * ldm - Support for Windows Logical Disk Manager (Dynamic Disks) 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org> 68c2ecf20Sopenharmony_ci * Copyright (c) 2001-2012 Anton Altaparmakov 78c2ecf20Sopenharmony_ci * Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Documentation is available at http://www.linux-ntfs.org/doku.php?id=downloads 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 148c2ecf20Sopenharmony_ci#include <linux/stringify.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/uuid.h> 178c2ecf20Sopenharmony_ci#include <linux/msdos_partition.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "ldm.h" 208c2ecf20Sopenharmony_ci#include "check.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* 238c2ecf20Sopenharmony_ci * ldm_debug/info/error/crit - Output an error message 248c2ecf20Sopenharmony_ci * @f: A printf format string containing the message 258c2ecf20Sopenharmony_ci * @...: Variables to substitute into @f 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * ldm_debug() writes a DEBUG level message to the syslog but only if the 288c2ecf20Sopenharmony_ci * driver was compiled with debug enabled. Otherwise, the call turns into a NOP. 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci#ifndef CONFIG_LDM_DEBUG 318c2ecf20Sopenharmony_ci#define ldm_debug(...) do {} while (0) 328c2ecf20Sopenharmony_ci#else 338c2ecf20Sopenharmony_ci#define ldm_debug(f, a...) _ldm_printk (KERN_DEBUG, __func__, f, ##a) 348c2ecf20Sopenharmony_ci#endif 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define ldm_crit(f, a...) _ldm_printk (KERN_CRIT, __func__, f, ##a) 378c2ecf20Sopenharmony_ci#define ldm_error(f, a...) _ldm_printk (KERN_ERR, __func__, f, ##a) 388c2ecf20Sopenharmony_ci#define ldm_info(f, a...) _ldm_printk (KERN_INFO, __func__, f, ##a) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic __printf(3, 4) 418c2ecf20Sopenharmony_civoid _ldm_printk(const char *level, const char *function, const char *fmt, ...) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct va_format vaf; 448c2ecf20Sopenharmony_ci va_list args; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci va_start (args, fmt); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci vaf.fmt = fmt; 498c2ecf20Sopenharmony_ci vaf.va = &args; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci printk("%s%s(): %pV\n", level, function, &vaf); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci va_end(args); 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/** 578c2ecf20Sopenharmony_ci * ldm_parse_privhead - Read the LDM Database PRIVHEAD structure 588c2ecf20Sopenharmony_ci * @data: Raw database PRIVHEAD structure loaded from the device 598c2ecf20Sopenharmony_ci * @ph: In-memory privhead structure in which to return parsed information 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci * This parses the LDM database PRIVHEAD structure supplied in @data and 628c2ecf20Sopenharmony_ci * sets up the in-memory privhead structure @ph with the obtained information. 638c2ecf20Sopenharmony_ci * 648c2ecf20Sopenharmony_ci * Return: 'true' @ph contains the PRIVHEAD data 658c2ecf20Sopenharmony_ci * 'false' @ph contents are undefined 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_cistatic bool ldm_parse_privhead(const u8 *data, struct privhead *ph) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci bool is_vista = false; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci BUG_ON(!data || !ph); 728c2ecf20Sopenharmony_ci if (MAGIC_PRIVHEAD != get_unaligned_be64(data)) { 738c2ecf20Sopenharmony_ci ldm_error("Cannot find PRIVHEAD structure. LDM database is" 748c2ecf20Sopenharmony_ci " corrupt. Aborting."); 758c2ecf20Sopenharmony_ci return false; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci ph->ver_major = get_unaligned_be16(data + 0x000C); 788c2ecf20Sopenharmony_ci ph->ver_minor = get_unaligned_be16(data + 0x000E); 798c2ecf20Sopenharmony_ci ph->logical_disk_start = get_unaligned_be64(data + 0x011B); 808c2ecf20Sopenharmony_ci ph->logical_disk_size = get_unaligned_be64(data + 0x0123); 818c2ecf20Sopenharmony_ci ph->config_start = get_unaligned_be64(data + 0x012B); 828c2ecf20Sopenharmony_ci ph->config_size = get_unaligned_be64(data + 0x0133); 838c2ecf20Sopenharmony_ci /* Version 2.11 is Win2k/XP and version 2.12 is Vista. */ 848c2ecf20Sopenharmony_ci if (ph->ver_major == 2 && ph->ver_minor == 12) 858c2ecf20Sopenharmony_ci is_vista = true; 868c2ecf20Sopenharmony_ci if (!is_vista && (ph->ver_major != 2 || ph->ver_minor != 11)) { 878c2ecf20Sopenharmony_ci ldm_error("Expected PRIVHEAD version 2.11 or 2.12, got %d.%d." 888c2ecf20Sopenharmony_ci " Aborting.", ph->ver_major, ph->ver_minor); 898c2ecf20Sopenharmony_ci return false; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci ldm_debug("PRIVHEAD version %d.%d (Windows %s).", ph->ver_major, 928c2ecf20Sopenharmony_ci ph->ver_minor, is_vista ? "Vista" : "2000/XP"); 938c2ecf20Sopenharmony_ci if (ph->config_size != LDM_DB_SIZE) { /* 1 MiB in sectors. */ 948c2ecf20Sopenharmony_ci /* Warn the user and continue, carefully. */ 958c2ecf20Sopenharmony_ci ldm_info("Database is normally %u bytes, it claims to " 968c2ecf20Sopenharmony_ci "be %llu bytes.", LDM_DB_SIZE, 978c2ecf20Sopenharmony_ci (unsigned long long)ph->config_size); 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci if ((ph->logical_disk_size == 0) || (ph->logical_disk_start + 1008c2ecf20Sopenharmony_ci ph->logical_disk_size > ph->config_start)) { 1018c2ecf20Sopenharmony_ci ldm_error("PRIVHEAD disk size doesn't match real disk size"); 1028c2ecf20Sopenharmony_ci return false; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci if (uuid_parse(data + 0x0030, &ph->disk_id)) { 1058c2ecf20Sopenharmony_ci ldm_error("PRIVHEAD contains an invalid GUID."); 1068c2ecf20Sopenharmony_ci return false; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci ldm_debug("Parsed PRIVHEAD successfully."); 1098c2ecf20Sopenharmony_ci return true; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/** 1138c2ecf20Sopenharmony_ci * ldm_parse_tocblock - Read the LDM Database TOCBLOCK structure 1148c2ecf20Sopenharmony_ci * @data: Raw database TOCBLOCK structure loaded from the device 1158c2ecf20Sopenharmony_ci * @toc: In-memory toc structure in which to return parsed information 1168c2ecf20Sopenharmony_ci * 1178c2ecf20Sopenharmony_ci * This parses the LDM Database TOCBLOCK (table of contents) structure supplied 1188c2ecf20Sopenharmony_ci * in @data and sets up the in-memory tocblock structure @toc with the obtained 1198c2ecf20Sopenharmony_ci * information. 1208c2ecf20Sopenharmony_ci * 1218c2ecf20Sopenharmony_ci * N.B. The *_start and *_size values returned in @toc are not range-checked. 1228c2ecf20Sopenharmony_ci * 1238c2ecf20Sopenharmony_ci * Return: 'true' @toc contains the TOCBLOCK data 1248c2ecf20Sopenharmony_ci * 'false' @toc contents are undefined 1258c2ecf20Sopenharmony_ci */ 1268c2ecf20Sopenharmony_cistatic bool ldm_parse_tocblock (const u8 *data, struct tocblock *toc) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci BUG_ON (!data || !toc); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (MAGIC_TOCBLOCK != get_unaligned_be64(data)) { 1318c2ecf20Sopenharmony_ci ldm_crit ("Cannot find TOCBLOCK, database may be corrupt."); 1328c2ecf20Sopenharmony_ci return false; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci strncpy (toc->bitmap1_name, data + 0x24, sizeof (toc->bitmap1_name)); 1358c2ecf20Sopenharmony_ci toc->bitmap1_name[sizeof (toc->bitmap1_name) - 1] = 0; 1368c2ecf20Sopenharmony_ci toc->bitmap1_start = get_unaligned_be64(data + 0x2E); 1378c2ecf20Sopenharmony_ci toc->bitmap1_size = get_unaligned_be64(data + 0x36); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (strncmp (toc->bitmap1_name, TOC_BITMAP1, 1408c2ecf20Sopenharmony_ci sizeof (toc->bitmap1_name)) != 0) { 1418c2ecf20Sopenharmony_ci ldm_crit ("TOCBLOCK's first bitmap is '%s', should be '%s'.", 1428c2ecf20Sopenharmony_ci TOC_BITMAP1, toc->bitmap1_name); 1438c2ecf20Sopenharmony_ci return false; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci strncpy (toc->bitmap2_name, data + 0x46, sizeof (toc->bitmap2_name)); 1468c2ecf20Sopenharmony_ci toc->bitmap2_name[sizeof (toc->bitmap2_name) - 1] = 0; 1478c2ecf20Sopenharmony_ci toc->bitmap2_start = get_unaligned_be64(data + 0x50); 1488c2ecf20Sopenharmony_ci toc->bitmap2_size = get_unaligned_be64(data + 0x58); 1498c2ecf20Sopenharmony_ci if (strncmp (toc->bitmap2_name, TOC_BITMAP2, 1508c2ecf20Sopenharmony_ci sizeof (toc->bitmap2_name)) != 0) { 1518c2ecf20Sopenharmony_ci ldm_crit ("TOCBLOCK's second bitmap is '%s', should be '%s'.", 1528c2ecf20Sopenharmony_ci TOC_BITMAP2, toc->bitmap2_name); 1538c2ecf20Sopenharmony_ci return false; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci ldm_debug ("Parsed TOCBLOCK successfully."); 1568c2ecf20Sopenharmony_ci return true; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci/** 1608c2ecf20Sopenharmony_ci * ldm_parse_vmdb - Read the LDM Database VMDB structure 1618c2ecf20Sopenharmony_ci * @data: Raw database VMDB structure loaded from the device 1628c2ecf20Sopenharmony_ci * @vm: In-memory vmdb structure in which to return parsed information 1638c2ecf20Sopenharmony_ci * 1648c2ecf20Sopenharmony_ci * This parses the LDM Database VMDB structure supplied in @data and sets up 1658c2ecf20Sopenharmony_ci * the in-memory vmdb structure @vm with the obtained information. 1668c2ecf20Sopenharmony_ci * 1678c2ecf20Sopenharmony_ci * N.B. The *_start, *_size and *_seq values will be range-checked later. 1688c2ecf20Sopenharmony_ci * 1698c2ecf20Sopenharmony_ci * Return: 'true' @vm contains VMDB info 1708c2ecf20Sopenharmony_ci * 'false' @vm contents are undefined 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_cistatic bool ldm_parse_vmdb (const u8 *data, struct vmdb *vm) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci BUG_ON (!data || !vm); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (MAGIC_VMDB != get_unaligned_be32(data)) { 1778c2ecf20Sopenharmony_ci ldm_crit ("Cannot find the VMDB, database may be corrupt."); 1788c2ecf20Sopenharmony_ci return false; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci vm->ver_major = get_unaligned_be16(data + 0x12); 1828c2ecf20Sopenharmony_ci vm->ver_minor = get_unaligned_be16(data + 0x14); 1838c2ecf20Sopenharmony_ci if ((vm->ver_major != 4) || (vm->ver_minor != 10)) { 1848c2ecf20Sopenharmony_ci ldm_error ("Expected VMDB version %d.%d, got %d.%d. " 1858c2ecf20Sopenharmony_ci "Aborting.", 4, 10, vm->ver_major, vm->ver_minor); 1868c2ecf20Sopenharmony_ci return false; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci vm->vblk_size = get_unaligned_be32(data + 0x08); 1908c2ecf20Sopenharmony_ci if (vm->vblk_size == 0) { 1918c2ecf20Sopenharmony_ci ldm_error ("Illegal VBLK size"); 1928c2ecf20Sopenharmony_ci return false; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci vm->vblk_offset = get_unaligned_be32(data + 0x0C); 1968c2ecf20Sopenharmony_ci vm->last_vblk_seq = get_unaligned_be32(data + 0x04); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci ldm_debug ("Parsed VMDB successfully."); 1998c2ecf20Sopenharmony_ci return true; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci/** 2038c2ecf20Sopenharmony_ci * ldm_compare_privheads - Compare two privhead objects 2048c2ecf20Sopenharmony_ci * @ph1: First privhead 2058c2ecf20Sopenharmony_ci * @ph2: Second privhead 2068c2ecf20Sopenharmony_ci * 2078c2ecf20Sopenharmony_ci * This compares the two privhead structures @ph1 and @ph2. 2088c2ecf20Sopenharmony_ci * 2098c2ecf20Sopenharmony_ci * Return: 'true' Identical 2108c2ecf20Sopenharmony_ci * 'false' Different 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_cistatic bool ldm_compare_privheads (const struct privhead *ph1, 2138c2ecf20Sopenharmony_ci const struct privhead *ph2) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci BUG_ON (!ph1 || !ph2); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci return ((ph1->ver_major == ph2->ver_major) && 2188c2ecf20Sopenharmony_ci (ph1->ver_minor == ph2->ver_minor) && 2198c2ecf20Sopenharmony_ci (ph1->logical_disk_start == ph2->logical_disk_start) && 2208c2ecf20Sopenharmony_ci (ph1->logical_disk_size == ph2->logical_disk_size) && 2218c2ecf20Sopenharmony_ci (ph1->config_start == ph2->config_start) && 2228c2ecf20Sopenharmony_ci (ph1->config_size == ph2->config_size) && 2238c2ecf20Sopenharmony_ci uuid_equal(&ph1->disk_id, &ph2->disk_id)); 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci/** 2278c2ecf20Sopenharmony_ci * ldm_compare_tocblocks - Compare two tocblock objects 2288c2ecf20Sopenharmony_ci * @toc1: First toc 2298c2ecf20Sopenharmony_ci * @toc2: Second toc 2308c2ecf20Sopenharmony_ci * 2318c2ecf20Sopenharmony_ci * This compares the two tocblock structures @toc1 and @toc2. 2328c2ecf20Sopenharmony_ci * 2338c2ecf20Sopenharmony_ci * Return: 'true' Identical 2348c2ecf20Sopenharmony_ci * 'false' Different 2358c2ecf20Sopenharmony_ci */ 2368c2ecf20Sopenharmony_cistatic bool ldm_compare_tocblocks (const struct tocblock *toc1, 2378c2ecf20Sopenharmony_ci const struct tocblock *toc2) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci BUG_ON (!toc1 || !toc2); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci return ((toc1->bitmap1_start == toc2->bitmap1_start) && 2428c2ecf20Sopenharmony_ci (toc1->bitmap1_size == toc2->bitmap1_size) && 2438c2ecf20Sopenharmony_ci (toc1->bitmap2_start == toc2->bitmap2_start) && 2448c2ecf20Sopenharmony_ci (toc1->bitmap2_size == toc2->bitmap2_size) && 2458c2ecf20Sopenharmony_ci !strncmp (toc1->bitmap1_name, toc2->bitmap1_name, 2468c2ecf20Sopenharmony_ci sizeof (toc1->bitmap1_name)) && 2478c2ecf20Sopenharmony_ci !strncmp (toc1->bitmap2_name, toc2->bitmap2_name, 2488c2ecf20Sopenharmony_ci sizeof (toc1->bitmap2_name))); 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci/** 2528c2ecf20Sopenharmony_ci * ldm_validate_privheads - Compare the primary privhead with its backups 2538c2ecf20Sopenharmony_ci * @state: Partition check state including device holding the LDM Database 2548c2ecf20Sopenharmony_ci * @ph1: Memory struct to fill with ph contents 2558c2ecf20Sopenharmony_ci * 2568c2ecf20Sopenharmony_ci * Read and compare all three privheads from disk. 2578c2ecf20Sopenharmony_ci * 2588c2ecf20Sopenharmony_ci * The privheads on disk show the size and location of the main disk area and 2598c2ecf20Sopenharmony_ci * the configuration area (the database). The values are range-checked against 2608c2ecf20Sopenharmony_ci * @hd, which contains the real size of the disk. 2618c2ecf20Sopenharmony_ci * 2628c2ecf20Sopenharmony_ci * Return: 'true' Success 2638c2ecf20Sopenharmony_ci * 'false' Error 2648c2ecf20Sopenharmony_ci */ 2658c2ecf20Sopenharmony_cistatic bool ldm_validate_privheads(struct parsed_partitions *state, 2668c2ecf20Sopenharmony_ci struct privhead *ph1) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci static const int off[3] = { OFF_PRIV1, OFF_PRIV2, OFF_PRIV3 }; 2698c2ecf20Sopenharmony_ci struct privhead *ph[3] = { ph1 }; 2708c2ecf20Sopenharmony_ci Sector sect; 2718c2ecf20Sopenharmony_ci u8 *data; 2728c2ecf20Sopenharmony_ci bool result = false; 2738c2ecf20Sopenharmony_ci long num_sects; 2748c2ecf20Sopenharmony_ci int i; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci BUG_ON (!state || !ph1); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci ph[1] = kmalloc (sizeof (*ph[1]), GFP_KERNEL); 2798c2ecf20Sopenharmony_ci ph[2] = kmalloc (sizeof (*ph[2]), GFP_KERNEL); 2808c2ecf20Sopenharmony_ci if (!ph[1] || !ph[2]) { 2818c2ecf20Sopenharmony_ci ldm_crit ("Out of memory."); 2828c2ecf20Sopenharmony_ci goto out; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* off[1 & 2] are relative to ph[0]->config_start */ 2868c2ecf20Sopenharmony_ci ph[0]->config_start = 0; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* Read and parse privheads */ 2898c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 2908c2ecf20Sopenharmony_ci data = read_part_sector(state, ph[0]->config_start + off[i], 2918c2ecf20Sopenharmony_ci §); 2928c2ecf20Sopenharmony_ci if (!data) { 2938c2ecf20Sopenharmony_ci ldm_crit ("Disk read failed."); 2948c2ecf20Sopenharmony_ci goto out; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci result = ldm_parse_privhead (data, ph[i]); 2978c2ecf20Sopenharmony_ci put_dev_sector (sect); 2988c2ecf20Sopenharmony_ci if (!result) { 2998c2ecf20Sopenharmony_ci ldm_error ("Cannot find PRIVHEAD %d.", i+1); /* Log again */ 3008c2ecf20Sopenharmony_ci if (i < 2) 3018c2ecf20Sopenharmony_ci goto out; /* Already logged */ 3028c2ecf20Sopenharmony_ci else 3038c2ecf20Sopenharmony_ci break; /* FIXME ignore for now, 3rd PH can fail on odd-sized disks */ 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci num_sects = state->bdev->bd_inode->i_size >> 9; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if ((ph[0]->config_start > num_sects) || 3108c2ecf20Sopenharmony_ci ((ph[0]->config_start + ph[0]->config_size) > num_sects)) { 3118c2ecf20Sopenharmony_ci ldm_crit ("Database extends beyond the end of the disk."); 3128c2ecf20Sopenharmony_ci goto out; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if ((ph[0]->logical_disk_start > ph[0]->config_start) || 3168c2ecf20Sopenharmony_ci ((ph[0]->logical_disk_start + ph[0]->logical_disk_size) 3178c2ecf20Sopenharmony_ci > ph[0]->config_start)) { 3188c2ecf20Sopenharmony_ci ldm_crit ("Disk and database overlap."); 3198c2ecf20Sopenharmony_ci goto out; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (!ldm_compare_privheads (ph[0], ph[1])) { 3238c2ecf20Sopenharmony_ci ldm_crit ("Primary and backup PRIVHEADs don't match."); 3248c2ecf20Sopenharmony_ci goto out; 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci /* FIXME ignore this for now 3278c2ecf20Sopenharmony_ci if (!ldm_compare_privheads (ph[0], ph[2])) { 3288c2ecf20Sopenharmony_ci ldm_crit ("Primary and backup PRIVHEADs don't match."); 3298c2ecf20Sopenharmony_ci goto out; 3308c2ecf20Sopenharmony_ci }*/ 3318c2ecf20Sopenharmony_ci ldm_debug ("Validated PRIVHEADs successfully."); 3328c2ecf20Sopenharmony_ci result = true; 3338c2ecf20Sopenharmony_ciout: 3348c2ecf20Sopenharmony_ci kfree (ph[1]); 3358c2ecf20Sopenharmony_ci kfree (ph[2]); 3368c2ecf20Sopenharmony_ci return result; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci/** 3408c2ecf20Sopenharmony_ci * ldm_validate_tocblocks - Validate the table of contents and its backups 3418c2ecf20Sopenharmony_ci * @state: Partition check state including device holding the LDM Database 3428c2ecf20Sopenharmony_ci * @base: Offset, into @state->bdev, of the database 3438c2ecf20Sopenharmony_ci * @ldb: Cache of the database structures 3448c2ecf20Sopenharmony_ci * 3458c2ecf20Sopenharmony_ci * Find and compare the four tables of contents of the LDM Database stored on 3468c2ecf20Sopenharmony_ci * @state->bdev and return the parsed information into @toc1. 3478c2ecf20Sopenharmony_ci * 3488c2ecf20Sopenharmony_ci * The offsets and sizes of the configs are range-checked against a privhead. 3498c2ecf20Sopenharmony_ci * 3508c2ecf20Sopenharmony_ci * Return: 'true' @toc1 contains validated TOCBLOCK info 3518c2ecf20Sopenharmony_ci * 'false' @toc1 contents are undefined 3528c2ecf20Sopenharmony_ci */ 3538c2ecf20Sopenharmony_cistatic bool ldm_validate_tocblocks(struct parsed_partitions *state, 3548c2ecf20Sopenharmony_ci unsigned long base, struct ldmdb *ldb) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci static const int off[4] = { OFF_TOCB1, OFF_TOCB2, OFF_TOCB3, OFF_TOCB4}; 3578c2ecf20Sopenharmony_ci struct tocblock *tb[4]; 3588c2ecf20Sopenharmony_ci struct privhead *ph; 3598c2ecf20Sopenharmony_ci Sector sect; 3608c2ecf20Sopenharmony_ci u8 *data; 3618c2ecf20Sopenharmony_ci int i, nr_tbs; 3628c2ecf20Sopenharmony_ci bool result = false; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci BUG_ON(!state || !ldb); 3658c2ecf20Sopenharmony_ci ph = &ldb->ph; 3668c2ecf20Sopenharmony_ci tb[0] = &ldb->toc; 3678c2ecf20Sopenharmony_ci tb[1] = kmalloc_array(3, sizeof(*tb[1]), GFP_KERNEL); 3688c2ecf20Sopenharmony_ci if (!tb[1]) { 3698c2ecf20Sopenharmony_ci ldm_crit("Out of memory."); 3708c2ecf20Sopenharmony_ci goto err; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci tb[2] = (struct tocblock*)((u8*)tb[1] + sizeof(*tb[1])); 3738c2ecf20Sopenharmony_ci tb[3] = (struct tocblock*)((u8*)tb[2] + sizeof(*tb[2])); 3748c2ecf20Sopenharmony_ci /* 3758c2ecf20Sopenharmony_ci * Try to read and parse all four TOCBLOCKs. 3768c2ecf20Sopenharmony_ci * 3778c2ecf20Sopenharmony_ci * Windows Vista LDM v2.12 does not always have all four TOCBLOCKs so 3788c2ecf20Sopenharmony_ci * skip any that fail as long as we get at least one valid TOCBLOCK. 3798c2ecf20Sopenharmony_ci */ 3808c2ecf20Sopenharmony_ci for (nr_tbs = i = 0; i < 4; i++) { 3818c2ecf20Sopenharmony_ci data = read_part_sector(state, base + off[i], §); 3828c2ecf20Sopenharmony_ci if (!data) { 3838c2ecf20Sopenharmony_ci ldm_error("Disk read failed for TOCBLOCK %d.", i); 3848c2ecf20Sopenharmony_ci continue; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci if (ldm_parse_tocblock(data, tb[nr_tbs])) 3878c2ecf20Sopenharmony_ci nr_tbs++; 3888c2ecf20Sopenharmony_ci put_dev_sector(sect); 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci if (!nr_tbs) { 3918c2ecf20Sopenharmony_ci ldm_crit("Failed to find a valid TOCBLOCK."); 3928c2ecf20Sopenharmony_ci goto err; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci /* Range check the TOCBLOCK against a privhead. */ 3958c2ecf20Sopenharmony_ci if (((tb[0]->bitmap1_start + tb[0]->bitmap1_size) > ph->config_size) || 3968c2ecf20Sopenharmony_ci ((tb[0]->bitmap2_start + tb[0]->bitmap2_size) > 3978c2ecf20Sopenharmony_ci ph->config_size)) { 3988c2ecf20Sopenharmony_ci ldm_crit("The bitmaps are out of range. Giving up."); 3998c2ecf20Sopenharmony_ci goto err; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci /* Compare all loaded TOCBLOCKs. */ 4028c2ecf20Sopenharmony_ci for (i = 1; i < nr_tbs; i++) { 4038c2ecf20Sopenharmony_ci if (!ldm_compare_tocblocks(tb[0], tb[i])) { 4048c2ecf20Sopenharmony_ci ldm_crit("TOCBLOCKs 0 and %d do not match.", i); 4058c2ecf20Sopenharmony_ci goto err; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci ldm_debug("Validated %d TOCBLOCKs successfully.", nr_tbs); 4098c2ecf20Sopenharmony_ci result = true; 4108c2ecf20Sopenharmony_cierr: 4118c2ecf20Sopenharmony_ci kfree(tb[1]); 4128c2ecf20Sopenharmony_ci return result; 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci/** 4168c2ecf20Sopenharmony_ci * ldm_validate_vmdb - Read the VMDB and validate it 4178c2ecf20Sopenharmony_ci * @state: Partition check state including device holding the LDM Database 4188c2ecf20Sopenharmony_ci * @base: Offset, into @bdev, of the database 4198c2ecf20Sopenharmony_ci * @ldb: Cache of the database structures 4208c2ecf20Sopenharmony_ci * 4218c2ecf20Sopenharmony_ci * Find the vmdb of the LDM Database stored on @bdev and return the parsed 4228c2ecf20Sopenharmony_ci * information in @ldb. 4238c2ecf20Sopenharmony_ci * 4248c2ecf20Sopenharmony_ci * Return: 'true' @ldb contains validated VBDB info 4258c2ecf20Sopenharmony_ci * 'false' @ldb contents are undefined 4268c2ecf20Sopenharmony_ci */ 4278c2ecf20Sopenharmony_cistatic bool ldm_validate_vmdb(struct parsed_partitions *state, 4288c2ecf20Sopenharmony_ci unsigned long base, struct ldmdb *ldb) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci Sector sect; 4318c2ecf20Sopenharmony_ci u8 *data; 4328c2ecf20Sopenharmony_ci bool result = false; 4338c2ecf20Sopenharmony_ci struct vmdb *vm; 4348c2ecf20Sopenharmony_ci struct tocblock *toc; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci BUG_ON (!state || !ldb); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci vm = &ldb->vm; 4398c2ecf20Sopenharmony_ci toc = &ldb->toc; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci data = read_part_sector(state, base + OFF_VMDB, §); 4428c2ecf20Sopenharmony_ci if (!data) { 4438c2ecf20Sopenharmony_ci ldm_crit ("Disk read failed."); 4448c2ecf20Sopenharmony_ci return false; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (!ldm_parse_vmdb (data, vm)) 4488c2ecf20Sopenharmony_ci goto out; /* Already logged */ 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci /* Are there uncommitted transactions? */ 4518c2ecf20Sopenharmony_ci if (get_unaligned_be16(data + 0x10) != 0x01) { 4528c2ecf20Sopenharmony_ci ldm_crit ("Database is not in a consistent state. Aborting."); 4538c2ecf20Sopenharmony_ci goto out; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if (vm->vblk_offset != 512) 4578c2ecf20Sopenharmony_ci ldm_info ("VBLKs start at offset 0x%04x.", vm->vblk_offset); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci /* 4608c2ecf20Sopenharmony_ci * The last_vblkd_seq can be before the end of the vmdb, just make sure 4618c2ecf20Sopenharmony_ci * it is not out of bounds. 4628c2ecf20Sopenharmony_ci */ 4638c2ecf20Sopenharmony_ci if ((vm->vblk_size * vm->last_vblk_seq) > (toc->bitmap1_size << 9)) { 4648c2ecf20Sopenharmony_ci ldm_crit ("VMDB exceeds allowed size specified by TOCBLOCK. " 4658c2ecf20Sopenharmony_ci "Database is corrupt. Aborting."); 4668c2ecf20Sopenharmony_ci goto out; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci result = true; 4708c2ecf20Sopenharmony_ciout: 4718c2ecf20Sopenharmony_ci put_dev_sector (sect); 4728c2ecf20Sopenharmony_ci return result; 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci/** 4778c2ecf20Sopenharmony_ci * ldm_validate_partition_table - Determine whether bdev might be a dynamic disk 4788c2ecf20Sopenharmony_ci * @state: Partition check state including device holding the LDM Database 4798c2ecf20Sopenharmony_ci * 4808c2ecf20Sopenharmony_ci * This function provides a weak test to decide whether the device is a dynamic 4818c2ecf20Sopenharmony_ci * disk or not. It looks for an MS-DOS-style partition table containing at 4828c2ecf20Sopenharmony_ci * least one partition of type 0x42 (formerly SFS, now used by Windows for 4838c2ecf20Sopenharmony_ci * dynamic disks). 4848c2ecf20Sopenharmony_ci * 4858c2ecf20Sopenharmony_ci * N.B. The only possible error can come from the read_part_sector and that is 4868c2ecf20Sopenharmony_ci * only likely to happen if the underlying device is strange. If that IS 4878c2ecf20Sopenharmony_ci * the case we should return zero to let someone else try. 4888c2ecf20Sopenharmony_ci * 4898c2ecf20Sopenharmony_ci * Return: 'true' @state->bdev is a dynamic disk 4908c2ecf20Sopenharmony_ci * 'false' @state->bdev is not a dynamic disk, or an error occurred 4918c2ecf20Sopenharmony_ci */ 4928c2ecf20Sopenharmony_cistatic bool ldm_validate_partition_table(struct parsed_partitions *state) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci Sector sect; 4958c2ecf20Sopenharmony_ci u8 *data; 4968c2ecf20Sopenharmony_ci struct msdos_partition *p; 4978c2ecf20Sopenharmony_ci int i; 4988c2ecf20Sopenharmony_ci bool result = false; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci BUG_ON(!state); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci data = read_part_sector(state, 0, §); 5038c2ecf20Sopenharmony_ci if (!data) { 5048c2ecf20Sopenharmony_ci ldm_info ("Disk read failed."); 5058c2ecf20Sopenharmony_ci return false; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (*(__le16*) (data + 0x01FE) != cpu_to_le16 (MSDOS_LABEL_MAGIC)) 5098c2ecf20Sopenharmony_ci goto out; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci p = (struct msdos_partition *)(data + 0x01BE); 5128c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++, p++) 5138c2ecf20Sopenharmony_ci if (p->sys_ind == LDM_PARTITION) { 5148c2ecf20Sopenharmony_ci result = true; 5158c2ecf20Sopenharmony_ci break; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci if (result) 5198c2ecf20Sopenharmony_ci ldm_debug ("Found W2K dynamic disk partition type."); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ciout: 5228c2ecf20Sopenharmony_ci put_dev_sector (sect); 5238c2ecf20Sopenharmony_ci return result; 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci/** 5278c2ecf20Sopenharmony_ci * ldm_get_disk_objid - Search a linked list of vblk's for a given Disk Id 5288c2ecf20Sopenharmony_ci * @ldb: Cache of the database structures 5298c2ecf20Sopenharmony_ci * 5308c2ecf20Sopenharmony_ci * The LDM Database contains a list of all partitions on all dynamic disks. 5318c2ecf20Sopenharmony_ci * The primary PRIVHEAD, at the beginning of the physical disk, tells us 5328c2ecf20Sopenharmony_ci * the GUID of this disk. This function searches for the GUID in a linked 5338c2ecf20Sopenharmony_ci * list of vblk's. 5348c2ecf20Sopenharmony_ci * 5358c2ecf20Sopenharmony_ci * Return: Pointer, A matching vblk was found 5368c2ecf20Sopenharmony_ci * NULL, No match, or an error 5378c2ecf20Sopenharmony_ci */ 5388c2ecf20Sopenharmony_cistatic struct vblk * ldm_get_disk_objid (const struct ldmdb *ldb) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci struct list_head *item; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci BUG_ON (!ldb); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci list_for_each (item, &ldb->v_disk) { 5458c2ecf20Sopenharmony_ci struct vblk *v = list_entry (item, struct vblk, list); 5468c2ecf20Sopenharmony_ci if (uuid_equal(&v->vblk.disk.disk_id, &ldb->ph.disk_id)) 5478c2ecf20Sopenharmony_ci return v; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci return NULL; 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci/** 5548c2ecf20Sopenharmony_ci * ldm_create_data_partitions - Create data partitions for this device 5558c2ecf20Sopenharmony_ci * @pp: List of the partitions parsed so far 5568c2ecf20Sopenharmony_ci * @ldb: Cache of the database structures 5578c2ecf20Sopenharmony_ci * 5588c2ecf20Sopenharmony_ci * The database contains ALL the partitions for ALL disk groups, so we need to 5598c2ecf20Sopenharmony_ci * filter out this specific disk. Using the disk's object id, we can find all 5608c2ecf20Sopenharmony_ci * the partitions in the database that belong to this disk. 5618c2ecf20Sopenharmony_ci * 5628c2ecf20Sopenharmony_ci * Add each partition in our database, to the parsed_partitions structure. 5638c2ecf20Sopenharmony_ci * 5648c2ecf20Sopenharmony_ci * N.B. This function creates the partitions in the order it finds partition 5658c2ecf20Sopenharmony_ci * objects in the linked list. 5668c2ecf20Sopenharmony_ci * 5678c2ecf20Sopenharmony_ci * Return: 'true' Partition created 5688c2ecf20Sopenharmony_ci * 'false' Error, probably a range checking problem 5698c2ecf20Sopenharmony_ci */ 5708c2ecf20Sopenharmony_cistatic bool ldm_create_data_partitions (struct parsed_partitions *pp, 5718c2ecf20Sopenharmony_ci const struct ldmdb *ldb) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci struct list_head *item; 5748c2ecf20Sopenharmony_ci struct vblk *vb; 5758c2ecf20Sopenharmony_ci struct vblk *disk; 5768c2ecf20Sopenharmony_ci struct vblk_part *part; 5778c2ecf20Sopenharmony_ci int part_num = 1; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci BUG_ON (!pp || !ldb); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci disk = ldm_get_disk_objid (ldb); 5828c2ecf20Sopenharmony_ci if (!disk) { 5838c2ecf20Sopenharmony_ci ldm_crit ("Can't find the ID of this disk in the database."); 5848c2ecf20Sopenharmony_ci return false; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci strlcat(pp->pp_buf, " [LDM]", PAGE_SIZE); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci /* Create the data partitions */ 5908c2ecf20Sopenharmony_ci list_for_each (item, &ldb->v_part) { 5918c2ecf20Sopenharmony_ci vb = list_entry (item, struct vblk, list); 5928c2ecf20Sopenharmony_ci part = &vb->vblk.part; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci if (part->disk_id != disk->obj_id) 5958c2ecf20Sopenharmony_ci continue; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci put_partition (pp, part_num, ldb->ph.logical_disk_start + 5988c2ecf20Sopenharmony_ci part->start, part->size); 5998c2ecf20Sopenharmony_ci part_num++; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci strlcat(pp->pp_buf, "\n", PAGE_SIZE); 6038c2ecf20Sopenharmony_ci return true; 6048c2ecf20Sopenharmony_ci} 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci/** 6088c2ecf20Sopenharmony_ci * ldm_relative - Calculate the next relative offset 6098c2ecf20Sopenharmony_ci * @buffer: Block of data being worked on 6108c2ecf20Sopenharmony_ci * @buflen: Size of the block of data 6118c2ecf20Sopenharmony_ci * @base: Size of the previous fixed width fields 6128c2ecf20Sopenharmony_ci * @offset: Cumulative size of the previous variable-width fields 6138c2ecf20Sopenharmony_ci * 6148c2ecf20Sopenharmony_ci * Because many of the VBLK fields are variable-width, it's necessary 6158c2ecf20Sopenharmony_ci * to calculate each offset based on the previous one and the length 6168c2ecf20Sopenharmony_ci * of the field it pointed to. 6178c2ecf20Sopenharmony_ci * 6188c2ecf20Sopenharmony_ci * Return: -1 Error, the calculated offset exceeded the size of the buffer 6198c2ecf20Sopenharmony_ci * n OK, a range-checked offset into buffer 6208c2ecf20Sopenharmony_ci */ 6218c2ecf20Sopenharmony_cistatic int ldm_relative(const u8 *buffer, int buflen, int base, int offset) 6228c2ecf20Sopenharmony_ci{ 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci base += offset; 6258c2ecf20Sopenharmony_ci if (!buffer || offset < 0 || base > buflen) { 6268c2ecf20Sopenharmony_ci if (!buffer) 6278c2ecf20Sopenharmony_ci ldm_error("!buffer"); 6288c2ecf20Sopenharmony_ci if (offset < 0) 6298c2ecf20Sopenharmony_ci ldm_error("offset (%d) < 0", offset); 6308c2ecf20Sopenharmony_ci if (base > buflen) 6318c2ecf20Sopenharmony_ci ldm_error("base (%d) > buflen (%d)", base, buflen); 6328c2ecf20Sopenharmony_ci return -1; 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci if (base + buffer[base] >= buflen) { 6358c2ecf20Sopenharmony_ci ldm_error("base (%d) + buffer[base] (%d) >= buflen (%d)", base, 6368c2ecf20Sopenharmony_ci buffer[base], buflen); 6378c2ecf20Sopenharmony_ci return -1; 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci return buffer[base] + offset + 1; 6408c2ecf20Sopenharmony_ci} 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci/** 6438c2ecf20Sopenharmony_ci * ldm_get_vnum - Convert a variable-width, big endian number, into cpu order 6448c2ecf20Sopenharmony_ci * @block: Pointer to the variable-width number to convert 6458c2ecf20Sopenharmony_ci * 6468c2ecf20Sopenharmony_ci * Large numbers in the LDM Database are often stored in a packed format. Each 6478c2ecf20Sopenharmony_ci * number is prefixed by a one byte width marker. All numbers in the database 6488c2ecf20Sopenharmony_ci * are stored in big-endian byte order. This function reads one of these 6498c2ecf20Sopenharmony_ci * numbers and returns the result 6508c2ecf20Sopenharmony_ci * 6518c2ecf20Sopenharmony_ci * N.B. This function DOES NOT perform any range checking, though the most 6528c2ecf20Sopenharmony_ci * it will read is eight bytes. 6538c2ecf20Sopenharmony_ci * 6548c2ecf20Sopenharmony_ci * Return: n A number 6558c2ecf20Sopenharmony_ci * 0 Zero, or an error occurred 6568c2ecf20Sopenharmony_ci */ 6578c2ecf20Sopenharmony_cistatic u64 ldm_get_vnum (const u8 *block) 6588c2ecf20Sopenharmony_ci{ 6598c2ecf20Sopenharmony_ci u64 tmp = 0; 6608c2ecf20Sopenharmony_ci u8 length; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci BUG_ON (!block); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci length = *block++; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci if (length && length <= 8) 6678c2ecf20Sopenharmony_ci while (length--) 6688c2ecf20Sopenharmony_ci tmp = (tmp << 8) | *block++; 6698c2ecf20Sopenharmony_ci else 6708c2ecf20Sopenharmony_ci ldm_error ("Illegal length %d.", length); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci return tmp; 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci/** 6768c2ecf20Sopenharmony_ci * ldm_get_vstr - Read a length-prefixed string into a buffer 6778c2ecf20Sopenharmony_ci * @block: Pointer to the length marker 6788c2ecf20Sopenharmony_ci * @buffer: Location to copy string to 6798c2ecf20Sopenharmony_ci * @buflen: Size of the output buffer 6808c2ecf20Sopenharmony_ci * 6818c2ecf20Sopenharmony_ci * Many of the strings in the LDM Database are not NULL terminated. Instead 6828c2ecf20Sopenharmony_ci * they are prefixed by a one byte length marker. This function copies one of 6838c2ecf20Sopenharmony_ci * these strings into a buffer. 6848c2ecf20Sopenharmony_ci * 6858c2ecf20Sopenharmony_ci * N.B. This function DOES NOT perform any range checking on the input. 6868c2ecf20Sopenharmony_ci * If the buffer is too small, the output will be truncated. 6878c2ecf20Sopenharmony_ci * 6888c2ecf20Sopenharmony_ci * Return: 0, Error and @buffer contents are undefined 6898c2ecf20Sopenharmony_ci * n, String length in characters (excluding NULL) 6908c2ecf20Sopenharmony_ci * buflen-1, String was truncated. 6918c2ecf20Sopenharmony_ci */ 6928c2ecf20Sopenharmony_cistatic int ldm_get_vstr (const u8 *block, u8 *buffer, int buflen) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci int length; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci BUG_ON (!block || !buffer); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci length = block[0]; 6998c2ecf20Sopenharmony_ci if (length >= buflen) { 7008c2ecf20Sopenharmony_ci ldm_error ("Truncating string %d -> %d.", length, buflen); 7018c2ecf20Sopenharmony_ci length = buflen - 1; 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci memcpy (buffer, block + 1, length); 7048c2ecf20Sopenharmony_ci buffer[length] = 0; 7058c2ecf20Sopenharmony_ci return length; 7068c2ecf20Sopenharmony_ci} 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci/** 7108c2ecf20Sopenharmony_ci * ldm_parse_cmp3 - Read a raw VBLK Component object into a vblk structure 7118c2ecf20Sopenharmony_ci * @buffer: Block of data being worked on 7128c2ecf20Sopenharmony_ci * @buflen: Size of the block of data 7138c2ecf20Sopenharmony_ci * @vb: In-memory vblk in which to return information 7148c2ecf20Sopenharmony_ci * 7158c2ecf20Sopenharmony_ci * Read a raw VBLK Component object (version 3) into a vblk structure. 7168c2ecf20Sopenharmony_ci * 7178c2ecf20Sopenharmony_ci * Return: 'true' @vb contains a Component VBLK 7188c2ecf20Sopenharmony_ci * 'false' @vb contents are not defined 7198c2ecf20Sopenharmony_ci */ 7208c2ecf20Sopenharmony_cistatic bool ldm_parse_cmp3 (const u8 *buffer, int buflen, struct vblk *vb) 7218c2ecf20Sopenharmony_ci{ 7228c2ecf20Sopenharmony_ci int r_objid, r_name, r_vstate, r_child, r_parent, r_stripe, r_cols, len; 7238c2ecf20Sopenharmony_ci struct vblk_comp *comp; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci BUG_ON (!buffer || !vb); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci r_objid = ldm_relative (buffer, buflen, 0x18, 0); 7288c2ecf20Sopenharmony_ci r_name = ldm_relative (buffer, buflen, 0x18, r_objid); 7298c2ecf20Sopenharmony_ci r_vstate = ldm_relative (buffer, buflen, 0x18, r_name); 7308c2ecf20Sopenharmony_ci r_child = ldm_relative (buffer, buflen, 0x1D, r_vstate); 7318c2ecf20Sopenharmony_ci r_parent = ldm_relative (buffer, buflen, 0x2D, r_child); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci if (buffer[0x12] & VBLK_FLAG_COMP_STRIPE) { 7348c2ecf20Sopenharmony_ci r_stripe = ldm_relative (buffer, buflen, 0x2E, r_parent); 7358c2ecf20Sopenharmony_ci r_cols = ldm_relative (buffer, buflen, 0x2E, r_stripe); 7368c2ecf20Sopenharmony_ci len = r_cols; 7378c2ecf20Sopenharmony_ci } else { 7388c2ecf20Sopenharmony_ci r_stripe = 0; 7398c2ecf20Sopenharmony_ci r_cols = 0; 7408c2ecf20Sopenharmony_ci len = r_parent; 7418c2ecf20Sopenharmony_ci } 7428c2ecf20Sopenharmony_ci if (len < 0) 7438c2ecf20Sopenharmony_ci return false; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci len += VBLK_SIZE_CMP3; 7468c2ecf20Sopenharmony_ci if (len != get_unaligned_be32(buffer + 0x14)) 7478c2ecf20Sopenharmony_ci return false; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci comp = &vb->vblk.comp; 7508c2ecf20Sopenharmony_ci ldm_get_vstr (buffer + 0x18 + r_name, comp->state, 7518c2ecf20Sopenharmony_ci sizeof (comp->state)); 7528c2ecf20Sopenharmony_ci comp->type = buffer[0x18 + r_vstate]; 7538c2ecf20Sopenharmony_ci comp->children = ldm_get_vnum (buffer + 0x1D + r_vstate); 7548c2ecf20Sopenharmony_ci comp->parent_id = ldm_get_vnum (buffer + 0x2D + r_child); 7558c2ecf20Sopenharmony_ci comp->chunksize = r_stripe ? ldm_get_vnum (buffer+r_parent+0x2E) : 0; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci return true; 7588c2ecf20Sopenharmony_ci} 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci/** 7618c2ecf20Sopenharmony_ci * ldm_parse_dgr3 - Read a raw VBLK Disk Group object into a vblk structure 7628c2ecf20Sopenharmony_ci * @buffer: Block of data being worked on 7638c2ecf20Sopenharmony_ci * @buflen: Size of the block of data 7648c2ecf20Sopenharmony_ci * @vb: In-memory vblk in which to return information 7658c2ecf20Sopenharmony_ci * 7668c2ecf20Sopenharmony_ci * Read a raw VBLK Disk Group object (version 3) into a vblk structure. 7678c2ecf20Sopenharmony_ci * 7688c2ecf20Sopenharmony_ci * Return: 'true' @vb contains a Disk Group VBLK 7698c2ecf20Sopenharmony_ci * 'false' @vb contents are not defined 7708c2ecf20Sopenharmony_ci */ 7718c2ecf20Sopenharmony_cistatic int ldm_parse_dgr3 (const u8 *buffer, int buflen, struct vblk *vb) 7728c2ecf20Sopenharmony_ci{ 7738c2ecf20Sopenharmony_ci int r_objid, r_name, r_diskid, r_id1, r_id2, len; 7748c2ecf20Sopenharmony_ci struct vblk_dgrp *dgrp; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci BUG_ON (!buffer || !vb); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci r_objid = ldm_relative (buffer, buflen, 0x18, 0); 7798c2ecf20Sopenharmony_ci r_name = ldm_relative (buffer, buflen, 0x18, r_objid); 7808c2ecf20Sopenharmony_ci r_diskid = ldm_relative (buffer, buflen, 0x18, r_name); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci if (buffer[0x12] & VBLK_FLAG_DGR3_IDS) { 7838c2ecf20Sopenharmony_ci r_id1 = ldm_relative (buffer, buflen, 0x24, r_diskid); 7848c2ecf20Sopenharmony_ci r_id2 = ldm_relative (buffer, buflen, 0x24, r_id1); 7858c2ecf20Sopenharmony_ci len = r_id2; 7868c2ecf20Sopenharmony_ci } else { 7878c2ecf20Sopenharmony_ci r_id1 = 0; 7888c2ecf20Sopenharmony_ci r_id2 = 0; 7898c2ecf20Sopenharmony_ci len = r_diskid; 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci if (len < 0) 7928c2ecf20Sopenharmony_ci return false; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci len += VBLK_SIZE_DGR3; 7958c2ecf20Sopenharmony_ci if (len != get_unaligned_be32(buffer + 0x14)) 7968c2ecf20Sopenharmony_ci return false; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci dgrp = &vb->vblk.dgrp; 7998c2ecf20Sopenharmony_ci ldm_get_vstr (buffer + 0x18 + r_name, dgrp->disk_id, 8008c2ecf20Sopenharmony_ci sizeof (dgrp->disk_id)); 8018c2ecf20Sopenharmony_ci return true; 8028c2ecf20Sopenharmony_ci} 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci/** 8058c2ecf20Sopenharmony_ci * ldm_parse_dgr4 - Read a raw VBLK Disk Group object into a vblk structure 8068c2ecf20Sopenharmony_ci * @buffer: Block of data being worked on 8078c2ecf20Sopenharmony_ci * @buflen: Size of the block of data 8088c2ecf20Sopenharmony_ci * @vb: In-memory vblk in which to return information 8098c2ecf20Sopenharmony_ci * 8108c2ecf20Sopenharmony_ci * Read a raw VBLK Disk Group object (version 4) into a vblk structure. 8118c2ecf20Sopenharmony_ci * 8128c2ecf20Sopenharmony_ci * Return: 'true' @vb contains a Disk Group VBLK 8138c2ecf20Sopenharmony_ci * 'false' @vb contents are not defined 8148c2ecf20Sopenharmony_ci */ 8158c2ecf20Sopenharmony_cistatic bool ldm_parse_dgr4 (const u8 *buffer, int buflen, struct vblk *vb) 8168c2ecf20Sopenharmony_ci{ 8178c2ecf20Sopenharmony_ci char buf[64]; 8188c2ecf20Sopenharmony_ci int r_objid, r_name, r_id1, r_id2, len; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci BUG_ON (!buffer || !vb); 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci r_objid = ldm_relative (buffer, buflen, 0x18, 0); 8238c2ecf20Sopenharmony_ci r_name = ldm_relative (buffer, buflen, 0x18, r_objid); 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci if (buffer[0x12] & VBLK_FLAG_DGR4_IDS) { 8268c2ecf20Sopenharmony_ci r_id1 = ldm_relative (buffer, buflen, 0x44, r_name); 8278c2ecf20Sopenharmony_ci r_id2 = ldm_relative (buffer, buflen, 0x44, r_id1); 8288c2ecf20Sopenharmony_ci len = r_id2; 8298c2ecf20Sopenharmony_ci } else { 8308c2ecf20Sopenharmony_ci r_id1 = 0; 8318c2ecf20Sopenharmony_ci r_id2 = 0; 8328c2ecf20Sopenharmony_ci len = r_name; 8338c2ecf20Sopenharmony_ci } 8348c2ecf20Sopenharmony_ci if (len < 0) 8358c2ecf20Sopenharmony_ci return false; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci len += VBLK_SIZE_DGR4; 8388c2ecf20Sopenharmony_ci if (len != get_unaligned_be32(buffer + 0x14)) 8398c2ecf20Sopenharmony_ci return false; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci ldm_get_vstr (buffer + 0x18 + r_objid, buf, sizeof (buf)); 8428c2ecf20Sopenharmony_ci return true; 8438c2ecf20Sopenharmony_ci} 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci/** 8468c2ecf20Sopenharmony_ci * ldm_parse_dsk3 - Read a raw VBLK Disk object into a vblk structure 8478c2ecf20Sopenharmony_ci * @buffer: Block of data being worked on 8488c2ecf20Sopenharmony_ci * @buflen: Size of the block of data 8498c2ecf20Sopenharmony_ci * @vb: In-memory vblk in which to return information 8508c2ecf20Sopenharmony_ci * 8518c2ecf20Sopenharmony_ci * Read a raw VBLK Disk object (version 3) into a vblk structure. 8528c2ecf20Sopenharmony_ci * 8538c2ecf20Sopenharmony_ci * Return: 'true' @vb contains a Disk VBLK 8548c2ecf20Sopenharmony_ci * 'false' @vb contents are not defined 8558c2ecf20Sopenharmony_ci */ 8568c2ecf20Sopenharmony_cistatic bool ldm_parse_dsk3 (const u8 *buffer, int buflen, struct vblk *vb) 8578c2ecf20Sopenharmony_ci{ 8588c2ecf20Sopenharmony_ci int r_objid, r_name, r_diskid, r_altname, len; 8598c2ecf20Sopenharmony_ci struct vblk_disk *disk; 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci BUG_ON (!buffer || !vb); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci r_objid = ldm_relative (buffer, buflen, 0x18, 0); 8648c2ecf20Sopenharmony_ci r_name = ldm_relative (buffer, buflen, 0x18, r_objid); 8658c2ecf20Sopenharmony_ci r_diskid = ldm_relative (buffer, buflen, 0x18, r_name); 8668c2ecf20Sopenharmony_ci r_altname = ldm_relative (buffer, buflen, 0x18, r_diskid); 8678c2ecf20Sopenharmony_ci len = r_altname; 8688c2ecf20Sopenharmony_ci if (len < 0) 8698c2ecf20Sopenharmony_ci return false; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci len += VBLK_SIZE_DSK3; 8728c2ecf20Sopenharmony_ci if (len != get_unaligned_be32(buffer + 0x14)) 8738c2ecf20Sopenharmony_ci return false; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci disk = &vb->vblk.disk; 8768c2ecf20Sopenharmony_ci ldm_get_vstr (buffer + 0x18 + r_diskid, disk->alt_name, 8778c2ecf20Sopenharmony_ci sizeof (disk->alt_name)); 8788c2ecf20Sopenharmony_ci if (uuid_parse(buffer + 0x19 + r_name, &disk->disk_id)) 8798c2ecf20Sopenharmony_ci return false; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci return true; 8828c2ecf20Sopenharmony_ci} 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci/** 8858c2ecf20Sopenharmony_ci * ldm_parse_dsk4 - Read a raw VBLK Disk object into a vblk structure 8868c2ecf20Sopenharmony_ci * @buffer: Block of data being worked on 8878c2ecf20Sopenharmony_ci * @buflen: Size of the block of data 8888c2ecf20Sopenharmony_ci * @vb: In-memory vblk in which to return information 8898c2ecf20Sopenharmony_ci * 8908c2ecf20Sopenharmony_ci * Read a raw VBLK Disk object (version 4) into a vblk structure. 8918c2ecf20Sopenharmony_ci * 8928c2ecf20Sopenharmony_ci * Return: 'true' @vb contains a Disk VBLK 8938c2ecf20Sopenharmony_ci * 'false' @vb contents are not defined 8948c2ecf20Sopenharmony_ci */ 8958c2ecf20Sopenharmony_cistatic bool ldm_parse_dsk4 (const u8 *buffer, int buflen, struct vblk *vb) 8968c2ecf20Sopenharmony_ci{ 8978c2ecf20Sopenharmony_ci int r_objid, r_name, len; 8988c2ecf20Sopenharmony_ci struct vblk_disk *disk; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci BUG_ON (!buffer || !vb); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci r_objid = ldm_relative (buffer, buflen, 0x18, 0); 9038c2ecf20Sopenharmony_ci r_name = ldm_relative (buffer, buflen, 0x18, r_objid); 9048c2ecf20Sopenharmony_ci len = r_name; 9058c2ecf20Sopenharmony_ci if (len < 0) 9068c2ecf20Sopenharmony_ci return false; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci len += VBLK_SIZE_DSK4; 9098c2ecf20Sopenharmony_ci if (len != get_unaligned_be32(buffer + 0x14)) 9108c2ecf20Sopenharmony_ci return false; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci disk = &vb->vblk.disk; 9138c2ecf20Sopenharmony_ci import_uuid(&disk->disk_id, buffer + 0x18 + r_name); 9148c2ecf20Sopenharmony_ci return true; 9158c2ecf20Sopenharmony_ci} 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci/** 9188c2ecf20Sopenharmony_ci * ldm_parse_prt3 - Read a raw VBLK Partition object into a vblk structure 9198c2ecf20Sopenharmony_ci * @buffer: Block of data being worked on 9208c2ecf20Sopenharmony_ci * @buflen: Size of the block of data 9218c2ecf20Sopenharmony_ci * @vb: In-memory vblk in which to return information 9228c2ecf20Sopenharmony_ci * 9238c2ecf20Sopenharmony_ci * Read a raw VBLK Partition object (version 3) into a vblk structure. 9248c2ecf20Sopenharmony_ci * 9258c2ecf20Sopenharmony_ci * Return: 'true' @vb contains a Partition VBLK 9268c2ecf20Sopenharmony_ci * 'false' @vb contents are not defined 9278c2ecf20Sopenharmony_ci */ 9288c2ecf20Sopenharmony_cistatic bool ldm_parse_prt3(const u8 *buffer, int buflen, struct vblk *vb) 9298c2ecf20Sopenharmony_ci{ 9308c2ecf20Sopenharmony_ci int r_objid, r_name, r_size, r_parent, r_diskid, r_index, len; 9318c2ecf20Sopenharmony_ci struct vblk_part *part; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci BUG_ON(!buffer || !vb); 9348c2ecf20Sopenharmony_ci r_objid = ldm_relative(buffer, buflen, 0x18, 0); 9358c2ecf20Sopenharmony_ci if (r_objid < 0) { 9368c2ecf20Sopenharmony_ci ldm_error("r_objid %d < 0", r_objid); 9378c2ecf20Sopenharmony_ci return false; 9388c2ecf20Sopenharmony_ci } 9398c2ecf20Sopenharmony_ci r_name = ldm_relative(buffer, buflen, 0x18, r_objid); 9408c2ecf20Sopenharmony_ci if (r_name < 0) { 9418c2ecf20Sopenharmony_ci ldm_error("r_name %d < 0", r_name); 9428c2ecf20Sopenharmony_ci return false; 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci r_size = ldm_relative(buffer, buflen, 0x34, r_name); 9458c2ecf20Sopenharmony_ci if (r_size < 0) { 9468c2ecf20Sopenharmony_ci ldm_error("r_size %d < 0", r_size); 9478c2ecf20Sopenharmony_ci return false; 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci r_parent = ldm_relative(buffer, buflen, 0x34, r_size); 9508c2ecf20Sopenharmony_ci if (r_parent < 0) { 9518c2ecf20Sopenharmony_ci ldm_error("r_parent %d < 0", r_parent); 9528c2ecf20Sopenharmony_ci return false; 9538c2ecf20Sopenharmony_ci } 9548c2ecf20Sopenharmony_ci r_diskid = ldm_relative(buffer, buflen, 0x34, r_parent); 9558c2ecf20Sopenharmony_ci if (r_diskid < 0) { 9568c2ecf20Sopenharmony_ci ldm_error("r_diskid %d < 0", r_diskid); 9578c2ecf20Sopenharmony_ci return false; 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci if (buffer[0x12] & VBLK_FLAG_PART_INDEX) { 9608c2ecf20Sopenharmony_ci r_index = ldm_relative(buffer, buflen, 0x34, r_diskid); 9618c2ecf20Sopenharmony_ci if (r_index < 0) { 9628c2ecf20Sopenharmony_ci ldm_error("r_index %d < 0", r_index); 9638c2ecf20Sopenharmony_ci return false; 9648c2ecf20Sopenharmony_ci } 9658c2ecf20Sopenharmony_ci len = r_index; 9668c2ecf20Sopenharmony_ci } else { 9678c2ecf20Sopenharmony_ci r_index = 0; 9688c2ecf20Sopenharmony_ci len = r_diskid; 9698c2ecf20Sopenharmony_ci } 9708c2ecf20Sopenharmony_ci if (len < 0) { 9718c2ecf20Sopenharmony_ci ldm_error("len %d < 0", len); 9728c2ecf20Sopenharmony_ci return false; 9738c2ecf20Sopenharmony_ci } 9748c2ecf20Sopenharmony_ci len += VBLK_SIZE_PRT3; 9758c2ecf20Sopenharmony_ci if (len > get_unaligned_be32(buffer + 0x14)) { 9768c2ecf20Sopenharmony_ci ldm_error("len %d > BE32(buffer + 0x14) %d", len, 9778c2ecf20Sopenharmony_ci get_unaligned_be32(buffer + 0x14)); 9788c2ecf20Sopenharmony_ci return false; 9798c2ecf20Sopenharmony_ci } 9808c2ecf20Sopenharmony_ci part = &vb->vblk.part; 9818c2ecf20Sopenharmony_ci part->start = get_unaligned_be64(buffer + 0x24 + r_name); 9828c2ecf20Sopenharmony_ci part->volume_offset = get_unaligned_be64(buffer + 0x2C + r_name); 9838c2ecf20Sopenharmony_ci part->size = ldm_get_vnum(buffer + 0x34 + r_name); 9848c2ecf20Sopenharmony_ci part->parent_id = ldm_get_vnum(buffer + 0x34 + r_size); 9858c2ecf20Sopenharmony_ci part->disk_id = ldm_get_vnum(buffer + 0x34 + r_parent); 9868c2ecf20Sopenharmony_ci if (vb->flags & VBLK_FLAG_PART_INDEX) 9878c2ecf20Sopenharmony_ci part->partnum = buffer[0x35 + r_diskid]; 9888c2ecf20Sopenharmony_ci else 9898c2ecf20Sopenharmony_ci part->partnum = 0; 9908c2ecf20Sopenharmony_ci return true; 9918c2ecf20Sopenharmony_ci} 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci/** 9948c2ecf20Sopenharmony_ci * ldm_parse_vol5 - Read a raw VBLK Volume object into a vblk structure 9958c2ecf20Sopenharmony_ci * @buffer: Block of data being worked on 9968c2ecf20Sopenharmony_ci * @buflen: Size of the block of data 9978c2ecf20Sopenharmony_ci * @vb: In-memory vblk in which to return information 9988c2ecf20Sopenharmony_ci * 9998c2ecf20Sopenharmony_ci * Read a raw VBLK Volume object (version 5) into a vblk structure. 10008c2ecf20Sopenharmony_ci * 10018c2ecf20Sopenharmony_ci * Return: 'true' @vb contains a Volume VBLK 10028c2ecf20Sopenharmony_ci * 'false' @vb contents are not defined 10038c2ecf20Sopenharmony_ci */ 10048c2ecf20Sopenharmony_cistatic bool ldm_parse_vol5(const u8 *buffer, int buflen, struct vblk *vb) 10058c2ecf20Sopenharmony_ci{ 10068c2ecf20Sopenharmony_ci int r_objid, r_name, r_vtype, r_disable_drive_letter, r_child, r_size; 10078c2ecf20Sopenharmony_ci int r_id1, r_id2, r_size2, r_drive, len; 10088c2ecf20Sopenharmony_ci struct vblk_volu *volu; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci BUG_ON(!buffer || !vb); 10118c2ecf20Sopenharmony_ci r_objid = ldm_relative(buffer, buflen, 0x18, 0); 10128c2ecf20Sopenharmony_ci if (r_objid < 0) { 10138c2ecf20Sopenharmony_ci ldm_error("r_objid %d < 0", r_objid); 10148c2ecf20Sopenharmony_ci return false; 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci r_name = ldm_relative(buffer, buflen, 0x18, r_objid); 10178c2ecf20Sopenharmony_ci if (r_name < 0) { 10188c2ecf20Sopenharmony_ci ldm_error("r_name %d < 0", r_name); 10198c2ecf20Sopenharmony_ci return false; 10208c2ecf20Sopenharmony_ci } 10218c2ecf20Sopenharmony_ci r_vtype = ldm_relative(buffer, buflen, 0x18, r_name); 10228c2ecf20Sopenharmony_ci if (r_vtype < 0) { 10238c2ecf20Sopenharmony_ci ldm_error("r_vtype %d < 0", r_vtype); 10248c2ecf20Sopenharmony_ci return false; 10258c2ecf20Sopenharmony_ci } 10268c2ecf20Sopenharmony_ci r_disable_drive_letter = ldm_relative(buffer, buflen, 0x18, r_vtype); 10278c2ecf20Sopenharmony_ci if (r_disable_drive_letter < 0) { 10288c2ecf20Sopenharmony_ci ldm_error("r_disable_drive_letter %d < 0", 10298c2ecf20Sopenharmony_ci r_disable_drive_letter); 10308c2ecf20Sopenharmony_ci return false; 10318c2ecf20Sopenharmony_ci } 10328c2ecf20Sopenharmony_ci r_child = ldm_relative(buffer, buflen, 0x2D, r_disable_drive_letter); 10338c2ecf20Sopenharmony_ci if (r_child < 0) { 10348c2ecf20Sopenharmony_ci ldm_error("r_child %d < 0", r_child); 10358c2ecf20Sopenharmony_ci return false; 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci r_size = ldm_relative(buffer, buflen, 0x3D, r_child); 10388c2ecf20Sopenharmony_ci if (r_size < 0) { 10398c2ecf20Sopenharmony_ci ldm_error("r_size %d < 0", r_size); 10408c2ecf20Sopenharmony_ci return false; 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ci if (buffer[0x12] & VBLK_FLAG_VOLU_ID1) { 10438c2ecf20Sopenharmony_ci r_id1 = ldm_relative(buffer, buflen, 0x52, r_size); 10448c2ecf20Sopenharmony_ci if (r_id1 < 0) { 10458c2ecf20Sopenharmony_ci ldm_error("r_id1 %d < 0", r_id1); 10468c2ecf20Sopenharmony_ci return false; 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci } else 10498c2ecf20Sopenharmony_ci r_id1 = r_size; 10508c2ecf20Sopenharmony_ci if (buffer[0x12] & VBLK_FLAG_VOLU_ID2) { 10518c2ecf20Sopenharmony_ci r_id2 = ldm_relative(buffer, buflen, 0x52, r_id1); 10528c2ecf20Sopenharmony_ci if (r_id2 < 0) { 10538c2ecf20Sopenharmony_ci ldm_error("r_id2 %d < 0", r_id2); 10548c2ecf20Sopenharmony_ci return false; 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci } else 10578c2ecf20Sopenharmony_ci r_id2 = r_id1; 10588c2ecf20Sopenharmony_ci if (buffer[0x12] & VBLK_FLAG_VOLU_SIZE) { 10598c2ecf20Sopenharmony_ci r_size2 = ldm_relative(buffer, buflen, 0x52, r_id2); 10608c2ecf20Sopenharmony_ci if (r_size2 < 0) { 10618c2ecf20Sopenharmony_ci ldm_error("r_size2 %d < 0", r_size2); 10628c2ecf20Sopenharmony_ci return false; 10638c2ecf20Sopenharmony_ci } 10648c2ecf20Sopenharmony_ci } else 10658c2ecf20Sopenharmony_ci r_size2 = r_id2; 10668c2ecf20Sopenharmony_ci if (buffer[0x12] & VBLK_FLAG_VOLU_DRIVE) { 10678c2ecf20Sopenharmony_ci r_drive = ldm_relative(buffer, buflen, 0x52, r_size2); 10688c2ecf20Sopenharmony_ci if (r_drive < 0) { 10698c2ecf20Sopenharmony_ci ldm_error("r_drive %d < 0", r_drive); 10708c2ecf20Sopenharmony_ci return false; 10718c2ecf20Sopenharmony_ci } 10728c2ecf20Sopenharmony_ci } else 10738c2ecf20Sopenharmony_ci r_drive = r_size2; 10748c2ecf20Sopenharmony_ci len = r_drive; 10758c2ecf20Sopenharmony_ci if (len < 0) { 10768c2ecf20Sopenharmony_ci ldm_error("len %d < 0", len); 10778c2ecf20Sopenharmony_ci return false; 10788c2ecf20Sopenharmony_ci } 10798c2ecf20Sopenharmony_ci len += VBLK_SIZE_VOL5; 10808c2ecf20Sopenharmony_ci if (len > get_unaligned_be32(buffer + 0x14)) { 10818c2ecf20Sopenharmony_ci ldm_error("len %d > BE32(buffer + 0x14) %d", len, 10828c2ecf20Sopenharmony_ci get_unaligned_be32(buffer + 0x14)); 10838c2ecf20Sopenharmony_ci return false; 10848c2ecf20Sopenharmony_ci } 10858c2ecf20Sopenharmony_ci volu = &vb->vblk.volu; 10868c2ecf20Sopenharmony_ci ldm_get_vstr(buffer + 0x18 + r_name, volu->volume_type, 10878c2ecf20Sopenharmony_ci sizeof(volu->volume_type)); 10888c2ecf20Sopenharmony_ci memcpy(volu->volume_state, buffer + 0x18 + r_disable_drive_letter, 10898c2ecf20Sopenharmony_ci sizeof(volu->volume_state)); 10908c2ecf20Sopenharmony_ci volu->size = ldm_get_vnum(buffer + 0x3D + r_child); 10918c2ecf20Sopenharmony_ci volu->partition_type = buffer[0x41 + r_size]; 10928c2ecf20Sopenharmony_ci memcpy(volu->guid, buffer + 0x42 + r_size, sizeof(volu->guid)); 10938c2ecf20Sopenharmony_ci if (buffer[0x12] & VBLK_FLAG_VOLU_DRIVE) { 10948c2ecf20Sopenharmony_ci ldm_get_vstr(buffer + 0x52 + r_size, volu->drive_hint, 10958c2ecf20Sopenharmony_ci sizeof(volu->drive_hint)); 10968c2ecf20Sopenharmony_ci } 10978c2ecf20Sopenharmony_ci return true; 10988c2ecf20Sopenharmony_ci} 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci/** 11018c2ecf20Sopenharmony_ci * ldm_parse_vblk - Read a raw VBLK object into a vblk structure 11028c2ecf20Sopenharmony_ci * @buf: Block of data being worked on 11038c2ecf20Sopenharmony_ci * @len: Size of the block of data 11048c2ecf20Sopenharmony_ci * @vb: In-memory vblk in which to return information 11058c2ecf20Sopenharmony_ci * 11068c2ecf20Sopenharmony_ci * Read a raw VBLK object into a vblk structure. This function just reads the 11078c2ecf20Sopenharmony_ci * information common to all VBLK types, then delegates the rest of the work to 11088c2ecf20Sopenharmony_ci * helper functions: ldm_parse_*. 11098c2ecf20Sopenharmony_ci * 11108c2ecf20Sopenharmony_ci * Return: 'true' @vb contains a VBLK 11118c2ecf20Sopenharmony_ci * 'false' @vb contents are not defined 11128c2ecf20Sopenharmony_ci */ 11138c2ecf20Sopenharmony_cistatic bool ldm_parse_vblk (const u8 *buf, int len, struct vblk *vb) 11148c2ecf20Sopenharmony_ci{ 11158c2ecf20Sopenharmony_ci bool result = false; 11168c2ecf20Sopenharmony_ci int r_objid; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci BUG_ON (!buf || !vb); 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci r_objid = ldm_relative (buf, len, 0x18, 0); 11218c2ecf20Sopenharmony_ci if (r_objid < 0) { 11228c2ecf20Sopenharmony_ci ldm_error ("VBLK header is corrupt."); 11238c2ecf20Sopenharmony_ci return false; 11248c2ecf20Sopenharmony_ci } 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci vb->flags = buf[0x12]; 11278c2ecf20Sopenharmony_ci vb->type = buf[0x13]; 11288c2ecf20Sopenharmony_ci vb->obj_id = ldm_get_vnum (buf + 0x18); 11298c2ecf20Sopenharmony_ci ldm_get_vstr (buf+0x18+r_objid, vb->name, sizeof (vb->name)); 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci switch (vb->type) { 11328c2ecf20Sopenharmony_ci case VBLK_CMP3: result = ldm_parse_cmp3 (buf, len, vb); break; 11338c2ecf20Sopenharmony_ci case VBLK_DSK3: result = ldm_parse_dsk3 (buf, len, vb); break; 11348c2ecf20Sopenharmony_ci case VBLK_DSK4: result = ldm_parse_dsk4 (buf, len, vb); break; 11358c2ecf20Sopenharmony_ci case VBLK_DGR3: result = ldm_parse_dgr3 (buf, len, vb); break; 11368c2ecf20Sopenharmony_ci case VBLK_DGR4: result = ldm_parse_dgr4 (buf, len, vb); break; 11378c2ecf20Sopenharmony_ci case VBLK_PRT3: result = ldm_parse_prt3 (buf, len, vb); break; 11388c2ecf20Sopenharmony_ci case VBLK_VOL5: result = ldm_parse_vol5 (buf, len, vb); break; 11398c2ecf20Sopenharmony_ci } 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci if (result) 11428c2ecf20Sopenharmony_ci ldm_debug ("Parsed VBLK 0x%llx (type: 0x%02x) ok.", 11438c2ecf20Sopenharmony_ci (unsigned long long) vb->obj_id, vb->type); 11448c2ecf20Sopenharmony_ci else 11458c2ecf20Sopenharmony_ci ldm_error ("Failed to parse VBLK 0x%llx (type: 0x%02x).", 11468c2ecf20Sopenharmony_ci (unsigned long long) vb->obj_id, vb->type); 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci return result; 11498c2ecf20Sopenharmony_ci} 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci/** 11538c2ecf20Sopenharmony_ci * ldm_ldmdb_add - Adds a raw VBLK entry to the ldmdb database 11548c2ecf20Sopenharmony_ci * @data: Raw VBLK to add to the database 11558c2ecf20Sopenharmony_ci * @len: Size of the raw VBLK 11568c2ecf20Sopenharmony_ci * @ldb: Cache of the database structures 11578c2ecf20Sopenharmony_ci * 11588c2ecf20Sopenharmony_ci * The VBLKs are sorted into categories. Partitions are also sorted by offset. 11598c2ecf20Sopenharmony_ci * 11608c2ecf20Sopenharmony_ci * N.B. This function does not check the validity of the VBLKs. 11618c2ecf20Sopenharmony_ci * 11628c2ecf20Sopenharmony_ci * Return: 'true' The VBLK was added 11638c2ecf20Sopenharmony_ci * 'false' An error occurred 11648c2ecf20Sopenharmony_ci */ 11658c2ecf20Sopenharmony_cistatic bool ldm_ldmdb_add (u8 *data, int len, struct ldmdb *ldb) 11668c2ecf20Sopenharmony_ci{ 11678c2ecf20Sopenharmony_ci struct vblk *vb; 11688c2ecf20Sopenharmony_ci struct list_head *item; 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci BUG_ON (!data || !ldb); 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci vb = kmalloc (sizeof (*vb), GFP_KERNEL); 11738c2ecf20Sopenharmony_ci if (!vb) { 11748c2ecf20Sopenharmony_ci ldm_crit ("Out of memory."); 11758c2ecf20Sopenharmony_ci return false; 11768c2ecf20Sopenharmony_ci } 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci if (!ldm_parse_vblk (data, len, vb)) { 11798c2ecf20Sopenharmony_ci kfree(vb); 11808c2ecf20Sopenharmony_ci return false; /* Already logged */ 11818c2ecf20Sopenharmony_ci } 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci /* Put vblk into the correct list. */ 11848c2ecf20Sopenharmony_ci switch (vb->type) { 11858c2ecf20Sopenharmony_ci case VBLK_DGR3: 11868c2ecf20Sopenharmony_ci case VBLK_DGR4: 11878c2ecf20Sopenharmony_ci list_add (&vb->list, &ldb->v_dgrp); 11888c2ecf20Sopenharmony_ci break; 11898c2ecf20Sopenharmony_ci case VBLK_DSK3: 11908c2ecf20Sopenharmony_ci case VBLK_DSK4: 11918c2ecf20Sopenharmony_ci list_add (&vb->list, &ldb->v_disk); 11928c2ecf20Sopenharmony_ci break; 11938c2ecf20Sopenharmony_ci case VBLK_VOL5: 11948c2ecf20Sopenharmony_ci list_add (&vb->list, &ldb->v_volu); 11958c2ecf20Sopenharmony_ci break; 11968c2ecf20Sopenharmony_ci case VBLK_CMP3: 11978c2ecf20Sopenharmony_ci list_add (&vb->list, &ldb->v_comp); 11988c2ecf20Sopenharmony_ci break; 11998c2ecf20Sopenharmony_ci case VBLK_PRT3: 12008c2ecf20Sopenharmony_ci /* Sort by the partition's start sector. */ 12018c2ecf20Sopenharmony_ci list_for_each (item, &ldb->v_part) { 12028c2ecf20Sopenharmony_ci struct vblk *v = list_entry (item, struct vblk, list); 12038c2ecf20Sopenharmony_ci if ((v->vblk.part.disk_id == vb->vblk.part.disk_id) && 12048c2ecf20Sopenharmony_ci (v->vblk.part.start > vb->vblk.part.start)) { 12058c2ecf20Sopenharmony_ci list_add_tail (&vb->list, &v->list); 12068c2ecf20Sopenharmony_ci return true; 12078c2ecf20Sopenharmony_ci } 12088c2ecf20Sopenharmony_ci } 12098c2ecf20Sopenharmony_ci list_add_tail (&vb->list, &ldb->v_part); 12108c2ecf20Sopenharmony_ci break; 12118c2ecf20Sopenharmony_ci } 12128c2ecf20Sopenharmony_ci return true; 12138c2ecf20Sopenharmony_ci} 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci/** 12168c2ecf20Sopenharmony_ci * ldm_frag_add - Add a VBLK fragment to a list 12178c2ecf20Sopenharmony_ci * @data: Raw fragment to be added to the list 12188c2ecf20Sopenharmony_ci * @size: Size of the raw fragment 12198c2ecf20Sopenharmony_ci * @frags: Linked list of VBLK fragments 12208c2ecf20Sopenharmony_ci * 12218c2ecf20Sopenharmony_ci * Fragmented VBLKs may not be consecutive in the database, so they are placed 12228c2ecf20Sopenharmony_ci * in a list so they can be pieced together later. 12238c2ecf20Sopenharmony_ci * 12248c2ecf20Sopenharmony_ci * Return: 'true' Success, the VBLK was added to the list 12258c2ecf20Sopenharmony_ci * 'false' Error, a problem occurred 12268c2ecf20Sopenharmony_ci */ 12278c2ecf20Sopenharmony_cistatic bool ldm_frag_add (const u8 *data, int size, struct list_head *frags) 12288c2ecf20Sopenharmony_ci{ 12298c2ecf20Sopenharmony_ci struct frag *f; 12308c2ecf20Sopenharmony_ci struct list_head *item; 12318c2ecf20Sopenharmony_ci int rec, num, group; 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci BUG_ON (!data || !frags); 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci if (size < 2 * VBLK_SIZE_HEAD) { 12368c2ecf20Sopenharmony_ci ldm_error("Value of size is too small."); 12378c2ecf20Sopenharmony_ci return false; 12388c2ecf20Sopenharmony_ci } 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci group = get_unaligned_be32(data + 0x08); 12418c2ecf20Sopenharmony_ci rec = get_unaligned_be16(data + 0x0C); 12428c2ecf20Sopenharmony_ci num = get_unaligned_be16(data + 0x0E); 12438c2ecf20Sopenharmony_ci if ((num < 1) || (num > 4)) { 12448c2ecf20Sopenharmony_ci ldm_error ("A VBLK claims to have %d parts.", num); 12458c2ecf20Sopenharmony_ci return false; 12468c2ecf20Sopenharmony_ci } 12478c2ecf20Sopenharmony_ci if (rec >= num) { 12488c2ecf20Sopenharmony_ci ldm_error("REC value (%d) exceeds NUM value (%d)", rec, num); 12498c2ecf20Sopenharmony_ci return false; 12508c2ecf20Sopenharmony_ci } 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci list_for_each (item, frags) { 12538c2ecf20Sopenharmony_ci f = list_entry (item, struct frag, list); 12548c2ecf20Sopenharmony_ci if (f->group == group) 12558c2ecf20Sopenharmony_ci goto found; 12568c2ecf20Sopenharmony_ci } 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci f = kmalloc (sizeof (*f) + size*num, GFP_KERNEL); 12598c2ecf20Sopenharmony_ci if (!f) { 12608c2ecf20Sopenharmony_ci ldm_crit ("Out of memory."); 12618c2ecf20Sopenharmony_ci return false; 12628c2ecf20Sopenharmony_ci } 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci f->group = group; 12658c2ecf20Sopenharmony_ci f->num = num; 12668c2ecf20Sopenharmony_ci f->rec = rec; 12678c2ecf20Sopenharmony_ci f->map = 0xFF << num; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci list_add_tail (&f->list, frags); 12708c2ecf20Sopenharmony_cifound: 12718c2ecf20Sopenharmony_ci if (rec >= f->num) { 12728c2ecf20Sopenharmony_ci ldm_error("REC value (%d) exceeds NUM value (%d)", rec, f->num); 12738c2ecf20Sopenharmony_ci return false; 12748c2ecf20Sopenharmony_ci } 12758c2ecf20Sopenharmony_ci if (f->map & (1 << rec)) { 12768c2ecf20Sopenharmony_ci ldm_error ("Duplicate VBLK, part %d.", rec); 12778c2ecf20Sopenharmony_ci f->map &= 0x7F; /* Mark the group as broken */ 12788c2ecf20Sopenharmony_ci return false; 12798c2ecf20Sopenharmony_ci } 12808c2ecf20Sopenharmony_ci f->map |= (1 << rec); 12818c2ecf20Sopenharmony_ci if (!rec) 12828c2ecf20Sopenharmony_ci memcpy(f->data, data, VBLK_SIZE_HEAD); 12838c2ecf20Sopenharmony_ci data += VBLK_SIZE_HEAD; 12848c2ecf20Sopenharmony_ci size -= VBLK_SIZE_HEAD; 12858c2ecf20Sopenharmony_ci memcpy(f->data + VBLK_SIZE_HEAD + rec * size, data, size); 12868c2ecf20Sopenharmony_ci return true; 12878c2ecf20Sopenharmony_ci} 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci/** 12908c2ecf20Sopenharmony_ci * ldm_frag_free - Free a linked list of VBLK fragments 12918c2ecf20Sopenharmony_ci * @list: Linked list of fragments 12928c2ecf20Sopenharmony_ci * 12938c2ecf20Sopenharmony_ci * Free a linked list of VBLK fragments 12948c2ecf20Sopenharmony_ci * 12958c2ecf20Sopenharmony_ci * Return: none 12968c2ecf20Sopenharmony_ci */ 12978c2ecf20Sopenharmony_cistatic void ldm_frag_free (struct list_head *list) 12988c2ecf20Sopenharmony_ci{ 12998c2ecf20Sopenharmony_ci struct list_head *item, *tmp; 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci BUG_ON (!list); 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci list_for_each_safe (item, tmp, list) 13048c2ecf20Sopenharmony_ci kfree (list_entry (item, struct frag, list)); 13058c2ecf20Sopenharmony_ci} 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci/** 13088c2ecf20Sopenharmony_ci * ldm_frag_commit - Validate fragmented VBLKs and add them to the database 13098c2ecf20Sopenharmony_ci * @frags: Linked list of VBLK fragments 13108c2ecf20Sopenharmony_ci * @ldb: Cache of the database structures 13118c2ecf20Sopenharmony_ci * 13128c2ecf20Sopenharmony_ci * Now that all the fragmented VBLKs have been collected, they must be added to 13138c2ecf20Sopenharmony_ci * the database for later use. 13148c2ecf20Sopenharmony_ci * 13158c2ecf20Sopenharmony_ci * Return: 'true' All the fragments we added successfully 13168c2ecf20Sopenharmony_ci * 'false' One or more of the fragments we invalid 13178c2ecf20Sopenharmony_ci */ 13188c2ecf20Sopenharmony_cistatic bool ldm_frag_commit (struct list_head *frags, struct ldmdb *ldb) 13198c2ecf20Sopenharmony_ci{ 13208c2ecf20Sopenharmony_ci struct frag *f; 13218c2ecf20Sopenharmony_ci struct list_head *item; 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci BUG_ON (!frags || !ldb); 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci list_for_each (item, frags) { 13268c2ecf20Sopenharmony_ci f = list_entry (item, struct frag, list); 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci if (f->map != 0xFF) { 13298c2ecf20Sopenharmony_ci ldm_error ("VBLK group %d is incomplete (0x%02x).", 13308c2ecf20Sopenharmony_ci f->group, f->map); 13318c2ecf20Sopenharmony_ci return false; 13328c2ecf20Sopenharmony_ci } 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci if (!ldm_ldmdb_add (f->data, f->num*ldb->vm.vblk_size, ldb)) 13358c2ecf20Sopenharmony_ci return false; /* Already logged */ 13368c2ecf20Sopenharmony_ci } 13378c2ecf20Sopenharmony_ci return true; 13388c2ecf20Sopenharmony_ci} 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci/** 13418c2ecf20Sopenharmony_ci * ldm_get_vblks - Read the on-disk database of VBLKs into memory 13428c2ecf20Sopenharmony_ci * @state: Partition check state including device holding the LDM Database 13438c2ecf20Sopenharmony_ci * @base: Offset, into @state->bdev, of the database 13448c2ecf20Sopenharmony_ci * @ldb: Cache of the database structures 13458c2ecf20Sopenharmony_ci * 13468c2ecf20Sopenharmony_ci * To use the information from the VBLKs, they need to be read from the disk, 13478c2ecf20Sopenharmony_ci * unpacked and validated. We cache them in @ldb according to their type. 13488c2ecf20Sopenharmony_ci * 13498c2ecf20Sopenharmony_ci * Return: 'true' All the VBLKs were read successfully 13508c2ecf20Sopenharmony_ci * 'false' An error occurred 13518c2ecf20Sopenharmony_ci */ 13528c2ecf20Sopenharmony_cistatic bool ldm_get_vblks(struct parsed_partitions *state, unsigned long base, 13538c2ecf20Sopenharmony_ci struct ldmdb *ldb) 13548c2ecf20Sopenharmony_ci{ 13558c2ecf20Sopenharmony_ci int size, perbuf, skip, finish, s, v, recs; 13568c2ecf20Sopenharmony_ci u8 *data = NULL; 13578c2ecf20Sopenharmony_ci Sector sect; 13588c2ecf20Sopenharmony_ci bool result = false; 13598c2ecf20Sopenharmony_ci LIST_HEAD (frags); 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci BUG_ON(!state || !ldb); 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci size = ldb->vm.vblk_size; 13648c2ecf20Sopenharmony_ci perbuf = 512 / size; 13658c2ecf20Sopenharmony_ci skip = ldb->vm.vblk_offset >> 9; /* Bytes to sectors */ 13668c2ecf20Sopenharmony_ci finish = (size * ldb->vm.last_vblk_seq) >> 9; 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci for (s = skip; s < finish; s++) { /* For each sector */ 13698c2ecf20Sopenharmony_ci data = read_part_sector(state, base + OFF_VMDB + s, §); 13708c2ecf20Sopenharmony_ci if (!data) { 13718c2ecf20Sopenharmony_ci ldm_crit ("Disk read failed."); 13728c2ecf20Sopenharmony_ci goto out; 13738c2ecf20Sopenharmony_ci } 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci for (v = 0; v < perbuf; v++, data+=size) { /* For each vblk */ 13768c2ecf20Sopenharmony_ci if (MAGIC_VBLK != get_unaligned_be32(data)) { 13778c2ecf20Sopenharmony_ci ldm_error ("Expected to find a VBLK."); 13788c2ecf20Sopenharmony_ci goto out; 13798c2ecf20Sopenharmony_ci } 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci recs = get_unaligned_be16(data + 0x0E); /* Number of records */ 13828c2ecf20Sopenharmony_ci if (recs == 1) { 13838c2ecf20Sopenharmony_ci if (!ldm_ldmdb_add (data, size, ldb)) 13848c2ecf20Sopenharmony_ci goto out; /* Already logged */ 13858c2ecf20Sopenharmony_ci } else if (recs > 1) { 13868c2ecf20Sopenharmony_ci if (!ldm_frag_add (data, size, &frags)) 13878c2ecf20Sopenharmony_ci goto out; /* Already logged */ 13888c2ecf20Sopenharmony_ci } 13898c2ecf20Sopenharmony_ci /* else Record is not in use, ignore it. */ 13908c2ecf20Sopenharmony_ci } 13918c2ecf20Sopenharmony_ci put_dev_sector (sect); 13928c2ecf20Sopenharmony_ci data = NULL; 13938c2ecf20Sopenharmony_ci } 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci result = ldm_frag_commit (&frags, ldb); /* Failures, already logged */ 13968c2ecf20Sopenharmony_ciout: 13978c2ecf20Sopenharmony_ci if (data) 13988c2ecf20Sopenharmony_ci put_dev_sector (sect); 13998c2ecf20Sopenharmony_ci ldm_frag_free (&frags); 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci return result; 14028c2ecf20Sopenharmony_ci} 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci/** 14058c2ecf20Sopenharmony_ci * ldm_free_vblks - Free a linked list of vblk's 14068c2ecf20Sopenharmony_ci * @lh: Head of a linked list of struct vblk 14078c2ecf20Sopenharmony_ci * 14088c2ecf20Sopenharmony_ci * Free a list of vblk's and free the memory used to maintain the list. 14098c2ecf20Sopenharmony_ci * 14108c2ecf20Sopenharmony_ci * Return: none 14118c2ecf20Sopenharmony_ci */ 14128c2ecf20Sopenharmony_cistatic void ldm_free_vblks (struct list_head *lh) 14138c2ecf20Sopenharmony_ci{ 14148c2ecf20Sopenharmony_ci struct list_head *item, *tmp; 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci BUG_ON (!lh); 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci list_for_each_safe (item, tmp, lh) 14198c2ecf20Sopenharmony_ci kfree (list_entry (item, struct vblk, list)); 14208c2ecf20Sopenharmony_ci} 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci/** 14248c2ecf20Sopenharmony_ci * ldm_partition - Find out whether a device is a dynamic disk and handle it 14258c2ecf20Sopenharmony_ci * @state: Partition check state including device holding the LDM Database 14268c2ecf20Sopenharmony_ci * 14278c2ecf20Sopenharmony_ci * This determines whether the device @bdev is a dynamic disk and if so creates 14288c2ecf20Sopenharmony_ci * the partitions necessary in the gendisk structure pointed to by @hd. 14298c2ecf20Sopenharmony_ci * 14308c2ecf20Sopenharmony_ci * We create a dummy device 1, which contains the LDM database, and then create 14318c2ecf20Sopenharmony_ci * each partition described by the LDM database in sequence as devices 2+. For 14328c2ecf20Sopenharmony_ci * example, if the device is hda, we would have: hda1: LDM database, hda2, hda3, 14338c2ecf20Sopenharmony_ci * and so on: the actual data containing partitions. 14348c2ecf20Sopenharmony_ci * 14358c2ecf20Sopenharmony_ci * Return: 1 Success, @state->bdev is a dynamic disk and we handled it 14368c2ecf20Sopenharmony_ci * 0 Success, @state->bdev is not a dynamic disk 14378c2ecf20Sopenharmony_ci * -1 An error occurred before enough information had been read 14388c2ecf20Sopenharmony_ci * Or @state->bdev is a dynamic disk, but it may be corrupted 14398c2ecf20Sopenharmony_ci */ 14408c2ecf20Sopenharmony_ciint ldm_partition(struct parsed_partitions *state) 14418c2ecf20Sopenharmony_ci{ 14428c2ecf20Sopenharmony_ci struct ldmdb *ldb; 14438c2ecf20Sopenharmony_ci unsigned long base; 14448c2ecf20Sopenharmony_ci int result = -1; 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci BUG_ON(!state); 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci /* Look for signs of a Dynamic Disk */ 14498c2ecf20Sopenharmony_ci if (!ldm_validate_partition_table(state)) 14508c2ecf20Sopenharmony_ci return 0; 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci ldb = kmalloc (sizeof (*ldb), GFP_KERNEL); 14538c2ecf20Sopenharmony_ci if (!ldb) { 14548c2ecf20Sopenharmony_ci ldm_crit ("Out of memory."); 14558c2ecf20Sopenharmony_ci goto out; 14568c2ecf20Sopenharmony_ci } 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci /* Parse and check privheads. */ 14598c2ecf20Sopenharmony_ci if (!ldm_validate_privheads(state, &ldb->ph)) 14608c2ecf20Sopenharmony_ci goto out; /* Already logged */ 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci /* All further references are relative to base (database start). */ 14638c2ecf20Sopenharmony_ci base = ldb->ph.config_start; 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci /* Parse and check tocs and vmdb. */ 14668c2ecf20Sopenharmony_ci if (!ldm_validate_tocblocks(state, base, ldb) || 14678c2ecf20Sopenharmony_ci !ldm_validate_vmdb(state, base, ldb)) 14688c2ecf20Sopenharmony_ci goto out; /* Already logged */ 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci /* Initialize vblk lists in ldmdb struct */ 14718c2ecf20Sopenharmony_ci INIT_LIST_HEAD (&ldb->v_dgrp); 14728c2ecf20Sopenharmony_ci INIT_LIST_HEAD (&ldb->v_disk); 14738c2ecf20Sopenharmony_ci INIT_LIST_HEAD (&ldb->v_volu); 14748c2ecf20Sopenharmony_ci INIT_LIST_HEAD (&ldb->v_comp); 14758c2ecf20Sopenharmony_ci INIT_LIST_HEAD (&ldb->v_part); 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci if (!ldm_get_vblks(state, base, ldb)) { 14788c2ecf20Sopenharmony_ci ldm_crit ("Failed to read the VBLKs from the database."); 14798c2ecf20Sopenharmony_ci goto cleanup; 14808c2ecf20Sopenharmony_ci } 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci /* Finally, create the data partition devices. */ 14838c2ecf20Sopenharmony_ci if (ldm_create_data_partitions(state, ldb)) { 14848c2ecf20Sopenharmony_ci ldm_debug ("Parsed LDM database successfully."); 14858c2ecf20Sopenharmony_ci result = 1; 14868c2ecf20Sopenharmony_ci } 14878c2ecf20Sopenharmony_ci /* else Already logged */ 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_cicleanup: 14908c2ecf20Sopenharmony_ci ldm_free_vblks (&ldb->v_dgrp); 14918c2ecf20Sopenharmony_ci ldm_free_vblks (&ldb->v_disk); 14928c2ecf20Sopenharmony_ci ldm_free_vblks (&ldb->v_volu); 14938c2ecf20Sopenharmony_ci ldm_free_vblks (&ldb->v_comp); 14948c2ecf20Sopenharmony_ci ldm_free_vblks (&ldb->v_part); 14958c2ecf20Sopenharmony_ciout: 14968c2ecf20Sopenharmony_ci kfree (ldb); 14978c2ecf20Sopenharmony_ci return result; 14988c2ecf20Sopenharmony_ci} 1499