1 /* $NetBSD: partitions.c,v 1.10 2020/01/28 07:43:42 martin Exp $ */ 2 3 /* 4 * Copyright 2018 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS'' 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 26 * THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 */ 29 30 #include "defs.h" 31 #include "mbr.h" 32 #include <assert.h> 33 34 /* 35 * A list of partitioning schemes, so we can iterate over everything 36 * supported (e.g. when partitioning a new disk). NULL terminated. 37 */ 38 const struct disk_partitioning_scheme **available_part_schemes; 39 /* 40 * The number of valid entries on above list 41 */ 42 size_t num_available_part_schemes; 43 44 extern const struct disk_partitioning_scheme disklabel_parts; 45 46 /* 47 * Generic reader - query a disk device and read all partitions from it. 48 * disk_size is in units of physical sector size, which is passe as 49 * bytes_per_sec. 50 */ 51 struct disk_partitions * 52 partitions_read_disk(const char *dev, daddr_t disk_size, size_t bytes_per_sec, 53 bool no_mbr) 54 { 55 const struct disk_partitioning_scheme **ps; 56 #ifdef HAVE_MBR 57 bool mbr_done = false, disklabel_done = false; 58 #endif 59 60 if (!available_part_schemes) 61 return NULL; 62 63 for (ps = available_part_schemes; *ps; ps++) { 64 #ifdef HAVE_MBR 65 if (!no_mbr && (*ps) == &disklabel_parts && !mbr_done) 66 continue; 67 if (no_mbr && (*ps)->name == MSG_parttype_mbr) 68 continue; 69 if ((*ps)->name == MSG_parttype_mbr) 70 mbr_done = true; 71 if ((*ps)->read_from_disk == disklabel_parts.read_from_disk) 72 disklabel_done = true; 73 #endif 74 struct disk_partitions *parts = 75 (*ps)->read_from_disk(dev, 0, disk_size, bytes_per_sec, 76 *ps); 77 if (parts) 78 return parts; 79 } 80 #ifdef HAVE_MBR 81 if (!disklabel_done) 82 return disklabel_parts.read_from_disk(dev, 0, disk_size, 83 bytes_per_sec, &disklabel_parts); 84 #endif 85 return NULL; 86 } 87 88 bool 89 generic_adapt_foreign_part_info(const struct disk_partitions *myself, 90 struct disk_part_info *dest, 91 const struct disk_partitioning_scheme *src_scheme, 92 const struct disk_part_info *src) 93 { 94 *dest = *src; 95 if (myself->pscheme == src_scheme) 96 return true; /* no conversion needed */ 97 98 if (src->nat_type == NULL) 99 return false; 100 101 /* slightly simplistic, enhance when needed */ 102 dest->nat_type = myself->pscheme->get_fs_part_type( 103 dest->nat_type ? dest->nat_type->generic_ptype : PT_root, 104 dest->fs_type, 105 dest->fs_sub_type); 106 if (dest->nat_type == NULL) 107 dest->nat_type = myself->pscheme->get_generic_part_type( 108 src->nat_type->generic_ptype); 109 if (dest->nat_type == NULL) 110 dest->nat_type = myself->pscheme->create_unknown_part_type(); 111 if (dest->nat_type == NULL) 112 dest->nat_type = myself->pscheme->get_generic_part_type( 113 PT_unknown); 114 115 return true; 116 } 117 118 /*************** global init ****************************************/ 119 /* 120 * Helper structure to fill our global list of available partitioning 121 * schemes. 122 */ 123 struct part_scheme_desc { 124 bool (*is_available)(void); 125 const struct disk_partitioning_scheme *ps; 126 }; 127 128 #ifdef HAVE_GPT 129 bool gpt_parts_check(void); 130 extern const struct disk_partitioning_scheme gpt_parts; 131 #endif 132 #ifdef HAVE_MBR 133 extern const struct disk_partitioning_scheme mbr_parts; 134 #endif 135 136 #if RAW_PART != 2 137 static struct disk_partitioning_scheme only_disklabel_parts; 138 139 /* 140 * If not overriden by MD code, we can not boot from plain 141 * disklabel disks (w/o MBR). 142 */ 143 static bool have_only_disklabel_boot_support(const char *disk) 144 { 145 #ifdef HAVE_PLAIN_DISKLABEL_BOOT 146 return HAVE_PLAIN_DISKLABEL_BOOT(disk); 147 #else 148 return false; 149 #endif 150 } 151 #endif 152 153 /* 154 * One time initialization 155 */ 156 void 157 partitions_init(void) 158 { 159 /* 160 * List of partitioning schemes. 161 * Order is important, the selection menu is created from start 162 * to end. Keep good defaults early. Most architectures will 163 * only offer very few entries. 164 */ 165 static const struct part_scheme_desc all_descs[] = { 166 #if RAW_PART == 2 /* only available as primary on some architectures */ 167 { NULL, &disklabel_parts }, 168 #endif 169 #ifdef HAVE_GPT 170 { gpt_parts_check, &gpt_parts }, 171 #endif 172 #ifdef HAVE_MBR 173 { NULL, &mbr_parts }, 174 #endif 175 #if RAW_PART != 2 /* "whole disk NetBSD" disklabel variant */ 176 { NULL, &only_disklabel_parts }, 177 #endif 178 }; 179 180 size_t i, avail; 181 const struct disk_partitioning_scheme **out; 182 bool *is_available; 183 static const size_t all_cnt = __arraycount(all_descs); 184 185 check_available_binaries(); 186 187 #if RAW_PART != 2 188 /* generate a variant of disklabel w/o parent scheme */ 189 only_disklabel_parts = disklabel_parts; 190 only_disklabel_parts.name = MSG_parttype_only_disklabel; 191 only_disklabel_parts.have_boot_support = 192 have_only_disklabel_boot_support; 193 #endif 194 195 196 is_available = malloc(all_cnt); 197 198 for (avail = i = 0; i < all_cnt; i++) { 199 is_available[i] = all_descs[i].is_available == NULL 200 || all_descs[i].is_available(); 201 if (is_available[i]) 202 avail++; 203 } 204 205 if (avail == 0) 206 return; 207 208 num_available_part_schemes = avail; 209 available_part_schemes = malloc(sizeof(*available_part_schemes) 210 * (avail+1)); 211 if (available_part_schemes == NULL) 212 return; 213 214 for (out = available_part_schemes, i = 0; i < all_cnt; i++) { 215 if (!is_available[i]) 216 continue; 217 *out++ = all_descs[i].ps; 218 } 219 *out = NULL; 220 221 free(is_available); 222 } 223 224 /* 225 * Final cleanup 226 */ 227 void 228 partitions_cleanup(void) 229 { 230 for (size_t i = 0; i < num_available_part_schemes; i++) 231 if (available_part_schemes[i]->cleanup != NULL) 232 available_part_schemes[i]->cleanup(); 233 free(available_part_schemes); 234 } 235