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