disklabel.c revision 1.39
1/* $NetBSD: disklabel.c,v 1.39 2020/09/29 15:29:17 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 == 3 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 == 3 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 == 3 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 == 3 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 == 3 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 case FS_EX2FS: pt = PT_EXT2; break; 586 case FS_SYSVBFS: 587 pt = PT_SYSVBFS; break; 588 default: pt = PT_unknown; break; 589 } 590 dl_types[i].generic_ptype = pt; 591 } 592} 593 594static uint8_t 595dl_part_type_from_generic(const struct part_type_desc *gent) 596{ 597 598 if (dl_types[0].description == NULL) 599 dl_init_types(); 600 for (size_t i = 0; i < __arraycount(dl_types); i++) 601 if (gent == &dl_types[i]) 602 return (uint8_t)i; 603 604 for (size_t i = 0; i < dl_custom_ptype_count; i++) 605 if (gent == &dl_custom_ptypes[i].desc) 606 return dl_custom_ptypes[i].type; 607 608 return 0; 609} 610 611static size_t 612disklabel_type_count(void) 613{ 614 return __arraycount(dl_types) + dl_custom_ptype_count; 615} 616 617static const struct part_type_desc * 618disklabel_get_type(size_t ndx) 619{ 620 if (dl_types[0].description == NULL) 621 dl_init_types(); 622 623 if (ndx < __arraycount(dl_types)) 624 return &dl_types[ndx]; 625 626 ndx -= __arraycount(dl_types); 627 if (ndx >= dl_custom_ptype_count) 628 return NULL; 629 630 return &dl_custom_ptypes[ndx].desc; 631} 632 633static const struct part_type_desc * 634disklabel_find_type(uint type, bool create_if_unknown) 635{ 636 if (dl_types[0].description == NULL) 637 dl_init_types(); 638 639 if (type < __arraycount(dl_types)) 640 return &dl_types[type]; 641 642 for (size_t i = 0; i < dl_custom_ptype_count; i++) 643 if (dl_custom_ptypes[i].type == type) 644 return &dl_custom_ptypes[i].desc; 645 646 if (create_if_unknown) { 647 struct dl_custom_ptype *nt; 648 649 nt = realloc(dl_custom_ptypes, dl_custom_ptype_count+1); 650 if (nt == NULL) 651 return NULL; 652 dl_custom_ptypes = nt; 653 nt = dl_custom_ptypes + dl_custom_ptype_count; 654 dl_custom_ptype_count++; 655 memset(nt, 0, sizeof(*nt)); 656 nt->type = type; 657 snprintf(nt->short_desc, sizeof(nt->short_desc), "%u", type); 658 nt->short_desc[sizeof(nt->short_desc)-1] = 0; 659 snprintf(nt->description, sizeof(nt->description), 660 "%s (%u)", msg_string(MSG_custom_type), type); 661 nt->description[sizeof(nt->description)-1] = 0; 662 nt->desc.generic_ptype = PT_unknown; 663 nt->desc.short_desc = nt->short_desc; 664 nt->desc.description = nt->description; 665 return &nt->desc; 666 } 667 668 return NULL; 669} 670 671static const struct part_type_desc * 672disklabel_create_custom_part_type(const char *custom, const char **err_msg) 673{ 674 char *endp; 675 unsigned long fstype; 676 677 fstype = strtoul(custom, &endp, 10); 678 if (*endp != 0) { 679 if (err_msg) 680 *err_msg = msg_string(MSG_dl_type_invalid); 681 return NULL; 682 } 683 684 return disklabel_find_type(fstype, true); 685} 686 687static const struct part_type_desc * 688disklabel_get_fs_part_type(enum part_type pt, unsigned fstype, unsigned subtype) 689{ 690 return disklabel_find_type(fstype, false); 691} 692 693static const struct part_type_desc * 694disklabel_create_unknown_part_type(void) 695{ 696 return disklabel_find_type(FS_OTHER, false); 697} 698 699static const struct part_type_desc * 700disklabel_get_generic_type(enum part_type pt) 701{ 702 size_t nt; 703 704 if (dl_types[0].description == NULL) 705 dl_init_types(); 706 707 switch (pt) { 708 case PT_root: nt = FS_BSDFFS; break; 709 case PT_swap: nt = FS_SWAP; break; 710 case PT_FAT: 711 case PT_EFI_SYSTEM: 712 nt = FS_MSDOS; break; 713 case PT_EXT2: nt = FS_EX2FS; break; 714 case PT_SYSVBFS: 715 nt = FS_SYSVBFS; break; 716 default: nt = FS_UNUSED; break; 717 } 718 719 return disklabel_get_type(nt); 720} 721 722static bool 723disklabel_get_default_fstype(const struct part_type_desc *nat_type, 724 unsigned *fstype, unsigned *fs_sub_type) 725{ 726 727 *fstype = dl_part_type_from_generic(nat_type); 728#ifdef DEFAULT_UFS2 729 if (*fstype == FS_BSDFFS) 730 *fs_sub_type = 2; 731 else 732#endif 733 *fs_sub_type = 0; 734 return true; 735} 736 737static bool 738disklabel_get_part_info(const struct disk_partitions *arg, part_id id, 739 struct disk_part_info *info) 740{ 741 const struct disklabel_disk_partitions *parts = 742 (const struct disklabel_disk_partitions*)arg; 743 part_id ndx; 744 745 if (dl_types[0].description == NULL) 746 dl_init_types(); 747 748 ndx = 0; 749 for (int part = 0; part < parts->l.d_npartitions; part++) { 750 if (parts->l.d_partitions[part].p_fstype == FS_UNUSED 751 && parts->l.d_partitions[part].p_size == 0) 752 continue; 753 754 if (ndx == id) { 755 memset(info, 0, sizeof(*info)); 756 info->start = parts->l.d_partitions[part].p_offset; 757 info->size = parts->l.d_partitions[part].p_size; 758 info->nat_type = disklabel_find_type( 759 parts->l.d_partitions[part].p_fstype, true); 760 if (parts->last_mounted[part][0] != 0) 761 info->last_mounted = parts->last_mounted[part]; 762 info->fs_type = parts->l.d_partitions[part].p_fstype; 763 info->fs_sub_type = parts->fs_sub_type[part]; 764 if (part == RAW_PART && 765 parts->l.d_partitions[part].p_fstype == FS_UNUSED) 766 info->flags |= 767 PTI_PSCHEME_INTERNAL|PTI_RAW_PART; 768#if RAW_PART == 3 769 if (part == (RAW_PART-1) && parts->dp.parent != NULL && 770 parts->l.d_partitions[part].p_fstype == FS_UNUSED) 771 info->flags |= 772 PTI_PSCHEME_INTERNAL|PTI_WHOLE_DISK; 773#endif 774 return true; 775 } 776 777 ndx++; 778 if (ndx > parts->dp.num_part || ndx > id) 779 break; 780 } 781 782 return false; 783} 784 785static bool 786disklabel_set_part_info(struct disk_partitions *arg, part_id id, 787 const struct disk_part_info *info, const char **err_msg) 788{ 789 struct disklabel_disk_partitions *parts = 790 (struct disklabel_disk_partitions*)arg; 791 part_id ndx; 792 793 if (dl_types[0].description == NULL) 794 dl_init_types(); 795 796 ndx = 0; 797 for (int part = 0; part < parts->l.d_npartitions; part++) { 798 if (parts->l.d_partitions[part].p_fstype == FS_UNUSED 799 && parts->l.d_partitions[part].p_size == 0) 800 continue; 801 802 if (ndx == id) { 803 parts->l.d_partitions[part].p_offset = info->start; 804 parts->l.d_partitions[part].p_size = info->size; 805 parts->l.d_partitions[part].p_fstype = 806 dl_part_type_from_generic(info->nat_type); 807 if (info->last_mounted != NULL && 808 info->last_mounted != parts->last_mounted[part]) 809 strlcpy(parts->last_mounted[part], 810 info->last_mounted, 811 sizeof(parts->last_mounted[part])); 812 assert(info->fs_type == 0 || info->fs_type == 813 parts->l.d_partitions[part].p_fstype); 814 if (info->fs_sub_type != 0) 815 parts->fs_sub_type[part] = info->fs_sub_type; 816 return true; 817 } 818 819 ndx++; 820 if (ndx > parts->dp.num_part || ndx > id) 821 break; 822 } 823 824 return false; 825} 826 827static size_t 828disklabel_get_free_spaces_internal(const struct 829 disklabel_disk_partitions *parts, 830 struct disk_part_free_space *result, size_t max_num_result, 831 daddr_t min_space_size, daddr_t align, daddr_t start, daddr_t ignore) 832{ 833 size_t cnt = 0, i; 834 daddr_t s, e, from, size, end_of_disk; 835 836 if (start < parts->dp.disk_start) 837 start = parts->dp.disk_start; 838 if (min_space_size < 1) 839 min_space_size = 1; 840 if (align > 1 && (start % align) != 0) 841 start = max(roundup(start, align), align); 842 end_of_disk = parts->dp.disk_start + parts->dp.disk_size; 843 from = start; 844 while (from < end_of_disk && cnt < max_num_result) { 845again: 846 size = parts->dp.disk_start + parts->dp.disk_size - from; 847 start = from; 848 for (i = 0; i < parts->l.d_npartitions; i++) { 849 if (i == RAW_PART) 850 continue; 851 if (parts->l.d_partitions[i].p_fstype == FS_UNUSED) 852 continue; 853 if (parts->l.d_partitions[i].p_size == 0) 854 continue; 855 856 s = parts->l.d_partitions[i].p_offset; 857 e = parts->l.d_partitions[i].p_size + s; 858 if (s == ignore) 859 continue; 860 if (e < from) 861 continue; 862 if (s <= from && e > from) { 863 if (e - 1 >= end_of_disk) 864 return cnt; 865 866 from = e + 1; 867 if (align > 1) { 868 from = max(roundup(from, align), align); 869 if (from >= end_of_disk) { 870 size = 0; 871 break; 872 } 873 } 874 goto again; 875 } 876 if (s > from && s - from < size) { 877 size = s - from; 878 } 879 } 880 if (size >= min_space_size) { 881 result->start = start; 882 result->size = size; 883 result++; 884 cnt++; 885 } 886 from += size + 1; 887 if (align > 1) 888 from = max(roundup(from, align), align); 889 } 890 891 return cnt; 892} 893 894static bool 895disklabel_can_add_partition(const struct disk_partitions *arg) 896{ 897 const struct disklabel_disk_partitions *parts = 898 (const struct disklabel_disk_partitions*)arg; 899 struct disk_part_free_space space; 900 int i; 901 902 if (dl_maxpart == 0) 903 dl_maxpart = getmaxpartitions(); 904 if (parts->dp.free_space < parts->ptn_alignment) 905 return false; 906 if (parts->dp.num_part >= dl_maxpart) 907 return false; 908 if (disklabel_get_free_spaces_internal(parts, &space, 1, 909 parts->ptn_alignment, parts->ptn_alignment, 0, -1) < 1) 910 return false; 911 912 for (i = 0; i < parts->l.d_npartitions; i++) { 913 if (i == RAW_PART) 914 continue; 915#if RAW_PART == 3 916 if (i == RAW_PART-1 && parts->dp.parent != NULL) 917 continue; 918#endif 919 if (parts->l.d_partitions[i].p_fstype == FS_UNUSED) 920 return true; 921 } 922 return false; 923} 924 925static bool 926disklabel_get_disk_pack_name(const struct disk_partitions *arg, 927 char *buf, size_t len) 928{ 929 const struct disklabel_disk_partitions *parts = 930 (const struct disklabel_disk_partitions*)arg; 931 932 strlcpy(buf, parts->l.d_packname, min(len, 933 sizeof(parts->l.d_packname)+1)); 934 return true; 935} 936 937static bool 938disklabel_set_disk_pack_name(struct disk_partitions *arg, const char *pack) 939{ 940 struct disklabel_disk_partitions *parts = 941 (struct disklabel_disk_partitions*)arg; 942 943 strncpy(parts->l.d_packname, pack, sizeof(parts->l.d_packname)); 944 return true; 945} 946 947static bool 948disklabel_get_part_device(const struct disk_partitions *arg, 949 part_id ptn, char *devname, size_t max_devname_len, int *part, 950 enum dev_name_usage which_name, bool with_path, bool life) 951{ 952 const struct disklabel_disk_partitions *parts = 953 (const struct disklabel_disk_partitions*)arg; 954 part_id id; 955 int part_index; 956 char pname; 957 958 if (ptn >= parts->l.d_npartitions) 959 return false; 960 961 for (id = part_index = 0; part_index < parts->l.d_npartitions; 962 part_index++) { 963 if (parts->l.d_partitions[part_index].p_fstype == FS_UNUSED && 964 parts->l.d_partitions[part_index].p_size == 0) 965 continue; 966 if (id == ptn) 967 break; 968 id++; 969 if (id > ptn) 970 return false; 971 } 972 973 if (part != 0) 974 *part = part_index; 975 976 pname = 'a'+ part_index; 977 978 switch (which_name) { 979 case parent_device_only: 980 strlcpy(devname, arg->disk, max_devname_len); 981 return true; 982 case logical_name: 983 case plain_name: 984 if (with_path) 985 snprintf(devname, max_devname_len, _PATH_DEV "%s%c", 986 arg->disk, pname); 987 else 988 snprintf(devname, max_devname_len, "%s%c", 989 arg->disk, pname); 990 return true; 991 case raw_dev_name: 992 if (with_path) 993 snprintf(devname, max_devname_len, _PATH_DEV "r%s%c", 994 arg->disk, pname); 995 else 996 snprintf(devname, max_devname_len, "r%s%c", 997 arg->disk, pname); 998 return true; 999 } 1000 1001 return false; 1002} 1003 1004/* 1005 * If the requested partition file system type internally skips 1006 * the disk label sector, we can allow it to start at the beginning 1007 * of the disk. In most cases though we have to move the partition 1008 * to start past the label sector. 1009 */ 1010static bool 1011need_to_skip_past_label(const struct disk_part_info *info) 1012{ 1013 switch (info->fs_type) { 1014 case FS_BSDFFS: 1015 case FS_RAID: 1016 return false; 1017 } 1018 1019 return true; 1020} 1021 1022static part_id 1023disklabel_add_partition(struct disk_partitions *arg, 1024 const struct disk_part_info *info, const char **err_msg) 1025{ 1026 struct disklabel_disk_partitions *parts = 1027 (struct disklabel_disk_partitions*)arg; 1028 int i, part = -1; 1029 part_id new_id; 1030 struct disk_part_free_space space; 1031 struct disk_part_info data = *info; 1032 1033 if (disklabel_get_free_spaces_internal(parts, &space, 1, 1, 1, 1034 data.start, -1) < 1) { 1035 if (err_msg) 1036 *err_msg = msg_string(MSG_No_free_space); 1037 return NO_PART; 1038 } 1039 if (space.start <= (parts->dp.disk_start + LABELSECTOR) && 1040 need_to_skip_past_label(&data)) { 1041 daddr_t new_start = roundup(parts->dp.disk_start + LABELSECTOR, 1042 parts->ptn_alignment); 1043 daddr_t off = new_start - space.start; 1044 space.start += off; 1045 space.size -= off; 1046 } 1047 if (data.size > space.size) 1048 data.size = space.size; 1049 daddr_t dend = data.start+data.size; 1050 if (space.start > data.start) 1051 data.start = space.start; 1052 if (space.start + space.size < dend) 1053 data.size = space.start+space.size-data.start; 1054 1055 if (dl_maxpart == 0) 1056 dl_maxpart = getmaxpartitions(); 1057 1058 for (new_id = 0, i = 0; i < parts->l.d_npartitions; i++) { 1059 if (parts->l.d_partitions[i].p_size > 0) 1060 new_id++; 1061 if (data.nat_type->generic_ptype != PT_root && 1062 data.nat_type->generic_ptype != PT_swap && i < RAW_PART) 1063 continue; 1064 if (i == 0 && data.nat_type->generic_ptype != PT_root) 1065 continue; 1066 if (i == 1 && data.nat_type->generic_ptype != PT_swap) 1067 continue; 1068 if (i == RAW_PART) 1069 continue; 1070#if RAW_PART == 3 1071 if (i == RAW_PART-1 && parts->dp.parent != NULL) 1072 continue; 1073#endif 1074 if (parts->l.d_partitions[i].p_size > 0) 1075 continue; 1076 part = i; 1077 break; 1078 } 1079 1080 if (part < 0) { 1081 if (parts->l.d_npartitions >= dl_maxpart) { 1082 if (err_msg) 1083 *err_msg = 1084 msg_string(MSG_err_too_many_partitions); 1085 return NO_PART; 1086 } 1087 1088 part = parts->l.d_npartitions++; 1089 } 1090 parts->l.d_partitions[part].p_offset = data.start; 1091 parts->l.d_partitions[part].p_size = data.size; 1092 parts->l.d_partitions[part].p_fstype = 1093 dl_part_type_from_generic(data.nat_type); 1094 if (data.last_mounted && data.last_mounted[0]) 1095 strlcpy(parts->last_mounted[part], data.last_mounted, 1096 sizeof(parts->last_mounted[part])); 1097 else 1098 parts->last_mounted[part][0] = 0; 1099 parts->fs_sub_type[part] = data.fs_sub_type; 1100 parts->dp.num_part++; 1101 if (data.size <= parts->dp.free_space) 1102 parts->dp.free_space -= data.size; 1103 else 1104 parts->dp.free_space = 0; 1105 1106 return new_id; 1107} 1108 1109static part_id 1110disklabel_add_outer_partition(struct disk_partitions *arg, 1111 const struct disk_part_info *info, const char **err_msg) 1112{ 1113 struct disklabel_disk_partitions *parts = 1114 (struct disklabel_disk_partitions*)arg; 1115 int i, part = -1; 1116 part_id new_id; 1117 1118 if (dl_maxpart == 0) 1119 dl_maxpart = getmaxpartitions(); 1120 1121 for (new_id = 0, i = 0; i < parts->l.d_npartitions; i++) { 1122 if (parts->l.d_partitions[i].p_size > 0) 1123 new_id++; 1124 if (info->nat_type->generic_ptype != PT_root && 1125 info->nat_type->generic_ptype != PT_swap && i < RAW_PART) 1126 continue; 1127 if (i == 0 && info->nat_type->generic_ptype != PT_root) 1128 continue; 1129 if (i == 1 && info->nat_type->generic_ptype != PT_swap) 1130 continue; 1131 if (i == RAW_PART) 1132 continue; 1133#if RAW_PART == 3 1134 if (i == RAW_PART-1 && parts->dp.parent != NULL) 1135 continue; 1136#endif 1137 if (parts->l.d_partitions[i].p_size > 0) 1138 continue; 1139 part = i; 1140 break; 1141 } 1142 1143 if (part < 0) { 1144 if (parts->l.d_npartitions >= dl_maxpart) { 1145 if (err_msg) 1146 *err_msg = 1147 msg_string(MSG_err_too_many_partitions); 1148 return NO_PART; 1149 } 1150 1151 part = parts->l.d_npartitions++; 1152 } 1153 parts->l.d_partitions[part].p_offset = info->start; 1154 parts->l.d_partitions[part].p_size = info->size; 1155 parts->l.d_partitions[part].p_fstype = 1156 dl_part_type_from_generic(info->nat_type); 1157 if (info->last_mounted && info->last_mounted[0]) 1158 strlcpy(parts->last_mounted[part], info->last_mounted, 1159 sizeof(parts->last_mounted[part])); 1160 else 1161 parts->last_mounted[part][0] = 0; 1162 parts->fs_sub_type[part] = info->fs_sub_type; 1163 parts->dp.num_part++; 1164 1165 return new_id; 1166} 1167 1168static size_t 1169disklabel_get_free_spaces(const struct disk_partitions *arg, 1170 struct disk_part_free_space *result, size_t max_num_result, 1171 daddr_t min_space_size, daddr_t align, daddr_t start, daddr_t ignore) 1172{ 1173 const struct disklabel_disk_partitions *parts = 1174 (const struct disklabel_disk_partitions*)arg; 1175 1176 return disklabel_get_free_spaces_internal(parts, result, 1177 max_num_result, min_space_size, align, start, ignore); 1178} 1179 1180static daddr_t 1181disklabel_max_free_space_at(const struct disk_partitions *arg, daddr_t start) 1182{ 1183 const struct disklabel_disk_partitions *parts = 1184 (const struct disklabel_disk_partitions*)arg; 1185 struct disk_part_free_space space; 1186 1187 if (disklabel_get_free_spaces_internal(parts, &space, 1, 1, 0, 1188 start, start) == 1) 1189 return space.size; 1190 1191 return 0; 1192} 1193 1194static daddr_t 1195disklabel_get_alignment(const struct disk_partitions *arg) 1196{ 1197 const struct disklabel_disk_partitions *parts = 1198 (const struct disklabel_disk_partitions*)arg; 1199 1200 return parts->ptn_alignment; 1201} 1202 1203static part_id 1204disklabel_find_by_name(struct disk_partitions *arg, const char *name) 1205{ 1206 const struct disklabel_disk_partitions *parts = 1207 (const struct disklabel_disk_partitions*)arg; 1208 char *sl, part; 1209 ptrdiff_t n; 1210 part_id pno, id, i; 1211 1212 sl = strrchr(name, '/'); 1213 if (sl == NULL) 1214 return NO_PART; 1215 n = sl - name; 1216 if (strncmp(name, parts->l.d_packname, n) != 0) 1217 return NO_PART; 1218 part = name[n+1]; 1219 if (part < 'a') 1220 return NO_PART; 1221 pno = part - 'a'; 1222 if (pno >= parts->l.d_npartitions) 1223 return NO_PART; 1224 if (parts->l.d_partitions[pno].p_fstype == FS_UNUSED) 1225 return NO_PART; 1226 for (id = 0, i = 0; i < pno; i++) 1227 if (parts->l.d_partitions[i].p_fstype != FS_UNUSED || 1228 parts->l.d_partitions[i].p_size != 0) 1229 id++; 1230 return id; 1231} 1232 1233static void 1234disklabel_free(struct disk_partitions *arg) 1235{ 1236 1237 assert(arg != NULL); 1238 free(__UNCONST(arg->disk)); 1239 free(arg); 1240} 1241 1242const struct disk_partitioning_scheme 1243disklabel_parts = { 1244 .name = MSG_parttype_disklabel, 1245 .short_name = MSG_parttype_disklabel_short, 1246 .new_type_prompt = MSG_dl_get_custom_fstype, 1247 .size_limit = (daddr_t)UINT32_MAX, 1248 .write_to_disk = disklabel_write_to_disk, 1249 .read_from_disk = disklabel_parts_read, 1250 .create_new_for_disk = disklabel_parts_new, 1251#ifdef NO_DISKLABEL_BOOT 1252 .have_boot_support = disklabel_non_bootable, 1253#endif 1254 .change_disk_geom = disklabel_change_geom, 1255 .get_cylinder_size = disklabel_cylinder_size, 1256 .find_by_name = disklabel_find_by_name, 1257 .get_disk_pack_name = disklabel_get_disk_pack_name, 1258 .set_disk_pack_name = disklabel_set_disk_pack_name, 1259 .delete_all_partitions = disklabel_delete_all, 1260 .delete_partitions_in_range = disklabel_delete_range, 1261 .delete_partition = disklabel_delete, 1262 .get_part_types_count = disklabel_type_count, 1263 .get_part_type = disklabel_get_type, 1264 .get_generic_part_type = disklabel_get_generic_type, 1265 .get_fs_part_type = disklabel_get_fs_part_type, 1266 .get_default_fstype = disklabel_get_default_fstype, 1267 .create_custom_part_type = disklabel_create_custom_part_type, 1268 .create_unknown_part_type = disklabel_create_unknown_part_type, 1269 .get_part_alignment = disklabel_get_alignment, 1270 .adapt_foreign_part_info = generic_adapt_foreign_part_info, 1271 .get_part_info = disklabel_get_part_info, 1272 .can_add_partition = disklabel_can_add_partition, 1273 .set_part_info = disklabel_set_part_info, 1274 .add_partition = disklabel_add_partition, 1275 .add_outer_partition = disklabel_add_outer_partition, 1276 .max_free_space_at = disklabel_max_free_space_at, 1277 .get_free_spaces = disklabel_get_free_spaces, 1278 .get_part_device = disklabel_get_part_device, 1279 .free = disklabel_free, 1280}; 1281