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