2 * Copyright (c) 1987, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
35 static const char copyright[] =
36 "@(#) Copyright (c) 1987, 1993\n\
37 The Regents of the University of California. All rights reserved.\n";
42 static char sccsid[] = "From: @(#)xinstall.c 8.1 (Berkeley) 7/21/93";
44 static const char rcsid[] =
50 * o for -C, compare original files except in -s case.
51 * o for -C, don't change anything if nothing needs be changed. In
52 * particular, don't toggle the immutable flags just to allow null
53 * attribute changes and don't clear the dump flag. (I think inode
54 * ctimes are not updated for null attribute changes, but this is a
56 * o independent of -C, if a copy must be made, then copy to a tmpfile,
57 * set all attributes except the immutable flags, then rename, then
58 * set the immutable flags. It's annoying that the immutable flags
59 * defeat the atomicicity of rename - it seems that there must be
60 * a window where the target is not immutable.
63 #include <sys/param.h>
67 #include <sys/mount.h>
83 #include "pathnames.h"
85 /* Bootstrap aid - this doesn't exist in most older releases */
87 #define MAP_FAILED ((void *)-1) /* from <sys/mman.h> */
90 int debug, docompare, docopy, dodir, dopreserve, dostrip, nommap, verbose;
91 int mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
92 char *group, *owner, pathbuf[MAXPATHLEN];
93 char pathbuf2[MAXPATHLEN];
95 #define DIRECTORY 0x01 /* Tell install it's a directory. */
96 #define SETFLAGS 0x02 /* Tell install to set flags. */
97 #define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)
99 void copy __P((int, char *, int, char *, off_t));
100 int compare __P((int, const char *, int, const char *,
101 const struct stat *, const struct stat *));
102 void install __P((char *, char *, u_long, u_int));
103 void install_dir __P((char *));
104 void strip __P((char *));
105 void usage __P((void));
106 int trymmap __P((int));
108 #define ALLOW_NUMERIC_IDS 1
109 #ifdef ALLOW_NUMERIC_IDS
114 uid_t resolve_uid __P((char *));
115 gid_t resolve_gid __P((char *));
116 u_long numeric_id __P((char *, char *));
123 #endif /* ALLOW_NUMERIC_IDS */
130 struct stat from_sb, to_sb;
135 char *flags, *to_name;
138 while ((ch = getopt(argc, argv, "CcdDf:g:m:Mo:psv")) != -1)
141 docompare = docopy = 1;
154 if (strtofflags(&flags, &fset, NULL))
155 errx(EX_USAGE, "%s: invalid flag", flags);
162 if (!(set = setmode(optarg)))
163 errx(EX_USAGE, "invalid file mode: %s",
165 mode = getmode(set, 0);
175 docompare = docopy = dopreserve = 1;
190 /* some options make no sense when creating directories */
191 if (dostrip && dodir)
194 /* must have at least two arguments, except when creating directories */
195 if (argc < 2 && !dodir)
198 #ifdef ALLOW_NUMERIC_IDS
201 uid = resolve_uid(owner);
203 gid = resolve_gid(group);
207 /* get group and owner id's */
208 if (owner && !(pp = getpwnam(owner)))
209 errx(EX_NOUSER, "unknown user %s", owner);
210 if (group && !(gp = getgrnam(group)))
211 errx(EX_NOUSER, "unknown group %s", group);
213 #endif /* ALLOW_NUMERIC_IDS */
216 for (; *argv != NULL; ++argv)
222 no_target = stat(to_name = argv[argc - 1], &to_sb);
223 if (!no_target && (to_sb.st_mode & S_IFMT) == S_IFDIR) {
224 for (; *argv != to_name; ++argv)
225 install(*argv, to_name, fset, iflags | DIRECTORY);
230 /* can't do file1 file2 directory/file */
235 if (stat(*argv, &from_sb))
236 err(EX_OSERR, "%s", *argv);
237 if (!S_ISREG(to_sb.st_mode)) {
239 err(EX_OSERR, "%s", to_name);
241 if (to_sb.st_dev == from_sb.st_dev &&
242 to_sb.st_ino == from_sb.st_ino)
244 "%s and %s are the same file", *argv, to_name);
246 * XXX - It's not at all clear why this code was here, since it completely
247 * duplicates code install(). The version in install() handles the -C flag
248 * correctly, so we'll just disable this for now.
252 * Unlink now... avoid ETXTBSY errors later. Try and turn
253 * off the append/immutable bits -- if we fail, go ahead,
256 if (to_sb.st_flags & NOCHANGEBITS)
257 (void)chflags(to_name,
258 to_sb.st_flags & ~(NOCHANGEBITS));
259 (void)unlink(to_name);
262 install(*argv, to_name, fset, iflags);
267 #ifdef ALLOW_NUMERIC_IDS
275 return ((pw = getpwnam(s)) == NULL) ?
276 (uid_t) numeric_id(s, "user") : pw->pw_uid;
285 return ((gr = getgrnam(s)) == NULL) ?
286 (gid_t) numeric_id(s, "group") : gr->gr_gid;
290 numeric_id(name, type)
298 * We know that uid_t's and gid_t's are unsigned longs.
301 val = strtoul(name, &ep, 10);
303 err(EX_NOUSER, "%s", name);
305 errx(EX_NOUSER, "unknown %s %s", type, name);
309 #endif /* ALLOW_NUMERIC_IDS */
313 * build a path name and install the file
316 install(from_name, to_name, fset, flags)
317 char *from_name, *to_name;
321 struct stat from_sb, to_sb;
322 int devnull, from_fd, to_fd, serrno;
323 char *p, *old_to_name = 0;
325 if (debug >= 2 && !docompare)
326 fprintf(stderr, "install: invoked without -C for %s to %s\n",
329 /* If try to install NULL file to a directory, fails. */
330 if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) {
331 if (stat(from_name, &from_sb))
332 err(EX_OSERR, "%s", from_name);
333 if (!S_ISREG(from_sb.st_mode)) {
335 err(EX_OSERR, "%s", from_name);
337 /* Build the target path. */
338 if (flags & DIRECTORY) {
339 (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
341 (p = strrchr(from_name, '/')) ? ++p : from_name);
346 from_sb.st_flags = 0; /* XXX */
351 old_to_name = to_name;
353 * Make a new temporary file in the same file system
354 * (actually, in in the same directory) as the target so
355 * that the temporary file can be renamed to the target.
357 snprintf(pathbuf2, sizeof pathbuf2, "%s", to_name);
358 p = strrchr(pathbuf2, '/');
359 p = (p == NULL ? pathbuf2 : p + 1);
360 snprintf(p, &pathbuf2[sizeof pathbuf2] - p, "INS@XXXX");
361 to_fd = mkstemp(pathbuf2);
363 /* XXX should fall back to not comparing. */
364 err(EX_OSERR, "mkstemp: %s for %s", pathbuf2, to_name);
368 * Unlink now... avoid errors later. Try to turn off the
369 * append/immutable bits -- if we fail, go ahead, it might
372 if (stat(to_name, &to_sb) == 0 && to_sb.st_flags & NOCHANGEBITS)
373 (void)chflags(to_name, to_sb.st_flags & ~NOCHANGEBITS);
377 to_fd = open(to_name,
378 O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR);
380 err(EX_OSERR, "%s", to_name);
384 if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) {
386 (void)unlink(to_name);
388 err(EX_OSERR, "%s", from_name);
390 copy(from_fd, from_name, to_fd, to_name, from_sb.st_size);
391 (void)close(from_fd);
400 to_fd = open(to_name, O_RDWR, 0);
402 err(EX_OSERR, "%s", to_name);
406 * Unfortunately, because we strip the installed file and not the
407 * original one, it is impossible to do the comparison without
408 * first laboriously copying things over and then comparing.
409 * It may be possible to better optimize the !dostrip case, however.
413 struct stat old_sb, new_sb, timestamp_sb;
417 old_fd = open(old_to_name, O_RDONLY, 0);
418 if (old_fd < 0 && errno == ENOENT)
421 err(EX_OSERR, "%s", old_to_name);
422 fstat(old_fd, &old_sb);
423 if (old_sb.st_flags & NOCHANGEBITS)
424 (void)fchflags(old_fd, old_sb.st_flags & ~NOCHANGEBITS);
425 fstat(to_fd, &new_sb);
426 if (compare(old_fd, old_to_name, to_fd, to_name, &old_sb,
431 "install: renaming for %s: %s to %s\n",
432 from_name, to_name, old_to_name);
434 printf("install: %s -> %s\n",
435 from_name, old_to_name);
436 if (dopreserve && stat(from_name, ×tamp_sb) == 0) {
437 utb.actime = timestamp_sb.st_atime;
438 utb.modtime = timestamp_sb.st_mtime;
439 (void)utime(to_name, &utb);
442 if (rename(to_name, old_to_name) < 0) {
447 err(EX_OSERR, "rename: %s to %s", to_name,
452 if (old_sb.st_nlink != 1) {
454 * Replace the target, although it hasn't
455 * changed, to snap the extra links. But
456 * preserve the target file times.
458 if (fstat(old_fd, ×tamp_sb) == 0) {
459 utb.actime = timestamp_sb.st_atime;
460 utb.modtime = timestamp_sb.st_mtime;
461 (void)utime(to_name, &utb);
465 if (unlink(to_name) < 0)
466 err(EX_OSERR, "unlink: %s", to_name);
470 to_name = old_to_name;
474 * Set owner, group, mode for target; do the chown first,
475 * chown may lose the setuid bits.
477 if ((group || owner) &&
478 #ifdef ALLOW_NUMERIC_IDS
479 fchown(to_fd, owner ? uid : -1, group ? gid : -1)) {
481 fchown(to_fd, owner ? pp->pw_uid : -1, group ? gp->gr_gid : -1)) {
484 (void)unlink(to_name);
486 err(EX_OSERR,"%s: chown/chgrp", to_name);
488 if (fchmod(to_fd, mode)) {
490 (void)unlink(to_name);
492 err(EX_OSERR, "%s: chmod", to_name);
496 * If provided a set of flags, set them, otherwise, preserve the
497 * flags, except for the dump flag.
498 * NFS does not support flags. Ignore EOPNOTSUPP flags if we're just
499 * trying to turn off UF_NODUMP. If we're trying to set real flags,
500 * then warn if the the fs doesn't support it, otherwise fail.
503 flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) {
504 if (flags & SETFLAGS) {
505 if (errno == EOPNOTSUPP)
506 warn("%s: chflags", to_name);
509 (void)unlink(to_name);
511 err(EX_OSERR, "%s: chflags", to_name);
517 if (!docopy && !devnull && unlink(from_name))
518 err(EX_OSERR, "%s", from_name);
523 * compare two files; non-zero means files differ
526 compare(int from_fd, const char *from_name, int to_fd, const char *to_name,
527 const struct stat *from_sb, const struct stat *to_sb)
535 if (from_sb->st_size != to_sb->st_size)
538 tsize = (size_t)from_sb->st_size;
540 if (tsize <= 8 * 1024 * 1024) {
542 if (trymmap(from_fd) && trymmap(to_fd)) {
543 p = mmap(NULL, tsize, PROT_READ, MAP_SHARED, from_fd, (off_t)0);
544 if (p == (char *)MAP_FAILED)
546 q = mmap(NULL, tsize, PROT_READ, MAP_SHARED, to_fd, (off_t)0);
547 if (q == (char *)MAP_FAILED) {
552 rv = memcmp(p, q, tsize);
564 lseek(from_fd, 0, SEEK_SET);
565 lseek(to_fd, 0, SEEK_SET);
567 n1 = read(from_fd, buf1, sizeof(buf1));
571 n2 = read(to_fd, buf2, n1);
573 rv = memcmp(buf1, buf2, n1);
575 rv = 1; /* out of sync */
577 rv = 1; /* read failure */
579 lseek(from_fd, 0, SEEK_SET);
580 lseek(to_fd, 0, SEEK_SET);
583 rv = 1; /* don't bother in this case */
590 * copy from one file to another
593 copy(from_fd, from_name, to_fd, to_name, size)
594 register int from_fd, to_fd;
595 char *from_name, *to_name;
600 char *p, buf[MAXBSIZE];
604 * Mmap and write if less than 8M (the limit is so we don't totally
605 * trash memory on big files. This is really a minor hack, but it
606 * wins some CPU back.
609 if (size <= 8 * 1048576 && trymmap(from_fd)) {
610 if ((p = mmap(NULL, (size_t)size, PROT_READ,
611 MAP_SHARED, from_fd, (off_t)0)) == (char *)MAP_FAILED)
613 if ((nw = write(to_fd, p, size)) != size) {
615 (void)unlink(to_name);
616 errno = nw > 0 ? EIO : serrno;
617 err(EX_OSERR, "%s", to_name);
623 while ((nr = read(from_fd, buf, sizeof(buf))) > 0)
624 if ((nw = write(to_fd, buf, nr)) != nr) {
626 (void)unlink(to_name);
627 errno = nw > 0 ? EIO : serrno;
628 err(EX_OSERR, "%s", to_name);
632 (void)unlink(to_name);
634 err(EX_OSERR, "%s", from_name);
641 * use strip(1) to strip the target file
652 (void)unlink(to_name);
654 err(EX_TEMPFAIL, "fork");
656 execlp("strip", "strip", to_name, NULL);
657 err(EX_OSERR, "exec(strip)");
659 if (wait(&status) == -1 || status) {
660 (void)unlink(to_name);
669 * build directory heirarchy
680 if (!*p || (p != path && *p == '/')) {
683 if (stat(path, &sb)) {
684 if (errno != ENOENT || mkdir(path, 0755) < 0) {
685 err(EX_OSERR, "mkdir %s", path);
688 } else if (!S_ISDIR(sb.st_mode))
689 errx(EX_OSERR, "%s exists but is not a directory", path);
694 if ((gid != (gid_t)-1 || uid != (uid_t)-1) && chown(path, uid, gid))
695 warn("chown %u:%u %s", uid, gid, path);
696 if (chmod(path, mode))
697 warn("chmod %o %s", mode, path);
702 * print a usage message and die
707 (void)fprintf(stderr,"\
708 usage: install [-CcDpsv] [-f flags] [-g group] [-m mode] [-o owner] file1 file2\n\
709 install [-CcDpsv] [-f flags] [-g group] [-m mode] [-o owner] file1 ...\n\
711 install -d [-v] [-g group] [-m mode] [-o owner] directory ...\n");
718 * return true (1) if mmap should be tried, false (0) if not.
725 * The ifdef is for bootstrapping - f_fstypename doesn't exist in
726 * pre-Lite2-merge systems.
731 if (nommap || fstatfs(fd, &stfs) != 0)
733 if (strcmp(stfs.f_fstypename, "ufs") == 0 ||
734 strcmp(stfs.f_fstypename, "cd9660") == 0)