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