disklabel.c revision 1.5
1/* $NetBSD: disklabel.c,v 1.5 2019/06/25 07:14:45 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 disklabel_init_default_alignment(parts, nhead * nsec); 103 104 parts->l.d_ncylinders = ncyl; 105 parts->l.d_ntracks = nhead; 106 parts->l.d_nsectors = nsec; 107 parts->l.d_secsize = DEV_BSIZE; 108 parts->l.d_secpercyl = nsec * nhead; 109 110 if (ncyl*nhead*nsec <= TINY_DISK_SIZE) 111 set_default_sizemult(1); 112 else 113 set_default_sizemult(MEG/512); 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 = 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{ 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 } 249 } 250 251 if (parts->l.d_partitions[part].p_size > parts->dp.free_space) 252 parts->dp.free_space = 0; 253 else 254 parts->dp.free_space -= 255 parts->l.d_partitions[part].p_size; 256 } 257 close(fd); 258 259 return &parts->dp; 260} 261 262static bool 263disklabel_write_to_disk(struct disk_partitions *arg) 264{ 265 struct disklabel_disk_partitions *parts = 266 (struct disklabel_disk_partitions*)arg; 267 FILE *f; 268 char fname[PATH_MAX], packname[sizeof(parts->l.d_packname)+1]; 269 int i, rv = 0; 270 const char *disk = parts->dp.disk, *s; 271 const struct partition *lp; 272 char *d; 273 size_t n; 274 275 assert(parts->l.d_secsize != 0); 276 277 sprintf(fname, "/tmp/disklabel.%u", getpid()); 278 f = fopen(fname, "w"); 279 if (f == NULL) 280 return false; 281 282 /* make sure we have a 0 terminated packname */ 283 strlcpy(packname, parts->l.d_packname, sizeof packname); 284 285 /* fill typename with disk name prefix, if not already set */ 286 if (strlen(parts->l.d_typename) == 0) { 287 for (n = 0, d = parts->l.d_typename, s = disk; 288 *s && n < sizeof(parts->l.d_typename); d++, s++, n++) { 289 if (isdigit((unsigned char)*s)) 290 break; 291 *d = *s; 292 } 293 } 294 parts->l.d_typename[sizeof(parts->l.d_typename)-1] = 0; 295 296 /* we need a valid disk type name, so enforce an arbitrary if 297 * above did not yield a usable one */ 298 if (strlen(parts->l.d_typename) == 0) 299 strncpy(parts->l.d_typename, "SCSI", 300 sizeof(parts->l.d_typename)); 301 302 lp = parts->l.d_partitions; 303 fprintf(f, "%s|NetBSD installation generated:\\\n", 304 parts->l.d_typename); 305 fprintf(f, "\t:nc#%d:nt#%d:ns#%d:\\\n", 306 parts->l.d_ncylinders, parts->l.d_ntracks, parts->l.d_nsectors); 307 fprintf(f, "\t:sc#%d:su#%" PRIu32 ":\\\n", 308 parts->l.d_secpercyl, lp[RAW_PART].p_offset+lp[RAW_PART].p_size); 309 fprintf(f, "\t:se#%d:\\\n", parts->l.d_secsize); 310 311 for (i = 0; i < parts->l.d_npartitions; i++) { 312 fprintf(f, "\t:p%c#%" PRIu32 ":o%c#%" PRIu32 313 ":t%c=%s:", 'a'+i, (uint32_t)lp[i].p_size, 314 'a'+i, (uint32_t)lp[i].p_offset, 'a'+i, 315 getfslabelname(lp[i].p_fstype, 0)); 316 if (lp[i].p_fstype == FS_BSDLFS || 317 lp[i].p_fstype == FS_BSDFFS) 318 fprintf (f, "b%c#%" PRIu32 ":f%c#%" PRIu32 319 ":", 'a'+i, 320 (uint32_t)(lp[i].p_fsize * 321 lp[i].p_frag), 322 'a'+i, (uint32_t)lp[i].p_fsize); 323 324 if (i < parts->l.d_npartitions - 1) 325 fprintf(f, "\\\n"); 326 else 327 fprintf(f, "\n"); 328 } 329 330 fclose(f); 331 332 /* 333 * Label a disk using an MD-specific string DISKLABEL_CMD for 334 * to invoke disklabel. 335 * if MD code does not define DISKLABEL_CMD, this is a no-op. 336 * 337 * i386 port uses "/sbin/disklabel -w -r", just like i386 338 * miniroot scripts, though this may leave a bogus incore label. 339 * 340 * Sun ports should use DISKLABEL_CMD "/sbin/disklabel -w" 341 * to get incore to ondisk inode translation for the Sun proms. 342 */ 343#ifdef DISKLABEL_CMD 344 /* disklabel the disk */ 345 rv = run_program(RUN_DISPLAY, "%s -f %s %s %s %s", 346 DISKLABEL_CMD, fname, disk, parts->l.d_typename, packname); 347#endif 348 349 unlink(fname); 350 351 return rv == 0; 352} 353 354static bool 355disklabel_delete_all(struct disk_partitions *arg) 356{ 357 struct disklabel_disk_partitions *parts = 358 (struct disklabel_disk_partitions*)arg; 359 daddr_t total_size = parts->l.d_partitions[RAW_PART].p_size; 360 361 memset(&parts->l, 0, sizeof(parts->l)); 362 parts->dp.num_part = 0; 363 364#if RAW_PART > 2 365 parts->l.d_partitions[RAW_PART-1].p_fstype = FS_UNUSED; 366 parts->l.d_partitions[RAW_PART-1].p_offset = parts->dp.disk_start; 367 parts->l.d_partitions[RAW_PART-1].p_size = parts->dp.disk_size; 368 parts->dp.num_part++; 369#endif 370 parts->l.d_partitions[RAW_PART].p_fstype = FS_UNUSED; 371 parts->l.d_partitions[RAW_PART].p_offset = 0; 372 parts->l.d_partitions[RAW_PART].p_size = total_size; 373 parts->dp.num_part++; 374 375 parts->l.d_npartitions = RAW_PART+1; 376 return true; 377} 378 379static bool 380disklabel_delete(struct disk_partitions *arg, part_id id, 381 const char **err_msg) 382{ 383 struct disklabel_disk_partitions *parts = 384 (struct disklabel_disk_partitions*)arg; 385 part_id ndx; 386 387 ndx = 0; 388 for (int part = 0; part < parts->l.d_npartitions; part++) { 389 if (parts->l.d_partitions[part].p_fstype == FS_UNUSED 390 && parts->l.d_partitions[part].p_size == 0) 391 continue; 392 393 if (ndx == id) { 394 if (part == RAW_PART 395#if RAW_PART > 2 396 || part == RAW_PART-1 397#endif 398 ) { 399 if (err_msg) 400 *err_msg = msg_string( 401 MSG_part_not_deletable); 402 return false; 403 } 404 parts->l.d_partitions[part].p_size = 0; 405 parts->l.d_partitions[part].p_offset = 0; 406 parts->l.d_partitions[part].p_fstype = FS_UNUSED; 407 parts->dp.num_part--; 408 return true; 409 } 410 ndx++; 411 } 412 413 if (err_msg) 414 *err_msg = INTERNAL_ERROR; 415 return false; 416} 417 418static bool 419disklabel_delete_range(struct disk_partitions *arg, daddr_t r_start, 420 daddr_t r_size) 421{ 422 struct disklabel_disk_partitions *parts = 423 (struct disklabel_disk_partitions*)arg; 424 425 for (int part = 0; part < parts->l.d_npartitions; part++) { 426 if (parts->l.d_partitions[part].p_fstype == FS_UNUSED 427 && parts->l.d_partitions[part].p_size == 0) 428 continue; 429 430 if (part == RAW_PART) 431 continue; 432 433 daddr_t start = parts->l.d_partitions[part].p_offset; 434 daddr_t end = start + parts->l.d_partitions[part].p_size; 435 436#if RAW_PART > 2 437 if (part == RAW_PART - 1 && start == r_start && 438 r_start + r_size == end) 439 continue; 440#endif 441 442 if ((start >= r_start && start <= r_start+r_size) || 443 (end >= r_start && end <= r_start+r_size)) { 444 if (parts->dp.num_part > 1) 445 parts->dp.num_part--; 446 parts->dp.free_space += 447 parts->l.d_partitions[part].p_size; 448 parts->l.d_partitions[part].p_fstype = FS_UNUSED; 449 parts->l.d_partitions[part].p_size = 0; 450 } 451 } 452 453 return true; 454} 455 456static void 457dl_init_types(void) 458{ 459 for (size_t i = 0; i < __arraycount(dl_types); i++) { 460 if (fstypenames[i] == NULL) 461 break; 462 dl_types[i].short_desc = 463 dl_types[i].description = getfslabelname(i, 0); 464 enum part_type pt; 465 switch (i) { 466 case FS_UNUSED: pt = PT_undef; break; 467 case FS_BSDFFS: pt = PT_root; break; 468 case FS_SWAP: pt = PT_swap; break; 469 case FS_MSDOS: pt = PT_FAT; break; 470 default: pt = PT_unknown; break; 471 } 472 dl_types[i].generic_ptype = pt; 473 } 474} 475 476static uint8_t 477dl_part_type_from_generic(const struct part_type_desc *gent) 478{ 479 480 if (dl_types[0].description == NULL) 481 dl_init_types(); 482 for (size_t i = 0; i < __arraycount(dl_types); i++) 483 if (gent == &dl_types[i]) 484 return (uint8_t)i; 485 486 for (size_t i = 0; i < dl_custom_ptype_count; i++) 487 if (gent == &dl_custom_ptypes[i].desc) 488 return dl_custom_ptypes[i].type; 489 490 return 0; 491} 492 493static size_t 494disklabel_type_count(void) 495{ 496 return __arraycount(dl_types) + dl_custom_ptype_count; 497} 498 499static const struct part_type_desc * 500disklabel_get_type(size_t ndx) 501{ 502 if (dl_types[0].description == NULL) 503 dl_init_types(); 504 505 if (ndx < __arraycount(dl_types)) 506 return &dl_types[ndx]; 507 508 ndx -= __arraycount(dl_types); 509 if (ndx >= dl_custom_ptype_count) 510 return NULL; 511 512 return &dl_custom_ptypes[ndx].desc; 513} 514 515static const struct part_type_desc * 516disklabel_find_type(uint type, bool create_if_unknown) 517{ 518 if (dl_types[0].description == NULL) 519 dl_init_types(); 520 521 if (type < __arraycount(dl_types)) 522 return &dl_types[type]; 523 524 for (size_t i = 0; i < dl_custom_ptype_count; i++) 525 if (dl_custom_ptypes[i].type == type) 526 return &dl_custom_ptypes[i].desc; 527 528 if (create_if_unknown) { 529 struct dl_custom_ptype *nt; 530 531 nt = realloc(dl_custom_ptypes, dl_custom_ptype_count+1); 532 if (nt == NULL) 533 return NULL; 534 dl_custom_ptypes = nt; 535 nt = dl_custom_ptypes + dl_custom_ptype_count; 536 dl_custom_ptype_count++; 537 memset(nt, 0, sizeof(*nt)); 538 nt->type = type; 539 snprintf(nt->short_desc, sizeof(nt->short_desc), "%u", type); 540 nt->short_desc[sizeof(nt->short_desc)-1] = 0; 541 snprintf(nt->description, sizeof(nt->description), 542 "%s (%u)", msg_string(MSG_custom_type), type); 543 nt->description[sizeof(nt->description)-1] = 0; 544 nt->desc.generic_ptype = PT_unknown; 545 nt->desc.short_desc = nt->short_desc; 546 nt->desc.description = nt->description; 547 return &nt->desc; 548 } 549 550 return NULL; 551} 552 553static const struct part_type_desc * 554disklabel_create_custom_part_type(const char *custom, const char **err_msg) 555{ 556 char *endp; 557 unsigned long fstype; 558 559 fstype = strtoul(custom, &endp, 10); 560 if (*endp != 0) { 561 if (err_msg) 562 *err_msg = msg_string(MSG_dl_type_invalid); 563 return NULL; 564 } 565 566 return disklabel_find_type(fstype, true); 567} 568 569static const struct part_type_desc * 570disklabel_get_fs_part_type(unsigned fstype, unsigned subtype) 571{ 572 return disklabel_find_type(fstype, false); 573} 574 575static const struct part_type_desc * 576disklabel_get_generic_type(enum part_type pt) 577{ 578 size_t nt; 579 580 if (dl_types[0].description == NULL) 581 dl_init_types(); 582 583 switch (pt) { 584 case PT_root: nt = FS_BSDFFS; break; 585 case PT_swap: nt = FS_SWAP; break; 586 case PT_FAT: 587 case PT_EFI_SYSTEM: 588 nt = FS_MSDOS; break; 589 default: nt = FS_UNUSED; break; 590 } 591 592 return disklabel_get_type(nt); 593} 594 595static bool 596disklabel_get_part_info(const struct disk_partitions *arg, part_id id, 597 struct disk_part_info *info) 598{ 599 const struct disklabel_disk_partitions *parts = 600 (const struct disklabel_disk_partitions*)arg; 601 part_id ndx; 602 603 if (dl_types[0].description == NULL) 604 dl_init_types(); 605 606 ndx = 0; 607 for (int part = 0; part < parts->l.d_npartitions; part++) { 608 if (parts->l.d_partitions[part].p_fstype == FS_UNUSED 609 && parts->l.d_partitions[part].p_size == 0) 610 continue; 611 612 if (ndx == id) { 613 memset(info, 0, sizeof(*info)); 614 info->start = parts->l.d_partitions[part].p_offset; 615 info->size = parts->l.d_partitions[part].p_size; 616 info->nat_type = disklabel_find_type( 617 parts->l.d_partitions[part].p_fstype, true); 618 if (parts->last_mounted[part][0] != 0) 619 info->last_mounted = parts->last_mounted[part]; 620 info->fs_type = parts->l.d_partitions[part].p_fstype; 621 info->fs_sub_type = parts->fs_sub_type[part]; 622 if (part == RAW_PART && 623 parts->l.d_partitions[part].p_fstype == FS_UNUSED) 624 info->flags |= 625 PTI_PSCHEME_INTERNAL|PTI_RAW_PART; 626#if RAW_PART > 2 627 if (part == (RAW_PART-1) && 628 parts->l.d_partitions[part].p_fstype == FS_UNUSED) 629 info->flags |= 630 PTI_PSCHEME_INTERNAL|PTI_WHOLE_DISK; 631#endif 632 return true; 633 } 634 635 ndx++; 636 if (ndx > parts->dp.num_part || ndx > id) 637 break; 638 } 639 640 return false; 641} 642 643static bool 644disklabel_set_part_info(struct disk_partitions *arg, part_id id, 645 const struct disk_part_info *info, const char **err_msg) 646{ 647 struct disklabel_disk_partitions *parts = 648 (struct disklabel_disk_partitions*)arg; 649 part_id ndx; 650 651 if (dl_types[0].description == NULL) 652 dl_init_types(); 653 654 ndx = 0; 655 for (int part = 0; part < parts->l.d_npartitions; part++) { 656 if (parts->l.d_partitions[part].p_fstype == FS_UNUSED 657 && parts->l.d_partitions[part].p_size == 0) 658 continue; 659 660 if (ndx == id) { 661 parts->l.d_partitions[part].p_offset = info->start; 662 parts->l.d_partitions[part].p_size = info->size; 663 parts->l.d_partitions[part].p_fstype = 664 dl_part_type_from_generic(info->nat_type); 665 if (info->last_mounted != NULL && 666 info->last_mounted != parts->last_mounted[part]) 667 strlcpy(parts->last_mounted[part], 668 info->last_mounted, 669 sizeof(parts->last_mounted[part])); 670 assert(info->fs_type == 0 || info->fs_type == 671 parts->l.d_partitions[part].p_fstype); 672 if (info->fs_sub_type != 0) 673 parts->fs_sub_type[part] = info->fs_sub_type; 674 return true; 675 } 676 677 ndx++; 678 if (ndx > parts->dp.num_part || ndx > id) 679 break; 680 } 681 682 return false; 683} 684 685static size_t 686disklabel_get_free_spaces_internal(const struct 687 disklabel_disk_partitions *parts, 688 struct disk_part_free_space *result, size_t max_num_result, 689 daddr_t min_space_size, daddr_t align, daddr_t start, daddr_t ignore) 690{ 691 size_t cnt = 0, i; 692 daddr_t s, e, from, size, end_of_disk; 693 694 if (start < parts->dp.disk_start) 695 start = parts->dp.disk_start; 696 if (min_space_size < 1) 697 min_space_size = 1; 698 if (align > 1 && (start % align) != 0) 699 start = max(roundup(start, align), align); 700 end_of_disk = parts->dp.disk_start + parts->dp.disk_size; 701 from = start; 702 while (from < end_of_disk && cnt < max_num_result) { 703again: 704 size = parts->dp.disk_start + parts->dp.disk_size - from; 705 start = from; 706 for (i = 0; i < parts->l.d_npartitions; i++) { 707 if (i == RAW_PART) 708 continue; 709 if (parts->l.d_partitions[i].p_fstype == FS_UNUSED) 710 continue; 711 712 s = parts->l.d_partitions[i].p_offset; 713 e = parts->l.d_partitions[i].p_size + s; 714 if (s == ignore) 715 continue; 716 if (e < from) 717 continue; 718 if (s <= from && e > from) { 719 if (e - 1 >= end_of_disk) 720 return cnt; 721 722 from = e + 1; 723 if (align > 1) { 724 from = max(roundup(from, align), align); 725 if (from >= end_of_disk) { 726 size = 0; 727 break; 728 } 729 } 730 goto again; 731 } 732 if (s > from && s - from < size) { 733 size = s - from; 734 } 735 } 736 if (size >= min_space_size) { 737 result->start = start; 738 result->size = size; 739 result++; 740 cnt++; 741 } 742 from += size + 1; 743 if (align > 1) 744 from = max(roundup(from, align), align); 745 } 746 747 return cnt; 748} 749 750static bool 751disklabel_can_add_partition(const struct disk_partitions *arg) 752{ 753 const struct disklabel_disk_partitions *parts = 754 (const struct disklabel_disk_partitions*)arg; 755 struct disk_part_free_space space; 756 int i; 757 758 if (dl_maxpart == 0) 759 dl_maxpart = getmaxpartitions(); 760 if (parts->dp.free_space < parts->ptn_alignment) 761 return false; 762 if (parts->dp.num_part >= dl_maxpart) 763 return false; 764 if (disklabel_get_free_spaces_internal(parts, &space, 1, 765 parts->ptn_alignment, parts->ptn_alignment, 0, -1) < 1) 766 return false; 767 768 for (i = 0; i < parts->l.d_npartitions; i++) { 769 if (i == RAW_PART) 770 continue; 771#if RAW_PART > 2 772 if (i == RAW_PART-1) 773 continue; 774#endif 775 if (parts->l.d_partitions[i].p_fstype == FS_UNUSED) 776 return true; 777 } 778 return false; 779} 780 781static bool 782disklabel_get_disk_pack_name(const struct disk_partitions *arg, 783 char *buf, size_t len) 784{ 785 const struct disklabel_disk_partitions *parts = 786 (const struct disklabel_disk_partitions*)arg; 787 788 strlcpy(buf, parts->l.d_packname, min(len, 789 sizeof(parts->l.d_packname)+1)); 790 return true; 791} 792 793static bool 794disklabel_set_disk_pack_name(struct disk_partitions *arg, const char *pack) 795{ 796 struct disklabel_disk_partitions *parts = 797 (struct disklabel_disk_partitions*)arg; 798 799 strncpy(parts->l.d_packname, pack, sizeof(parts->l.d_packname)); 800 return true; 801} 802 803static bool 804disklabel_get_part_device(const struct disk_partitions *arg, 805 part_id ptn, char *devname, size_t max_devname_len, int *part, 806 enum dev_name_usage which_name, bool with_path) 807{ 808 809 if (part != 0) 810 *part = ptn; 811 812 switch (which_name) { 813 case parent_device_only: 814 strlcpy(devname, arg->disk, max_devname_len); 815 return true; 816 case logical_name: 817 case plain_name: 818 if (with_path) 819 snprintf(devname, max_devname_len, _PATH_DEV "%s%c", 820 arg->disk, (char)ptn + 'a'); 821 else 822 snprintf(devname, max_devname_len, "%s%c", 823 arg->disk, (char)ptn + 'a'); 824 return true; 825 case raw_dev_name: 826 if (with_path) 827 snprintf(devname, max_devname_len, _PATH_DEV "r%s%c", 828 arg->disk, (char)ptn + 'a'); 829 else 830 snprintf(devname, max_devname_len, "r%s%c", 831 arg->disk, (char)ptn + 'a'); 832 return true; 833 } 834 835 return false; 836} 837 838static part_id 839disklabel_add_partition(struct disk_partitions *arg, 840 const struct disk_part_info *info, const char **err_msg) 841{ 842 struct disklabel_disk_partitions *parts = 843 (struct disklabel_disk_partitions*)arg; 844 int i, part = -1; 845 part_id new_id; 846 struct disk_part_free_space space; 847 struct disk_part_info data = *info; 848 849 if (disklabel_get_free_spaces_internal(parts, &space, 1, 1, 1, 850 info->start, -1) < 1) { 851 if (err_msg) 852 *err_msg = msg_string(MSG_No_free_space); 853 return NO_PART; 854 } 855 if (data.size > space.size) 856 data.size = space.size; 857 daddr_t dend = data.start+data.size; 858 if (space.start > data.start) 859 data.start = space.start; 860 if (space.start + space.size < dend) 861 data.size = space.start+space.size-data.start; 862 863 if (dl_maxpart == 0) 864 dl_maxpart = getmaxpartitions(); 865 866 for (new_id = 0, i = 0; i < parts->l.d_npartitions; i++) { 867 if (parts->l.d_partitions[i].p_size > 0) 868 new_id++; 869 if (info->nat_type->generic_ptype != PT_root && 870 info->nat_type->generic_ptype != PT_swap && i < RAW_PART) 871 continue; 872 if (i == 0 && info->nat_type->generic_ptype != PT_root) 873 continue; 874 if (i == 1 && info->nat_type->generic_ptype != PT_swap) 875 continue; 876 if (i == RAW_PART) 877 continue; 878#if RAW_PART > 2 879 if (i == RAW_PART-1) 880 continue; 881#endif 882 if (parts->l.d_partitions[i].p_size > 0) 883 continue; 884 part = i; 885 break; 886 } 887 888 if (part < 0) { 889 if (parts->l.d_npartitions >= dl_maxpart) { 890 if (err_msg) 891 *err_msg = 892 msg_string(MSG_err_too_many_partitions); 893 return NO_PART; 894 } 895 896 part = parts->l.d_npartitions++; 897 } 898 parts->l.d_partitions[part].p_offset = data.start; 899 parts->l.d_partitions[part].p_size = data.size; 900 parts->l.d_partitions[part].p_fstype = 901 dl_part_type_from_generic(info->nat_type); 902 if (info->last_mounted && info->last_mounted[0]) 903 strlcpy(parts->last_mounted[part], info->last_mounted, 904 sizeof(parts->last_mounted[part])); 905 else 906 parts->last_mounted[part][0] = 0; 907 parts->fs_sub_type[part] = info->fs_sub_type; 908 parts->dp.num_part++; 909 if (data.size <= parts->dp.free_space) 910 parts->dp.free_space -= data.size; 911 else 912 parts->dp.free_space = 0; 913 914 return new_id; 915} 916 917static size_t 918disklabel_get_free_spaces(const struct disk_partitions *arg, 919 struct disk_part_free_space *result, size_t max_num_result, 920 daddr_t min_space_size, daddr_t align, daddr_t start, daddr_t ignore) 921{ 922 const struct disklabel_disk_partitions *parts = 923 (const struct disklabel_disk_partitions*)arg; 924 925 return disklabel_get_free_spaces_internal(parts, result, 926 max_num_result, min_space_size, align, start, ignore); 927} 928 929static daddr_t 930disklabel_max_free_space_at(const struct disk_partitions *arg, daddr_t start) 931{ 932 const struct disklabel_disk_partitions *parts = 933 (const struct disklabel_disk_partitions*)arg; 934 struct disk_part_free_space space; 935 936 if (disklabel_get_free_spaces_internal(parts, &space, 1, 1, 0, 937 start, start) == 1) 938 return space.size; 939 940 return 0; 941} 942 943static daddr_t 944disklabel_get_alignment(const struct disk_partitions *arg) 945{ 946 const struct disklabel_disk_partitions *parts = 947 (const struct disklabel_disk_partitions*)arg; 948 949 return parts->ptn_alignment; 950} 951 952static void 953disklabel_free(struct disk_partitions *arg) 954{ 955 956 assert(arg != NULL); 957 free(arg); 958} 959 960const struct disk_partitioning_scheme 961disklabel_parts = { 962 .name = MSG_parttype_disklabel, 963 .short_name = MSG_parttype_disklabel_short, 964 .new_type_prompt = MSG_dl_get_custom_fstype, 965 .size_limit = (daddr_t)UINT32_MAX, 966 .write_to_disk = disklabel_write_to_disk, 967 .read_from_disk = disklabel_parts_read, 968 .create_new_for_disk = disklabel_parts_new, 969 .change_disk_geom = disklabel_change_geom, 970 .get_disk_pack_name = disklabel_get_disk_pack_name, 971 .set_disk_pack_name = disklabel_set_disk_pack_name, 972 .delete_all_partitions = disklabel_delete_all, 973 .delete_partitions_in_range = disklabel_delete_range, 974 .delete_partition = disklabel_delete, 975 .get_part_types_count = disklabel_type_count, 976 .get_part_type = disklabel_get_type, 977 .get_generic_part_type = disklabel_get_generic_type, 978 .get_fs_part_type = disklabel_get_fs_part_type, 979 .create_custom_part_type = disklabel_create_custom_part_type, 980 .get_part_alignment = disklabel_get_alignment, 981 .get_part_info = disklabel_get_part_info, 982 .can_add_partition = disklabel_can_add_partition, 983 .set_part_info = disklabel_set_part_info, 984 .add_partition = disklabel_add_partition, 985 .max_free_space_at = disklabel_max_free_space_at, 986 .get_free_spaces = disklabel_get_free_spaces, 987 .get_part_device = disklabel_get_part_device, 988 .free = disklabel_free, 989}; 990