137d1a121SAndrey V. Elsukov /*- 23728855aSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 33728855aSPedro F. Giffuni * 437d1a121SAndrey V. Elsukov * Copyright (c) 2012 Andrey V. Elsukov <ae@FreeBSD.org> 537d1a121SAndrey V. Elsukov * All rights reserved. 637d1a121SAndrey V. Elsukov * 737d1a121SAndrey V. Elsukov * Redistribution and use in source and binary forms, with or without 837d1a121SAndrey V. Elsukov * modification, are permitted provided that the following conditions 937d1a121SAndrey V. Elsukov * are met: 1037d1a121SAndrey V. Elsukov * 1137d1a121SAndrey V. Elsukov * 1. Redistributions of source code must retain the above copyright 1237d1a121SAndrey V. Elsukov * notice, this list of conditions and the following disclaimer. 1337d1a121SAndrey V. Elsukov * 2. Redistributions in binary form must reproduce the above copyright 1437d1a121SAndrey V. Elsukov * notice, this list of conditions and the following disclaimer in the 1537d1a121SAndrey V. Elsukov * documentation and/or other materials provided with the distribution. 1637d1a121SAndrey V. Elsukov * 1737d1a121SAndrey V. Elsukov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1837d1a121SAndrey V. Elsukov * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1937d1a121SAndrey V. Elsukov * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2037d1a121SAndrey V. Elsukov * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2137d1a121SAndrey V. Elsukov * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2237d1a121SAndrey V. Elsukov * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2337d1a121SAndrey V. Elsukov * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2437d1a121SAndrey V. Elsukov * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2537d1a121SAndrey V. Elsukov * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2637d1a121SAndrey V. Elsukov * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2737d1a121SAndrey V. Elsukov */ 2837d1a121SAndrey V. Elsukov 2937d1a121SAndrey V. Elsukov #include <sys/cdefs.h> 3037d1a121SAndrey V. Elsukov __FBSDID("$FreeBSD$"); 3137d1a121SAndrey V. Elsukov 3237d1a121SAndrey V. Elsukov #include <sys/param.h> 3337d1a121SAndrey V. Elsukov #include <sys/bio.h> 3437d1a121SAndrey V. Elsukov #include <sys/diskmbr.h> 3537d1a121SAndrey V. Elsukov #include <sys/endian.h> 3637d1a121SAndrey V. Elsukov #include <sys/gpt.h> 3737d1a121SAndrey V. Elsukov #include <sys/kernel.h> 3837d1a121SAndrey V. Elsukov #include <sys/kobj.h> 3937d1a121SAndrey V. Elsukov #include <sys/limits.h> 4037d1a121SAndrey V. Elsukov #include <sys/lock.h> 4137d1a121SAndrey V. Elsukov #include <sys/malloc.h> 4237d1a121SAndrey V. Elsukov #include <sys/mutex.h> 4337d1a121SAndrey V. Elsukov #include <sys/queue.h> 4437d1a121SAndrey V. Elsukov #include <sys/sbuf.h> 4537d1a121SAndrey V. Elsukov #include <sys/systm.h> 4637d1a121SAndrey V. Elsukov #include <sys/sysctl.h> 4737d1a121SAndrey V. Elsukov #include <sys/uuid.h> 4837d1a121SAndrey V. Elsukov #include <geom/geom.h> 4937d1a121SAndrey V. Elsukov #include <geom/part/g_part.h> 5037d1a121SAndrey V. Elsukov 5137d1a121SAndrey V. Elsukov #include "g_part_if.h" 5237d1a121SAndrey V. Elsukov 5337d1a121SAndrey V. Elsukov FEATURE(geom_part_ldm, "GEOM partitioning class for LDM support"); 5437d1a121SAndrey V. Elsukov 5537d1a121SAndrey V. Elsukov SYSCTL_DECL(_kern_geom_part); 56*7029da5cSPawel Biernacki static SYSCTL_NODE(_kern_geom_part, OID_AUTO, ldm, 57*7029da5cSPawel Biernacki CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 5837d1a121SAndrey V. Elsukov "GEOM_PART_LDM Logical Disk Manager"); 5937d1a121SAndrey V. Elsukov 6037d1a121SAndrey V. Elsukov static u_int ldm_debug = 0; 61f1104f71SAndrey V. Elsukov SYSCTL_UINT(_kern_geom_part_ldm, OID_AUTO, debug, 62af3b2549SHans Petter Selasky CTLFLAG_RWTUN, &ldm_debug, 0, "Debug level"); 6337d1a121SAndrey V. Elsukov 6437d1a121SAndrey V. Elsukov /* 6537d1a121SAndrey V. Elsukov * This allows access to mirrored LDM volumes. Since we do not 6637d1a121SAndrey V. Elsukov * doing mirroring here, it is not enabled by default. 6737d1a121SAndrey V. Elsukov */ 6837d1a121SAndrey V. Elsukov static u_int show_mirrors = 0; 69f1104f71SAndrey V. Elsukov SYSCTL_UINT(_kern_geom_part_ldm, OID_AUTO, show_mirrors, 70af3b2549SHans Petter Selasky CTLFLAG_RWTUN, &show_mirrors, 0, "Show mirrored volumes"); 7137d1a121SAndrey V. Elsukov 7237d1a121SAndrey V. Elsukov #define LDM_DEBUG(lvl, fmt, ...) do { \ 7337d1a121SAndrey V. Elsukov if (ldm_debug >= (lvl)) { \ 7437d1a121SAndrey V. Elsukov printf("GEOM_PART: " fmt "\n", __VA_ARGS__); \ 7537d1a121SAndrey V. Elsukov } \ 7637d1a121SAndrey V. Elsukov } while (0) 7737d1a121SAndrey V. Elsukov #define LDM_DUMP(buf, size) do { \ 7837d1a121SAndrey V. Elsukov if (ldm_debug > 1) { \ 7937d1a121SAndrey V. Elsukov hexdump(buf, size, NULL, 0); \ 8037d1a121SAndrey V. Elsukov } \ 8137d1a121SAndrey V. Elsukov } while (0) 8237d1a121SAndrey V. Elsukov 8337d1a121SAndrey V. Elsukov /* 8437d1a121SAndrey V. Elsukov * There are internal representations of LDM structures. 8537d1a121SAndrey V. Elsukov * 8637d1a121SAndrey V. Elsukov * We do not keep all fields of on-disk structures, only most useful. 8737d1a121SAndrey V. Elsukov * All numbers in an on-disk structures are in big-endian format. 8837d1a121SAndrey V. Elsukov */ 8937d1a121SAndrey V. Elsukov 9037d1a121SAndrey V. Elsukov /* 9137d1a121SAndrey V. Elsukov * Private header is 512 bytes long. There are three copies on each disk. 9237d1a121SAndrey V. Elsukov * Offset and sizes are in sectors. Location of each copy: 9337d1a121SAndrey V. Elsukov * - the first offset is relative to the disk start; 9437d1a121SAndrey V. Elsukov * - the second and third offset are relative to the LDM database start. 9537d1a121SAndrey V. Elsukov * 9637d1a121SAndrey V. Elsukov * On a disk partitioned with GPT, the LDM has not first private header. 9737d1a121SAndrey V. Elsukov */ 9837d1a121SAndrey V. Elsukov #define LDM_PH_MBRINDEX 0 9937d1a121SAndrey V. Elsukov #define LDM_PH_GPTINDEX 2 10037d1a121SAndrey V. Elsukov static const uint64_t ldm_ph_off[] = {6, 1856, 2047}; 10137d1a121SAndrey V. Elsukov #define LDM_VERSION_2K 0x2000b 10237d1a121SAndrey V. Elsukov #define LDM_VERSION_VISTA 0x2000c 10337d1a121SAndrey V. Elsukov #define LDM_PH_VERSION_OFF 0x00c 10437d1a121SAndrey V. Elsukov #define LDM_PH_DISKGUID_OFF 0x030 10537d1a121SAndrey V. Elsukov #define LDM_PH_DGGUID_OFF 0x0b0 10637d1a121SAndrey V. Elsukov #define LDM_PH_DGNAME_OFF 0x0f0 10737d1a121SAndrey V. Elsukov #define LDM_PH_START_OFF 0x11b 10837d1a121SAndrey V. Elsukov #define LDM_PH_SIZE_OFF 0x123 10937d1a121SAndrey V. Elsukov #define LDM_PH_DB_OFF 0x12b 11037d1a121SAndrey V. Elsukov #define LDM_PH_DBSIZE_OFF 0x133 11137d1a121SAndrey V. Elsukov #define LDM_PH_TH1_OFF 0x13b 11237d1a121SAndrey V. Elsukov #define LDM_PH_TH2_OFF 0x143 11337d1a121SAndrey V. Elsukov #define LDM_PH_CONFSIZE_OFF 0x153 11437d1a121SAndrey V. Elsukov #define LDM_PH_LOGSIZE_OFF 0x15b 11537d1a121SAndrey V. Elsukov #define LDM_PH_SIGN "PRIVHEAD" 11637d1a121SAndrey V. Elsukov struct ldm_privhdr { 11737d1a121SAndrey V. Elsukov struct uuid disk_guid; 11837d1a121SAndrey V. Elsukov struct uuid dg_guid; 11937d1a121SAndrey V. Elsukov u_char dg_name[32]; 12037d1a121SAndrey V. Elsukov uint64_t start; /* logical disk start */ 12137d1a121SAndrey V. Elsukov uint64_t size; /* logical disk size */ 12237d1a121SAndrey V. Elsukov uint64_t db_offset; /* LDM database start */ 12337d1a121SAndrey V. Elsukov #define LDM_DB_SIZE 2048 12437d1a121SAndrey V. Elsukov uint64_t db_size; /* LDM database size */ 12537d1a121SAndrey V. Elsukov #define LDM_TH_COUNT 2 12637d1a121SAndrey V. Elsukov uint64_t th_offset[LDM_TH_COUNT]; /* TOC header offsets */ 12737d1a121SAndrey V. Elsukov uint64_t conf_size; /* configuration size */ 12837d1a121SAndrey V. Elsukov uint64_t log_size; /* size of log */ 12937d1a121SAndrey V. Elsukov }; 13037d1a121SAndrey V. Elsukov 13137d1a121SAndrey V. Elsukov /* 13237d1a121SAndrey V. Elsukov * Table of contents header is 512 bytes long. 13337d1a121SAndrey V. Elsukov * There are two identical copies at offsets from the private header. 13437d1a121SAndrey V. Elsukov * Offsets are relative to the LDM database start. 13537d1a121SAndrey V. Elsukov */ 13637d1a121SAndrey V. Elsukov #define LDM_TH_SIGN "TOCBLOCK" 13737d1a121SAndrey V. Elsukov #define LDM_TH_NAME1 "config" 13837d1a121SAndrey V. Elsukov #define LDM_TH_NAME2 "log" 13937d1a121SAndrey V. Elsukov #define LDM_TH_NAME1_OFF 0x024 14037d1a121SAndrey V. Elsukov #define LDM_TH_CONF_OFF 0x02e 14137d1a121SAndrey V. Elsukov #define LDM_TH_CONFSIZE_OFF 0x036 14237d1a121SAndrey V. Elsukov #define LDM_TH_NAME2_OFF 0x046 14337d1a121SAndrey V. Elsukov #define LDM_TH_LOG_OFF 0x050 14437d1a121SAndrey V. Elsukov #define LDM_TH_LOGSIZE_OFF 0x058 14537d1a121SAndrey V. Elsukov struct ldm_tochdr { 14637d1a121SAndrey V. Elsukov uint64_t conf_offset; /* configuration offset */ 14737d1a121SAndrey V. Elsukov uint64_t log_offset; /* log offset */ 14837d1a121SAndrey V. Elsukov }; 14937d1a121SAndrey V. Elsukov 15037d1a121SAndrey V. Elsukov /* 15137d1a121SAndrey V. Elsukov * LDM database header is 512 bytes long. 15237d1a121SAndrey V. Elsukov */ 15337d1a121SAndrey V. Elsukov #define LDM_VMDB_SIGN "VMDB" 15437d1a121SAndrey V. Elsukov #define LDM_DB_LASTSEQ_OFF 0x004 15537d1a121SAndrey V. Elsukov #define LDM_DB_SIZE_OFF 0x008 15637d1a121SAndrey V. Elsukov #define LDM_DB_STATUS_OFF 0x010 15737d1a121SAndrey V. Elsukov #define LDM_DB_VERSION_OFF 0x012 15837d1a121SAndrey V. Elsukov #define LDM_DB_DGNAME_OFF 0x016 15937d1a121SAndrey V. Elsukov #define LDM_DB_DGGUID_OFF 0x035 16037d1a121SAndrey V. Elsukov struct ldm_vmdbhdr { 16137d1a121SAndrey V. Elsukov uint32_t last_seq; /* sequence number of last VBLK */ 16237d1a121SAndrey V. Elsukov uint32_t size; /* size of VBLK */ 16337d1a121SAndrey V. Elsukov }; 16437d1a121SAndrey V. Elsukov 16537d1a121SAndrey V. Elsukov /* 16637d1a121SAndrey V. Elsukov * The LDM database configuration section contains VMDB header and 16737d1a121SAndrey V. Elsukov * many VBLKs. Each VBLK represents a disk group, disk partition, 16837d1a121SAndrey V. Elsukov * component or volume. 16937d1a121SAndrey V. Elsukov * 17037d1a121SAndrey V. Elsukov * The most interesting for us are volumes, they are represents 17137d1a121SAndrey V. Elsukov * partitions in the GEOM_PART meaning. But volume VBLK does not 17237d1a121SAndrey V. Elsukov * contain all information needed to create GEOM provider. And we 17337d1a121SAndrey V. Elsukov * should get this information from the related VBLK. This is how 17437d1a121SAndrey V. Elsukov * VBLK releated: 17537d1a121SAndrey V. Elsukov * Volumes <- Components <- Partitions -> Disks 17637d1a121SAndrey V. Elsukov * 17737d1a121SAndrey V. Elsukov * One volume can contain several components. In this case LDM 17837d1a121SAndrey V. Elsukov * does mirroring of volume data to each component. 17937d1a121SAndrey V. Elsukov * 18037d1a121SAndrey V. Elsukov * Also each component can contain several partitions (spanned or 18137d1a121SAndrey V. Elsukov * striped volumes). 18237d1a121SAndrey V. Elsukov */ 18337d1a121SAndrey V. Elsukov 18437d1a121SAndrey V. Elsukov struct ldm_component { 18537d1a121SAndrey V. Elsukov uint64_t id; /* object id */ 18637d1a121SAndrey V. Elsukov uint64_t vol_id; /* parent volume object id */ 18737d1a121SAndrey V. Elsukov 18837d1a121SAndrey V. Elsukov int count; 18937d1a121SAndrey V. Elsukov LIST_HEAD(, ldm_partition) partitions; 19037d1a121SAndrey V. Elsukov LIST_ENTRY(ldm_component) entry; 19137d1a121SAndrey V. Elsukov }; 19237d1a121SAndrey V. Elsukov 19337d1a121SAndrey V. Elsukov struct ldm_volume { 19437d1a121SAndrey V. Elsukov uint64_t id; /* object id */ 19537d1a121SAndrey V. Elsukov uint64_t size; /* volume size */ 19637d1a121SAndrey V. Elsukov uint8_t number; /* used for ordering */ 19737d1a121SAndrey V. Elsukov uint8_t part_type; /* partition type */ 19837d1a121SAndrey V. Elsukov 19937d1a121SAndrey V. Elsukov int count; 20037d1a121SAndrey V. Elsukov LIST_HEAD(, ldm_component) components; 20137d1a121SAndrey V. Elsukov LIST_ENTRY(ldm_volume) entry; 20237d1a121SAndrey V. Elsukov }; 20337d1a121SAndrey V. Elsukov 20437d1a121SAndrey V. Elsukov struct ldm_disk { 20537d1a121SAndrey V. Elsukov uint64_t id; /* object id */ 20637d1a121SAndrey V. Elsukov struct uuid guid; /* disk guid */ 20737d1a121SAndrey V. Elsukov 20837d1a121SAndrey V. Elsukov LIST_ENTRY(ldm_disk) entry; 20937d1a121SAndrey V. Elsukov }; 21037d1a121SAndrey V. Elsukov 21137d1a121SAndrey V. Elsukov #if 0 21237d1a121SAndrey V. Elsukov struct ldm_disk_group { 21337d1a121SAndrey V. Elsukov uint64_t id; /* object id */ 21437d1a121SAndrey V. Elsukov struct uuid guid; /* disk group guid */ 21537d1a121SAndrey V. Elsukov u_char name[32]; /* disk group name */ 21637d1a121SAndrey V. Elsukov 21737d1a121SAndrey V. Elsukov LIST_ENTRY(ldm_disk_group) entry; 21837d1a121SAndrey V. Elsukov }; 21937d1a121SAndrey V. Elsukov #endif 22037d1a121SAndrey V. Elsukov 22137d1a121SAndrey V. Elsukov struct ldm_partition { 22237d1a121SAndrey V. Elsukov uint64_t id; /* object id */ 22337d1a121SAndrey V. Elsukov uint64_t disk_id; /* disk object id */ 22437d1a121SAndrey V. Elsukov uint64_t comp_id; /* parent component object id */ 22537d1a121SAndrey V. Elsukov uint64_t start; /* offset relative to disk start */ 22637d1a121SAndrey V. Elsukov uint64_t offset; /* offset for spanned volumes */ 22737d1a121SAndrey V. Elsukov uint64_t size; /* partition size */ 22837d1a121SAndrey V. Elsukov 22937d1a121SAndrey V. Elsukov LIST_ENTRY(ldm_partition) entry; 23037d1a121SAndrey V. Elsukov }; 23137d1a121SAndrey V. Elsukov 23237d1a121SAndrey V. Elsukov /* 23337d1a121SAndrey V. Elsukov * Each VBLK is 128 bytes long and has standard 16 bytes header. 23437d1a121SAndrey V. Elsukov * Some of VBLK's fields are fixed size, but others has variable size. 23537d1a121SAndrey V. Elsukov * Fields with variable size are prefixed with one byte length marker. 23637d1a121SAndrey V. Elsukov * Some fields are strings and also can have fixed size and variable. 23737d1a121SAndrey V. Elsukov * Strings with fixed size are NULL-terminated, others are not. 23837d1a121SAndrey V. Elsukov * All VBLKs have same several first fields: 23937d1a121SAndrey V. Elsukov * Offset Size Description 24037d1a121SAndrey V. Elsukov * ---------------+---------------+-------------------------- 24137d1a121SAndrey V. Elsukov * 0x00 16 standard VBLK header 24237d1a121SAndrey V. Elsukov * 0x10 2 update status 24337d1a121SAndrey V. Elsukov * 0x13 1 VBLK type 24437d1a121SAndrey V. Elsukov * 0x18 PS object id 24537d1a121SAndrey V. Elsukov * 0x18+ PN object name 24637d1a121SAndrey V. Elsukov * 24737d1a121SAndrey V. Elsukov * o Offset 0x18+ means '0x18 + length of all variable-width fields' 24837d1a121SAndrey V. Elsukov * o 'P' in size column means 'prefixed' (variable-width), 24937d1a121SAndrey V. Elsukov * 'S' - string, 'N' - number. 25037d1a121SAndrey V. Elsukov */ 25137d1a121SAndrey V. Elsukov #define LDM_VBLK_SIGN "VBLK" 25237d1a121SAndrey V. Elsukov #define LDM_VBLK_SEQ_OFF 0x04 25337d1a121SAndrey V. Elsukov #define LDM_VBLK_GROUP_OFF 0x08 25437d1a121SAndrey V. Elsukov #define LDM_VBLK_INDEX_OFF 0x0c 25537d1a121SAndrey V. Elsukov #define LDM_VBLK_COUNT_OFF 0x0e 25637d1a121SAndrey V. Elsukov #define LDM_VBLK_TYPE_OFF 0x13 25737d1a121SAndrey V. Elsukov #define LDM_VBLK_OID_OFF 0x18 25837d1a121SAndrey V. Elsukov struct ldm_vblkhdr { 25937d1a121SAndrey V. Elsukov uint32_t seq; /* sequence number */ 26037d1a121SAndrey V. Elsukov uint32_t group; /* group number */ 26137d1a121SAndrey V. Elsukov uint16_t index; /* index in the group */ 26237d1a121SAndrey V. Elsukov uint16_t count; /* number of entries in the group */ 26337d1a121SAndrey V. Elsukov }; 26437d1a121SAndrey V. Elsukov 26537d1a121SAndrey V. Elsukov #define LDM_VBLK_T_COMPONENT 0x32 26637d1a121SAndrey V. Elsukov #define LDM_VBLK_T_PARTITION 0x33 26737d1a121SAndrey V. Elsukov #define LDM_VBLK_T_DISK 0x34 26837d1a121SAndrey V. Elsukov #define LDM_VBLK_T_DISKGROUP 0x35 26937d1a121SAndrey V. Elsukov #define LDM_VBLK_T_DISK4 0x44 27037d1a121SAndrey V. Elsukov #define LDM_VBLK_T_DISKGROUP4 0x45 27137d1a121SAndrey V. Elsukov #define LDM_VBLK_T_VOLUME 0x51 27237d1a121SAndrey V. Elsukov struct ldm_vblk { 27337d1a121SAndrey V. Elsukov uint8_t type; /* VBLK type */ 27437d1a121SAndrey V. Elsukov union { 27537d1a121SAndrey V. Elsukov uint64_t id; 27637d1a121SAndrey V. Elsukov struct ldm_volume vol; 27737d1a121SAndrey V. Elsukov struct ldm_component comp; 27837d1a121SAndrey V. Elsukov struct ldm_disk disk; 27937d1a121SAndrey V. Elsukov struct ldm_partition part; 28037d1a121SAndrey V. Elsukov #if 0 28137d1a121SAndrey V. Elsukov struct ldm_disk_group disk_group; 28237d1a121SAndrey V. Elsukov #endif 28337d1a121SAndrey V. Elsukov } u; 28437d1a121SAndrey V. Elsukov LIST_ENTRY(ldm_vblk) entry; 28537d1a121SAndrey V. Elsukov }; 28637d1a121SAndrey V. Elsukov 28737d1a121SAndrey V. Elsukov /* 28837d1a121SAndrey V. Elsukov * Some VBLKs contains a bit more data than can fit into 128 bytes. These 28937d1a121SAndrey V. Elsukov * VBLKs are called eXtended VBLK. Before parsing, the data from these VBLK 29037d1a121SAndrey V. Elsukov * should be placed into continuous memory buffer. We can determine xVBLK 29137d1a121SAndrey V. Elsukov * by the count field in the standard VBLK header (count > 1). 29237d1a121SAndrey V. Elsukov */ 29337d1a121SAndrey V. Elsukov struct ldm_xvblk { 29437d1a121SAndrey V. Elsukov uint32_t group; /* xVBLK group number */ 29537d1a121SAndrey V. Elsukov uint32_t size; /* the total size of xVBLK */ 29637d1a121SAndrey V. Elsukov uint8_t map; /* bitmask of currently saved VBLKs */ 29737d1a121SAndrey V. Elsukov u_char *data; /* xVBLK data */ 29837d1a121SAndrey V. Elsukov 29937d1a121SAndrey V. Elsukov LIST_ENTRY(ldm_xvblk) entry; 30037d1a121SAndrey V. Elsukov }; 30137d1a121SAndrey V. Elsukov 30237d1a121SAndrey V. Elsukov /* The internal representation of LDM database. */ 30337d1a121SAndrey V. Elsukov struct ldm_db { 30437d1a121SAndrey V. Elsukov struct ldm_privhdr ph; /* private header */ 30537d1a121SAndrey V. Elsukov struct ldm_tochdr th; /* TOC header */ 30637d1a121SAndrey V. Elsukov struct ldm_vmdbhdr dh; /* VMDB header */ 30737d1a121SAndrey V. Elsukov 30837d1a121SAndrey V. Elsukov LIST_HEAD(, ldm_volume) volumes; 30937d1a121SAndrey V. Elsukov LIST_HEAD(, ldm_disk) disks; 31037d1a121SAndrey V. Elsukov LIST_HEAD(, ldm_vblk) vblks; 31137d1a121SAndrey V. Elsukov LIST_HEAD(, ldm_xvblk) xvblks; 31237d1a121SAndrey V. Elsukov }; 31337d1a121SAndrey V. Elsukov 31437d1a121SAndrey V. Elsukov static struct uuid gpt_uuid_ms_ldm_metadata = GPT_ENT_TYPE_MS_LDM_METADATA; 31537d1a121SAndrey V. Elsukov 31637d1a121SAndrey V. Elsukov struct g_part_ldm_table { 31737d1a121SAndrey V. Elsukov struct g_part_table base; 31837d1a121SAndrey V. Elsukov uint64_t db_offset; 31937d1a121SAndrey V. Elsukov int is_gpt; 32037d1a121SAndrey V. Elsukov }; 32137d1a121SAndrey V. Elsukov struct g_part_ldm_entry { 32237d1a121SAndrey V. Elsukov struct g_part_entry base; 32337d1a121SAndrey V. Elsukov uint8_t type; 32437d1a121SAndrey V. Elsukov }; 32537d1a121SAndrey V. Elsukov 32637d1a121SAndrey V. Elsukov static int g_part_ldm_add(struct g_part_table *, struct g_part_entry *, 32737d1a121SAndrey V. Elsukov struct g_part_parms *); 32837d1a121SAndrey V. Elsukov static int g_part_ldm_bootcode(struct g_part_table *, struct g_part_parms *); 32937d1a121SAndrey V. Elsukov static int g_part_ldm_create(struct g_part_table *, struct g_part_parms *); 33037d1a121SAndrey V. Elsukov static int g_part_ldm_destroy(struct g_part_table *, struct g_part_parms *); 33137d1a121SAndrey V. Elsukov static void g_part_ldm_dumpconf(struct g_part_table *, struct g_part_entry *, 33237d1a121SAndrey V. Elsukov struct sbuf *, const char *); 33337d1a121SAndrey V. Elsukov static int g_part_ldm_dumpto(struct g_part_table *, struct g_part_entry *); 33437d1a121SAndrey V. Elsukov static int g_part_ldm_modify(struct g_part_table *, struct g_part_entry *, 33537d1a121SAndrey V. Elsukov struct g_part_parms *); 33637d1a121SAndrey V. Elsukov static const char *g_part_ldm_name(struct g_part_table *, struct g_part_entry *, 33737d1a121SAndrey V. Elsukov char *, size_t); 33837d1a121SAndrey V. Elsukov static int g_part_ldm_probe(struct g_part_table *, struct g_consumer *); 33937d1a121SAndrey V. Elsukov static int g_part_ldm_read(struct g_part_table *, struct g_consumer *); 34037d1a121SAndrey V. Elsukov static const char *g_part_ldm_type(struct g_part_table *, struct g_part_entry *, 34137d1a121SAndrey V. Elsukov char *, size_t); 34237d1a121SAndrey V. Elsukov static int g_part_ldm_write(struct g_part_table *, struct g_consumer *); 34337d1a121SAndrey V. Elsukov 34437d1a121SAndrey V. Elsukov static kobj_method_t g_part_ldm_methods[] = { 34537d1a121SAndrey V. Elsukov KOBJMETHOD(g_part_add, g_part_ldm_add), 34637d1a121SAndrey V. Elsukov KOBJMETHOD(g_part_bootcode, g_part_ldm_bootcode), 34737d1a121SAndrey V. Elsukov KOBJMETHOD(g_part_create, g_part_ldm_create), 34837d1a121SAndrey V. Elsukov KOBJMETHOD(g_part_destroy, g_part_ldm_destroy), 34937d1a121SAndrey V. Elsukov KOBJMETHOD(g_part_dumpconf, g_part_ldm_dumpconf), 35037d1a121SAndrey V. Elsukov KOBJMETHOD(g_part_dumpto, g_part_ldm_dumpto), 35137d1a121SAndrey V. Elsukov KOBJMETHOD(g_part_modify, g_part_ldm_modify), 35237d1a121SAndrey V. Elsukov KOBJMETHOD(g_part_name, g_part_ldm_name), 35337d1a121SAndrey V. Elsukov KOBJMETHOD(g_part_probe, g_part_ldm_probe), 35437d1a121SAndrey V. Elsukov KOBJMETHOD(g_part_read, g_part_ldm_read), 35537d1a121SAndrey V. Elsukov KOBJMETHOD(g_part_type, g_part_ldm_type), 35637d1a121SAndrey V. Elsukov KOBJMETHOD(g_part_write, g_part_ldm_write), 35737d1a121SAndrey V. Elsukov { 0, 0 } 35837d1a121SAndrey V. Elsukov }; 35937d1a121SAndrey V. Elsukov 36037d1a121SAndrey V. Elsukov static struct g_part_scheme g_part_ldm_scheme = { 36137d1a121SAndrey V. Elsukov "LDM", 36237d1a121SAndrey V. Elsukov g_part_ldm_methods, 36337d1a121SAndrey V. Elsukov sizeof(struct g_part_ldm_table), 36437d1a121SAndrey V. Elsukov .gps_entrysz = sizeof(struct g_part_ldm_entry) 36537d1a121SAndrey V. Elsukov }; 36637d1a121SAndrey V. Elsukov G_PART_SCHEME_DECLARE(g_part_ldm); 36774d6c131SKyle Evans MODULE_VERSION(geom_part_ldm, 0); 36837d1a121SAndrey V. Elsukov 36937d1a121SAndrey V. Elsukov static struct g_part_ldm_alias { 37037d1a121SAndrey V. Elsukov u_char typ; 37137d1a121SAndrey V. Elsukov int alias; 37237d1a121SAndrey V. Elsukov } ldm_alias_match[] = { 373a0a8412bSEd Maste { DOSPTYP_386BSD, G_PART_ALIAS_FREEBSD }, 37437d1a121SAndrey V. Elsukov { DOSPTYP_FAT32, G_PART_ALIAS_MS_FAT32 }, 375b525a10aSEd Maste { DOSPTYP_FAT32LBA, G_PART_ALIAS_MS_FAT32LBA }, 37637d1a121SAndrey V. Elsukov { DOSPTYP_LDM, G_PART_ALIAS_MS_LDM_DATA }, 37737d1a121SAndrey V. Elsukov { DOSPTYP_LINLVM, G_PART_ALIAS_LINUX_LVM }, 37837d1a121SAndrey V. Elsukov { DOSPTYP_LINRAID, G_PART_ALIAS_LINUX_RAID }, 379a0a8412bSEd Maste { DOSPTYP_LINSWP, G_PART_ALIAS_LINUX_SWAP }, 380a0a8412bSEd Maste { DOSPTYP_LINUX, G_PART_ALIAS_LINUX_DATA }, 381a0a8412bSEd Maste { DOSPTYP_NTFS, G_PART_ALIAS_MS_NTFS }, 38237d1a121SAndrey V. Elsukov }; 38337d1a121SAndrey V. Elsukov 38437d1a121SAndrey V. Elsukov static u_char* 38537d1a121SAndrey V. Elsukov ldm_privhdr_read(struct g_consumer *cp, uint64_t off, int *error) 38637d1a121SAndrey V. Elsukov { 38737d1a121SAndrey V. Elsukov struct g_provider *pp; 38837d1a121SAndrey V. Elsukov u_char *buf; 38937d1a121SAndrey V. Elsukov 39037d1a121SAndrey V. Elsukov pp = cp->provider; 39137d1a121SAndrey V. Elsukov buf = g_read_data(cp, off, pp->sectorsize, error); 39237d1a121SAndrey V. Elsukov if (buf == NULL) 39337d1a121SAndrey V. Elsukov return (NULL); 39437d1a121SAndrey V. Elsukov 39537d1a121SAndrey V. Elsukov if (memcmp(buf, LDM_PH_SIGN, strlen(LDM_PH_SIGN)) != 0) { 39637d1a121SAndrey V. Elsukov LDM_DEBUG(1, "%s: invalid LDM private header signature", 39737d1a121SAndrey V. Elsukov pp->name); 39837d1a121SAndrey V. Elsukov g_free(buf); 39937d1a121SAndrey V. Elsukov buf = NULL; 40037d1a121SAndrey V. Elsukov *error = EINVAL; 40137d1a121SAndrey V. Elsukov } 40237d1a121SAndrey V. Elsukov return (buf); 40337d1a121SAndrey V. Elsukov } 40437d1a121SAndrey V. Elsukov 40537d1a121SAndrey V. Elsukov static int 40637d1a121SAndrey V. Elsukov ldm_privhdr_parse(struct g_consumer *cp, struct ldm_privhdr *hdr, 40737d1a121SAndrey V. Elsukov const u_char *buf) 40837d1a121SAndrey V. Elsukov { 40937d1a121SAndrey V. Elsukov uint32_t version; 41037d1a121SAndrey V. Elsukov int error; 41137d1a121SAndrey V. Elsukov 41237d1a121SAndrey V. Elsukov memset(hdr, 0, sizeof(*hdr)); 41337d1a121SAndrey V. Elsukov version = be32dec(buf + LDM_PH_VERSION_OFF); 41437d1a121SAndrey V. Elsukov if (version != LDM_VERSION_2K && 41537d1a121SAndrey V. Elsukov version != LDM_VERSION_VISTA) { 41637d1a121SAndrey V. Elsukov LDM_DEBUG(0, "%s: unsupported LDM version %u.%u", 41737d1a121SAndrey V. Elsukov cp->provider->name, version >> 16, 41837d1a121SAndrey V. Elsukov version & 0xFFFF); 41937d1a121SAndrey V. Elsukov return (ENXIO); 42037d1a121SAndrey V. Elsukov } 42137d1a121SAndrey V. Elsukov error = parse_uuid(buf + LDM_PH_DISKGUID_OFF, &hdr->disk_guid); 42237d1a121SAndrey V. Elsukov if (error != 0) 42337d1a121SAndrey V. Elsukov return (error); 42437d1a121SAndrey V. Elsukov error = parse_uuid(buf + LDM_PH_DGGUID_OFF, &hdr->dg_guid); 42537d1a121SAndrey V. Elsukov if (error != 0) 42637d1a121SAndrey V. Elsukov return (error); 42737d1a121SAndrey V. Elsukov strncpy(hdr->dg_name, buf + LDM_PH_DGNAME_OFF, sizeof(hdr->dg_name)); 42837d1a121SAndrey V. Elsukov hdr->start = be64dec(buf + LDM_PH_START_OFF); 42937d1a121SAndrey V. Elsukov hdr->size = be64dec(buf + LDM_PH_SIZE_OFF); 43037d1a121SAndrey V. Elsukov hdr->db_offset = be64dec(buf + LDM_PH_DB_OFF); 43137d1a121SAndrey V. Elsukov hdr->db_size = be64dec(buf + LDM_PH_DBSIZE_OFF); 43237d1a121SAndrey V. Elsukov hdr->th_offset[0] = be64dec(buf + LDM_PH_TH1_OFF); 43337d1a121SAndrey V. Elsukov hdr->th_offset[1] = be64dec(buf + LDM_PH_TH2_OFF); 43437d1a121SAndrey V. Elsukov hdr->conf_size = be64dec(buf + LDM_PH_CONFSIZE_OFF); 43537d1a121SAndrey V. Elsukov hdr->log_size = be64dec(buf + LDM_PH_LOGSIZE_OFF); 43637d1a121SAndrey V. Elsukov return (0); 43737d1a121SAndrey V. Elsukov } 43837d1a121SAndrey V. Elsukov 43937d1a121SAndrey V. Elsukov static int 44037d1a121SAndrey V. Elsukov ldm_privhdr_check(struct ldm_db *db, struct g_consumer *cp, int is_gpt) 44137d1a121SAndrey V. Elsukov { 44237d1a121SAndrey V. Elsukov struct g_consumer *cp2; 44337d1a121SAndrey V. Elsukov struct g_provider *pp; 44437d1a121SAndrey V. Elsukov struct ldm_privhdr hdr; 44537d1a121SAndrey V. Elsukov uint64_t offset, last; 44637d1a121SAndrey V. Elsukov int error, found, i; 44737d1a121SAndrey V. Elsukov u_char *buf; 44837d1a121SAndrey V. Elsukov 44937d1a121SAndrey V. Elsukov pp = cp->provider; 45037d1a121SAndrey V. Elsukov if (is_gpt) { 45137d1a121SAndrey V. Elsukov /* 45237d1a121SAndrey V. Elsukov * The last LBA is used in several checks below, for the 45337d1a121SAndrey V. Elsukov * GPT case it should be calculated relative to the whole 45437d1a121SAndrey V. Elsukov * disk. 45537d1a121SAndrey V. Elsukov */ 45637d1a121SAndrey V. Elsukov cp2 = LIST_FIRST(&pp->geom->consumer); 45737d1a121SAndrey V. Elsukov last = 45837d1a121SAndrey V. Elsukov cp2->provider->mediasize / cp2->provider->sectorsize - 1; 45937d1a121SAndrey V. Elsukov } else 46037d1a121SAndrey V. Elsukov last = pp->mediasize / pp->sectorsize - 1; 46163b6b7a7SPedro F. Giffuni for (found = 0, i = is_gpt; i < nitems(ldm_ph_off); i++) { 46237d1a121SAndrey V. Elsukov offset = ldm_ph_off[i]; 46337d1a121SAndrey V. Elsukov /* 46437d1a121SAndrey V. Elsukov * In the GPT case consumer is attached to the LDM metadata 46537d1a121SAndrey V. Elsukov * partition and we don't need add db_offset. 46637d1a121SAndrey V. Elsukov */ 46737d1a121SAndrey V. Elsukov if (!is_gpt) 46837d1a121SAndrey V. Elsukov offset += db->ph.db_offset; 46937d1a121SAndrey V. Elsukov if (i == LDM_PH_MBRINDEX) { 47037d1a121SAndrey V. Elsukov /* 47137d1a121SAndrey V. Elsukov * Prepare to errors and setup new base offset 47237d1a121SAndrey V. Elsukov * to read backup private headers. Assume that LDM 47337d1a121SAndrey V. Elsukov * database is in the last 1Mbyte area. 47437d1a121SAndrey V. Elsukov */ 47537d1a121SAndrey V. Elsukov db->ph.db_offset = last - LDM_DB_SIZE; 47637d1a121SAndrey V. Elsukov } 47737d1a121SAndrey V. Elsukov buf = ldm_privhdr_read(cp, offset * pp->sectorsize, &error); 47837d1a121SAndrey V. Elsukov if (buf == NULL) { 47937d1a121SAndrey V. Elsukov LDM_DEBUG(1, "%s: failed to read private header " 48037d1a121SAndrey V. Elsukov "%d at LBA %ju", pp->name, i, (uintmax_t)offset); 48137d1a121SAndrey V. Elsukov continue; 48237d1a121SAndrey V. Elsukov } 48337d1a121SAndrey V. Elsukov error = ldm_privhdr_parse(cp, &hdr, buf); 48437d1a121SAndrey V. Elsukov if (error != 0) { 48537d1a121SAndrey V. Elsukov LDM_DEBUG(1, "%s: failed to parse private " 48637d1a121SAndrey V. Elsukov "header %d", pp->name, i); 48737d1a121SAndrey V. Elsukov LDM_DUMP(buf, pp->sectorsize); 48837d1a121SAndrey V. Elsukov g_free(buf); 48937d1a121SAndrey V. Elsukov continue; 49037d1a121SAndrey V. Elsukov } 49137d1a121SAndrey V. Elsukov g_free(buf); 49237d1a121SAndrey V. Elsukov if (hdr.start > last || 49337d1a121SAndrey V. Elsukov hdr.start + hdr.size - 1 > last || 494ba289b84SAndrey V. Elsukov (hdr.start + hdr.size - 1 > hdr.db_offset && !is_gpt) || 49537d1a121SAndrey V. Elsukov hdr.db_size != LDM_DB_SIZE || 49637d1a121SAndrey V. Elsukov hdr.db_offset + LDM_DB_SIZE - 1 > last || 49737d1a121SAndrey V. Elsukov hdr.th_offset[0] >= LDM_DB_SIZE || 49837d1a121SAndrey V. Elsukov hdr.th_offset[1] >= LDM_DB_SIZE || 49937d1a121SAndrey V. Elsukov hdr.conf_size + hdr.log_size >= LDM_DB_SIZE) { 50037d1a121SAndrey V. Elsukov LDM_DEBUG(1, "%s: invalid values in the " 50137d1a121SAndrey V. Elsukov "private header %d", pp->name, i); 50237d1a121SAndrey V. Elsukov LDM_DEBUG(2, "%s: start: %jd, size: %jd, " 50337d1a121SAndrey V. Elsukov "db_offset: %jd, db_size: %jd, th_offset0: %jd, " 50437d1a121SAndrey V. Elsukov "th_offset1: %jd, conf_size: %jd, log_size: %jd, " 50537d1a121SAndrey V. Elsukov "last: %jd", pp->name, hdr.start, hdr.size, 50637d1a121SAndrey V. Elsukov hdr.db_offset, hdr.db_size, hdr.th_offset[0], 50737d1a121SAndrey V. Elsukov hdr.th_offset[1], hdr.conf_size, hdr.log_size, 50837d1a121SAndrey V. Elsukov last); 50937d1a121SAndrey V. Elsukov continue; 51037d1a121SAndrey V. Elsukov } 51137d1a121SAndrey V. Elsukov if (found != 0 && memcmp(&db->ph, &hdr, sizeof(hdr)) != 0) { 51237d1a121SAndrey V. Elsukov LDM_DEBUG(0, "%s: private headers are not equal", 51337d1a121SAndrey V. Elsukov pp->name); 51437d1a121SAndrey V. Elsukov if (i > 1) { 51537d1a121SAndrey V. Elsukov /* 51637d1a121SAndrey V. Elsukov * We have different headers in the LDM. 51737d1a121SAndrey V. Elsukov * We can not trust this metadata. 51837d1a121SAndrey V. Elsukov */ 51937d1a121SAndrey V. Elsukov LDM_DEBUG(0, "%s: refuse LDM metadata", 52037d1a121SAndrey V. Elsukov pp->name); 52137d1a121SAndrey V. Elsukov return (EINVAL); 52237d1a121SAndrey V. Elsukov } 52337d1a121SAndrey V. Elsukov /* 52437d1a121SAndrey V. Elsukov * We already have read primary private header 52537d1a121SAndrey V. Elsukov * and it differs from this backup one. 52637d1a121SAndrey V. Elsukov * Prefer the backup header and save it. 52737d1a121SAndrey V. Elsukov */ 52837d1a121SAndrey V. Elsukov found = 0; 52937d1a121SAndrey V. Elsukov } 53037d1a121SAndrey V. Elsukov if (found == 0) 53137d1a121SAndrey V. Elsukov memcpy(&db->ph, &hdr, sizeof(hdr)); 53237d1a121SAndrey V. Elsukov found = 1; 53337d1a121SAndrey V. Elsukov } 53437d1a121SAndrey V. Elsukov if (found == 0) { 53537d1a121SAndrey V. Elsukov LDM_DEBUG(1, "%s: valid LDM private header not found", 53637d1a121SAndrey V. Elsukov pp->name); 53737d1a121SAndrey V. Elsukov return (ENXIO); 53837d1a121SAndrey V. Elsukov } 53937d1a121SAndrey V. Elsukov return (0); 54037d1a121SAndrey V. Elsukov } 54137d1a121SAndrey V. Elsukov 54237d1a121SAndrey V. Elsukov static int 54337d1a121SAndrey V. Elsukov ldm_gpt_check(struct ldm_db *db, struct g_consumer *cp) 54437d1a121SAndrey V. Elsukov { 54537d1a121SAndrey V. Elsukov struct g_part_table *gpt; 54637d1a121SAndrey V. Elsukov struct g_part_entry *e; 54737d1a121SAndrey V. Elsukov struct g_consumer *cp2; 54837d1a121SAndrey V. Elsukov int error; 54937d1a121SAndrey V. Elsukov 55037d1a121SAndrey V. Elsukov cp2 = LIST_NEXT(cp, consumer); 55137d1a121SAndrey V. Elsukov g_topology_lock(); 55237d1a121SAndrey V. Elsukov gpt = cp->provider->geom->softc; 55337d1a121SAndrey V. Elsukov error = 0; 55437d1a121SAndrey V. Elsukov LIST_FOREACH(e, &gpt->gpt_entry, gpe_entry) { 55537d1a121SAndrey V. Elsukov if (cp->provider == e->gpe_pp) { 55637d1a121SAndrey V. Elsukov /* ms-ldm-metadata partition */ 55737d1a121SAndrey V. Elsukov if (e->gpe_start != db->ph.db_offset || 55837d1a121SAndrey V. Elsukov e->gpe_end != db->ph.db_offset + LDM_DB_SIZE - 1) 55937d1a121SAndrey V. Elsukov error++; 56037d1a121SAndrey V. Elsukov } else if (cp2->provider == e->gpe_pp) { 56137d1a121SAndrey V. Elsukov /* ms-ldm-data partition */ 56237d1a121SAndrey V. Elsukov if (e->gpe_start != db->ph.start || 56337d1a121SAndrey V. Elsukov e->gpe_end != db->ph.start + db->ph.size - 1) 56437d1a121SAndrey V. Elsukov error++; 56537d1a121SAndrey V. Elsukov } 56637d1a121SAndrey V. Elsukov if (error != 0) { 56737d1a121SAndrey V. Elsukov LDM_DEBUG(0, "%s: GPT partition %d boundaries " 56837d1a121SAndrey V. Elsukov "do not match with the LDM metadata", 56937d1a121SAndrey V. Elsukov e->gpe_pp->name, e->gpe_index); 57037d1a121SAndrey V. Elsukov error = ENXIO; 57137d1a121SAndrey V. Elsukov break; 57237d1a121SAndrey V. Elsukov } 57337d1a121SAndrey V. Elsukov } 57437d1a121SAndrey V. Elsukov g_topology_unlock(); 57537d1a121SAndrey V. Elsukov return (error); 57637d1a121SAndrey V. Elsukov } 57737d1a121SAndrey V. Elsukov 57837d1a121SAndrey V. Elsukov static int 57937d1a121SAndrey V. Elsukov ldm_tochdr_check(struct ldm_db *db, struct g_consumer *cp) 58037d1a121SAndrey V. Elsukov { 58137d1a121SAndrey V. Elsukov struct g_provider *pp; 58237d1a121SAndrey V. Elsukov struct ldm_tochdr hdr; 58337d1a121SAndrey V. Elsukov uint64_t offset, conf_size, log_size; 58437d1a121SAndrey V. Elsukov int error, found, i; 58537d1a121SAndrey V. Elsukov u_char *buf; 58637d1a121SAndrey V. Elsukov 58737d1a121SAndrey V. Elsukov pp = cp->provider; 58837d1a121SAndrey V. Elsukov for (i = 0, found = 0; i < LDM_TH_COUNT; i++) { 58937d1a121SAndrey V. Elsukov offset = db->ph.db_offset + db->ph.th_offset[i]; 59037d1a121SAndrey V. Elsukov buf = g_read_data(cp, 59137d1a121SAndrey V. Elsukov offset * pp->sectorsize, pp->sectorsize, &error); 59237d1a121SAndrey V. Elsukov if (buf == NULL) { 59337d1a121SAndrey V. Elsukov LDM_DEBUG(1, "%s: failed to read TOC header " 59437d1a121SAndrey V. Elsukov "at LBA %ju", pp->name, (uintmax_t)offset); 59537d1a121SAndrey V. Elsukov continue; 59637d1a121SAndrey V. Elsukov } 59737d1a121SAndrey V. Elsukov if (memcmp(buf, LDM_TH_SIGN, strlen(LDM_TH_SIGN)) != 0 || 59837d1a121SAndrey V. Elsukov memcmp(buf + LDM_TH_NAME1_OFF, LDM_TH_NAME1, 59937d1a121SAndrey V. Elsukov strlen(LDM_TH_NAME1)) != 0 || 60037d1a121SAndrey V. Elsukov memcmp(buf + LDM_TH_NAME2_OFF, LDM_TH_NAME2, 60137d1a121SAndrey V. Elsukov strlen(LDM_TH_NAME2)) != 0) { 60237d1a121SAndrey V. Elsukov LDM_DEBUG(1, "%s: failed to parse TOC header " 60337d1a121SAndrey V. Elsukov "at LBA %ju", pp->name, (uintmax_t)offset); 60437d1a121SAndrey V. Elsukov LDM_DUMP(buf, pp->sectorsize); 60537d1a121SAndrey V. Elsukov g_free(buf); 60637d1a121SAndrey V. Elsukov continue; 60737d1a121SAndrey V. Elsukov } 60837d1a121SAndrey V. Elsukov hdr.conf_offset = be64dec(buf + LDM_TH_CONF_OFF); 60937d1a121SAndrey V. Elsukov hdr.log_offset = be64dec(buf + LDM_TH_LOG_OFF); 61037d1a121SAndrey V. Elsukov conf_size = be64dec(buf + LDM_TH_CONFSIZE_OFF); 61137d1a121SAndrey V. Elsukov log_size = be64dec(buf + LDM_TH_LOGSIZE_OFF); 61237d1a121SAndrey V. Elsukov if (conf_size != db->ph.conf_size || 61337d1a121SAndrey V. Elsukov hdr.conf_offset + conf_size >= LDM_DB_SIZE || 61437d1a121SAndrey V. Elsukov log_size != db->ph.log_size || 61537d1a121SAndrey V. Elsukov hdr.log_offset + log_size >= LDM_DB_SIZE) { 61637d1a121SAndrey V. Elsukov LDM_DEBUG(1, "%s: invalid values in the " 61737d1a121SAndrey V. Elsukov "TOC header at LBA %ju", pp->name, 61837d1a121SAndrey V. Elsukov (uintmax_t)offset); 61937d1a121SAndrey V. Elsukov LDM_DUMP(buf, pp->sectorsize); 62037d1a121SAndrey V. Elsukov g_free(buf); 62137d1a121SAndrey V. Elsukov continue; 62237d1a121SAndrey V. Elsukov } 62337d1a121SAndrey V. Elsukov g_free(buf); 62437d1a121SAndrey V. Elsukov if (found == 0) 62537d1a121SAndrey V. Elsukov memcpy(&db->th, &hdr, sizeof(hdr)); 62637d1a121SAndrey V. Elsukov found = 1; 62737d1a121SAndrey V. Elsukov } 62837d1a121SAndrey V. Elsukov if (found == 0) { 62937d1a121SAndrey V. Elsukov LDM_DEBUG(0, "%s: valid LDM TOC header not found.", 63037d1a121SAndrey V. Elsukov pp->name); 63137d1a121SAndrey V. Elsukov return (ENXIO); 63237d1a121SAndrey V. Elsukov } 63337d1a121SAndrey V. Elsukov return (0); 63437d1a121SAndrey V. Elsukov } 63537d1a121SAndrey V. Elsukov 63637d1a121SAndrey V. Elsukov static int 63737d1a121SAndrey V. Elsukov ldm_vmdbhdr_check(struct ldm_db *db, struct g_consumer *cp) 63837d1a121SAndrey V. Elsukov { 63937d1a121SAndrey V. Elsukov struct g_provider *pp; 64037d1a121SAndrey V. Elsukov struct uuid dg_guid; 64137d1a121SAndrey V. Elsukov uint64_t offset; 64237d1a121SAndrey V. Elsukov uint32_t version; 64337d1a121SAndrey V. Elsukov int error; 64437d1a121SAndrey V. Elsukov u_char *buf; 64537d1a121SAndrey V. Elsukov 64637d1a121SAndrey V. Elsukov pp = cp->provider; 64737d1a121SAndrey V. Elsukov offset = db->ph.db_offset + db->th.conf_offset; 64837d1a121SAndrey V. Elsukov buf = g_read_data(cp, offset * pp->sectorsize, pp->sectorsize, 64937d1a121SAndrey V. Elsukov &error); 65037d1a121SAndrey V. Elsukov if (buf == NULL) { 65137d1a121SAndrey V. Elsukov LDM_DEBUG(0, "%s: failed to read VMDB header at " 65237d1a121SAndrey V. Elsukov "LBA %ju", pp->name, (uintmax_t)offset); 65337d1a121SAndrey V. Elsukov return (error); 65437d1a121SAndrey V. Elsukov } 65537d1a121SAndrey V. Elsukov if (memcmp(buf, LDM_VMDB_SIGN, strlen(LDM_VMDB_SIGN)) != 0) { 65637d1a121SAndrey V. Elsukov g_free(buf); 65737d1a121SAndrey V. Elsukov LDM_DEBUG(0, "%s: failed to parse VMDB header at " 65837d1a121SAndrey V. Elsukov "LBA %ju", pp->name, (uintmax_t)offset); 65937d1a121SAndrey V. Elsukov return (ENXIO); 66037d1a121SAndrey V. Elsukov } 66137d1a121SAndrey V. Elsukov /* Check version. */ 66237d1a121SAndrey V. Elsukov version = be32dec(buf + LDM_DB_VERSION_OFF); 66337d1a121SAndrey V. Elsukov if (version != 0x4000A) { 66437d1a121SAndrey V. Elsukov g_free(buf); 66537d1a121SAndrey V. Elsukov LDM_DEBUG(0, "%s: unsupported VMDB version %u.%u", 66637d1a121SAndrey V. Elsukov pp->name, version >> 16, version & 0xFFFF); 66737d1a121SAndrey V. Elsukov return (ENXIO); 66837d1a121SAndrey V. Elsukov } 66937d1a121SAndrey V. Elsukov /* 67037d1a121SAndrey V. Elsukov * Check VMDB update status: 67137d1a121SAndrey V. Elsukov * 1 - in a consistent state; 67237d1a121SAndrey V. Elsukov * 2 - in a creation phase; 67337d1a121SAndrey V. Elsukov * 3 - in a deletion phase; 67437d1a121SAndrey V. Elsukov */ 67537d1a121SAndrey V. Elsukov if (be16dec(buf + LDM_DB_STATUS_OFF) != 1) { 67637d1a121SAndrey V. Elsukov g_free(buf); 67737d1a121SAndrey V. Elsukov LDM_DEBUG(0, "%s: VMDB is not in a consistent state", 67837d1a121SAndrey V. Elsukov pp->name); 67937d1a121SAndrey V. Elsukov return (ENXIO); 68037d1a121SAndrey V. Elsukov } 68137d1a121SAndrey V. Elsukov db->dh.last_seq = be32dec(buf + LDM_DB_LASTSEQ_OFF); 68237d1a121SAndrey V. Elsukov db->dh.size = be32dec(buf + LDM_DB_SIZE_OFF); 68337d1a121SAndrey V. Elsukov error = parse_uuid(buf + LDM_DB_DGGUID_OFF, &dg_guid); 68437d1a121SAndrey V. Elsukov /* Compare disk group name and guid from VMDB and private headers */ 68537d1a121SAndrey V. Elsukov if (error != 0 || db->dh.size == 0 || 68637d1a121SAndrey V. Elsukov pp->sectorsize % db->dh.size != 0 || 68737d1a121SAndrey V. Elsukov strncmp(buf + LDM_DB_DGNAME_OFF, db->ph.dg_name, 31) != 0 || 68837d1a121SAndrey V. Elsukov memcmp(&dg_guid, &db->ph.dg_guid, sizeof(dg_guid)) != 0 || 68937d1a121SAndrey V. Elsukov db->dh.size * db->dh.last_seq > 69037d1a121SAndrey V. Elsukov db->ph.conf_size * pp->sectorsize) { 69137d1a121SAndrey V. Elsukov LDM_DEBUG(0, "%s: invalid values in the VMDB header", 69237d1a121SAndrey V. Elsukov pp->name); 69337d1a121SAndrey V. Elsukov LDM_DUMP(buf, pp->sectorsize); 69437d1a121SAndrey V. Elsukov g_free(buf); 69537d1a121SAndrey V. Elsukov return (EINVAL); 69637d1a121SAndrey V. Elsukov } 69737d1a121SAndrey V. Elsukov g_free(buf); 69837d1a121SAndrey V. Elsukov return (0); 69937d1a121SAndrey V. Elsukov } 70037d1a121SAndrey V. Elsukov 70137d1a121SAndrey V. Elsukov static int 70237d1a121SAndrey V. Elsukov ldm_xvblk_handle(struct ldm_db *db, struct ldm_vblkhdr *vh, const u_char *p) 70337d1a121SAndrey V. Elsukov { 70437d1a121SAndrey V. Elsukov struct ldm_xvblk *blk; 70537d1a121SAndrey V. Elsukov size_t size; 70637d1a121SAndrey V. Elsukov 70737d1a121SAndrey V. Elsukov size = db->dh.size - 16; 70837d1a121SAndrey V. Elsukov LIST_FOREACH(blk, &db->xvblks, entry) 70937d1a121SAndrey V. Elsukov if (blk->group == vh->group) 71037d1a121SAndrey V. Elsukov break; 71137d1a121SAndrey V. Elsukov if (blk == NULL) { 71237d1a121SAndrey V. Elsukov blk = g_malloc(sizeof(*blk), M_WAITOK | M_ZERO); 71337d1a121SAndrey V. Elsukov blk->group = vh->group; 71437d1a121SAndrey V. Elsukov blk->size = size * vh->count + 16; 71537d1a121SAndrey V. Elsukov blk->data = g_malloc(blk->size, M_WAITOK | M_ZERO); 71637d1a121SAndrey V. Elsukov blk->map = 0xFF << vh->count; 71737d1a121SAndrey V. Elsukov LIST_INSERT_HEAD(&db->xvblks, blk, entry); 71837d1a121SAndrey V. Elsukov } 71937d1a121SAndrey V. Elsukov if ((blk->map & (1 << vh->index)) != 0) { 72037d1a121SAndrey V. Elsukov /* Block with given index has been already saved. */ 72137d1a121SAndrey V. Elsukov return (EINVAL); 72237d1a121SAndrey V. Elsukov } 72337d1a121SAndrey V. Elsukov /* Copy the data block to the place related to index. */ 72437d1a121SAndrey V. Elsukov memcpy(blk->data + size * vh->index + 16, p + 16, size); 72537d1a121SAndrey V. Elsukov blk->map |= 1 << vh->index; 72637d1a121SAndrey V. Elsukov return (0); 72737d1a121SAndrey V. Elsukov } 72837d1a121SAndrey V. Elsukov 72937d1a121SAndrey V. Elsukov /* Read the variable-width numeric field and return new offset */ 73037d1a121SAndrey V. Elsukov static int 73137d1a121SAndrey V. Elsukov ldm_vnum_get(const u_char *buf, int offset, uint64_t *result, size_t range) 73237d1a121SAndrey V. Elsukov { 73337d1a121SAndrey V. Elsukov uint64_t num; 73437d1a121SAndrey V. Elsukov uint8_t len; 73537d1a121SAndrey V. Elsukov 73637d1a121SAndrey V. Elsukov len = buf[offset++]; 73737d1a121SAndrey V. Elsukov if (len > sizeof(uint64_t) || len + offset >= range) 73837d1a121SAndrey V. Elsukov return (-1); 73937d1a121SAndrey V. Elsukov for (num = 0; len > 0; len--) 74037d1a121SAndrey V. Elsukov num = (num << 8) | buf[offset++]; 74137d1a121SAndrey V. Elsukov *result = num; 74237d1a121SAndrey V. Elsukov return (offset); 74337d1a121SAndrey V. Elsukov } 74437d1a121SAndrey V. Elsukov 74537d1a121SAndrey V. Elsukov /* Read the variable-width string and return new offset */ 74637d1a121SAndrey V. Elsukov static int 74737d1a121SAndrey V. Elsukov ldm_vstr_get(const u_char *buf, int offset, u_char *result, 74837d1a121SAndrey V. Elsukov size_t maxlen, size_t range) 74937d1a121SAndrey V. Elsukov { 75037d1a121SAndrey V. Elsukov uint8_t len; 75137d1a121SAndrey V. Elsukov 75237d1a121SAndrey V. Elsukov len = buf[offset++]; 75337d1a121SAndrey V. Elsukov if (len >= maxlen || len + offset >= range) 75437d1a121SAndrey V. Elsukov return (-1); 75537d1a121SAndrey V. Elsukov memcpy(result, buf + offset, len); 75637d1a121SAndrey V. Elsukov result[len] = '\0'; 75737d1a121SAndrey V. Elsukov return (offset + len); 75837d1a121SAndrey V. Elsukov } 75937d1a121SAndrey V. Elsukov 76037d1a121SAndrey V. Elsukov /* Just skip the variable-width variable and return new offset */ 76137d1a121SAndrey V. Elsukov static int 76237d1a121SAndrey V. Elsukov ldm_vparm_skip(const u_char *buf, int offset, size_t range) 76337d1a121SAndrey V. Elsukov { 76437d1a121SAndrey V. Elsukov uint8_t len; 76537d1a121SAndrey V. Elsukov 76637d1a121SAndrey V. Elsukov len = buf[offset++]; 76737d1a121SAndrey V. Elsukov if (offset + len >= range) 76837d1a121SAndrey V. Elsukov return (-1); 76937d1a121SAndrey V. Elsukov 77037d1a121SAndrey V. Elsukov return (offset + len); 77137d1a121SAndrey V. Elsukov } 77237d1a121SAndrey V. Elsukov 77337d1a121SAndrey V. Elsukov static int 77437d1a121SAndrey V. Elsukov ldm_vblk_handle(struct ldm_db *db, const u_char *p, size_t size) 77537d1a121SAndrey V. Elsukov { 77637d1a121SAndrey V. Elsukov struct ldm_vblk *blk; 77737d1a121SAndrey V. Elsukov struct ldm_volume *volume, *last; 77837d1a121SAndrey V. Elsukov const char *errstr; 77937d1a121SAndrey V. Elsukov u_char vstr[64]; 78037d1a121SAndrey V. Elsukov int error, offset; 78137d1a121SAndrey V. Elsukov 78237d1a121SAndrey V. Elsukov blk = g_malloc(sizeof(*blk), M_WAITOK | M_ZERO); 78337d1a121SAndrey V. Elsukov blk->type = p[LDM_VBLK_TYPE_OFF]; 78437d1a121SAndrey V. Elsukov offset = ldm_vnum_get(p, LDM_VBLK_OID_OFF, &blk->u.id, size); 78537d1a121SAndrey V. Elsukov if (offset < 0) { 78637d1a121SAndrey V. Elsukov errstr = "object id"; 78737d1a121SAndrey V. Elsukov goto fail; 78837d1a121SAndrey V. Elsukov } 78937d1a121SAndrey V. Elsukov offset = ldm_vstr_get(p, offset, vstr, sizeof(vstr), size); 79037d1a121SAndrey V. Elsukov if (offset < 0) { 79137d1a121SAndrey V. Elsukov errstr = "object name"; 79237d1a121SAndrey V. Elsukov goto fail; 79337d1a121SAndrey V. Elsukov } 79437d1a121SAndrey V. Elsukov switch (blk->type) { 79537d1a121SAndrey V. Elsukov /* 79637d1a121SAndrey V. Elsukov * Component VBLK fields: 79737d1a121SAndrey V. Elsukov * Offset Size Description 79837d1a121SAndrey V. Elsukov * ------------+-------+------------------------ 79937d1a121SAndrey V. Elsukov * 0x18+ PS volume state 80037d1a121SAndrey V. Elsukov * 0x18+5 PN component children count 80137d1a121SAndrey V. Elsukov * 0x1D+16 PN parent's volume object id 80237d1a121SAndrey V. Elsukov * 0x2D+1 PN stripe size 80337d1a121SAndrey V. Elsukov */ 80437d1a121SAndrey V. Elsukov case LDM_VBLK_T_COMPONENT: 80537d1a121SAndrey V. Elsukov offset = ldm_vparm_skip(p, offset, size); 80637d1a121SAndrey V. Elsukov if (offset < 0) { 80737d1a121SAndrey V. Elsukov errstr = "volume state"; 80837d1a121SAndrey V. Elsukov goto fail; 80937d1a121SAndrey V. Elsukov } 81037d1a121SAndrey V. Elsukov offset = ldm_vparm_skip(p, offset + 5, size); 81137d1a121SAndrey V. Elsukov if (offset < 0) { 81237d1a121SAndrey V. Elsukov errstr = "children count"; 81337d1a121SAndrey V. Elsukov goto fail; 81437d1a121SAndrey V. Elsukov } 81537d1a121SAndrey V. Elsukov offset = ldm_vnum_get(p, offset + 16, 81637d1a121SAndrey V. Elsukov &blk->u.comp.vol_id, size); 81737d1a121SAndrey V. Elsukov if (offset < 0) { 81837d1a121SAndrey V. Elsukov errstr = "volume id"; 81937d1a121SAndrey V. Elsukov goto fail; 82037d1a121SAndrey V. Elsukov } 82137d1a121SAndrey V. Elsukov break; 82237d1a121SAndrey V. Elsukov /* 82337d1a121SAndrey V. Elsukov * Partition VBLK fields: 82437d1a121SAndrey V. Elsukov * Offset Size Description 82537d1a121SAndrey V. Elsukov * ------------+-------+------------------------ 82637d1a121SAndrey V. Elsukov * 0x18+12 8 partition start offset 82737d1a121SAndrey V. Elsukov * 0x18+20 8 volume offset 82837d1a121SAndrey V. Elsukov * 0x18+28 PN partition size 82937d1a121SAndrey V. Elsukov * 0x34+ PN parent's component object id 83037d1a121SAndrey V. Elsukov * 0x34+ PN disk's object id 83137d1a121SAndrey V. Elsukov */ 83237d1a121SAndrey V. Elsukov case LDM_VBLK_T_PARTITION: 83337d1a121SAndrey V. Elsukov if (offset + 28 >= size) { 83437d1a121SAndrey V. Elsukov errstr = "too small buffer"; 83537d1a121SAndrey V. Elsukov goto fail; 83637d1a121SAndrey V. Elsukov } 83737d1a121SAndrey V. Elsukov blk->u.part.start = be64dec(p + offset + 12); 83837d1a121SAndrey V. Elsukov blk->u.part.offset = be64dec(p + offset + 20); 83937d1a121SAndrey V. Elsukov offset = ldm_vnum_get(p, offset + 28, &blk->u.part.size, size); 84037d1a121SAndrey V. Elsukov if (offset < 0) { 84137d1a121SAndrey V. Elsukov errstr = "partition size"; 84237d1a121SAndrey V. Elsukov goto fail; 84337d1a121SAndrey V. Elsukov } 84437d1a121SAndrey V. Elsukov offset = ldm_vnum_get(p, offset, &blk->u.part.comp_id, size); 84537d1a121SAndrey V. Elsukov if (offset < 0) { 84637d1a121SAndrey V. Elsukov errstr = "component id"; 84737d1a121SAndrey V. Elsukov goto fail; 84837d1a121SAndrey V. Elsukov } 84937d1a121SAndrey V. Elsukov offset = ldm_vnum_get(p, offset, &blk->u.part.disk_id, size); 85037d1a121SAndrey V. Elsukov if (offset < 0) { 85137d1a121SAndrey V. Elsukov errstr = "disk id"; 85237d1a121SAndrey V. Elsukov goto fail; 85337d1a121SAndrey V. Elsukov } 85437d1a121SAndrey V. Elsukov break; 85537d1a121SAndrey V. Elsukov /* 85637d1a121SAndrey V. Elsukov * Disk VBLK fields: 85737d1a121SAndrey V. Elsukov * Offset Size Description 85837d1a121SAndrey V. Elsukov * ------------+-------+------------------------ 85937d1a121SAndrey V. Elsukov * 0x18+ PS disk GUID 86037d1a121SAndrey V. Elsukov */ 86137d1a121SAndrey V. Elsukov case LDM_VBLK_T_DISK: 86237d1a121SAndrey V. Elsukov errstr = "disk guid"; 86337d1a121SAndrey V. Elsukov offset = ldm_vstr_get(p, offset, vstr, sizeof(vstr), size); 86437d1a121SAndrey V. Elsukov if (offset < 0) 86537d1a121SAndrey V. Elsukov goto fail; 86637d1a121SAndrey V. Elsukov error = parse_uuid(vstr, &blk->u.disk.guid); 86737d1a121SAndrey V. Elsukov if (error != 0) 86837d1a121SAndrey V. Elsukov goto fail; 86937d1a121SAndrey V. Elsukov LIST_INSERT_HEAD(&db->disks, &blk->u.disk, entry); 87037d1a121SAndrey V. Elsukov break; 87137d1a121SAndrey V. Elsukov /* 87237d1a121SAndrey V. Elsukov * Disk group VBLK fields: 87337d1a121SAndrey V. Elsukov * Offset Size Description 87437d1a121SAndrey V. Elsukov * ------------+-------+------------------------ 87537d1a121SAndrey V. Elsukov * 0x18+ PS disk group GUID 87637d1a121SAndrey V. Elsukov */ 87737d1a121SAndrey V. Elsukov case LDM_VBLK_T_DISKGROUP: 87837d1a121SAndrey V. Elsukov #if 0 87937d1a121SAndrey V. Elsukov strncpy(blk->u.disk_group.name, vstr, 88037d1a121SAndrey V. Elsukov sizeof(blk->u.disk_group.name)); 88137d1a121SAndrey V. Elsukov offset = ldm_vstr_get(p, offset, vstr, sizeof(vstr), size); 88237d1a121SAndrey V. Elsukov if (offset < 0) { 88337d1a121SAndrey V. Elsukov errstr = "disk group guid"; 88437d1a121SAndrey V. Elsukov goto fail; 88537d1a121SAndrey V. Elsukov } 88637d1a121SAndrey V. Elsukov error = parse_uuid(name, &blk->u.disk_group.guid); 88737d1a121SAndrey V. Elsukov if (error != 0) { 88837d1a121SAndrey V. Elsukov errstr = "disk group guid"; 88937d1a121SAndrey V. Elsukov goto fail; 89037d1a121SAndrey V. Elsukov } 89137d1a121SAndrey V. Elsukov LIST_INSERT_HEAD(&db->groups, &blk->u.disk_group, entry); 89237d1a121SAndrey V. Elsukov #endif 89337d1a121SAndrey V. Elsukov break; 89437d1a121SAndrey V. Elsukov /* 89537d1a121SAndrey V. Elsukov * Disk VBLK fields: 89637d1a121SAndrey V. Elsukov * Offset Size Description 89737d1a121SAndrey V. Elsukov * ------------+-------+------------------------ 89837d1a121SAndrey V. Elsukov * 0x18+ 16 disk GUID 89937d1a121SAndrey V. Elsukov */ 90037d1a121SAndrey V. Elsukov case LDM_VBLK_T_DISK4: 90137d1a121SAndrey V. Elsukov be_uuid_dec(p + offset, &blk->u.disk.guid); 90237d1a121SAndrey V. Elsukov LIST_INSERT_HEAD(&db->disks, &blk->u.disk, entry); 90337d1a121SAndrey V. Elsukov break; 90437d1a121SAndrey V. Elsukov /* 90537d1a121SAndrey V. Elsukov * Disk group VBLK fields: 90637d1a121SAndrey V. Elsukov * Offset Size Description 90737d1a121SAndrey V. Elsukov * ------------+-------+------------------------ 90837d1a121SAndrey V. Elsukov * 0x18+ 16 disk GUID 90937d1a121SAndrey V. Elsukov */ 91037d1a121SAndrey V. Elsukov case LDM_VBLK_T_DISKGROUP4: 91137d1a121SAndrey V. Elsukov #if 0 91237d1a121SAndrey V. Elsukov strncpy(blk->u.disk_group.name, vstr, 91337d1a121SAndrey V. Elsukov sizeof(blk->u.disk_group.name)); 91437d1a121SAndrey V. Elsukov be_uuid_dec(p + offset, &blk->u.disk.guid); 91537d1a121SAndrey V. Elsukov LIST_INSERT_HEAD(&db->groups, &blk->u.disk_group, entry); 91637d1a121SAndrey V. Elsukov #endif 91737d1a121SAndrey V. Elsukov break; 91837d1a121SAndrey V. Elsukov /* 91937d1a121SAndrey V. Elsukov * Volume VBLK fields: 92037d1a121SAndrey V. Elsukov * Offset Size Description 92137d1a121SAndrey V. Elsukov * ------------+-------+------------------------ 92237d1a121SAndrey V. Elsukov * 0x18+ PS volume type 92337d1a121SAndrey V. Elsukov * 0x18+ PS unknown 92437d1a121SAndrey V. Elsukov * 0x18+ 14(S) volume state 92537d1a121SAndrey V. Elsukov * 0x18+16 1 volume number 92637d1a121SAndrey V. Elsukov * 0x18+21 PN volume children count 92737d1a121SAndrey V. Elsukov * 0x2D+16 PN volume size 92837d1a121SAndrey V. Elsukov * 0x3D+4 1 partition type 92937d1a121SAndrey V. Elsukov */ 93037d1a121SAndrey V. Elsukov case LDM_VBLK_T_VOLUME: 93137d1a121SAndrey V. Elsukov offset = ldm_vparm_skip(p, offset, size); 93237d1a121SAndrey V. Elsukov if (offset < 0) { 93337d1a121SAndrey V. Elsukov errstr = "volume type"; 93437d1a121SAndrey V. Elsukov goto fail; 93537d1a121SAndrey V. Elsukov } 93637d1a121SAndrey V. Elsukov offset = ldm_vparm_skip(p, offset, size); 93737d1a121SAndrey V. Elsukov if (offset < 0) { 93837d1a121SAndrey V. Elsukov errstr = "unknown param"; 93937d1a121SAndrey V. Elsukov goto fail; 94037d1a121SAndrey V. Elsukov } 94137d1a121SAndrey V. Elsukov if (offset + 21 >= size) { 94237d1a121SAndrey V. Elsukov errstr = "too small buffer"; 94337d1a121SAndrey V. Elsukov goto fail; 94437d1a121SAndrey V. Elsukov } 94537d1a121SAndrey V. Elsukov blk->u.vol.number = p[offset + 16]; 94637d1a121SAndrey V. Elsukov offset = ldm_vparm_skip(p, offset + 21, size); 94737d1a121SAndrey V. Elsukov if (offset < 0) { 94837d1a121SAndrey V. Elsukov errstr = "children count"; 94937d1a121SAndrey V. Elsukov goto fail; 95037d1a121SAndrey V. Elsukov } 95137d1a121SAndrey V. Elsukov offset = ldm_vnum_get(p, offset + 16, &blk->u.vol.size, size); 95237d1a121SAndrey V. Elsukov if (offset < 0) { 95337d1a121SAndrey V. Elsukov errstr = "volume size"; 95437d1a121SAndrey V. Elsukov goto fail; 95537d1a121SAndrey V. Elsukov } 95637d1a121SAndrey V. Elsukov if (offset + 4 >= size) { 95737d1a121SAndrey V. Elsukov errstr = "too small buffer"; 95837d1a121SAndrey V. Elsukov goto fail; 95937d1a121SAndrey V. Elsukov } 96037d1a121SAndrey V. Elsukov blk->u.vol.part_type = p[offset + 4]; 96137d1a121SAndrey V. Elsukov /* keep volumes ordered by volume number */ 96237d1a121SAndrey V. Elsukov last = NULL; 96337d1a121SAndrey V. Elsukov LIST_FOREACH(volume, &db->volumes, entry) { 96437d1a121SAndrey V. Elsukov if (volume->number > blk->u.vol.number) 96537d1a121SAndrey V. Elsukov break; 96637d1a121SAndrey V. Elsukov last = volume; 96737d1a121SAndrey V. Elsukov } 96837d1a121SAndrey V. Elsukov if (last != NULL) 96937d1a121SAndrey V. Elsukov LIST_INSERT_AFTER(last, &blk->u.vol, entry); 97037d1a121SAndrey V. Elsukov else 97137d1a121SAndrey V. Elsukov LIST_INSERT_HEAD(&db->volumes, &blk->u.vol, entry); 97237d1a121SAndrey V. Elsukov break; 97337d1a121SAndrey V. Elsukov default: 97437d1a121SAndrey V. Elsukov LDM_DEBUG(1, "unknown VBLK type 0x%02x\n", blk->type); 97537d1a121SAndrey V. Elsukov LDM_DUMP(p, size); 97637d1a121SAndrey V. Elsukov } 97737d1a121SAndrey V. Elsukov LIST_INSERT_HEAD(&db->vblks, blk, entry); 97837d1a121SAndrey V. Elsukov return (0); 97937d1a121SAndrey V. Elsukov fail: 98037d1a121SAndrey V. Elsukov LDM_DEBUG(0, "failed to parse '%s' in VBLK of type 0x%02x\n", 98137d1a121SAndrey V. Elsukov errstr, blk->type); 98237d1a121SAndrey V. Elsukov LDM_DUMP(p, size); 98337d1a121SAndrey V. Elsukov g_free(blk); 98437d1a121SAndrey V. Elsukov return (EINVAL); 98537d1a121SAndrey V. Elsukov } 98637d1a121SAndrey V. Elsukov 98737d1a121SAndrey V. Elsukov static void 98837d1a121SAndrey V. Elsukov ldm_vmdb_free(struct ldm_db *db) 98937d1a121SAndrey V. Elsukov { 99037d1a121SAndrey V. Elsukov struct ldm_vblk *vblk; 99137d1a121SAndrey V. Elsukov struct ldm_xvblk *xvblk; 99237d1a121SAndrey V. Elsukov 99337d1a121SAndrey V. Elsukov while (!LIST_EMPTY(&db->xvblks)) { 99437d1a121SAndrey V. Elsukov xvblk = LIST_FIRST(&db->xvblks); 99537d1a121SAndrey V. Elsukov LIST_REMOVE(xvblk, entry); 99637d1a121SAndrey V. Elsukov g_free(xvblk->data); 99737d1a121SAndrey V. Elsukov g_free(xvblk); 99837d1a121SAndrey V. Elsukov } 99937d1a121SAndrey V. Elsukov while (!LIST_EMPTY(&db->vblks)) { 100037d1a121SAndrey V. Elsukov vblk = LIST_FIRST(&db->vblks); 100137d1a121SAndrey V. Elsukov LIST_REMOVE(vblk, entry); 100237d1a121SAndrey V. Elsukov g_free(vblk); 100337d1a121SAndrey V. Elsukov } 100437d1a121SAndrey V. Elsukov } 100537d1a121SAndrey V. Elsukov 100637d1a121SAndrey V. Elsukov static int 100737d1a121SAndrey V. Elsukov ldm_vmdb_parse(struct ldm_db *db, struct g_consumer *cp) 100837d1a121SAndrey V. Elsukov { 100937d1a121SAndrey V. Elsukov struct g_provider *pp; 101037d1a121SAndrey V. Elsukov struct ldm_vblk *vblk; 101137d1a121SAndrey V. Elsukov struct ldm_xvblk *xvblk; 101237d1a121SAndrey V. Elsukov struct ldm_volume *volume; 101337d1a121SAndrey V. Elsukov struct ldm_component *comp; 101437d1a121SAndrey V. Elsukov struct ldm_vblkhdr vh; 101537d1a121SAndrey V. Elsukov u_char *buf, *p; 101637d1a121SAndrey V. Elsukov size_t size, n, sectors; 101737d1a121SAndrey V. Elsukov uint64_t offset; 101837d1a121SAndrey V. Elsukov int error; 101937d1a121SAndrey V. Elsukov 102037d1a121SAndrey V. Elsukov pp = cp->provider; 102155e0987aSPedro F. Giffuni size = howmany(db->dh.last_seq * db->dh.size, pp->sectorsize); 102237d1a121SAndrey V. Elsukov size -= 1; /* one sector takes vmdb header */ 102337d1a121SAndrey V. Elsukov for (n = 0; n < size; n += MAXPHYS / pp->sectorsize) { 102437d1a121SAndrey V. Elsukov offset = db->ph.db_offset + db->th.conf_offset + n + 1; 102537d1a121SAndrey V. Elsukov sectors = (size - n) > (MAXPHYS / pp->sectorsize) ? 102637d1a121SAndrey V. Elsukov MAXPHYS / pp->sectorsize: size - n; 102737d1a121SAndrey V. Elsukov /* read VBLKs */ 102837d1a121SAndrey V. Elsukov buf = g_read_data(cp, offset * pp->sectorsize, 102937d1a121SAndrey V. Elsukov sectors * pp->sectorsize, &error); 103037d1a121SAndrey V. Elsukov if (buf == NULL) { 103137d1a121SAndrey V. Elsukov LDM_DEBUG(0, "%s: failed to read VBLK\n", 103237d1a121SAndrey V. Elsukov pp->name); 103337d1a121SAndrey V. Elsukov goto fail; 103437d1a121SAndrey V. Elsukov } 103537d1a121SAndrey V. Elsukov for (p = buf; p < buf + sectors * pp->sectorsize; 103637d1a121SAndrey V. Elsukov p += db->dh.size) { 103737d1a121SAndrey V. Elsukov if (memcmp(p, LDM_VBLK_SIGN, 103837d1a121SAndrey V. Elsukov strlen(LDM_VBLK_SIGN)) != 0) { 103937d1a121SAndrey V. Elsukov LDM_DEBUG(0, "%s: no VBLK signature\n", 104037d1a121SAndrey V. Elsukov pp->name); 104137d1a121SAndrey V. Elsukov LDM_DUMP(p, db->dh.size); 104237d1a121SAndrey V. Elsukov goto fail; 104337d1a121SAndrey V. Elsukov } 104437d1a121SAndrey V. Elsukov vh.seq = be32dec(p + LDM_VBLK_SEQ_OFF); 104537d1a121SAndrey V. Elsukov vh.group = be32dec(p + LDM_VBLK_GROUP_OFF); 104637d1a121SAndrey V. Elsukov /* skip empty blocks */ 104737d1a121SAndrey V. Elsukov if (vh.seq == 0 || vh.group == 0) 104837d1a121SAndrey V. Elsukov continue; 104937d1a121SAndrey V. Elsukov vh.index = be16dec(p + LDM_VBLK_INDEX_OFF); 105037d1a121SAndrey V. Elsukov vh.count = be16dec(p + LDM_VBLK_COUNT_OFF); 105137d1a121SAndrey V. Elsukov if (vh.count == 0 || vh.count > 4 || 105237d1a121SAndrey V. Elsukov vh.seq > db->dh.last_seq) { 105337d1a121SAndrey V. Elsukov LDM_DEBUG(0, "%s: invalid values " 105437d1a121SAndrey V. Elsukov "in the VBLK header\n", pp->name); 105537d1a121SAndrey V. Elsukov LDM_DUMP(p, db->dh.size); 105637d1a121SAndrey V. Elsukov goto fail; 105737d1a121SAndrey V. Elsukov } 105837d1a121SAndrey V. Elsukov if (vh.count > 1) { 105937d1a121SAndrey V. Elsukov error = ldm_xvblk_handle(db, &vh, p); 106037d1a121SAndrey V. Elsukov if (error != 0) { 106137d1a121SAndrey V. Elsukov LDM_DEBUG(0, "%s: xVBLK " 106237d1a121SAndrey V. Elsukov "is corrupted\n", pp->name); 106337d1a121SAndrey V. Elsukov LDM_DUMP(p, db->dh.size); 106437d1a121SAndrey V. Elsukov goto fail; 106537d1a121SAndrey V. Elsukov } 106637d1a121SAndrey V. Elsukov continue; 106737d1a121SAndrey V. Elsukov } 106837d1a121SAndrey V. Elsukov if (be16dec(p + 16) != 0) 106937d1a121SAndrey V. Elsukov LDM_DEBUG(1, "%s: VBLK update" 107037d1a121SAndrey V. Elsukov " status is %u\n", pp->name, 107137d1a121SAndrey V. Elsukov be16dec(p + 16)); 107237d1a121SAndrey V. Elsukov error = ldm_vblk_handle(db, p, db->dh.size); 107337d1a121SAndrey V. Elsukov if (error != 0) 107437d1a121SAndrey V. Elsukov goto fail; 107537d1a121SAndrey V. Elsukov } 107637d1a121SAndrey V. Elsukov g_free(buf); 107737d1a121SAndrey V. Elsukov buf = NULL; 107837d1a121SAndrey V. Elsukov } 107937d1a121SAndrey V. Elsukov /* Parse xVBLKs */ 108037d1a121SAndrey V. Elsukov while (!LIST_EMPTY(&db->xvblks)) { 108137d1a121SAndrey V. Elsukov xvblk = LIST_FIRST(&db->xvblks); 108237d1a121SAndrey V. Elsukov if (xvblk->map == 0xFF) { 108337d1a121SAndrey V. Elsukov error = ldm_vblk_handle(db, xvblk->data, xvblk->size); 108437d1a121SAndrey V. Elsukov if (error != 0) 108537d1a121SAndrey V. Elsukov goto fail; 108637d1a121SAndrey V. Elsukov } else { 108737d1a121SAndrey V. Elsukov LDM_DEBUG(0, "%s: incomplete or corrupt " 108837d1a121SAndrey V. Elsukov "xVBLK found\n", pp->name); 108937d1a121SAndrey V. Elsukov goto fail; 109037d1a121SAndrey V. Elsukov } 109137d1a121SAndrey V. Elsukov LIST_REMOVE(xvblk, entry); 109237d1a121SAndrey V. Elsukov g_free(xvblk->data); 109337d1a121SAndrey V. Elsukov g_free(xvblk); 109437d1a121SAndrey V. Elsukov } 109537d1a121SAndrey V. Elsukov /* construct all VBLKs relations */ 109637d1a121SAndrey V. Elsukov LIST_FOREACH(volume, &db->volumes, entry) { 109737d1a121SAndrey V. Elsukov LIST_FOREACH(vblk, &db->vblks, entry) 109837d1a121SAndrey V. Elsukov if (vblk->type == LDM_VBLK_T_COMPONENT && 109937d1a121SAndrey V. Elsukov vblk->u.comp.vol_id == volume->id) { 110037d1a121SAndrey V. Elsukov LIST_INSERT_HEAD(&volume->components, 110137d1a121SAndrey V. Elsukov &vblk->u.comp, entry); 110237d1a121SAndrey V. Elsukov volume->count++; 110337d1a121SAndrey V. Elsukov } 110437d1a121SAndrey V. Elsukov LIST_FOREACH(comp, &volume->components, entry) 110537d1a121SAndrey V. Elsukov LIST_FOREACH(vblk, &db->vblks, entry) 110637d1a121SAndrey V. Elsukov if (vblk->type == LDM_VBLK_T_PARTITION && 110737d1a121SAndrey V. Elsukov vblk->u.part.comp_id == comp->id) { 110837d1a121SAndrey V. Elsukov LIST_INSERT_HEAD(&comp->partitions, 110937d1a121SAndrey V. Elsukov &vblk->u.part, entry); 111037d1a121SAndrey V. Elsukov comp->count++; 111137d1a121SAndrey V. Elsukov } 111237d1a121SAndrey V. Elsukov } 111337d1a121SAndrey V. Elsukov return (0); 111437d1a121SAndrey V. Elsukov fail: 111537d1a121SAndrey V. Elsukov ldm_vmdb_free(db); 111637d1a121SAndrey V. Elsukov g_free(buf); 111737d1a121SAndrey V. Elsukov return (ENXIO); 111837d1a121SAndrey V. Elsukov } 111937d1a121SAndrey V. Elsukov 112037d1a121SAndrey V. Elsukov static int 112137d1a121SAndrey V. Elsukov g_part_ldm_add(struct g_part_table *basetable, struct g_part_entry *baseentry, 112237d1a121SAndrey V. Elsukov struct g_part_parms *gpp) 112337d1a121SAndrey V. Elsukov { 112437d1a121SAndrey V. Elsukov 112537d1a121SAndrey V. Elsukov return (ENOSYS); 112637d1a121SAndrey V. Elsukov } 112737d1a121SAndrey V. Elsukov 112837d1a121SAndrey V. Elsukov static int 112937d1a121SAndrey V. Elsukov g_part_ldm_bootcode(struct g_part_table *basetable, struct g_part_parms *gpp) 113037d1a121SAndrey V. Elsukov { 113137d1a121SAndrey V. Elsukov 113237d1a121SAndrey V. Elsukov return (ENOSYS); 113337d1a121SAndrey V. Elsukov } 113437d1a121SAndrey V. Elsukov 113537d1a121SAndrey V. Elsukov static int 113637d1a121SAndrey V. Elsukov g_part_ldm_create(struct g_part_table *basetable, struct g_part_parms *gpp) 113737d1a121SAndrey V. Elsukov { 113837d1a121SAndrey V. Elsukov 113937d1a121SAndrey V. Elsukov return (ENOSYS); 114037d1a121SAndrey V. Elsukov } 114137d1a121SAndrey V. Elsukov 114237d1a121SAndrey V. Elsukov static int 114337d1a121SAndrey V. Elsukov g_part_ldm_destroy(struct g_part_table *basetable, struct g_part_parms *gpp) 114437d1a121SAndrey V. Elsukov { 114537d1a121SAndrey V. Elsukov struct g_part_ldm_table *table; 114637d1a121SAndrey V. Elsukov struct g_provider *pp; 114737d1a121SAndrey V. Elsukov 114837d1a121SAndrey V. Elsukov table = (struct g_part_ldm_table *)basetable; 114937d1a121SAndrey V. Elsukov /* 115037d1a121SAndrey V. Elsukov * To destroy LDM on a disk partitioned with GPT we should delete 115137d1a121SAndrey V. Elsukov * ms-ldm-metadata partition, but we can't do this via standard 115237d1a121SAndrey V. Elsukov * GEOM_PART method. 115337d1a121SAndrey V. Elsukov */ 115437d1a121SAndrey V. Elsukov if (table->is_gpt) 115537d1a121SAndrey V. Elsukov return (ENOSYS); 115637d1a121SAndrey V. Elsukov pp = LIST_FIRST(&basetable->gpt_gp->consumer)->provider; 115737d1a121SAndrey V. Elsukov /* 115837d1a121SAndrey V. Elsukov * To destroy LDM we should wipe MBR, first private header and 115937d1a121SAndrey V. Elsukov * backup private headers. 116037d1a121SAndrey V. Elsukov */ 116137d1a121SAndrey V. Elsukov basetable->gpt_smhead = (1 << ldm_ph_off[0]) | 1; 116237d1a121SAndrey V. Elsukov /* 116337d1a121SAndrey V. Elsukov * Don't touch last backup private header when LDM database is 116437d1a121SAndrey V. Elsukov * not located in the last 1MByte area. 116537d1a121SAndrey V. Elsukov * XXX: can't remove all blocks. 116637d1a121SAndrey V. Elsukov */ 116737d1a121SAndrey V. Elsukov if (table->db_offset + LDM_DB_SIZE == 116837d1a121SAndrey V. Elsukov pp->mediasize / pp->sectorsize) 116937d1a121SAndrey V. Elsukov basetable->gpt_smtail = 1; 117037d1a121SAndrey V. Elsukov return (0); 117137d1a121SAndrey V. Elsukov } 117237d1a121SAndrey V. Elsukov 117337d1a121SAndrey V. Elsukov static void 117437d1a121SAndrey V. Elsukov g_part_ldm_dumpconf(struct g_part_table *basetable, 117537d1a121SAndrey V. Elsukov struct g_part_entry *baseentry, struct sbuf *sb, const char *indent) 117637d1a121SAndrey V. Elsukov { 117737d1a121SAndrey V. Elsukov struct g_part_ldm_entry *entry; 117837d1a121SAndrey V. Elsukov 117937d1a121SAndrey V. Elsukov entry = (struct g_part_ldm_entry *)baseentry; 118037d1a121SAndrey V. Elsukov if (indent == NULL) { 118137d1a121SAndrey V. Elsukov /* conftxt: libdisk compatibility */ 118237d1a121SAndrey V. Elsukov sbuf_printf(sb, " xs LDM xt %u", entry->type); 118337d1a121SAndrey V. Elsukov } else if (entry != NULL) { 118437d1a121SAndrey V. Elsukov /* confxml: partition entry information */ 118537d1a121SAndrey V. Elsukov sbuf_printf(sb, "%s<rawtype>%u</rawtype>\n", indent, 118637d1a121SAndrey V. Elsukov entry->type); 118737d1a121SAndrey V. Elsukov } else { 118837d1a121SAndrey V. Elsukov /* confxml: scheme information */ 118937d1a121SAndrey V. Elsukov } 119037d1a121SAndrey V. Elsukov } 119137d1a121SAndrey V. Elsukov 119237d1a121SAndrey V. Elsukov static int 119337d1a121SAndrey V. Elsukov g_part_ldm_dumpto(struct g_part_table *table, struct g_part_entry *baseentry) 119437d1a121SAndrey V. Elsukov { 119537d1a121SAndrey V. Elsukov 119637d1a121SAndrey V. Elsukov return (0); 119737d1a121SAndrey V. Elsukov } 119837d1a121SAndrey V. Elsukov 119937d1a121SAndrey V. Elsukov static int 120037d1a121SAndrey V. Elsukov g_part_ldm_modify(struct g_part_table *basetable, 120137d1a121SAndrey V. Elsukov struct g_part_entry *baseentry, struct g_part_parms *gpp) 120237d1a121SAndrey V. Elsukov { 120337d1a121SAndrey V. Elsukov 120437d1a121SAndrey V. Elsukov return (ENOSYS); 120537d1a121SAndrey V. Elsukov } 120637d1a121SAndrey V. Elsukov 120737d1a121SAndrey V. Elsukov static const char * 120837d1a121SAndrey V. Elsukov g_part_ldm_name(struct g_part_table *table, struct g_part_entry *baseentry, 120937d1a121SAndrey V. Elsukov char *buf, size_t bufsz) 121037d1a121SAndrey V. Elsukov { 121137d1a121SAndrey V. Elsukov 121237d1a121SAndrey V. Elsukov snprintf(buf, bufsz, "s%d", baseentry->gpe_index); 121337d1a121SAndrey V. Elsukov return (buf); 121437d1a121SAndrey V. Elsukov } 121537d1a121SAndrey V. Elsukov 121637d1a121SAndrey V. Elsukov static int 121737d1a121SAndrey V. Elsukov ldm_gpt_probe(struct g_part_table *basetable, struct g_consumer *cp) 121837d1a121SAndrey V. Elsukov { 121937d1a121SAndrey V. Elsukov struct g_part_ldm_table *table; 122037d1a121SAndrey V. Elsukov struct g_part_table *gpt; 122137d1a121SAndrey V. Elsukov struct g_part_entry *entry; 122237d1a121SAndrey V. Elsukov struct g_consumer *cp2; 122337d1a121SAndrey V. Elsukov struct gpt_ent *part; 122437d1a121SAndrey V. Elsukov u_char *buf; 122537d1a121SAndrey V. Elsukov int error; 122637d1a121SAndrey V. Elsukov 122737d1a121SAndrey V. Elsukov /* 1228e8d57122SPedro F. Giffuni * XXX: We use some knowledge about GEOM_PART_GPT internal 122937d1a121SAndrey V. Elsukov * structures, but it is easier than parse GPT by himself. 123037d1a121SAndrey V. Elsukov */ 123137d1a121SAndrey V. Elsukov g_topology_lock(); 123237d1a121SAndrey V. Elsukov gpt = cp->provider->geom->softc; 123337d1a121SAndrey V. Elsukov LIST_FOREACH(entry, &gpt->gpt_entry, gpe_entry) { 123437d1a121SAndrey V. Elsukov part = (struct gpt_ent *)(entry + 1); 123537d1a121SAndrey V. Elsukov /* Search ms-ldm-metadata partition */ 123637d1a121SAndrey V. Elsukov if (memcmp(&part->ent_type, 123737d1a121SAndrey V. Elsukov &gpt_uuid_ms_ldm_metadata, sizeof(struct uuid)) != 0 || 123837d1a121SAndrey V. Elsukov entry->gpe_end - entry->gpe_start < LDM_DB_SIZE - 1) 123937d1a121SAndrey V. Elsukov continue; 124037d1a121SAndrey V. Elsukov 124137d1a121SAndrey V. Elsukov /* Create new consumer and attach it to metadata partition */ 124237d1a121SAndrey V. Elsukov cp2 = g_new_consumer(cp->geom); 124337d1a121SAndrey V. Elsukov error = g_attach(cp2, entry->gpe_pp); 124437d1a121SAndrey V. Elsukov if (error != 0) { 124537d1a121SAndrey V. Elsukov g_destroy_consumer(cp2); 124637d1a121SAndrey V. Elsukov g_topology_unlock(); 124737d1a121SAndrey V. Elsukov return (ENXIO); 124837d1a121SAndrey V. Elsukov } 124937d1a121SAndrey V. Elsukov error = g_access(cp2, 1, 0, 0); 125037d1a121SAndrey V. Elsukov if (error != 0) { 125137d1a121SAndrey V. Elsukov g_detach(cp2); 125237d1a121SAndrey V. Elsukov g_destroy_consumer(cp2); 125337d1a121SAndrey V. Elsukov g_topology_unlock(); 125437d1a121SAndrey V. Elsukov return (ENXIO); 125537d1a121SAndrey V. Elsukov } 125637d1a121SAndrey V. Elsukov g_topology_unlock(); 125737d1a121SAndrey V. Elsukov 125837d1a121SAndrey V. Elsukov LDM_DEBUG(2, "%s: LDM metadata partition %s found in the GPT", 125937d1a121SAndrey V. Elsukov cp->provider->name, cp2->provider->name); 126037d1a121SAndrey V. Elsukov /* Read the LDM private header */ 126137d1a121SAndrey V. Elsukov buf = ldm_privhdr_read(cp2, 126237d1a121SAndrey V. Elsukov ldm_ph_off[LDM_PH_GPTINDEX] * cp2->provider->sectorsize, 126337d1a121SAndrey V. Elsukov &error); 126437d1a121SAndrey V. Elsukov if (buf != NULL) { 126537d1a121SAndrey V. Elsukov table = (struct g_part_ldm_table *)basetable; 126637d1a121SAndrey V. Elsukov table->is_gpt = 1; 126737d1a121SAndrey V. Elsukov g_free(buf); 126837d1a121SAndrey V. Elsukov return (G_PART_PROBE_PRI_HIGH); 126937d1a121SAndrey V. Elsukov } 127037d1a121SAndrey V. Elsukov 127137d1a121SAndrey V. Elsukov /* second consumer is no longer needed. */ 127237d1a121SAndrey V. Elsukov g_topology_lock(); 127337d1a121SAndrey V. Elsukov g_access(cp2, -1, 0, 0); 127437d1a121SAndrey V. Elsukov g_detach(cp2); 127537d1a121SAndrey V. Elsukov g_destroy_consumer(cp2); 127637d1a121SAndrey V. Elsukov break; 127737d1a121SAndrey V. Elsukov } 127837d1a121SAndrey V. Elsukov g_topology_unlock(); 127937d1a121SAndrey V. Elsukov return (ENXIO); 128037d1a121SAndrey V. Elsukov } 128137d1a121SAndrey V. Elsukov 128237d1a121SAndrey V. Elsukov static int 128337d1a121SAndrey V. Elsukov g_part_ldm_probe(struct g_part_table *basetable, struct g_consumer *cp) 128437d1a121SAndrey V. Elsukov { 128537d1a121SAndrey V. Elsukov struct g_provider *pp; 128637d1a121SAndrey V. Elsukov u_char *buf, type[64]; 128737d1a121SAndrey V. Elsukov int error, idx; 128837d1a121SAndrey V. Elsukov 128937d1a121SAndrey V. Elsukov 129037d1a121SAndrey V. Elsukov pp = cp->provider; 129137d1a121SAndrey V. Elsukov if (pp->sectorsize != 512) 129237d1a121SAndrey V. Elsukov return (ENXIO); 129337d1a121SAndrey V. Elsukov 129437d1a121SAndrey V. Elsukov error = g_getattr("PART::scheme", cp, &type); 129537d1a121SAndrey V. Elsukov if (error == 0 && strcmp(type, "GPT") == 0) { 129637d1a121SAndrey V. Elsukov if (g_getattr("PART::type", cp, &type) != 0 || 129737d1a121SAndrey V. Elsukov strcmp(type, "ms-ldm-data") != 0) 129837d1a121SAndrey V. Elsukov return (ENXIO); 129937d1a121SAndrey V. Elsukov error = ldm_gpt_probe(basetable, cp); 130037d1a121SAndrey V. Elsukov return (error); 130137d1a121SAndrey V. Elsukov } 130237d1a121SAndrey V. Elsukov 130337d1a121SAndrey V. Elsukov if (basetable->gpt_depth != 0) 130437d1a121SAndrey V. Elsukov return (ENXIO); 130537d1a121SAndrey V. Elsukov 130637d1a121SAndrey V. Elsukov /* LDM has 1M metadata area */ 130737d1a121SAndrey V. Elsukov if (pp->mediasize <= 1024 * 1024) 130837d1a121SAndrey V. Elsukov return (ENOSPC); 130937d1a121SAndrey V. Elsukov 131037d1a121SAndrey V. Elsukov /* Check that there's a MBR */ 131137d1a121SAndrey V. Elsukov buf = g_read_data(cp, 0, pp->sectorsize, &error); 131237d1a121SAndrey V. Elsukov if (buf == NULL) 131337d1a121SAndrey V. Elsukov return (error); 131437d1a121SAndrey V. Elsukov 131537d1a121SAndrey V. Elsukov if (le16dec(buf + DOSMAGICOFFSET) != DOSMAGIC) { 131637d1a121SAndrey V. Elsukov g_free(buf); 131737d1a121SAndrey V. Elsukov return (ENXIO); 131837d1a121SAndrey V. Elsukov } 131937d1a121SAndrey V. Elsukov error = ENXIO; 132037d1a121SAndrey V. Elsukov /* Check that we have LDM partitions in the MBR */ 132137d1a121SAndrey V. Elsukov for (idx = 0; idx < NDOSPART && error != 0; idx++) { 132237d1a121SAndrey V. Elsukov if (buf[DOSPARTOFF + idx * DOSPARTSIZE + 4] == DOSPTYP_LDM) 132337d1a121SAndrey V. Elsukov error = 0; 132437d1a121SAndrey V. Elsukov } 132537d1a121SAndrey V. Elsukov g_free(buf); 132637d1a121SAndrey V. Elsukov if (error == 0) { 132737d1a121SAndrey V. Elsukov LDM_DEBUG(2, "%s: LDM data partitions found in MBR", 132837d1a121SAndrey V. Elsukov pp->name); 132937d1a121SAndrey V. Elsukov /* Read the LDM private header */ 133037d1a121SAndrey V. Elsukov buf = ldm_privhdr_read(cp, 133137d1a121SAndrey V. Elsukov ldm_ph_off[LDM_PH_MBRINDEX] * pp->sectorsize, &error); 133237d1a121SAndrey V. Elsukov if (buf == NULL) 133337d1a121SAndrey V. Elsukov return (error); 133437d1a121SAndrey V. Elsukov g_free(buf); 133537d1a121SAndrey V. Elsukov return (G_PART_PROBE_PRI_HIGH); 133637d1a121SAndrey V. Elsukov } 133737d1a121SAndrey V. Elsukov return (error); 133837d1a121SAndrey V. Elsukov } 133937d1a121SAndrey V. Elsukov 134037d1a121SAndrey V. Elsukov static int 134137d1a121SAndrey V. Elsukov g_part_ldm_read(struct g_part_table *basetable, struct g_consumer *cp) 134237d1a121SAndrey V. Elsukov { 134337d1a121SAndrey V. Elsukov struct g_part_ldm_table *table; 134437d1a121SAndrey V. Elsukov struct g_part_ldm_entry *entry; 134537d1a121SAndrey V. Elsukov struct g_consumer *cp2; 134637d1a121SAndrey V. Elsukov struct ldm_component *comp; 134737d1a121SAndrey V. Elsukov struct ldm_partition *part; 134837d1a121SAndrey V. Elsukov struct ldm_volume *vol; 134937d1a121SAndrey V. Elsukov struct ldm_disk *disk; 135037d1a121SAndrey V. Elsukov struct ldm_db db; 135137d1a121SAndrey V. Elsukov int error, index, skipped; 135237d1a121SAndrey V. Elsukov 135337d1a121SAndrey V. Elsukov table = (struct g_part_ldm_table *)basetable; 135437d1a121SAndrey V. Elsukov memset(&db, 0, sizeof(db)); 135537d1a121SAndrey V. Elsukov cp2 = cp; /* ms-ldm-data */ 135637d1a121SAndrey V. Elsukov if (table->is_gpt) 135737d1a121SAndrey V. Elsukov cp = LIST_FIRST(&cp->geom->consumer); /* ms-ldm-metadata */ 135837d1a121SAndrey V. Elsukov /* Read and parse LDM private headers. */ 135937d1a121SAndrey V. Elsukov error = ldm_privhdr_check(&db, cp, table->is_gpt); 136037d1a121SAndrey V. Elsukov if (error != 0) 13611c45872bSAndrey V. Elsukov goto gpt_cleanup; 136237d1a121SAndrey V. Elsukov basetable->gpt_first = table->is_gpt ? 0: db.ph.start; 136337d1a121SAndrey V. Elsukov basetable->gpt_last = basetable->gpt_first + db.ph.size - 1; 136437d1a121SAndrey V. Elsukov table->db_offset = db.ph.db_offset; 136537d1a121SAndrey V. Elsukov /* Make additional checks for GPT */ 136637d1a121SAndrey V. Elsukov if (table->is_gpt) { 13671c45872bSAndrey V. Elsukov error = ldm_gpt_check(&db, cp); 13681c45872bSAndrey V. Elsukov if (error != 0) 13691c45872bSAndrey V. Elsukov goto gpt_cleanup; 137037d1a121SAndrey V. Elsukov /* 137137d1a121SAndrey V. Elsukov * Now we should reset database offset to zero, because our 137237d1a121SAndrey V. Elsukov * consumer cp is attached to the ms-ldm-metadata partition 137337d1a121SAndrey V. Elsukov * and we don't need add db_offset to read from it. 137437d1a121SAndrey V. Elsukov */ 137537d1a121SAndrey V. Elsukov db.ph.db_offset = 0; 137637d1a121SAndrey V. Elsukov } 137737d1a121SAndrey V. Elsukov /* Read and parse LDM TOC headers. */ 137837d1a121SAndrey V. Elsukov error = ldm_tochdr_check(&db, cp); 137937d1a121SAndrey V. Elsukov if (error != 0) 13801c45872bSAndrey V. Elsukov goto gpt_cleanup; 138137d1a121SAndrey V. Elsukov /* Read and parse LDM VMDB header. */ 138237d1a121SAndrey V. Elsukov error = ldm_vmdbhdr_check(&db, cp); 138337d1a121SAndrey V. Elsukov if (error != 0) 13841c45872bSAndrey V. Elsukov goto gpt_cleanup; 138537d1a121SAndrey V. Elsukov error = ldm_vmdb_parse(&db, cp); 13861c45872bSAndrey V. Elsukov /* 13871c45872bSAndrey V. Elsukov * For the GPT case we must detach and destroy 13881c45872bSAndrey V. Elsukov * second consumer before return. 13891c45872bSAndrey V. Elsukov */ 13901c45872bSAndrey V. Elsukov gpt_cleanup: 13911c45872bSAndrey V. Elsukov if (table->is_gpt) { 13921c45872bSAndrey V. Elsukov g_topology_lock(); 13931c45872bSAndrey V. Elsukov g_access(cp, -1, 0, 0); 13941c45872bSAndrey V. Elsukov g_detach(cp); 13951c45872bSAndrey V. Elsukov g_destroy_consumer(cp); 13961c45872bSAndrey V. Elsukov g_topology_unlock(); 13971c45872bSAndrey V. Elsukov cp = cp2; 13981c45872bSAndrey V. Elsukov } 139937d1a121SAndrey V. Elsukov if (error != 0) 140037d1a121SAndrey V. Elsukov return (error); 140137d1a121SAndrey V. Elsukov /* Search current disk in the disk list. */ 140237d1a121SAndrey V. Elsukov LIST_FOREACH(disk, &db.disks, entry) 140337d1a121SAndrey V. Elsukov if (memcmp(&disk->guid, &db.ph.disk_guid, 140437d1a121SAndrey V. Elsukov sizeof(struct uuid)) == 0) 140537d1a121SAndrey V. Elsukov break; 140637d1a121SAndrey V. Elsukov if (disk == NULL) { 140737d1a121SAndrey V. Elsukov LDM_DEBUG(1, "%s: no LDM volumes on this disk", 140837d1a121SAndrey V. Elsukov cp->provider->name); 140937d1a121SAndrey V. Elsukov ldm_vmdb_free(&db); 141037d1a121SAndrey V. Elsukov return (ENXIO); 141137d1a121SAndrey V. Elsukov } 141237d1a121SAndrey V. Elsukov index = 1; 141337d1a121SAndrey V. Elsukov LIST_FOREACH(vol, &db.volumes, entry) { 141437d1a121SAndrey V. Elsukov LIST_FOREACH(comp, &vol->components, entry) { 141537d1a121SAndrey V. Elsukov /* Skip volumes from different disks. */ 141637d1a121SAndrey V. Elsukov part = LIST_FIRST(&comp->partitions); 141737d1a121SAndrey V. Elsukov if (part->disk_id != disk->id) 141837d1a121SAndrey V. Elsukov continue; 141937d1a121SAndrey V. Elsukov skipped = 0; 142037d1a121SAndrey V. Elsukov /* We don't support spanned and striped volumes. */ 142137d1a121SAndrey V. Elsukov if (comp->count > 1 || part->offset != 0) { 142237d1a121SAndrey V. Elsukov LDM_DEBUG(1, "%s: LDM volume component " 142337d1a121SAndrey V. Elsukov "%ju has %u partitions. Skipped", 142437d1a121SAndrey V. Elsukov cp->provider->name, (uintmax_t)comp->id, 142537d1a121SAndrey V. Elsukov comp->count); 142637d1a121SAndrey V. Elsukov skipped = 1; 142737d1a121SAndrey V. Elsukov } 142837d1a121SAndrey V. Elsukov /* 142937d1a121SAndrey V. Elsukov * Allow mirrored volumes only when they are explicitly 143037d1a121SAndrey V. Elsukov * allowed with kern.geom.part.ldm.show_mirrors=1. 143137d1a121SAndrey V. Elsukov */ 143237d1a121SAndrey V. Elsukov if (vol->count > 1 && show_mirrors == 0) { 143337d1a121SAndrey V. Elsukov LDM_DEBUG(1, "%s: LDM volume %ju has %u " 143437d1a121SAndrey V. Elsukov "components. Skipped", 143537d1a121SAndrey V. Elsukov cp->provider->name, (uintmax_t)vol->id, 143637d1a121SAndrey V. Elsukov vol->count); 143737d1a121SAndrey V. Elsukov skipped = 1; 143837d1a121SAndrey V. Elsukov } 143937d1a121SAndrey V. Elsukov entry = (struct g_part_ldm_entry *)g_part_new_entry( 144037d1a121SAndrey V. Elsukov basetable, index++, 144137d1a121SAndrey V. Elsukov basetable->gpt_first + part->start, 144237d1a121SAndrey V. Elsukov basetable->gpt_first + part->start + 144337d1a121SAndrey V. Elsukov part->size - 1); 144437d1a121SAndrey V. Elsukov /* 144537d1a121SAndrey V. Elsukov * Mark skipped partition as ms-ldm-data partition. 144637d1a121SAndrey V. Elsukov * We do not support them, but it is better to show 144737d1a121SAndrey V. Elsukov * that we have something there, than just show 144837d1a121SAndrey V. Elsukov * free space. 144937d1a121SAndrey V. Elsukov */ 145037d1a121SAndrey V. Elsukov if (skipped == 0) 145137d1a121SAndrey V. Elsukov entry->type = vol->part_type; 145237d1a121SAndrey V. Elsukov else 145337d1a121SAndrey V. Elsukov entry->type = DOSPTYP_LDM; 145437d1a121SAndrey V. Elsukov LDM_DEBUG(1, "%s: new volume id: %ju, start: %ju," 145537d1a121SAndrey V. Elsukov " end: %ju, type: 0x%02x\n", cp->provider->name, 145637d1a121SAndrey V. Elsukov (uintmax_t)part->id,(uintmax_t)part->start + 145737d1a121SAndrey V. Elsukov basetable->gpt_first, (uintmax_t)part->start + 145837d1a121SAndrey V. Elsukov part->size + basetable->gpt_first - 1, 145937d1a121SAndrey V. Elsukov vol->part_type); 146037d1a121SAndrey V. Elsukov } 146137d1a121SAndrey V. Elsukov } 146237d1a121SAndrey V. Elsukov ldm_vmdb_free(&db); 146337d1a121SAndrey V. Elsukov return (error); 146437d1a121SAndrey V. Elsukov } 146537d1a121SAndrey V. Elsukov 146637d1a121SAndrey V. Elsukov static const char * 146737d1a121SAndrey V. Elsukov g_part_ldm_type(struct g_part_table *basetable, struct g_part_entry *baseentry, 146837d1a121SAndrey V. Elsukov char *buf, size_t bufsz) 146937d1a121SAndrey V. Elsukov { 147037d1a121SAndrey V. Elsukov struct g_part_ldm_entry *entry; 147137d1a121SAndrey V. Elsukov int i; 147237d1a121SAndrey V. Elsukov 147337d1a121SAndrey V. Elsukov entry = (struct g_part_ldm_entry *)baseentry; 147463b6b7a7SPedro F. Giffuni for (i = 0; i < nitems(ldm_alias_match); i++) { 147537d1a121SAndrey V. Elsukov if (ldm_alias_match[i].typ == entry->type) 147637d1a121SAndrey V. Elsukov return (g_part_alias_name(ldm_alias_match[i].alias)); 147737d1a121SAndrey V. Elsukov } 147837d1a121SAndrey V. Elsukov snprintf(buf, bufsz, "!%d", entry->type); 147937d1a121SAndrey V. Elsukov return (buf); 148037d1a121SAndrey V. Elsukov } 148137d1a121SAndrey V. Elsukov 148237d1a121SAndrey V. Elsukov static int 148337d1a121SAndrey V. Elsukov g_part_ldm_write(struct g_part_table *basetable, struct g_consumer *cp) 148437d1a121SAndrey V. Elsukov { 148537d1a121SAndrey V. Elsukov 148637d1a121SAndrey V. Elsukov return (ENOSYS); 148737d1a121SAndrey V. Elsukov } 1488