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