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 (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright (c) 2016 by Delphix. All rights reserved. 25 */ 26 27 /* 28 * This file contains functions that operate on partition tables. 29 */ 30 #include <string.h> 31 #include <stdlib.h> 32 #include "global.h" 33 #include "partition.h" 34 #include "misc.h" 35 #include "menu_command.h" 36 #include "menu_partition.h" 37 38 39 /* 40 * Default vtoc information for non-SVr4 partitions 41 */ 42 struct dk_map2 default_vtoc_map[NDKMAP] = { 43 { V_ROOT, 0 }, /* a - 0 */ 44 { V_SWAP, V_UNMNT }, /* b - 1 */ 45 { V_BACKUP, V_UNMNT }, /* c - 2 */ 46 { V_UNASSIGNED, 0 }, /* d - 3 */ 47 { V_UNASSIGNED, 0 }, /* e - 4 */ 48 { V_UNASSIGNED, 0 }, /* f - 5 */ 49 { V_USR, 0 }, /* g - 6 */ 50 { V_UNASSIGNED, 0 }, /* h - 7 */ 51 52 #if defined(_SUNOS_VTOC_16) 53 54 #if defined(i386) 55 { V_BOOT, V_UNMNT }, /* i - 8 */ 56 { V_ALTSCTR, 0 }, /* j - 9 */ 57 58 #else 59 #error No VTOC format defined. 60 #endif /* defined(i386) */ 61 62 { V_UNASSIGNED, 0 }, /* k - 10 */ 63 { V_UNASSIGNED, 0 }, /* l - 11 */ 64 { V_UNASSIGNED, 0 }, /* m - 12 */ 65 { V_UNASSIGNED, 0 }, /* n - 13 */ 66 { V_UNASSIGNED, 0 }, /* o - 14 */ 67 { V_UNASSIGNED, 0 }, /* p - 15 */ 68 #endif /* defined(_SUNOS_VTOC_16) */ 69 }; 70 71 /* 72 * This routine finds the last usable sector in the partition table. 73 * It skips the BACKUP partition. 74 */ 75 static uint64_t 76 maxofN(struct dk_gpt *map) 77 { 78 uint64_t max; 79 uint64_t sec_no[2], start[2], size[2]; 80 int i; 81 82 for (i = 0; i < map->efi_nparts - 1; i++) { 83 start[0] = map->efi_parts[i].p_start; 84 size[0] = map->efi_parts[i].p_size; 85 sec_no[0] = start[0] + size[0]; 86 87 start[1] = map->efi_parts[i+1].p_start; 88 size[1] = map->efi_parts[i+1].p_size; 89 sec_no[1] = start[1] + size[1]; 90 91 if (map->efi_parts[i].p_tag == V_BACKUP) { 92 sec_no[0] = 0; 93 } 94 if (map->efi_parts[i+1].p_tag == V_BACKUP) { 95 sec_no[1] = 0; 96 } 97 if (i == 0) { 98 max = sec_no[1]; 99 } 100 if (sec_no[0] > max) { 101 max = sec_no[0]; 102 } else { 103 max = max; 104 } 105 } 106 if (max == 0) 107 max = map->efi_first_u_lba; 108 return (max); 109 } 110 111 /* 112 * This routine allows the user to change the boundaries of the given 113 * partition in the current partition map. 114 */ 115 void 116 change_partition(int num) 117 { 118 uint_t i; 119 uint64_t i64, j64; 120 uint_t j; 121 int deflt; 122 part_deflt_t p_deflt; 123 u_ioparam_t ioparam; 124 int tag; 125 int flag; 126 char msg[256]; 127 blkaddr32_t cyl_offset = 0; 128 efi_deflt_t efi_deflt; 129 130 /* 131 * check if there exists a partition table for the disk. 132 */ 133 if (cur_parts == NULL) { 134 err_print("Current Disk has no partition table.\n"); 135 return; 136 } 137 138 if (cur_label == L_TYPE_EFI) { 139 if (num > cur_parts->etoc->efi_nparts - 1) { 140 err_print("Invalid partition for EFI label\n"); 141 return; 142 } 143 print_efi_partition(cur_parts->etoc, num, 1); 144 fmt_print("\n"); 145 /* 146 * Prompt for p_tag and p_flag values for this partition 147 */ 148 deflt = cur_parts->etoc->efi_parts[num].p_tag; 149 if (deflt == V_UNASSIGNED) { 150 deflt = V_USR; 151 } 152 (void) sprintf(msg, "Enter partition id tag"); 153 ioparam.io_slist = ptag_choices; 154 tag = input(FIO_SLIST, msg, ':', &ioparam, &deflt, DATA_INPUT); 155 156 deflt = cur_parts->etoc->efi_parts[num].p_flag; 157 (void) sprintf(msg, "Enter partition permission flags"); 158 ioparam.io_slist = pflag_choices; 159 flag = input(FIO_SLIST, msg, ':', &ioparam, &deflt, DATA_INPUT); 160 161 ioparam.io_bounds.lower = cur_parts->etoc->efi_first_u_lba; 162 ioparam.io_bounds.upper = cur_parts->etoc->efi_last_u_lba; 163 164 efi_deflt.start_sector = maxofN(cur_parts->etoc); 165 if ((cur_parts->etoc->efi_parts[num].p_start != 0) && 166 (cur_parts->etoc->efi_parts[num].p_size != 0)) { 167 efi_deflt.start_sector = 168 cur_parts->etoc->efi_parts[num].p_start; 169 } 170 efi_deflt.end_sector = ioparam.io_bounds.upper - 171 efi_deflt.start_sector; 172 i64 = input(FIO_INT64, "Enter new starting Sector", ':', &ioparam, 173 (int *)&efi_deflt, DATA_INPUT); 174 175 ioparam.io_bounds.lower = 0; 176 ioparam.io_bounds.upper = cur_parts->etoc->efi_last_u_lba; 177 efi_deflt.end_sector = cur_parts->etoc->efi_parts[num].p_size; 178 efi_deflt.start_sector = i64; 179 j64 = input(FIO_EFI, "Enter partition size", ':', &ioparam, 180 (int *)&efi_deflt, DATA_INPUT); 181 if (j64 == 0) { 182 tag = V_UNASSIGNED; 183 i64 = 0; 184 } else if ((j64 != 0) && (tag == V_UNASSIGNED)) { 185 tag = V_USR; 186 } 187 188 if (cur_parts->pinfo_name != NULL) 189 make_partition(); 190 191 cur_parts->etoc->efi_parts[num].p_tag = tag; 192 cur_parts->etoc->efi_parts[num].p_flag = flag; 193 cur_parts->etoc->efi_parts[num].p_start = i64; 194 cur_parts->etoc->efi_parts[num].p_size = j64; 195 /* 196 * We are now done with EFI part, so return now 197 */ 198 return; 199 } 200 /* 201 * Print out the given partition so the user knows what they're 202 * getting into. 203 */ 204 print_partition(cur_parts, num, 1); 205 fmt_print("\n"); 206 207 /* 208 * Prompt for p_tag and p_flag values for this partition. 209 */ 210 assert(cur_parts->vtoc.v_version == V_VERSION); 211 deflt = cur_parts->vtoc.v_part[num].p_tag; 212 (void) sprintf(msg, "Enter partition id tag"); 213 ioparam.io_slist = ptag_choices; 214 tag = input(FIO_SLIST, msg, ':', &ioparam, &deflt, DATA_INPUT); 215 216 deflt = cur_parts->vtoc.v_part[num].p_flag; 217 (void) sprintf(msg, "Enter partition permission flags"); 218 ioparam.io_slist = pflag_choices; 219 flag = input(FIO_SLIST, msg, ':', &ioparam, &deflt, DATA_INPUT); 220 221 /* 222 * Ask for the new values. The old values are the defaults, and 223 * strict bounds checking is done on the values given. 224 */ 225 226 #if defined(i386) 227 228 if (tag != V_UNASSIGNED && tag != V_BACKUP && tag != V_BOOT) { 229 /* 230 * Determine cyl offset for boot and alternate partitions. 231 * Assuming that the alternate sectors partition (slice) 232 * physical location immediately follows the boot 233 * partition and partition sizes are expressed in multiples 234 * of cylinder size. 235 */ 236 cyl_offset = cur_parts->pinfo_map[I_PARTITION].dkl_cylno + 1; 237 if (tag != V_ALTSCTR) { 238 if (cur_parts->pinfo_map[J_PARTITION].dkl_nblk != 0) { 239 cyl_offset = 240 cur_parts->pinfo_map[J_PARTITION].dkl_cylno + 241 ((cur_parts->pinfo_map[J_PARTITION].dkl_nblk + 242 (spc()-1)) / spc()); 243 } 244 } 245 } 246 #endif /* defined(i386) */ 247 248 ioparam.io_bounds.lower = 0; 249 ioparam.io_bounds.upper = ncyl - 1; 250 deflt = max(cur_parts->pinfo_map[num].dkl_cylno, 251 cyl_offset); 252 i = (uint_t)input(FIO_INT, "Enter new starting cyl", ':', &ioparam, 253 &deflt, DATA_INPUT); 254 255 ioparam.io_bounds.lower = 0; 256 ioparam.io_bounds.upper = (ncyl - i) * spc(); 257 258 /* fill in defaults for the current partition */ 259 p_deflt.start_cyl = i; 260 p_deflt.deflt_size = 261 min(cur_parts->pinfo_map[num].dkl_nblk, 262 ioparam.io_bounds.upper); 263 264 /* call input, passing p_deflt's address, typecast to (int *) */ 265 j = (uint_t)input(FIO_ECYL, "Enter partition size", ':', &ioparam, 266 (int *)&p_deflt, DATA_INPUT); 267 268 /* 269 * If the current partition has a size of zero change the 270 * tag to Unassigned and the starting cylinder to zero 271 */ 272 273 if (j == 0) { 274 tag = V_UNASSIGNED; 275 i = 0; 276 } 277 278 279 #if defined(i386) 280 281 if (i < cyl_offset && tag != V_UNASSIGNED && tag != V_BACKUP && 282 tag != V_BOOT) { 283 /* 284 * This slice overlaps boot and/or alternates slice 285 * Check if it's the boot or alternates slice and warn 286 * accordingly 287 */ 288 if (i < cur_parts->pinfo_map[I_PARTITION].dkl_cylno + 1) { 289 fmt_print("\nWarning: Partition overlaps boot "); 290 fmt_print("partition. Specify different start cyl.\n"); 291 return; 292 } 293 /* 294 * Cyl offset for alternates partition was calculated before 295 */ 296 if (i < cyl_offset) { 297 fmt_print("\nWarning: Partition overlaps alternates "); 298 fmt_print("partition. Specify different start cyl.\n"); 299 return; 300 } 301 } 302 303 #endif /* defined(i386) */ 304 305 /* 306 * If user has entered a V_BACKUP tag then the partition 307 * size should specify full disk capacity else 308 * return an Error. 309 */ 310 if (tag == V_BACKUP) { 311 uint_t fullsz; 312 313 fullsz = ncyl * nhead * nsect; 314 if (fullsz != j) { 315 /* 316 * V_BACKUP Tag Partition != full disk capacity. 317 * print useful messages. 318 */ 319 fmt_print("\nWarning: Partition with V_BACKUP tag should "); 320 fmt_print("specify full disk capacity. \n"); 321 return; 322 } 323 } 324 325 326 /* 327 * If the current partition is named, we can't change it. 328 * We create a new current partition map instead. 329 */ 330 if (cur_parts->pinfo_name != NULL) 331 make_partition(); 332 /* 333 * Change the values. 334 */ 335 cur_parts->pinfo_map[num].dkl_cylno = i; 336 cur_parts->pinfo_map[num].dkl_nblk = j; 337 338 #if defined(_SUNOS_VTOC_16) 339 cur_parts->vtoc.v_part[num].p_start = (daddr_t)(i * (nhead * nsect)); 340 cur_parts->vtoc.v_part[num].p_size = (long)j; 341 #endif /* defined(_SUNOS_VTOC_16) */ 342 343 /* 344 * Install the p_tag and p_flag values for this partition 345 */ 346 assert(cur_parts->vtoc.v_version == V_VERSION); 347 cur_parts->vtoc.v_part[num].p_tag = (ushort_t)tag; 348 cur_parts->vtoc.v_part[num].p_flag = (ushort_t)flag; 349 } 350 351 352 /* 353 * This routine picks to closest partition table which matches the 354 * selected disk type. It is called each time the disk type is 355 * changed. If no match is found, it uses the first element 356 * of the partition table. If no table exists, a dummy is 357 * created. 358 */ 359 int 360 get_partition() 361 { 362 register struct partition_info *pptr; 363 register struct partition_info *parts; 364 365 /* 366 * If there are no pre-defined maps for this disk type, it's 367 * an error. 368 */ 369 parts = cur_dtype->dtype_plist; 370 if (parts == NULL) { 371 err_print("No defined partition tables.\n"); 372 make_partition(); 373 return (-1); 374 } 375 /* 376 * Loop through the pre-defined maps searching for one which match 377 * disk type. If found copy it into unmamed partition. 378 */ 379 enter_critical(); 380 for (pptr = parts; pptr != NULL; pptr = pptr->pinfo_next) { 381 if (cur_dtype->dtype_asciilabel) { 382 if (pptr->pinfo_name != NULL && strcmp(pptr->pinfo_name, 383 cur_dtype->dtype_asciilabel) == 0) { 384 /* 385 * Set current partition and name it. 386 */ 387 cur_disk->disk_parts = cur_parts = pptr; 388 cur_parts->pinfo_name = pptr->pinfo_name; 389 exit_critical(); 390 return (0); 391 } 392 } 393 } 394 /* 395 * If we couldn't find a match, take the first one. 396 * Set current partition and name it. 397 */ 398 cur_disk->disk_parts = cur_parts = cur_dtype->dtype_plist; 399 cur_parts->pinfo_name = parts->pinfo_name; 400 exit_critical(); 401 return (0); 402 } 403 404 405 /* 406 * This routine creates a new partition map and sets it current. If there 407 * was a current map, the new map starts out identical to it. Otherwise 408 * the new map starts out all zeroes. 409 */ 410 void 411 make_partition() 412 { 413 register struct partition_info *pptr, *parts; 414 int i; 415 416 /* 417 * Lock out interrupts so the lists don't get mangled. 418 */ 419 enter_critical(); 420 /* 421 * Get space for for the new map and link it into the list 422 * of maps for the current disk type. 423 */ 424 pptr = (struct partition_info *)zalloc(sizeof (struct partition_info)); 425 parts = cur_dtype->dtype_plist; 426 if (parts == NULL) { 427 cur_dtype->dtype_plist = pptr; 428 } else { 429 while (parts->pinfo_next != NULL) { 430 parts = parts->pinfo_next; 431 } 432 parts->pinfo_next = pptr; 433 pptr->pinfo_next = NULL; 434 } 435 /* 436 * If there was a current map, copy its values. 437 */ 438 if (cur_label == L_TYPE_EFI) { 439 struct dk_gpt *map; 440 int nparts; 441 int size; 442 443 nparts = cur_parts->etoc->efi_nparts; 444 size = sizeof (struct dk_part) * nparts + sizeof (struct dk_gpt); 445 map = zalloc(size); 446 (void) memcpy(map, cur_parts->etoc, size); 447 pptr->etoc = map; 448 cur_disk->disk_parts = cur_parts = pptr; 449 exit_critical(); 450 return; 451 } 452 if (cur_parts != NULL) { 453 for (i = 0; i < NDKMAP; i++) { 454 pptr->pinfo_map[i] = cur_parts->pinfo_map[i]; 455 } 456 pptr->vtoc = cur_parts->vtoc; 457 } else { 458 /* 459 * Otherwise set initial default vtoc values 460 */ 461 set_vtoc_defaults(pptr); 462 } 463 464 /* 465 * Make the new one current. 466 */ 467 cur_disk->disk_parts = cur_parts = pptr; 468 exit_critical(); 469 } 470 471 472 /* 473 * This routine deletes a partition map from the list of maps for 474 * the given disk type. 475 */ 476 void 477 delete_partition(struct partition_info *parts) 478 { 479 struct partition_info *pptr; 480 481 /* 482 * If there isn't a current map, it's an error. 483 */ 484 if (cur_dtype->dtype_plist == NULL) { 485 err_print("Error: unexpected null partition list.\n"); 486 fullabort(); 487 } 488 /* 489 * Remove the map from the list. 490 */ 491 if (cur_dtype->dtype_plist == parts) 492 cur_dtype->dtype_plist = parts->pinfo_next; 493 else { 494 for (pptr = cur_dtype->dtype_plist; pptr->pinfo_next != parts; 495 pptr = pptr->pinfo_next) 496 ; 497 pptr->pinfo_next = parts->pinfo_next; 498 } 499 /* 500 * Free the space it was using. 501 */ 502 destroy_data((char *)parts); 503 } 504 505 506 /* 507 * Set all partition vtoc fields to defaults 508 */ 509 void 510 set_vtoc_defaults(struct partition_info *part) 511 { 512 int i; 513 514 bzero((caddr_t)&part->vtoc, sizeof (struct dk_vtoc)); 515 516 part->vtoc.v_version = V_VERSION; 517 part->vtoc.v_nparts = NDKMAP; 518 part->vtoc.v_sanity = VTOC_SANE; 519 520 for (i = 0; i < NDKMAP; i++) { 521 part->vtoc.v_part[i].p_tag = default_vtoc_map[i].p_tag; 522 part->vtoc.v_part[i].p_flag = default_vtoc_map[i].p_flag; 523 } 524 } 525