1/* 2 * Copyright (c) 2012, 2013 SRI International 3 * Copyright (c) 1987, 1993 4 * The Regents of the University of California. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 4. Neither the name of the University nor the names of its contributors 15 * may be used to endorse or promote products derived from this software 16 * without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31#ifndef lint 32static const char copyright[] = 33"@(#) Copyright (c) 1987, 1993\n\ 34 The Regents of the University of California. All rights reserved.\n"; 35#endif /* not lint */ 36 37#if 0 38#ifndef lint 39static char sccsid[] = "@(#)xinstall.c 8.1 (Berkeley) 7/21/93"; 40#endif /* not lint */ 41#endif 42 43#include <sys/cdefs.h> 44__FBSDID("$FreeBSD: stable/10/usr.bin/xinstall/xinstall.c 318096 2017-05-09 19:14:26Z bdrewery $"); 45 46#include <sys/param.h> 47#include <sys/mman.h> 48#include <sys/mount.h> 49#include <sys/stat.h> 50#include <sys/time.h> 51#include <sys/wait.h> 52 53#include <err.h> 54#include <errno.h> 55#include <fcntl.h> 56#include <grp.h> 57#include <libgen.h> 58#include <md5.h> 59#include <paths.h> 60#include <pwd.h> 61#include <ripemd.h> 62#include <sha.h> 63#include <sha256.h> 64#include <sha512.h> 65#include <stdint.h> 66#include <stdio.h> 67#include <stdlib.h> 68#include <string.h> 69#include <sysexits.h> 70#include <unistd.h> 71#include <vis.h> 72 73#include "mtree.h" 74 75/* Bootstrap aid - this doesn't exist in most older releases */ 76#ifndef MAP_FAILED 77#define MAP_FAILED ((void *)-1) /* from <sys/mman.h> */ 78#endif 79 80#define MAX_CMP_SIZE (16 * 1024 * 1024) 81 82#define LN_ABSOLUTE 0x01 83#define LN_RELATIVE 0x02 84#define LN_HARD 0x04 85#define LN_SYMBOLIC 0x08 86#define LN_MIXED 0x10 87 88#define DIRECTORY 0x01 /* Tell install it's a directory. */ 89#define SETFLAGS 0x02 /* Tell install to set flags. */ 90#define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND) 91#define BACKUP_SUFFIX ".old" 92 93typedef union { 94 MD5_CTX MD5; 95 RIPEMD160_CTX RIPEMD160; 96 SHA1_CTX SHA1; 97 SHA256_CTX SHA256; 98 SHA512_CTX SHA512; 99} DIGEST_CTX; 100 101static enum { 102 DIGEST_NONE = 0, 103 DIGEST_MD5, 104 DIGEST_RIPEMD160, 105 DIGEST_SHA1, 106 DIGEST_SHA256, 107 DIGEST_SHA512, 108} digesttype = DIGEST_NONE; 109 110static gid_t gid; 111static uid_t uid; 112static int dobackup, docompare, dodir, dolink, dopreserve, dostrip, dounpriv, 113 safecopy, verbose; 114static int haveopt_f, haveopt_g, haveopt_m, haveopt_o; 115static mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; 116static FILE *metafp; 117static const char *group, *owner; 118static const char *suffix = BACKUP_SUFFIX; 119static char *destdir, *digest, *fflags, *metafile, *tags; 120 121static int compare(int, const char *, size_t, int, const char *, size_t, 122 char **); 123static char *copy(int, const char *, int, const char *, off_t); 124static int create_newfile(const char *, int, struct stat *); 125static int create_tempfile(const char *, char *, size_t); 126static char *quiet_mktemp(char *template); 127static char *digest_file(const char *); 128static void digest_init(DIGEST_CTX *); 129static void digest_update(DIGEST_CTX *, const unsigned char *, size_t); 130static char *digest_end(DIGEST_CTX *, char *); 131static int do_link(const char *, const char *, const struct stat *); 132static void do_symlink(const char *, const char *, const struct stat *); 133static void makelink(const char *, const char *, const struct stat *); 134static void install(const char *, const char *, u_long, u_int); 135static void install_dir(char *); 136static void metadata_log(const char *, const char *, struct timeval *, 137 const char *, const char *, off_t); 138static int parseid(const char *, id_t *); 139static void strip(const char *); 140static int trymmap(int); 141static void usage(void); 142 143int 144main(int argc, char *argv[]) 145{ 146 struct stat from_sb, to_sb; 147 mode_t *set; 148 u_long fset; 149 int ch, no_target; 150 u_int iflags; 151 char *p; 152 const char *to_name; 153 154 fset = 0; 155 iflags = 0; 156 group = owner = NULL; 157 while ((ch = getopt(argc, argv, "B:bCcD:df:g:h:l:M:m:N:o:pSsT:Uv")) != 158 -1) 159 switch((char)ch) { 160 case 'B': 161 suffix = optarg; 162 /* FALLTHROUGH */ 163 case 'b': 164 dobackup = 1; 165 break; 166 case 'C': 167 docompare = 1; 168 break; 169 case 'c': 170 /* For backwards compatibility. */ 171 break; 172 case 'D': 173 destdir = optarg; 174 break; 175 case 'd': 176 dodir = 1; 177 break; 178 case 'f': 179 haveopt_f = 1; 180 fflags = optarg; 181 break; 182 case 'g': 183 haveopt_g = 1; 184 group = optarg; 185 break; 186 case 'h': 187 digest = optarg; 188 break; 189 case 'l': 190 for (p = optarg; *p != '\0'; p++) 191 switch (*p) { 192 case 's': 193 dolink &= ~(LN_HARD|LN_MIXED); 194 dolink |= LN_SYMBOLIC; 195 break; 196 case 'h': 197 dolink &= ~(LN_SYMBOLIC|LN_MIXED); 198 dolink |= LN_HARD; 199 break; 200 case 'm': 201 dolink &= ~(LN_SYMBOLIC|LN_HARD); 202 dolink |= LN_MIXED; 203 break; 204 case 'a': 205 dolink &= ~LN_RELATIVE; 206 dolink |= LN_ABSOLUTE; 207 break; 208 case 'r': 209 dolink &= ~LN_ABSOLUTE; 210 dolink |= LN_RELATIVE; 211 break; 212 default: 213 errx(1, "%c: invalid link type", *p); 214 /* NOTREACHED */ 215 } 216 break; 217 case 'M': 218 metafile = optarg; 219 break; 220 case 'm': 221 haveopt_m = 1; 222 if (!(set = setmode(optarg))) 223 errx(EX_USAGE, "invalid file mode: %s", 224 optarg); 225 mode = getmode(set, 0); 226 free(set); 227 break; 228 case 'N': 229 if (!setup_getid(optarg)) 230 err(EX_OSERR, "Unable to use user and group " 231 "databases in `%s'", optarg); 232 break; 233 case 'o': 234 haveopt_o = 1; 235 owner = optarg; 236 break; 237 case 'p': 238 docompare = dopreserve = 1; 239 break; 240 case 'S': 241 safecopy = 1; 242 break; 243 case 's': 244 dostrip = 1; 245 break; 246 case 'T': 247 tags = optarg; 248 break; 249 case 'U': 250 dounpriv = 1; 251 break; 252 case 'v': 253 verbose = 1; 254 break; 255 case '?': 256 default: 257 usage(); 258 } 259 argc -= optind; 260 argv += optind; 261 262 /* some options make no sense when creating directories */ 263 if (dostrip && dodir) { 264 warnx("-d and -s may not be specified together"); 265 usage(); 266 } 267 268 if (getenv("DONTSTRIP") != NULL) { 269 warnx("DONTSTRIP set - will not strip installed binaries"); 270 dostrip = 0; 271 } 272 273 /* must have at least two arguments, except when creating directories */ 274 if (argc == 0 || (argc == 1 && !dodir)) 275 usage(); 276 277 if (digest != NULL) { 278 if (strcmp(digest, "none") == 0) { 279 digesttype = DIGEST_NONE; 280 } else if (strcmp(digest, "md5") == 0) { 281 digesttype = DIGEST_MD5; 282 } else if (strcmp(digest, "rmd160") == 0) { 283 digesttype = DIGEST_RIPEMD160; 284 } else if (strcmp(digest, "sha1") == 0) { 285 digesttype = DIGEST_SHA1; 286 } else if (strcmp(digest, "sha256") == 0) { 287 digesttype = DIGEST_SHA256; 288 } else if (strcmp(digest, "sha512") == 0) { 289 digesttype = DIGEST_SHA512; 290 } else { 291 warnx("unknown digest `%s'", digest); 292 usage(); 293 } 294 } 295 296 /* need to make a temp copy so we can compare stripped version */ 297 if (docompare && dostrip) 298 safecopy = 1; 299 300 /* get group and owner id's */ 301 if (group != NULL && !dounpriv) { 302 if (gid_from_group(group, &gid) == -1) { 303 id_t id; 304 if (!parseid(group, &id)) 305 errx(1, "unknown group %s", group); 306 gid = id; 307 } 308 } else 309 gid = (gid_t)-1; 310 311 if (owner != NULL && !dounpriv) { 312 if (uid_from_user(owner, &uid) == -1) { 313 id_t id; 314 if (!parseid(owner, &id)) 315 errx(1, "unknown user %s", owner); 316 uid = id; 317 } 318 } else 319 uid = (uid_t)-1; 320 321 if (fflags != NULL && !dounpriv) { 322 if (strtofflags(&fflags, &fset, NULL)) 323 errx(EX_USAGE, "%s: invalid flag", fflags); 324 iflags |= SETFLAGS; 325 } 326 327 if (metafile != NULL) { 328 if ((metafp = fopen(metafile, "a")) == NULL) 329 warn("open %s", metafile); 330 } else 331 digesttype = DIGEST_NONE; 332 333 if (dodir) { 334 for (; *argv != NULL; ++argv) 335 install_dir(*argv); 336 exit(EX_OK); 337 /* NOTREACHED */ 338 } 339 340 to_name = argv[argc - 1]; 341 no_target = stat(to_name, &to_sb); 342 if (!no_target && S_ISDIR(to_sb.st_mode)) { 343 if (dolink & LN_SYMBOLIC) { 344 if (lstat(to_name, &to_sb) != 0) 345 err(EX_OSERR, "%s vanished", to_name); 346 if (S_ISLNK(to_sb.st_mode)) { 347 if (argc != 2) { 348 errno = ENOTDIR; 349 err(EX_USAGE, "%s", to_name); 350 } 351 install(*argv, to_name, fset, iflags); 352 exit(EX_OK); 353 } 354 } 355 for (; *argv != to_name; ++argv) 356 install(*argv, to_name, fset, iflags | DIRECTORY); 357 exit(EX_OK); 358 /* NOTREACHED */ 359 } 360 361 /* can't do file1 file2 directory/file */ 362 if (argc != 2) { 363 if (no_target) 364 warnx("target directory `%s' does not exist", 365 argv[argc - 1]); 366 else 367 warnx("target `%s' is not a directory", 368 argv[argc - 1]); 369 usage(); 370 } 371 372 if (!no_target && !dolink) { 373 if (stat(*argv, &from_sb)) 374 err(EX_OSERR, "%s", *argv); 375 if (!S_ISREG(to_sb.st_mode)) { 376 errno = EFTYPE; 377 err(EX_OSERR, "%s", to_name); 378 } 379 if (to_sb.st_dev == from_sb.st_dev && 380 to_sb.st_ino == from_sb.st_ino) 381 errx(EX_USAGE, 382 "%s and %s are the same file", *argv, to_name); 383 } 384 install(*argv, to_name, fset, iflags); 385 exit(EX_OK); 386 /* NOTREACHED */ 387} 388 389static char * 390digest_file(const char *name) 391{ 392 393 switch (digesttype) { 394 case DIGEST_MD5: 395 return (MD5File(name, NULL)); 396 case DIGEST_RIPEMD160: 397 return (RIPEMD160_File(name, NULL)); 398 case DIGEST_SHA1: 399 return (SHA1_File(name, NULL)); 400 case DIGEST_SHA256: 401 return (SHA256_File(name, NULL)); 402 case DIGEST_SHA512: 403 return (SHA512_File(name, NULL)); 404 default: 405 return (NULL); 406 } 407} 408 409static void 410digest_init(DIGEST_CTX *c) 411{ 412 413 switch (digesttype) { 414 case DIGEST_NONE: 415 break; 416 case DIGEST_MD5: 417 MD5Init(&(c->MD5)); 418 break; 419 case DIGEST_RIPEMD160: 420 RIPEMD160_Init(&(c->RIPEMD160)); 421 break; 422 case DIGEST_SHA1: 423 SHA1_Init(&(c->SHA1)); 424 break; 425 case DIGEST_SHA256: 426 SHA256_Init(&(c->SHA256)); 427 break; 428 case DIGEST_SHA512: 429 SHA512_Init(&(c->SHA512)); 430 break; 431 } 432} 433 434static void 435digest_update(DIGEST_CTX *c, const unsigned char *data, size_t len) 436{ 437 438 switch (digesttype) { 439 case DIGEST_NONE: 440 break; 441 case DIGEST_MD5: 442 MD5Update(&(c->MD5), data, len); 443 break; 444 case DIGEST_RIPEMD160: 445 RIPEMD160_Update(&(c->RIPEMD160), data, len); 446 break; 447 case DIGEST_SHA1: 448 SHA1_Update(&(c->SHA1), data, len); 449 break; 450 case DIGEST_SHA256: 451 SHA256_Update(&(c->SHA256), data, len); 452 break; 453 case DIGEST_SHA512: 454 SHA512_Update(&(c->SHA512), data, len); 455 break; 456 } 457} 458 459static char * 460digest_end(DIGEST_CTX *c, char *buf) 461{ 462 463 switch (digesttype) { 464 case DIGEST_MD5: 465 return (MD5End(&(c->MD5), buf)); 466 case DIGEST_RIPEMD160: 467 return (RIPEMD160_End(&(c->RIPEMD160), buf)); 468 case DIGEST_SHA1: 469 return (SHA1_End(&(c->SHA1), buf)); 470 case DIGEST_SHA256: 471 return (SHA256_End(&(c->SHA256), buf)); 472 case DIGEST_SHA512: 473 return (SHA512_End(&(c->SHA512), buf)); 474 default: 475 return (NULL); 476 } 477} 478 479/* 480 * parseid -- 481 * parse uid or gid from arg into id, returning non-zero if successful 482 */ 483static int 484parseid(const char *name, id_t *id) 485{ 486 char *ep; 487 errno = 0; 488 *id = (id_t)strtoul(name, &ep, 10); 489 if (errno || *ep != '\0') 490 return (0); 491 return (1); 492} 493 494/* 495 * quiet_mktemp -- 496 * mktemp implementation used mkstemp to avoid mktemp warnings. We 497 * really do need mktemp semantics here as we will be creating a link. 498 */ 499static char * 500quiet_mktemp(char *template) 501{ 502 int fd; 503 504 if ((fd = mkstemp(template)) == -1) 505 return (NULL); 506 close (fd); 507 if (unlink(template) == -1) 508 err(EX_OSERR, "unlink %s", template); 509 return (template); 510} 511 512/* 513 * do_link -- 514 * make a hard link, obeying dorename if set 515 * return -1 on failure 516 */ 517static int 518do_link(const char *from_name, const char *to_name, 519 const struct stat *target_sb) 520{ 521 char tmpl[MAXPATHLEN]; 522 int ret; 523 524 if (safecopy && target_sb != NULL) { 525 (void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name); 526 /* This usage is safe. */ 527 if (quiet_mktemp(tmpl) == NULL) 528 err(EX_OSERR, "%s: mktemp", tmpl); 529 ret = link(from_name, tmpl); 530 if (ret == 0) { 531 if (target_sb->st_mode & S_IFDIR && rmdir(to_name) == 532 -1) { 533 unlink(tmpl); 534 err(EX_OSERR, "%s", to_name); 535 } 536 if (target_sb->st_flags & NOCHANGEBITS) 537 (void)chflags(to_name, target_sb->st_flags & 538 ~NOCHANGEBITS); 539 if (verbose) 540 printf("install: link %s -> %s\n", 541 from_name, to_name); 542 ret = rename(tmpl, to_name); 543 /* 544 * If rename has posix semantics, then the temporary 545 * file may still exist when from_name and to_name point 546 * to the same file, so unlink it unconditionally. 547 */ 548 (void)unlink(tmpl); 549 } 550 return (ret); 551 } else { 552 if (verbose) 553 printf("install: link %s -> %s\n", 554 from_name, to_name); 555 return (link(from_name, to_name)); 556 } 557} 558 559/* 560 * do_symlink -- 561 * Make a symbolic link, obeying dorename if set. Exit on failure. 562 */ 563static void 564do_symlink(const char *from_name, const char *to_name, 565 const struct stat *target_sb) 566{ 567 char tmpl[MAXPATHLEN]; 568 569 if (safecopy && target_sb != NULL) { 570 (void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name); 571 /* This usage is safe. */ 572 if (quiet_mktemp(tmpl) == NULL) 573 err(EX_OSERR, "%s: mktemp", tmpl); 574 575 if (symlink(from_name, tmpl) == -1) 576 err(EX_OSERR, "symlink %s -> %s", from_name, tmpl); 577 578 if (target_sb->st_mode & S_IFDIR && rmdir(to_name) == -1) { 579 (void)unlink(tmpl); 580 err(EX_OSERR, "%s", to_name); 581 } 582 if (target_sb->st_flags & NOCHANGEBITS) 583 (void)chflags(to_name, target_sb->st_flags & 584 ~NOCHANGEBITS); 585 if (verbose) 586 printf("install: symlink %s -> %s\n", 587 from_name, to_name); 588 if (rename(tmpl, to_name) == -1) { 589 /* Remove temporary link before exiting. */ 590 (void)unlink(tmpl); 591 err(EX_OSERR, "%s: rename", to_name); 592 } 593 } else { 594 if (verbose) 595 printf("install: symlink %s -> %s\n", 596 from_name, to_name); 597 if (symlink(from_name, to_name) == -1) 598 err(EX_OSERR, "symlink %s -> %s", from_name, to_name); 599 } 600} 601 602/* 603 * makelink -- 604 * make a link from source to destination 605 */ 606static void 607makelink(const char *from_name, const char *to_name, 608 const struct stat *target_sb) 609{ 610 char src[MAXPATHLEN], dst[MAXPATHLEN], lnk[MAXPATHLEN]; 611 struct stat to_sb; 612 613 /* Try hard links first. */ 614 if (dolink & (LN_HARD|LN_MIXED)) { 615 if (do_link(from_name, to_name, target_sb) == -1) { 616 if ((dolink & LN_HARD) || errno != EXDEV) 617 err(EX_OSERR, "link %s -> %s", from_name, to_name); 618 } else { 619 if (stat(to_name, &to_sb)) 620 err(EX_OSERR, "%s: stat", to_name); 621 if (S_ISREG(to_sb.st_mode)) { 622 /* 623 * XXX: hard links to anything other than 624 * plain files are not metalogged 625 */ 626 int omode; 627 const char *oowner, *ogroup; 628 char *offlags; 629 char *dres; 630 631 /* 632 * XXX: use underlying perms, unless 633 * overridden on command line. 634 */ 635 omode = mode; 636 if (!haveopt_m) 637 mode = (to_sb.st_mode & 0777); 638 oowner = owner; 639 if (!haveopt_o) 640 owner = NULL; 641 ogroup = group; 642 if (!haveopt_g) 643 group = NULL; 644 offlags = fflags; 645 if (!haveopt_f) 646 fflags = NULL; 647 dres = digest_file(from_name); 648 metadata_log(to_name, "file", NULL, NULL, 649 dres, to_sb.st_size); 650 free(dres); 651 mode = omode; 652 owner = oowner; 653 group = ogroup; 654 fflags = offlags; 655 } 656 return; 657 } 658 } 659 660 /* Symbolic links. */ 661 if (dolink & LN_ABSOLUTE) { 662 /* Convert source path to absolute. */ 663 if (realpath(from_name, src) == NULL) 664 err(EX_OSERR, "%s: realpath", from_name); 665 do_symlink(src, to_name, target_sb); 666 /* XXX: src may point outside of destdir */ 667 metadata_log(to_name, "link", NULL, src, NULL, 0); 668 return; 669 } 670 671 if (dolink & LN_RELATIVE) { 672 char *to_name_copy, *cp, *d, *s; 673 674 /* Resolve pathnames. */ 675 if (realpath(from_name, src) == NULL) 676 err(EX_OSERR, "%s: realpath", from_name); 677 678 /* 679 * The last component of to_name may be a symlink, 680 * so use realpath to resolve only the directory. 681 */ 682 to_name_copy = strdup(to_name); 683 if (to_name_copy == NULL) 684 err(EX_OSERR, "%s: strdup", to_name); 685 cp = dirname(to_name_copy); 686 if (realpath(cp, dst) == NULL) 687 err(EX_OSERR, "%s: realpath", cp); 688 /* .. and add the last component. */ 689 if (strcmp(dst, "/") != 0) { 690 if (strlcat(dst, "/", sizeof(dst)) > sizeof(dst)) 691 errx(1, "resolved pathname too long"); 692 } 693 strcpy(to_name_copy, to_name); 694 cp = basename(to_name_copy); 695 if (strlcat(dst, cp, sizeof(dst)) > sizeof(dst)) 696 errx(1, "resolved pathname too long"); 697 free(to_name_copy); 698 699 /* Trim common path components. */ 700 for (s = src, d = dst; *s == *d; s++, d++) 701 continue; 702 while (*s != '/') 703 s--, d--; 704 705 /* Count the number of directories we need to backtrack. */ 706 for (++d, lnk[0] = '\0'; *d; d++) 707 if (*d == '/') 708 (void)strlcat(lnk, "../", sizeof(lnk)); 709 710 (void)strlcat(lnk, ++s, sizeof(lnk)); 711 712 do_symlink(lnk, to_name, target_sb); 713 /* XXX: Link may point outside of destdir. */ 714 metadata_log(to_name, "link", NULL, lnk, NULL, 0); 715 return; 716 } 717 718 /* 719 * If absolute or relative was not specified, try the names the 720 * user provided. 721 */ 722 do_symlink(from_name, to_name, target_sb); 723 /* XXX: from_name may point outside of destdir. */ 724 metadata_log(to_name, "link", NULL, from_name, NULL, 0); 725} 726 727/* 728 * install -- 729 * build a path name and install the file 730 */ 731static void 732install(const char *from_name, const char *to_name, u_long fset, u_int flags) 733{ 734 struct stat from_sb, temp_sb, to_sb; 735 struct timeval tvb[2]; 736 int devnull, files_match, from_fd, serrno, target; 737 int tempcopy, temp_fd, to_fd; 738 char backup[MAXPATHLEN], *p, pathbuf[MAXPATHLEN], tempfile[MAXPATHLEN]; 739 char *digestresult; 740 741 files_match = 0; 742 from_fd = -1; 743 to_fd = -1; 744 745 /* If try to install NULL file to a directory, fails. */ 746 if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) { 747 if (!dolink) { 748 if (stat(from_name, &from_sb)) 749 err(EX_OSERR, "%s", from_name); 750 if (!S_ISREG(from_sb.st_mode)) { 751 errno = EFTYPE; 752 err(EX_OSERR, "%s", from_name); 753 } 754 } 755 /* Build the target path. */ 756 if (flags & DIRECTORY) { 757 (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s", 758 to_name, 759 (p = strrchr(from_name, '/')) ? ++p : from_name); 760 to_name = pathbuf; 761 } 762 devnull = 0; 763 } else { 764 devnull = 1; 765 } 766 767 target = (lstat(to_name, &to_sb) == 0); 768 769 if (dolink) { 770 if (target && !safecopy) { 771 if (to_sb.st_mode & S_IFDIR && rmdir(to_name) == -1) 772 err(EX_OSERR, "%s", to_name); 773 if (to_sb.st_flags & NOCHANGEBITS) 774 (void)chflags(to_name, 775 to_sb.st_flags & ~NOCHANGEBITS); 776 unlink(to_name); 777 } 778 makelink(from_name, to_name, target ? &to_sb : NULL); 779 return; 780 } 781 782 if (target && !S_ISREG(to_sb.st_mode) && !S_ISLNK(to_sb.st_mode)) { 783 errno = EFTYPE; 784 warn("%s", to_name); 785 return; 786 } 787 788 /* Only copy safe if the target exists. */ 789 tempcopy = safecopy && target; 790 791 if (!devnull && (from_fd = open(from_name, O_RDONLY, 0)) < 0) 792 err(EX_OSERR, "%s", from_name); 793 794 /* If we don't strip, we can compare first. */ 795 if (docompare && !dostrip && target && S_ISREG(to_sb.st_mode)) { 796 if ((to_fd = open(to_name, O_RDONLY, 0)) < 0) 797 err(EX_OSERR, "%s", to_name); 798 if (devnull) 799 files_match = to_sb.st_size == 0; 800 else 801 files_match = !(compare(from_fd, from_name, 802 (size_t)from_sb.st_size, to_fd, 803 to_name, (size_t)to_sb.st_size, &digestresult)); 804 805 /* Close "to" file unless we match. */ 806 if (!files_match) 807 (void)close(to_fd); 808 } 809 810 if (!files_match) { 811 if (tempcopy) { 812 to_fd = create_tempfile(to_name, tempfile, 813 sizeof(tempfile)); 814 if (to_fd < 0) 815 err(EX_OSERR, "%s", tempfile); 816 } else { 817 if ((to_fd = create_newfile(to_name, target, 818 &to_sb)) < 0) 819 err(EX_OSERR, "%s", to_name); 820 if (verbose) 821 (void)printf("install: %s -> %s\n", 822 from_name, to_name); 823 } 824 if (!devnull) 825 digestresult = copy(from_fd, from_name, to_fd, 826 tempcopy ? tempfile : to_name, from_sb.st_size); 827 else 828 digestresult = NULL; 829 } 830 831 if (dostrip) { 832 strip(tempcopy ? tempfile : to_name); 833 834 /* 835 * Re-open our fd on the target, in case we used a strip 836 * that does not work in-place -- like GNU binutils strip. 837 */ 838 close(to_fd); 839 to_fd = open(tempcopy ? tempfile : to_name, O_RDONLY, 0); 840 if (to_fd < 0) 841 err(EX_OSERR, "stripping %s", to_name); 842 } 843 844 /* 845 * Compare the stripped temp file with the target. 846 */ 847 if (docompare && dostrip && target && S_ISREG(to_sb.st_mode)) { 848 temp_fd = to_fd; 849 850 /* Re-open to_fd using the real target name. */ 851 if ((to_fd = open(to_name, O_RDONLY, 0)) < 0) 852 err(EX_OSERR, "%s", to_name); 853 854 if (fstat(temp_fd, &temp_sb)) { 855 serrno = errno; 856 (void)unlink(tempfile); 857 errno = serrno; 858 err(EX_OSERR, "%s", tempfile); 859 } 860 861 if (compare(temp_fd, tempfile, (size_t)temp_sb.st_size, to_fd, 862 to_name, (size_t)to_sb.st_size, &digestresult) 863 == 0) { 864 /* 865 * If target has more than one link we need to 866 * replace it in order to snap the extra links. 867 * Need to preserve target file times, though. 868 */ 869 if (to_sb.st_nlink != 1) { 870 tvb[0].tv_sec = to_sb.st_atime; 871 tvb[0].tv_usec = 0; 872 tvb[1].tv_sec = to_sb.st_mtime; 873 tvb[1].tv_usec = 0; 874 (void)utimes(tempfile, tvb); 875 } else { 876 files_match = 1; 877 (void)unlink(tempfile); 878 } 879 (void) close(temp_fd); 880 } 881 } else if (dostrip) 882 digestresult = digest_file(tempfile); 883 884 /* 885 * Move the new file into place if doing a safe copy 886 * and the files are different (or just not compared). 887 */ 888 if (tempcopy && !files_match) { 889 /* Try to turn off the immutable bits. */ 890 if (to_sb.st_flags & NOCHANGEBITS) 891 (void)chflags(to_name, to_sb.st_flags & ~NOCHANGEBITS); 892 if (dobackup) { 893 if ((size_t)snprintf(backup, MAXPATHLEN, "%s%s", to_name, 894 suffix) != strlen(to_name) + strlen(suffix)) { 895 unlink(tempfile); 896 errx(EX_OSERR, "%s: backup filename too long", 897 to_name); 898 } 899 if (verbose) 900 (void)printf("install: %s -> %s\n", to_name, backup); 901 if (unlink(backup) < 0 && errno != ENOENT) { 902 serrno = errno; 903 if (to_sb.st_flags & NOCHANGEBITS) 904 (void)chflags(to_name, to_sb.st_flags); 905 unlink(tempfile); 906 errno = serrno; 907 err(EX_OSERR, "unlink: %s", backup); 908 } 909 if (link(to_name, backup) < 0) { 910 serrno = errno; 911 unlink(tempfile); 912 if (to_sb.st_flags & NOCHANGEBITS) 913 (void)chflags(to_name, to_sb.st_flags); 914 errno = serrno; 915 err(EX_OSERR, "link: %s to %s", to_name, 916 backup); 917 } 918 } 919 if (verbose) 920 (void)printf("install: %s -> %s\n", from_name, to_name); 921 if (rename(tempfile, to_name) < 0) { 922 serrno = errno; 923 unlink(tempfile); 924 errno = serrno; 925 err(EX_OSERR, "rename: %s to %s", 926 tempfile, to_name); 927 } 928 929 /* Re-open to_fd so we aren't hosed by the rename(2). */ 930 (void) close(to_fd); 931 if ((to_fd = open(to_name, O_RDONLY, 0)) < 0) 932 err(EX_OSERR, "%s", to_name); 933 } 934 935 /* 936 * Preserve the timestamp of the source file if necessary. 937 */ 938 if (dopreserve && !files_match && !devnull) { 939 tvb[0].tv_sec = from_sb.st_atime; 940 tvb[0].tv_usec = 0; 941 tvb[1].tv_sec = from_sb.st_mtime; 942 tvb[1].tv_usec = 0; 943 (void)utimes(to_name, tvb); 944 } 945 946 if (fstat(to_fd, &to_sb) == -1) { 947 serrno = errno; 948 (void)unlink(to_name); 949 errno = serrno; 950 err(EX_OSERR, "%s", to_name); 951 } 952 953 /* 954 * Set owner, group, mode for target; do the chown first, 955 * chown may lose the setuid bits. 956 */ 957 if (!dounpriv && ((gid != (gid_t)-1 && gid != to_sb.st_gid) || 958 (uid != (uid_t)-1 && uid != to_sb.st_uid) || 959 (mode != (to_sb.st_mode & ALLPERMS)))) { 960 /* Try to turn off the immutable bits. */ 961 if (to_sb.st_flags & NOCHANGEBITS) 962 (void)fchflags(to_fd, to_sb.st_flags & ~NOCHANGEBITS); 963 } 964 965 if (!dounpriv & 966 (gid != (gid_t)-1 && gid != to_sb.st_gid) || 967 (uid != (uid_t)-1 && uid != to_sb.st_uid)) 968 if (fchown(to_fd, uid, gid) == -1) { 969 serrno = errno; 970 (void)unlink(to_name); 971 errno = serrno; 972 err(EX_OSERR,"%s: chown/chgrp", to_name); 973 } 974 975 if (mode != (to_sb.st_mode & ALLPERMS)) { 976 if (fchmod(to_fd, 977 dounpriv ? mode & (S_IRWXU|S_IRWXG|S_IRWXO) : mode)) { 978 serrno = errno; 979 (void)unlink(to_name); 980 errno = serrno; 981 err(EX_OSERR, "%s: chmod", to_name); 982 } 983 } 984 985 /* 986 * If provided a set of flags, set them, otherwise, preserve the 987 * flags, except for the dump flag. 988 * NFS does not support flags. Ignore EOPNOTSUPP flags if we're just 989 * trying to turn off UF_NODUMP. If we're trying to set real flags, 990 * then warn if the fs doesn't support it, otherwise fail. 991 */ 992 if (!dounpriv & !devnull && (flags & SETFLAGS || 993 (from_sb.st_flags & ~UF_NODUMP) != to_sb.st_flags) && 994 fchflags(to_fd, 995 flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) { 996 if (flags & SETFLAGS) { 997 if (errno == EOPNOTSUPP) 998 warn("%s: chflags", to_name); 999 else { 1000 serrno = errno; 1001 (void)unlink(to_name); 1002 errno = serrno; 1003 err(EX_OSERR, "%s: chflags", to_name); 1004 } 1005 } 1006 } 1007 1008 (void)close(to_fd); 1009 if (!devnull) 1010 (void)close(from_fd); 1011 1012 metadata_log(to_name, "file", tvb, NULL, digestresult, to_sb.st_size); 1013 free(digestresult); 1014} 1015 1016/* 1017 * compare -- 1018 * compare two files; non-zero means files differ 1019 */ 1020static int 1021compare(int from_fd, const char *from_name __unused, size_t from_len, 1022 int to_fd, const char *to_name __unused, size_t to_len, 1023 char **dresp) 1024{ 1025 char *p, *q; 1026 int rv; 1027 int done_compare; 1028 DIGEST_CTX ctx; 1029 1030 rv = 0; 1031 if (from_len != to_len) 1032 return 1; 1033 1034 if (from_len <= MAX_CMP_SIZE) { 1035 if (dresp != NULL) 1036 digest_init(&ctx); 1037 done_compare = 0; 1038 if (trymmap(from_fd) && trymmap(to_fd)) { 1039 p = mmap(NULL, from_len, PROT_READ, MAP_SHARED, 1040 from_fd, (off_t)0); 1041 if (p == (char *)MAP_FAILED) 1042 goto out; 1043 q = mmap(NULL, from_len, PROT_READ, MAP_SHARED, 1044 to_fd, (off_t)0); 1045 if (q == (char *)MAP_FAILED) { 1046 munmap(p, from_len); 1047 goto out; 1048 } 1049 1050 rv = memcmp(p, q, from_len); 1051 if (dresp != NULL) 1052 digest_update(&ctx, p, from_len); 1053 munmap(p, from_len); 1054 munmap(q, from_len); 1055 done_compare = 1; 1056 } 1057 out: 1058 if (!done_compare) { 1059 char buf1[MAXBSIZE]; 1060 char buf2[MAXBSIZE]; 1061 int n1, n2; 1062 1063 rv = 0; 1064 lseek(from_fd, 0, SEEK_SET); 1065 lseek(to_fd, 0, SEEK_SET); 1066 while (rv == 0) { 1067 n1 = read(from_fd, buf1, sizeof(buf1)); 1068 if (n1 == 0) 1069 break; /* EOF */ 1070 else if (n1 > 0) { 1071 n2 = read(to_fd, buf2, n1); 1072 if (n2 == n1) 1073 rv = memcmp(buf1, buf2, n1); 1074 else 1075 rv = 1; /* out of sync */ 1076 } else 1077 rv = 1; /* read failure */ 1078 digest_update(&ctx, buf1, n1); 1079 } 1080 lseek(from_fd, 0, SEEK_SET); 1081 lseek(to_fd, 0, SEEK_SET); 1082 } 1083 } else 1084 rv = 1; /* don't bother in this case */ 1085 1086 if (dresp != NULL) { 1087 if (rv == 0) 1088 *dresp = digest_end(&ctx, NULL); 1089 else 1090 (void)digest_end(&ctx, NULL); 1091 } 1092 1093 return rv; 1094} 1095 1096/* 1097 * create_tempfile -- 1098 * create a temporary file based on path and open it 1099 */ 1100static int 1101create_tempfile(const char *path, char *temp, size_t tsize) 1102{ 1103 char *p; 1104 1105 (void)strncpy(temp, path, tsize); 1106 temp[tsize - 1] = '\0'; 1107 if ((p = strrchr(temp, '/')) != NULL) 1108 p++; 1109 else 1110 p = temp; 1111 (void)strncpy(p, "INS@XXXX", &temp[tsize - 1] - p); 1112 temp[tsize - 1] = '\0'; 1113 return (mkstemp(temp)); 1114} 1115 1116/* 1117 * create_newfile -- 1118 * create a new file, overwriting an existing one if necessary 1119 */ 1120static int 1121create_newfile(const char *path, int target, struct stat *sbp) 1122{ 1123 char backup[MAXPATHLEN]; 1124 int saved_errno = 0; 1125 int newfd; 1126 1127 if (target) { 1128 /* 1129 * Unlink now... avoid ETXTBSY errors later. Try to turn 1130 * off the append/immutable bits -- if we fail, go ahead, 1131 * it might work. 1132 */ 1133 if (sbp->st_flags & NOCHANGEBITS) 1134 (void)chflags(path, sbp->st_flags & ~NOCHANGEBITS); 1135 1136 if (dobackup) { 1137 if ((size_t)snprintf(backup, MAXPATHLEN, "%s%s", 1138 path, suffix) != strlen(path) + strlen(suffix)) { 1139 saved_errno = errno; 1140 if (sbp->st_flags & NOCHANGEBITS) 1141 (void)chflags(path, sbp->st_flags); 1142 errno = saved_errno; 1143 errx(EX_OSERR, "%s: backup filename too long", 1144 path); 1145 } 1146 (void)snprintf(backup, MAXPATHLEN, "%s%s", 1147 path, suffix); 1148 if (verbose) 1149 (void)printf("install: %s -> %s\n", 1150 path, backup); 1151 if (rename(path, backup) < 0) { 1152 saved_errno = errno; 1153 if (sbp->st_flags & NOCHANGEBITS) 1154 (void)chflags(path, sbp->st_flags); 1155 errno = saved_errno; 1156 err(EX_OSERR, "rename: %s to %s", path, backup); 1157 } 1158 } else 1159 if (unlink(path) < 0) 1160 saved_errno = errno; 1161 } 1162 1163 newfd = open(path, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR); 1164 if (newfd < 0 && saved_errno != 0) 1165 errno = saved_errno; 1166 return newfd; 1167} 1168 1169/* 1170 * copy -- 1171 * copy from one file to another 1172 */ 1173static char * 1174copy(int from_fd, const char *from_name, int to_fd, const char *to_name, 1175 off_t size) 1176{ 1177 int nr, nw; 1178 int serrno; 1179 char *p, buf[MAXBSIZE]; 1180 int done_copy; 1181 DIGEST_CTX ctx; 1182 1183 /* Rewind file descriptors. */ 1184 if (lseek(from_fd, (off_t)0, SEEK_SET) == (off_t)-1) 1185 err(EX_OSERR, "lseek: %s", from_name); 1186 if (lseek(to_fd, (off_t)0, SEEK_SET) == (off_t)-1) 1187 err(EX_OSERR, "lseek: %s", to_name); 1188 1189 digest_init(&ctx); 1190 1191 /* 1192 * Mmap and write if less than 8M (the limit is so we don't totally 1193 * trash memory on big files. This is really a minor hack, but it 1194 * wins some CPU back. 1195 */ 1196 done_copy = 0; 1197 if (size <= 8 * 1048576 && trymmap(from_fd) && 1198 (p = mmap(NULL, (size_t)size, PROT_READ, MAP_SHARED, 1199 from_fd, (off_t)0)) != (char *)MAP_FAILED) { 1200 nw = write(to_fd, p, size); 1201 if (nw != size) { 1202 serrno = errno; 1203 (void)unlink(to_name); 1204 if (nw >= 0) { 1205 errx(EX_OSERR, 1206 "short write to %s: %jd bytes written, %jd bytes asked to write", 1207 to_name, (uintmax_t)nw, (uintmax_t)size); 1208 } else { 1209 errno = serrno; 1210 err(EX_OSERR, "%s", to_name); 1211 } 1212 } 1213 digest_update(&ctx, p, size); 1214 (void)munmap(p, size); 1215 done_copy = 1; 1216 } 1217 if (!done_copy) { 1218 while ((nr = read(from_fd, buf, sizeof(buf))) > 0) { 1219 if ((nw = write(to_fd, buf, nr)) != nr) { 1220 serrno = errno; 1221 (void)unlink(to_name); 1222 if (nw >= 0) { 1223 errx(EX_OSERR, 1224 "short write to %s: %jd bytes written, %jd bytes asked to write", 1225 to_name, (uintmax_t)nw, 1226 (uintmax_t)size); 1227 } else { 1228 errno = serrno; 1229 err(EX_OSERR, "%s", to_name); 1230 } 1231 } 1232 digest_update(&ctx, buf, nr); 1233 } 1234 if (nr != 0) { 1235 serrno = errno; 1236 (void)unlink(to_name); 1237 errno = serrno; 1238 err(EX_OSERR, "%s", from_name); 1239 } 1240 } 1241 return (digest_end(&ctx, NULL)); 1242} 1243 1244/* 1245 * strip -- 1246 * use strip(1) to strip the target file 1247 */ 1248static void 1249strip(const char *to_name) 1250{ 1251 const char *stripbin; 1252 int serrno, status; 1253 1254 switch (fork()) { 1255 case -1: 1256 serrno = errno; 1257 (void)unlink(to_name); 1258 errno = serrno; 1259 err(EX_TEMPFAIL, "fork"); 1260 case 0: 1261 stripbin = getenv("STRIPBIN"); 1262 if (stripbin == NULL) 1263 stripbin = "strip"; 1264 execlp(stripbin, stripbin, to_name, (char *)NULL); 1265 err(EX_OSERR, "exec(%s)", stripbin); 1266 default: 1267 if (wait(&status) == -1 || status) { 1268 serrno = errno; 1269 (void)unlink(to_name); 1270 errc(EX_SOFTWARE, serrno, "wait"); 1271 /* NOTREACHED */ 1272 } 1273 } 1274} 1275 1276/* 1277 * install_dir -- 1278 * build directory hierarchy 1279 */ 1280static void 1281install_dir(char *path) 1282{ 1283 char *p; 1284 struct stat sb; 1285 int ch; 1286 1287 for (p = path;; ++p) 1288 if (!*p || (p != path && *p == '/')) { 1289 ch = *p; 1290 *p = '\0'; 1291 if (stat(path, &sb)) { 1292 if (errno != ENOENT || mkdir(path, 0755) < 0) { 1293 err(EX_OSERR, "mkdir %s", path); 1294 /* NOTREACHED */ 1295 } else if (verbose) 1296 (void)printf("install: mkdir %s\n", 1297 path); 1298 } else if (!S_ISDIR(sb.st_mode)) 1299 errx(EX_OSERR, "%s exists but is not a directory", path); 1300 if (!(*p = ch)) 1301 break; 1302 } 1303 1304 if (!dounpriv) { 1305 if ((gid != (gid_t)-1 || uid != (uid_t)-1) && 1306 chown(path, uid, gid)) 1307 warn("chown %u:%u %s", uid, gid, path); 1308 /* XXXBED: should we do the chmod in the dounpriv case? */ 1309 if (chmod(path, mode)) 1310 warn("chmod %o %s", mode, path); 1311 } 1312 metadata_log(path, "dir", NULL, NULL, NULL, 0); 1313} 1314 1315/* 1316 * metadata_log -- 1317 * if metafp is not NULL, output mtree(8) full path name and settings to 1318 * metafp, to allow permissions to be set correctly by other tools, 1319 * or to allow integrity checks to be performed. 1320 */ 1321static void 1322metadata_log(const char *path, const char *type, struct timeval *tv, 1323 const char *slink, const char *digestresult, off_t size) 1324{ 1325 static const char extra[] = { ' ', '\t', '\n', '\\', '#', '\0' }; 1326 const char *p; 1327 char *buf; 1328 size_t destlen; 1329 struct flock metalog_lock; 1330 1331 if (!metafp) 1332 return; 1333 /* Buffer for strsvis(3). */ 1334 buf = (char *)malloc(4 * strlen(path) + 1); 1335 if (buf == NULL) { 1336 warnx("%s", strerror(ENOMEM)); 1337 return; 1338 } 1339 1340 /* Lock log file. */ 1341 metalog_lock.l_start = 0; 1342 metalog_lock.l_len = 0; 1343 metalog_lock.l_whence = SEEK_SET; 1344 metalog_lock.l_type = F_WRLCK; 1345 if (fcntl(fileno(metafp), F_SETLKW, &metalog_lock) == -1) { 1346 warn("can't lock %s", metafile); 1347 free(buf); 1348 return; 1349 } 1350 1351 /* Remove destdir. */ 1352 p = path; 1353 if (destdir) { 1354 destlen = strlen(destdir); 1355 if (strncmp(p, destdir, destlen) == 0 && 1356 (p[destlen] == '/' || p[destlen] == '\0')) 1357 p += destlen; 1358 } 1359 while (*p && *p == '/') 1360 p++; 1361 strsvis(buf, p, VIS_OCTAL, extra); 1362 p = buf; 1363 /* Print details. */ 1364 fprintf(metafp, ".%s%s type=%s", *p ? "/" : "", p, type); 1365 if (owner) 1366 fprintf(metafp, " uname=%s", owner); 1367 if (group) 1368 fprintf(metafp, " gname=%s", group); 1369 fprintf(metafp, " mode=%#o", mode); 1370 if (slink) { 1371 strsvis(buf, slink, VIS_CSTYLE, extra); /* encode link */ 1372 fprintf(metafp, " link=%s", buf); 1373 } 1374 if (*type == 'f') /* type=file */ 1375 fprintf(metafp, " size=%lld", (long long)size); 1376 if (tv != NULL && dopreserve) 1377 fprintf(metafp, " time=%lld.%ld", 1378 (long long)tv[1].tv_sec, (long)tv[1].tv_usec); 1379 if (digestresult && digest) 1380 fprintf(metafp, " %s=%s", digest, digestresult); 1381 if (fflags) 1382 fprintf(metafp, " flags=%s", fflags); 1383 if (tags) 1384 fprintf(metafp, " tags=%s", tags); 1385 fputc('\n', metafp); 1386 /* Flush line. */ 1387 fflush(metafp); 1388 1389 /* Unlock log file. */ 1390 metalog_lock.l_type = F_UNLCK; 1391 if (fcntl(fileno(metafp), F_SETLKW, &metalog_lock) == -1) 1392 warn("can't unlock %s", metafile); 1393 free(buf); 1394} 1395 1396/* 1397 * usage -- 1398 * print a usage message and die 1399 */ 1400static void 1401usage(void) 1402{ 1403 (void)fprintf(stderr, 1404"usage: install [-bCcpSsUv] [-f flags] [-g group] [-m mode] [-o owner]\n" 1405" [-M log] [-D dest] [-h hash] [-T tags]\n" 1406" [-B suffix] [-l linkflags] [-N dbdir]\n" 1407" file1 file2\n" 1408" install [-bCcpSsUv] [-f flags] [-g group] [-m mode] [-o owner]\n" 1409" [-M log] [-D dest] [-h hash] [-T tags]\n" 1410" [-B suffix] [-l linkflags] [-N dbdir]\n" 1411" file1 ... fileN directory\n" 1412" install -dU [-vU] [-g group] [-m mode] [-N dbdir] [-o owner]\n" 1413" [-M log] [-D dest] [-h hash] [-T tags]\n" 1414" directory ...\n"); 1415 exit(EX_USAGE); 1416 /* NOTREACHED */ 1417} 1418 1419/* 1420 * trymmap -- 1421 * return true (1) if mmap should be tried, false (0) if not. 1422 */ 1423static int 1424trymmap(int fd) 1425{ 1426/* 1427 * The ifdef is for bootstrapping - f_fstypename doesn't exist in 1428 * pre-Lite2-merge systems. 1429 */ 1430#ifdef MFSNAMELEN 1431 struct statfs stfs; 1432 1433 if (fstatfs(fd, &stfs) != 0) 1434 return (0); 1435 if (strcmp(stfs.f_fstypename, "ufs") == 0 || 1436 strcmp(stfs.f_fstypename, "cd9660") == 0) 1437 return (1); 1438#endif 1439 return (0); 1440} 1441