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