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