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