1 /* $NetBSD: target.c,v 1.13 2020/02/19 18:08:03 martin Exp $ */ 2 3 /* 4 * Copyright 1997 Jonathan Stone 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed for the NetBSD Project by 18 * Jonathan Stone. 19 * 4. The name of Jonathan Stone may not be used to endorse 20 * or promote products derived from this software without specific prior 21 * written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY JONATHAN STONE ``AS IS'' 24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE 27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 33 * THE POSSIBILITY OF SUCH DAMAGE. 34 * 35 */ 36 37 /* Copyright below applies to the realpath() code */ 38 39 /* 40 * Copyright (c) 1989, 1991, 1993, 1995 41 * The Regents of the University of California. All rights reserved. 42 * 43 * This code is derived from software contributed to Berkeley by 44 * Jan-Simon Pendry. 45 * 46 * Redistribution and use in source and binary forms, with or without 47 * modification, are permitted provided that the following conditions 48 * are met: 49 * 1. Redistributions of source code must retain the above copyright 50 * notice, this list of conditions and the following disclaimer. 51 * 2. Redistributions in binary form must reproduce the above copyright 52 * notice, this list of conditions and the following disclaimer in the 53 * documentation and/or other materials provided with the distribution. 54 * 3. Neither the name of the University nor the names of its contributors 55 * may be used to endorse or promote products derived from this software 56 * without specific prior written permission. 57 * 58 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 59 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 60 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 61 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 62 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 63 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 64 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 65 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 66 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 67 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 68 * SUCH DAMAGE. 69 */ 70 71 72 #include <sys/cdefs.h> 73 #if defined(LIBC_SCCS) && !defined(lint) 74 __RCSID("$NetBSD: target.c,v 1.13 2020/02/19 18:08:03 martin Exp $"); 75 #endif 76 77 /* 78 * target.c -- path-prefixing routines to access the target installation 79 * filesystems. Makes the install tools more independent of whether 80 * we're installing into a separate filesystem hierarchy mounted under 81 * /targetroot, or into the currently active root mounted on /. 82 */ 83 84 #include <sys/param.h> /* XXX vm_param.h always defines TRUE*/ 85 #include <sys/types.h> 86 #include <sys/sysctl.h> 87 #include <sys/stat.h> /* stat() */ 88 #include <sys/mount.h> /* statfs() */ 89 90 #include <fcntl.h> 91 #include <stdio.h> 92 #include <stdarg.h> 93 #include <unistd.h> 94 #include <curses.h> /* defines TRUE, but checks */ 95 #include <errno.h> 96 97 98 #include "defs.h" 99 #include "md.h" 100 #include "msg_defs.h" 101 #include "menu_defs.h" 102 103 /* 104 * local prototypes 105 */ 106 107 static void make_prefixed_dir (const char *prefix, const char *path); 108 static int do_target_chdir (const char *dir, int flag); 109 int target_test(unsigned int mode, const char *path); 110 int target_test_dir (const char *path); /* deprecated */ 111 int target_test_file (const char *path); /* deprecated */ 112 int target_test_symlink (const char *path); /* deprecated */ 113 114 void unwind_mounts(void); 115 116 /* Record a mount for later unwinding of target mounts. */ 117 struct unwind_mount { 118 struct unwind_mount *um_prev; 119 char um_mountpoint[4]; /* Allocated longer... */ 120 }; 121 122 /* Unwind-mount stack */ 123 struct unwind_mount *unwind_mountlist = NULL; 124 125 /* 126 * Debugging options 127 */ 128 /*#define DEBUG_ROOT*/ /* turn on what-is-root? debugging. */ 129 /*#define DEBUG_UNWIND*/ /* turn on unwind-target-mount debugging. */ 130 131 /* 132 * debugging helper. curses... 133 */ 134 #if defined(DEBUG) || defined(DEBUG_ROOT) 135 void 136 backtowin(void) 137 { 138 139 fflush(stdout); /* curses does not leave stdout linebuffered. */ 140 getchar(); /* wait for user to press return */ 141 wrefresh(stdscr); 142 } 143 #endif 144 145 146 /* 147 * Is the root partition we're running from the same as the root 148 * which the user has selected to install/upgrade? 149 * Uses global variable "pm->diskdev" to find the selected device for 150 * install/upgrade. 151 */ 152 int 153 target_already_root(void) 154 { 155 char dev[PATH_MAX]; 156 int rootpart = -1; 157 static struct pm_devs *last_pm; 158 static int last_res; 159 part_id ptn; 160 struct disk_partitions *parts, *inner; 161 struct disk_part_info info; 162 163 if (pm == last_pm) 164 return last_res; 165 166 if (pm->cur_system) 167 return 1; 168 169 last_pm = pm; 170 last_res = 0; 171 172 parts = pm->parts; 173 if (parts == NULL) { 174 last_res = 0; 175 return 0; 176 } 177 178 if (pm->no_part) { 179 last_res = is_active_rootpart(pm->diskdev, -1); 180 return last_res; 181 } 182 183 if (pm->parts->pscheme->secondary_partitions != NULL) { 184 inner = pm->parts->pscheme->secondary_partitions(parts, 185 pm->ptstart, false); 186 if (inner != NULL) 187 parts = inner; 188 } 189 190 for (ptn = 0; ptn < parts->num_part; ptn++) { 191 if (!parts->pscheme->get_part_info(parts, ptn, &info)) 192 continue; 193 if (info.nat_type->generic_ptype != PT_root) 194 continue; 195 if (!is_root_part_mount(info.last_mounted)) 196 continue; 197 if (!parts->pscheme->get_part_device(parts, ptn, 198 dev, sizeof dev, &rootpart, plain_name, false, true)) 199 continue; 200 201 last_res = is_active_rootpart(dev, rootpart); 202 break; 203 } 204 205 return last_res; 206 } 207 208 /* 209 * Could something with this "last mounted on" information be a potential 210 * root partition? 211 */ 212 bool 213 is_root_part_mount(const char *last_mounted) 214 { 215 if (last_mounted == NULL) 216 return false; 217 218 return strcmp(last_mounted, "/") == 0 || 219 strcmp(last_mounted, "/targetroot") == 0 || 220 strcmp(last_mounted, "/altroot") == 0; 221 } 222 223 /* 224 * Is this device partition (e.g., "sd0a") mounted as root? 225 */ 226 int 227 is_active_rootpart(const char *dev, int ptn) 228 { 229 int mib[2]; 230 char rootdev[SSTRSIZE]; 231 int rootptn; 232 size_t varlen; 233 234 mib[0] = CTL_KERN; 235 mib[1] = KERN_ROOT_DEVICE; 236 varlen = sizeof(rootdev); 237 if (sysctl(mib, 2, rootdev, &varlen, NULL, 0) < 0) 238 return 1; 239 240 if (strcmp(dev, rootdev) != 0) 241 return 0; 242 243 if (ptn < 0) 244 return 1; /* device only check, or wedge */ 245 246 mib[1] = KERN_ROOT_PARTITION; 247 varlen = sizeof rootptn; 248 rootptn = -1; 249 if (sysctl(mib, 2, &rootptn, &varlen, NULL, 0) < 0) 250 return 1; 251 252 return ptn == rootptn; 253 } 254 255 /* 256 * Pathname prefixing glue to support installation either 257 * from in-ramdisk miniroots or on-disk diskimages. 258 * If our root is on the target disk, the install target is mounted 259 * on /targetroot and we need to prefix installed pathnames with /targetroot. 260 * otherwise we are installing to the currently-active root and 261 * no prefix is needed. 262 */ 263 const char * 264 target_prefix(void) 265 { 266 /* 267 * XXX fetch sysctl variable for current root, and compare 268 * to the devicename of the install target disk. 269 */ 270 return(target_already_root() ? "" : targetroot_mnt); 271 } 272 273 /* 274 * concatenate two pathnames. 275 * XXX returns either input args or result in a static buffer. 276 * The caller must copy if it wants to use the pathname past the 277 * next call to a target-prefixing function, or to modify the inputs.. 278 * Used only internally so this is probably safe. 279 */ 280 const char * 281 concat_paths(const char *prefix, const char *suffix) 282 { 283 static char real_path[MAXPATHLEN]; 284 285 /* absolute prefix and null suffix? */ 286 if (prefix[0] == '/' && suffix[0] == 0) 287 return prefix; 288 289 /* null prefix and absolute suffix? */ 290 if (prefix[0] == 0 && suffix[0] == '/') 291 return suffix; 292 293 /* avoid "//" */ 294 if (suffix[0] == '/' || suffix[0] == 0) 295 snprintf(real_path, sizeof(real_path), "%s%s", prefix, suffix); 296 else 297 snprintf(real_path, sizeof(real_path), "%s/%s", 298 prefix, suffix); 299 return (real_path); 300 } 301 302 /* 303 * Do target prefix expansion on a pathname. 304 * XXX uses concat_paths and so returns result in a static buffer. 305 * The caller must copy if it wants to use the pathname past the 306 * next call to a target-prefixing function, or to modify the inputs.. 307 * Used only internally so this is probably safe. 308 * 309 * Not static so other functions can generate target related file names. 310 */ 311 const char * 312 target_expand(const char *tgtpath) 313 { 314 315 return concat_paths(target_prefix(), tgtpath); 316 } 317 318 /* Make a directory, with a prefix like "/targetroot" or possibly just "". */ 319 static void 320 make_prefixed_dir(const char *prefix, const char *path) 321 { 322 323 run_program(0, "/bin/mkdir -p %s", concat_paths(prefix, path)); 324 } 325 326 /* Make a directory with a pathname relative to the installation target. */ 327 void 328 make_target_dir(const char *path) 329 { 330 331 make_prefixed_dir(target_prefix(), path); 332 } 333 334 335 static int 336 do_target_chdir(const char *dir, int must_succeed) 337 { 338 const char *tgt_dir; 339 int error; 340 341 error = 0; 342 tgt_dir = target_expand(dir); 343 344 #ifdef DEBUG 345 printf("target_chdir (%s)\n", tgt_dir); 346 //return (0); 347 #endif 348 /* chdir returns -1 on error and sets errno. */ 349 if (chdir(tgt_dir) < 0) 350 error = errno; 351 if (logfp) { 352 fprintf(logfp, "cd to %s\n", tgt_dir); 353 fflush(logfp); 354 } 355 if (script) { 356 scripting_fprintf(NULL, "cd %s\n", tgt_dir); 357 fflush(script); 358 } 359 360 if (error && must_succeed) { 361 const char *args[] = { target_prefix(), strerror(error) }; 362 char *err = str_arg_subst(msg_string(MSG_realdir), 363 __arraycount(args), args); 364 fprintf(stderr, "%s\n", err); 365 if (logfp) 366 fprintf(logfp, "%s\n", err); 367 free(err); 368 exit(1); 369 } 370 errno = error; 371 return (error); 372 } 373 374 void 375 target_chdir_or_die(const char *dir) 376 { 377 378 (void)do_target_chdir(dir, 1); 379 } 380 381 #ifdef notdef 382 int 383 target_chdir(const char *dir) 384 { 385 386 return do_target_chdir(dir, 0); 387 } 388 #endif 389 390 /* 391 * Copy a file from the current root into the target system, 392 * where the destination pathname is relative to the target root. 393 * Does not check for copy-to-self when target is current root. 394 */ 395 int 396 cp_to_target(const char *srcpath, const char *tgt_path) 397 { 398 const char *real_path = target_expand(tgt_path); 399 400 return run_program(0, "/bin/cp %s %s", srcpath, real_path); 401 } 402 403 /* 404 * Duplicate a file from the current root to the same pathname 405 * in the target system. Pathname must be an absolute pathname. 406 * If we're running in the target, do nothing. 407 */ 408 void 409 dup_file_into_target(const char *filename) 410 { 411 412 if (!target_already_root()) 413 cp_to_target(filename, filename); 414 } 415 416 417 /* 418 * Do a mv where both pathnames are within the target filesystem. 419 */ 420 void 421 mv_within_target_or_die(const char *frompath, const char *topath) 422 { 423 char realfrom[STRSIZE]; 424 char realto[STRSIZE]; 425 426 strlcpy(realfrom, target_expand(frompath), sizeof realfrom); 427 strlcpy(realto, target_expand(topath), sizeof realto); 428 429 run_program(RUN_FATAL, "mv %s %s", realfrom, realto); 430 } 431 432 /* Do a cp where both pathnames are within the target filesystem. */ 433 int 434 cp_within_target(const char *frompath, const char *topath, int optional) 435 { 436 char realfrom[STRSIZE]; 437 char realto[STRSIZE]; 438 439 strlcpy(realfrom, target_expand(frompath), sizeof realfrom); 440 strlcpy(realto, target_expand(topath), sizeof realto); 441 442 if (access(realfrom, R_OK) == -1 && optional) 443 return 0; 444 return (run_program(0, "cp -p %s %s", realfrom, realto)); 445 } 446 447 /* fopen a pathname in the target. */ 448 FILE * 449 target_fopen(const char *filename, const char *type) 450 { 451 452 return fopen(target_expand(filename), type); 453 } 454 455 /* 456 * Do a mount onto a mountpoint in the install target. 457 * Record mountpoint so we can unmount when finished. 458 * NB: does not prefix mount-from, which probably breaks nullfs mounts. 459 */ 460 int 461 target_mount_do(const char *opts, const char *from, const char *on) 462 { 463 struct unwind_mount *m; 464 int error; 465 int len; 466 467 len = strlen(on); 468 m = malloc(sizeof *m + len); 469 if (m == 0) 470 return (ENOMEM); /* XXX */ 471 472 memcpy(m->um_mountpoint, on, len + 1); 473 474 #ifdef DEBUG_UNWIND 475 endwin(); 476 fprintf(stderr, "mounting %s with unwind\n", on); 477 backtowin(); 478 #endif 479 480 error = run_program(0, "/sbin/mount %s %s %s%s", 481 opts, from, target_prefix(), on); 482 if (error) { 483 free(m); 484 return error; 485 } 486 m->um_prev = unwind_mountlist; 487 unwind_mountlist = m; 488 return 0; 489 } 490 491 /* 492 * Special case - we have mounted the target / readonly 493 * to peek at etc/fstab, and now want it undone. 494 */ 495 void 496 umount_root(void) 497 { 498 499 /* verify this is the only mount */ 500 if (unwind_mountlist == NULL) 501 return; 502 if (unwind_mountlist->um_prev != NULL) 503 return; 504 505 if (run_program(0, "/sbin/umount %s", target_prefix()) != 0) 506 return; 507 508 free(unwind_mountlist); 509 unwind_mountlist = NULL; 510 } 511 512 513 int 514 target_mount(const char *opts, const char *from, const char *on) 515 { 516 return target_mount_do(opts, from, on); 517 } 518 519 /* 520 * unwind the mount stack, unmounting mounted filesystems. 521 * For now, ignore any errors in unmount. 522 * (Why would we be unable to unmount? The user has suspended 523 * us and forked shell sitting somewhere in the target root?) 524 */ 525 void 526 unwind_mounts(void) 527 { 528 struct unwind_mount *m; 529 static volatile int unwind_in_progress = 0; 530 531 /* signal safety */ 532 if (unwind_in_progress) 533 return; 534 unwind_in_progress = 1; 535 536 while ((m = unwind_mountlist) != NULL) { 537 unwind_mountlist = m->um_prev; 538 #ifdef DEBUG_UNWIND 539 endwin(); 540 fprintf(stderr, "unmounting %s\n", m->um_mountpoint); 541 backtowin(); 542 #endif 543 run_program(0, "/sbin/umount %s%s", 544 target_prefix(), m->um_mountpoint); 545 free(m); 546 } 547 unwind_in_progress = 0; 548 } 549 550 int 551 target_collect_file(int kind, char **buffer, const char *name) 552 { 553 const char *realname = target_expand(name); 554 555 #ifdef DEBUG 556 printf("collect real name %s\n", realname); 557 #endif 558 return collect(kind, buffer, "%s", realname); 559 } 560 561 /* 562 * Verify a pathname already exists in the target root filesystem, 563 * by running test "testflag" on the expanded target pathname. 564 */ 565 int 566 target_test(unsigned int mode, const char *path) 567 { 568 const char *real_path = target_expand(path); 569 register int result; 570 571 result = !file_mode_match(real_path, mode); 572 scripting_fprintf(NULL, "if [ $? != 0 ]; then echo \"%s does not exist!\"; fi\n", real_path); 573 574 #if defined(DEBUG) 575 printf("target_test(%o, %s) returning %d\n", mode, real_path, result); 576 #endif 577 return (result); 578 } 579 580 /* 581 * Verify a directory already exists in the target root 582 * filesystem. Do not create the directory if it doesn't exist. 583 * Assumes that sysinst has already mounted the target root. 584 */ 585 int 586 target_test_dir(const char *path) 587 { 588 589 return target_test(S_IFDIR, path); 590 } 591 592 /* 593 * Verify an ordinary file already exists in the target root 594 * filesystem. Do not create the directory if it doesn't exist. 595 * Assumes that sysinst has already mounted the target root. 596 */ 597 int 598 target_test_file(const char *path) 599 { 600 601 return target_test(S_IFREG, path); 602 } 603 604 int 605 target_test_symlink(const char *path) 606 { 607 608 return target_test(S_IFLNK, path); 609 } 610 611 int 612 target_file_exists_p(const char *path) 613 { 614 615 return (target_test_file(path) == 0); 616 } 617 618 int 619 target_dir_exists_p(const char *path) 620 { 621 622 return (target_test_dir(path) == 0); 623 } 624 625 int 626 target_symlink_exists_p(const char *path) 627 { 628 629 return (target_test_symlink(path) == 0); 630 } 631 632 int 633 target_mounted(void) 634 { 635 return (unwind_mountlist != NULL); 636 } 637