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