1 /* $NetBSD: disks.c,v 1.58 2019/12/11 19:23:37 martin Exp $ */ 2 3 /* 4 * Copyright 1997 Piermont Information Systems Inc. 5 * All rights reserved. 6 * 7 * Written by Philip A. Nelson for Piermont Information Systems Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. The name of Piermont Information Systems Inc. may not be used to endorse 18 * or promote products derived from this software without specific prior 19 * written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS'' 22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE 25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 31 * THE POSSIBILITY OF SUCH DAMAGE. 32 * 33 */ 34 35 /* disks.c -- routines to deal with finding disks and labeling disks. */ 36 37 38 #include <assert.h> 39 #include <errno.h> 40 #include <inttypes.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <unistd.h> 44 #include <fcntl.h> 45 #include <fnmatch.h> 46 #include <util.h> 47 #include <uuid.h> 48 #include <paths.h> 49 #include <fstab.h> 50 51 #include <sys/param.h> 52 #include <sys/sysctl.h> 53 #include <sys/swap.h> 54 #include <sys/disklabel_gpt.h> 55 #include <ufs/ufs/dinode.h> 56 #include <ufs/ffs/fs.h> 57 58 #include <dev/scsipi/scsipi_all.h> 59 #include <sys/scsiio.h> 60 61 #include <dev/ata/atareg.h> 62 #include <sys/ataio.h> 63 64 #include "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, p->dlsize, is_boot_drive); 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_no_mbr); 908 } 909 } 910 continue; 911 } 912 } 913 pm = pm_new; 914 pm->found = 1; 915 pm->ptstart = 0; 916 pm->ptsize = 0; 917 pm->bootable = 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 = disk->dd_cyl * disk->dd_head 933 * disk->dd_sec; 934 935 pm->parts = partitions_read_disk(pm->diskdev, 936 disk->dd_totsec, 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 = disk->dd_cyl * disk->dd_head 965 * 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 * 512U, 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 && 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); 1141 1142 parts->pscheme->get_part_device(parts, ptn->cur_part_id, 1143 rdev, sizeof rdev, &partno, raw_dev_name, 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 if (ptn->type != PT_swap && 1304 (ptn->instflags & PUIINST_MOUNT) == 0) 1305 continue; 1306 1307 const char *s = ""; 1308 const char *mp = ptn->mount; 1309 const char *fstype = "ffs"; 1310 int fsck_pass = 0, dump_freq = 0; 1311 1312 if (ptn->parts->pscheme->get_part_device(ptn->parts, 1313 ptn->cur_part_id, dev_buf, sizeof dev_buf, NULL, 1314 logical_name, true)) 1315 dev = dev_buf; 1316 else 1317 dev = NULL; 1318 1319 if (!*mp) { 1320 /* 1321 * No mount point specified, comment out line and 1322 * use /mnt as a placeholder for the mount point. 1323 */ 1324 s = "# "; 1325 mp = "/mnt"; 1326 } 1327 1328 switch (ptn->fs_type) { 1329 case FS_UNUSED: 1330 continue; 1331 case FS_BSDLFS: 1332 /* If there is no LFS, just comment it out. */ 1333 if (!check_lfs_progs()) 1334 s = "# "; 1335 fstype = "lfs"; 1336 /* FALLTHROUGH */ 1337 case FS_BSDFFS: 1338 fsck_pass = (strcmp(mp, "/") == 0) ? 1 : 2; 1339 dump_freq = 1; 1340 break; 1341 case FS_MSDOS: 1342 fstype = "msdos"; 1343 break; 1344 case FS_SWAP: 1345 if (swap_dev[0] == 0) { 1346 strncpy(swap_dev, dev, sizeof swap_dev); 1347 dump_dev = ",dp"; 1348 } else { 1349 dump_dev = ""; 1350 } 1351 scripting_fprintf(f, "%s\t\tnone\tswap\tsw%s\t\t 0 0\n", 1352 dev, dump_dev); 1353 continue; 1354 case FS_SYSVBFS: 1355 fstype = "sysvbfs"; 1356 make_target_dir("/stand"); 1357 break; 1358 default: 1359 fstype = "???"; 1360 s = "# "; 1361 break; 1362 } 1363 /* The code that remounts root rw doesn't check the partition */ 1364 if (strcmp(mp, "/") == 0 && 1365 (ptn->instflags & PUIINST_MOUNT) == 0) 1366 s = "# "; 1367 1368 scripting_fprintf(f, 1369 "%s%s\t\t%s\t%s\trw%s%s%s%s%s%s%s%s\t\t %d %d\n", 1370 s, dev, mp, fstype, 1371 ptn->mountflags & PUIMNT_LOG ? ",log" : "", 1372 ptn->mountflags & PUIMNT_NOAUTO ? ",noauto" : "", 1373 ptn->mountflags & PUIMNT_ASYNC ? ",async" : "", 1374 ptn->mountflags & PUIMNT_NOATIME ? ",noatime" : "", 1375 ptn->mountflags & PUIMNT_NODEV ? ",nodev" : "", 1376 ptn->mountflags & PUIMNT_NODEVMTIME ? ",nodevmtime" : "", 1377 ptn->mountflags & PUIMNT_NOEXEC ? ",noexec" : "", 1378 ptn->mountflags & PUIMNT_NOSUID ? ",nosuid" : "", 1379 dump_freq, fsck_pass); 1380 } 1381 1382 done_with_disks: 1383 if (tmp_ramdisk_size > 0) { 1384 #ifdef HAVE_TMPFS 1385 scripting_fprintf(f, "tmpfs\t\t/tmp\ttmpfs\trw,-m=1777,-s=%" 1386 PRIu64 "\n", 1387 tmp_ramdisk_size * 512); 1388 #else 1389 if (swap_dev[0] != 0) 1390 scripting_fprintf(f, "%s\t\t/tmp\tmfs\trw,-s=%" 1391 PRIu64 "\n", swap_dev, tmp_ramdisk_size); 1392 else 1393 scripting_fprintf(f, "swap\t\t/tmp\tmfs\trw,-s=%" 1394 PRIu64 "\n", tmp_ramdisk_size); 1395 #endif 1396 } 1397 1398 if (cdrom_dev[0] == 0) 1399 get_default_cdrom(cdrom_dev, sizeof(cdrom_dev)); 1400 1401 /* Add /kern, /proc and /dev/pts to fstab and make mountpoint. */ 1402 scripting_fprintf(f, "kernfs\t\t/kern\tkernfs\trw\n"); 1403 scripting_fprintf(f, "ptyfs\t\t/dev/pts\tptyfs\trw\n"); 1404 scripting_fprintf(f, "procfs\t\t/proc\tprocfs\trw\n"); 1405 scripting_fprintf(f, "/dev/%s\t\t/cdrom\tcd9660\tro,noauto\n", 1406 cdrom_dev); 1407 scripting_fprintf(f, "%stmpfs\t\t/var/shm\ttmpfs\trw,-m1777,-sram%%25\n", 1408 tmpfs_on_var_shm() ? "" : "#"); 1409 make_target_dir("/kern"); 1410 make_target_dir("/proc"); 1411 make_target_dir("/dev/pts"); 1412 make_target_dir("/cdrom"); 1413 make_target_dir("/var/shm"); 1414 1415 scripting_fprintf(NULL, "EOF\n"); 1416 1417 fclose(f); 1418 fflush(NULL); 1419 return 0; 1420 } 1421 1422 static bool 1423 find_part_by_name(const char *name, struct disk_partitions **parts, 1424 part_id *pno) 1425 { 1426 struct pm_devs *i; 1427 struct disk_partitions *ps; 1428 part_id id; 1429 struct disk_desc disks[MAX_DISKS]; 1430 int n, cnt; 1431 1432 if (SLIST_EMPTY(&pm_head)) { 1433 /* 1434 * List has not been filled, only "pm" is valid - check 1435 * that first. 1436 */ 1437 if (pm->parts->pscheme->find_by_name != NULL) { 1438 id = pm->parts->pscheme->find_by_name(pm->parts, name); 1439 if (id != NO_PART) { 1440 *pno = id; 1441 *parts = pm->parts; 1442 return true; 1443 } 1444 } 1445 /* 1446 * Not that easy - check all other disks 1447 */ 1448 cnt = get_disks(disks, false); 1449 for (n = 0; n < cnt; n++) { 1450 if (strcmp(disks[n].dd_name, pm->diskdev) == 0) 1451 continue; 1452 ps = partitions_read_disk(disks[n].dd_name, 1453 disks[n].dd_totsec, disks[n].dd_no_mbr); 1454 if (ps == NULL) 1455 continue; 1456 if (ps->pscheme->find_by_name == NULL) 1457 continue; 1458 id = ps->pscheme->find_by_name(ps, name); 1459 if (id != NO_PART) { 1460 *pno = id; 1461 *parts = ps; 1462 return true; /* XXX this leaks memory */ 1463 } 1464 ps->pscheme->free(ps); 1465 } 1466 } else { 1467 SLIST_FOREACH(i, &pm_head, l) { 1468 if (i->parts == NULL) 1469 continue; 1470 if (i->parts->pscheme->find_by_name == NULL) 1471 continue; 1472 id = i->parts->pscheme->find_by_name(i->parts, name); 1473 if (id == NO_PART) 1474 continue; 1475 *pno = id; 1476 *parts = i->parts; 1477 return true; 1478 } 1479 } 1480 1481 *pno = NO_PART; 1482 *parts = NULL; 1483 return false; 1484 } 1485 1486 static int 1487 /*ARGSUSED*/ 1488 process_found_fs(struct data *list, size_t num, const struct lookfor *item, 1489 bool with_fsck) 1490 { 1491 int error; 1492 char rdev[PATH_MAX], dev[PATH_MAX], 1493 options[STRSIZE], tmp[STRSIZE], *op, *last; 1494 const char *fsname = (const char*)item->var; 1495 part_id pno; 1496 struct disk_partitions *parts; 1497 size_t len; 1498 bool first, is_root; 1499 1500 if (num < 2 || strstr(list[2].u.s_val, "noauto") != NULL) 1501 return 0; 1502 1503 is_root = strcmp(list[1].u.s_val, "/") == 0; 1504 if (is_root && target_mounted()) 1505 return 0; 1506 1507 if (strcmp(item->head, name_prefix) == 0) { 1508 /* this fstab entry uses NAME= syntax */ 1509 if (!find_part_by_name(list[0].u.s_val, 1510 &parts, &pno) || parts == NULL || pno == NO_PART) 1511 return 0; 1512 parts->pscheme->get_part_device(parts, pno, 1513 dev, sizeof(dev), NULL, plain_name, true); 1514 parts->pscheme->get_part_device(parts, pno, 1515 rdev, sizeof(rdev), NULL, raw_dev_name, true); 1516 } else { 1517 /* this fstab entry uses the plain device name */ 1518 if (is_root) { 1519 /* 1520 * PR 54480: we can not use the current device name 1521 * as it might be different from the real environment. 1522 * This is an abuse of the functionality, but it used 1523 * to work before (and still does work if only a single 1524 * target disk is involved). 1525 * Use the device name from the current "pm" instead. 1526 */ 1527 strcpy(rdev, "/dev/r"); 1528 strlcat(rdev, pm->diskdev, sizeof(rdev)); 1529 strcpy(dev, "/dev/"); 1530 strlcat(dev, pm->diskdev, sizeof(dev)); 1531 /* copy over the partition letter, if any */ 1532 len = strlen(list[0].u.s_val); 1533 if (list[0].u.s_val[len-1] >= 'a' && 1534 list[0].u.s_val[len-1] <= 1535 ('a' + getmaxpartitions())) { 1536 strlcat(rdev, &list[0].u.s_val[len-1], 1537 sizeof(rdev)); 1538 strlcat(dev, &list[0].u.s_val[len-1], 1539 sizeof(dev)); 1540 } 1541 } else { 1542 strcpy(rdev, "/dev/r"); 1543 strlcat(rdev, list[0].u.s_val, sizeof(rdev)); 1544 strcpy(dev, "/dev/"); 1545 strlcat(dev, list[0].u.s_val, sizeof(dev)); 1546 } 1547 } 1548 1549 if (with_fsck) { 1550 /* need the raw device for fsck_preen */ 1551 error = fsck_preen(rdev, fsname, false); 1552 if (error != 0) 1553 return error; 1554 } 1555 1556 /* add mount option for fs type */ 1557 strcpy(options, "-t "); 1558 strlcat(options, fsname, sizeof(options)); 1559 1560 /* extract mount options from fstab */ 1561 strlcpy(tmp, list[2].u.s_val, sizeof(tmp)); 1562 for (first = true, op = strtok_r(tmp, ",", &last); op != NULL; 1563 op = strtok_r(NULL, ",", &last)) { 1564 if (strcmp(op, FSTAB_RW) == 0 || 1565 strcmp(op, FSTAB_RQ) == 0 || 1566 strcmp(op, FSTAB_RO) == 0 || 1567 strcmp(op, FSTAB_SW) == 0 || 1568 strcmp(op, FSTAB_DP) == 0 || 1569 strcmp(op, FSTAB_XX) == 0) 1570 continue; 1571 if (first) { 1572 first = false; 1573 strlcat(options, " -o ", sizeof(options)); 1574 } else { 1575 strlcat(options, ",", sizeof(options)); 1576 } 1577 strlcat(options, op, sizeof(options)); 1578 } 1579 1580 error = target_mount(options, dev, list[1].u.s_val); 1581 if (error != 0) { 1582 msg_fmt_display(MSG_mount_failed, "%s", list[0].u.s_val); 1583 if (!ask_noyes(NULL)) 1584 return error; 1585 } 1586 return 0; 1587 } 1588 1589 static int 1590 /*ARGSUSED*/ 1591 found_fs(struct data *list, size_t num, const struct lookfor *item) 1592 { 1593 return process_found_fs(list, num, item, true); 1594 } 1595 1596 static int 1597 /*ARGSUSED*/ 1598 found_fs_nocheck(struct data *list, size_t num, const struct lookfor *item) 1599 { 1600 return process_found_fs(list, num, item, false); 1601 } 1602 1603 /* 1604 * Do an fsck. On failure, inform the user by showing a warning 1605 * message and doing menu_ok() before proceeding. 1606 * The device passed should be the full qualified path to raw disk 1607 * (e.g. /dev/rwd0a). 1608 * Returns 0 on success, or nonzero return code from fsck() on failure. 1609 */ 1610 static int 1611 fsck_preen(const char *disk, const char *fsname, bool silent) 1612 { 1613 char *prog, err[12]; 1614 int error; 1615 1616 if (fsname == NULL) 1617 return 0; 1618 /* first, check if fsck program exists, if not, assume ok */ 1619 asprintf(&prog, "/sbin/fsck_%s", fsname); 1620 if (prog == NULL) 1621 return 0; 1622 if (access(prog, X_OK) != 0) { 1623 free(prog); 1624 return 0; 1625 } 1626 if (!strcmp(fsname,"ffs")) 1627 fixsb(prog, disk); 1628 error = run_program(silent? RUN_SILENT|RUN_ERROR_OK : 0, "%s -p -q %s", prog, disk); 1629 free(prog); 1630 if (error != 0 && !silent) { 1631 sprintf(err, "%d", error); 1632 msg_display_subst(msg_string(MSG_badfs), 3, 1633 disk, fsname, err); 1634 if (ask_noyes(NULL)) 1635 error = 0; 1636 /* XXX at this point maybe we should run a full fsck? */ 1637 } 1638 return error; 1639 } 1640 1641 /* This performs the same function as the etc/rc.d/fixsb script 1642 * which attempts to correct problems with ffs1 filesystems 1643 * which may have been introduced by booting a netbsd-current kernel 1644 * from between April of 2003 and January 2004. For more information 1645 * This script was developed as a response to NetBSD pr install/25138 1646 * Additional prs regarding the original issue include: 1647 * bin/17910 kern/21283 kern/21404 port-macppc/23925 port-macppc/23926 1648 */ 1649 static void 1650 fixsb(const char *prog, const char *disk) 1651 { 1652 int fd; 1653 int rval; 1654 union { 1655 struct fs fs; 1656 char buf[SBLOCKSIZE]; 1657 } sblk; 1658 struct fs *fs = &sblk.fs; 1659 1660 fd = open(disk, O_RDONLY); 1661 if (fd == -1) 1662 return; 1663 1664 /* Read ffsv1 main superblock */ 1665 rval = pread(fd, sblk.buf, sizeof sblk.buf, SBLOCK_UFS1); 1666 close(fd); 1667 if (rval != sizeof sblk.buf) 1668 return; 1669 1670 if (fs->fs_magic != FS_UFS1_MAGIC && 1671 fs->fs_magic != FS_UFS1_MAGIC_SWAPPED) 1672 /* Not FFSv1 */ 1673 return; 1674 if (fs->fs_old_flags & FS_FLAGS_UPDATED) 1675 /* properly updated fslevel 4 */ 1676 return; 1677 if (fs->fs_bsize != fs->fs_maxbsize) 1678 /* not messed up */ 1679 return; 1680 1681 /* 1682 * OK we have a munged fs, first 'upgrade' to fslevel 4, 1683 * We specify -b16 in order to stop fsck bleating that the 1684 * sb doesn't match the first alternate. 1685 */ 1686 run_program(RUN_DISPLAY | RUN_PROGRESS, 1687 "%s -p -b 16 -c 4 %s", prog, disk); 1688 /* Then downgrade to fslevel 3 */ 1689 run_program(RUN_DISPLAY | RUN_PROGRESS, 1690 "%s -p -c 3 %s", prog, disk); 1691 } 1692 1693 /* 1694 * fsck and mount the root partition. 1695 * devdev is the fully qualified block device name. 1696 */ 1697 static int 1698 mount_root(const char *devdev, bool first, bool writeable, 1699 struct install_partition_desc *install) 1700 { 1701 int error; 1702 1703 error = fsck_preen(devdev, "ffs", false); 1704 if (error != 0) 1705 return error; 1706 1707 if (first) 1708 md_pre_mount(install, 0); 1709 1710 /* Mount devdev on target's "". 1711 * If we pass "" as mount-on, Prefixing will DTRT. 1712 * for now, use no options. 1713 * XXX consider -o remount in case target root is 1714 * current root, still readonly from single-user? 1715 */ 1716 return target_mount(writeable? "" : "-r", devdev, ""); 1717 } 1718 1719 /* Get information on the file systems mounted from the root filesystem. 1720 * Offer to convert them into 4.4BSD inodes if they are not 4.4BSD 1721 * inodes. Fsck them. Mount them. 1722 */ 1723 1724 int 1725 mount_disks(struct install_partition_desc *install) 1726 { 1727 char *fstab; 1728 int fstabsize; 1729 int error; 1730 char devdev[PATH_MAX]; 1731 size_t i, num_fs_types, num_entries; 1732 struct lookfor *fstabbuf, *l; 1733 1734 if (install->cur_system) 1735 return 0; 1736 1737 /* 1738 * Check what file system tools are available and create parsers 1739 * for the corresponding fstab(5) entries - all others will be 1740 * ignored. 1741 */ 1742 num_fs_types = 1; /* ffs is implicit */ 1743 for (i = 0; i < __arraycount(extern_fs_with_chk); i++) { 1744 sprintf(devdev, "/sbin/newfs_%s", extern_fs_with_chk[i]); 1745 if (file_exists_p(devdev)) 1746 num_fs_types++; 1747 } 1748 for (i = 0; i < __arraycount(extern_fs_newfs_only); i++) { 1749 sprintf(devdev, "/sbin/newfs_%s", extern_fs_newfs_only[i]); 1750 if (file_exists_p(devdev)) 1751 num_fs_types++; 1752 } 1753 num_entries = 2 * num_fs_types + 1; /* +1 for "ufs" special case */ 1754 fstabbuf = calloc(num_entries, sizeof(*fstabbuf)); 1755 if (fstabbuf == NULL) 1756 return -1; 1757 l = fstabbuf; 1758 l->head = "/dev/"; 1759 l->fmt = strdup("/dev/%s %s ffs %s"); 1760 l->todo = "c"; 1761 l->var = __UNCONST("ffs"); 1762 l->func = found_fs; 1763 l++; 1764 l->head = "/dev/"; 1765 l->fmt = strdup("/dev/%s %s ufs %s"); 1766 l->todo = "c"; 1767 l->var = __UNCONST("ffs"); 1768 l->func = found_fs; 1769 l++; 1770 l->head = NAME_PREFIX; 1771 l->fmt = strdup(NAME_PREFIX "%s %s ffs %s"); 1772 l->todo = "c"; 1773 l->var = __UNCONST("ffs"); 1774 l->func = found_fs; 1775 l++; 1776 for (i = 0; i < __arraycount(extern_fs_with_chk); i++) { 1777 sprintf(devdev, "/sbin/newfs_%s", extern_fs_with_chk[i]); 1778 if (!file_exists_p(devdev)) 1779 continue; 1780 sprintf(devdev, "/dev/%%s %%s %s %%s", extern_fs_with_chk[i]); 1781 l->head = "/dev/"; 1782 l->fmt = strdup(devdev); 1783 l->todo = "c"; 1784 l->var = __UNCONST(extern_fs_with_chk[i]); 1785 l->func = found_fs; 1786 l++; 1787 sprintf(devdev, NAME_PREFIX "%%s %%s %s %%s", 1788 extern_fs_with_chk[i]); 1789 l->head = NAME_PREFIX; 1790 l->fmt = strdup(devdev); 1791 l->todo = "c"; 1792 l->var = __UNCONST(extern_fs_with_chk[i]); 1793 l->func = found_fs; 1794 l++; 1795 } 1796 for (i = 0; i < __arraycount(extern_fs_newfs_only); i++) { 1797 sprintf(devdev, "/sbin/newfs_%s", extern_fs_newfs_only[i]); 1798 if (!file_exists_p(devdev)) 1799 continue; 1800 sprintf(devdev, "/dev/%%s %%s %s %%s", extern_fs_newfs_only[i]); 1801 l->head = "/dev/"; 1802 l->fmt = strdup(devdev); 1803 l->todo = "c"; 1804 l->var = __UNCONST(extern_fs_newfs_only[i]); 1805 l->func = found_fs_nocheck; 1806 l++; 1807 sprintf(devdev, NAME_PREFIX "%%s %%s %s %%s", 1808 extern_fs_newfs_only[i]); 1809 l->head = NAME_PREFIX; 1810 l->fmt = strdup(devdev); 1811 l->todo = "c"; 1812 l->var = __UNCONST(extern_fs_newfs_only[i]); 1813 l->func = found_fs_nocheck; 1814 l++; 1815 } 1816 assert((size_t)(l - fstabbuf) == num_entries); 1817 1818 /* First the root device. */ 1819 if (target_already_root()) 1820 /* avoid needing to call target_already_root() again */ 1821 targetroot_mnt[0] = 0; 1822 else { 1823 for (i = 0; i < install->num; i++) { 1824 if (is_root_part_mount(install->infos[i].mount)) 1825 break; 1826 } 1827 1828 if (i >= install->num) { 1829 hit_enter_to_continue(MSG_noroot, NULL); 1830 return -1; 1831 } 1832 1833 if (!install->infos[i].parts->pscheme->get_part_device( 1834 install->infos[i].parts, install->infos[i].cur_part_id, 1835 devdev, sizeof devdev, NULL, plain_name, true)) 1836 return -1; 1837 error = mount_root(devdev, true, false, install); 1838 if (error != 0 && error != EBUSY) 1839 return -1; 1840 } 1841 1842 /* Check the target /etc/fstab exists before trying to parse it. */ 1843 if (target_dir_exists_p("/etc") == 0 || 1844 target_file_exists_p("/etc/fstab") == 0) { 1845 msg_fmt_display(MSG_noetcfstab, "%s", pm->diskdev); 1846 hit_enter_to_continue(NULL, NULL); 1847 return -1; 1848 } 1849 1850 1851 /* Get fstab entries from the target-root /etc/fstab. */ 1852 fstabsize = target_collect_file(T_FILE, &fstab, "/etc/fstab"); 1853 if (fstabsize < 0) { 1854 /* error ! */ 1855 msg_fmt_display(MSG_badetcfstab, "%s", pm->diskdev); 1856 hit_enter_to_continue(NULL, NULL); 1857 umount_root(); 1858 return -2; 1859 } 1860 /* 1861 * We unmount the read-only root again, so we can mount it 1862 * with proper options from /etc/fstab 1863 */ 1864 umount_root(); 1865 1866 /* 1867 * Now do all entries in /etc/fstab and mount them if required 1868 */ 1869 error = walk(fstab, (size_t)fstabsize, fstabbuf, num_entries); 1870 free(fstab); 1871 for (i = 0; i < num_entries; i++) 1872 free(__UNCONST(fstabbuf[i].fmt)); 1873 free(fstabbuf); 1874 1875 return error; 1876 } 1877 1878 int 1879 set_swap_if_low_ram(struct install_partition_desc *install) 1880 { 1881 if (get_ramsize() <= 32) 1882 return set_swap(install); 1883 return 0; 1884 } 1885 1886 int 1887 set_swap(struct install_partition_desc *install) 1888 { 1889 size_t i; 1890 char dev_buf[PATH_MAX]; 1891 int rval; 1892 1893 for (i = 0; i < install->num; i++) { 1894 if (install->infos[i].type == PT_swap) 1895 break; 1896 } 1897 if (i >= install->num) 1898 return 0; 1899 1900 if (!install->infos[i].parts->pscheme->get_part_device( 1901 install->infos[i].parts, install->infos[i].cur_part_id, dev_buf, 1902 sizeof dev_buf, NULL, plain_name, true)) 1903 return -1; 1904 1905 rval = swapctl(SWAP_ON, dev_buf, 0); 1906 if (rval != 0) 1907 return -1; 1908 1909 return 0; 1910 } 1911 1912 int 1913 check_swap(const char *disk, int remove_swap) 1914 { 1915 struct swapent *swap; 1916 char *cp; 1917 int nswap; 1918 int l; 1919 int rval = 0; 1920 1921 nswap = swapctl(SWAP_NSWAP, 0, 0); 1922 if (nswap <= 0) 1923 return 0; 1924 1925 swap = malloc(nswap * sizeof *swap); 1926 if (swap == NULL) 1927 return -1; 1928 1929 nswap = swapctl(SWAP_STATS, swap, nswap); 1930 if (nswap < 0) 1931 goto bad_swap; 1932 1933 l = strlen(disk); 1934 while (--nswap >= 0) { 1935 /* Should we check the se_dev or se_path? */ 1936 cp = swap[nswap].se_path; 1937 if (memcmp(cp, "/dev/", 5) != 0) 1938 continue; 1939 if (memcmp(cp + 5, disk, l) != 0) 1940 continue; 1941 if (!isalpha(*(unsigned char *)(cp + 5 + l))) 1942 continue; 1943 if (cp[5 + l + 1] != 0) 1944 continue; 1945 /* ok path looks like it is for this device */ 1946 if (!remove_swap) { 1947 /* count active swap areas */ 1948 rval++; 1949 continue; 1950 } 1951 if (swapctl(SWAP_OFF, cp, 0) == -1) 1952 rval = -1; 1953 } 1954 1955 done: 1956 free(swap); 1957 return rval; 1958 1959 bad_swap: 1960 rval = -1; 1961 goto done; 1962 } 1963 1964 #ifdef HAVE_BOOTXX_xFS 1965 char * 1966 bootxx_name(struct install_partition_desc *install) 1967 { 1968 size_t i; 1969 int fstype = -1; 1970 const char *bootxxname; 1971 char *bootxx; 1972 1973 /* find a partition to be mounted as / */ 1974 for (i = 0; i < install->num; i++) { 1975 if ((install->infos[i].instflags & PUIINST_MOUNT) 1976 && strcmp(install->infos[i].mount, "/") == 0) { 1977 fstype = install->infos[i].fs_type; 1978 break; 1979 } 1980 } 1981 if (fstype < 0) { 1982 /* not found? take first root type partition instead */ 1983 for (i = 0; i < install->num; i++) { 1984 if (install->infos[i].type == PT_root) { 1985 fstype = install->infos[i].fs_type; 1986 break; 1987 } 1988 } 1989 } 1990 1991 /* check we have boot code for the root partition type */ 1992 switch (fstype) { 1993 #if defined(BOOTXX_FFSV1) || defined(BOOTXX_FFSV2) 1994 case FS_BSDFFS: 1995 if (install->infos[0].fs_version == 2) { 1996 #ifdef BOOTXX_FFSV2 1997 bootxxname = BOOTXX_FFSV2; 1998 #else 1999 bootxxname = NULL; 2000 #endif 2001 } else { 2002 #ifdef BOOTXX_FFSV1 2003 bootxxname = BOOTXX_FFSV1; 2004 #else 2005 bootxxname = NULL; 2006 #endif 2007 } 2008 break; 2009 #endif 2010 #ifdef BOOTXX_LFSV2 2011 case FS_BSDLFS: 2012 bootxxname = BOOTXX_LFSV2; 2013 break; 2014 #endif 2015 default: 2016 bootxxname = NULL; 2017 break; 2018 } 2019 2020 if (bootxxname == NULL) 2021 return NULL; 2022 2023 asprintf(&bootxx, "%s/%s", BOOTXXDIR, bootxxname); 2024 return bootxx; 2025 } 2026 #endif 2027 2028 /* from dkctl.c */ 2029 static int 2030 get_dkwedges_sort(const void *a, const void *b) 2031 { 2032 const struct dkwedge_info *dkwa = a, *dkwb = b; 2033 const daddr_t oa = dkwa->dkw_offset, ob = dkwb->dkw_offset; 2034 return (oa < ob) ? -1 : (oa > ob) ? 1 : 0; 2035 } 2036 2037 int 2038 get_dkwedges(struct dkwedge_info **dkw, const char *diskdev) 2039 { 2040 struct dkwedge_list dkwl; 2041 2042 *dkw = NULL; 2043 if (!get_wedge_list(diskdev, &dkwl)) 2044 return -1; 2045 2046 if (dkwl.dkwl_nwedges > 0 && *dkw != NULL) { 2047 qsort(*dkw, dkwl.dkwl_nwedges, sizeof(**dkw), 2048 get_dkwedges_sort); 2049 } 2050 2051 return dkwl.dkwl_nwedges; 2052 } 2053 2054 #ifndef NO_CLONES 2055 /* 2056 * Helper structures used in the partition select menu 2057 */ 2058 struct single_partition { 2059 struct disk_partitions *parts; 2060 part_id id; 2061 }; 2062 2063 struct sel_menu_data { 2064 struct single_partition *partitions; 2065 struct selected_partition result; 2066 }; 2067 2068 static int 2069 select_single_part(menudesc *m, void *arg) 2070 { 2071 struct sel_menu_data *data = arg; 2072 2073 data->result.parts = data->partitions[m->cursel].parts; 2074 data->result.id = data->partitions[m->cursel].id; 2075 2076 return 1; 2077 } 2078 2079 static void 2080 display_single_part(menudesc *m, int opt, void *arg) 2081 { 2082 const struct sel_menu_data *data = arg; 2083 struct disk_part_info info; 2084 struct disk_partitions *parts = data->partitions[opt].parts; 2085 part_id id = data->partitions[opt].id; 2086 int l; 2087 const char *desc = NULL; 2088 char line[MENUSTRSIZE*2]; 2089 2090 if (!parts->pscheme->get_part_info(parts, id, &info)) 2091 return; 2092 2093 if (parts->pscheme->other_partition_identifier != NULL) 2094 desc = parts->pscheme->other_partition_identifier( 2095 parts, id); 2096 2097 daddr_t start = info.start / sizemult; 2098 daddr_t size = info.size / sizemult; 2099 snprintf(line, sizeof line, "%s [%" PRIu64 " @ %" PRIu64 "]", 2100 parts->disk, size, start); 2101 2102 if (info.nat_type != NULL) { 2103 strlcat(line, " ", sizeof line); 2104 strlcat(line, info.nat_type->description, sizeof line); 2105 } 2106 2107 if (desc != NULL) { 2108 strlcat(line, ": ", sizeof line); 2109 strlcat(line, desc, sizeof line); 2110 } 2111 2112 l = strlen(line); 2113 if (l >= (m->w)) 2114 strcpy(line + (m->w-3), "..."); 2115 wprintw(m->mw, "%s", line); 2116 } 2117 2118 /* 2119 * is the given "test" partitions set used in the selected set? 2120 */ 2121 static bool 2122 selection_has_parts(struct selected_partitions *sel, 2123 const struct disk_partitions *test) 2124 { 2125 size_t i; 2126 2127 for (i = 0; i < sel->num_sel; i++) { 2128 if (sel->selection[i].parts == test) 2129 return true; 2130 } 2131 return false; 2132 } 2133 2134 /* 2135 * is the given "test" partition in the selected set? 2136 */ 2137 static bool 2138 selection_has_partition(struct selected_partitions *sel, 2139 const struct disk_partitions *test, part_id test_id) 2140 { 2141 size_t i; 2142 2143 for (i = 0; i < sel->num_sel; i++) { 2144 if (sel->selection[i].parts == test && 2145 sel->selection[i].id == test_id) 2146 return true; 2147 } 2148 return false; 2149 } 2150 2151 /* 2152 * let the user select a partition, optionally skipping all partitions 2153 * on the "ignore" device 2154 */ 2155 static bool 2156 add_select_partition(struct selected_partitions *res, 2157 struct disk_partitions **all_parts, size_t all_cnt) 2158 { 2159 struct disk_partitions *ps; 2160 struct disk_part_info info; 2161 part_id id; 2162 struct single_partition *partitions, *pp; 2163 struct menu_ent *part_menu_opts, *menup; 2164 size_t n, part_cnt; 2165 int sel_menu; 2166 2167 /* 2168 * count how many items our menu will have 2169 */ 2170 part_cnt = 0; 2171 for (n = 0; n < all_cnt; n++) { 2172 ps = all_parts[n]; 2173 for (id = 0; id < ps->num_part; id++) { 2174 if (selection_has_partition(res, ps, id)) 2175 continue; 2176 if (!ps->pscheme->get_part_info(ps, id, &info)) 2177 continue; 2178 if (info.flags & (PTI_SEC_CONTAINER|PTI_WHOLE_DISK| 2179 PTI_PSCHEME_INTERNAL|PTI_RAW_PART)) 2180 continue; 2181 part_cnt++; 2182 } 2183 } 2184 2185 /* 2186 * create a menu from this and let the user 2187 * select one partition 2188 */ 2189 part_menu_opts = NULL; 2190 partitions = calloc(part_cnt, sizeof *partitions); 2191 if (partitions == NULL) 2192 goto done; 2193 part_menu_opts = calloc(part_cnt, sizeof *part_menu_opts); 2194 if (part_menu_opts == NULL) 2195 goto done; 2196 pp = partitions; 2197 menup = part_menu_opts; 2198 for (n = 0; n < all_cnt; n++) { 2199 ps = all_parts[n]; 2200 for (id = 0; id < ps->num_part; id++) { 2201 if (selection_has_partition(res, ps, id)) 2202 continue; 2203 if (!ps->pscheme->get_part_info(ps, id, &info)) 2204 continue; 2205 if (info.flags & (PTI_SEC_CONTAINER|PTI_WHOLE_DISK| 2206 PTI_PSCHEME_INTERNAL|PTI_RAW_PART)) 2207 continue; 2208 pp->parts = ps; 2209 pp->id = id; 2210 pp++; 2211 menup->opt_action = select_single_part; 2212 menup++; 2213 } 2214 } 2215 sel_menu = new_menu(MSG_select_foreign_part, part_menu_opts, part_cnt, 2216 3, 3, 0, 60, 2217 MC_SUBMENU | MC_SCROLL | MC_NOCLEAR, 2218 NULL, display_single_part, NULL, 2219 NULL, MSG_exit_menu_generic); 2220 if (sel_menu != -1) { 2221 struct selected_partition *newsels; 2222 struct sel_menu_data data; 2223 2224 memset(&data, 0, sizeof data); 2225 data.partitions = partitions; 2226 process_menu(sel_menu, &data); 2227 free_menu(sel_menu); 2228 2229 if (data.result.parts != NULL) { 2230 newsels = realloc(res->selection, 2231 sizeof(*res->selection)*(res->num_sel+1)); 2232 if (newsels != NULL) { 2233 res->selection = newsels; 2234 newsels += res->num_sel++; 2235 newsels->parts = data.result.parts; 2236 newsels->id = data.result.id; 2237 } 2238 } 2239 } 2240 2241 /* 2242 * Final cleanup 2243 */ 2244 done: 2245 free(part_menu_opts); 2246 free(partitions); 2247 2248 return res->num_sel > 0; 2249 } 2250 2251 struct part_selection_and_all_parts { 2252 struct selected_partitions *selection; 2253 struct disk_partitions **all_parts; 2254 size_t all_cnt; 2255 char *title; 2256 bool cancelled; 2257 }; 2258 2259 static int 2260 toggle_clone_data(struct menudesc *m, void *arg) 2261 { 2262 struct part_selection_and_all_parts *sel = arg; 2263 2264 sel->selection->with_data = !sel->selection->with_data; 2265 return 0; 2266 } 2267 2268 static int 2269 add_another(struct menudesc *m, void *arg) 2270 { 2271 struct part_selection_and_all_parts *sel = arg; 2272 2273 add_select_partition(sel->selection, sel->all_parts, sel->all_cnt); 2274 return 0; 2275 } 2276 2277 static int 2278 cancel_clone(struct menudesc *m, void *arg) 2279 { 2280 struct part_selection_and_all_parts *sel = arg; 2281 2282 sel->cancelled = true; 2283 return 1; 2284 } 2285 2286 static void 2287 update_sel_part_title(struct part_selection_and_all_parts *sel) 2288 { 2289 struct disk_part_info info; 2290 char *buf, line[MENUSTRSIZE]; 2291 size_t buf_len, i; 2292 2293 buf_len = MENUSTRSIZE * (1+sel->selection->num_sel); 2294 buf = malloc(buf_len); 2295 if (buf == NULL) 2296 return; 2297 2298 strcpy(buf, msg_string(MSG_select_source_hdr)); 2299 for (i = 0; i < sel->selection->num_sel; i++) { 2300 struct selected_partition *s = 2301 &sel->selection->selection[i]; 2302 if (!s->parts->pscheme->get_part_info(s->parts, s->id, &info)) 2303 continue; 2304 daddr_t start = info.start / sizemult; 2305 daddr_t size = info.size / sizemult; 2306 sprintf(line, "\n %s [%" PRIu64 " @ %" PRIu64 "] ", 2307 s->parts->disk, size, start); 2308 if (info.nat_type != NULL) 2309 strlcat(line, info.nat_type->description, sizeof(line)); 2310 strlcat(buf, line, buf_len); 2311 } 2312 free(sel->title); 2313 sel->title = buf; 2314 } 2315 2316 static void 2317 post_sel_part(struct menudesc *m, void *arg) 2318 { 2319 struct part_selection_and_all_parts *sel = arg; 2320 2321 if (m->mw == NULL) 2322 return; 2323 update_sel_part_title(sel); 2324 m->title = sel->title; 2325 m->h = 0; 2326 resize_menu_height(m); 2327 } 2328 2329 static void 2330 fmt_sel_part_line(struct menudesc *m, int i, void *arg) 2331 { 2332 struct part_selection_and_all_parts *sel = arg; 2333 2334 wprintw(m->mw, "%s: %s", msg_string(MSG_clone_with_data), 2335 sel->selection->with_data ? 2336 msg_string(MSG_Yes) : 2337 msg_string(MSG_No)); 2338 } 2339 2340 bool 2341 select_partitions(struct selected_partitions *res, 2342 const struct disk_partitions *ignore) 2343 { 2344 struct disk_desc disks[MAX_DISKS]; 2345 struct disk_partitions *ps; 2346 struct part_selection_and_all_parts data; 2347 struct pm_devs *i; 2348 size_t j; 2349 int cnt, n, m; 2350 static menu_ent men[] = { 2351 { .opt_name = MSG_select_source_add, 2352 .opt_action = add_another }, 2353 { .opt_action = toggle_clone_data }, 2354 { .opt_name = MSG_cancel, .opt_action = cancel_clone }, 2355 }; 2356 2357 memset(res, 0, sizeof *res); 2358 memset(&data, 0, sizeof data); 2359 data.selection = res; 2360 2361 /* 2362 * collect all available partition sets 2363 */ 2364 data.all_cnt = 0; 2365 if (SLIST_EMPTY(&pm_head)) { 2366 cnt = get_disks(disks, false); 2367 if (cnt <= 0) 2368 return false; 2369 2370 /* 2371 * allocate two slots for each disk (primary/secondary) 2372 */ 2373 data.all_parts = calloc(2*cnt, sizeof *data.all_parts); 2374 if (data.all_parts == NULL) 2375 return false; 2376 2377 for (n = 0; n < cnt; n++) { 2378 if (ignore != NULL && 2379 strcmp(disks[n].dd_name, ignore->disk) == 0) 2380 continue; 2381 2382 ps = partitions_read_disk(disks[n].dd_name, 2383 disks[n].dd_totsec, disks[n].dd_no_mbr); 2384 if (ps == NULL) 2385 continue; 2386 data.all_parts[data.all_cnt++] = ps; 2387 ps = get_inner_parts(ps); 2388 if (ps == NULL) 2389 continue; 2390 data.all_parts[data.all_cnt++] = ps; 2391 } 2392 if (data.all_cnt > 0) 2393 res->free_parts = true; 2394 } else { 2395 cnt = 0; 2396 SLIST_FOREACH(i, &pm_head, l) 2397 cnt++; 2398 2399 data.all_parts = calloc(cnt, sizeof *data.all_parts); 2400 if (data.all_parts == NULL) 2401 return false; 2402 2403 SLIST_FOREACH(i, &pm_head, l) { 2404 if (i->parts == NULL) 2405 continue; 2406 if (i->parts == ignore) 2407 continue; 2408 data.all_parts[data.all_cnt++] = i->parts; 2409 } 2410 } 2411 2412 if (!add_select_partition(res, data.all_parts, data.all_cnt)) 2413 goto fail; 2414 2415 /* loop with menu */ 2416 update_sel_part_title(&data); 2417 m = new_menu(data.title, men, __arraycount(men), 3, 2, 0, 65, MC_SCROLL, 2418 post_sel_part, fmt_sel_part_line, NULL, NULL, MSG_clone_src_done); 2419 process_menu(m, &data); 2420 free(data.title); 2421 if (res->num_sel == 0) 2422 goto fail; 2423 2424 /* cleanup */ 2425 if (res->free_parts) { 2426 for (j = 0; j < data.all_cnt; j++) { 2427 if (selection_has_parts(res, data.all_parts[j])) 2428 continue; 2429 if (data.all_parts[j]->parent != NULL) 2430 continue; 2431 data.all_parts[j]->pscheme->free(data.all_parts[j]); 2432 } 2433 } 2434 free(data.all_parts); 2435 return true; 2436 2437 fail: 2438 if (res->free_parts) { 2439 for (j = 0; j < data.all_cnt; j++) { 2440 if (data.all_parts[j]->parent != NULL) 2441 continue; 2442 data.all_parts[j]->pscheme->free(data.all_parts[j]); 2443 } 2444 } 2445 free(data.all_parts); 2446 return false; 2447 } 2448 2449 void 2450 free_selected_partitions(struct selected_partitions *selected) 2451 { 2452 size_t i; 2453 struct disk_partitions *parts; 2454 2455 if (!selected->free_parts) 2456 return; 2457 2458 for (i = 0; i < selected->num_sel; i++) { 2459 parts = selected->selection[i].parts; 2460 2461 /* remove from list before testing for other instances */ 2462 selected->selection[i].parts = NULL; 2463 2464 /* if this is the secondary partion set, the parent owns it */ 2465 if (parts->parent != NULL) 2466 continue; 2467 2468 /* only free once (we use the last one) */ 2469 if (selection_has_parts(selected, parts)) 2470 continue; 2471 parts->pscheme->free(parts); 2472 } 2473 free(selected->selection); 2474 } 2475 2476 daddr_t 2477 selected_parts_size(struct selected_partitions *selected) 2478 { 2479 struct disk_part_info info; 2480 size_t i; 2481 daddr_t s = 0; 2482 2483 for (i = 0; i < selected->num_sel; i++) { 2484 if (!selected->selection[i].parts->pscheme->get_part_info( 2485 selected->selection[i].parts, 2486 selected->selection[i].id, &info)) 2487 continue; 2488 s += info.size; 2489 } 2490 2491 return s; 2492 } 2493 2494 int 2495 clone_target_select(menudesc *m, void *arg) 2496 { 2497 struct clone_target_menu_data *data = arg; 2498 2499 data->res = m->cursel; 2500 return 1; 2501 } 2502 2503 bool 2504 clone_partition_data(struct disk_partitions *dest_parts, part_id did, 2505 struct disk_partitions *src_parts, part_id sid) 2506 { 2507 char src_dev[MAXPATHLEN], target_dev[MAXPATHLEN]; 2508 2509 if (!src_parts->pscheme->get_part_device( 2510 src_parts, sid, src_dev, sizeof src_dev, NULL, 2511 raw_dev_name, true)) 2512 return false; 2513 if (!dest_parts->pscheme->get_part_device( 2514 dest_parts, did, target_dev, sizeof target_dev, NULL, 2515 raw_dev_name, true)) 2516 return false; 2517 2518 return run_program(RUN_DISPLAY | RUN_PROGRESS, 2519 "progress -f %s -b 1m dd bs=1m of=%s", 2520 src_dev, target_dev) == 0; 2521 } 2522 #endif 2523 2524