buildworld - Fix parallel build race in ncurses
[dragonfly.git] / usr.bin / xinstall / xinstall.c
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  * @(#) Copyright (c) 1987, 1993 The Regents of the University of California.  All rights reserved.
34  * @(#)xinstall.c       8.1 (Berkeley) 7/21/93
35  * $FreeBSD: src/usr.bin/xinstall/xinstall.c,v 1.38.2.8 2002/08/07 16:29:48 ru Exp $
36  */
37
38 #include <sys/param.h>
39 #include <sys/mman.h>
40 #include <sys/mount.h>
41 #include <sys/stat.h>
42 #include <sys/wait.h>
43
44 #include <ctype.h>
45 #include <err.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <grp.h>
49 #include <paths.h>
50 #include <pwd.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <sysexits.h>
55 #include <unistd.h>
56 #include <utime.h>
57
58 #include "pathnames.h"
59
60 /* Bootstrap aid - this doesn't exist in most older releases */
61 #ifndef MAP_FAILED
62 #define MAP_FAILED ((void *)-1) /* from <sys/mman.h> */
63 #endif
64 #ifndef UF_NOHISTORY
65 #define UF_NOHISTORY    0
66 #endif
67
68 #define MAX_CMP_SIZE    (16 * 1024 * 1024)
69
70 #define DIRECTORY       0x01            /* Tell install it's a directory. */
71 #define SETFLAGS        0x02            /* Tell install to set flags. */
72 #define NOCHANGEBITS    (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)
73 #define BACKUP_SUFFIX   ".old"
74
75 struct passwd *pp;
76 struct group *gp;
77 gid_t gid;
78 uid_t uid;
79 int dobackup, docompare, dodir, dopreserve, dostrip, nommap, safecopy, verbose;
80 mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
81 const char *suffix = BACKUP_SUFFIX;
82 char  *destdir;
83
84 static int file_getgroup(const char *etcdir, const char *group, gid_t *gidret);
85 static int file_getowner(const char *etcdir, const char *owner, uid_t *uidret);
86
87 void    copy(int, const char *, int, const char *, off_t);
88 int     compare(int, const char *, size_t, int, const char *, size_t);
89 int     create_newfile(const char *, int, struct stat *);
90 int     create_tempfile(const char *, char *, size_t);
91 void    install(const char *, const char *, u_long, u_long, u_int);
92 void    install_dir(char *);
93 u_long  numeric_id(const char *, const char *);
94 void    strip(const char *);
95 int     trymmap(int);
96 void    usage(void);
97
98 int
99 main(int argc, char **argv)
100 {
101         struct stat from_sb, to_sb;
102         mode_t *set;
103         u_long fset;
104         u_long fclr;
105         int ch, no_target;
106         int trysys;
107         u_int iflags;
108         char *flags;
109         const char *group, *owner, *to_name;
110         const char *etcdir;
111
112         iflags = 0;
113         trysys = 0;
114         group = NULL;
115         owner = NULL;
116         etcdir = NULL;
117
118         while ((ch = getopt(argc, argv, "L:B:bCcdf:g:lMm:o:pSsv:D:")) != -1)
119                 switch((char)ch) {
120                 case 'L':
121                         etcdir = optarg;
122                         break;
123                 case 'B':
124                         suffix = optarg;
125                         /* FALLTHROUGH */
126                 case 'b':
127                         dobackup = 1;
128                         break;
129                 case 'C':
130                         docompare = 1;
131                         break;
132                 case 'c':
133                         /* For backwards compatibility. */
134                         break;
135                 case 'd':
136                         dodir = 1;
137                         break;
138                 case 'D':
139                         destdir = optarg;
140                         break;
141                 case 'f':
142                         flags = optarg;
143                         if (strtofflags(&flags, &fset, &fclr))
144                                 errx(EX_USAGE, "%s: invalid flag", flags);
145                         iflags |= SETFLAGS;
146                         break;
147                 case 'g':
148                         group = optarg;
149                         break;
150                 case 'l':
151                         trysys = 1;
152                         break;
153                 case 'M':
154                         nommap = 1;
155                         break;
156                 case 'm':
157                         if (!(set = setmode(optarg)))
158                                 errx(EX_USAGE, "invalid file mode: %s",
159                                      optarg);
160                         mode = getmode(set, 0);
161                         free(set);
162                         break;
163                 case 'o':
164                         owner = optarg;
165                         break;
166                 case 'p':
167                         docompare = dopreserve = 1;
168                         break;
169                 case 'S':
170                         safecopy = 1;
171                         break;
172                 case 's':
173                         dostrip = 1;
174                         break;
175                 case 'v':
176                         verbose = 1;
177                         break;
178                 case '?':
179                 default:
180                         usage();
181                 }
182         argc -= optind;
183         argv += optind;
184
185         /* some options make no sense when creating directories */
186         if (dostrip && dodir)
187                 usage();
188
189         /* must have at least two arguments, except when creating directories */
190         if (argc < 2 && !dodir)
191                 usage();
192
193         /* need to make a temp copy so we can compare stripped version */
194         if (docompare && dostrip)
195                 safecopy = 1;
196
197         /* no etcdir specified, always try the system */
198         if (etcdir == NULL)
199                 trysys = 1;
200         uid = (uid_t)-1;
201         gid = (gid_t)-1;
202
203         /* get group and owner id's */
204         if (group != NULL) {
205                 if (etcdir && file_getgroup(etcdir, group, &gid)) {
206                         ;
207                 } else if (trysys && (gp = getgrnam(group)) != NULL) {
208                         gid = gp->gr_gid;
209                 } else {
210                         gid = (gid_t)numeric_id(group, "group");
211                 }
212         }
213
214         if (owner != NULL) {
215                 if (etcdir && file_getowner(etcdir, owner, &uid)) {
216                         ;
217                 } else if (trysys && (pp = getpwnam(owner)) != NULL) {
218                         uid = pp->pw_uid;
219                 } else {
220                         uid = (uid_t)numeric_id(owner, "user");
221                 }
222         }
223
224         if (dodir) {
225                 for (; *argv != NULL; ++argv)
226                         install_dir(*argv);
227                 exit(EX_OK);
228                 /* NOTREACHED */
229         }
230
231         no_target = stat(to_name = argv[argc - 1], &to_sb);
232         if (!no_target && S_ISDIR(to_sb.st_mode)) {
233                 for (; *argv != to_name; ++argv)
234                         install(*argv, to_name, fset, fclr, iflags | DIRECTORY);
235                 exit(EX_OK);
236                 /* NOTREACHED */
237         }
238
239         /* can't do file1 file2 directory/file */
240         if (argc != 2)
241                 usage();
242
243         if (!no_target) {
244                 if (stat(*argv, &from_sb))
245                         err(EX_OSERR, "%s", *argv);
246                 if (!S_ISREG(to_sb.st_mode)) {
247                         errno = EFTYPE;
248                         err(EX_OSERR, "%s", to_name);
249                 }
250                 if (to_sb.st_dev == from_sb.st_dev &&
251                     to_sb.st_ino == from_sb.st_ino)
252                         errx(EX_USAGE, 
253                             "%s and %s are the same file", *argv, to_name);
254         }
255         install(*argv, to_name, fset, fclr, iflags);
256         exit(EX_OK);
257         /* NOTREACHED */
258 }
259
260 u_long
261 numeric_id(const char *name, const char *type)
262 {
263         u_long val;
264         char *ep;
265
266         /*
267          * XXX
268          * We know that uid_t's and gid_t's are unsigned longs.
269          */
270         errno = 0;
271         val = strtoul(name, &ep, 10);
272         if (errno)
273                 err(EX_NOUSER, "%s", name);
274         if (*ep != '\0')
275                 errx(EX_NOUSER, "unknown %s %s", type, name);
276         return (val);
277 }
278
279 static
280 int
281 file_getgroup(const char *etcdir, const char *group, gid_t *gidret)
282 {
283         FILE *fp;
284         size_t len;
285         size_t grlen;
286         char *path;
287         char *ptr;
288         char *scan;
289
290         grlen = strlen(group);
291
292         if (asprintf(&path, "%s/group", etcdir) < 0)
293                 errx(EX_OSERR, "asprintf()");
294         if ((fp = fopen(path, "r")) != NULL) {
295                 while ((ptr = fgetln(fp, &len)) != NULL && len) {
296                         ptr[len - 1] = 0;
297                         if ((scan = strchr(ptr, ':')) == NULL)
298                                 continue;
299                         if ((size_t)(scan - ptr) != grlen)
300                                 continue;
301                         if (strncmp(ptr, group, grlen) != 0)
302                                 continue;
303                         if ((scan = strchr(scan + 1, ':')) == NULL)
304                                 continue;
305                         *gidret = strtoul(scan + 1, NULL, 10);
306                         break;
307                 }
308                 fclose(fp);
309         }
310         free(path);
311         return((*gidret == (gid_t)-1) ? 0 : 1);
312 }
313
314 static
315 int
316 file_getowner(const char *etcdir, const char *owner, uid_t *uidret)
317 {
318         FILE *fp;
319         size_t len;
320         size_t owner_len;
321         char *path;
322         char *ptr;
323         char *scan;
324
325         owner_len = strlen(owner);
326
327         if (asprintf(&path, "%s/master.passwd", etcdir) < 0)
328                 errx(EX_OSERR, "asprintf()");
329         if ((fp = fopen(path, "r")) != NULL) {
330                 while ((ptr = fgetln(fp, &len)) != NULL && len) {
331                         ptr[len - 1] = 0;
332                         if ((scan = strchr(ptr, ':')) == NULL)
333                                 continue;
334                         if ((size_t)(scan - ptr) != owner_len)
335                                 continue;
336                         if (strncmp(ptr, owner, owner_len) != 0)
337                                 continue;
338                         if ((scan = strchr(scan + 1, ':')) == NULL)
339                                 continue;
340                         *uidret = strtoul(scan + 1, NULL, 10);
341                         break;
342                 }
343                 fclose(fp);
344         }
345         free(path);
346         return((*uidret == (uid_t)-1) ? 0 : 1);
347 }
348
349 /*
350  * install --
351  *      build a path name and install the file
352  */
353 void
354 install(const char *from_name, const char *to_name, u_long fset, u_long fclr,
355         u_int flags)
356 {
357         struct stat from_sb, temp_sb, to_sb;
358         struct utimbuf utb;
359         int devnull, files_match, from_fd, serrno, target;
360         int tempcopy, temp_fd, to_fd;
361         u_long nfset;
362         char backup[MAXPATHLEN], *p, pathbuf[MAXPATHLEN], tempfile[MAXPATHLEN];
363
364         files_match = 0;
365         from_fd = -1;
366
367         /* If try to install NULL file to a directory, fails. */
368         if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) {
369                 if (stat(from_name, &from_sb))
370                         err(EX_OSERR, "%s", from_name);
371                 if (!S_ISREG(from_sb.st_mode)) {
372                         errno = EFTYPE;
373                         err(EX_OSERR, "%s", from_name);
374                 }
375                 /* Build the target path. */
376                 if (flags & DIRECTORY) {
377                         snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
378                             to_name,
379                             (p = strrchr(from_name, '/')) ? ++p : from_name);
380                         to_name = pathbuf;
381                 }
382                 devnull = 0;
383         } else {
384                 devnull = 1;
385         }
386
387         target = stat(to_name, &to_sb) == 0;
388
389         /* Only install to regular files. */
390         if (target && !S_ISREG(to_sb.st_mode)) {
391                 errno = EFTYPE;
392                 warn("%s", to_name);
393                 return;
394         }
395
396         /* Only copy safe if the target exists. */
397         tempcopy = safecopy && target;
398
399         if (!devnull && (from_fd = open(from_name, O_RDONLY, 0)) < 0)
400                 err(EX_OSERR, "%s", from_name);
401
402         /* If we don't strip, we can compare first. */
403         if (docompare && !dostrip && target) {
404                 if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
405                         err(EX_OSERR, "%s", to_name);
406                 if (devnull)
407                         files_match = to_sb.st_size == 0;
408                 else
409                         files_match = !(compare(from_fd, from_name,
410                             (size_t)from_sb.st_size, to_fd,
411                             to_name, (size_t)to_sb.st_size));
412
413                 /* Close "to" file unless we match. */
414                 if (!files_match)
415                         close(to_fd);
416         }
417
418         if (!files_match) {
419                 if (tempcopy) {
420                         to_fd = create_tempfile(to_name, tempfile,
421                             sizeof(tempfile));
422                         if (to_fd < 0)
423                                 err(EX_OSERR, "%s", tempfile);
424                 } else {
425                         if ((to_fd = create_newfile(to_name, target,
426                             &to_sb)) < 0)
427                                 err(EX_OSERR, "%s", to_name);
428                         if (verbose)
429                                 printf("install: %s -> %s\n",
430                                     from_name, to_name);
431                 }
432                 if (!devnull)
433                         copy(from_fd, from_name, to_fd,
434                              tempcopy ? tempfile : to_name, from_sb.st_size);
435         }
436
437         if (dostrip) {
438                 strip(tempcopy ? tempfile : to_name);
439
440                 /*
441                  * Re-open our fd on the target, in case we used a strip
442                  * that does not work in-place -- like GNU binutils strip.
443                  */
444                 close(to_fd);
445                 to_fd = open(tempcopy ? tempfile : to_name, O_RDONLY, 0);
446                 if (to_fd < 0)
447                         err(EX_OSERR, "stripping %s", to_name);
448         }
449
450         /*
451          * Compare the stripped temp file with the target.
452          */
453         if (docompare && dostrip && target) {
454                 temp_fd = to_fd;
455
456                 /* Re-open to_fd using the real target name. */
457                 if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
458                         err(EX_OSERR, "%s", to_name);
459
460                 if (fstat(temp_fd, &temp_sb)) {
461                         serrno = errno;
462                         unlink(tempfile);
463                         errno = serrno;
464                         err(EX_OSERR, "%s", tempfile);
465                 }
466
467                 if (compare(temp_fd, tempfile, (size_t)temp_sb.st_size, to_fd,
468                             to_name, (size_t)to_sb.st_size) == 0) {
469                         /*
470                          * If target has more than one link we need to
471                          * replace it in order to snap the extra links.
472                          * Need to preserve target file times, though.
473                          */
474                         if (to_sb.st_nlink != 1) {
475                                 utb.actime = to_sb.st_atime;
476                                 utb.modtime = to_sb.st_mtime;
477                                 utime(tempfile, &utb);
478                         } else {
479                                 files_match = 1;
480                                 unlink(tempfile);
481                         }
482                         close(temp_fd);
483                 }
484         }
485
486         /*
487          * Move the new file into place if doing a safe copy
488          * and the files are different (or just not compared).
489          */
490         if (tempcopy && !files_match) {
491                 /* Try to turn off the immutable bits. */
492                 if (to_sb.st_flags & NOCHANGEBITS)
493                         chflags(to_name, to_sb.st_flags & ~NOCHANGEBITS);
494                 if (dobackup) {
495                         if ((size_t)snprintf(backup, MAXPATHLEN, "%s%s", to_name,
496                             suffix) != strlen(to_name) + strlen(suffix)) {
497                                 unlink(tempfile);
498                                 errx(EX_OSERR, "%s: backup filename too long",
499                                     to_name);
500                         }
501                         if (verbose)
502                                 printf("install: %s -> %s\n", to_name, backup);
503                         if (rename(to_name, backup) < 0) {
504                                 serrno = errno;
505                                 unlink(tempfile);
506                                 errno = serrno;
507                                 err(EX_OSERR, "rename: %s to %s", to_name,
508                                      backup);
509                         }
510                 }
511                 if (verbose)
512                         printf("install: %s -> %s\n", from_name, to_name);
513                 if (rename(tempfile, to_name) < 0) {
514                         serrno = errno;
515                         unlink(tempfile);
516                         errno = serrno;
517                         err(EX_OSERR, "rename: %s to %s",
518                             tempfile, to_name);
519                 }
520
521                 /* Re-open to_fd so we aren't hosed by the rename(2). */
522                 close(to_fd);
523                 if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
524                         err(EX_OSERR, "%s", to_name);
525         }
526
527         /*
528          * Preserve the timestamp of the source file if necessary.
529          */
530         if (dopreserve && !files_match && !devnull) {
531                 utb.actime = from_sb.st_atime;
532                 utb.modtime = from_sb.st_mtime;
533                 utime(to_name, &utb);
534         }
535
536         if (fstat(to_fd, &to_sb) == -1) {
537                 serrno = errno;
538                 unlink(to_name);
539                 errno = serrno;
540                 err(EX_OSERR, "%s", to_name);
541         }
542
543         /*
544          * Set owner, group, mode for target; do the chown first,
545          * chown may lose the setuid bits.
546          */
547         if ((gid != (gid_t)-1 && gid != to_sb.st_gid) ||
548             (uid != (uid_t)-1 && uid != to_sb.st_uid) ||
549             (mode != to_sb.st_mode)) {
550                 /* Try to turn off the immutable bits. */
551                 if (to_sb.st_flags & NOCHANGEBITS)
552                         fchflags(to_fd, to_sb.st_flags & ~NOCHANGEBITS);
553         }
554
555         if ((gid != (gid_t)-1 && gid != to_sb.st_gid) ||
556             (uid != (uid_t)-1 && uid != to_sb.st_uid))
557                 if (fchown(to_fd, uid, gid) == -1) {
558                         serrno = errno;
559                         unlink(to_name);
560                         errno = serrno;
561                         err(EX_OSERR,"%s: chown/chgrp", to_name);
562                 }
563
564         if (mode != to_sb.st_mode)
565                 if (fchmod(to_fd, mode)) {
566                         serrno = errno;
567                         unlink(to_name);
568                         errno = serrno;
569                         err(EX_OSERR, "%s: chmod", to_name);
570                 }
571
572         /*
573          * If provided a set of flags, set them, otherwise, preserve the
574          * flags, except for the dump and history flags.  The dump flag
575          * is left clear on the target while the history flag from when
576          * the target was created (which is inherited from the target's
577          * parent directory) is retained.
578          */
579         if (flags & SETFLAGS) {
580                 nfset = (to_sb.st_flags | fset) & ~fclr;
581         } else {
582                 nfset = (from_sb.st_flags & ~(UF_NODUMP | UF_NOHISTORY)) |
583                         (to_sb.st_flags & UF_NOHISTORY);
584         }
585
586         /*
587          * NFS does not support flags.  Ignore EOPNOTSUPP flags if we're just
588          * trying to turn off UF_NODUMP.  If we're trying to set real flags,
589          * then warn if the the fs doesn't support it, otherwise fail.
590          */
591         if (!devnull && fchflags(to_fd, nfset)) {
592                 if (flags & SETFLAGS) {
593                         if (errno == EOPNOTSUPP)
594                                 warn("%s: chflags", to_name);
595                         else {
596                                 serrno = errno;
597                                 unlink(to_name);
598                                 errno = serrno;
599                                 err(EX_OSERR, "%s: chflags", to_name);
600                         }
601                 }
602         }
603
604         close(to_fd);
605         if (!devnull)
606                 close(from_fd);
607 }
608
609 /*
610  * compare --
611  *      compare two files; non-zero means files differ
612  */
613 int
614 compare(int from_fd, const char *from_name __unused, size_t from_len,
615         int to_fd, const char *to_name __unused, size_t to_len)
616 {
617         char *p, *q;
618         int rv;
619         int done_compare;
620
621         rv = 0;
622         if (from_len != to_len)
623                 return 1;
624
625         if (from_len <= MAX_CMP_SIZE) {
626                 done_compare = 0;
627                 if (trymmap(from_fd) && trymmap(to_fd)) {
628                         p = mmap(NULL, from_len, PROT_READ, MAP_SHARED, from_fd, (off_t)0);
629                         if (p == (char *)MAP_FAILED)
630                                 goto out;
631                         q = mmap(NULL, from_len, PROT_READ, MAP_SHARED, to_fd, (off_t)0);
632                         if (q == (char *)MAP_FAILED) {
633                                 munmap(p, from_len);
634                                 goto out;
635                         }
636
637                         rv = memcmp(p, q, from_len);
638                         munmap(p, from_len);
639                         munmap(q, from_len);
640                         done_compare = 1;
641                 }
642         out:
643                 if (!done_compare) {
644                         char buf1[MAXBSIZE];
645                         char buf2[MAXBSIZE];
646                         int n1, n2;
647
648                         rv = 0;
649                         lseek(from_fd, 0, SEEK_SET);
650                         lseek(to_fd, 0, SEEK_SET);
651                         while (rv == 0) {
652                                 n1 = read(from_fd, buf1, sizeof(buf1));
653                                 if (n1 == 0)
654                                         break;          /* EOF */
655                                 else if (n1 > 0) {
656                                         n2 = read(to_fd, buf2, n1);
657                                         if (n2 == n1)
658                                                 rv = memcmp(buf1, buf2, n1);
659                                         else
660                                                 rv = 1; /* out of sync */
661                                 } else
662                                         rv = 1;         /* read failure */
663                         }
664                         lseek(from_fd, 0, SEEK_SET);
665                         lseek(to_fd, 0, SEEK_SET);
666                 }
667         } else
668                 rv = 1; /* don't bother in this case */
669
670         return rv;
671 }
672
673 /*
674  * create_tempfile --
675  *      create a temporary file based on path and open it
676  */
677 int
678 create_tempfile(const char *path, char *temp, size_t tsize)
679 {
680         char *p;
681
682         strncpy(temp, path, tsize);
683         temp[tsize - 1] = '\0';
684         if ((p = strrchr(temp, '/')) != NULL)
685                 p++;
686         else
687                 p = temp;
688         strncpy(p, "INS@XXXX", &temp[tsize - 1] - p);
689         temp[tsize - 1] = '\0';
690         return (mkstemp(temp));
691 }
692
693 /*
694  * create_newfile --
695  *      create a new file, overwriting an existing one if necessary
696  */
697 int
698 create_newfile(const char *path, int target, struct stat *sbp)
699 {
700         char backup[MAXPATHLEN];
701
702         if (target) {
703                 /*
704                  * Unlink now... avoid ETXTBSY errors later.  Try to turn
705                  * off the append/immutable bits -- if we fail, go ahead,
706                  * it might work.
707                  */
708                 if (sbp->st_flags & NOCHANGEBITS)
709                         chflags(path, sbp->st_flags & ~NOCHANGEBITS);
710
711                 if (dobackup) {
712                         if ((size_t)snprintf(backup, MAXPATHLEN, "%s%s",
713                             path, suffix) != strlen(path) + strlen(suffix))
714                                 errx(EX_OSERR, "%s: backup filename too long",
715                                     path);
716                         snprintf(backup, MAXPATHLEN, "%s%s",
717                             path, suffix);
718                         if (verbose)
719                                 printf("install: %s -> %s\n",
720                                     path, backup);
721                         if (rename(path, backup) < 0)
722                                 err(EX_OSERR, "rename: %s to %s", path, backup);
723                 } else
724                         unlink(path);
725         }
726
727         return (open(path, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR));
728 }
729
730 /*
731  * copy --
732  *      copy from one file to another
733  */
734 void
735 copy(int from_fd, const char *from_name, int to_fd,
736      const char *to_name, off_t size)
737 {
738         int nr, nw;
739         int serrno;
740         char *p, buf[MAXBSIZE];
741         int done_copy;
742
743         /* Rewind file descriptors. */
744         if (lseek(from_fd, (off_t)0, SEEK_SET) == (off_t)-1)
745                 err(EX_OSERR, "lseek: %s", from_name);
746         if (lseek(to_fd, (off_t)0, SEEK_SET) == (off_t)-1)
747                 err(EX_OSERR, "lseek: %s", to_name);
748
749         /*
750          * Mmap and write if less than 8M (the limit is so we don't totally
751          * trash memory on big files.  This is really a minor hack, but it
752          * wins some CPU back.
753          */
754         done_copy = 0;
755         if (size <= 8 * 1048576 && trymmap(from_fd) &&
756             (p = mmap(NULL, (size_t)size, PROT_READ, MAP_SHARED,
757                     from_fd, (off_t)0)) != (char *)MAP_FAILED) {
758                 if ((nw = write(to_fd, p, size)) != size) {
759                         serrno = errno;
760                         unlink(to_name);
761                         errno = nw > 0 ? EIO : serrno;
762                         err(EX_OSERR, "%s", to_name);
763                 }
764                 done_copy = 1;
765         }
766         if (!done_copy) {
767                 while ((nr = read(from_fd, buf, sizeof(buf))) > 0)
768                         if ((nw = write(to_fd, buf, nr)) != nr) {
769                                 serrno = errno;
770                                 unlink(to_name);
771                                 errno = nw > 0 ? EIO : serrno;
772                                 err(EX_OSERR, "%s", to_name);
773                         }
774                 if (nr != 0) {
775                         serrno = errno;
776                         unlink(to_name);
777                         errno = serrno;
778                         err(EX_OSERR, "%s", from_name);
779                 }
780         }
781 }
782
783 /*
784  * strip --
785  *      use strip(1) to strip the target file
786  */
787 void
788 strip(const char *to_name)
789 {
790         const char *stripbin;
791         int serrno, status;
792
793         switch (fork()) {
794         case -1:
795                 serrno = errno;
796                 unlink(to_name);
797                 errno = serrno;
798                 err(EX_TEMPFAIL, "fork");
799         case 0:
800                 stripbin = getenv("STRIPBIN");
801                 if (stripbin == NULL)
802                         stripbin = "strip";
803                 execlp(stripbin, stripbin, to_name, NULL);
804                 err(EX_OSERR, "exec(%s)", stripbin);
805         default:
806                 if (wait(&status) == -1 || status) {
807                         serrno = errno;
808                         unlink(to_name);
809                         errc(EX_SOFTWARE, serrno, "wait");
810                         /* NOTREACHED */
811                 }
812         }
813 }
814
815 /*
816  * install_dir --
817  *      build directory hierarchy
818  */
819 void
820 install_dir(char *path)
821 {
822         char *p;
823         struct stat sb;
824         int ch;
825
826         for (p = path;; ++p)
827                 if (!*p || (p != path && *p  == '/')) {
828                         ch = *p;
829                         *p = '\0';
830                         if (stat(path, &sb)) {
831                                 if (errno != ENOENT || mkdir(path, 0755) < 0) {
832                                         err(EX_OSERR, "mkdir %s", path);
833                                         /* NOTREACHED */
834                                 } else if (verbose)
835                                         printf("install: mkdir %s\n",
836                                                      path);
837                         } else if (!S_ISDIR(sb.st_mode))
838                                 errx(EX_OSERR, "%s exists but is not a directory", path);
839                         if (!(*p = ch))
840                                 break;
841                 }
842
843         if ((gid != (gid_t)-1 || uid != (uid_t)-1) && chown(path, uid, gid))
844                 warn("chown %u:%u %s", uid, gid, path);
845         if (chmod(path, mode))
846                 warn("chmod %o %s", mode, path);
847 }
848
849 /*
850  * usage --
851  *      print a usage message and die
852  */
853 void
854 usage(void)
855 {
856         fprintf(stderr, "\
857 usage: install [-bCcpSsv] [-B suffix] [-D dest] [-f flags] [-g group] [-m mode]\n\
858                [-o owner] file1 file2\n\
859        install [-bCcpSsv] [-B suffix] [-D dest] [-f flags] [-g group] [-m mode]\n\
860                [-o owner] file1 ... fileN directory\n\
861        install -d [-v] [-D dest] [-g group] [-m mode] [-o owner] directory ...\n");
862         exit(EX_USAGE);
863         /* NOTREACHED */
864 }
865
866 /*
867  * trymmap --
868  *      return true (1) if mmap should be tried, false (0) if not.
869  */
870 int
871 trymmap(int fd)
872 {
873 /*
874  * The ifdef is for bootstrapping - f_fstypename doesn't exist in
875  * pre-Lite2-merge systems.
876  */
877 #ifdef MFSNAMELEN
878         struct statfs stfs;
879
880         if (nommap || fstatfs(fd, &stfs) != 0)
881                 return (0);
882         if (strcmp(stfs.f_fstypename, "ufs") == 0 ||
883             strcmp(stfs.f_fstypename, "cd9660") == 0)
884                 return (1);
885 #endif
886         return (0);
887 }