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