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