1 /* $NetBSD: disks.c,v 1.72 2021/01/31 22:45:46 rillig Exp $ */ 2 3 /* 4 * Copyright 1997 Piermont Information Systems Inc. 5 * All rights reserved. 6 * 7 * Written by Philip A. Nelson for Piermont Information Systems Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. The name of Piermont Information Systems Inc. may not be used to endorse 18 * or promote products derived from this software without specific prior 19 * written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS'' 22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE 25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 31 * THE POSSIBILITY OF SUCH DAMAGE. 32 * 33 */ 34 35 /* disks.c -- routines to deal with finding disks and labeling disks. */ 36 37 38 #include <assert.h> 39 #include <errno.h> 40 #include <inttypes.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <unistd.h> 44 #include <fcntl.h> 45 #include <fnmatch.h> 46 #include <util.h> 47 #include <uuid.h> 48 #include <paths.h> 49 #include <fstab.h> 50 51 #include <sys/param.h> 52 #include <sys/sysctl.h> 53 #include <sys/swap.h> 54 #include <sys/disklabel_gpt.h> 55 #include <ufs/ufs/dinode.h> 56 #include <ufs/ffs/fs.h> 57 58 #include <dev/scsipi/scsipi_all.h> 59 #include <sys/scsiio.h> 60 61 #include <dev/ata/atareg.h> 62 #include <sys/ataio.h> 63 64 #include "defs.h" 65 #include "md.h" 66 #include "msg_defs.h" 67 #include "menu_defs.h" 68 #include "txtwalk.h" 69 70 /* #define DEBUG_VERBOSE 1 */ 71 72 /* Disk descriptions */ 73 struct disk_desc { 74 char dd_name[SSTRSIZE]; 75 char dd_descr[256]; 76 bool dd_no_mbr, dd_no_part; 77 uint dd_cyl; 78 uint dd_head; 79 uint dd_sec; 80 uint dd_secsize; 81 daddr_t dd_totsec; 82 }; 83 84 #define NAME_PREFIX "NAME=" 85 static const char name_prefix[] = NAME_PREFIX; 86 87 /* things we could have as /sbin/newfs_* and /sbin/fsck_* */ 88 static const char *extern_fs_with_chk[] = { 89 "ext2fs", "lfs", "msdos", "v7fs" 90 }; 91 92 /* things we could have as /sbin/newfs_* but not /sbin/fsck_* */ 93 static const char *extern_fs_newfs_only[] = { 94 "sysvbfs", "udf" 95 }; 96 97 /* Local prototypes */ 98 static int found_fs(struct data *, size_t, const struct lookfor*); 99 static int found_fs_nocheck(struct data *, size_t, const struct lookfor*); 100 static int fsck_preen(const char *, const char *, bool silent); 101 static void fixsb(const char *, const char *); 102 103 104 static bool tmpfs_on_var_shm(void); 105 106 const char * 107 getfslabelname(uint f, uint f_version) 108 { 109 if (f == FS_TMPFS) 110 return "tmpfs"; 111 else if (f == FS_MFS) 112 return "mfs"; 113 else if (f == FS_BSDFFS && f_version > 0) 114 return f_version == 2 ? 115 msg_string(MSG_fs_type_ffsv2) : msg_string(MSG_fs_type_ffs); 116 else if (f == FS_EX2FS && f_version == 1) 117 return msg_string(MSG_fs_type_ext2old); 118 else if (f >= __arraycount(fstypenames) || fstypenames[f] == NULL) 119 return "invalid"; 120 return fstypenames[f]; 121 } 122 123 /* 124 * Decide wether we want to mount a tmpfs on /var/shm: we do this always 125 * when the machine has more than 16 MB of user memory. On smaller machines, 126 * shm_open() and friends will not perform well anyway. 127 */ 128 static bool 129 tmpfs_on_var_shm() 130 { 131 uint64_t ram; 132 size_t len; 133 134 len = sizeof(ram); 135 if (sysctlbyname("hw.usermem64", &ram, &len, NULL, 0)) 136 return false; 137 138 return ram > 16 * MEG; 139 } 140 141 /* from src/sbin/atactl/atactl.c 142 * extract_string: copy a block of bytes out of ataparams and make 143 * a proper string out of it, truncating trailing spaces and preserving 144 * strict typing. And also, not doing unaligned accesses. 145 */ 146 static void 147 ata_extract_string(char *buf, size_t bufmax, 148 uint8_t *bytes, unsigned numbytes, 149 int needswap) 150 { 151 unsigned i; 152 size_t j; 153 unsigned char ch1, ch2; 154 155 for (i = 0, j = 0; i < numbytes; i += 2) { 156 ch1 = bytes[i]; 157 ch2 = bytes[i+1]; 158 if (needswap && j < bufmax-1) { 159 buf[j++] = ch2; 160 } 161 if (j < bufmax-1) { 162 buf[j++] = ch1; 163 } 164 if (!needswap && j < bufmax-1) { 165 buf[j++] = ch2; 166 } 167 } 168 while (j > 0 && buf[j-1] == ' ') { 169 j--; 170 } 171 buf[j] = '\0'; 172 } 173 174 /* 175 * from src/sbin/scsictl/scsi_subr.c 176 */ 177 #define STRVIS_ISWHITE(x) ((x) == ' ' || (x) == '\0' || (x) == (u_char)'\377') 178 179 static void 180 scsi_strvis(char *sdst, size_t dlen, const char *ssrc, size_t slen) 181 { 182 u_char *dst = (u_char *)sdst; 183 const u_char *src = (const u_char *)ssrc; 184 185 /* Trim leading and trailing blanks and NULs. */ 186 while (slen > 0 && STRVIS_ISWHITE(src[0])) 187 ++src, --slen; 188 while (slen > 0 && STRVIS_ISWHITE(src[slen - 1])) 189 --slen; 190 191 while (slen > 0) { 192 if (*src < 0x20 || *src >= 0x80) { 193 /* non-printable characters */ 194 dlen -= 4; 195 if (dlen < 1) 196 break; 197 *dst++ = '\\'; 198 *dst++ = ((*src & 0300) >> 6) + '0'; 199 *dst++ = ((*src & 0070) >> 3) + '0'; 200 *dst++ = ((*src & 0007) >> 0) + '0'; 201 } else if (*src == '\\') { 202 /* quote characters */ 203 dlen -= 2; 204 if (dlen < 1) 205 break; 206 *dst++ = '\\'; 207 *dst++ = '\\'; 208 } else { 209 /* normal characters */ 210 if (--dlen < 1) 211 break; 212 *dst++ = *src; 213 } 214 ++src, --slen; 215 } 216 217 *dst++ = 0; 218 } 219 220 221 static int 222 get_descr_scsi(struct disk_desc *dd) 223 { 224 struct scsipi_inquiry_data inqbuf; 225 struct scsipi_inquiry cmd; 226 scsireq_t req; 227 /* x4 in case every character is escaped, +1 for NUL. */ 228 char vendor[(sizeof(inqbuf.vendor) * 4) + 1], 229 product[(sizeof(inqbuf.product) * 4) + 1], 230 revision[(sizeof(inqbuf.revision) * 4) + 1]; 231 char size[5]; 232 233 memset(&inqbuf, 0, sizeof(inqbuf)); 234 memset(&cmd, 0, sizeof(cmd)); 235 memset(&req, 0, sizeof(req)); 236 237 cmd.opcode = INQUIRY; 238 cmd.length = sizeof(inqbuf); 239 memcpy(req.cmd, &cmd, sizeof(cmd)); 240 req.cmdlen = sizeof(cmd); 241 req.databuf = &inqbuf; 242 req.datalen = sizeof(inqbuf); 243 req.timeout = 10000; 244 req.flags = SCCMD_READ; 245 req.senselen = SENSEBUFLEN; 246 247 if (!disk_ioctl(dd->dd_name, SCIOCCOMMAND, &req) 248 || req.retsts != SCCMD_OK) 249 return 0; 250 251 scsi_strvis(vendor, sizeof(vendor), inqbuf.vendor, 252 sizeof(inqbuf.vendor)); 253 scsi_strvis(product, sizeof(product), inqbuf.product, 254 sizeof(inqbuf.product)); 255 scsi_strvis(revision, sizeof(revision), inqbuf.revision, 256 sizeof(inqbuf.revision)); 257 258 humanize_number(size, sizeof(size), 259 (uint64_t)dd->dd_secsize * (uint64_t)dd->dd_totsec, 260 "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 261 262 snprintf(dd->dd_descr, sizeof(dd->dd_descr), 263 "%s (%s, %s %s)", 264 dd->dd_name, size, vendor, product); 265 266 return 1; 267 } 268 269 static int 270 get_descr_ata(struct disk_desc *dd) 271 { 272 struct atareq req; 273 static union { 274 unsigned char inbuf[DEV_BSIZE]; 275 struct ataparams inqbuf; 276 } inbuf; 277 struct ataparams *inqbuf = &inbuf.inqbuf; 278 char model[sizeof(inqbuf->atap_model)+1]; 279 char size[5]; 280 int needswap = 0; 281 282 memset(&inbuf, 0, sizeof(inbuf)); 283 memset(&req, 0, sizeof(req)); 284 285 req.flags = ATACMD_READ; 286 req.command = WDCC_IDENTIFY; 287 req.databuf = (void *)&inbuf; 288 req.datalen = sizeof(inbuf); 289 req.timeout = 1000; 290 291 if (!disk_ioctl(dd->dd_name, ATAIOCCOMMAND, &req) 292 || req.retsts != ATACMD_OK) 293 return 0; 294 295 #if BYTE_ORDER == LITTLE_ENDIAN 296 /* 297 * On little endian machines, we need to shuffle the string 298 * byte order. However, we don't have to do this for NEC or 299 * Mitsumi ATAPI devices 300 */ 301 302 if (!(inqbuf->atap_config != WDC_CFG_CFA_MAGIC && 303 (inqbuf->atap_config & WDC_CFG_ATAPI) && 304 ((inqbuf->atap_model[0] == 'N' && 305 inqbuf->atap_model[1] == 'E') || 306 (inqbuf->atap_model[0] == 'F' && 307 inqbuf->atap_model[1] == 'X')))) { 308 needswap = 1; 309 } 310 #endif 311 312 ata_extract_string(model, sizeof(model), 313 inqbuf->atap_model, sizeof(inqbuf->atap_model), needswap); 314 humanize_number(size, sizeof(size), 315 (uint64_t)dd->dd_secsize * (uint64_t)dd->dd_totsec, 316 "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 317 318 snprintf(dd->dd_descr, sizeof(dd->dd_descr), "%s (%s, %s)", 319 dd->dd_name, size, model); 320 321 return 1; 322 } 323 324 static void 325 get_descr(struct disk_desc *dd) 326 { 327 char size[5]; 328 dd->dd_descr[0] = '\0'; 329 330 /* try ATA */ 331 if (get_descr_ata(dd)) 332 goto done; 333 /* try SCSI */ 334 if (get_descr_scsi(dd)) 335 goto done; 336 337 /* XXX: identify for ld @ NVME or microSD */ 338 339 /* XXX: get description from raid, cgd, vnd... */ 340 done: 341 /* punt, just give some generic info */ 342 humanize_number(size, sizeof(size), 343 (uint64_t)dd->dd_secsize * (uint64_t)dd->dd_totsec, 344 "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 345 346 snprintf(dd->dd_descr, sizeof(dd->dd_descr), 347 "%s (%s)", dd->dd_name, size); 348 } 349 350 /* 351 * State for helper callback for get_default_cdrom 352 */ 353 struct default_cdrom_data { 354 char *device; 355 size_t max_len; 356 bool found; 357 }; 358 359 /* 360 * Helper function for get_default_cdrom, gets passed a device 361 * name and a void pointer to default_cdrom_data. 362 */ 363 static bool 364 get_default_cdrom_helper(void *state, const char *dev) 365 { 366 struct default_cdrom_data *data = state; 367 368 if (!is_cdrom_device(dev, false)) 369 return true; 370 371 strlcpy(data->device, dev, data->max_len); 372 strlcat(data->device, "a", data->max_len); /* default to partition a */ 373 data->found = true; 374 375 return false; /* one is enough, stop iteration */ 376 } 377 378 /* 379 * Set the argument to the name of the first CD devices actually 380 * available, leave it unmodified otherwise. 381 * Return true if a device has been found. 382 */ 383 bool 384 get_default_cdrom(char *cd, size_t max_len) 385 { 386 struct default_cdrom_data state; 387 388 state.device = cd; 389 state.max_len = max_len; 390 state.found = false; 391 392 if (enumerate_disks(&state, get_default_cdrom_helper)) 393 return state.found; 394 395 return false; 396 } 397 398 static bool 399 get_wedge_descr(struct disk_desc *dd) 400 { 401 struct dkwedge_info dkw; 402 403 if (!get_wedge_info(dd->dd_name, &dkw)) 404 return false; 405 406 snprintf(dd->dd_descr, sizeof(dd->dd_descr), "%s (%s@%s)", 407 dkw.dkw_wname, dkw.dkw_devname, dkw.dkw_parent); 408 return true; 409 } 410 411 static bool 412 get_name_and_parent(const char *dev, char *name, char *parent) 413 { 414 struct dkwedge_info dkw; 415 416 if (!get_wedge_info(dev, &dkw)) 417 return false; 418 strcpy(name, (const char *)dkw.dkw_wname); 419 strcpy(parent, dkw.dkw_parent); 420 return true; 421 } 422 423 static bool 424 find_swap_part_on(const char *dev, char *swap_name) 425 { 426 struct dkwedge_list dkwl; 427 struct dkwedge_info *dkw; 428 u_int i; 429 bool res = false; 430 431 if (!get_wedge_list(dev, &dkwl)) 432 return false; 433 434 dkw = dkwl.dkwl_buf; 435 for (i = 0; i < dkwl.dkwl_nwedges; i++) { 436 res = strcmp(dkw[i].dkw_ptype, DKW_PTYPE_SWAP) == 0; 437 if (res) { 438 strcpy(swap_name, (const char*)dkw[i].dkw_wname); 439 break; 440 } 441 } 442 free(dkwl.dkwl_buf); 443 444 return res; 445 } 446 447 static bool 448 is_ffs_wedge(const char *dev) 449 { 450 struct dkwedge_info dkw; 451 452 if (!get_wedge_info(dev, &dkw)) 453 return false; 454 455 return strcmp(dkw.dkw_ptype, DKW_PTYPE_FFS) == 0; 456 } 457 458 /* 459 * Does this device match an entry in our default CDROM device list? 460 * If looking for install targets, we also flag floopy devices. 461 */ 462 bool 463 is_cdrom_device(const char *dev, bool as_target) 464 { 465 static const char *target_devices[] = { 466 #ifdef CD_NAMES 467 CD_NAMES 468 #endif 469 #if defined(CD_NAMES) && defined(FLOPPY_NAMES) 470 , 471 #endif 472 #ifdef FLOPPY_NAMES 473 FLOPPY_NAMES 474 #endif 475 #if defined(CD_NAMES) || defined(FLOPPY_NAMES) 476 , 477 #endif 478 0 479 }; 480 static const char *src_devices[] = { 481 #ifdef CD_NAMES 482 CD_NAMES , 483 #endif 484 0 485 }; 486 487 for (const char **dev_pat = as_target ? target_devices : src_devices; 488 *dev_pat; dev_pat++) 489 if (fnmatch(*dev_pat, dev, 0) == 0) 490 return true; 491 492 return false; 493 } 494 495 /* does this device match any entry in the driver list? */ 496 static bool 497 dev_in_list(const char *dev, const char **list) 498 { 499 500 for ( ; *list; list++) { 501 502 size_t len = strlen(*list); 503 504 /* start of name matches? */ 505 if (strncmp(dev, *list, len) == 0) { 506 char *endp; 507 int e; 508 509 /* remainder of name is a decimal number? */ 510 strtou(dev+len, &endp, 10, 0, INT_MAX, &e); 511 if (endp && *endp == 0 && e == 0) 512 return true; 513 } 514 } 515 516 return false; 517 } 518 519 bool 520 is_bootable_device(const char *dev) 521 { 522 static const char *non_bootable_devs[] = { 523 "raid", /* bootcode lives outside of raid */ 524 "xbd", /* xen virtual device, can not boot from that */ 525 NULL 526 }; 527 528 return !dev_in_list(dev, non_bootable_devs); 529 } 530 531 bool 532 is_partitionable_device(const char *dev) 533 { 534 static const char *non_partitionable_devs[] = { 535 "dk", /* this is already a partitioned slice */ 536 NULL 537 }; 538 539 return !dev_in_list(dev, non_partitionable_devs); 540 } 541 542 /* 543 * Multi-purpose helper function: 544 * iterate all known disks, invoke a callback for each. 545 * Stop iteration when the callback returns false. 546 * Return true when iteration actually happend, false on error. 547 */ 548 bool 549 enumerate_disks(void *state, bool (*func)(void *state, const char *dev)) 550 { 551 static const int mib[] = { CTL_HW, HW_DISKNAMES }; 552 static const unsigned int miblen = __arraycount(mib); 553 const char *xd; 554 char *disk_names; 555 size_t len; 556 557 if (sysctl(mib, miblen, NULL, &len, NULL, 0) == -1) 558 return false; 559 560 disk_names = malloc(len); 561 if (disk_names == NULL) 562 return false; 563 564 if (sysctl(mib, miblen, disk_names, &len, NULL, 0) == -1) { 565 free(disk_names); 566 return false; 567 } 568 569 for (xd = strtok(disk_names, " "); xd != NULL; xd = strtok(NULL, " ")) { 570 if (!(*func)(state, xd)) 571 break; 572 } 573 free(disk_names); 574 575 return true; 576 } 577 578 /* 579 * Helper state for get_disks 580 */ 581 struct get_disks_state { 582 int numdisks; 583 struct disk_desc *dd; 584 bool with_non_partitionable; 585 }; 586 587 /* 588 * Helper function for get_disks enumartion 589 */ 590 static bool 591 get_disks_helper(void *arg, const char *dev) 592 { 593 struct get_disks_state *state = arg; 594 struct disk_geom geo; 595 596 /* is this a CD device? */ 597 if (is_cdrom_device(dev, true)) 598 return true; 599 600 memset(state->dd, 0, sizeof(*state->dd)); 601 strlcpy(state->dd->dd_name, dev, sizeof state->dd->dd_name - 2); 602 state->dd->dd_no_mbr = !is_bootable_device(dev); 603 state->dd->dd_no_part = !is_partitionable_device(dev); 604 605 if (state->dd->dd_no_part && !state->with_non_partitionable) 606 return true; 607 608 if (!get_disk_geom(state->dd->dd_name, &geo)) { 609 if (errno == ENOENT) 610 return true; 611 if (errno != ENOTTY || !state->dd->dd_no_part) 612 /* 613 * Allow plain partitions, 614 * like already existing wedges 615 * (like dk0) if marked as 616 * non-partitioning device. 617 * For all other cases, continue 618 * with the next disk. 619 */ 620 return true; 621 if (!is_ffs_wedge(state->dd->dd_name)) 622 return true; 623 } 624 625 /* 626 * Exclude a disk mounted as root partition, 627 * in case of install-image on a USB memstick. 628 */ 629 if (is_active_rootpart(state->dd->dd_name, 630 state->dd->dd_no_part ? -1 : 0)) 631 return true; 632 633 state->dd->dd_cyl = geo.dg_ncylinders; 634 state->dd->dd_head = geo.dg_ntracks; 635 state->dd->dd_sec = geo.dg_nsectors; 636 state->dd->dd_secsize = geo.dg_secsize; 637 state->dd->dd_totsec = geo.dg_secperunit; 638 639 if (!state->dd->dd_no_part || !get_wedge_descr(state->dd)) 640 get_descr(state->dd); 641 state->dd++; 642 state->numdisks++; 643 if (state->numdisks == MAX_DISKS) 644 return false; 645 646 return true; 647 } 648 649 /* 650 * Get all disk devices that are not CDs. 651 * Optionally leave out those that can not be partitioned further. 652 */ 653 static int 654 get_disks(struct disk_desc *dd, bool with_non_partitionable) 655 { 656 struct get_disks_state state; 657 658 /* initialize */ 659 state.numdisks = 0; 660 state.dd = dd; 661 state.with_non_partitionable = with_non_partitionable; 662 663 if (enumerate_disks(&state, get_disks_helper)) 664 return state.numdisks; 665 666 return 0; 667 } 668 669 #ifdef DEBUG_VERBOSE 670 static void 671 dump_parts(const struct disk_partitions *parts) 672 { 673 fprintf(stderr, "%s partitions on %s:\n", 674 MSG_XLAT(parts->pscheme->short_name), parts->disk); 675 676 for (size_t p = 0; p < parts->num_part; p++) { 677 struct disk_part_info info; 678 679 if (parts->pscheme->get_part_info( 680 parts, p, &info)) { 681 fprintf(stderr, " #%zu: start: %" PRIu64 " " 682 "size: %" PRIu64 ", flags: %x\n", 683 p, info.start, info.size, 684 info.flags); 685 if (info.nat_type) 686 fprintf(stderr, "\ttype: %s\n", 687 info.nat_type->description); 688 } else { 689 fprintf(stderr, "failed to get info " 690 "for partition #%zu\n", p); 691 } 692 } 693 fprintf(stderr, "%" PRIu64 " sectors free, disk size %" PRIu64 694 " sectors, %zu partitions used\n", parts->free_space, 695 parts->disk_size, parts->num_part); 696 } 697 #endif 698 699 static bool 700 delete_scheme(struct pm_devs *p) 701 { 702 703 if (!ask_noyes(MSG_removepartswarn)) 704 return false; 705 706 p->parts->pscheme->free(p->parts); 707 p->parts = NULL; 708 return true; 709 } 710 711 712 static void 713 convert_copy(struct disk_partitions *old_parts, 714 struct disk_partitions *new_parts) 715 { 716 struct disk_part_info oinfo, ninfo; 717 part_id i; 718 719 for (i = 0; i < old_parts->num_part; i++) { 720 if (!old_parts->pscheme->get_part_info(old_parts, i, &oinfo)) 721 continue; 722 723 if (oinfo.flags & PTI_PSCHEME_INTERNAL) 724 continue; 725 726 if (oinfo.flags & PTI_SEC_CONTAINER) { 727 if (old_parts->pscheme->secondary_partitions) { 728 struct disk_partitions *sec_part = 729 old_parts->pscheme-> 730 secondary_partitions( 731 old_parts, oinfo.start, false); 732 if (sec_part) 733 convert_copy(sec_part, new_parts); 734 } 735 continue; 736 } 737 738 if (!new_parts->pscheme->adapt_foreign_part_info(new_parts, 739 &ninfo, old_parts->pscheme, &oinfo)) 740 continue; 741 new_parts->pscheme->add_partition(new_parts, &ninfo, NULL); 742 } 743 } 744 745 bool 746 convert_scheme(struct pm_devs *p, bool is_boot_drive, const char **err_msg) 747 { 748 struct disk_partitions *old_parts, *new_parts; 749 const struct disk_partitioning_scheme *new_scheme; 750 751 *err_msg = NULL; 752 753 old_parts = p->parts; 754 new_scheme = select_part_scheme(p, old_parts->pscheme, 755 false, MSG_select_other_partscheme); 756 757 if (new_scheme == NULL) 758 return false; 759 760 new_parts = new_scheme->create_new_for_disk(p->diskdev, 761 0, p->dlsize, is_boot_drive, NULL); 762 if (new_parts == NULL) 763 return false; 764 765 convert_copy(old_parts, new_parts); 766 767 if (new_parts->num_part == 0) { 768 /* need to cleanup */ 769 new_parts->pscheme->free(new_parts); 770 return false; 771 } 772 773 old_parts->pscheme->free(old_parts); 774 p->parts = new_parts; 775 return true; 776 } 777 778 static struct pm_devs * 779 dummy_whole_system_pm(void) 780 { 781 static struct pm_devs whole_system = { 782 .diskdev = "/", 783 .no_mbr = true, 784 .no_part = true, 785 .cur_system = true, 786 }; 787 static bool init = false; 788 789 if (!init) { 790 strlcpy(whole_system.diskdev_descr, 791 msg_string(MSG_running_system), 792 sizeof whole_system.diskdev_descr); 793 } 794 795 return &whole_system; 796 } 797 798 int 799 find_disks(const char *doingwhat, bool allow_cur_system) 800 { 801 struct disk_desc disks[MAX_DISKS]; 802 /* need two more menu entries: current system + extended partitioning */ 803 menu_ent dsk_menu[__arraycount(disks) + 2]; 804 struct disk_desc *disk; 805 int i = 0, skipped = 0; 806 int already_found, numdisks, selected_disk = -1; 807 int menu_no; 808 struct pm_devs *pm_i, *pm_last = NULL; 809 810 memset(dsk_menu, 0, sizeof(dsk_menu)); 811 812 /* Find disks. */ 813 numdisks = get_disks(disks, partman_go <= 0); 814 815 /* need a redraw here, kernel messages hose everything */ 816 touchwin(stdscr); 817 refresh(); 818 /* Kill typeahead, it won't be what the user had in mind */ 819 fpurge(stdin); 820 821 /* 822 * partman_go: <0 - we want to see menu with extended partitioning 823 * ==0 - we want to see simple select disk menu 824 * >0 - we do not want to see any menus, just detect 825 * all disks 826 */ 827 if (partman_go <= 0) { 828 if (numdisks == 0 && !allow_cur_system) { 829 /* No disks found! */ 830 hit_enter_to_continue(MSG_nodisk, NULL); 831 /*endwin();*/ 832 return -1; 833 } else { 834 /* One or more disks found or current system allowed */ 835 i = 0; 836 if (allow_cur_system) { 837 dsk_menu[i].opt_name = MSG_running_system; 838 dsk_menu[i].opt_flags = OPT_EXIT; 839 dsk_menu[i].opt_action = set_menu_select; 840 i++; 841 } 842 for (; i < numdisks+allow_cur_system; i++) { 843 dsk_menu[i].opt_name = 844 disks[i-allow_cur_system].dd_descr; 845 dsk_menu[i].opt_flags = OPT_EXIT; 846 dsk_menu[i].opt_action = set_menu_select; 847 } 848 if (partman_go < 0) { 849 dsk_menu[i].opt_name = MSG_partman; 850 dsk_menu[i].opt_flags = OPT_EXIT; 851 dsk_menu[i].opt_action = set_menu_select; 852 i++; 853 } 854 menu_no = new_menu(MSG_Available_disks, 855 dsk_menu, i, -1, 856 4, 0, 0, MC_SCROLL, 857 NULL, NULL, NULL, NULL, MSG_exit_menu_generic); 858 if (menu_no == -1) 859 return -1; 860 msg_fmt_display(MSG_ask_disk, "%s", doingwhat); 861 process_menu(menu_no, &selected_disk); 862 free_menu(menu_no); 863 if (allow_cur_system) { 864 if (selected_disk == 0) { 865 pm = dummy_whole_system_pm(); 866 return 1; 867 } else { 868 selected_disk--; 869 } 870 } 871 } 872 if (partman_go < 0 && selected_disk == numdisks) { 873 partman_go = 1; 874 return -2; 875 } else 876 partman_go = 0; 877 if (selected_disk < 0 || selected_disk >= numdisks) 878 return -1; 879 } 880 881 /* Fill pm struct with device(s) info */ 882 for (i = 0; i < numdisks; i++) { 883 if (! partman_go) 884 disk = disks + selected_disk; 885 else { 886 disk = disks + i; 887 already_found = 0; 888 SLIST_FOREACH(pm_i, &pm_head, l) { 889 pm_last = pm_i; 890 if (strcmp(pm_i->diskdev, disk->dd_name) == 0) { 891 already_found = 1; 892 break; 893 } 894 } 895 if (pm_i != NULL && already_found) { 896 /* 897 * We already added this device, but 898 * partitions might have changed 899 */ 900 if (!pm_i->found) { 901 pm_i->found = true; 902 if (pm_i->parts == NULL) { 903 pm_i->parts = 904 partitions_read_disk( 905 pm_i->diskdev, 906 disk->dd_totsec, 907 disk->dd_secsize, 908 disk->dd_no_mbr); 909 } 910 } 911 continue; 912 } 913 } 914 pm = pm_new; 915 pm->found = 1; 916 pm->ptstart = 0; 917 pm->ptsize = 0; 918 strlcpy(pm->diskdev, disk->dd_name, sizeof pm->diskdev); 919 strlcpy(pm->diskdev_descr, disk->dd_descr, sizeof pm->diskdev_descr); 920 /* Use as a default disk if the user has the sets on a local disk */ 921 strlcpy(localfs_dev, disk->dd_name, sizeof localfs_dev); 922 923 /* 924 * Init disk size and geometry 925 */ 926 pm->sectorsize = disk->dd_secsize; 927 pm->dlcyl = disk->dd_cyl; 928 pm->dlhead = disk->dd_head; 929 pm->dlsec = disk->dd_sec; 930 pm->dlsize = disk->dd_totsec; 931 if (pm->dlsize == 0) 932 pm->dlsize = 933 disk->dd_cyl * disk->dd_head * disk->dd_sec; 934 935 pm->parts = partitions_read_disk(pm->diskdev, 936 pm->dlsize, disk->dd_secsize, disk->dd_no_mbr); 937 938 again: 939 940 #ifdef DEBUG_VERBOSE 941 if (pm->parts) { 942 fputs("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", stderr); 943 dump_parts(pm->parts); 944 945 if (pm->parts->pscheme->secondary_partitions) { 946 const struct disk_partitions *sparts = 947 pm->parts->pscheme->secondary_partitions( 948 pm->parts, pm->ptstart, false); 949 if (sparts != NULL) 950 dump_parts(sparts); 951 } 952 } 953 #endif 954 955 pm->no_mbr = disk->dd_no_mbr; 956 pm->no_part = disk->dd_no_part; 957 if (!pm->no_part) { 958 pm->sectorsize = disk->dd_secsize; 959 pm->dlcyl = disk->dd_cyl; 960 pm->dlhead = disk->dd_head; 961 pm->dlsec = disk->dd_sec; 962 pm->dlsize = disk->dd_totsec; 963 if (pm->dlsize == 0) 964 pm->dlsize = 965 disk->dd_cyl * disk->dd_head * disk->dd_sec; 966 967 if (pm->parts && pm->parts->pscheme->size_limit != 0 968 && pm->dlsize > pm->parts->pscheme->size_limit 969 && ! partman_go) { 970 971 char size[5], limit[5]; 972 973 humanize_number(size, sizeof(size), 974 (uint64_t)pm->dlsize * pm->sectorsize, 975 "", HN_AUTOSCALE, HN_B | HN_NOSPACE 976 | HN_DECIMAL); 977 978 humanize_number(limit, sizeof(limit), 979 (uint64_t)pm->parts->pscheme->size_limit 980 * 512U, 981 "", HN_AUTOSCALE, HN_B | HN_NOSPACE 982 | HN_DECIMAL); 983 984 if (logfp) 985 fprintf(logfp, 986 "disk %s: is too big (%" PRIu64 987 " blocks, %s), will be truncated\n", 988 pm->diskdev, pm->dlsize, 989 size); 990 991 msg_display_subst(MSG_toobigdisklabel, 5, 992 pm->diskdev, 993 msg_string(pm->parts->pscheme->name), 994 msg_string(pm->parts->pscheme->short_name), 995 size, limit); 996 997 int sel = -1; 998 const char *err = NULL; 999 process_menu(MENU_convertscheme, &sel); 1000 if (sel == 1) { 1001 if (!delete_scheme(pm)) { 1002 return -1; 1003 } 1004 goto again; 1005 } else if (sel == 2) { 1006 if (!convert_scheme(pm, 1007 partman_go < 0, &err)) { 1008 if (err != NULL) 1009 err_msg_win(err); 1010 return -1; 1011 } 1012 goto again; 1013 } else if (sel == 3) { 1014 return -1; 1015 } 1016 pm->dlsize = pm->parts->pscheme->size_limit; 1017 } 1018 } else { 1019 pm->sectorsize = 0; 1020 pm->dlcyl = 0; 1021 pm->dlhead = 0; 1022 pm->dlsec = 0; 1023 pm->dlsize = 0; 1024 pm->no_mbr = 1; 1025 } 1026 pm->dlcylsize = pm->dlhead * pm->dlsec; 1027 1028 if (partman_go) { 1029 pm_getrefdev(pm_new); 1030 if (SLIST_EMPTY(&pm_head) || pm_last == NULL) 1031 SLIST_INSERT_HEAD(&pm_head, pm_new, l); 1032 else 1033 SLIST_INSERT_AFTER(pm_last, pm_new, l); 1034 pm_new = malloc(sizeof (struct pm_devs)); 1035 memset(pm_new, 0, sizeof *pm_new); 1036 } else 1037 /* We are not in partman and do not want to process 1038 * all devices, exit */ 1039 break; 1040 } 1041 1042 return numdisks-skipped; 1043 } 1044 1045 static int 1046 sort_part_usage_by_mount(const void *a, const void *b) 1047 { 1048 const struct part_usage_info *pa = a, *pb = b; 1049 1050 /* sort all real partitions by mount point */ 1051 if ((pa->instflags & PUIINST_MOUNT) && 1052 (pb->instflags & PUIINST_MOUNT)) 1053 return strcmp(pa->mount, pb->mount); 1054 1055 /* real partitions go first */ 1056 if (pa->instflags & PUIINST_MOUNT) 1057 return -1; 1058 if (pb->instflags & PUIINST_MOUNT) 1059 return 1; 1060 1061 /* arbitrary order for all other partitions */ 1062 if (pa->type == PT_swap) 1063 return -1; 1064 if (pb->type == PT_swap) 1065 return 1; 1066 if (pa->type < pb->type) 1067 return -1; 1068 if (pa->type > pb->type) 1069 return 1; 1070 if (pa->cur_part_id < pb->cur_part_id) 1071 return -1; 1072 if (pa->cur_part_id > pb->cur_part_id) 1073 return 1; 1074 return (uintptr_t)a < (uintptr_t)b ? -1 : 1; 1075 } 1076 1077 int 1078 make_filesystems(struct install_partition_desc *install) 1079 { 1080 int error = 0, partno = -1; 1081 char *newfs = NULL, devdev[PATH_MAX], rdev[PATH_MAX], 1082 opts[200], opt[30]; 1083 size_t i; 1084 struct part_usage_info *ptn; 1085 struct disk_partitions *parts; 1086 const char *mnt_opts = NULL, *fsname = NULL; 1087 1088 if (pm->cur_system) 1089 return 1; 1090 1091 if (pm->no_part) { 1092 /* check if this target device already has a ffs */ 1093 snprintf(rdev, sizeof rdev, _PATH_DEV "/r%s", pm->diskdev); 1094 error = fsck_preen(rdev, "ffs", true); 1095 if (error) { 1096 if (!ask_noyes(MSG_No_filesystem_newfs)) 1097 return EINVAL; 1098 error = run_program(RUN_DISPLAY | RUN_PROGRESS, 1099 "/sbin/newfs -V2 -O2 %s", rdev); 1100 } 1101 1102 md_pre_mount(install, 0); 1103 1104 make_target_dir("/"); 1105 1106 snprintf(devdev, sizeof devdev, _PATH_DEV "%s", pm->diskdev); 1107 error = target_mount_do("-o async", devdev, "/"); 1108 if (error) { 1109 msg_display_subst(MSG_mountfail, 2, devdev, "/"); 1110 hit_enter_to_continue(NULL, NULL); 1111 } 1112 1113 return error; 1114 } 1115 1116 /* Making new file systems and mounting them */ 1117 1118 /* sort to ensure /usr/local is mounted after /usr (etc) */ 1119 qsort(install->infos, install->num, sizeof(*install->infos), 1120 sort_part_usage_by_mount); 1121 1122 for (i = 0; i < install->num; i++) { 1123 /* 1124 * Newfs all file systems marked as needing this. 1125 * Mount the ones that have a mountpoint in the target. 1126 */ 1127 ptn = &install->infos[i]; 1128 parts = ptn->parts; 1129 newfs = NULL; 1130 fsname = NULL; 1131 1132 if (ptn->size == 0 || parts == NULL|| ptn->type == PT_swap) 1133 continue; 1134 1135 if (parts->pscheme->get_part_device(parts, ptn->cur_part_id, 1136 devdev, sizeof devdev, &partno, parent_device_only, false, 1137 false) && is_active_rootpart(devdev, partno)) 1138 continue; 1139 1140 parts->pscheme->get_part_device(parts, ptn->cur_part_id, 1141 devdev, sizeof devdev, &partno, plain_name, true, true); 1142 1143 parts->pscheme->get_part_device(parts, ptn->cur_part_id, 1144 rdev, sizeof rdev, &partno, raw_dev_name, true, true); 1145 1146 opts[0] = 0; 1147 switch (ptn->fs_type) { 1148 case FS_APPLEUFS: 1149 if (ptn->fs_opt3 != 0) 1150 snprintf(opts, sizeof opts, "-i %u", 1151 ptn->fs_opt3); 1152 asprintf(&newfs, "/sbin/newfs %s", opts); 1153 mnt_opts = "-tffs -o async"; 1154 fsname = "ffs"; 1155 break; 1156 case FS_BSDFFS: 1157 if (ptn->fs_opt3 != 0) 1158 snprintf(opts, sizeof opts, "-i %u ", 1159 ptn->fs_opt3); 1160 if (ptn->fs_opt1 != 0) { 1161 snprintf(opt, sizeof opt, "-b %u ", 1162 ptn->fs_opt1); 1163 strcat(opts, opt); 1164 } 1165 if (ptn->fs_opt2 != 0) { 1166 snprintf(opt, sizeof opt, "-f %u ", 1167 ptn->fs_opt2); 1168 strcat(opts, opt); 1169 } 1170 asprintf(&newfs, 1171 "/sbin/newfs -V2 -O %d %s", 1172 ptn->fs_version == 2 ? 2 : 1, opts); 1173 if (ptn->mountflags & PUIMNT_LOG) 1174 mnt_opts = "-tffs -o log"; 1175 else 1176 mnt_opts = "-tffs -o async"; 1177 fsname = "ffs"; 1178 break; 1179 case FS_BSDLFS: 1180 if (ptn->fs_opt1 != 0 && ptn->fs_opt2 != 0) 1181 snprintf(opts, sizeof opts, "-b %u", 1182 ptn->fs_opt1 * ptn->fs_opt2); 1183 asprintf(&newfs, "/sbin/newfs_lfs %s", opts); 1184 mnt_opts = "-tlfs"; 1185 fsname = "lfs"; 1186 break; 1187 case FS_MSDOS: 1188 asprintf(&newfs, "/sbin/newfs_msdos"); 1189 mnt_opts = "-tmsdos"; 1190 fsname = "msdos"; 1191 break; 1192 case FS_SYSVBFS: 1193 asprintf(&newfs, "/sbin/newfs_sysvbfs"); 1194 mnt_opts = "-tsysvbfs"; 1195 fsname = "sysvbfs"; 1196 break; 1197 case FS_V7: 1198 asprintf(&newfs, "/sbin/newfs_v7fs"); 1199 mnt_opts = "-tv7fs"; 1200 fsname = "v7fs"; 1201 break; 1202 case FS_EX2FS: 1203 asprintf(&newfs, 1204 ptn->fs_version == 1 ? 1205 "/sbin/newfs_ext2fs -O 0" : 1206 "/sbin/newfs_ext2fs"); 1207 mnt_opts = "-text2fs"; 1208 fsname = "ext2fs"; 1209 break; 1210 } 1211 if ((ptn->instflags & PUIINST_NEWFS) && newfs != NULL) { 1212 error = run_program(RUN_DISPLAY | RUN_PROGRESS, 1213 "%s %s", newfs, rdev); 1214 } else if ((ptn->instflags & (PUIINST_MOUNT|PUIINST_BOOT)) 1215 && fsname != NULL) { 1216 /* We'd better check it isn't dirty */ 1217 error = fsck_preen(devdev, fsname, false); 1218 } 1219 free(newfs); 1220 if (error != 0) 1221 return error; 1222 1223 ptn->instflags &= ~PUIINST_NEWFS; 1224 md_pre_mount(install, i); 1225 1226 if (partman_go == 0 && (ptn->instflags & PUIINST_MOUNT) && 1227 mnt_opts != NULL) { 1228 make_target_dir(ptn->mount); 1229 error = target_mount_do(mnt_opts, devdev, 1230 ptn->mount); 1231 if (error) { 1232 msg_display_subst(MSG_mountfail, 2, devdev, 1233 ptn->mount); 1234 hit_enter_to_continue(NULL, NULL); 1235 return error; 1236 } 1237 } 1238 } 1239 return 0; 1240 } 1241 1242 int 1243 make_fstab(struct install_partition_desc *install) 1244 { 1245 FILE *f; 1246 const char *dump_dev = NULL; 1247 const char *dev; 1248 char dev_buf[PATH_MAX], swap_dev[PATH_MAX]; 1249 1250 if (pm->cur_system) 1251 return 1; 1252 1253 swap_dev[0] = 0; 1254 1255 /* Create the fstab. */ 1256 make_target_dir("/etc"); 1257 f = target_fopen("/etc/fstab", "w"); 1258 scripting_fprintf(NULL, "cat <<EOF >%s/etc/fstab\n", target_prefix()); 1259 1260 if (logfp) 1261 (void)fprintf(logfp, 1262 "Making %s/etc/fstab (%s).\n", target_prefix(), 1263 pm->diskdev); 1264 1265 if (f == NULL) { 1266 msg_display(MSG_createfstab); 1267 if (logfp) 1268 (void)fprintf(logfp, "Failed to make /etc/fstab!\n"); 1269 hit_enter_to_continue(NULL, NULL); 1270 #ifndef DEBUG 1271 return 1; 1272 #else 1273 f = stdout; 1274 #endif 1275 } 1276 1277 scripting_fprintf(f, "# NetBSD /etc/fstab\n# See /usr/share/examples/" 1278 "fstab/ for more examples.\n"); 1279 1280 if (pm->no_part) { 1281 /* single dk? target */ 1282 char buf[200], parent[200], swap[200], *prompt; 1283 int res; 1284 1285 if (!get_name_and_parent(pm->diskdev, buf, parent)) 1286 goto done_with_disks; 1287 scripting_fprintf(f, NAME_PREFIX "%s\t/\tffs\trw\t\t1 1\n", 1288 buf); 1289 if (!find_swap_part_on(parent, swap)) 1290 goto done_with_disks; 1291 const char *args[] = { parent, swap }; 1292 prompt = str_arg_subst(msg_string(MSG_Auto_add_swap_part), 1293 __arraycount(args), args); 1294 res = ask_yesno(prompt); 1295 free(prompt); 1296 if (res) 1297 scripting_fprintf(f, NAME_PREFIX "%s\tnone" 1298 "\tswap\tsw,dp\t\t0 0\n", swap); 1299 goto done_with_disks; 1300 } 1301 1302 for (size_t i = 0; i < install->num; i++) { 1303 1304 const struct part_usage_info *ptn = &install->infos[i]; 1305 1306 if (ptn->size == 0) 1307 continue; 1308 1309 bool is_tmpfs = ptn->type == PT_root && 1310 ptn->fs_type == FS_TMPFS && 1311 (ptn->flags & PUIFLG_JUST_MOUNTPOINT); 1312 1313 if (!is_tmpfs && ptn->type != PT_swap && 1314 (ptn->instflags & PUIINST_MOUNT) == 0) 1315 continue; 1316 1317 const char *s = ""; 1318 const char *mp = ptn->mount; 1319 const char *fstype = "ffs"; 1320 int fsck_pass = 0, dump_freq = 0; 1321 1322 if (ptn->parts->pscheme->get_part_device(ptn->parts, 1323 ptn->cur_part_id, dev_buf, sizeof dev_buf, NULL, 1324 logical_name, true, false)) 1325 dev = dev_buf; 1326 else 1327 dev = NULL; 1328 1329 if (!*mp) { 1330 /* 1331 * No mount point specified, comment out line and 1332 * use /mnt as a placeholder for the mount point. 1333 */ 1334 s = "# "; 1335 mp = "/mnt"; 1336 } 1337 1338 switch (ptn->fs_type) { 1339 case FS_UNUSED: 1340 continue; 1341 case FS_BSDLFS: 1342 /* If there is no LFS, just comment it out. */ 1343 if (!check_lfs_progs()) 1344 s = "# "; 1345 fstype = "lfs"; 1346 /* FALLTHROUGH */ 1347 case FS_BSDFFS: 1348 fsck_pass = (strcmp(mp, "/") == 0) ? 1 : 2; 1349 dump_freq = 1; 1350 break; 1351 case FS_MSDOS: 1352 fstype = "msdos"; 1353 break; 1354 case FS_SWAP: 1355 if (swap_dev[0] == 0) { 1356 strlcpy(swap_dev, dev, sizeof swap_dev); 1357 dump_dev = ",dp"; 1358 } else { 1359 dump_dev = ""; 1360 } 1361 scripting_fprintf(f, "%s\t\tnone\tswap\tsw%s\t\t 0 0\n", 1362 dev, dump_dev); 1363 continue; 1364 #ifdef HAVE_TMPFS 1365 case FS_TMPFS: 1366 if (ptn->size < 0) 1367 scripting_fprintf(f, 1368 "tmpfs\t\t/tmp\ttmpfs\trw,-m=1777," 1369 "-s=ram%%%" PRIu64 "\n", -ptn->size); 1370 else 1371 scripting_fprintf(f, 1372 "tmpfs\t\t/tmp\ttmpfs\trw,-m=1777," 1373 "-s=%" PRIu64 "M\n", ptn->size); 1374 continue; 1375 #else 1376 case FS_MFS: 1377 if (swap_dev[0] != 0) 1378 scripting_fprintf(f, 1379 "%s\t\t/tmp\tmfs\trw,-s=%" 1380 PRIu64 "\n", swap_dev, ptn->size); 1381 else 1382 scripting_fprintf(f, 1383 "swap\t\t/tmp\tmfs\trw,-s=%" 1384 PRIu64 "\n", ptn->size); 1385 continue; 1386 #endif 1387 case FS_SYSVBFS: 1388 fstype = "sysvbfs"; 1389 make_target_dir("/stand"); 1390 break; 1391 default: 1392 fstype = "???"; 1393 s = "# "; 1394 break; 1395 } 1396 /* The code that remounts root rw doesn't check the partition */ 1397 if (strcmp(mp, "/") == 0 && 1398 (ptn->instflags & PUIINST_MOUNT) == 0) 1399 s = "# "; 1400 1401 scripting_fprintf(f, 1402 "%s%s\t\t%s\t%s\trw%s%s%s%s%s%s%s%s\t\t %d %d\n", 1403 s, dev, mp, fstype, 1404 ptn->mountflags & PUIMNT_LOG ? ",log" : "", 1405 ptn->mountflags & PUIMNT_NOAUTO ? ",noauto" : "", 1406 ptn->mountflags & PUIMNT_ASYNC ? ",async" : "", 1407 ptn->mountflags & PUIMNT_NOATIME ? ",noatime" : "", 1408 ptn->mountflags & PUIMNT_NODEV ? ",nodev" : "", 1409 ptn->mountflags & PUIMNT_NODEVMTIME ? ",nodevmtime" : "", 1410 ptn->mountflags & PUIMNT_NOEXEC ? ",noexec" : "", 1411 ptn->mountflags & PUIMNT_NOSUID ? ",nosuid" : "", 1412 dump_freq, fsck_pass); 1413 } 1414 1415 done_with_disks: 1416 if (cdrom_dev[0] == 0) 1417 get_default_cdrom(cdrom_dev, sizeof(cdrom_dev)); 1418 1419 /* Add /kern, /proc and /dev/pts to fstab and make mountpoint. */ 1420 scripting_fprintf(f, "kernfs\t\t/kern\tkernfs\trw\n"); 1421 scripting_fprintf(f, "ptyfs\t\t/dev/pts\tptyfs\trw\n"); 1422 scripting_fprintf(f, "procfs\t\t/proc\tprocfs\trw\n"); 1423 if (cdrom_dev[0] != 0) 1424 scripting_fprintf(f, "/dev/%s\t\t/cdrom\tcd9660\tro,noauto\n", 1425 cdrom_dev); 1426 scripting_fprintf(f, "%stmpfs\t\t/var/shm\ttmpfs\trw,-m1777,-sram%%25\n", 1427 tmpfs_on_var_shm() ? "" : "#"); 1428 make_target_dir("/kern"); 1429 make_target_dir("/proc"); 1430 make_target_dir("/dev/pts"); 1431 if (cdrom_dev[0] != 0) 1432 make_target_dir("/cdrom"); 1433 make_target_dir("/var/shm"); 1434 1435 scripting_fprintf(NULL, "EOF\n"); 1436 1437 fclose(f); 1438 fflush(NULL); 1439 return 0; 1440 } 1441 1442 static bool 1443 find_part_by_name(const char *name, struct disk_partitions **parts, 1444 part_id *pno) 1445 { 1446 struct pm_devs *i; 1447 struct disk_partitions *ps; 1448 part_id id; 1449 struct disk_desc disks[MAX_DISKS]; 1450 int n, cnt; 1451 1452 if (SLIST_EMPTY(&pm_head)) { 1453 /* 1454 * List has not been filled, only "pm" is valid - check 1455 * that first. 1456 */ 1457 if (pm->parts != NULL && 1458 pm->parts->pscheme->find_by_name != NULL) { 1459 id = pm->parts->pscheme->find_by_name(pm->parts, name); 1460 if (id != NO_PART) { 1461 *pno = id; 1462 *parts = pm->parts; 1463 return true; 1464 } 1465 } 1466 /* 1467 * Not that easy - check all other disks 1468 */ 1469 cnt = get_disks(disks, false); 1470 for (n = 0; n < cnt; n++) { 1471 if (strcmp(disks[n].dd_name, pm->diskdev) == 0) 1472 continue; 1473 ps = partitions_read_disk(disks[n].dd_name, 1474 disks[n].dd_totsec, 1475 disks[n].dd_secsize, 1476 disks[n].dd_no_mbr); 1477 if (ps == NULL) 1478 continue; 1479 if (ps->pscheme->find_by_name == NULL) 1480 continue; 1481 id = ps->pscheme->find_by_name(ps, name); 1482 if (id != NO_PART) { 1483 *pno = id; 1484 *parts = ps; 1485 return true; /* XXX this leaks memory */ 1486 } 1487 ps->pscheme->free(ps); 1488 } 1489 } else { 1490 SLIST_FOREACH(i, &pm_head, l) { 1491 if (i->parts == NULL) 1492 continue; 1493 if (i->parts->pscheme->find_by_name == NULL) 1494 continue; 1495 id = i->parts->pscheme->find_by_name(i->parts, name); 1496 if (id == NO_PART) 1497 continue; 1498 *pno = id; 1499 *parts = i->parts; 1500 return true; 1501 } 1502 } 1503 1504 *pno = NO_PART; 1505 *parts = NULL; 1506 return false; 1507 } 1508 1509 static int 1510 /*ARGSUSED*/ 1511 process_found_fs(struct data *list, size_t num, const struct lookfor *item, 1512 bool with_fsck) 1513 { 1514 int error; 1515 char rdev[PATH_MAX], dev[PATH_MAX], 1516 options[STRSIZE], tmp[STRSIZE], *op, *last; 1517 const char *fsname = (const char*)item->var; 1518 part_id pno; 1519 struct disk_partitions *parts; 1520 size_t len; 1521 bool first, is_root; 1522 1523 if (num < 2 || strstr(list[2].u.s_val, "noauto") != NULL) 1524 return 0; 1525 1526 is_root = strcmp(list[1].u.s_val, "/") == 0; 1527 if (is_root && target_mounted()) 1528 return 0; 1529 1530 if (strcmp(item->head, name_prefix) == 0) { 1531 /* this fstab entry uses NAME= syntax */ 1532 1533 /* unescape */ 1534 char *src, *dst; 1535 for (src = list[0].u.s_val, dst =src; src[0] != 0; ) { 1536 if (src[0] == '\\' && src[1] != 0) 1537 src++; 1538 *dst++ = *src++; 1539 } 1540 *dst = 0; 1541 1542 if (!find_part_by_name(list[0].u.s_val, 1543 &parts, &pno) || parts == NULL || pno == NO_PART) 1544 return 0; 1545 parts->pscheme->get_part_device(parts, pno, 1546 dev, sizeof(dev), NULL, plain_name, true, true); 1547 parts->pscheme->get_part_device(parts, pno, 1548 rdev, sizeof(rdev), NULL, raw_dev_name, true, true); 1549 } else { 1550 /* this fstab entry uses the plain device name */ 1551 if (is_root) { 1552 /* 1553 * PR 54480: we can not use the current device name 1554 * as it might be different from the real environment. 1555 * This is an abuse of the functionality, but it used 1556 * to work before (and still does work if only a single 1557 * target disk is involved). 1558 * Use the device name from the current "pm" instead. 1559 */ 1560 strcpy(rdev, "/dev/r"); 1561 strlcat(rdev, pm->diskdev, sizeof(rdev)); 1562 strcpy(dev, "/dev/"); 1563 strlcat(dev, pm->diskdev, sizeof(dev)); 1564 /* copy over the partition letter, if any */ 1565 len = strlen(list[0].u.s_val); 1566 if (list[0].u.s_val[len-1] >= 'a' && 1567 list[0].u.s_val[len-1] <= 1568 ('a' + getmaxpartitions())) { 1569 strlcat(rdev, &list[0].u.s_val[len-1], 1570 sizeof(rdev)); 1571 strlcat(dev, &list[0].u.s_val[len-1], 1572 sizeof(dev)); 1573 } 1574 } else { 1575 strcpy(rdev, "/dev/r"); 1576 strlcat(rdev, list[0].u.s_val, sizeof(rdev)); 1577 strcpy(dev, "/dev/"); 1578 strlcat(dev, list[0].u.s_val, sizeof(dev)); 1579 } 1580 } 1581 1582 if (with_fsck) { 1583 /* need the raw device for fsck_preen */ 1584 error = fsck_preen(rdev, fsname, false); 1585 if (error != 0) 1586 return error; 1587 } 1588 1589 /* add mount option for fs type */ 1590 strcpy(options, "-t "); 1591 strlcat(options, fsname, sizeof(options)); 1592 1593 /* extract mount options from fstab */ 1594 strlcpy(tmp, list[2].u.s_val, sizeof(tmp)); 1595 for (first = true, op = strtok_r(tmp, ",", &last); op != NULL; 1596 op = strtok_r(NULL, ",", &last)) { 1597 if (strcmp(op, FSTAB_RW) == 0 || 1598 strcmp(op, FSTAB_RQ) == 0 || 1599 strcmp(op, FSTAB_RO) == 0 || 1600 strcmp(op, FSTAB_SW) == 0 || 1601 strcmp(op, FSTAB_DP) == 0 || 1602 strcmp(op, FSTAB_XX) == 0) 1603 continue; 1604 if (first) { 1605 first = false; 1606 strlcat(options, " -o ", sizeof(options)); 1607 } else { 1608 strlcat(options, ",", sizeof(options)); 1609 } 1610 strlcat(options, op, sizeof(options)); 1611 } 1612 1613 error = target_mount(options, dev, list[1].u.s_val); 1614 if (error != 0) { 1615 msg_fmt_display(MSG_mount_failed, "%s", list[0].u.s_val); 1616 if (!ask_noyes(NULL)) 1617 return error; 1618 } 1619 return 0; 1620 } 1621 1622 static int 1623 /*ARGSUSED*/ 1624 found_fs(struct data *list, size_t num, const struct lookfor *item) 1625 { 1626 return process_found_fs(list, num, item, true); 1627 } 1628 1629 static int 1630 /*ARGSUSED*/ 1631 found_fs_nocheck(struct data *list, size_t num, const struct lookfor *item) 1632 { 1633 return process_found_fs(list, num, item, false); 1634 } 1635 1636 /* 1637 * Do an fsck. On failure, inform the user by showing a warning 1638 * message and doing menu_ok() before proceeding. 1639 * The device passed should be the full qualified path to raw disk 1640 * (e.g. /dev/rwd0a). 1641 * Returns 0 on success, or nonzero return code from fsck() on failure. 1642 */ 1643 static int 1644 fsck_preen(const char *disk, const char *fsname, bool silent) 1645 { 1646 char *prog, err[12]; 1647 int error; 1648 1649 if (fsname == NULL) 1650 return 0; 1651 /* first, check if fsck program exists, if not, assume ok */ 1652 asprintf(&prog, "/sbin/fsck_%s", fsname); 1653 if (prog == NULL) 1654 return 0; 1655 if (access(prog, X_OK) != 0) { 1656 free(prog); 1657 return 0; 1658 } 1659 if (!strcmp(fsname,"ffs")) 1660 fixsb(prog, disk); 1661 error = run_program(silent? RUN_SILENT|RUN_ERROR_OK : 0, "%s -p -q %s", prog, disk); 1662 free(prog); 1663 if (error != 0 && !silent) { 1664 sprintf(err, "%d", error); 1665 msg_display_subst(msg_string(MSG_badfs), 3, 1666 disk, fsname, err); 1667 if (ask_noyes(NULL)) 1668 error = 0; 1669 /* XXX at this point maybe we should run a full fsck? */ 1670 } 1671 return error; 1672 } 1673 1674 /* This performs the same function as the etc/rc.d/fixsb script 1675 * which attempts to correct problems with ffs1 filesystems 1676 * which may have been introduced by booting a netbsd-current kernel 1677 * from between April of 2003 and January 2004. For more information 1678 * This script was developed as a response to NetBSD pr install/25138 1679 * Additional prs regarding the original issue include: 1680 * bin/17910 kern/21283 kern/21404 port-macppc/23925 port-macppc/23926 1681 */ 1682 static void 1683 fixsb(const char *prog, const char *disk) 1684 { 1685 int fd; 1686 int rval; 1687 union { 1688 struct fs fs; 1689 char buf[SBLOCKSIZE]; 1690 } sblk; 1691 struct fs *fs = &sblk.fs; 1692 1693 fd = open(disk, O_RDONLY); 1694 if (fd == -1) 1695 return; 1696 1697 /* Read ffsv1 main superblock */ 1698 rval = pread(fd, sblk.buf, sizeof sblk.buf, SBLOCK_UFS1); 1699 close(fd); 1700 if (rval != sizeof sblk.buf) 1701 return; 1702 1703 if (fs->fs_magic != FS_UFS1_MAGIC && 1704 fs->fs_magic != FS_UFS1_MAGIC_SWAPPED) 1705 /* Not FFSv1 */ 1706 return; 1707 if (fs->fs_old_flags & FS_FLAGS_UPDATED) 1708 /* properly updated fslevel 4 */ 1709 return; 1710 if (fs->fs_bsize != fs->fs_maxbsize) 1711 /* not messed up */ 1712 return; 1713 1714 /* 1715 * OK we have a munged fs, first 'upgrade' to fslevel 4, 1716 * We specify -b16 in order to stop fsck bleating that the 1717 * sb doesn't match the first alternate. 1718 */ 1719 run_program(RUN_DISPLAY | RUN_PROGRESS, 1720 "%s -p -b 16 -c 4 %s", prog, disk); 1721 /* Then downgrade to fslevel 3 */ 1722 run_program(RUN_DISPLAY | RUN_PROGRESS, 1723 "%s -p -c 3 %s", prog, disk); 1724 } 1725 1726 /* 1727 * fsck and mount the root partition. 1728 * devdev is the fully qualified block device name. 1729 */ 1730 static int 1731 mount_root(const char *devdev, bool first, bool writeable, 1732 struct install_partition_desc *install) 1733 { 1734 int error; 1735 1736 error = fsck_preen(devdev, "ffs", false); 1737 if (error != 0) 1738 return error; 1739 1740 if (first) 1741 md_pre_mount(install, 0); 1742 1743 /* Mount devdev on target's "". 1744 * If we pass "" as mount-on, Prefixing will DTRT. 1745 * for now, use no options. 1746 * XXX consider -o remount in case target root is 1747 * current root, still readonly from single-user? 1748 */ 1749 return target_mount(writeable? "" : "-r", devdev, ""); 1750 } 1751 1752 /* Get information on the file systems mounted from the root filesystem. 1753 * Offer to convert them into 4.4BSD inodes if they are not 4.4BSD 1754 * inodes. Fsck them. Mount them. 1755 */ 1756 1757 int 1758 mount_disks(struct install_partition_desc *install) 1759 { 1760 char *fstab; 1761 int fstabsize; 1762 int error; 1763 char devdev[PATH_MAX]; 1764 size_t i, num_fs_types, num_entries; 1765 struct lookfor *fstabbuf, *l; 1766 1767 if (install->cur_system) 1768 return 0; 1769 1770 /* 1771 * Check what file system tools are available and create parsers 1772 * for the corresponding fstab(5) entries - all others will be 1773 * ignored. 1774 */ 1775 num_fs_types = 1; /* ffs is implicit */ 1776 for (i = 0; i < __arraycount(extern_fs_with_chk); i++) { 1777 sprintf(devdev, "/sbin/newfs_%s", extern_fs_with_chk[i]); 1778 if (file_exists_p(devdev)) 1779 num_fs_types++; 1780 } 1781 for (i = 0; i < __arraycount(extern_fs_newfs_only); i++) { 1782 sprintf(devdev, "/sbin/newfs_%s", extern_fs_newfs_only[i]); 1783 if (file_exists_p(devdev)) 1784 num_fs_types++; 1785 } 1786 num_entries = 2 * num_fs_types + 1; /* +1 for "ufs" special case */ 1787 fstabbuf = calloc(num_entries, sizeof(*fstabbuf)); 1788 if (fstabbuf == NULL) 1789 return -1; 1790 l = fstabbuf; 1791 l->head = "/dev/"; 1792 l->fmt = strdup("/dev/%s %s ffs %s"); 1793 l->todo = "c"; 1794 l->var = __UNCONST("ffs"); 1795 l->func = found_fs; 1796 l++; 1797 l->head = "/dev/"; 1798 l->fmt = strdup("/dev/%s %s ufs %s"); 1799 l->todo = "c"; 1800 l->var = __UNCONST("ffs"); 1801 l->func = found_fs; 1802 l++; 1803 l->head = NAME_PREFIX; 1804 l->fmt = strdup(NAME_PREFIX "%s %s ffs %s"); 1805 l->todo = "c"; 1806 l->var = __UNCONST("ffs"); 1807 l->func = found_fs; 1808 l++; 1809 for (i = 0; i < __arraycount(extern_fs_with_chk); i++) { 1810 sprintf(devdev, "/sbin/newfs_%s", extern_fs_with_chk[i]); 1811 if (!file_exists_p(devdev)) 1812 continue; 1813 sprintf(devdev, "/dev/%%s %%s %s %%s", extern_fs_with_chk[i]); 1814 l->head = "/dev/"; 1815 l->fmt = strdup(devdev); 1816 l->todo = "c"; 1817 l->var = __UNCONST(extern_fs_with_chk[i]); 1818 l->func = found_fs; 1819 l++; 1820 sprintf(devdev, NAME_PREFIX "%%s %%s %s %%s", 1821 extern_fs_with_chk[i]); 1822 l->head = NAME_PREFIX; 1823 l->fmt = strdup(devdev); 1824 l->todo = "c"; 1825 l->var = __UNCONST(extern_fs_with_chk[i]); 1826 l->func = found_fs; 1827 l++; 1828 } 1829 for (i = 0; i < __arraycount(extern_fs_newfs_only); i++) { 1830 sprintf(devdev, "/sbin/newfs_%s", extern_fs_newfs_only[i]); 1831 if (!file_exists_p(devdev)) 1832 continue; 1833 sprintf(devdev, "/dev/%%s %%s %s %%s", extern_fs_newfs_only[i]); 1834 l->head = "/dev/"; 1835 l->fmt = strdup(devdev); 1836 l->todo = "c"; 1837 l->var = __UNCONST(extern_fs_newfs_only[i]); 1838 l->func = found_fs_nocheck; 1839 l++; 1840 sprintf(devdev, NAME_PREFIX "%%s %%s %s %%s", 1841 extern_fs_newfs_only[i]); 1842 l->head = NAME_PREFIX; 1843 l->fmt = strdup(devdev); 1844 l->todo = "c"; 1845 l->var = __UNCONST(extern_fs_newfs_only[i]); 1846 l->func = found_fs_nocheck; 1847 l++; 1848 } 1849 assert((size_t)(l - fstabbuf) == num_entries); 1850 1851 /* First the root device. */ 1852 if (target_already_root()) { 1853 /* avoid needing to call target_already_root() again */ 1854 targetroot_mnt[0] = 0; 1855 } else if (pm->no_part) { 1856 snprintf(devdev, sizeof devdev, _PATH_DEV "%s", pm->diskdev); 1857 error = mount_root(devdev, true, false, install); 1858 if (error != 0 && error != EBUSY) 1859 return -1; 1860 } else { 1861 for (i = 0; i < install->num; i++) { 1862 if (is_root_part_mount(install->infos[i].mount)) 1863 break; 1864 } 1865 1866 if (i >= install->num) { 1867 hit_enter_to_continue(MSG_noroot, NULL); 1868 return -1; 1869 } 1870 1871 if (!install->infos[i].parts->pscheme->get_part_device( 1872 install->infos[i].parts, install->infos[i].cur_part_id, 1873 devdev, sizeof devdev, NULL, plain_name, true, true)) 1874 return -1; 1875 error = mount_root(devdev, true, false, install); 1876 if (error != 0 && error != EBUSY) 1877 return -1; 1878 } 1879 1880 /* Check the target /etc/fstab exists before trying to parse it. */ 1881 if (target_dir_exists_p("/etc") == 0 || 1882 target_file_exists_p("/etc/fstab") == 0) { 1883 msg_fmt_display(MSG_noetcfstab, "%s", pm->diskdev); 1884 hit_enter_to_continue(NULL, NULL); 1885 return -1; 1886 } 1887 1888 1889 /* Get fstab entries from the target-root /etc/fstab. */ 1890 fstabsize = target_collect_file(T_FILE, &fstab, "/etc/fstab"); 1891 if (fstabsize < 0) { 1892 /* error ! */ 1893 msg_fmt_display(MSG_badetcfstab, "%s", pm->diskdev); 1894 hit_enter_to_continue(NULL, NULL); 1895 umount_root(); 1896 return -2; 1897 } 1898 /* 1899 * We unmount the read-only root again, so we can mount it 1900 * with proper options from /etc/fstab 1901 */ 1902 umount_root(); 1903 1904 /* 1905 * Now do all entries in /etc/fstab and mount them if required 1906 */ 1907 error = walk(fstab, (size_t)fstabsize, fstabbuf, num_entries); 1908 free(fstab); 1909 for (i = 0; i < num_entries; i++) 1910 free(__UNCONST(fstabbuf[i].fmt)); 1911 free(fstabbuf); 1912 1913 return error; 1914 } 1915 1916 static char swap_dev[PATH_MAX]; 1917 1918 int 1919 set_swap_if_low_ram(struct install_partition_desc *install) 1920 { 1921 swap_dev[0] = 0; 1922 if (get_ramsize() <= TINY_RAM_SIZE) 1923 return set_swap(install); 1924 return 0; 1925 } 1926 1927 int 1928 set_swap(struct install_partition_desc *install) 1929 { 1930 size_t i; 1931 int rval; 1932 1933 swap_dev[0] = 0; 1934 for (i = 0; i < install->num; i++) { 1935 if (install->infos[i].type == PT_swap) 1936 break; 1937 } 1938 if (i >= install->num) 1939 return 0; 1940 1941 if (!install->infos[i].parts->pscheme->get_part_device( 1942 install->infos[i].parts, install->infos[i].cur_part_id, swap_dev, 1943 sizeof swap_dev, NULL, plain_name, true, true)) 1944 return -1; 1945 1946 rval = swapctl(SWAP_ON, swap_dev, 0); 1947 if (rval != 0) { 1948 swap_dev[0] = 0; 1949 return -1; 1950 } 1951 1952 return 1; 1953 } 1954 1955 void 1956 clear_swap(void) 1957 { 1958 1959 if (swap_dev[0] == 0) 1960 return; 1961 swapctl(SWAP_OFF, swap_dev, 0); 1962 swap_dev[0] = 0; 1963 } 1964 1965 int 1966 check_swap(const char *disk, int remove_swap) 1967 { 1968 struct swapent *swap; 1969 char *cp; 1970 int nswap; 1971 int l; 1972 int rval = 0; 1973 1974 nswap = swapctl(SWAP_NSWAP, 0, 0); 1975 if (nswap <= 0) 1976 return 0; 1977 1978 swap = malloc(nswap * sizeof *swap); 1979 if (swap == NULL) 1980 return -1; 1981 1982 nswap = swapctl(SWAP_STATS, swap, nswap); 1983 if (nswap < 0) 1984 goto bad_swap; 1985 1986 l = strlen(disk); 1987 while (--nswap >= 0) { 1988 /* Should we check the se_dev or se_path? */ 1989 cp = swap[nswap].se_path; 1990 if (memcmp(cp, "/dev/", 5) != 0) 1991 continue; 1992 if (memcmp(cp + 5, disk, l) != 0) 1993 continue; 1994 if (!isalpha(*(unsigned char *)(cp + 5 + l))) 1995 continue; 1996 if (cp[5 + l + 1] != 0) 1997 continue; 1998 /* ok path looks like it is for this device */ 1999 if (!remove_swap) { 2000 /* count active swap areas */ 2001 rval++; 2002 continue; 2003 } 2004 if (swapctl(SWAP_OFF, cp, 0) == -1) 2005 rval = -1; 2006 } 2007 2008 done: 2009 free(swap); 2010 return rval; 2011 2012 bad_swap: 2013 rval = -1; 2014 goto done; 2015 } 2016 2017 #ifdef HAVE_BOOTXX_xFS 2018 char * 2019 bootxx_name(struct install_partition_desc *install) 2020 { 2021 size_t i; 2022 int fstype = -1; 2023 const char *bootxxname; 2024 char *bootxx; 2025 2026 /* find a partition to be mounted as / */ 2027 for (i = 0; i < install->num; i++) { 2028 if ((install->infos[i].instflags & PUIINST_MOUNT) 2029 && strcmp(install->infos[i].mount, "/") == 0) { 2030 fstype = install->infos[i].fs_type; 2031 break; 2032 } 2033 } 2034 if (fstype < 0) { 2035 /* not found? take first root type partition instead */ 2036 for (i = 0; i < install->num; i++) { 2037 if (install->infos[i].type == PT_root) { 2038 fstype = install->infos[i].fs_type; 2039 break; 2040 } 2041 } 2042 } 2043 2044 /* check we have boot code for the root partition type */ 2045 switch (fstype) { 2046 #if defined(BOOTXX_FFSV1) || defined(BOOTXX_FFSV2) 2047 case FS_BSDFFS: 2048 if (install->infos[i].fs_version == 2) { 2049 #ifdef BOOTXX_FFSV2 2050 bootxxname = BOOTXX_FFSV2; 2051 #else 2052 bootxxname = NULL; 2053 #endif 2054 } else { 2055 #ifdef BOOTXX_FFSV1 2056 bootxxname = BOOTXX_FFSV1; 2057 #else 2058 bootxxname = NULL; 2059 #endif 2060 } 2061 break; 2062 #endif 2063 #ifdef BOOTXX_LFSV2 2064 case FS_BSDLFS: 2065 bootxxname = BOOTXX_LFSV2; 2066 break; 2067 #endif 2068 default: 2069 bootxxname = NULL; 2070 break; 2071 } 2072 2073 if (bootxxname == NULL) 2074 return NULL; 2075 2076 asprintf(&bootxx, "%s/%s", BOOTXXDIR, bootxxname); 2077 return bootxx; 2078 } 2079 #endif 2080 2081 /* from dkctl.c */ 2082 static int 2083 get_dkwedges_sort(const void *a, const void *b) 2084 { 2085 const struct dkwedge_info *dkwa = a, *dkwb = b; 2086 const daddr_t oa = dkwa->dkw_offset, ob = dkwb->dkw_offset; 2087 return (oa < ob) ? -1 : (oa > ob) ? 1 : 0; 2088 } 2089 2090 int 2091 get_dkwedges(struct dkwedge_info **dkw, const char *diskdev) 2092 { 2093 struct dkwedge_list dkwl; 2094 2095 *dkw = NULL; 2096 if (!get_wedge_list(diskdev, &dkwl)) 2097 return -1; 2098 2099 if (dkwl.dkwl_nwedges > 0 && *dkw != NULL) { 2100 qsort(*dkw, dkwl.dkwl_nwedges, sizeof(**dkw), 2101 get_dkwedges_sort); 2102 } 2103 2104 return dkwl.dkwl_nwedges; 2105 } 2106 2107 #ifndef NO_CLONES 2108 /* 2109 * Helper structures used in the partition select menu 2110 */ 2111 struct single_partition { 2112 struct disk_partitions *parts; 2113 part_id id; 2114 }; 2115 2116 struct sel_menu_data { 2117 struct single_partition *partitions; 2118 struct selected_partition result; 2119 }; 2120 2121 static int 2122 select_single_part(menudesc *m, void *arg) 2123 { 2124 struct sel_menu_data *data = arg; 2125 2126 data->result.parts = data->partitions[m->cursel].parts; 2127 data->result.id = data->partitions[m->cursel].id; 2128 2129 return 1; 2130 } 2131 2132 static void 2133 display_single_part(menudesc *m, int opt, void *arg) 2134 { 2135 const struct sel_menu_data *data = arg; 2136 struct disk_part_info info; 2137 struct disk_partitions *parts = data->partitions[opt].parts; 2138 part_id id = data->partitions[opt].id; 2139 int l; 2140 const char *desc = NULL; 2141 char line[MENUSTRSIZE*2]; 2142 2143 if (!parts->pscheme->get_part_info(parts, id, &info)) 2144 return; 2145 2146 if (parts->pscheme->other_partition_identifier != NULL) 2147 desc = parts->pscheme->other_partition_identifier( 2148 parts, id); 2149 2150 daddr_t start = info.start / sizemult; 2151 daddr_t size = info.size / sizemult; 2152 snprintf(line, sizeof line, "%s [%" PRIu64 " @ %" PRIu64 "]", 2153 parts->disk, size, start); 2154 2155 if (info.nat_type != NULL) { 2156 strlcat(line, " ", sizeof line); 2157 strlcat(line, info.nat_type->description, sizeof line); 2158 } 2159 2160 if (desc != NULL) { 2161 strlcat(line, ": ", sizeof line); 2162 strlcat(line, desc, sizeof line); 2163 } 2164 2165 l = strlen(line); 2166 if (l >= (m->w)) 2167 strcpy(line + (m->w-3), "..."); 2168 wprintw(m->mw, "%s", line); 2169 } 2170 2171 /* 2172 * is the given "test" partitions set used in the selected set? 2173 */ 2174 static bool 2175 selection_has_parts(struct selected_partitions *sel, 2176 const struct disk_partitions *test) 2177 { 2178 size_t i; 2179 2180 for (i = 0; i < sel->num_sel; i++) { 2181 if (sel->selection[i].parts == test) 2182 return true; 2183 } 2184 return false; 2185 } 2186 2187 /* 2188 * is the given "test" partition in the selected set? 2189 */ 2190 static bool 2191 selection_has_partition(struct selected_partitions *sel, 2192 const struct disk_partitions *test, part_id test_id) 2193 { 2194 size_t i; 2195 2196 for (i = 0; i < sel->num_sel; i++) { 2197 if (sel->selection[i].parts == test && 2198 sel->selection[i].id == test_id) 2199 return true; 2200 } 2201 return false; 2202 } 2203 2204 /* 2205 * let the user select a partition, optionally skipping all partitions 2206 * on the "ignore" device 2207 */ 2208 static bool 2209 add_select_partition(struct selected_partitions *res, 2210 struct disk_partitions **all_parts, size_t all_cnt) 2211 { 2212 struct disk_partitions *ps; 2213 struct disk_part_info info; 2214 part_id id; 2215 struct single_partition *partitions, *pp; 2216 struct menu_ent *part_menu_opts, *menup; 2217 size_t n, part_cnt; 2218 int sel_menu; 2219 2220 /* 2221 * count how many items our menu will have 2222 */ 2223 part_cnt = 0; 2224 for (n = 0; n < all_cnt; n++) { 2225 ps = all_parts[n]; 2226 for (id = 0; id < ps->num_part; id++) { 2227 if (selection_has_partition(res, ps, id)) 2228 continue; 2229 if (!ps->pscheme->get_part_info(ps, id, &info)) 2230 continue; 2231 if (info.flags & (PTI_SEC_CONTAINER|PTI_WHOLE_DISK| 2232 PTI_PSCHEME_INTERNAL|PTI_RAW_PART)) 2233 continue; 2234 part_cnt++; 2235 } 2236 } 2237 2238 /* 2239 * create a menu from this and let the user 2240 * select one partition 2241 */ 2242 part_menu_opts = NULL; 2243 partitions = calloc(part_cnt, sizeof *partitions); 2244 if (partitions == NULL) 2245 goto done; 2246 part_menu_opts = calloc(part_cnt, sizeof *part_menu_opts); 2247 if (part_menu_opts == NULL) 2248 goto done; 2249 pp = partitions; 2250 menup = part_menu_opts; 2251 for (n = 0; n < all_cnt; n++) { 2252 ps = all_parts[n]; 2253 for (id = 0; id < ps->num_part; id++) { 2254 if (selection_has_partition(res, ps, id)) 2255 continue; 2256 if (!ps->pscheme->get_part_info(ps, id, &info)) 2257 continue; 2258 if (info.flags & (PTI_SEC_CONTAINER|PTI_WHOLE_DISK| 2259 PTI_PSCHEME_INTERNAL|PTI_RAW_PART)) 2260 continue; 2261 pp->parts = ps; 2262 pp->id = id; 2263 pp++; 2264 menup->opt_action = select_single_part; 2265 menup++; 2266 } 2267 } 2268 sel_menu = new_menu(MSG_select_foreign_part, part_menu_opts, part_cnt, 2269 3, 3, 0, 60, 2270 MC_SUBMENU | MC_SCROLL | MC_NOCLEAR, 2271 NULL, display_single_part, NULL, 2272 NULL, MSG_exit_menu_generic); 2273 if (sel_menu != -1) { 2274 struct selected_partition *newsels; 2275 struct sel_menu_data data; 2276 2277 memset(&data, 0, sizeof data); 2278 data.partitions = partitions; 2279 process_menu(sel_menu, &data); 2280 free_menu(sel_menu); 2281 2282 if (data.result.parts != NULL) { 2283 newsels = realloc(res->selection, 2284 sizeof(*res->selection)*(res->num_sel+1)); 2285 if (newsels != NULL) { 2286 res->selection = newsels; 2287 newsels += res->num_sel++; 2288 newsels->parts = data.result.parts; 2289 newsels->id = data.result.id; 2290 } 2291 } 2292 } 2293 2294 /* 2295 * Final cleanup 2296 */ 2297 done: 2298 free(part_menu_opts); 2299 free(partitions); 2300 2301 return res->num_sel > 0; 2302 } 2303 2304 struct part_selection_and_all_parts { 2305 struct selected_partitions *selection; 2306 struct disk_partitions **all_parts; 2307 size_t all_cnt; 2308 char *title; 2309 bool cancelled; 2310 }; 2311 2312 static int 2313 toggle_clone_data(struct menudesc *m, void *arg) 2314 { 2315 struct part_selection_and_all_parts *sel = arg; 2316 2317 sel->selection->with_data = !sel->selection->with_data; 2318 return 0; 2319 } 2320 2321 static int 2322 add_another(struct menudesc *m, void *arg) 2323 { 2324 struct part_selection_and_all_parts *sel = arg; 2325 2326 add_select_partition(sel->selection, sel->all_parts, sel->all_cnt); 2327 return 0; 2328 } 2329 2330 static int 2331 cancel_clone(struct menudesc *m, void *arg) 2332 { 2333 struct part_selection_and_all_parts *sel = arg; 2334 2335 sel->cancelled = true; 2336 return 1; 2337 } 2338 2339 static void 2340 update_sel_part_title(struct part_selection_and_all_parts *sel) 2341 { 2342 struct disk_part_info info; 2343 char *buf, line[MENUSTRSIZE]; 2344 size_t buf_len, i; 2345 2346 buf_len = MENUSTRSIZE * (1+sel->selection->num_sel); 2347 buf = malloc(buf_len); 2348 if (buf == NULL) 2349 return; 2350 2351 strcpy(buf, msg_string(MSG_select_source_hdr)); 2352 for (i = 0; i < sel->selection->num_sel; i++) { 2353 struct selected_partition *s = 2354 &sel->selection->selection[i]; 2355 if (!s->parts->pscheme->get_part_info(s->parts, s->id, &info)) 2356 continue; 2357 daddr_t start = info.start / sizemult; 2358 daddr_t size = info.size / sizemult; 2359 sprintf(line, "\n %s [%" PRIu64 " @ %" PRIu64 "] ", 2360 s->parts->disk, size, start); 2361 if (info.nat_type != NULL) 2362 strlcat(line, info.nat_type->description, sizeof(line)); 2363 strlcat(buf, line, buf_len); 2364 } 2365 free(sel->title); 2366 sel->title = buf; 2367 } 2368 2369 static void 2370 post_sel_part(struct menudesc *m, void *arg) 2371 { 2372 struct part_selection_and_all_parts *sel = arg; 2373 2374 if (m->mw == NULL) 2375 return; 2376 update_sel_part_title(sel); 2377 m->title = sel->title; 2378 m->h = 0; 2379 resize_menu_height(m); 2380 } 2381 2382 static void 2383 fmt_sel_part_line(struct menudesc *m, int i, void *arg) 2384 { 2385 struct part_selection_and_all_parts *sel = arg; 2386 2387 wprintw(m->mw, "%s: %s", msg_string(MSG_clone_with_data), 2388 sel->selection->with_data ? 2389 msg_string(MSG_Yes) : 2390 msg_string(MSG_No)); 2391 } 2392 2393 bool 2394 select_partitions(struct selected_partitions *res, 2395 const struct disk_partitions *ignore) 2396 { 2397 struct disk_desc disks[MAX_DISKS]; 2398 struct disk_partitions *ps; 2399 struct part_selection_and_all_parts data; 2400 struct pm_devs *i; 2401 size_t j; 2402 int cnt, n, m; 2403 static menu_ent men[] = { 2404 { .opt_name = MSG_select_source_add, 2405 .opt_action = add_another }, 2406 { .opt_action = toggle_clone_data }, 2407 { .opt_name = MSG_cancel, .opt_action = cancel_clone }, 2408 }; 2409 2410 memset(res, 0, sizeof *res); 2411 memset(&data, 0, sizeof data); 2412 data.selection = res; 2413 2414 /* 2415 * collect all available partition sets 2416 */ 2417 data.all_cnt = 0; 2418 if (SLIST_EMPTY(&pm_head)) { 2419 cnt = get_disks(disks, false); 2420 if (cnt <= 0) 2421 return false; 2422 2423 /* 2424 * allocate two slots for each disk (primary/secondary) 2425 */ 2426 data.all_parts = calloc(2*cnt, sizeof *data.all_parts); 2427 if (data.all_parts == NULL) 2428 return false; 2429 2430 for (n = 0; n < cnt; n++) { 2431 if (ignore != NULL && 2432 strcmp(disks[n].dd_name, ignore->disk) == 0) 2433 continue; 2434 2435 ps = partitions_read_disk(disks[n].dd_name, 2436 disks[n].dd_totsec, 2437 disks[n].dd_secsize, 2438 disks[n].dd_no_mbr); 2439 if (ps == NULL) 2440 continue; 2441 data.all_parts[data.all_cnt++] = ps; 2442 ps = get_inner_parts(ps); 2443 if (ps == NULL) 2444 continue; 2445 data.all_parts[data.all_cnt++] = ps; 2446 } 2447 if (data.all_cnt > 0) 2448 res->free_parts = true; 2449 } else { 2450 cnt = 0; 2451 SLIST_FOREACH(i, &pm_head, l) 2452 cnt++; 2453 2454 data.all_parts = calloc(cnt, sizeof *data.all_parts); 2455 if (data.all_parts == NULL) 2456 return false; 2457 2458 SLIST_FOREACH(i, &pm_head, l) { 2459 if (i->parts == NULL) 2460 continue; 2461 if (i->parts == ignore) 2462 continue; 2463 data.all_parts[data.all_cnt++] = i->parts; 2464 } 2465 } 2466 2467 if (!add_select_partition(res, data.all_parts, data.all_cnt)) 2468 goto fail; 2469 2470 /* loop with menu */ 2471 update_sel_part_title(&data); 2472 m = new_menu(data.title, men, __arraycount(men), 3, 2, 0, 65, MC_SCROLL, 2473 post_sel_part, fmt_sel_part_line, NULL, NULL, MSG_clone_src_done); 2474 process_menu(m, &data); 2475 free(data.title); 2476 if (res->num_sel == 0) 2477 goto fail; 2478 2479 /* cleanup */ 2480 if (res->free_parts) { 2481 for (j = 0; j < data.all_cnt; j++) { 2482 if (selection_has_parts(res, data.all_parts[j])) 2483 continue; 2484 if (data.all_parts[j]->parent != NULL) 2485 continue; 2486 data.all_parts[j]->pscheme->free(data.all_parts[j]); 2487 } 2488 } 2489 free(data.all_parts); 2490 return true; 2491 2492 fail: 2493 if (res->free_parts) { 2494 for (j = 0; j < data.all_cnt; j++) { 2495 if (data.all_parts[j]->parent != NULL) 2496 continue; 2497 data.all_parts[j]->pscheme->free(data.all_parts[j]); 2498 } 2499 } 2500 free(data.all_parts); 2501 return false; 2502 } 2503 2504 void 2505 free_selected_partitions(struct selected_partitions *selected) 2506 { 2507 size_t i; 2508 struct disk_partitions *parts; 2509 2510 if (!selected->free_parts) 2511 return; 2512 2513 for (i = 0; i < selected->num_sel; i++) { 2514 parts = selected->selection[i].parts; 2515 2516 /* remove from list before testing for other instances */ 2517 selected->selection[i].parts = NULL; 2518 2519 /* if this is the secondary partion set, the parent owns it */ 2520 if (parts->parent != NULL) 2521 continue; 2522 2523 /* only free once (we use the last one) */ 2524 if (selection_has_parts(selected, parts)) 2525 continue; 2526 parts->pscheme->free(parts); 2527 } 2528 free(selected->selection); 2529 } 2530 2531 daddr_t 2532 selected_parts_size(struct selected_partitions *selected) 2533 { 2534 struct disk_part_info info; 2535 size_t i; 2536 daddr_t s = 0; 2537 2538 for (i = 0; i < selected->num_sel; i++) { 2539 if (!selected->selection[i].parts->pscheme->get_part_info( 2540 selected->selection[i].parts, 2541 selected->selection[i].id, &info)) 2542 continue; 2543 s += info.size; 2544 } 2545 2546 return s; 2547 } 2548 2549 int 2550 clone_target_select(menudesc *m, void *arg) 2551 { 2552 struct clone_target_menu_data *data = arg; 2553 2554 data->res = m->cursel; 2555 return 1; 2556 } 2557 2558 bool 2559 clone_partition_data(struct disk_partitions *dest_parts, part_id did, 2560 struct disk_partitions *src_parts, part_id sid) 2561 { 2562 char src_dev[MAXPATHLEN], target_dev[MAXPATHLEN]; 2563 2564 if (!src_parts->pscheme->get_part_device( 2565 src_parts, sid, src_dev, sizeof src_dev, NULL, 2566 raw_dev_name, true, true)) 2567 return false; 2568 if (!dest_parts->pscheme->get_part_device( 2569 dest_parts, did, target_dev, sizeof target_dev, NULL, 2570 raw_dev_name, true, true)) 2571 return false; 2572 2573 return run_program(RUN_DISPLAY | RUN_PROGRESS, 2574 "progress -f %s -b 1m dd bs=1m of=%s", 2575 src_dev, target_dev) == 0; 2576 } 2577 #endif 2578 2579