1 /* $NetBSD: disks.c,v 1.98 2026/01/12 13:46:18 nia 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 static bool 788 delete_scheme(struct pm_devs *p) 789 { 790 791 if (!ask_noyes(MSG_removepartswarn)) 792 return false; 793 794 p->parts->pscheme->free(p->parts); 795 p->parts = NULL; 796 return true; 797 } 798 799 800 static bool 801 convert_copy(struct disk_partitions *old_parts, 802 struct disk_partitions *new_parts) 803 { 804 struct disk_part_info oinfo, ninfo; 805 part_id i; 806 bool err = false; 807 808 for (i = 0; i < old_parts->num_part; i++) { 809 if (!old_parts->pscheme->get_part_info(old_parts, i, &oinfo)) 810 continue; 811 812 if (oinfo.flags & PTI_PSCHEME_INTERNAL) 813 continue; 814 815 if (oinfo.flags & PTI_SEC_CONTAINER) { 816 if (old_parts->pscheme->secondary_partitions) { 817 struct disk_partitions *sec_part = 818 old_parts->pscheme-> 819 secondary_partitions( 820 old_parts, oinfo.start, false); 821 if (sec_part && !convert_copy(sec_part, 822 new_parts)) 823 err = true; 824 } 825 continue; 826 } 827 828 if (!new_parts->pscheme->adapt_foreign_part_info(new_parts, 829 &ninfo, old_parts->pscheme, &oinfo)) { 830 err = true; 831 continue; 832 } 833 if (!new_parts->pscheme->add_partition(new_parts, &ninfo, 834 NULL)) 835 err = true; 836 } 837 return !err; 838 } 839 840 bool 841 convert_scheme(struct pm_devs *p, bool is_boot_drive, const char **err_msg) 842 { 843 struct disk_partitions *old_parts, *new_parts; 844 const struct disk_partitioning_scheme *new_scheme; 845 846 *err_msg = NULL; 847 848 old_parts = p->parts; 849 new_scheme = select_part_scheme(p, old_parts->pscheme, 850 false, MSG_select_other_partscheme); 851 852 if (new_scheme == NULL) { 853 if (err_msg) 854 *err_msg = INTERNAL_ERROR; 855 return false; 856 } 857 858 new_parts = new_scheme->create_new_for_disk(p->diskdev, 859 0, p->dlsize, is_boot_drive, NULL); 860 if (new_parts == NULL) { 861 if (err_msg) 862 *err_msg = MSG_out_of_memory; 863 return false; 864 } 865 866 if (!convert_copy(old_parts, new_parts)) { 867 /* need to cleanup */ 868 if (err_msg) 869 *err_msg = MSG_cvtscheme_error; 870 new_parts->pscheme->free(new_parts); 871 return false; 872 } 873 874 old_parts->pscheme->free(old_parts); 875 p->parts = new_parts; 876 return true; 877 } 878 879 static struct pm_devs * 880 dummy_whole_system_pm(void) 881 { 882 static struct pm_devs whole_system = { 883 .diskdev = "/", 884 .no_mbr = true, 885 .no_part = true, 886 .cur_system = true, 887 }; 888 static bool init = false; 889 890 if (!init) { 891 strlcpy(whole_system.diskdev_descr, 892 msg_string(MSG_running_system), 893 sizeof whole_system.diskdev_descr); 894 } 895 896 return &whole_system; 897 } 898 899 int 900 find_disks(const char *doingwhat, bool allow_cur_system) 901 { 902 struct disk_desc disks[MAX_DISKS]; 903 /* need two more menu entries: current system + extended partitioning */ 904 menu_ent dsk_menu[__arraycount(disks) + 2], 905 wedge_menu[__arraycount(dsk_menu)]; 906 int disk_no[__arraycount(dsk_menu)], wedge_no[__arraycount(dsk_menu)]; 907 struct disk_desc *disk; 908 int i = 0, dno, wno, skipped = 0; 909 int already_found, numdisks, selected_disk = -1; 910 int menu_no, w_menu_no; 911 size_t max_desc_len; 912 struct pm_devs *pm_i, *pm_last = NULL; 913 bool any_wedges = false; 914 915 memset(dsk_menu, 0, sizeof(dsk_menu)); 916 memset(wedge_menu, 0, sizeof(wedge_menu)); 917 918 /* Find disks. */ 919 numdisks = get_disks(disks, partman_go <= 0); 920 921 /* need a redraw here, kernel messages hose everything */ 922 touchwin(stdscr); 923 refresh(); 924 /* Kill typeahead, it won't be what the user had in mind */ 925 fpurge(stdin); 926 /* 927 * we need space for the menu box and the row label, 928 * this sums up to 7 characters. 929 */ 930 max_desc_len = getmaxx(stdscr) - 8; 931 if (max_desc_len >= __arraycount(disks[0].dd_descr)) 932 max_desc_len = __arraycount(disks[0].dd_descr) - 1; 933 934 /* 935 * partman_go: <0 - we want to see menu with extended partitioning 936 * ==0 - we want to see simple select disk menu 937 * >0 - we do not want to see any menus, just detect 938 * all disks 939 */ 940 if (partman_go <= 0) { 941 if (numdisks == 0 && !allow_cur_system) { 942 /* No disks found! */ 943 hit_enter_to_continue(MSG_nodisk, NULL); 944 /*endwin();*/ 945 return -1; 946 } else { 947 /* One or more disks found or current system allowed */ 948 dno = wno = 0; 949 if (allow_cur_system) { 950 dsk_menu[dno].opt_name = MSG_running_system; 951 dsk_menu[dno].opt_flags = OPT_EXIT; 952 dsk_menu[dno].opt_action = set_menu_select; 953 disk_no[dno] = -1; 954 i++; dno++; 955 } 956 for (i = 0; i < numdisks; i++) { 957 if (disks[i].dd_no_part) { 958 any_wedges = true; 959 wedge_menu[wno].opt_name = 960 disks[i].dd_descr; 961 disks[i].dd_descr[max_desc_len] = 0; 962 wedge_menu[wno].opt_flags = OPT_EXIT; 963 wedge_menu[wno].opt_action = 964 set_menu_select; 965 wedge_no[wno] = i; 966 wno++; 967 } else { 968 dsk_menu[dno].opt_name = 969 disks[i].dd_descr; 970 disks[i].dd_descr[max_desc_len] = 0; 971 dsk_menu[dno].opt_flags = OPT_EXIT; 972 dsk_menu[dno].opt_action = 973 set_menu_select; 974 disk_no[dno] = i; 975 dno++; 976 } 977 } 978 if (any_wedges) { 979 dsk_menu[dno].opt_name = MSG_selectwedge; 980 dsk_menu[dno].opt_flags = OPT_EXIT; 981 dsk_menu[dno].opt_action = set_menu_select; 982 disk_no[dno] = -2; 983 dno++; 984 } 985 if (partman_go < 0) { 986 dsk_menu[dno].opt_name = MSG_partman; 987 dsk_menu[dno].opt_flags = OPT_EXIT; 988 dsk_menu[dno].opt_action = set_menu_select; 989 disk_no[dno] = -3; 990 dno++; 991 } 992 w_menu_no = -1; 993 menu_no = new_menu(MSG_Available_disks, 994 dsk_menu, dno, -1, 995 7, 0, 0, MC_SCROLL, 996 NULL, NULL, NULL, NULL, MSG_exit_menu_generic); 997 if (menu_no == -1) 998 return -1; 999 for (;;) { 1000 msg_fmt_display(MSG_ask_disk, "%s", doingwhat); 1001 i = -1; 1002 process_menu(menu_no, &i); 1003 if (i == -1) 1004 return -1; 1005 if (disk_no[i] == -2) { 1006 /* do wedges menu */ 1007 if (w_menu_no == -1) { 1008 w_menu_no = new_menu( 1009 MSG_Available_wedges, 1010 wedge_menu, wno, -1, 1011 4, 0, 0, MC_SCROLL, 1012 NULL, NULL, NULL, NULL, 1013 MSG_exit_menu_generic); 1014 if (w_menu_no == -1) { 1015 selected_disk = -1; 1016 break; 1017 } 1018 } 1019 i = -1; 1020 process_menu(w_menu_no, &i); 1021 if (i == -1) 1022 continue; 1023 selected_disk = wedge_no[i]; 1024 break; 1025 } 1026 selected_disk = disk_no[i]; 1027 break; 1028 } 1029 if (w_menu_no >= 0) 1030 free_menu(w_menu_no); 1031 free_menu(menu_no); 1032 if (allow_cur_system && selected_disk == -1) { 1033 pm = dummy_whole_system_pm(); 1034 return 1; 1035 } 1036 } 1037 if (partman_go < 0 && selected_disk == -3) { 1038 partman_go = 1; 1039 return -2; 1040 } else 1041 partman_go = 0; 1042 if (selected_disk < 0 || selected_disk < 0 1043 || selected_disk >= numdisks) 1044 return -1; 1045 } 1046 1047 /* Fill pm struct with device(s) info */ 1048 for (i = 0; i < numdisks; i++) { 1049 if (! partman_go) 1050 disk = disks + selected_disk; 1051 else { 1052 disk = disks + i; 1053 already_found = 0; 1054 SLIST_FOREACH(pm_i, &pm_head, l) { 1055 pm_last = pm_i; 1056 if (strcmp(pm_i->diskdev, disk->dd_name) == 0) { 1057 already_found = 1; 1058 break; 1059 } 1060 } 1061 if (pm_i != NULL && already_found) { 1062 /* 1063 * We already added this device, but 1064 * partitions might have changed 1065 */ 1066 if (!pm_i->found) { 1067 pm_i->found = true; 1068 if (pm_i->parts == NULL) { 1069 pm_i->parts = 1070 partitions_read_disk( 1071 pm_i->diskdev, 1072 disk->dd_totsec, 1073 disk->dd_secsize, 1074 disk->dd_no_mbr); 1075 } 1076 } 1077 continue; 1078 } 1079 } 1080 pm = pm_new; 1081 pm->found = 1; 1082 pm->ptstart = 0; 1083 pm->ptsize = 0; 1084 strlcpy(pm->diskdev, disk->dd_name, sizeof pm->diskdev); 1085 strlcpy(pm->diskdev_descr, disk->dd_descr, sizeof pm->diskdev_descr); 1086 /* Use as a default disk if the user has the sets on a local disk */ 1087 strlcpy(localfs_dev, disk->dd_name, sizeof localfs_dev); 1088 1089 /* 1090 * Init disk size and geometry 1091 */ 1092 pm->sectorsize = disk->dd_secsize; 1093 pm->dlcyl = disk->dd_cyl; 1094 pm->dlhead = disk->dd_head; 1095 pm->dlsec = disk->dd_sec; 1096 pm->dlsize = disk->dd_totsec; 1097 if (pm->dlsize == 0) 1098 pm->dlsize = 1099 disk->dd_cyl * disk->dd_head * disk->dd_sec; 1100 1101 pm->parts = partitions_read_disk(pm->diskdev, 1102 pm->dlsize, disk->dd_secsize, disk->dd_no_mbr); 1103 1104 again: 1105 1106 #ifdef DEBUG_VERBOSE 1107 if (pm->parts) { 1108 fputs("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", stderr); 1109 dump_parts(pm->parts); 1110 1111 if (pm->parts->pscheme->secondary_partitions) { 1112 const struct disk_partitions *sparts = 1113 pm->parts->pscheme->secondary_partitions( 1114 pm->parts, pm->ptstart, false); 1115 if (sparts != NULL) 1116 dump_parts(sparts); 1117 } 1118 } 1119 #endif 1120 1121 pm->no_mbr = disk->dd_no_mbr; 1122 pm->no_part = disk->dd_no_part; 1123 if (!pm->no_part) { 1124 pm->sectorsize = disk->dd_secsize; 1125 pm->dlcyl = disk->dd_cyl; 1126 pm->dlhead = disk->dd_head; 1127 pm->dlsec = disk->dd_sec; 1128 pm->dlsize = disk->dd_totsec; 1129 if (pm->dlsize == 0) 1130 pm->dlsize = 1131 disk->dd_cyl * disk->dd_head * disk->dd_sec; 1132 1133 if (pm->parts && pm->parts->pscheme->size_limit != 0 1134 && pm->dlsize > pm->parts->pscheme->size_limit 1135 && ! partman_go) { 1136 1137 char size[5], limit[5]; 1138 1139 humanize_number(size, sizeof(size), 1140 (uint64_t)pm->dlsize * pm->sectorsize, 1141 "", HN_AUTOSCALE, HN_B | HN_NOSPACE 1142 | HN_DECIMAL); 1143 1144 humanize_number(limit, sizeof(limit), 1145 (uint64_t)pm->parts->pscheme->size_limit 1146 * 512U, 1147 "", HN_AUTOSCALE, HN_B | HN_NOSPACE 1148 | HN_DECIMAL); 1149 1150 if (logfp) 1151 fprintf(logfp, 1152 "disk %s: is too big (%" PRIu64 1153 " blocks, %s), will be truncated\n", 1154 pm->diskdev, pm->dlsize, 1155 size); 1156 1157 msg_display_subst(MSG_toobigdisklabel, 5, 1158 pm->diskdev, 1159 msg_string(pm->parts->pscheme->name), 1160 msg_string(pm->parts->pscheme->short_name), 1161 size, limit); 1162 1163 int sel = -1; 1164 const char *err = NULL; 1165 process_menu(MENU_convertscheme, &sel); 1166 if (sel == 1) { 1167 if (!delete_scheme(pm)) { 1168 return -1; 1169 } 1170 goto again; 1171 } else if (sel == 2) { 1172 if (!convert_scheme(pm, 1173 partman_go < 0, &err)) { 1174 if (err != NULL) 1175 err_msg_win(err); 1176 return -1; 1177 } 1178 goto again; 1179 } else if (sel == 3) { 1180 return -1; 1181 } 1182 pm->dlsize = pm->parts->pscheme->size_limit; 1183 } 1184 } else { 1185 pm->sectorsize = 0; 1186 pm->dlcyl = 0; 1187 pm->dlhead = 0; 1188 pm->dlsec = 0; 1189 pm->dlsize = 0; 1190 pm->no_mbr = 1; 1191 } 1192 pm->dlcylsize = pm->dlhead * pm->dlsec; 1193 1194 if (partman_go) { 1195 pm_getrefdev(pm_new); 1196 if (SLIST_EMPTY(&pm_head) || pm_last == NULL) 1197 SLIST_INSERT_HEAD(&pm_head, pm_new, l); 1198 else 1199 SLIST_INSERT_AFTER(pm_last, pm_new, l); 1200 pm_new = malloc(sizeof (struct pm_devs)); 1201 memset(pm_new, 0, sizeof *pm_new); 1202 } else 1203 /* We are not in partman and do not want to process 1204 * all devices, exit */ 1205 break; 1206 } 1207 1208 return numdisks-skipped; 1209 } 1210 1211 static int 1212 sort_part_usage_by_mount(const void *a, const void *b) 1213 { 1214 const struct part_usage_info *pa = a, *pb = b; 1215 1216 /* sort all real partitions by mount point */ 1217 if ((pa->instflags & PUIINST_MOUNT) && 1218 (pb->instflags & PUIINST_MOUNT)) 1219 return strcmp(pa->mount, pb->mount); 1220 1221 /* real partitions go first */ 1222 if (pa->instflags & PUIINST_MOUNT) 1223 return -1; 1224 if (pb->instflags & PUIINST_MOUNT) 1225 return 1; 1226 1227 /* arbitrary order for all other partitions */ 1228 if (pa->type == PT_swap) 1229 return -1; 1230 if (pb->type == PT_swap) 1231 return 1; 1232 if (pa->type < pb->type) 1233 return -1; 1234 if (pa->type > pb->type) 1235 return 1; 1236 if (pa->cur_part_id < pb->cur_part_id) 1237 return -1; 1238 if (pa->cur_part_id > pb->cur_part_id) 1239 return 1; 1240 return (uintptr_t)a < (uintptr_t)b ? -1 : 1; 1241 } 1242 1243 /* 1244 * Are we able to newfs this type of file system? 1245 * Keep in sync with switch labels below! 1246 */ 1247 bool 1248 can_newfs_fstype(unsigned int t) 1249 { 1250 switch (t) { 1251 case FS_APPLEUFS: 1252 case FS_BSDFFS: 1253 case FS_BSDLFS: 1254 case FS_MSDOS: 1255 case FS_EFI_SP: 1256 case FS_SYSVBFS: 1257 case FS_V7: 1258 case FS_EX2FS: 1259 return true; 1260 } 1261 return false; 1262 } 1263 1264 int 1265 make_filesystems(struct install_partition_desc *install) 1266 { 1267 int error = 0, partno = -1; 1268 char *newfs = NULL, devdev[PATH_MAX], rdev[PATH_MAX], 1269 opts[200], opt[30]; 1270 size_t i; 1271 struct part_usage_info *ptn; 1272 struct disk_partitions *parts; 1273 const char *mnt_opts = NULL, *fsname = NULL; 1274 1275 if (pm->cur_system) 1276 return 1; 1277 1278 if (pm->no_part) { 1279 /* check if this target device already has a ffs */ 1280 snprintf(rdev, sizeof rdev, _PATH_DEV "/r%s", pm->diskdev); 1281 error = fsck_preen(rdev, "ffs", true); 1282 if (error) { 1283 if (!ask_noyes(MSG_No_filesystem_newfs)) 1284 return EINVAL; 1285 error = run_program(RUN_DISPLAY | RUN_PROGRESS, 1286 "/sbin/newfs -V2 -O2ea %s", rdev); 1287 } 1288 1289 md_pre_mount(install, 0); 1290 1291 make_target_dir("/"); 1292 1293 snprintf(devdev, sizeof devdev, _PATH_DEV "%s", pm->diskdev); 1294 error = target_mount_do("-o async", devdev, "/"); 1295 if (error) { 1296 msg_display_subst(MSG_mountfail, 2, devdev, "/"); 1297 hit_enter_to_continue(NULL, NULL); 1298 } 1299 1300 return error; 1301 } 1302 1303 /* Making new file systems and mounting them */ 1304 1305 /* sort to ensure /usr/local is mounted after /usr (etc) */ 1306 qsort(install->infos, install->num, sizeof(*install->infos), 1307 sort_part_usage_by_mount); 1308 1309 for (i = 0; i < install->num; i++) { 1310 /* 1311 * Newfs all file systems marked as needing this. 1312 * Mount the ones that have a mountpoint in the target. 1313 */ 1314 ptn = &install->infos[i]; 1315 parts = ptn->parts; 1316 newfs = NULL; 1317 fsname = NULL; 1318 1319 if (ptn->size == 0 || parts == NULL|| ptn->type == PT_swap) 1320 continue; 1321 1322 if (parts->pscheme->get_part_device(parts, ptn->cur_part_id, 1323 devdev, sizeof devdev, &partno, parent_device_only, false, 1324 false) && is_active_rootpart(devdev, partno)) 1325 continue; 1326 1327 parts->pscheme->get_part_device(parts, ptn->cur_part_id, 1328 devdev, sizeof devdev, &partno, plain_name, true, true); 1329 1330 parts->pscheme->get_part_device(parts, ptn->cur_part_id, 1331 rdev, sizeof rdev, &partno, raw_dev_name, true, true); 1332 1333 opts[0] = 0; 1334 switch (ptn->fs_type) { 1335 case FS_APPLEUFS: 1336 if (ptn->fs_opt3 != 0) 1337 snprintf(opts, sizeof opts, "-i %u", 1338 ptn->fs_opt3); 1339 asprintf(&newfs, "/sbin/newfs %s", opts); 1340 mnt_opts = "-tffs -o async"; 1341 fsname = "ffs"; 1342 break; 1343 case FS_BSDFFS: 1344 if (ptn->fs_opt3 != 0) 1345 snprintf(opts, sizeof opts, "-i %u ", 1346 ptn->fs_opt3); 1347 if (ptn->fs_opt1 != 0) { 1348 snprintf(opt, sizeof opt, "-b %u ", 1349 ptn->fs_opt1); 1350 strcat(opts, opt); 1351 } 1352 if (ptn->fs_opt2 != 0) { 1353 snprintf(opt, sizeof opt, "-f %u ", 1354 ptn->fs_opt2); 1355 strcat(opts, opt); 1356 } 1357 const char *ffs_fmt; 1358 switch (ptn->fs_version) { 1359 case 3: ffs_fmt = "2ea"; break; 1360 case 2: ffs_fmt = "2"; break; 1361 case 1: 1362 default: ffs_fmt = "1"; break; 1363 } 1364 asprintf(&newfs, 1365 "/sbin/newfs -V2 -O %s %s", 1366 ffs_fmt, opts); 1367 if (ptn->mountflags & PUIMNT_LOG) 1368 mnt_opts = "-tffs -o log"; 1369 else 1370 mnt_opts = "-tffs -o async"; 1371 fsname = "ffs"; 1372 break; 1373 case FS_BSDLFS: 1374 if (ptn->fs_opt1 != 0 && ptn->fs_opt2 != 0) 1375 snprintf(opts, sizeof opts, "-b %u", 1376 ptn->fs_opt1 * ptn->fs_opt2); 1377 asprintf(&newfs, "/sbin/newfs_lfs %s", opts); 1378 mnt_opts = "-tlfs"; 1379 fsname = "lfs"; 1380 break; 1381 case FS_MSDOS: 1382 case FS_EFI_SP: 1383 asprintf(&newfs, "/sbin/newfs_msdos"); 1384 mnt_opts = "-tmsdos"; 1385 fsname = "msdos"; 1386 break; 1387 case FS_SYSVBFS: 1388 asprintf(&newfs, "/sbin/newfs_sysvbfs"); 1389 mnt_opts = "-tsysvbfs"; 1390 fsname = "sysvbfs"; 1391 break; 1392 case FS_V7: 1393 asprintf(&newfs, "/sbin/newfs_v7fs"); 1394 mnt_opts = "-tv7fs"; 1395 fsname = "v7fs"; 1396 break; 1397 case FS_EX2FS: 1398 asprintf(&newfs, 1399 ptn->fs_version == 1 ? 1400 "/sbin/newfs_ext2fs -O 0" : 1401 "/sbin/newfs_ext2fs"); 1402 mnt_opts = "-text2fs"; 1403 fsname = "ext2fs"; 1404 break; 1405 } 1406 if ((ptn->instflags & PUIINST_NEWFS) && newfs != NULL) { 1407 error = run_program(RUN_DISPLAY | RUN_PROGRESS, 1408 "%s %s", newfs, rdev); 1409 } else if ((ptn->instflags & (PUIINST_MOUNT|PUIINST_BOOT)) 1410 && fsname != NULL) { 1411 /* We'd better check it isn't dirty */ 1412 error = fsck_preen(devdev, fsname, false); 1413 } 1414 free(newfs); 1415 if (error != 0) 1416 return error; 1417 1418 ptn->instflags &= ~PUIINST_NEWFS; 1419 md_pre_mount(install, i); 1420 1421 if (partman_go == 0 && (ptn->instflags & PUIINST_MOUNT) && 1422 mnt_opts != NULL) { 1423 make_target_dir(ptn->mount); 1424 error = target_mount_do(mnt_opts, devdev, 1425 ptn->mount); 1426 if (error) { 1427 msg_display_subst(MSG_mountfail, 2, devdev, 1428 ptn->mount); 1429 hit_enter_to_continue(NULL, NULL); 1430 return error; 1431 } 1432 } 1433 } 1434 return 0; 1435 } 1436 1437 int 1438 make_fstab(struct install_partition_desc *install) 1439 { 1440 FILE *f; 1441 const char *dump_dev = NULL; 1442 const char *dev; 1443 char dev_buf[PATH_MAX], swap_dev[PATH_MAX]; 1444 1445 if (pm->cur_system) 1446 return 1; 1447 1448 swap_dev[0] = 0; 1449 1450 /* Create the fstab. */ 1451 make_target_dir("/etc"); 1452 f = target_fopen("/etc/fstab", "w"); 1453 scripting_fprintf(NULL, "cat <<EOF >%s/etc/fstab\n", target_prefix()); 1454 1455 if (logfp) 1456 (void)fprintf(logfp, 1457 "Making %s/etc/fstab (%s).\n", target_prefix(), 1458 pm->diskdev); 1459 1460 if (f == NULL) { 1461 msg_display(MSG_createfstab); 1462 if (logfp) 1463 (void)fprintf(logfp, "Failed to make /etc/fstab!\n"); 1464 hit_enter_to_continue(NULL, NULL); 1465 #ifndef DEBUG 1466 return 1; 1467 #else 1468 f = stdout; 1469 #endif 1470 } 1471 1472 scripting_fprintf(f, "# NetBSD /etc/fstab\n# See /usr/share/examples/" 1473 "fstab/ for more examples.\n"); 1474 1475 if (pm->no_part) { 1476 /* single dk? target */ 1477 char buf[200], parent[200], swap[200], *prompt; 1478 int res; 1479 1480 if (!get_name_and_parent(pm->diskdev, buf, parent)) 1481 goto done_with_disks; 1482 scripting_fprintf(f, NAME_PREFIX "%s\t/\tffs\trw\t\t1 1\n", 1483 buf); 1484 if (!find_swap_part_on(parent, swap)) 1485 goto done_with_disks; 1486 const char *args[] = { parent, swap }; 1487 prompt = str_arg_subst(msg_string(MSG_Auto_add_swap_part), 1488 __arraycount(args), args); 1489 res = ask_yesno(prompt); 1490 free(prompt); 1491 if (res) 1492 scripting_fprintf(f, NAME_PREFIX "%s\tnone" 1493 "\tswap\tsw,dp\t\t0 0\n", swap); 1494 goto done_with_disks; 1495 } 1496 1497 for (size_t i = 0; i < install->num; i++) { 1498 1499 const struct part_usage_info *ptn = &install->infos[i]; 1500 1501 if (ptn->size == 0) 1502 continue; 1503 1504 bool is_tmpfs = ptn->type == PT_root && 1505 ptn->fs_type == FS_TMPFS && 1506 (ptn->flags & PUIFLG_JUST_MOUNTPOINT); 1507 1508 if (!is_tmpfs && ptn->type != PT_swap && 1509 (ptn->instflags & PUIINST_MOUNT) == 0) 1510 continue; 1511 1512 const char *s = ""; 1513 const char *mp = ptn->mount; 1514 const char *fstype = "ffs"; 1515 int fsck_pass = 0, dump_freq = 0; 1516 1517 if (ptn->parts->pscheme->get_part_device(ptn->parts, 1518 ptn->cur_part_id, dev_buf, sizeof dev_buf, NULL, 1519 logical_name, true, false)) 1520 dev = dev_buf; 1521 else 1522 dev = NULL; 1523 1524 if (!*mp) { 1525 /* 1526 * No mount point specified, comment out line and 1527 * use /mnt as a placeholder for the mount point. 1528 */ 1529 s = "# "; 1530 mp = "/mnt"; 1531 } 1532 1533 switch (ptn->fs_type) { 1534 case FS_UNUSED: 1535 continue; 1536 case FS_BSDLFS: 1537 /* If there is no LFS, just comment it out. */ 1538 if (!check_lfs_progs()) 1539 s = "# "; 1540 fstype = "lfs"; 1541 /* FALLTHROUGH */ 1542 case FS_BSDFFS: 1543 fsck_pass = (strcmp(mp, "/") == 0) ? 1 : 2; 1544 dump_freq = 1; 1545 break; 1546 case FS_MSDOS: 1547 fstype = "msdos"; 1548 break; 1549 case FS_SWAP: 1550 if (swap_dev[0] == 0) { 1551 strlcpy(swap_dev, dev, sizeof swap_dev); 1552 dump_dev = ",dp"; 1553 } else { 1554 dump_dev = ""; 1555 } 1556 scripting_fprintf(f, "%s\t\tnone\tswap\tsw%s\t\t 0 0\n", 1557 dev, dump_dev); 1558 continue; 1559 #ifdef HAVE_TMPFS 1560 case FS_TMPFS: 1561 if (ptn->size < 0) 1562 scripting_fprintf(f, 1563 "tmpfs\t\t/tmp\ttmpfs\trw,-m1777," 1564 "-sram%%%" PRIu64 "\n", -ptn->size); 1565 else 1566 scripting_fprintf(f, 1567 "tmpfs\t\t/tmp\ttmpfs\trw,-m1777," 1568 "-s%" PRIu64 "M\n", ptn->size); 1569 continue; 1570 #else 1571 case FS_MFS: 1572 if (swap_dev[0] != 0) 1573 scripting_fprintf(f, 1574 "%s\t\t/tmp\tmfs\trw,-s=%" 1575 PRIu64 "\n", swap_dev, ptn->size); 1576 else 1577 scripting_fprintf(f, 1578 "swap\t\t/tmp\tmfs\trw,-s=%" 1579 PRIu64 "\n", ptn->size); 1580 continue; 1581 #endif 1582 case FS_SYSVBFS: 1583 fstype = "sysvbfs"; 1584 make_target_dir("/stand"); 1585 break; 1586 default: 1587 fstype = "???"; 1588 s = "# "; 1589 break; 1590 } 1591 /* The code that remounts root rw doesn't check the partition */ 1592 if (strcmp(mp, "/") == 0 && 1593 (ptn->instflags & PUIINST_MOUNT) == 0) 1594 s = "# "; 1595 1596 scripting_fprintf(f, 1597 "%s%s\t\t%s\t%s\trw%s%s%s%s%s%s%s%s\t\t %d %d\n", 1598 s, dev, mp, fstype, 1599 ptn->mountflags & PUIMNT_LOG ? ",log" : "", 1600 ptn->mountflags & PUIMNT_NOAUTO ? ",noauto" : "", 1601 ptn->mountflags & PUIMNT_ASYNC ? ",async" : "", 1602 ptn->mountflags & PUIMNT_NOATIME ? ",noatime" : "", 1603 ptn->mountflags & PUIMNT_NODEV ? ",nodev" : "", 1604 ptn->mountflags & PUIMNT_NODEVMTIME ? ",nodevmtime" : "", 1605 ptn->mountflags & PUIMNT_NOEXEC ? ",noexec" : "", 1606 ptn->mountflags & PUIMNT_NOSUID ? ",nosuid" : "", 1607 dump_freq, fsck_pass); 1608 } 1609 1610 done_with_disks: 1611 if (cdrom_dev[0] == 0) 1612 get_default_cdrom(cdrom_dev, sizeof(cdrom_dev)); 1613 1614 /* Add /kern, /proc and /dev/pts to fstab and make mountpoint. */ 1615 scripting_fprintf(f, "kernfs\t\t/kern\tkernfs\trw\n"); 1616 scripting_fprintf(f, "ptyfs\t\t/dev/pts\tptyfs\trw\n"); 1617 scripting_fprintf(f, "procfs\t\t/proc\tprocfs\trw\n"); 1618 if (cdrom_dev[0] != 0) 1619 scripting_fprintf(f, "/dev/%s\t\t/cdrom\tcd9660\tro,noauto\n", 1620 cdrom_dev); 1621 scripting_fprintf(f, "%stmpfs\t\t/var/shm\ttmpfs\trw,-m1777,-sram%%25\n", 1622 tmpfs_on_var_shm() ? "" : "#"); 1623 make_target_dir("/kern"); 1624 make_target_dir("/proc"); 1625 make_target_dir("/dev/pts"); 1626 if (cdrom_dev[0] != 0) 1627 make_target_dir("/cdrom"); 1628 make_target_dir("/var/shm"); 1629 1630 scripting_fprintf(NULL, "EOF\n"); 1631 1632 fclose(f); 1633 fflush(NULL); 1634 return 0; 1635 } 1636 1637 static bool 1638 find_part_by_name(const char *name, struct disk_partitions **parts, 1639 part_id *pno) 1640 { 1641 struct pm_devs *i; 1642 struct disk_partitions *ps; 1643 part_id id; 1644 struct disk_desc disks[MAX_DISKS]; 1645 int n, cnt; 1646 1647 if (SLIST_EMPTY(&pm_head)) { 1648 /* 1649 * List has not been filled, only "pm" is valid - check 1650 * that first. 1651 */ 1652 if (pm->parts != NULL && 1653 pm->parts->pscheme->find_by_name != NULL) { 1654 id = pm->parts->pscheme->find_by_name(pm->parts, name); 1655 if (id != NO_PART) { 1656 *pno = id; 1657 *parts = pm->parts; 1658 return true; 1659 } 1660 } 1661 /* 1662 * Not that easy - check all other disks 1663 */ 1664 cnt = get_disks(disks, false); 1665 for (n = 0; n < cnt; n++) { 1666 if (strcmp(disks[n].dd_name, pm->diskdev) == 0) 1667 continue; 1668 ps = partitions_read_disk(disks[n].dd_name, 1669 disks[n].dd_totsec, 1670 disks[n].dd_secsize, 1671 disks[n].dd_no_mbr); 1672 if (ps == NULL) 1673 continue; 1674 if (ps->pscheme->find_by_name == NULL) 1675 continue; 1676 id = ps->pscheme->find_by_name(ps, name); 1677 if (id != NO_PART) { 1678 *pno = id; 1679 *parts = ps; 1680 return true; /* XXX this leaks memory */ 1681 } 1682 ps->pscheme->free(ps); 1683 } 1684 } else { 1685 SLIST_FOREACH(i, &pm_head, l) { 1686 if (i->parts == NULL) 1687 continue; 1688 if (i->parts->pscheme->find_by_name == NULL) 1689 continue; 1690 id = i->parts->pscheme->find_by_name(i->parts, name); 1691 if (id == NO_PART) 1692 continue; 1693 *pno = id; 1694 *parts = i->parts; 1695 return true; 1696 } 1697 } 1698 1699 *pno = NO_PART; 1700 *parts = NULL; 1701 return false; 1702 } 1703 1704 static int 1705 /*ARGSUSED*/ 1706 process_found_fs(struct data *list, size_t num, const struct lookfor *item, 1707 bool with_fsck) 1708 { 1709 int error; 1710 char rdev[PATH_MAX], dev[PATH_MAX], 1711 options[STRSIZE], tmp[STRSIZE], *op, *last; 1712 const char *fsname = (const char*)item->var; 1713 part_id pno; 1714 struct disk_partitions *parts; 1715 size_t len; 1716 bool first, is_root; 1717 1718 if (num < 2 || strstr(list[2].u.s_val, "noauto") != NULL) 1719 return 0; 1720 1721 is_root = strcmp(list[1].u.s_val, "/") == 0; 1722 if (is_root && target_mounted()) 1723 return 0; 1724 1725 if (strcmp(item->head, name_prefix) == 0) { 1726 /* this fstab entry uses NAME= syntax */ 1727 1728 /* unescape */ 1729 char *src, *dst; 1730 for (src = list[0].u.s_val, dst =src; src[0] != 0; ) { 1731 if (src[0] == '\\' && src[1] != 0) 1732 src++; 1733 *dst++ = *src++; 1734 } 1735 *dst = 0; 1736 1737 if (!find_part_by_name(list[0].u.s_val, 1738 &parts, &pno) || parts == NULL || pno == NO_PART) 1739 return 0; 1740 parts->pscheme->get_part_device(parts, pno, 1741 dev, sizeof(dev), NULL, plain_name, true, true); 1742 parts->pscheme->get_part_device(parts, pno, 1743 rdev, sizeof(rdev), NULL, raw_dev_name, true, true); 1744 } else { 1745 /* this fstab entry uses the plain device name */ 1746 if (is_root) { 1747 /* 1748 * PR 54480: we can not use the current device name 1749 * as it might be different from the real environment. 1750 * This is an abuse of the functionality, but it used 1751 * to work before (and still does work if only a single 1752 * target disk is involved). 1753 * Use the device name from the current "pm" instead. 1754 */ 1755 strcpy(rdev, "/dev/r"); 1756 strlcat(rdev, pm->diskdev, sizeof(rdev)); 1757 strcpy(dev, "/dev/"); 1758 strlcat(dev, pm->diskdev, sizeof(dev)); 1759 /* copy over the partition letter, if any */ 1760 len = strlen(list[0].u.s_val); 1761 if (list[0].u.s_val[len-1] >= 'a' && 1762 list[0].u.s_val[len-1] <= 1763 ('a' + getmaxpartitions())) { 1764 strlcat(rdev, &list[0].u.s_val[len-1], 1765 sizeof(rdev)); 1766 strlcat(dev, &list[0].u.s_val[len-1], 1767 sizeof(dev)); 1768 } 1769 } else { 1770 strcpy(rdev, "/dev/r"); 1771 strlcat(rdev, list[0].u.s_val, sizeof(rdev)); 1772 strcpy(dev, "/dev/"); 1773 strlcat(dev, list[0].u.s_val, sizeof(dev)); 1774 } 1775 } 1776 1777 if (with_fsck) { 1778 /* need the raw device for fsck_preen */ 1779 error = fsck_preen(rdev, fsname, false); 1780 if (error != 0) 1781 return error; 1782 } 1783 1784 /* add mount option for fs type */ 1785 strcpy(options, "-t "); 1786 strlcat(options, fsname, sizeof(options)); 1787 1788 /* extract mount options from fstab */ 1789 strlcpy(tmp, list[2].u.s_val, sizeof(tmp)); 1790 for (first = true, op = strtok_r(tmp, ",", &last); op != NULL; 1791 op = strtok_r(NULL, ",", &last)) { 1792 if (strcmp(op, FSTAB_RW) == 0 || 1793 strcmp(op, FSTAB_RQ) == 0 || 1794 strcmp(op, FSTAB_RO) == 0 || 1795 strcmp(op, FSTAB_SW) == 0 || 1796 strcmp(op, FSTAB_DP) == 0 || 1797 strcmp(op, FSTAB_XX) == 0) 1798 continue; 1799 if (first) { 1800 first = false; 1801 strlcat(options, " -o ", sizeof(options)); 1802 } else { 1803 strlcat(options, ",", sizeof(options)); 1804 } 1805 strlcat(options, op, sizeof(options)); 1806 } 1807 1808 error = target_mount(options, dev, list[1].u.s_val); 1809 if (error != 0) { 1810 msg_fmt_display(MSG_mount_failed, "%s", list[0].u.s_val); 1811 if (!ask_noyes(NULL)) 1812 return error; 1813 } 1814 return 0; 1815 } 1816 1817 static int 1818 /*ARGSUSED*/ 1819 found_fs(struct data *list, size_t num, const struct lookfor *item) 1820 { 1821 return process_found_fs(list, num, item, true); 1822 } 1823 1824 static int 1825 /*ARGSUSED*/ 1826 found_fs_nocheck(struct data *list, size_t num, const struct lookfor *item) 1827 { 1828 return process_found_fs(list, num, item, false); 1829 } 1830 1831 /* 1832 * Do an fsck. On failure, inform the user by showing a warning 1833 * message and doing menu_ok() before proceeding. 1834 * The device passed should be the full qualified path to raw disk 1835 * (e.g. /dev/rwd0a). 1836 * Returns 0 on success, or nonzero return code from fsck() on failure. 1837 */ 1838 static int 1839 fsck_preen(const char *disk, const char *fsname, bool silent) 1840 { 1841 char *prog, err[12]; 1842 int error; 1843 1844 if (fsname == NULL) 1845 return 0; 1846 /* first, check if fsck program exists, if not, assume ok */ 1847 asprintf(&prog, "/sbin/fsck_%s", fsname); 1848 if (prog == NULL) 1849 return 0; 1850 if (access(prog, X_OK) != 0) { 1851 free(prog); 1852 return 0; 1853 } 1854 if (!strcmp(fsname,"ffs")) 1855 fixsb(prog, disk); 1856 error = run_program(silent? RUN_SILENT|RUN_ERROR_OK : 0, "%s -p -q %s", prog, disk); 1857 free(prog); 1858 if (error != 0 && !silent) { 1859 sprintf(err, "%d", error); 1860 msg_display_subst(msg_string(MSG_badfs), 3, 1861 disk, fsname, err); 1862 if (ask_noyes(NULL)) 1863 error = 0; 1864 /* XXX at this point maybe we should run a full fsck? */ 1865 } 1866 return error; 1867 } 1868 1869 /* This performs the same function as the etc/rc.d/fixsb script 1870 * which attempts to correct problems with ffs1 filesystems 1871 * which may have been introduced by booting a netbsd-current kernel 1872 * from between April of 2003 and January 2004. For more information 1873 * This script was developed as a response to NetBSD pr install/25138 1874 * Additional prs regarding the original issue include: 1875 * bin/17910 kern/21283 kern/21404 port-macppc/23925 port-macppc/23926 1876 */ 1877 static void 1878 fixsb(const char *prog, const char *disk) 1879 { 1880 int fd; 1881 int rval; 1882 union { 1883 struct fs fs; 1884 char buf[SBLOCKSIZE]; 1885 } sblk; 1886 struct fs *fs = &sblk.fs; 1887 1888 fd = open(disk, O_RDONLY); 1889 if (fd == -1) 1890 return; 1891 1892 /* Read ffsv1 main superblock */ 1893 rval = pread(fd, sblk.buf, sizeof sblk.buf, SBLOCK_UFS1); 1894 close(fd); 1895 if (rval != sizeof sblk.buf) 1896 return; 1897 1898 if (fs->fs_magic != FS_UFS1_MAGIC && 1899 fs->fs_magic != FS_UFS1_MAGIC_SWAPPED) 1900 /* Not FFSv1 */ 1901 return; 1902 if (fs->fs_old_flags & FS_FLAGS_UPDATED) 1903 /* properly updated fslevel 4 */ 1904 return; 1905 if (fs->fs_bsize != fs->fs_maxbsize) 1906 /* not messed up */ 1907 return; 1908 1909 /* 1910 * OK we have a munged fs, first 'upgrade' to fslevel 4, 1911 * We specify -b16 in order to stop fsck bleating that the 1912 * sb doesn't match the first alternate. 1913 */ 1914 run_program(RUN_DISPLAY | RUN_PROGRESS, 1915 "%s -p -b 16 -c 4 %s", prog, disk); 1916 /* Then downgrade to fslevel 3 */ 1917 run_program(RUN_DISPLAY | RUN_PROGRESS, 1918 "%s -p -c 3 %s", prog, disk); 1919 } 1920 1921 /* 1922 * fsck and mount the root partition. 1923 * devdev is the fully qualified block device name. 1924 */ 1925 static int 1926 mount_root(const char *devdev, bool first, bool writeable, 1927 struct install_partition_desc *install) 1928 { 1929 int error; 1930 1931 error = fsck_preen(devdev, "ffs", false); 1932 if (error != 0) 1933 return error; 1934 1935 if (first) 1936 md_pre_mount(install, 0); 1937 1938 /* Mount devdev on target's "". 1939 * If we pass "" as mount-on, Prefixing will DTRT. 1940 * for now, use no options. 1941 * XXX consider -o remount in case target root is 1942 * current root, still readonly from single-user? 1943 */ 1944 return target_mount(writeable? "" : "-r", devdev, ""); 1945 } 1946 1947 /* Get information on the file systems mounted from the root filesystem. 1948 * Offer to convert them into 4.4BSD inodes if they are not 4.4BSD 1949 * inodes. Fsck them. Mount them. 1950 */ 1951 1952 int 1953 mount_disks(struct install_partition_desc *install) 1954 { 1955 char *fstab; 1956 int fstabsize; 1957 int error; 1958 char devdev[PATH_MAX]; 1959 size_t i, num_fs_types, num_entries; 1960 struct lookfor *fstabbuf, *l; 1961 1962 if (install->cur_system) 1963 return 0; 1964 1965 /* 1966 * Check what file system tools are available and create parsers 1967 * for the corresponding fstab(5) entries - all others will be 1968 * ignored. 1969 */ 1970 num_fs_types = 1; /* ffs is implicit */ 1971 for (i = 0; i < __arraycount(extern_fs_with_chk); i++) { 1972 sprintf(devdev, "/sbin/newfs_%s", extern_fs_with_chk[i]); 1973 if (file_exists_p(devdev)) 1974 num_fs_types++; 1975 } 1976 for (i = 0; i < __arraycount(extern_fs_newfs_only); i++) { 1977 sprintf(devdev, "/sbin/newfs_%s", extern_fs_newfs_only[i]); 1978 if (file_exists_p(devdev)) 1979 num_fs_types++; 1980 } 1981 num_entries = 2 * num_fs_types + 1; /* +1 for "ufs" special case */ 1982 fstabbuf = calloc(num_entries, sizeof(*fstabbuf)); 1983 if (fstabbuf == NULL) 1984 return -1; 1985 l = fstabbuf; 1986 l->head = "/dev/"; 1987 l->fmt = strdup("/dev/%s %s ffs %s"); 1988 l->todo = "c"; 1989 l->var = __UNCONST("ffs"); 1990 l->func = found_fs; 1991 l++; 1992 l->head = "/dev/"; 1993 l->fmt = strdup("/dev/%s %s ufs %s"); 1994 l->todo = "c"; 1995 l->var = __UNCONST("ffs"); 1996 l->func = found_fs; 1997 l++; 1998 l->head = NAME_PREFIX; 1999 l->fmt = strdup(NAME_PREFIX "%s %s ffs %s"); 2000 l->todo = "c"; 2001 l->var = __UNCONST("ffs"); 2002 l->func = found_fs; 2003 l++; 2004 for (i = 0; i < __arraycount(extern_fs_with_chk); i++) { 2005 sprintf(devdev, "/sbin/newfs_%s", extern_fs_with_chk[i]); 2006 if (!file_exists_p(devdev)) 2007 continue; 2008 sprintf(devdev, "/dev/%%s %%s %s %%s", extern_fs_with_chk[i]); 2009 l->head = "/dev/"; 2010 l->fmt = strdup(devdev); 2011 l->todo = "c"; 2012 l->var = __UNCONST(extern_fs_with_chk[i]); 2013 l->func = found_fs; 2014 l++; 2015 sprintf(devdev, NAME_PREFIX "%%s %%s %s %%s", 2016 extern_fs_with_chk[i]); 2017 l->head = NAME_PREFIX; 2018 l->fmt = strdup(devdev); 2019 l->todo = "c"; 2020 l->var = __UNCONST(extern_fs_with_chk[i]); 2021 l->func = found_fs; 2022 l++; 2023 } 2024 for (i = 0; i < __arraycount(extern_fs_newfs_only); i++) { 2025 sprintf(devdev, "/sbin/newfs_%s", extern_fs_newfs_only[i]); 2026 if (!file_exists_p(devdev)) 2027 continue; 2028 sprintf(devdev, "/dev/%%s %%s %s %%s", extern_fs_newfs_only[i]); 2029 l->head = "/dev/"; 2030 l->fmt = strdup(devdev); 2031 l->todo = "c"; 2032 l->var = __UNCONST(extern_fs_newfs_only[i]); 2033 l->func = found_fs_nocheck; 2034 l++; 2035 sprintf(devdev, NAME_PREFIX "%%s %%s %s %%s", 2036 extern_fs_newfs_only[i]); 2037 l->head = NAME_PREFIX; 2038 l->fmt = strdup(devdev); 2039 l->todo = "c"; 2040 l->var = __UNCONST(extern_fs_newfs_only[i]); 2041 l->func = found_fs_nocheck; 2042 l++; 2043 } 2044 assert((size_t)(l - fstabbuf) == num_entries); 2045 2046 /* First the root device. */ 2047 if (target_already_root()) { 2048 /* avoid needing to call target_already_root() again */ 2049 targetroot_mnt[0] = 0; 2050 } else if (pm->no_part) { 2051 snprintf(devdev, sizeof devdev, _PATH_DEV "%s", pm->diskdev); 2052 error = mount_root(devdev, true, false, install); 2053 if (error != 0 && error != EBUSY) 2054 return -1; 2055 } else { 2056 for (i = 0; i < install->num; i++) { 2057 if (is_root_part_mount(install->infos[i].mount)) 2058 break; 2059 } 2060 2061 if (i >= install->num) { 2062 hit_enter_to_continue(MSG_noroot, NULL); 2063 return -1; 2064 } 2065 2066 if (!install->infos[i].parts->pscheme->get_part_device( 2067 install->infos[i].parts, install->infos[i].cur_part_id, 2068 devdev, sizeof devdev, NULL, plain_name, true, true)) 2069 return -1; 2070 error = mount_root(devdev, true, false, install); 2071 if (error != 0 && error != EBUSY) 2072 return -1; 2073 } 2074 2075 /* Check the target /etc/fstab exists before trying to parse it. */ 2076 if (target_dir_exists_p("/etc") == 0 || 2077 target_file_exists_p("/etc/fstab") == 0) { 2078 msg_fmt_display(MSG_noetcfstab, "%s", pm->diskdev); 2079 hit_enter_to_continue(NULL, NULL); 2080 return -1; 2081 } 2082 2083 2084 /* Get fstab entries from the target-root /etc/fstab. */ 2085 fstabsize = target_collect_file(T_FILE, &fstab, "/etc/fstab"); 2086 if (fstabsize < 0) { 2087 /* error ! */ 2088 msg_fmt_display(MSG_badetcfstab, "%s", pm->diskdev); 2089 hit_enter_to_continue(NULL, NULL); 2090 umount_root(); 2091 return -2; 2092 } 2093 /* 2094 * We unmount the read-only root again, so we can mount it 2095 * with proper options from /etc/fstab 2096 */ 2097 umount_root(); 2098 2099 /* 2100 * Now do all entries in /etc/fstab and mount them if required 2101 */ 2102 error = walk(fstab, (size_t)fstabsize, fstabbuf, num_entries); 2103 free(fstab); 2104 for (i = 0; i < num_entries; i++) 2105 free(__UNCONST(fstabbuf[i].fmt)); 2106 free(fstabbuf); 2107 2108 return error; 2109 } 2110 2111 static char swap_dev[PATH_MAX]; 2112 2113 void 2114 set_swap_if_low_ram(struct install_partition_desc *install) 2115 { 2116 swap_dev[0] = 0; 2117 if (get_ramsize() <= TINY_RAM_SIZE) 2118 set_swap(install); 2119 } 2120 2121 void 2122 set_swap(struct install_partition_desc *install) 2123 { 2124 size_t i; 2125 int rval; 2126 2127 swap_dev[0] = 0; 2128 for (i = 0; i < install->num; i++) { 2129 if (install->infos[i].type == PT_swap) 2130 break; 2131 } 2132 if (i >= install->num) 2133 return; 2134 2135 if (!install->infos[i].parts->pscheme->get_part_device( 2136 install->infos[i].parts, install->infos[i].cur_part_id, swap_dev, 2137 sizeof swap_dev, NULL, plain_name, true, true)) 2138 return; 2139 2140 rval = swapctl(SWAP_ON, swap_dev, 0); 2141 if (rval != 0) 2142 swap_dev[0] = 0; 2143 } 2144 2145 void 2146 clear_swap(void) 2147 { 2148 2149 if (swap_dev[0] == 0) 2150 return; 2151 swapctl(SWAP_OFF, swap_dev, 0); 2152 swap_dev[0] = 0; 2153 } 2154 2155 int 2156 check_swap(const char *disk, int remove_swap) 2157 { 2158 struct swapent *swap; 2159 char *cp; 2160 int nswap; 2161 int l; 2162 int rval = 0; 2163 2164 nswap = swapctl(SWAP_NSWAP, 0, 0); 2165 if (nswap <= 0) 2166 return 0; 2167 2168 swap = malloc(nswap * sizeof *swap); 2169 if (swap == NULL) 2170 return -1; 2171 2172 nswap = swapctl(SWAP_STATS, swap, nswap); 2173 if (nswap < 0) 2174 goto bad_swap; 2175 2176 l = strlen(disk); 2177 while (--nswap >= 0) { 2178 /* Should we check the se_dev or se_path? */ 2179 cp = swap[nswap].se_path; 2180 if (memcmp(cp, "/dev/", 5) != 0) 2181 continue; 2182 if (memcmp(cp + 5, disk, l) != 0) 2183 continue; 2184 if (!isalpha(*(unsigned char *)(cp + 5 + l))) 2185 continue; 2186 if (cp[5 + l + 1] != 0) 2187 continue; 2188 /* ok path looks like it is for this device */ 2189 if (!remove_swap) { 2190 /* count active swap areas */ 2191 rval++; 2192 continue; 2193 } 2194 if (swapctl(SWAP_OFF, cp, 0) == -1) 2195 rval = -1; 2196 } 2197 2198 done: 2199 free(swap); 2200 return rval; 2201 2202 bad_swap: 2203 rval = -1; 2204 goto done; 2205 } 2206 2207 #ifdef HAVE_BOOTXX_xFS 2208 char * 2209 bootxx_name(struct install_partition_desc *install) 2210 { 2211 size_t i; 2212 int fstype = -1; 2213 const char *bootxxname; 2214 char *bootxx; 2215 2216 /* find a partition to be mounted as / */ 2217 for (i = 0; i < install->num; i++) { 2218 if ((install->infos[i].instflags & PUIINST_MOUNT) 2219 && strcmp(install->infos[i].mount, "/") == 0) { 2220 fstype = install->infos[i].fs_type; 2221 break; 2222 } 2223 } 2224 if (fstype < 0) { 2225 /* not found? take first root type partition instead */ 2226 for (i = 0; i < install->num; i++) { 2227 if (install->infos[i].type == PT_root) { 2228 fstype = install->infos[i].fs_type; 2229 break; 2230 } 2231 } 2232 } 2233 2234 /* check we have boot code for the root partition type */ 2235 switch (fstype) { 2236 #if defined(BOOTXX_FFSV1) || defined(BOOTXX_FFSV2) 2237 case FS_BSDFFS: 2238 if (install->infos[i].fs_version >= 2) { 2239 #ifdef BOOTXX_FFSV2 2240 bootxxname = BOOTXX_FFSV2; 2241 #else 2242 bootxxname = NULL; 2243 #endif 2244 } else { 2245 #ifdef BOOTXX_FFSV1 2246 bootxxname = BOOTXX_FFSV1; 2247 #else 2248 bootxxname = NULL; 2249 #endif 2250 } 2251 break; 2252 #endif 2253 #ifdef BOOTXX_LFSV2 2254 case FS_BSDLFS: 2255 bootxxname = BOOTXX_LFSV2; 2256 break; 2257 #endif 2258 default: 2259 bootxxname = NULL; 2260 break; 2261 } 2262 2263 if (bootxxname == NULL) 2264 return NULL; 2265 2266 asprintf(&bootxx, "%s/%s", BOOTXXDIR, bootxxname); 2267 return bootxx; 2268 } 2269 #endif 2270 2271 /* from dkctl.c */ 2272 static int 2273 get_dkwedges_sort(const void *a, const void *b) 2274 { 2275 const struct dkwedge_info *dkwa = a, *dkwb = b; 2276 const daddr_t oa = dkwa->dkw_offset, ob = dkwb->dkw_offset; 2277 return (oa < ob) ? -1 : (oa > ob) ? 1 : 0; 2278 } 2279 2280 int 2281 get_dkwedges(struct dkwedge_info **dkw, const char *diskdev) 2282 { 2283 struct dkwedge_list dkwl; 2284 2285 *dkw = NULL; 2286 if (!get_wedge_list(diskdev, &dkwl)) 2287 return -1; 2288 2289 if (dkwl.dkwl_nwedges > 0 && *dkw != NULL) { 2290 qsort(*dkw, dkwl.dkwl_nwedges, sizeof(**dkw), 2291 get_dkwedges_sort); 2292 } 2293 2294 return dkwl.dkwl_nwedges; 2295 } 2296 2297 #ifndef NO_CLONES 2298 /* 2299 * Helper structures used in the partition select menu 2300 */ 2301 struct single_partition { 2302 struct disk_partitions *parts; 2303 part_id id; 2304 }; 2305 2306 struct sel_menu_data { 2307 struct single_partition *partitions; 2308 struct selected_partition result; 2309 }; 2310 2311 static int 2312 select_single_part(menudesc *m, void *arg) 2313 { 2314 struct sel_menu_data *data = arg; 2315 2316 data->result.parts = data->partitions[m->cursel].parts; 2317 data->result.id = data->partitions[m->cursel].id; 2318 2319 return 1; 2320 } 2321 2322 static void 2323 display_single_part(menudesc *m, int opt, void *arg) 2324 { 2325 const struct sel_menu_data *data = arg; 2326 struct disk_part_info info; 2327 struct disk_partitions *parts = data->partitions[opt].parts; 2328 part_id id = data->partitions[opt].id; 2329 int l; 2330 const char *desc = NULL; 2331 char line[MENUSTRSIZE*2]; 2332 2333 if (!parts->pscheme->get_part_info(parts, id, &info)) 2334 return; 2335 2336 if (parts->pscheme->other_partition_identifier != NULL) 2337 desc = parts->pscheme->other_partition_identifier( 2338 parts, id); 2339 2340 daddr_t start = info.start / sizemult; 2341 daddr_t size = info.size / sizemult; 2342 snprintf(line, sizeof line, "%s [%" PRIu64 " @ %" PRIu64 "]", 2343 parts->disk, size, start); 2344 2345 if (info.nat_type != NULL) { 2346 strlcat(line, " ", sizeof line); 2347 strlcat(line, info.nat_type->description, sizeof line); 2348 } 2349 2350 if (desc != NULL) { 2351 strlcat(line, ": ", sizeof line); 2352 strlcat(line, desc, sizeof line); 2353 } 2354 2355 l = strlen(line); 2356 if (l >= (m->w)) 2357 strcpy(line + (m->w-3), "..."); 2358 wprintw(m->mw, "%s", line); 2359 } 2360 2361 /* 2362 * is the given "test" partitions set used in the selected set? 2363 */ 2364 static bool 2365 selection_has_parts(struct selected_partitions *sel, 2366 const struct disk_partitions *test) 2367 { 2368 size_t i; 2369 2370 for (i = 0; i < sel->num_sel; i++) { 2371 if (sel->selection[i].parts == test) 2372 return true; 2373 } 2374 return false; 2375 } 2376 2377 /* 2378 * is the given "test" partition in the selected set? 2379 */ 2380 static bool 2381 selection_has_partition(struct selected_partitions *sel, 2382 const struct disk_partitions *test, part_id test_id) 2383 { 2384 size_t i; 2385 2386 for (i = 0; i < sel->num_sel; i++) { 2387 if (sel->selection[i].parts == test && 2388 sel->selection[i].id == test_id) 2389 return true; 2390 } 2391 return false; 2392 } 2393 2394 /* 2395 * let the user select a partition, optionally skipping all partitions 2396 * on the "ignore" device 2397 */ 2398 static bool 2399 add_select_partition(struct selected_partitions *res, 2400 struct disk_partitions **all_parts, size_t all_cnt) 2401 { 2402 struct disk_partitions *ps; 2403 struct disk_part_info info; 2404 part_id id; 2405 struct single_partition *partitions, *pp; 2406 struct menu_ent *part_menu_opts, *menup; 2407 size_t n, part_cnt; 2408 int sel_menu; 2409 2410 /* 2411 * count how many items our menu will have 2412 */ 2413 part_cnt = 0; 2414 for (n = 0; n < all_cnt; n++) { 2415 ps = all_parts[n]; 2416 for (id = 0; id < ps->num_part; id++) { 2417 if (selection_has_partition(res, ps, id)) 2418 continue; 2419 if (!ps->pscheme->get_part_info(ps, id, &info)) 2420 continue; 2421 if (info.flags & (PTI_SEC_CONTAINER|PTI_WHOLE_DISK| 2422 PTI_PSCHEME_INTERNAL|PTI_RAW_PART)) 2423 continue; 2424 part_cnt++; 2425 } 2426 } 2427 2428 /* 2429 * create a menu from this and let the user 2430 * select one partition 2431 */ 2432 part_menu_opts = NULL; 2433 partitions = calloc(part_cnt, sizeof *partitions); 2434 if (partitions == NULL) 2435 goto done; 2436 part_menu_opts = calloc(part_cnt, sizeof *part_menu_opts); 2437 if (part_menu_opts == NULL) 2438 goto done; 2439 pp = partitions; 2440 menup = part_menu_opts; 2441 for (n = 0; n < all_cnt; n++) { 2442 ps = all_parts[n]; 2443 for (id = 0; id < ps->num_part; id++) { 2444 if (selection_has_partition(res, ps, id)) 2445 continue; 2446 if (!ps->pscheme->get_part_info(ps, id, &info)) 2447 continue; 2448 if (info.flags & (PTI_SEC_CONTAINER|PTI_WHOLE_DISK| 2449 PTI_PSCHEME_INTERNAL|PTI_RAW_PART)) 2450 continue; 2451 pp->parts = ps; 2452 pp->id = id; 2453 pp++; 2454 menup->opt_action = select_single_part; 2455 menup++; 2456 } 2457 } 2458 sel_menu = new_menu(MSG_select_foreign_part, part_menu_opts, part_cnt, 2459 3, 3, 0, 60, 2460 MC_SUBMENU | MC_SCROLL | MC_NOCLEAR, 2461 NULL, display_single_part, NULL, 2462 NULL, MSG_exit_menu_generic); 2463 if (sel_menu != -1) { 2464 struct selected_partition *newsels; 2465 struct sel_menu_data data; 2466 2467 memset(&data, 0, sizeof data); 2468 data.partitions = partitions; 2469 process_menu(sel_menu, &data); 2470 free_menu(sel_menu); 2471 2472 if (data.result.parts != NULL) { 2473 newsels = realloc(res->selection, 2474 sizeof(*res->selection)*(res->num_sel+1)); 2475 if (newsels != NULL) { 2476 res->selection = newsels; 2477 newsels += res->num_sel++; 2478 newsels->parts = data.result.parts; 2479 newsels->id = data.result.id; 2480 } 2481 } 2482 } 2483 2484 /* 2485 * Final cleanup 2486 */ 2487 done: 2488 free(part_menu_opts); 2489 free(partitions); 2490 2491 return res->num_sel > 0; 2492 } 2493 2494 struct part_selection_and_all_parts { 2495 struct selected_partitions *selection; 2496 struct disk_partitions **all_parts; 2497 size_t all_cnt; 2498 char *title; 2499 bool cancelled; 2500 }; 2501 2502 static int 2503 toggle_clone_data(struct menudesc *m, void *arg) 2504 { 2505 struct part_selection_and_all_parts *sel = arg; 2506 2507 sel->selection->with_data = !sel->selection->with_data; 2508 return 0; 2509 } 2510 2511 static int 2512 add_another(struct menudesc *m, void *arg) 2513 { 2514 struct part_selection_and_all_parts *sel = arg; 2515 2516 add_select_partition(sel->selection, sel->all_parts, sel->all_cnt); 2517 return 0; 2518 } 2519 2520 static int 2521 cancel_clone(struct menudesc *m, void *arg) 2522 { 2523 struct part_selection_and_all_parts *sel = arg; 2524 2525 sel->cancelled = true; 2526 return 1; 2527 } 2528 2529 static void 2530 update_sel_part_title(struct part_selection_and_all_parts *sel) 2531 { 2532 struct disk_part_info info; 2533 char *buf, line[MENUSTRSIZE]; 2534 size_t buf_len, i; 2535 2536 buf_len = MENUSTRSIZE * (1+sel->selection->num_sel); 2537 buf = malloc(buf_len); 2538 if (buf == NULL) 2539 return; 2540 2541 strcpy(buf, msg_string(MSG_select_source_hdr)); 2542 for (i = 0; i < sel->selection->num_sel; i++) { 2543 struct selected_partition *s = 2544 &sel->selection->selection[i]; 2545 if (!s->parts->pscheme->get_part_info(s->parts, s->id, &info)) 2546 continue; 2547 daddr_t start = info.start / sizemult; 2548 daddr_t size = info.size / sizemult; 2549 sprintf(line, "\n %s [%" PRIu64 " @ %" PRIu64 "] ", 2550 s->parts->disk, size, start); 2551 if (info.nat_type != NULL) 2552 strlcat(line, info.nat_type->description, sizeof(line)); 2553 strlcat(buf, line, buf_len); 2554 } 2555 free(sel->title); 2556 sel->title = buf; 2557 } 2558 2559 static void 2560 post_sel_part(struct menudesc *m, void *arg) 2561 { 2562 struct part_selection_and_all_parts *sel = arg; 2563 2564 if (m->mw == NULL) 2565 return; 2566 update_sel_part_title(sel); 2567 m->title = sel->title; 2568 m->h = 0; 2569 resize_menu_height(m); 2570 } 2571 2572 static void 2573 fmt_sel_part_line(struct menudesc *m, int i, void *arg) 2574 { 2575 struct part_selection_and_all_parts *sel = arg; 2576 2577 wprintw(m->mw, "%s: %s", msg_string(MSG_clone_with_data), 2578 sel->selection->with_data ? 2579 msg_string(MSG_Yes) : 2580 msg_string(MSG_No)); 2581 } 2582 2583 bool 2584 select_partitions(struct selected_partitions *res, 2585 const struct disk_partitions *ignore) 2586 { 2587 struct disk_desc disks[MAX_DISKS]; 2588 struct disk_partitions *ps; 2589 struct part_selection_and_all_parts data; 2590 struct pm_devs *i; 2591 size_t j; 2592 int cnt, n, m; 2593 static menu_ent men[] = { 2594 { .opt_name = MSG_select_source_add, 2595 .opt_action = add_another }, 2596 { .opt_action = toggle_clone_data }, 2597 { .opt_name = MSG_cancel, .opt_action = cancel_clone }, 2598 }; 2599 2600 memset(res, 0, sizeof *res); 2601 memset(&data, 0, sizeof data); 2602 data.selection = res; 2603 2604 /* 2605 * collect all available partition sets 2606 */ 2607 data.all_cnt = 0; 2608 if (SLIST_EMPTY(&pm_head)) { 2609 cnt = get_disks(disks, false); 2610 if (cnt <= 0) 2611 return false; 2612 2613 /* 2614 * allocate two slots for each disk (primary/secondary) 2615 */ 2616 data.all_parts = calloc(2*cnt, sizeof *data.all_parts); 2617 if (data.all_parts == NULL) 2618 return false; 2619 2620 for (n = 0; n < cnt; n++) { 2621 if (ignore != NULL && 2622 strcmp(disks[n].dd_name, ignore->disk) == 0) 2623 continue; 2624 2625 ps = partitions_read_disk(disks[n].dd_name, 2626 disks[n].dd_totsec, 2627 disks[n].dd_secsize, 2628 disks[n].dd_no_mbr); 2629 if (ps == NULL) 2630 continue; 2631 data.all_parts[data.all_cnt++] = ps; 2632 ps = get_inner_parts(ps); 2633 if (ps == NULL) 2634 continue; 2635 data.all_parts[data.all_cnt++] = ps; 2636 } 2637 if (data.all_cnt > 0) 2638 res->free_parts = true; 2639 } else { 2640 cnt = 0; 2641 SLIST_FOREACH(i, &pm_head, l) 2642 cnt++; 2643 2644 data.all_parts = calloc(cnt, sizeof *data.all_parts); 2645 if (data.all_parts == NULL) 2646 return false; 2647 2648 SLIST_FOREACH(i, &pm_head, l) { 2649 if (i->parts == NULL) 2650 continue; 2651 if (i->parts == ignore) 2652 continue; 2653 data.all_parts[data.all_cnt++] = i->parts; 2654 } 2655 } 2656 2657 if (!add_select_partition(res, data.all_parts, data.all_cnt)) 2658 goto fail; 2659 2660 /* loop with menu */ 2661 update_sel_part_title(&data); 2662 m = new_menu(data.title, men, __arraycount(men), 3, 2, 0, 65, MC_SCROLL, 2663 post_sel_part, fmt_sel_part_line, NULL, NULL, MSG_clone_src_done); 2664 process_menu(m, &data); 2665 free(data.title); 2666 if (res->num_sel == 0) 2667 goto fail; 2668 2669 /* cleanup */ 2670 if (res->free_parts) { 2671 for (j = 0; j < data.all_cnt; j++) { 2672 if (selection_has_parts(res, data.all_parts[j])) 2673 continue; 2674 if (data.all_parts[j]->parent != NULL) 2675 continue; 2676 data.all_parts[j]->pscheme->free(data.all_parts[j]); 2677 } 2678 } 2679 free(data.all_parts); 2680 return true; 2681 2682 fail: 2683 if (res->free_parts) { 2684 for (j = 0; j < data.all_cnt; j++) { 2685 if (data.all_parts[j]->parent != NULL) 2686 continue; 2687 data.all_parts[j]->pscheme->free(data.all_parts[j]); 2688 } 2689 } 2690 free(data.all_parts); 2691 return false; 2692 } 2693 2694 void 2695 free_selected_partitions(struct selected_partitions *selected) 2696 { 2697 size_t i; 2698 struct disk_partitions *parts; 2699 2700 if (!selected->free_parts) 2701 return; 2702 2703 for (i = 0; i < selected->num_sel; i++) { 2704 parts = selected->selection[i].parts; 2705 2706 /* remove from list before testing for other instances */ 2707 selected->selection[i].parts = NULL; 2708 2709 /* if this is the secondary partition set, the parent owns it */ 2710 if (parts->parent != NULL) 2711 continue; 2712 2713 /* only free once (we use the last one) */ 2714 if (selection_has_parts(selected, parts)) 2715 continue; 2716 parts->pscheme->free(parts); 2717 } 2718 free(selected->selection); 2719 } 2720 2721 daddr_t 2722 selected_parts_size(struct selected_partitions *selected) 2723 { 2724 struct disk_part_info info; 2725 size_t i; 2726 daddr_t s = 0; 2727 2728 for (i = 0; i < selected->num_sel; i++) { 2729 if (!selected->selection[i].parts->pscheme->get_part_info( 2730 selected->selection[i].parts, 2731 selected->selection[i].id, &info)) 2732 continue; 2733 s += info.size; 2734 } 2735 2736 return s; 2737 } 2738 2739 int 2740 clone_target_select(menudesc *m, void *arg) 2741 { 2742 struct clone_target_menu_data *data = arg; 2743 2744 data->res = m->cursel; 2745 return 1; 2746 } 2747 2748 bool 2749 clone_partition_data(struct disk_partitions *dest_parts, part_id did, 2750 struct disk_partitions *src_parts, part_id sid) 2751 { 2752 char src_dev[MAXPATHLEN], target_dev[MAXPATHLEN]; 2753 2754 if (!src_parts->pscheme->get_part_device( 2755 src_parts, sid, src_dev, sizeof src_dev, NULL, 2756 raw_dev_name, true, true)) 2757 return false; 2758 if (!dest_parts->pscheme->get_part_device( 2759 dest_parts, did, target_dev, sizeof target_dev, NULL, 2760 raw_dev_name, true, true)) 2761 return false; 2762 2763 return run_program(RUN_DISPLAY | RUN_PROGRESS, 2764 "progress -f %s -b 1m dd bs=1m of=%s", 2765 src_dev, target_dev) == 0; 2766 } 2767 #endif 2768 2769