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