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