disklabel.c revision 1.31
1/* $NetBSD: disklabel.c,v 1.31 2020/01/20 21:26:35 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 "md.h" 32#include <assert.h> 33#include <util.h> 34#include <paths.h> 35#include <sys/ioctl.h> 36#include <sys/param.h> 37 38const struct disk_partitioning_scheme disklabel_parts; 39 40/*************** disklabel ******************************************/ 41/* a disklabel based disk_partitions interface */ 42struct disklabel_disk_partitions { 43 struct disk_partitions dp; 44 struct disklabel l; 45 daddr_t ptn_alignment; 46 char last_mounted[MAXPARTITIONS][MOUNTLEN]; 47 uint fs_sub_type[MAXPARTITIONS]; 48}; 49 50/* 51 * Maximum number of disklabel partitions the current kernel supports 52 */ 53size_t dl_maxpart; 54 55/* index into this arrray is the type code */ 56static struct part_type_desc dl_types[__arraycount(fstypenames)-1]; 57 58struct dl_custom_ptype { 59 unsigned int type; 60 char short_desc[6], description[30]; 61 struct part_type_desc desc; 62}; 63struct dl_custom_ptype * dl_custom_ptypes; 64size_t dl_custom_ptype_count; 65 66static uint8_t dl_part_type_from_generic(const struct part_type_desc*); 67 68static void 69disklabel_init_default_alignment(struct disklabel_disk_partitions *parts, 70 uint track) 71{ 72 if (track == 0) 73 track = MEG / 512; 74 75 if (dl_maxpart == 0) 76 dl_maxpart = getmaxpartitions(); 77 78#ifdef MD_DISKLABEL_SET_ALIGN_PRE 79 if (MD_DISKLABEL_SET_ALIGN_PRE(parts->ptn_alignment, track)) 80 return; 81#endif 82 /* Use 1MB alignemnt for large (>128GB) disks */ 83 if (parts->dp.disk_size > HUGE_DISK_SIZE) { 84 parts->ptn_alignment = 2048; 85 } else if (parts->dp.disk_size > TINY_DISK_SIZE) { 86 parts->ptn_alignment = 64; 87 } else { 88 parts->ptn_alignment = 1; 89 } 90#ifdef MD_DISKLABEL_SET_ALIGN_POST 91 MD_DISKLABEL_SET_ALIGN_POST(parts->ptn_alignment, track); 92#endif 93} 94 95static bool 96disklabel_change_geom(struct disk_partitions *arg, int ncyl, int nhead, 97 int nsec) 98{ 99 struct disklabel_disk_partitions *parts = 100 (struct disklabel_disk_partitions*)arg; 101 102 assert(parts->l.d_secsize != 0); 103 assert(parts->l.d_nsectors != 0); 104 assert(parts->l.d_ntracks != 0); 105 assert(parts->l.d_ncylinders != 0); 106 assert(parts->l.d_secpercyl != 0); 107 108 disklabel_init_default_alignment(parts, nhead * nsec); 109 if (ncyl*nhead*nsec <= TINY_DISK_SIZE) 110 set_default_sizemult(1); 111 else 112 set_default_sizemult(MEG/512); 113 114 return true; 115} 116 117static size_t 118disklabel_cylinder_size(const struct disk_partitions *arg) 119{ 120 const struct disklabel_disk_partitions *parts = 121 (const struct disklabel_disk_partitions*)arg; 122 123 return parts->l.d_secpercyl; 124} 125 126static bool 127disklabel_non_bootable(const char *disk) 128{ 129 130 return false; 131} 132 133static struct disk_partitions * 134disklabel_parts_new(const char *dev, daddr_t start, daddr_t len, 135 daddr_t total_size, bool is_boot_drive, struct disk_partitions *parent) 136{ 137 struct disklabel_disk_partitions *parts; 138 struct disk_geom geo; 139 140 if (!get_disk_geom(dev, &geo)) 141 return NULL; 142 143 parts = calloc(1, sizeof(*parts)); 144 if (parts == NULL) 145 return NULL; 146 147 if (len > disklabel_parts.size_limit) 148 len = disklabel_parts.size_limit; 149 if (total_size > disklabel_parts.size_limit) 150 total_size = disklabel_parts.size_limit; 151 152 parts->l.d_ncylinders = geo.dg_ncylinders; 153 parts->l.d_ntracks = geo.dg_ntracks; 154 parts->l.d_nsectors = geo.dg_nsectors; 155 parts->l.d_secsize = geo.dg_secsize; 156 parts->l.d_secpercyl = geo.dg_nsectors * geo.dg_ntracks; 157 158 parts->dp.pscheme = &disklabel_parts; 159 parts->dp.disk = strdup(dev); 160 parts->dp.disk_start = start; 161 parts->dp.disk_size = parts->dp.free_space = len; 162 disklabel_init_default_alignment(parts, parts->l.d_secpercyl); 163 parts->dp.parent = parent; 164 165 strncpy(parts->l.d_packname, "fictious", sizeof parts->l.d_packname); 166 167#if RAW_PART > 2 168 if (parts->dp.parent != NULL) { 169 parts->l.d_partitions[RAW_PART-1].p_fstype = FS_UNUSED; 170 parts->l.d_partitions[RAW_PART-1].p_offset = start; 171 parts->l.d_partitions[RAW_PART-1].p_size = len; 172 parts->dp.num_part++; 173 } 174#endif 175 parts->l.d_partitions[RAW_PART].p_fstype = FS_UNUSED; 176 parts->l.d_partitions[RAW_PART].p_offset = 0; 177 parts->l.d_partitions[RAW_PART].p_size = total_size; 178 parts->dp.num_part++; 179 180 parts->l.d_npartitions = RAW_PART+1; 181 182 return &parts->dp; 183} 184 185static struct disk_partitions * 186disklabel_parts_read(const char *disk, daddr_t start, daddr_t len, 187 const struct disk_partitioning_scheme *scheme) 188{ 189 int fd; 190 char diskpath[MAXPATHLEN]; 191 uint flags; 192#ifndef DISKLABEL_NO_ONDISK_VERIFY 193 bool have_raw_label = false; 194 195 /* 196 * Verify we really have a disklabel. 197 */ 198 if (run_program(RUN_SILENT | RUN_ERROR_OK, 199 "disklabel -r %s", disk) == 0) 200 have_raw_label = true; 201#endif 202 203 /* read partitions */ 204 205 struct disklabel_disk_partitions *parts = calloc(1, sizeof(*parts)); 206 if (parts == NULL) 207 return NULL; 208 209 fd = opendisk(disk, O_RDONLY, diskpath, sizeof(diskpath), 0); 210 if (fd == -1) { 211 free(parts); 212 return NULL; 213 } 214 215 /* 216 * We should actually try to read the label inside the start/len 217 * boundary, but for simplicity just rely on the kernel and 218 * instead verify a FS_UNUSED partition at RAW_PART-1 (if 219 * RAW_PART > 'c') is within the given limits. 220 */ 221 if (ioctl(fd, DIOCGDINFO, &parts->l) < 0) { 222 free(parts); 223 close(fd); 224 return NULL; 225 } 226#if RAW_PART > 2 227 if (parts->l.d_partitions[RAW_PART-1].p_fstype == FS_UNUSED) { 228 daddr_t dlstart = parts->l.d_partitions[RAW_PART-1].p_offset; 229 daddr_t dlend = start + 230 parts->l.d_partitions[RAW_PART-1].p_size; 231 232 if (dlstart < start && dlend > (start+len)) { 233 assert(false); 234 free(parts); 235 close(fd); 236 return NULL; 237 } 238 } 239#endif 240 241 if (len > disklabel_parts.size_limit) 242 len = disklabel_parts.size_limit; 243 parts->dp.pscheme = scheme; 244 parts->dp.disk = strdup(disk); 245 parts->dp.disk_start = start; 246 parts->dp.disk_size = parts->dp.free_space = len; 247 disklabel_init_default_alignment(parts, parts->l.d_secpercyl); 248 249 for (int part = 0; part < parts->l.d_npartitions; part++) { 250 if (parts->l.d_partitions[part].p_fstype == FS_UNUSED 251 && parts->l.d_partitions[part].p_size == 0) 252 continue; 253 254 parts->dp.num_part++; 255 if (parts->l.d_partitions[part].p_fstype == FS_UNUSED) 256 continue; 257 258 flags = 0; 259 if (parts->l.d_partitions[part].p_fstype == FS_MSDOS) 260 flags = GLM_MAYBE_FAT32; 261 else if (parts->l.d_partitions[part].p_fstype == FS_BSDFFS) 262 flags = GLM_LIKELY_FFS; 263 if (flags != 0) { 264 uint fs_type, fs_sub_type; 265 const char *lm = get_last_mounted(fd, 266 parts->l.d_partitions[part].p_offset, 267 &fs_type, &fs_sub_type, flags); 268 if (lm != NULL && *lm != 0) { 269 strlcpy(parts->last_mounted[part], lm, 270 sizeof(parts->last_mounted[part])); 271 if (parts->l.d_partitions[part].p_fstype == 272 fs_type) 273 parts->fs_sub_type[part] = fs_sub_type; 274 canonicalize_last_mounted( 275 parts->last_mounted[part]); 276 } 277 } 278 279 if (parts->l.d_partitions[part].p_size > parts->dp.free_space) 280 parts->dp.free_space = 0; 281 else 282 parts->dp.free_space -= 283 parts->l.d_partitions[part].p_size; 284 } 285 close(fd); 286 287#ifndef DISKLABEL_NO_ONDISK_VERIFY 288 if (!have_raw_label) { 289 bool found_real_part = false; 290 291 if (parts->l.d_npartitions <= RAW_PART || 292 parts->l.d_partitions[RAW_PART].p_size == 0) 293 goto no_valid_label; 294 295 /* 296 * Check if kernel translation gave us "something" besides 297 * the raw or the whole-disk partition. 298 * If not: report missing disklabel. 299 */ 300 for (int part = 0; part < parts->l.d_npartitions; part++) { 301 if (parts->l.d_partitions[part].p_fstype == FS_UNUSED) 302 continue; 303 if (part == 0 && 304 parts->l.d_partitions[part].p_offset == 305 parts->l.d_partitions[RAW_PART].p_offset && 306 parts->l.d_partitions[part].p_size == 307 parts->l.d_partitions[RAW_PART].p_size) 308 continue; 309 if (part == RAW_PART) 310 continue; 311 found_real_part = true; 312 break; 313 } 314 if (!found_real_part) { 315 /* no partion there yet */ 316no_valid_label: 317 free(parts); 318 return NULL; 319 } 320 } 321#endif 322 323 return &parts->dp; 324} 325 326/* 327 * Escape a string for usage as a tag name in a capfile(5), 328 * we really know there is enough space in the destination buffer... 329 */ 330static void 331escape_capfile(char *dest, const char *src, size_t len) 332{ 333 while (*src && len > 0) { 334 if (*src == ':') 335 *dest++ = ' '; 336 else 337 *dest++ = *src; 338 src++; 339 len--; 340 } 341 *dest = 0; 342} 343 344static bool 345disklabel_write_to_disk(struct disk_partitions *arg) 346{ 347 struct disklabel_disk_partitions *parts = 348 (struct disklabel_disk_partitions*)arg; 349 FILE *f; 350 char fname[PATH_MAX], packname[sizeof(parts->l.d_packname)+1], 351 disktype[sizeof(parts->l.d_typename)+1]; 352 int i, rv = 0; 353 const char *disk = parts->dp.disk, *s; 354 const struct partition *lp; 355 char *d; 356 size_t n; 357 358 assert(parts->l.d_secsize != 0); 359 assert(parts->l.d_nsectors != 0); 360 assert(parts->l.d_ntracks != 0); 361 assert(parts->l.d_ncylinders != 0); 362 assert(parts->l.d_secpercyl != 0); 363 364 /* make sure we have a 0 terminated packname */ 365 strlcpy(packname, parts->l.d_packname, sizeof packname); 366 if (packname[0] == 0) 367 strcpy(packname, "fictious"); 368 369 /* fill typename with disk name prefix, if not already set */ 370 if (strlen(parts->l.d_typename) == 0) { 371 for (n = 0, d = parts->l.d_typename, s = disk; 372 *s && n < sizeof(parts->l.d_typename); d++, s++, n++) { 373 if (isdigit((unsigned char)*s)) 374 break; 375 *d = *s; 376 } 377 } 378 379 /* we need a valid disk type name, so enforce an arbitrary if 380 * above did not yield a usable one */ 381 if (strlen(parts->l.d_typename) == 0) 382 strncpy(parts->l.d_typename, "SCSI", 383 sizeof(parts->l.d_typename)); 384 escape_capfile(disktype, parts->l.d_typename, 385 sizeof(parts->l.d_typename)); 386 387 sprintf(fname, "/tmp/disklabel.%u", getpid()); 388 f = fopen(fname, "w"); 389 if (f == NULL) 390 return false; 391 392 lp = parts->l.d_partitions; 393 scripting_fprintf(NULL, "cat <<EOF >%s\n", fname); 394 scripting_fprintf(f, "%s|NetBSD installation generated:\\\n", 395 disktype); 396 scripting_fprintf(f, "\t:nc#%d:nt#%d:ns#%d:\\\n", 397 parts->l.d_ncylinders, parts->l.d_ntracks, parts->l.d_nsectors); 398 scripting_fprintf(f, "\t:sc#%d:su#%" PRIu32 ":\\\n", 399 parts->l.d_secpercyl, lp[RAW_PART].p_offset+lp[RAW_PART].p_size); 400 scripting_fprintf(f, "\t:se#%d:\\\n", parts->l.d_secsize); 401 402 for (i = 0; i < parts->l.d_npartitions; i++) { 403 scripting_fprintf(f, "\t:p%c#%" PRIu32 ":o%c#%" PRIu32 404 ":t%c=%s:", 'a'+i, (uint32_t)lp[i].p_size, 405 'a'+i, (uint32_t)lp[i].p_offset, 'a'+i, 406 getfslabelname(lp[i].p_fstype, 0)); 407 if (lp[i].p_fstype == FS_BSDLFS || 408 lp[i].p_fstype == FS_BSDFFS) 409 scripting_fprintf (f, "b%c#%" PRIu32 ":f%c#%" PRIu32 410 ":", 'a'+i, 411 (uint32_t)(lp[i].p_fsize * 412 lp[i].p_frag), 413 'a'+i, (uint32_t)lp[i].p_fsize); 414 415 if (i < parts->l.d_npartitions - 1) 416 scripting_fprintf(f, "\\\n"); 417 else 418 scripting_fprintf(f, "\n"); 419 } 420 scripting_fprintf(NULL, "EOF\n"); 421 422 fclose(f); 423 424 /* 425 * Label a disk using an MD-specific string DISKLABEL_CMD for 426 * to invoke disklabel. 427 * if MD code does not define DISKLABEL_CMD, this is a no-op. 428 * 429 * i386 port uses "/sbin/disklabel -w -r", just like i386 430 * miniroot scripts, though this may leave a bogus incore label. 431 * 432 * Sun ports should use DISKLABEL_CMD "/sbin/disklabel -w" 433 * to get incore to ondisk inode translation for the Sun proms. 434 */ 435#ifdef DISKLABEL_CMD 436 /* disklabel the disk */ 437 rv = run_program(0, "%s -f %s %s '%s' '%s'", 438 DISKLABEL_CMD, fname, disk, disktype, packname); 439#endif 440 441 unlink(fname); 442 443 return rv == 0; 444} 445 446static bool 447disklabel_delete_all(struct disk_partitions *arg) 448{ 449 struct disklabel_disk_partitions *parts = 450 (struct disklabel_disk_partitions*)arg; 451 daddr_t total_size = parts->l.d_partitions[RAW_PART].p_size; 452 453 memset(&parts->l.d_partitions, 0, sizeof(parts->l.d_partitions)); 454 parts->dp.num_part = 0; 455 456#if RAW_PART > 2 457 if (parts->dp.parent != NULL) { 458 parts->l.d_partitions[RAW_PART-1].p_fstype = FS_UNUSED; 459 parts->l.d_partitions[RAW_PART-1].p_offset = 460 parts->dp.disk_start; 461 parts->l.d_partitions[RAW_PART-1].p_size = parts->dp.disk_size; 462 parts->dp.num_part++; 463 } 464#endif 465 parts->l.d_partitions[RAW_PART].p_fstype = FS_UNUSED; 466 parts->l.d_partitions[RAW_PART].p_offset = 0; 467 parts->l.d_partitions[RAW_PART].p_size = total_size; 468 parts->dp.num_part++; 469 470 parts->l.d_npartitions = RAW_PART+1; 471 return true; 472} 473 474static bool 475disklabel_delete(struct disk_partitions *arg, part_id id, 476 const char **err_msg) 477{ 478 struct disklabel_disk_partitions *parts = 479 (struct disklabel_disk_partitions*)arg; 480 part_id ndx; 481 482 ndx = 0; 483 for (int part = 0; part < parts->l.d_npartitions; part++) { 484 if (parts->l.d_partitions[part].p_fstype == FS_UNUSED 485 && parts->l.d_partitions[part].p_size == 0) 486 continue; 487 488 if (ndx == id) { 489 if (part == RAW_PART 490#if RAW_PART > 2 491 || (part == RAW_PART-1 && 492 parts->dp.parent != NULL) 493#endif 494 ) { 495 if (err_msg) 496 *err_msg = msg_string( 497 MSG_part_not_deletable); 498 return false; 499 } 500 parts->l.d_partitions[part].p_size = 0; 501 parts->l.d_partitions[part].p_offset = 0; 502 parts->l.d_partitions[part].p_fstype = FS_UNUSED; 503 parts->dp.num_part--; 504 return true; 505 } 506 ndx++; 507 } 508 509 if (err_msg) 510 *err_msg = INTERNAL_ERROR; 511 return false; 512} 513 514static bool 515disklabel_delete_range(struct disk_partitions *arg, daddr_t r_start, 516 daddr_t r_size) 517{ 518 struct disklabel_disk_partitions *parts = 519 (struct disklabel_disk_partitions*)arg; 520 521 for (int part = 0; part < parts->l.d_npartitions; part++) { 522 if (parts->l.d_partitions[part].p_fstype == FS_UNUSED 523 && parts->l.d_partitions[part].p_size == 0) 524 continue; 525 526 if (part == RAW_PART) 527 continue; 528 529 daddr_t start = parts->l.d_partitions[part].p_offset; 530 daddr_t end = start + parts->l.d_partitions[part].p_size; 531 532#if RAW_PART > 2 533 if (parts->dp.parent != NULL && 534 part == RAW_PART - 1 && start == r_start && 535 r_start + r_size == end) 536 continue; 537#endif 538 539 if ((start >= r_start && start <= r_start+r_size) || 540 (end >= r_start && end <= r_start+r_size)) { 541 if (parts->dp.num_part > 1) 542 parts->dp.num_part--; 543 parts->dp.free_space += 544 parts->l.d_partitions[part].p_size; 545 parts->l.d_partitions[part].p_fstype = FS_UNUSED; 546 parts->l.d_partitions[part].p_size = 0; 547 } 548 } 549 550 return true; 551} 552 553static void 554dl_init_types(void) 555{ 556 for (size_t i = 0; i < __arraycount(dl_types); i++) { 557 if (fstypenames[i] == NULL) 558 break; 559 dl_types[i].short_desc = 560 dl_types[i].description = getfslabelname(i, 0); 561 enum part_type pt; 562 switch (i) { 563 case FS_UNUSED: pt = PT_undef; break; 564 case FS_BSDFFS: 565 case FS_RAID: 566 case FS_BSDLFS: 567 case FS_CGD: 568 pt = PT_root; break; 569 case FS_SWAP: pt = PT_swap; break; 570 case FS_MSDOS: pt = PT_FAT; break; 571 default: pt = PT_unknown; break; 572 } 573 dl_types[i].generic_ptype = pt; 574 } 575} 576 577static uint8_t 578dl_part_type_from_generic(const struct part_type_desc *gent) 579{ 580 581 if (dl_types[0].description == NULL) 582 dl_init_types(); 583 for (size_t i = 0; i < __arraycount(dl_types); i++) 584 if (gent == &dl_types[i]) 585 return (uint8_t)i; 586 587 for (size_t i = 0; i < dl_custom_ptype_count; i++) 588 if (gent == &dl_custom_ptypes[i].desc) 589 return dl_custom_ptypes[i].type; 590 591 return 0; 592} 593 594static size_t 595disklabel_type_count(void) 596{ 597 return __arraycount(dl_types) + dl_custom_ptype_count; 598} 599 600static const struct part_type_desc * 601disklabel_get_type(size_t ndx) 602{ 603 if (dl_types[0].description == NULL) 604 dl_init_types(); 605 606 if (ndx < __arraycount(dl_types)) 607 return &dl_types[ndx]; 608 609 ndx -= __arraycount(dl_types); 610 if (ndx >= dl_custom_ptype_count) 611 return NULL; 612 613 return &dl_custom_ptypes[ndx].desc; 614} 615 616static const struct part_type_desc * 617disklabel_find_type(uint type, bool create_if_unknown) 618{ 619 if (dl_types[0].description == NULL) 620 dl_init_types(); 621 622 if (type < __arraycount(dl_types)) 623 return &dl_types[type]; 624 625 for (size_t i = 0; i < dl_custom_ptype_count; i++) 626 if (dl_custom_ptypes[i].type == type) 627 return &dl_custom_ptypes[i].desc; 628 629 if (create_if_unknown) { 630 struct dl_custom_ptype *nt; 631 632 nt = realloc(dl_custom_ptypes, dl_custom_ptype_count+1); 633 if (nt == NULL) 634 return NULL; 635 dl_custom_ptypes = nt; 636 nt = dl_custom_ptypes + dl_custom_ptype_count; 637 dl_custom_ptype_count++; 638 memset(nt, 0, sizeof(*nt)); 639 nt->type = type; 640 snprintf(nt->short_desc, sizeof(nt->short_desc), "%u", type); 641 nt->short_desc[sizeof(nt->short_desc)-1] = 0; 642 snprintf(nt->description, sizeof(nt->description), 643 "%s (%u)", msg_string(MSG_custom_type), type); 644 nt->description[sizeof(nt->description)-1] = 0; 645 nt->desc.generic_ptype = PT_unknown; 646 nt->desc.short_desc = nt->short_desc; 647 nt->desc.description = nt->description; 648 return &nt->desc; 649 } 650 651 return NULL; 652} 653 654static const struct part_type_desc * 655disklabel_create_custom_part_type(const char *custom, const char **err_msg) 656{ 657 char *endp; 658 unsigned long fstype; 659 660 fstype = strtoul(custom, &endp, 10); 661 if (*endp != 0) { 662 if (err_msg) 663 *err_msg = msg_string(MSG_dl_type_invalid); 664 return NULL; 665 } 666 667 return disklabel_find_type(fstype, true); 668} 669 670static const struct part_type_desc * 671disklabel_get_fs_part_type(enum part_type pt, unsigned fstype, unsigned subtype) 672{ 673 return disklabel_find_type(fstype, false); 674} 675 676static const struct part_type_desc * 677disklabel_create_unknown_part_type(void) 678{ 679 return disklabel_find_type(FS_OTHER, false); 680} 681 682static const struct part_type_desc * 683disklabel_get_generic_type(enum part_type pt) 684{ 685 size_t nt; 686 687 if (dl_types[0].description == NULL) 688 dl_init_types(); 689 690 switch (pt) { 691 case PT_root: nt = FS_BSDFFS; break; 692 case PT_swap: nt = FS_SWAP; break; 693 case PT_FAT: 694 case PT_EFI_SYSTEM: 695 nt = FS_MSDOS; break; 696 default: nt = FS_UNUSED; break; 697 } 698 699 return disklabel_get_type(nt); 700} 701 702static bool 703disklabel_get_default_fstype(const struct part_type_desc *nat_type, 704 unsigned *fstype, unsigned *fs_sub_type) 705{ 706 707 *fstype = dl_part_type_from_generic(nat_type); 708#ifdef DEFAULT_UFS2 709 if (*fstype == FS_BSDFFS) 710 *fs_sub_type = 2; 711 else 712#endif 713 *fs_sub_type = 0; 714 return true; 715} 716 717static bool 718disklabel_get_part_info(const struct disk_partitions *arg, part_id id, 719 struct disk_part_info *info) 720{ 721 const struct disklabel_disk_partitions *parts = 722 (const struct disklabel_disk_partitions*)arg; 723 part_id ndx; 724 725 if (dl_types[0].description == NULL) 726 dl_init_types(); 727 728 ndx = 0; 729 for (int part = 0; part < parts->l.d_npartitions; part++) { 730 if (parts->l.d_partitions[part].p_fstype == FS_UNUSED 731 && parts->l.d_partitions[part].p_size == 0) 732 continue; 733 734 if (ndx == id) { 735 memset(info, 0, sizeof(*info)); 736 info->start = parts->l.d_partitions[part].p_offset; 737 info->size = parts->l.d_partitions[part].p_size; 738 info->nat_type = disklabel_find_type( 739 parts->l.d_partitions[part].p_fstype, true); 740 if (parts->last_mounted[part][0] != 0) 741 info->last_mounted = parts->last_mounted[part]; 742 info->fs_type = parts->l.d_partitions[part].p_fstype; 743 info->fs_sub_type = parts->fs_sub_type[part]; 744 if (part == RAW_PART && 745 parts->l.d_partitions[part].p_fstype == FS_UNUSED) 746 info->flags |= 747 PTI_PSCHEME_INTERNAL|PTI_RAW_PART; 748#if RAW_PART > 2 749 if (part == (RAW_PART-1) && parts->dp.parent != NULL && 750 parts->l.d_partitions[part].p_fstype == FS_UNUSED) 751 info->flags |= 752 PTI_PSCHEME_INTERNAL|PTI_WHOLE_DISK; 753#endif 754 return true; 755 } 756 757 ndx++; 758 if (ndx > parts->dp.num_part || ndx > id) 759 break; 760 } 761 762 return false; 763} 764 765static bool 766disklabel_set_part_info(struct disk_partitions *arg, part_id id, 767 const struct disk_part_info *info, const char **err_msg) 768{ 769 struct disklabel_disk_partitions *parts = 770 (struct disklabel_disk_partitions*)arg; 771 part_id ndx; 772 773 if (dl_types[0].description == NULL) 774 dl_init_types(); 775 776 ndx = 0; 777 for (int part = 0; part < parts->l.d_npartitions; part++) { 778 if (parts->l.d_partitions[part].p_fstype == FS_UNUSED 779 && parts->l.d_partitions[part].p_size == 0) 780 continue; 781 782 if (ndx == id) { 783 parts->l.d_partitions[part].p_offset = info->start; 784 parts->l.d_partitions[part].p_size = info->size; 785 parts->l.d_partitions[part].p_fstype = 786 dl_part_type_from_generic(info->nat_type); 787 if (info->last_mounted != NULL && 788 info->last_mounted != parts->last_mounted[part]) 789 strlcpy(parts->last_mounted[part], 790 info->last_mounted, 791 sizeof(parts->last_mounted[part])); 792 assert(info->fs_type == 0 || info->fs_type == 793 parts->l.d_partitions[part].p_fstype); 794 if (info->fs_sub_type != 0) 795 parts->fs_sub_type[part] = info->fs_sub_type; 796 return true; 797 } 798 799 ndx++; 800 if (ndx > parts->dp.num_part || ndx > id) 801 break; 802 } 803 804 return false; 805} 806 807static size_t 808disklabel_get_free_spaces_internal(const struct 809 disklabel_disk_partitions *parts, 810 struct disk_part_free_space *result, size_t max_num_result, 811 daddr_t min_space_size, daddr_t align, daddr_t start, daddr_t ignore) 812{ 813 size_t cnt = 0, i; 814 daddr_t s, e, from, size, end_of_disk; 815 816 if (start < parts->dp.disk_start) 817 start = parts->dp.disk_start; 818 if (min_space_size < 1) 819 min_space_size = 1; 820 if (align > 1 && (start % align) != 0) 821 start = max(roundup(start, align), align); 822 end_of_disk = parts->dp.disk_start + parts->dp.disk_size; 823 from = start; 824 while (from < end_of_disk && cnt < max_num_result) { 825again: 826 size = parts->dp.disk_start + parts->dp.disk_size - from; 827 start = from; 828 for (i = 0; i < parts->l.d_npartitions; i++) { 829 if (i == RAW_PART) 830 continue; 831 if (parts->l.d_partitions[i].p_fstype == FS_UNUSED) 832 continue; 833 if (parts->l.d_partitions[i].p_size == 0) 834 continue; 835 836 s = parts->l.d_partitions[i].p_offset; 837 e = parts->l.d_partitions[i].p_size + s; 838 if (s == ignore) 839 continue; 840 if (e < from) 841 continue; 842 if (s <= from && e > from) { 843 if (e - 1 >= end_of_disk) 844 return cnt; 845 846 from = e + 1; 847 if (align > 1) { 848 from = max(roundup(from, align), align); 849 if (from >= end_of_disk) { 850 size = 0; 851 break; 852 } 853 } 854 goto again; 855 } 856 if (s > from && s - from < size) { 857 size = s - from; 858 } 859 } 860 if (size >= min_space_size) { 861 result->start = start; 862 result->size = size; 863 result++; 864 cnt++; 865 } 866 from += size + 1; 867 if (align > 1) 868 from = max(roundup(from, align), align); 869 } 870 871 return cnt; 872} 873 874static bool 875disklabel_can_add_partition(const struct disk_partitions *arg) 876{ 877 const struct disklabel_disk_partitions *parts = 878 (const struct disklabel_disk_partitions*)arg; 879 struct disk_part_free_space space; 880 int i; 881 882 if (dl_maxpart == 0) 883 dl_maxpart = getmaxpartitions(); 884 if (parts->dp.free_space < parts->ptn_alignment) 885 return false; 886 if (parts->dp.num_part >= dl_maxpart) 887 return false; 888 if (disklabel_get_free_spaces_internal(parts, &space, 1, 889 parts->ptn_alignment, parts->ptn_alignment, 0, -1) < 1) 890 return false; 891 892 for (i = 0; i < parts->l.d_npartitions; i++) { 893 if (i == RAW_PART) 894 continue; 895#if RAW_PART > 2 896 if (i == RAW_PART-1 && parts->dp.parent != NULL) 897 continue; 898#endif 899 if (parts->l.d_partitions[i].p_fstype == FS_UNUSED) 900 return true; 901 } 902 return false; 903} 904 905static bool 906disklabel_get_disk_pack_name(const struct disk_partitions *arg, 907 char *buf, size_t len) 908{ 909 const struct disklabel_disk_partitions *parts = 910 (const struct disklabel_disk_partitions*)arg; 911 912 strlcpy(buf, parts->l.d_packname, min(len, 913 sizeof(parts->l.d_packname)+1)); 914 return true; 915} 916 917static bool 918disklabel_set_disk_pack_name(struct disk_partitions *arg, const char *pack) 919{ 920 struct disklabel_disk_partitions *parts = 921 (struct disklabel_disk_partitions*)arg; 922 923 strncpy(parts->l.d_packname, pack, sizeof(parts->l.d_packname)); 924 return true; 925} 926 927static bool 928disklabel_get_part_device(const struct disk_partitions *arg, 929 part_id ptn, char *devname, size_t max_devname_len, int *part, 930 enum dev_name_usage which_name, bool with_path, bool life) 931{ 932 const struct disklabel_disk_partitions *parts = 933 (const struct disklabel_disk_partitions*)arg; 934 part_id id; 935 int part_index; 936 char pname; 937 938 if (ptn >= parts->l.d_npartitions) 939 return false; 940 941 for (id = part_index = 0; part_index < parts->l.d_npartitions; 942 part_index++) { 943 if (parts->l.d_partitions[part_index].p_fstype == FS_UNUSED && 944 parts->l.d_partitions[part_index].p_size == 0) 945 continue; 946 if (id == ptn) 947 break; 948 id++; 949 if (id > ptn) 950 return false; 951 } 952 953 if (part != 0) 954 *part = part_index; 955 956 pname = 'a'+ part_index; 957 958 switch (which_name) { 959 case parent_device_only: 960 strlcpy(devname, arg->disk, max_devname_len); 961 return true; 962 case logical_name: 963 case plain_name: 964 if (with_path) 965 snprintf(devname, max_devname_len, _PATH_DEV "%s%c", 966 arg->disk, pname); 967 else 968 snprintf(devname, max_devname_len, "%s%c", 969 arg->disk, pname); 970 return true; 971 case raw_dev_name: 972 if (with_path) 973 snprintf(devname, max_devname_len, _PATH_DEV "r%s%c", 974 arg->disk, pname); 975 else 976 snprintf(devname, max_devname_len, "r%s%c", 977 arg->disk, pname); 978 return true; 979 } 980 981 return false; 982} 983 984/* 985 * If the requested partition file system type internally skips 986 * the disk label sector, we can allow it to start at the beginning 987 * of the disk. In most cases though we have to move the partition 988 * to start past the label sector. 989 */ 990static bool 991need_to_skip_past_label(const struct disk_part_info *info) 992{ 993 switch (info->fs_type) { 994 case FS_BSDFFS: 995 case FS_RAID: 996 return false; 997 } 998 999 return true; 1000} 1001 1002static part_id 1003disklabel_add_partition(struct disk_partitions *arg, 1004 const struct disk_part_info *info, const char **err_msg) 1005{ 1006 struct disklabel_disk_partitions *parts = 1007 (struct disklabel_disk_partitions*)arg; 1008 int i, part = -1; 1009 part_id new_id; 1010 struct disk_part_free_space space; 1011 struct disk_part_info data = *info; 1012 1013 if (disklabel_get_free_spaces_internal(parts, &space, 1, 1, 1, 1014 info->start, -1) < 1) { 1015 if (err_msg) 1016 *err_msg = msg_string(MSG_No_free_space); 1017 return NO_PART; 1018 } 1019 if (space.start <= (parts->dp.disk_start + LABELSECTOR) && 1020 need_to_skip_past_label(info)) { 1021 daddr_t new_start = roundup(parts->dp.disk_start + LABELSECTOR, 1022 parts->ptn_alignment); 1023 daddr_t off = new_start - space.start; 1024 space.start += off; 1025 space.size -= off; 1026 } 1027 if (data.size > space.size) 1028 data.size = space.size; 1029 daddr_t dend = data.start+data.size; 1030 if (space.start > data.start) 1031 data.start = space.start; 1032 if (space.start + space.size < dend) 1033 data.size = space.start+space.size-data.start; 1034 1035 if (dl_maxpart == 0) 1036 dl_maxpart = getmaxpartitions(); 1037 1038 for (new_id = 0, i = 0; i < parts->l.d_npartitions; i++) { 1039 if (parts->l.d_partitions[i].p_size > 0) 1040 new_id++; 1041 if (info->nat_type->generic_ptype != PT_root && 1042 info->nat_type->generic_ptype != PT_swap && i < RAW_PART) 1043 continue; 1044 if (i == 0 && info->nat_type->generic_ptype != PT_root) 1045 continue; 1046 if (i == 1 && info->nat_type->generic_ptype != PT_swap) 1047 continue; 1048 if (i == RAW_PART) 1049 continue; 1050#if RAW_PART > 2 1051 if (i == RAW_PART-1 && parts->dp.parent != NULL) 1052 continue; 1053#endif 1054 if (parts->l.d_partitions[i].p_size > 0) 1055 continue; 1056 part = i; 1057 break; 1058 } 1059 1060 if (part < 0) { 1061 if (parts->l.d_npartitions >= dl_maxpart) { 1062 if (err_msg) 1063 *err_msg = 1064 msg_string(MSG_err_too_many_partitions); 1065 return NO_PART; 1066 } 1067 1068 part = parts->l.d_npartitions++; 1069 } 1070 parts->l.d_partitions[part].p_offset = data.start; 1071 parts->l.d_partitions[part].p_size = data.size; 1072 parts->l.d_partitions[part].p_fstype = 1073 dl_part_type_from_generic(info->nat_type); 1074 if (info->last_mounted && info->last_mounted[0]) 1075 strlcpy(parts->last_mounted[part], info->last_mounted, 1076 sizeof(parts->last_mounted[part])); 1077 else 1078 parts->last_mounted[part][0] = 0; 1079 parts->fs_sub_type[part] = info->fs_sub_type; 1080 parts->dp.num_part++; 1081 if (data.size <= parts->dp.free_space) 1082 parts->dp.free_space -= data.size; 1083 else 1084 parts->dp.free_space = 0; 1085 1086 return new_id; 1087} 1088 1089static part_id 1090disklabel_add_outer_partition(struct disk_partitions *arg, 1091 const struct disk_part_info *info, const char **err_msg) 1092{ 1093 struct disklabel_disk_partitions *parts = 1094 (struct disklabel_disk_partitions*)arg; 1095 int i, part = -1; 1096 part_id new_id; 1097 1098 if (dl_maxpart == 0) 1099 dl_maxpart = getmaxpartitions(); 1100 1101 for (new_id = 0, i = 0; i < parts->l.d_npartitions; i++) { 1102 if (parts->l.d_partitions[i].p_size > 0) 1103 new_id++; 1104 if (info->nat_type->generic_ptype != PT_root && 1105 info->nat_type->generic_ptype != PT_swap && i < RAW_PART) 1106 continue; 1107 if (i == 0 && info->nat_type->generic_ptype != PT_root) 1108 continue; 1109 if (i == 1 && info->nat_type->generic_ptype != PT_swap) 1110 continue; 1111 if (i == RAW_PART) 1112 continue; 1113#if RAW_PART > 2 1114 if (i == RAW_PART-1 && parts->dp.parent != NULL) 1115 continue; 1116#endif 1117 if (parts->l.d_partitions[i].p_size > 0) 1118 continue; 1119 part = i; 1120 break; 1121 } 1122 1123 if (part < 0) { 1124 if (parts->l.d_npartitions >= dl_maxpart) { 1125 if (err_msg) 1126 *err_msg = 1127 msg_string(MSG_err_too_many_partitions); 1128 return NO_PART; 1129 } 1130 1131 part = parts->l.d_npartitions++; 1132 } 1133 parts->l.d_partitions[part].p_offset = info->start; 1134 parts->l.d_partitions[part].p_size = info->size; 1135 parts->l.d_partitions[part].p_fstype = 1136 dl_part_type_from_generic(info->nat_type); 1137 if (info->last_mounted && info->last_mounted[0]) 1138 strlcpy(parts->last_mounted[part], info->last_mounted, 1139 sizeof(parts->last_mounted[part])); 1140 else 1141 parts->last_mounted[part][0] = 0; 1142 parts->fs_sub_type[part] = info->fs_sub_type; 1143 parts->dp.num_part++; 1144 1145 return new_id; 1146} 1147 1148static size_t 1149disklabel_get_free_spaces(const struct disk_partitions *arg, 1150 struct disk_part_free_space *result, size_t max_num_result, 1151 daddr_t min_space_size, daddr_t align, daddr_t start, daddr_t ignore) 1152{ 1153 const struct disklabel_disk_partitions *parts = 1154 (const struct disklabel_disk_partitions*)arg; 1155 1156 return disklabel_get_free_spaces_internal(parts, result, 1157 max_num_result, min_space_size, align, start, ignore); 1158} 1159 1160static daddr_t 1161disklabel_max_free_space_at(const struct disk_partitions *arg, daddr_t start) 1162{ 1163 const struct disklabel_disk_partitions *parts = 1164 (const struct disklabel_disk_partitions*)arg; 1165 struct disk_part_free_space space; 1166 1167 if (disklabel_get_free_spaces_internal(parts, &space, 1, 1, 0, 1168 start, start) == 1) 1169 return space.size; 1170 1171 return 0; 1172} 1173 1174static daddr_t 1175disklabel_get_alignment(const struct disk_partitions *arg) 1176{ 1177 const struct disklabel_disk_partitions *parts = 1178 (const struct disklabel_disk_partitions*)arg; 1179 1180 return parts->ptn_alignment; 1181} 1182 1183static part_id 1184disklabel_find_by_name(struct disk_partitions *arg, const char *name) 1185{ 1186 const struct disklabel_disk_partitions *parts = 1187 (const struct disklabel_disk_partitions*)arg; 1188 char *sl, part; 1189 ptrdiff_t n; 1190 part_id pno, id, i; 1191 1192 sl = strrchr(name, '/'); 1193 if (sl == NULL) 1194 return NO_PART; 1195 n = sl - name; 1196 if (strncmp(name, parts->l.d_packname, n) != 0) 1197 return NO_PART; 1198 part = name[n+1]; 1199 if (part < 'a') 1200 return NO_PART; 1201 pno = part - 'a'; 1202 if (pno >= parts->l.d_npartitions) 1203 return NO_PART; 1204 if (parts->l.d_partitions[pno].p_fstype == FS_UNUSED) 1205 return NO_PART; 1206 for (id = 0, i = 0; i < pno; i++) 1207 if (parts->l.d_partitions[i].p_fstype != FS_UNUSED || 1208 parts->l.d_partitions[i].p_size != 0) 1209 id++; 1210 return id; 1211} 1212 1213static void 1214disklabel_free(struct disk_partitions *arg) 1215{ 1216 1217 assert(arg != NULL); 1218 free(__UNCONST(arg->disk)); 1219 free(arg); 1220} 1221 1222const struct disk_partitioning_scheme 1223disklabel_parts = { 1224 .name = MSG_parttype_disklabel, 1225 .short_name = MSG_parttype_disklabel_short, 1226 .new_type_prompt = MSG_dl_get_custom_fstype, 1227 .size_limit = (daddr_t)UINT32_MAX, 1228 .write_to_disk = disklabel_write_to_disk, 1229 .read_from_disk = disklabel_parts_read, 1230 .create_new_for_disk = disklabel_parts_new, 1231#ifdef NO_DISKLABEL_BOOT 1232 .have_boot_support = disklabel_non_bootable, 1233#endif 1234 .change_disk_geom = disklabel_change_geom, 1235 .get_cylinder_size = disklabel_cylinder_size, 1236 .find_by_name = disklabel_find_by_name, 1237 .get_disk_pack_name = disklabel_get_disk_pack_name, 1238 .set_disk_pack_name = disklabel_set_disk_pack_name, 1239 .delete_all_partitions = disklabel_delete_all, 1240 .delete_partitions_in_range = disklabel_delete_range, 1241 .delete_partition = disklabel_delete, 1242 .get_part_types_count = disklabel_type_count, 1243 .get_part_type = disklabel_get_type, 1244 .get_generic_part_type = disklabel_get_generic_type, 1245 .get_fs_part_type = disklabel_get_fs_part_type, 1246 .get_default_fstype = disklabel_get_default_fstype, 1247 .create_custom_part_type = disklabel_create_custom_part_type, 1248 .create_unknown_part_type = disklabel_create_unknown_part_type, 1249 .get_part_alignment = disklabel_get_alignment, 1250 .adapt_foreign_part_info = generic_adapt_foreign_part_info, 1251 .get_part_info = disklabel_get_part_info, 1252 .can_add_partition = disklabel_can_add_partition, 1253 .set_part_info = disklabel_set_part_info, 1254 .add_partition = disklabel_add_partition, 1255 .add_outer_partition = disklabel_add_outer_partition, 1256 .max_free_space_at = disklabel_max_free_space_at, 1257 .get_free_spaces = disklabel_get_free_spaces, 1258 .get_part_device = disklabel_get_part_device, 1259 .free = disklabel_free, 1260}; 1261