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