1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "@(#)vtoc.c 1.12 09/05/26 SMI" 28 29 #include <diskomizer/vtoc.h> 30 #include <diskomizer/log.h> 31 #include "args.h" 32 #include <string.h> 33 #include <stdlib.h> 34 #include <unistd.h> 35 #include <stropts.h> 36 #include <dlfcn.h> 37 #include <errno.h> 38 39 /* 40 * Shamelessly stolen from vtoc.h so that this will compile on older 41 * Operating systems and be able to take advantage of the new code when 42 * it is available. 43 */ 44 #ifndef V_EXTVERSION 45 46 struct extpartition { 47 ushort_t p_tag; /* ID tag of partition */ 48 ushort_t p_flag; /* permission flags */ 49 ushort_t p_pad[2]; 50 diskaddr_t p_start; /* start sector no of partition */ 51 diskaddr_t p_size; /* # of blocks in partition */ 52 }; 53 54 struct extvtoc { 55 uint64_t v_bootinfo[3]; /* info needed by mboot (unsupported) */ 56 uint64_t v_sanity; /* to verify vtoc sanity */ 57 uint64_t v_version; /* layout version */ 58 char v_volume[LEN_DKL_VVOL]; /* volume name */ 59 ushort_t v_sectorsz; /* sector size in bytes */ 60 ushort_t v_nparts; /* number of partitions */ 61 ushort_t pad[2]; 62 uint64_t v_reserved[10]; 63 struct extpartition v_part[V_NUMPAR]; /* partition headers */ 64 uint64_t timestamp[V_NUMPAR]; /* partition timestamp (unsupported) */ 65 char v_asciilabel[LEN_DKL_ASCII]; /* for compatibility */ 66 }; 67 68 extern int read_extvtoc(int, struct extvtoc *); 69 #pragma weak read_extvtoc 70 #define READ_EXTVTOC(X, F, S) (read_extvtoc != NULL && \ 71 (X = read_extvtoc((F), (S))) >= 0) 72 #else 73 #define READ_EXTVTOC(X, F, S) ((X = read_extvtoc((F), (S))) >= 0) 74 #endif 75 #ifndef DKIOCGEXTVTOC 76 #define DKIOCGEXTVTOC (DKIOC|23) /* Get extended VTOC */ 77 #endif 78 #define VTOC_USER_LIB "VTOC_USER_LIB" 79 80 static struct disko_partition 81 extvtoc_part_to_diskopart(struct extpartition *p) 82 { 83 struct disko_partition dp; 84 85 #define P_TO_DP(A) dp.A = (uint64_t)p->A 86 87 P_TO_DP(p_tag); 88 P_TO_DP(p_flag); 89 P_TO_DP(p_start); 90 P_TO_DP(p_size); 91 92 #undef P_TO_DP 93 return (dp); 94 } 95 96 static struct disko_partition 97 vtoc_part_to_diskopartition(struct partition *p) 98 { 99 struct disko_partition dp; 100 101 #define P_TO_DP(A) dp.A = (uint64_t)p->A 102 103 P_TO_DP(p_tag); 104 P_TO_DP(p_flag); 105 P_TO_DP(p_start); 106 P_TO_DP(p_size); 107 108 #undef P_TO_DP 109 return (dp); 110 } 111 112 struct disko_vtoc * 113 alloc_disko_vtoc(uint_t nparts) 114 { 115 struct disko_vtoc *d; 116 117 d = ((struct disko_vtoc *) malloc(sizeof (struct disko_vtoc) + 118 (nparts - 1) * sizeof (d->v_part[0]))); 119 return (d); 120 } 121 122 static char * 123 not_null_dlerror(void) 124 { 125 static char no_error[] = "No error"; 126 char *x; 127 128 x = dlerror(); 129 return ((x == NULL) ? &no_error[0] : x); 130 } 131 132 static void * 133 try_efi(int fd) 134 { 135 struct dk_gpt *efi = NULL; 136 int i; 137 uint_t x; 138 void *handle; 139 struct disko_vtoc *dv; 140 short (*my_efi_alloc_and_read)(int fd, struct dk_gpt **); 141 void (*my_efi_free)(struct dk_gpt *); 142 143 if ((handle = dlopen("libefi.so.1", RTLD_NOW)) == NULL) { 144 plog(LOG_DEBUG, "%s\n", not_null_dlerror()); 145 return (NULL); 146 } 147 my_efi_alloc_and_read = (short (*)(int, struct dk_gpt **)) 148 dlsym(handle, "efi_alloc_and_read"); 149 my_efi_free = (void (*)(struct dk_gpt *))dlsym(handle, 150 "efi_free"); 151 152 153 if (my_efi_alloc_and_read == NULL || 154 ((int)(x = my_efi_alloc_and_read(fd, &efi))) < 0) { 155 plog(LOG_DEBUG, "%s\n", not_null_dlerror()); 156 dlclose(handle); 157 return (NULL); 158 } 159 160 /* 161 * Work around bug 4888423. The Current version was reduced but 162 * there could be efi labels out there with the incorrect 163 * EFI_VERSION102 164 */ 165 if (efi->efi_version != EFI_VERSION_CURRENT && 166 !(efi->efi_version == EFI_VERSION102 && 167 opts.obscure_allow_efi_102)) { 168 if (efi->efi_version == EFI_VERSION102) { 169 plog(LOG_ERR, 170 "EFI version mismatch. Got %#x expected %#x, " 171 "try setting option obscure_allow_efi_102\n", 172 efi->efi_version, EFI_VERSION_CURRENT); 173 } else { 174 plog(LOG_ERR, 175 "EFI version mismatch. Got %#x expected %#x\n", 176 efi->efi_version, EFI_VERSION_CURRENT); 177 } 178 dlclose(handle); 179 return (NULL); 180 } 181 182 for (i = 0; i < 183 (sizeof (efi->efi_reserved) / 184 sizeof (efi->efi_reserved[0])); i++) { 185 if (efi->efi_reserved[i] != 0) { 186 plog(LOG_WARNING, 187 "efi->efi_reserved[%d] is not zero (%#x)\n", i, 188 efi->efi_reserved[i]); 189 if (!opts.obscure_allow_non_zero_efi_reserved) { 190 dlclose(handle); 191 return (NULL); 192 } 193 } 194 } 195 196 if ((dv = alloc_disko_vtoc(efi->efi_nparts)) != NULL) { 197 198 dv->disko_vtoc_size = sizeof (*dv); 199 dv->disko_part_size = sizeof (dv->v_part[0]); 200 201 memset(&dv->v_volume[0], 0, sizeof (dv->v_volume)); 202 memset(&dv->v_asciilabel[0], 0, 203 sizeof (dv->v_asciilabel)); 204 205 dv->v_sectorsz = efi->efi_lbasize; 206 dv->v_nparts = efi->efi_nparts; 207 dv->this_part = x; 208 209 for (i = 0; i < efi->efi_nparts; i++) { 210 dv->v_part[i].p_tag = efi->efi_parts[i].p_tag; 211 dv->v_part[i].p_flag = efi->efi_parts[i].p_flag; 212 dv->v_part[i].p_start = 213 efi->efi_parts[i].p_start; 214 dv->v_part[i].p_size = efi->efi_parts[i].p_size; 215 } 216 } 217 if (my_efi_free) { 218 my_efi_free(efi); 219 } 220 dlclose(handle); 221 return (dv); 222 } 223 /* 224 * Try to read the vtoc using a user supplied shared library containing the 225 * symbol user_read_vtoc which should be a function that is of type 226 * user_read_vtoc_t. If it is not a function this code will not fail 227 * gracefully. 228 * 229 * The path to the users library is supplied as an option to diskomizer 230 * with the VTOC_USER_LIB, option. Currently this means that this option 231 * is not documented automatically like the others in the main diskomizer 232 * code base. 233 * 234 * To prevent version problems it must fill in the first three fields of 235 * the structure with some known good values which are in the header file 236 * user_vtoc.h which it should be compiled with. 237 * 238 */ 239 static void * 240 try_user_vtoc(int fd) 241 { 242 static int have_opts; 243 static char *user_lib_name; 244 245 if (have_opts == 0) { 246 const struct option_ops *ops; 247 ops = opts_init(); 248 249 if (ops->opt_str(VTOC_USER_LIB, &user_lib_name) == OPT_OK) { 250 user_lib_name = strdup(user_lib_name); 251 } 252 have_opts = 1; 253 opts_fini(); 254 } 255 if (user_lib_name != NULL) { 256 user_read_vtoc_t my_user_read_vtoc; 257 void *handle; 258 struct disko_vtoc *dv; 259 260 if ((handle = dlopen(user_lib_name, RTLD_NOW)) == NULL) { 261 return (NULL); 262 } 263 if ((my_user_read_vtoc = (user_read_vtoc_t)dlsym(handle, 264 "user_read_vtoc")) == NULL) { 265 dlclose(handle); 266 return (NULL); 267 } 268 dv = my_user_read_vtoc(fd); 269 dlclose(handle); 270 if (dv->disko_vtoc_version != DISKO_VTOC_VERSION) { 271 plog(LOG_WARNING, 272 "%s:user_read_vtoc() returned incorrect version.\n", 273 user_lib_name); 274 disko_vtoc_free(dv); 275 return (NULL); 276 } 277 if (dv->disko_vtoc_size != sizeof (*dv) || 278 dv->disko_part_size != sizeof (dv->v_part[0])) { 279 plog(LOG_WARNING, 280 "%s:user_read_vtoc() returned incorrect " 281 "structure size[s].\n", user_lib_name); 282 disko_vtoc_free(dv); 283 return (NULL); 284 } 285 286 return (dv); 287 } 288 return (NULL); 289 } 290 291 void * 292 read_disko_vtoc(int fd) 293 { 294 int x; 295 union { 296 struct vtoc vtoc; 297 struct extvtoc extvtoc; 298 } u; 299 300 if (READ_EXTVTOC(x, fd, &u.extvtoc) || 301 (x = ioctl(fd, DKIOCGEXTVTOC, &u.extvtoc)) >= 0) { 302 struct disko_vtoc *dv; 303 int i; 304 305 if ((dv = alloc_disko_vtoc(u.extvtoc.v_nparts)) == NULL) { 306 return (NULL); 307 } 308 309 dv->disko_vtoc_size = sizeof (*dv); 310 dv->disko_part_size = sizeof (dv->v_part[0]); 311 312 memcpy(dv->v_volume, u.extvtoc.v_volume, LEN_DKL_VVOL); 313 dv->v_volume[LEN_DKL_VVOL] = NULL; 314 memcpy(dv->v_asciilabel, u.extvtoc.v_asciilabel, LEN_DKL_ASCII); 315 dv->v_asciilabel[LEN_DKL_ASCII] = NULL; 316 dv->v_sectorsz = u.extvtoc.v_sectorsz; 317 dv->v_nparts = u.extvtoc.v_nparts; 318 dv->this_part = x; 319 320 for (i = 0; i < u.extvtoc.v_nparts; i++) { 321 dv->v_part[i] = 322 extvtoc_part_to_diskopart(&u.extvtoc.v_part[i]); 323 } 324 return (dv); 325 } else if ((x = read_vtoc(fd, &u.vtoc)) >= 0 || 326 (x = ioctl(fd, DKIOCGVTOC, &u.vtoc)) >= 0) { 327 struct disko_vtoc *dv; 328 int i; 329 330 if ((dv = alloc_disko_vtoc(u.vtoc.v_nparts)) == NULL) { 331 return (NULL); 332 } 333 334 dv->disko_vtoc_size = sizeof (*dv); 335 dv->disko_part_size = sizeof (dv->v_part[0]); 336 337 memcpy(dv->v_volume, u.vtoc.v_volume, LEN_DKL_VVOL); 338 dv->v_volume[LEN_DKL_VVOL] = NULL; 339 memcpy(dv->v_asciilabel, u.vtoc.v_asciilabel, LEN_DKL_ASCII); 340 dv->v_asciilabel[LEN_DKL_ASCII] = NULL; 341 dv->v_sectorsz = u.vtoc.v_sectorsz; 342 dv->v_nparts = u.vtoc.v_nparts; 343 dv->this_part = x; 344 345 for (i = 0; i < u.vtoc.v_nparts; i++) { 346 dv->v_part[i] = 347 vtoc_part_to_diskopartition(&u.vtoc.v_part[i]); 348 } 349 return (dv); 350 } else if ((x == -1) && (errno == ENOTSUP || errno == ENOTTY)) { 351 void * handle; 352 353 handle = try_efi(fd); 354 if (handle == NULL) { 355 handle = try_user_vtoc(fd); 356 } 357 return (handle); 358 } else { 359 return (NULL); 360 } 361 } 362 363 const char * 364 disko_vtoc_volume(void *handle) 365 { 366 struct disko_vtoc *vt = (struct disko_vtoc *)handle; 367 368 return (vt->v_volume); 369 } 370 371 const char * 372 disko_vtoc_asciilabel(void *handle) 373 { 374 struct disko_vtoc *vt = (struct disko_vtoc *)handle; 375 376 return (vt->v_asciilabel); 377 } 378 379 uint_t 380 disko_vtoc_sectorsz(void *handle) 381 { 382 struct disko_vtoc *vt = (struct disko_vtoc *)handle; 383 384 return (vt->v_sectorsz); 385 } 386 387 uint_t 388 disko_vtoc_nparts(void *handle) 389 { 390 struct disko_vtoc *vt = (struct disko_vtoc *)handle; 391 392 return (vt->v_nparts); 393 } 394 395 const struct disko_partition * 396 disko_vtoc_parts(void *handle, uint_t part) 397 { 398 struct disko_vtoc *vt = (struct disko_vtoc *)handle; 399 400 return (&vt->v_part[part]); 401 } 402 403 uint_t 404 disko_vtoc_this_part(void *handle) 405 { 406 struct disko_vtoc *vt = (struct disko_vtoc *)handle; 407 408 return (vt->this_part); 409 } 410 411 const struct disko_partition * 412 disko_vtoc_this_partition(void *handle) 413 { 414 return (disko_vtoc_parts(handle, disko_vtoc_this_part(handle))); 415 } 416 417 void 418 disko_vtoc_free(void *handle) 419 { 420 free(handle); 421 } 422