1 /* 2 * Copyright (c) 1987, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * 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 THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifndef lint 35 static const char copyright[] = 36 "@(#) Copyright (c) 1987, 1993\n\ 37 The Regents of the University of California. All rights reserved.\n"; 38 #endif /* not lint */ 39 40 #ifndef lint 41 /*static char sccsid[] = "From: @(#)xinstall.c 8.1 (Berkeley) 7/21/93";*/ 42 static const char rcsid[] = 43 "$Id: xinstall.c,v 1.21 1997/02/22 19:57:55 peter Exp $"; 44 #endif /* not lint */ 45 46 /*- 47 * Todo: 48 * o for -C, compare original files except in -s case. 49 * o for -C, don't change anything if nothing needs be changed. In 50 * particular, don't toggle the immutable flags just to allow null 51 * attribute changes and don't clear the dump flag. (I think inode 52 * ctimes are not updated for null attribute changes, but this is a 53 * bug.) 54 * o independent of -C, if a copy must be made, then copy to a tmpfile, 55 * set all attributes except the immutable flags, then rename, then 56 * set the immutable flags. It's annoying that the immutable flags 57 * defeat the atomicicity of rename - it seems that there must be 58 * o a window where the target is not immutable. 59 */ 60 61 #include <sys/param.h> 62 #include <sys/wait.h> 63 #include <sys/mman.h> 64 #include <sys/stat.h> 65 #include <sys/mount.h> 66 67 #include <ctype.h> 68 #include <err.h> 69 #include <errno.h> 70 #include <fcntl.h> 71 #include <grp.h> 72 #include <paths.h> 73 #include <pwd.h> 74 #include <stdio.h> 75 #include <stdlib.h> 76 #include <string.h> 77 #include <unistd.h> 78 #include <sysexits.h> 79 #include <utime.h> 80 81 #include "pathnames.h" 82 83 int debug, docompare, docopy, dodir, dopreserve, dostrip, verbose; 84 int mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; 85 char *group, *owner, pathbuf[MAXPATHLEN]; 86 char pathbuf2[MAXPATHLEN]; 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 92 void copy __P((int, char *, int, char *, off_t)); 93 int compare __P((int, const char *, int, const char *, 94 const struct stat *, const struct stat *)); 95 void install __P((char *, char *, u_long, u_int)); 96 void install_dir __P((char *)); 97 u_long string_to_flags __P((char **, u_long *, u_long *)); 98 void strip __P((char *)); 99 void usage __P((void)); 100 int trymmap __P((int)); 101 102 #define ALLOW_NUMERIC_IDS 1 103 #ifdef ALLOW_NUMERIC_IDS 104 105 uid_t uid; 106 gid_t gid; 107 108 uid_t resolve_uid __P((char *)); 109 gid_t resolve_gid __P((char *)); 110 u_long numeric_id __P((char *, char *)); 111 112 #else 113 114 struct passwd *pp; 115 struct group *gp; 116 117 #endif /* ALLOW_NUMERIC_IDS */ 118 119 int 120 main(argc, argv) 121 int argc; 122 char *argv[]; 123 { 124 struct stat from_sb, to_sb; 125 mode_t *set; 126 u_long fset; 127 u_int iflags; 128 int ch, no_target; 129 char *flags, *to_name; 130 131 iflags = 0; 132 while ((ch = getopt(argc, argv, "CcdDf:g:m:o:psv")) != -1) 133 switch((char)ch) { 134 case 'C': 135 docompare = docopy = 1; 136 break; 137 case 'c': 138 docopy = 1; 139 break; 140 case 'D': 141 debug++; 142 break; 143 case 'd': 144 dodir = 1; 145 break; 146 case 'f': 147 flags = optarg; 148 if (string_to_flags(&flags, &fset, NULL)) 149 errx(EX_USAGE, "%s: invalid flag", flags); 150 iflags |= SETFLAGS; 151 break; 152 case 'g': 153 group = optarg; 154 break; 155 case 'm': 156 if (!(set = setmode(optarg))) 157 errx(EX_USAGE, "invalid file mode: %s", 158 optarg); 159 mode = getmode(set, 0); 160 break; 161 case 'o': 162 owner = optarg; 163 break; 164 case 'p': 165 docompare = docopy = dopreserve = 1; 166 break; 167 case 's': 168 dostrip = 1; 169 break; 170 case 'v': 171 verbose = 1; 172 break; 173 case '?': 174 default: 175 usage(); 176 } 177 argc -= optind; 178 argv += optind; 179 180 /* some options make no sense when creating directories */ 181 if ((docompare || dostrip) && dodir) 182 usage(); 183 184 /* must have at least two arguments, except when creating directories */ 185 if (argc < 2 && !dodir) 186 usage(); 187 188 #ifdef ALLOW_NUMERIC_IDS 189 190 if (owner) 191 uid = resolve_uid(owner); 192 if (group) 193 gid = resolve_gid(group); 194 195 #else 196 197 /* get group and owner id's */ 198 if (owner && !(pp = getpwnam(owner))) 199 errx(EX_NOUSER, "unknown user %s", owner); 200 if (group && !(gp = getgrnam(group))) 201 errx(EX_NOUSER, "unknown group %s", group); 202 203 #endif /* ALLOW_NUMERIC_IDS */ 204 205 if (dodir) { 206 for (; *argv != NULL; ++argv) 207 install_dir(*argv); 208 exit(EX_OK); 209 /* NOTREACHED */ 210 } 211 212 no_target = stat(to_name = argv[argc - 1], &to_sb); 213 if (!no_target && (to_sb.st_mode & S_IFMT) == S_IFDIR) { 214 for (; *argv != to_name; ++argv) 215 install(*argv, to_name, fset, iflags | DIRECTORY); 216 exit(EX_OK); 217 /* NOTREACHED */ 218 } 219 220 /* can't do file1 file2 directory/file */ 221 if (argc != 2) 222 usage(); 223 224 if (!no_target) { 225 if (stat(*argv, &from_sb)) 226 err(EX_OSERR, "%s", *argv); 227 if (!S_ISREG(to_sb.st_mode)) { 228 errno = EFTYPE; 229 err(EX_OSERR, "%s", to_name); 230 } 231 if (to_sb.st_dev == from_sb.st_dev && 232 to_sb.st_ino == from_sb.st_ino) 233 errx(EX_USAGE, 234 "%s and %s are the same file", *argv, to_name); 235 /* 236 * XXX - It's not at all clear why this code was here, since it completely 237 * duplicates code install(). The version in install() handles the -C flag 238 * correctly, so we'll just disable this for now. 239 */ 240 #if 0 241 /* 242 * Unlink now... avoid ETXTBSY errors later. Try and turn 243 * off the append/immutable bits -- if we fail, go ahead, 244 * it might work. 245 */ 246 if (to_sb.st_flags & NOCHANGEBITS) 247 (void)chflags(to_name, 248 to_sb.st_flags & ~(NOCHANGEBITS)); 249 (void)unlink(to_name); 250 #endif 251 } 252 install(*argv, to_name, fset, iflags); 253 exit(EX_OK); 254 /* NOTREACHED */ 255 } 256 257 #ifdef ALLOW_NUMERIC_IDS 258 259 uid_t 260 resolve_uid(s) 261 char *s; 262 { 263 struct passwd *pw; 264 265 return ((pw = getpwnam(s)) == NULL) ? 266 (uid_t) numeric_id(s, "user") : pw->pw_uid; 267 } 268 269 gid_t 270 resolve_gid(s) 271 char *s; 272 { 273 struct group *gr; 274 275 return ((gr = getgrnam(s)) == NULL) ? 276 (gid_t) numeric_id(s, "group") : gr->gr_gid; 277 } 278 279 u_long 280 numeric_id(name, type) 281 char *name, *type; 282 { 283 u_long val; 284 char *ep; 285 286 /* 287 * XXX 288 * We know that uid_t's and gid_t's are unsigned longs. 289 */ 290 errno = 0; 291 val = strtoul(name, &ep, 10); 292 if (errno) 293 err(EX_NOUSER, "%s", name); 294 if (*ep != '\0') 295 errx(EX_NOUSER, "unknown %s %s", type, name); 296 return (val); 297 } 298 299 #endif /* ALLOW_NUMERIC_IDS */ 300 301 /* 302 * install -- 303 * build a path name and install the file 304 */ 305 void 306 install(from_name, to_name, fset, flags) 307 char *from_name, *to_name; 308 u_long fset; 309 u_int flags; 310 { 311 struct stat from_sb, to_sb; 312 int devnull, from_fd, to_fd, serrno; 313 char *p, *old_to_name = 0; 314 315 if (debug >= 2 && !docompare) 316 fprintf(stderr, "install: invoked without -C for %s to %s\n", 317 from_name, to_name); 318 319 /* If try to install NULL file to a directory, fails. */ 320 if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) { 321 if (stat(from_name, &from_sb)) 322 err(EX_OSERR, "%s", from_name); 323 if (!S_ISREG(from_sb.st_mode)) { 324 errno = EFTYPE; 325 err(EX_OSERR, "%s", from_name); 326 } 327 /* Build the target path. */ 328 if (flags & DIRECTORY) { 329 (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s", 330 to_name, 331 (p = strrchr(from_name, '/')) ? ++p : from_name); 332 to_name = pathbuf; 333 } 334 devnull = 0; 335 } else { 336 from_sb.st_flags = 0; /* XXX */ 337 devnull = 1; 338 } 339 340 if (docompare) { 341 old_to_name = to_name; 342 /* 343 * Make a new temporary file in the same file system 344 * (actually, in in the same directory) as the target so 345 * that the temporary file can be renamed to the target. 346 */ 347 snprintf(pathbuf2, sizeof pathbuf2, "%s", to_name); 348 p = strrchr(pathbuf2, '/'); 349 p = (p == NULL ? pathbuf2 : p + 1); 350 snprintf(p, &pathbuf2[sizeof pathbuf2] - p, "INS@XXXX"); 351 to_fd = mkstemp(pathbuf2); 352 if (to_fd < 0) 353 /* XXX should fall back to not comparing. */ 354 err(EX_OSERR, "mkstemp: %s for %s", pathbuf2, to_name); 355 to_name = pathbuf2; 356 } else { 357 /* 358 * Unlink now... avoid errors later. Try to turn off the 359 * append/immutable bits -- if we fail, go ahead, it might 360 * work. 361 */ 362 if (stat(to_name, &to_sb) == 0 && to_sb.st_flags & NOCHANGEBITS) 363 (void)chflags(to_name, to_sb.st_flags & ~NOCHANGEBITS); 364 unlink(to_name); 365 366 /* Create target. */ 367 to_fd = open(to_name, 368 O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR); 369 if (to_fd < 0) 370 err(EX_OSERR, "%s", to_name); 371 } 372 373 if (!devnull) { 374 if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) { 375 serrno = errno; 376 (void)unlink(to_name); 377 errno = serrno; 378 err(EX_OSERR, "%s", from_name); 379 } 380 copy(from_fd, from_name, to_fd, to_name, from_sb.st_size); 381 (void)close(from_fd); 382 } 383 384 if (dostrip) 385 strip(to_name); 386 387 /* 388 * Unfortunately, because we strip the installed file and not the 389 * original one, it is impossible to do the comparison without 390 * first laboriously copying things over and then comparing. 391 * It may be possible to better optimize the !dostrip case, however. 392 * For further study. 393 */ 394 if (docompare) { 395 struct stat old_sb, new_sb, timestamp_sb; 396 int old_fd; 397 struct utimbuf utb; 398 399 old_fd = open(old_to_name, O_RDONLY, 0); 400 if (old_fd < 0 && errno == ENOENT) 401 goto different; 402 if (old_fd < 0) 403 err(EX_OSERR, "%s", old_to_name); 404 fstat(old_fd, &old_sb); 405 if (old_sb.st_flags & NOCHANGEBITS) 406 (void)fchflags(old_fd, old_sb.st_flags & ~NOCHANGEBITS); 407 fstat(to_fd, &new_sb); 408 if (compare(old_fd, old_to_name, to_fd, to_name, &old_sb, 409 &new_sb)) { 410 different: 411 if (debug != 0) 412 fprintf(stderr, 413 "install: renaming for %s: %s to %s\n", 414 from_name, to_name, old_to_name); 415 if (dopreserve && stat(from_name, ×tamp_sb) == 0) { 416 utb.actime = from_sb.st_atime; 417 utb.modtime = from_sb.st_mtime; 418 (void)utime(to_name, &utb); 419 } 420 moveit: 421 if (verbose) { 422 printf("install: %s -> %s\n", 423 from_name, old_to_name); 424 } 425 if (rename(to_name, old_to_name) < 0) { 426 serrno = errno; 427 unlink(to_name); 428 unlink(old_to_name); 429 errno = serrno; 430 err(EX_OSERR, "rename: %s to %s", to_name, 431 old_to_name); 432 } 433 close(old_fd); 434 } else { 435 if (old_sb.st_nlink != 1) { 436 /* 437 * Replace the target, although it hasn't 438 * changed, to snap the extra links. But 439 * preserve the target file times. 440 */ 441 if (fstat(old_fd, ×tamp_sb) == 0) { 442 utb.actime = timestamp_sb.st_atime; 443 utb.modtime = timestamp_sb.st_mtime; 444 (void)utime(to_name, &utb); 445 } 446 goto moveit; 447 } 448 if (unlink(to_name) < 0) 449 err(EX_OSERR, "unlink: %s", to_name); 450 close(to_fd); 451 to_fd = old_fd; 452 } 453 to_name = old_to_name; 454 } 455 456 /* 457 * Set owner, group, mode for target; do the chown first, 458 * chown may lose the setuid bits. 459 */ 460 if ((group || owner) && 461 #ifdef ALLOW_NUMERIC_IDS 462 fchown(to_fd, owner ? uid : -1, group ? gid : -1)) { 463 #else 464 fchown(to_fd, owner ? pp->pw_uid : -1, group ? gp->gr_gid : -1)) { 465 #endif 466 serrno = errno; 467 (void)unlink(to_name); 468 errno = serrno; 469 err(EX_OSERR,"%s: chown/chgrp", to_name); 470 } 471 if (fchmod(to_fd, mode)) { 472 serrno = errno; 473 (void)unlink(to_name); 474 errno = serrno; 475 err(EX_OSERR, "%s: chmod", to_name); 476 } 477 478 /* 479 * If provided a set of flags, set them, otherwise, preserve the 480 * flags, except for the dump flag. 481 */ 482 if (fchflags(to_fd, 483 flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) { 484 serrno = errno; 485 (void)unlink(to_name); 486 errno = serrno; 487 err(EX_OSERR, "%s: chflags", to_name); 488 } 489 490 (void)close(to_fd); 491 if (!docopy && !devnull && unlink(from_name)) 492 err(EX_OSERR, "%s", from_name); 493 } 494 495 /* 496 * compare -- 497 * compare two files; non-zero means files differ 498 */ 499 int 500 compare(int from_fd, const char *from_name, int to_fd, const char *to_name, 501 const struct stat *from_sb, const struct stat *to_sb) 502 { 503 char *p, *q; 504 int rv; 505 size_t tsize; 506 int done_compare; 507 508 if (from_sb->st_size != to_sb->st_size) 509 return 1; 510 511 tsize = (size_t)from_sb->st_size; 512 513 if (tsize <= 8 * 1024 * 1024) { 514 done_compare = 0; 515 if (trymmap(from_fd) && trymmap(to_fd)) { 516 p = mmap(NULL, tsize, PROT_READ, MAP_SHARED, from_fd, (off_t)0); 517 if (p == (char *)MAP_FAILED) 518 goto out; 519 q = mmap(NULL, tsize, PROT_READ, MAP_SHARED, to_fd, (off_t)0); 520 if (q == (char *)MAP_FAILED) { 521 munmap(p, tsize); 522 goto out; 523 } 524 525 rv = memcmp(p, q, tsize); 526 munmap(p, tsize); 527 munmap(q, tsize); 528 done_compare = 1; 529 } 530 out: 531 if (!done_compare) { 532 char buf1[MAXBSIZE]; 533 char buf2[MAXBSIZE]; 534 int n1, n2; 535 536 rv = 0; 537 lseek(from_fd, 0, SEEK_SET); 538 lseek(to_fd, 0, SEEK_SET); 539 while (rv == 0) { 540 n1 = read(from_fd, buf1, sizeof(buf1)); 541 if (n1 == 0) 542 break; /* EOF */ 543 else if (n1 > 0) { 544 n2 = read(to_fd, buf2, n1); 545 if (n2 == n1) 546 rv = memcmp(buf1, buf2, n1); 547 else 548 rv = 1; /* out of sync */ 549 } else 550 rv = 1; /* read failure */ 551 } 552 lseek(from_fd, 0, SEEK_SET); 553 lseek(to_fd, 0, SEEK_SET); 554 } 555 } else 556 rv = 1; /* don't bother in this case */ 557 558 return rv; 559 } 560 561 /* 562 * copy -- 563 * copy from one file to another 564 */ 565 void 566 copy(from_fd, from_name, to_fd, to_name, size) 567 register int from_fd, to_fd; 568 char *from_name, *to_name; 569 off_t size; 570 { 571 register int nr, nw; 572 int serrno; 573 char *p, buf[MAXBSIZE]; 574 int done_copy; 575 576 /* 577 * Mmap and write if less than 8M (the limit is so we don't totally 578 * trash memory on big files. This is really a minor hack, but it 579 * wins some CPU back. 580 */ 581 done_copy = 0; 582 if (size <= 8 * 1048576 && trymmap(from_fd)) { 583 if ((p = mmap(NULL, (size_t)size, PROT_READ, 584 MAP_SHARED, from_fd, (off_t)0)) == (char *)MAP_FAILED) 585 goto out; 586 if ((nw = write(to_fd, p, size)) != size) { 587 serrno = errno; 588 (void)unlink(to_name); 589 errno = nw > 0 ? EIO : serrno; 590 err(EX_OSERR, "%s", to_name); 591 } 592 done_copy = 1; 593 out: 594 } 595 if (!done_copy) { 596 while ((nr = read(from_fd, buf, sizeof(buf))) > 0) 597 if ((nw = write(to_fd, buf, nr)) != nr) { 598 serrno = errno; 599 (void)unlink(to_name); 600 errno = nw > 0 ? EIO : serrno; 601 err(EX_OSERR, "%s", to_name); 602 } 603 if (nr != 0) { 604 serrno = errno; 605 (void)unlink(to_name); 606 errno = serrno; 607 err(EX_OSERR, "%s", from_name); 608 } 609 } 610 } 611 612 /* 613 * strip -- 614 * use strip(1) to strip the target file 615 */ 616 void 617 strip(to_name) 618 char *to_name; 619 { 620 int serrno, status; 621 622 switch (vfork()) { 623 case -1: 624 serrno = errno; 625 (void)unlink(to_name); 626 errno = serrno; 627 err(EX_TEMPFAIL, "fork"); 628 case 0: 629 execlp("strip", "strip", to_name, NULL); 630 err(EX_OSERR, "exec(strip)"); 631 default: 632 if (wait(&status) == -1 || status) { 633 (void)unlink(to_name); 634 exit(EX_SOFTWARE); 635 /* NOTREACHED */ 636 } 637 } 638 } 639 640 /* 641 * install_dir -- 642 * build directory heirarchy 643 */ 644 void 645 install_dir(path) 646 char *path; 647 { 648 register char *p; 649 struct stat sb; 650 int ch; 651 652 for (p = path;; ++p) 653 if (!*p || (p != path && *p == '/')) { 654 ch = *p; 655 *p = '\0'; 656 if (stat(path, &sb)) { 657 if (errno != ENOENT || mkdir(path, 0777) < 0) { 658 err(EX_OSERR, "%s", path); 659 /* NOTREACHED */ 660 } 661 } 662 if (!(*p = ch)) 663 break; 664 } 665 666 if (((gid != (gid_t)-1 || uid != (uid_t)-1) && chown(path, uid, gid)) || 667 chmod(path, mode)) { 668 warn("%s", path); 669 } 670 } 671 672 /* 673 * usage -- 674 * print a usage message and die 675 */ 676 void 677 usage() 678 { 679 (void)fprintf(stderr,"\ 680 usage: install [-CcDps] [-f flags] [-g group] [-m mode] [-o owner] file1 file2\n\ 681 install [-CcDps] [-f flags] [-g group] [-m mode] [-o owner] file1 ...\n\ 682 fileN directory\n\ 683 install -d [-g group] [-m mode] [-o owner] directory ...\n"); 684 exit(EX_USAGE); 685 /* NOTREACHED */ 686 } 687 688 /* 689 * trymmap -- 690 * return true (1) if mmap should be tried, false (0) if not. 691 */ 692 int 693 trymmap(fd) 694 int fd; 695 { 696 struct statfs stfs; 697 698 if (fstatfs(fd, &stfs) < 0) 699 return 0; 700 switch(stfs.f_type) { 701 case MOUNT_UFS: /* should be safe.. */ 702 case MOUNT_CD9660: /* should be safe.. */ 703 return 1; 704 } 705 return 0; 706 } 707