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