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