xref: /dragonfly/usr.bin/xinstall/xinstall.c (revision 03a689c27de456131d38970ba41d96165f10a021)
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. 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  * @(#) Copyright (c) 1987, 1993 The Regents of the University of California.  All rights reserved.
30  * @(#)xinstall.c   8.1 (Berkeley) 7/21/93
31  */
32 
33 #include <sys/param.h>
34 #include <sys/mman.h>
35 #include <sys/mount.h>
36 #include <sys/stat.h>
37 #include <sys/wait.h>
38 
39 #include <ctype.h>
40 #include <err.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <grp.h>
44 #include <libgen.h>
45 #include <paths.h>
46 #include <pwd.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <sysexits.h>
51 #include <unistd.h>
52 #include <utime.h>
53 #include <vis.h>
54 
55 #include "mtree.h"
56 
57 /* Bootstrap aid - this doesn't exist in most older releases */
58 #ifndef MAP_FAILED
59 #define MAP_FAILED ((void *)-1)         /* from <sys/mman.h> */
60 #endif
61 #ifndef UF_NOHISTORY
62 #define UF_NOHISTORY          0
63 #endif
64 
65 #define MAX_CMP_SIZE          (16 * 1024 * 1024)
66 
67 #define   LN_ABSOLUTE         0x01
68 #define   LN_RELATIVE         0x02
69 #define   LN_HARD             0x04
70 #define   LN_SYMBOLIC         0x08
71 #define   LN_MIXED  0x10
72 
73 #define   DIRECTORY 0x01                /* Tell install it's a directory. */
74 #define   SETFLAGS  0x02                /* Tell install to set flags. */
75 #define   NOCHANGEBITS        (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)
76 #define   BACKUP_SUFFIX       ".old"
77 
78 static gid_t gid;
79 static uid_t uid;
80 static int dobackup, docompare, dodir, dolink, dopreserve, dostrip, dounpriv,
81     nommap, safecopy, verbose;
82 static int haveopt_f, haveopt_g, haveopt_m, haveopt_o;
83 static mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
84 static const char *group, *owner;
85 static const char *suffix = BACKUP_SUFFIX;
86 static char *destdir, *fflags;
87 
88 static int          compare(int, const char *, size_t, int, const char *, size_t);
89 static void         copy(int, const char *, int, const char *, off_t);
90 static int          create_newfile(const char *, int, struct stat *);
91 static int          create_tempfile(const char *, char *, size_t);
92 static int          do_link(const char *, const char *, const struct stat *);
93 static void         do_symlink(const char *, const char *, const struct stat *);
94 static void         makelink(const char *, const char *, const struct stat *);
95 static void         install(const char *, const char *, u_long, u_long, u_int);
96 static void         install_dir(char *);
97 static int          parseid(const char *, id_t *);
98 static void         strip(const char *);
99 static int          trymmap(int);
100 static void         usage(void);
101 
102 int
main(int argc,char * argv[])103 main(int argc, char *argv[])
104 {
105           struct stat from_sb, to_sb;
106           mode_t *set;
107           u_long fset;
108           u_long fclr;
109           int ch, no_target;
110           u_int iflags;
111           char *p;
112           const char *to_name;
113 
114           fclr = 0;
115           fset = 0;
116           iflags = 0;
117           group = NULL;
118           owner = NULL;
119 
120           /* NOTE: please also update 'tools/install.sh' */
121           while ((ch = getopt(argc, argv, "B:bCcD:df:g:L:l:M:m:N:o:pSsUv")) != -1)
122                     switch((char)ch) {
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                               destdir = optarg;
137                               break;
138                     case 'd':
139                               dodir = 1;
140                               break;
141                     case 'f':
142 #ifdef _ST_FLAGS_PRESENT_
143                               haveopt_f = 1;
144                               fflags = optarg;
145 #endif
146                               break;
147                     case 'g':
148                               haveopt_g = 1;
149                               group = optarg;
150                               break;
151                     case 'l':
152                               for (p = optarg; *p; p++)
153                                         switch (*p) {
154                                         case 's':
155                                                   dolink &= ~(LN_HARD|LN_MIXED);
156                                                   dolink |= LN_SYMBOLIC;
157                                                   break;
158                                         case 'h':
159                                                   dolink &= ~(LN_SYMBOLIC|LN_MIXED);
160                                                   dolink |= LN_HARD;
161                                                   break;
162                                         case 'm':
163                                                   dolink &= ~(LN_SYMBOLIC|LN_HARD);
164                                                   dolink |= LN_MIXED;
165                                                   break;
166                                         case 'a':
167                                                   dolink &= ~LN_RELATIVE;
168                                                   dolink |= LN_ABSOLUTE;
169                                                   break;
170                                         case 'r':
171                                                   dolink &= ~LN_ABSOLUTE;
172                                                   dolink |= LN_RELATIVE;
173                                                   break;
174                                         default:
175                                                   errx(EXIT_FAILURE, "%c: invalid link type", *p);
176                                                   /* NOTREACHED */
177                                         }
178                               break;
179                     case 'M':
180                               nommap = 1;
181                               break;
182                     case 'm':
183                               haveopt_m = 1;
184                               if (!(set = setmode(optarg)))
185                                         errx(EX_USAGE, "invalid file mode: %s",
186                                              optarg);
187                               mode = getmode(set, 0);
188                               free(set);
189                               break;
190                     case 'L':
191                               /* -L kept for compatibility with pre-5.4 DragonFly */
192                               warnx("Option -L is deprecated, use -N instead");
193                               /* FALLTHROUGH */
194                     case 'N':
195                               if (!setup_getid(optarg))
196                                         err(EX_OSERR, "Unable to use user and group "
197                                             "databases in `%s'", optarg);
198                               break;
199                     case 'o':
200                               haveopt_o = 1;
201                               owner = optarg;
202                               break;
203                     case 'p':
204                               docompare = dopreserve = 1;
205                               break;
206                     case 'S':
207                               safecopy = 1;
208                               break;
209                     case 's':
210                               dostrip = 1;
211                               break;
212                     case 'U':
213                               dounpriv = 1;
214                               break;
215                     case 'v':
216                               verbose = 1;
217                               break;
218                     case '?':
219                     default:
220                               usage();
221                     }
222           argc -= optind;
223           argv += optind;
224 
225           /* some options make no sense when creating directories */
226           if (dostrip && dodir) {
227                     warnx("-d and -s may not be specified together");
228                     usage();
229           }
230 
231           if (getenv("DONTSTRIP") != NULL) {
232                     warnx("DONTSTRIP set - will not strip installed binaries");
233                     dostrip = 0;
234           }
235 
236           /* must have at least two arguments, except when creating directories */
237           if (argc == 0 || (argc == 1 && !dodir))
238                     usage();
239 
240           /* need to make a temp copy so we can compare stripped version */
241           if (docompare && dostrip)
242                     safecopy = 1;
243 
244           /* get group and owner id's */
245           if (group != NULL && !dounpriv) {
246                     if (gid_from_group(group, &gid) == -1) {
247                               id_t id;
248                               if (!parseid(group, &id))
249                                         errx(1, "unknown group %s", group);
250                               gid = id;
251                     }
252           } else
253                     gid = (gid_t)-1;
254 
255           if (owner != NULL && !dounpriv) {
256                     if (uid_from_user(owner, &uid) == -1) {
257                               id_t id;
258                               if (!parseid(owner, &id))
259                                         errx(1, "unknown user %s", owner);
260                               uid = id;
261                     }
262           } else
263                     uid = (uid_t)-1;
264 
265 #ifdef _ST_FLAGS_PRESENT_
266           if (fflags != NULL && !dounpriv) {
267                     if (strtofflags(&fflags, &fset, &fclr))
268                               errx(EX_USAGE, "%s: invalid flag", fflags);
269                     iflags |= SETFLAGS;
270           }
271 #endif
272 
273           if (dodir) {
274                     for (; *argv != NULL; ++argv)
275                               install_dir(*argv);
276                     exit(EX_OK);
277                     /* NOTREACHED */
278           }
279 
280           to_name = argv[argc - 1];
281           no_target = stat(to_name, &to_sb);
282           if (!no_target && S_ISDIR(to_sb.st_mode)) {
283                     if (dolink & LN_SYMBOLIC) {
284                               if (lstat(to_name, &to_sb) != 0)
285                                         err(EX_OSERR, "%s vanished", to_name);
286                               if (S_ISLNK(to_sb.st_mode)) {
287                                         if (argc != 2) {
288                                                   errno = ENOTDIR;
289                                                   err(EX_USAGE, "%s", to_name);
290                                         }
291                                         install(*argv, to_name, fset, fclr, iflags);
292                                         exit(EX_OK);
293                               }
294                     }
295                     for (; *argv != to_name; ++argv)
296                               install(*argv, to_name, fset, fclr, iflags | DIRECTORY);
297                     exit(EX_OK);
298                     /* NOTREACHED */
299           }
300 
301           /* can't do file1 file2 directory/file */
302           if (argc != 2) {
303                     if (no_target)
304                               warnx("target directory `%s' does not exist",
305                                   argv[argc - 1]);
306                     else
307                               warnx("target `%s' is not a directory",
308                                   argv[argc - 1]);
309                     usage();
310           }
311 
312           if (!no_target && !dolink) {
313                     if (stat(*argv, &from_sb))
314                               err(EX_OSERR, "%s", *argv);
315                     if (!S_ISREG(to_sb.st_mode)) {
316                               errno = EFTYPE;
317                               err(EX_OSERR, "%s", to_name);
318                     }
319                     if (to_sb.st_dev == from_sb.st_dev &&
320                         to_sb.st_ino == from_sb.st_ino)
321                               errx(EX_USAGE,
322                                   "%s and %s are the same file", *argv, to_name);
323           }
324           install(*argv, to_name, fset, fclr, iflags);
325           exit(EX_OK);
326           /* NOTREACHED */
327 }
328 
329 /*
330  * parseid --
331  *        parse uid or gid from arg into id, returning non-zero if successful
332  */
333 static int
parseid(const char * name,id_t * id)334 parseid(const char *name, id_t *id)
335 {
336           char      *ep;
337           errno = 0;
338           *id = (id_t)strtoul(name, &ep, 10);
339           if (errno || *ep != '\0')
340                     return (0);
341           return (1);
342 }
343 
344 /*
345  * quiet_mktemp --
346  *        mktemp implementation used mkstemp to avoid mktemp warnings.  We
347  *        really do need mktemp semantics here as we will be creating a link.
348  */
349 static char *
quiet_mktemp(char * template)350 quiet_mktemp(char *template)
351 {
352           int fd;
353 
354           if ((fd = mkstemp(template)) == -1)
355                     return (NULL);
356           close (fd);
357           if (unlink(template) == -1)
358                     err(EX_OSERR, "unlink %s", template);
359           return (template);
360 }
361 
362 /*
363  * do_link --
364  *        make a hard link, obeying dorename if set
365  *        return -1 on failure
366  */
367 static int
do_link(const char * from_name,const char * to_name,const struct stat * target_sb)368 do_link(const char *from_name, const char *to_name,
369     const struct stat *target_sb)
370 {
371           char tmpl[MAXPATHLEN];
372           int ret;
373 
374           if (safecopy && target_sb != NULL) {
375                     (void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name);
376                     /* This usage is safe. */
377                     if (quiet_mktemp(tmpl) == NULL)
378                               err(EX_OSERR, "%s: mktemp", tmpl);
379                     ret = link(from_name, tmpl);
380                     if (ret == 0) {
381                               if (target_sb->st_mode & S_IFDIR && rmdir(to_name) ==
382                                   -1) {
383                                         unlink(tmpl);
384                                         err(EX_OSERR, "%s", to_name);
385                               }
386 #ifdef _ST_FLAGS_PRESENT_
387                               if (target_sb->st_flags & NOCHANGEBITS)
388                                         (void)chflags(to_name, target_sb->st_flags &
389                                              ~NOCHANGEBITS);
390 #endif
391                               if (verbose)
392                                         printf("install: link %s -> %s\n",
393                                             from_name, to_name);
394                               ret = rename(tmpl, to_name);
395                               /*
396                                * If rename has posix semantics, then the temporary
397                                * file may still exist when from_name and to_name point
398                                * to the same file, so unlink it unconditionally.
399                                */
400                               (void)unlink(tmpl);
401                     }
402                     return (ret);
403           } else {
404                     if (verbose)
405                               printf("install: link %s -> %s\n",
406                                   from_name, to_name);
407                     return (link(from_name, to_name));
408           }
409 }
410 
411 /*
412  * do_symlink --
413  *        Make a symbolic link, obeying dorename if set. Exit on failure.
414  */
415 static void
do_symlink(const char * from_name,const char * to_name,const struct stat * target_sb)416 do_symlink(const char *from_name, const char *to_name,
417     const struct stat *target_sb)
418 {
419           char tmpl[MAXPATHLEN];
420 
421           if (safecopy && target_sb != NULL) {
422                     (void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name);
423                     /* This usage is safe. */
424                     if (quiet_mktemp(tmpl) == NULL)
425                               err(EX_OSERR, "%s: mktemp", tmpl);
426 
427                     if (symlink(from_name, tmpl) == -1)
428                               err(EX_OSERR, "symlink %s -> %s", from_name, tmpl);
429 
430                     if (target_sb->st_mode & S_IFDIR && rmdir(to_name) == -1) {
431                               (void)unlink(tmpl);
432                               err(EX_OSERR, "%s", to_name);
433                     }
434 #ifdef _ST_FLAGS_PRESENT_
435                     if (target_sb->st_flags & NOCHANGEBITS)
436                               (void)chflags(to_name, target_sb->st_flags &
437                                    ~NOCHANGEBITS);
438 #endif
439                     if (verbose)
440                               printf("install: symlink %s -> %s\n",
441                                   from_name, to_name);
442                     if (rename(tmpl, to_name) == -1) {
443                               /* Remove temporary link before exiting. */
444                               (void)unlink(tmpl);
445                               err(EX_OSERR, "%s: rename", to_name);
446                     }
447           } else {
448                     if (verbose)
449                               printf("install: symlink %s -> %s\n",
450                                   from_name, to_name);
451                     if (symlink(from_name, to_name) == -1)
452                               err(EX_OSERR, "symlink %s -> %s", from_name, to_name);
453           }
454 }
455 
456 /*
457  * makelink --
458  *        make a link from source to destination
459  */
460 static void
makelink(const char * from_name,const char * to_name,const struct stat * target_sb)461 makelink(const char *from_name, const char *to_name,
462     const struct stat *target_sb)
463 {
464           char      src[MAXPATHLEN], dst[MAXPATHLEN], lnk[MAXPATHLEN];
465           struct stat         to_sb;
466 
467           /* Try hard links first. */
468           if (dolink & (LN_HARD|LN_MIXED)) {
469                     if (do_link(from_name, to_name, target_sb) == -1) {
470                               if ((dolink & LN_HARD) || errno != EXDEV)
471                                         err(EX_OSERR, "link %s -> %s", from_name, to_name);
472                     } else {
473                               if (stat(to_name, &to_sb))
474                                         err(EX_OSERR, "%s: stat", to_name);
475                               if (S_ISREG(to_sb.st_mode)) {
476                                         /*
477                                          * XXX: hard links to anything other than
478                                          * plain files are not metalogged
479                                          */
480                                         int omode;
481                                         const char *oowner, *ogroup;
482                                         char *offlags;
483 
484                                         /*
485                                          * XXX: use underlying perms, unless
486                                          * overridden on command line.
487                                          */
488                                         omode = mode;
489                                         if (!haveopt_m)
490                                                   mode = (to_sb.st_mode & 0777);
491                                         oowner = owner;
492                                         if (!haveopt_o)
493                                                   owner = NULL;
494                                         ogroup = group;
495                                         if (!haveopt_g)
496                                                   group = NULL;
497                                         offlags = fflags;
498                                         if (!haveopt_f)
499                                                   fflags = NULL;
500                                         mode = omode;
501                                         owner = oowner;
502                                         group = ogroup;
503                                         fflags = offlags;
504                               }
505                               return;
506                     }
507           }
508 
509           /* Symbolic links. */
510           if (dolink & LN_ABSOLUTE) {
511                     /* Convert source path to absolute. */
512                     if (realpath(from_name, src) == NULL)
513                               err(EX_OSERR, "%s: realpath", from_name);
514                     do_symlink(src, to_name, target_sb);
515                     /* XXX: src may point outside of destdir */
516                     return;
517           }
518 
519           if (dolink & LN_RELATIVE) {
520                     char *to_name_copy, *cp, *d, *s;
521 
522                     if (*from_name != '/') {
523                               /* this is already a relative link */
524                               do_symlink(from_name, to_name, target_sb);
525                               /* XXX: from_name may point outside of destdir. */
526                               return;
527                     }
528 
529                     /* Resolve pathnames. */
530                     if (realpath(from_name, src) == NULL)
531                               err(EX_OSERR, "%s: realpath", from_name);
532 
533                     /*
534                      * The last component of to_name may be a symlink,
535                      * so use realpath to resolve only the directory.
536                      */
537                     to_name_copy = strdup(to_name);
538                     if (to_name_copy == NULL)
539                               err(EX_OSERR, "%s: strdup", to_name);
540                     cp = dirname(to_name_copy);
541                     if (realpath(cp, dst) == NULL)
542                               err(EX_OSERR, "%s: realpath", cp);
543                     /* .. and add the last component. */
544                     if (strcmp(dst, "/") != 0) {
545                               if (strlcat(dst, "/", sizeof(dst)) > sizeof(dst))
546                                         errx(1, "resolved pathname too long");
547                     }
548                     strcpy(to_name_copy, to_name);
549                     cp = basename(to_name_copy);
550                     if (strlcat(dst, cp, sizeof(dst)) > sizeof(dst))
551                               errx(1, "resolved pathname too long");
552                     free(to_name_copy);
553 
554                     /* Trim common path components. */
555                     for (s = src, d = dst; *s == *d; s++, d++)
556                               continue;
557                     while (*s != '/')
558                               s--, d--;
559 
560                     /* Count the number of directories we need to backtrack. */
561                     for (++d, lnk[0] = '\0'; *d; d++)
562                               if (*d == '/')
563                                         (void)strlcat(lnk, "../", sizeof(lnk));
564 
565                     (void)strlcat(lnk, ++s, sizeof(lnk));
566 
567                     do_symlink(lnk, to_name, target_sb);
568                     /* XXX: Link may point outside of destdir. */
569                     return;
570           }
571 
572           /*
573            * If absolute or relative was not specified, try the names the
574            * user provided.
575            */
576           do_symlink(from_name, to_name, target_sb);
577           /* XXX: from_name may point outside of destdir. */
578 }
579 
580 /*
581  * install --
582  *        build a path name and install the file
583  */
584 static void
install(const char * from_name,const char * to_name,u_long fset,u_long fclr,u_int flags)585 install(const char *from_name, const char *to_name, u_long fset, u_long fclr,
586           u_int flags)
587 {
588           struct stat from_sb, temp_sb, to_sb;
589           struct utimbuf utb;
590           int devnull, files_match, from_fd, serrno, target;
591           int tempcopy, temp_fd, to_fd;
592 #ifdef _ST_FLAGS_PRESENT_
593           u_long nfset;
594 #endif
595           char backup[MAXPATHLEN], *p, pathbuf[MAXPATHLEN], tempfile[MAXPATHLEN];
596 
597           files_match = 0;
598           from_fd = -1;
599           to_fd = -1;
600 
601           /* If try to install NULL file to a directory, fails. */
602           if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) {
603                     if (!dolink) {
604                               if (stat(from_name, &from_sb))
605                                         err(EX_OSERR, "%s", from_name);
606                               if (!S_ISREG(from_sb.st_mode)) {
607                                         errno = EFTYPE;
608                                         err(EX_OSERR, "%s", from_name);
609                               }
610                     }
611                     /* Build the target path. */
612                     if (flags & DIRECTORY) {
613                               (void)snprintf(pathbuf, sizeof(pathbuf), "%s%s%s",
614                                   to_name,
615                                   to_name[strlen(to_name) - 1] == '/' ? "" : "/",
616                                   (p = strrchr(from_name, '/')) ? ++p : from_name);
617                               to_name = pathbuf;
618                     }
619                     devnull = 0;
620           } else {
621                     devnull = 1;
622           }
623 
624           target = (lstat(to_name, &to_sb) == 0);
625 
626           if (dolink) {
627                     if (target && !safecopy) {
628                               if (to_sb.st_mode & S_IFDIR && rmdir(to_name) == -1)
629                                         err(EX_OSERR, "%s", to_name);
630 #ifdef _ST_FLAGS_PRESENT_
631                               if (to_sb.st_flags & NOCHANGEBITS)
632                                         (void)chflags(to_name,
633                                             to_sb.st_flags & ~NOCHANGEBITS);
634 #endif
635                               unlink(to_name);
636                     }
637                     makelink(from_name, to_name, target ? &to_sb : NULL);
638                     return;
639           }
640 
641           if (target && !S_ISREG(to_sb.st_mode) && !S_ISLNK(to_sb.st_mode)) {
642                     errno = EFTYPE;
643                     warn("%s", to_name);
644                     return;
645           }
646 
647           /* Only copy safe if the target exists. */
648           tempcopy = safecopy && target;
649 
650           if (!devnull && (from_fd = open(from_name, O_RDONLY, 0)) < 0)
651                     err(EX_OSERR, "%s", from_name);
652 
653           /* If we don't strip, we can compare first. */
654           if (docompare && !dostrip && target && S_ISREG(to_sb.st_mode)) {
655                     if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
656                               err(EX_OSERR, "%s", to_name);
657                     if (devnull)
658                               files_match = to_sb.st_size == 0;
659                     else
660                               files_match = !(compare(from_fd, from_name,
661                                   (size_t)from_sb.st_size, to_fd,
662                                   to_name, (size_t)to_sb.st_size));
663 
664                     /* Close "to" file unless we match. */
665                     if (!files_match)
666                               (void)close(to_fd);
667           }
668 
669           if (!files_match) {
670                     if (tempcopy) {
671                               to_fd = create_tempfile(to_name, tempfile,
672                                   sizeof(tempfile));
673                               if (to_fd < 0)
674                                         err(EX_OSERR, "%s", tempfile);
675                     } else {
676                               if ((to_fd = create_newfile(to_name, target,
677                                   &to_sb)) < 0)
678                                         err(EX_OSERR, "%s", to_name);
679                               if (verbose)
680                                         (void)printf("install: %s -> %s\n",
681                                             from_name, to_name);
682                     }
683                     if (!devnull)
684                               copy(from_fd, from_name, to_fd,
685                                    tempcopy ? tempfile : to_name, from_sb.st_size);
686           }
687 
688           if (dostrip) {
689                     strip(tempcopy ? tempfile : to_name);
690 
691                     /*
692                      * Re-open our fd on the target, in case we used a strip
693                      * that does not work in-place -- like GNU binutils strip.
694                      */
695                     close(to_fd);
696                     to_fd = open(tempcopy ? tempfile : to_name, O_RDONLY, 0);
697                     if (to_fd < 0)
698                               err(EX_OSERR, "stripping %s", to_name);
699           }
700 
701           /*
702            * Compare the stripped temp file with the target.
703            */
704           if (docompare && dostrip && target && S_ISREG(to_sb.st_mode)) {
705                     temp_fd = to_fd;
706 
707                     /* Re-open to_fd using the real target name. */
708                     if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
709                               err(EX_OSERR, "%s", to_name);
710 
711                     if (fstat(temp_fd, &temp_sb)) {
712                               serrno = errno;
713                               (void)unlink(tempfile);
714                               errno = serrno;
715                               err(EX_OSERR, "%s", tempfile);
716                     }
717 
718                     if (compare(temp_fd, tempfile, (size_t)temp_sb.st_size, to_fd,
719                                   to_name, (size_t)to_sb.st_size) == 0) {
720                               /*
721                                * If target has more than one link we need to
722                                * replace it in order to snap the extra links.
723                                * Need to preserve target file times, though.
724                                */
725                               if (to_sb.st_nlink != 1) {
726                                         utb.actime = to_sb.st_atime;
727                                         utb.modtime = to_sb.st_mtime;
728                                         utime(tempfile, &utb);
729                               } else {
730                                         files_match = 1;
731                                         (void)unlink(tempfile);
732                               }
733                               (void) close(temp_fd);
734                     }
735           }
736 
737           /*
738            * Move the new file into place if doing a safe copy
739            * and the files are different (or just not compared).
740            */
741           if (tempcopy && !files_match) {
742 #ifdef _ST_FLAGS_PRESENT_
743                     /* Try to turn off the immutable bits. */
744                     if (to_sb.st_flags & NOCHANGEBITS)
745                               (void)chflags(to_name, to_sb.st_flags & ~NOCHANGEBITS);
746 #endif
747                     if (dobackup) {
748                               if ((size_t)snprintf(backup, MAXPATHLEN, "%s%s", to_name,
749                                   suffix) != strlen(to_name) + strlen(suffix)) {
750                                         unlink(tempfile);
751                                         errx(EX_OSERR, "%s: backup filename too long",
752                                             to_name);
753                               }
754                               if (verbose)
755                                         (void)printf("install: %s -> %s\n", to_name, backup);
756                               if (rename(to_name, backup) < 0) {
757                                         serrno = errno;
758                                         unlink(tempfile);
759                                         errno = serrno;
760                                         err(EX_OSERR, "rename: %s to %s", to_name,
761                                              backup);
762                               }
763                     }
764                     if (verbose)
765                               (void)printf("install: %s -> %s\n", from_name, to_name);
766                     if (rename(tempfile, to_name) < 0) {
767                               serrno = errno;
768                               unlink(tempfile);
769                               errno = serrno;
770                               err(EX_OSERR, "rename: %s to %s",
771                                   tempfile, to_name);
772                     }
773 
774                     /* Re-open to_fd so we aren't hosed by the rename(2). */
775                     (void) close(to_fd);
776                     if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
777                               err(EX_OSERR, "%s", to_name);
778           }
779 
780           /*
781            * Preserve the timestamp of the source file if necessary.
782            */
783           if (dopreserve && !files_match && !devnull) {
784                     utb.actime = from_sb.st_atime;
785                     utb.modtime = from_sb.st_mtime;
786                     utime(to_name, &utb);
787           }
788 
789           if (fstat(to_fd, &to_sb) == -1) {
790                     serrno = errno;
791                     (void)unlink(to_name);
792                     errno = serrno;
793                     err(EX_OSERR, "%s", to_name);
794           }
795 
796           /*
797            * Set owner, group, mode for target; do the chown first,
798            * chown may lose the setuid bits.
799            */
800 #ifdef _ST_FLAGS_PRESENT_
801           if (!dounpriv && ((gid != (gid_t)-1 && gid != to_sb.st_gid) ||
802               (uid != (uid_t)-1 && uid != to_sb.st_uid) ||
803               (mode != to_sb.st_mode))) {
804                     /* Try to turn off the immutable bits. */
805                     if (to_sb.st_flags & NOCHANGEBITS)
806                               (void)fchflags(to_fd, to_sb.st_flags & ~NOCHANGEBITS);
807           }
808 #endif
809 
810           if (!dounpriv && (
811               (gid != (gid_t)-1 && gid != to_sb.st_gid) ||
812               (uid != (uid_t)-1 && uid != to_sb.st_uid)))
813                     if (fchown(to_fd, uid, gid) == -1) {
814                               serrno = errno;
815                               (void)unlink(to_name);
816                               errno = serrno;
817                               err(EX_OSERR,"%s: chown/chgrp", to_name);
818                     }
819 
820           if (mode != to_sb.st_mode) {
821                     if (fchmod(to_fd,
822                          dounpriv ? mode & (S_IRWXU|S_IRWXG|S_IRWXO) : mode)) {
823                               serrno = errno;
824                               (void)unlink(to_name);
825                               errno = serrno;
826                               err(EX_OSERR, "%s: chmod", to_name);
827                     }
828           }
829 
830           /*
831            * If provided a set of flags, set them, otherwise, preserve the
832            * flags, except for the dump and history flags.  The dump flag
833            * is left clear on the target while the history flag from when
834            * the target was created (which is inherited from the target's
835            * parent directory) is retained.
836            */
837 #ifdef _ST_FLAGS_PRESENT_
838           if (flags & SETFLAGS) {
839                     nfset = (to_sb.st_flags | fset) & ~fclr;
840           } else {
841                     nfset = (from_sb.st_flags & ~(UF_NODUMP | UF_NOHISTORY)) |
842                               (to_sb.st_flags & UF_NOHISTORY);
843           }
844 
845           /*
846            * NFS does not support flags.  Ignore EOPNOTSUPP flags if we're just
847            * trying to turn off UF_NODUMP.  If we're trying to set real flags,
848            * then warn if the fs doesn't support it, otherwise fail.
849            */
850           if (!dounpriv && !devnull && fchflags(to_fd, nfset)) {
851                     if (flags & SETFLAGS) {
852                               if (errno == EOPNOTSUPP)
853                                         warn("%s: chflags", to_name);
854                               else {
855                                         serrno = errno;
856                                         (void)unlink(to_name);
857                                         errno = serrno;
858                                         err(EX_OSERR, "%s: chflags", to_name);
859                               }
860                     }
861           }
862 #endif
863 
864           (void)close(to_fd);
865           if (!devnull)
866                     (void)close(from_fd);
867 }
868 
869 /*
870  * compare --
871  *        compare two files; non-zero means files differ
872  */
873 static int
compare(int from_fd,const char * from_name __unused,size_t from_len,int to_fd,const char * to_name __unused,size_t to_len)874 compare(int from_fd, const char *from_name __unused, size_t from_len,
875           int to_fd, const char *to_name __unused, size_t to_len)
876 {
877           char *p, *q;
878           int rv;
879           int done_compare;
880 
881           rv = 0;
882           if (from_len != to_len)
883                     return 1;
884 
885           if (from_len <= MAX_CMP_SIZE) {
886                     done_compare = 0;
887                     if (trymmap(from_fd) && trymmap(to_fd)) {
888                               p = mmap(NULL, from_len, PROT_READ, MAP_SHARED,
889                                   from_fd, (off_t)0);
890                               if (p == (char *)MAP_FAILED)
891                                         goto out;
892                               q = mmap(NULL, from_len, PROT_READ, MAP_SHARED,
893                                   to_fd, (off_t)0);
894                               if (q == (char *)MAP_FAILED) {
895                                         munmap(p, from_len);
896                                         goto out;
897                               }
898 
899                               rv = memcmp(p, q, from_len);
900                               munmap(p, from_len);
901                               munmap(q, from_len);
902                               done_compare = 1;
903                     }
904           out:
905                     if (!done_compare) {
906                               char buf1[MAXBSIZE];
907                               char buf2[MAXBSIZE];
908                               int n1, n2;
909 
910                               rv = 0;
911                               lseek(from_fd, 0, SEEK_SET);
912                               lseek(to_fd, 0, SEEK_SET);
913                               while (rv == 0) {
914                                         n1 = read(from_fd, buf1, sizeof(buf1));
915                                         if (n1 == 0)
916                                                   break;              /* EOF */
917                                         else if (n1 > 0) {
918                                                   n2 = read(to_fd, buf2, n1);
919                                                   if (n2 == n1)
920                                                             rv = memcmp(buf1, buf2, n1);
921                                                   else
922                                                             rv = 1;   /* out of sync */
923                                         } else
924                                                   rv = 1;             /* read failure */
925                               }
926                               lseek(from_fd, 0, SEEK_SET);
927                               lseek(to_fd, 0, SEEK_SET);
928                     }
929           } else
930                     rv = 1;   /* don't bother in this case */
931 
932           return rv;
933 }
934 
935 /*
936  * create_tempfile --
937  *        create a temporary file based on path and open it
938  */
939 static int
create_tempfile(const char * path,char * temp,size_t tsize)940 create_tempfile(const char *path, char *temp, size_t tsize)
941 {
942           char *p;
943 
944           (void)strncpy(temp, path, tsize);
945           temp[tsize - 1] = '\0';
946           if ((p = strrchr(temp, '/')) != NULL)
947                     p++;
948           else
949                     p = temp;
950           (void)strncpy(p, "INS@XXXXXX", &temp[tsize - 1] - p);
951           temp[tsize - 1] = '\0';
952           return (mkstemp(temp));
953 }
954 
955 /*
956  * create_newfile --
957  *        create a new file, overwriting an existing one if necessary
958  */
959 static int
create_newfile(const char * path,int target,struct stat * sbp)960 create_newfile(const char *path, int target, struct stat *sbp)
961 {
962           char backup[MAXPATHLEN];
963 
964           if (target) {
965                     /*
966                      * Unlink now... avoid ETXTBSY errors later.  Try to turn
967                      * off the append/immutable bits -- if we fail, go ahead,
968                      * it might work.
969                      */
970 #ifdef _ST_FLAGS_PRESENT_
971                     if (sbp->st_flags & NOCHANGEBITS)
972                               (void)chflags(path, sbp->st_flags & ~NOCHANGEBITS);
973 #endif
974 
975                     if (dobackup) {
976                               if ((size_t)snprintf(backup, MAXPATHLEN, "%s%s",
977                                   path, suffix) != strlen(path) + strlen(suffix))
978                                         errx(EX_OSERR, "%s: backup filename too long",
979                                             path);
980                               (void)snprintf(backup, MAXPATHLEN, "%s%s",
981                                   path, suffix);
982                               if (verbose)
983                                         (void)printf("install: %s -> %s\n",
984                                             path, backup);
985                               if (rename(path, backup) < 0)
986                                         err(EX_OSERR, "rename: %s to %s", path, backup);
987                     } else
988                               unlink(path);
989           }
990 
991           return (open(path, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR));
992 }
993 
994 /*
995  * copy --
996  *        copy from one file to another
997  */
998 static void
copy(int from_fd,const char * from_name,int to_fd,const char * to_name,off_t size)999 copy(int from_fd, const char *from_name, int to_fd,
1000      const char *to_name, off_t size)
1001 {
1002           int nr, nw;
1003           int serrno;
1004           char *p;
1005           char buf[MAXBSIZE];
1006           int done_copy;
1007 
1008           /* Rewind file descriptors. */
1009           if (lseek(from_fd, (off_t)0, SEEK_SET) == (off_t)-1)
1010                     err(EX_OSERR, "lseek: %s", from_name);
1011           if (lseek(to_fd, (off_t)0, SEEK_SET) == (off_t)-1)
1012                     err(EX_OSERR, "lseek: %s", to_name);
1013 
1014           /*
1015            * Mmap and write if less than 8M (the limit is so we don't totally
1016            * trash memory on big files.  This is really a minor hack, but it
1017            * wins some CPU back.
1018            */
1019           done_copy = 0;
1020           if (size <= 8 * 1048576 && trymmap(from_fd) &&
1021               (p = mmap(NULL, (size_t)size, PROT_READ, MAP_SHARED,
1022                         from_fd, (off_t)0)) != (char *)MAP_FAILED) {
1023                     nw = write(to_fd, p, size);
1024                     if (nw != size) {
1025                               serrno = errno;
1026                               (void)unlink(to_name);
1027                               errno = nw > 0 ? EIO : serrno;
1028                               err(EX_OSERR, "%s", to_name);
1029                     }
1030                     done_copy = 1;
1031           }
1032           if (!done_copy) {
1033                     while ((nr = read(from_fd, buf, sizeof(buf))) > 0) {
1034                               if ((nw = write(to_fd, buf, nr)) != nr) {
1035                                         serrno = errno;
1036                                         (void)unlink(to_name);
1037                                         errno = nw > 0 ? EIO : serrno;
1038                                         err(EX_OSERR, "%s", to_name);
1039                               }
1040                     }
1041                     if (nr != 0) {
1042                               serrno = errno;
1043                               (void)unlink(to_name);
1044                               errno = serrno;
1045                               err(EX_OSERR, "%s", from_name);
1046                     }
1047           }
1048 }
1049 
1050 /*
1051  * strip --
1052  *        use strip(1) to strip the target file
1053  */
1054 static void
strip(const char * to_name)1055 strip(const char *to_name)
1056 {
1057           const char *stripbin;
1058           int serrno, status;
1059 
1060           switch (fork()) {
1061           case -1:
1062                     serrno = errno;
1063                     (void)unlink(to_name);
1064                     errno = serrno;
1065                     err(EX_TEMPFAIL, "fork");
1066           case 0:
1067                     stripbin = getenv("STRIPBIN");
1068                     if (stripbin == NULL)
1069                               stripbin = "strip";
1070                     execlp(stripbin, stripbin, to_name, NULL);
1071                     err(EX_OSERR, "exec(%s)", stripbin);
1072           default:
1073                     if (wait(&status) == -1 || status) {
1074                               serrno = errno;
1075                               (void)unlink(to_name);
1076                               errc(EX_SOFTWARE, serrno, "wait");
1077                               /* NOTREACHED */
1078                     }
1079           }
1080 }
1081 
1082 /*
1083  * When doing a concurrent make -j N multiple install's can race the mkdir.
1084  */
1085 static
1086 int
mkdir_race(const char * path,int nmode)1087 mkdir_race(const char *path, int nmode)
1088 {
1089           int res;
1090           struct stat sb;
1091 
1092           res = mkdir(path, nmode);
1093           if (res < 0 && errno == EEXIST) {
1094                     if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode))
1095                               return(0);
1096                     res = mkdir(path, nmode);
1097           }
1098           return (res);
1099 }
1100 
1101 /*
1102  * install_dir --
1103  *        build directory hierarchy
1104  */
1105 static void
install_dir(char * path)1106 install_dir(char *path)
1107 {
1108           char *p;
1109           struct stat sb;
1110           int ch;
1111 
1112           for (p = path;; ++p)
1113                     if (!*p || (p != path && *p  == '/')) {
1114                               ch = *p;
1115                               *p = '\0';
1116                               if (stat(path, &sb)) {
1117                                         if (errno != ENOENT ||
1118                                             mkdir_race(path, 0755) < 0) {
1119                                                   err(EX_OSERR, "mkdir %s", path);
1120                                                   /* NOTREACHED */
1121                                         } else if (verbose)
1122                                                   (void)printf("install: mkdir %s\n",
1123                                                                  path);
1124                               } else if (!S_ISDIR(sb.st_mode))
1125                                         errx(EX_OSERR, "%s exists but is not a directory", path);
1126                               if (!(*p = ch))
1127                                         break;
1128                     }
1129 
1130           if (!dounpriv) {
1131                     if ((gid != (gid_t)-1 || uid != (uid_t)-1) &&
1132                         chown(path, uid, gid))
1133                               warn("chown %u:%u %s", uid, gid, path);
1134                     /* XXXBED: should we do the chmod in the dounpriv case? */
1135                     if (chmod(path, mode))
1136                               warn("chmod %o %s", mode, path);
1137           }
1138 }
1139 
1140 /*
1141  * usage --
1142  *        print a usage message and die
1143  */
1144 static void
usage(void)1145 usage(void)
1146 {
1147           fprintf(stderr,
1148 "usage: install [-bCcpSsUv] [-f flags] [-g group] [-m mode] [-o owner]\n"
1149 "               [-D dest] [-h hash]\n"
1150 "               [-B suffix] [-l linkflags] [-N dbdir]\n"
1151 "               file1 file2\n"
1152 "       install [-bCcpSsUv] [-B suffix] [-D dest] [-f flags] [-g group]\n"
1153 "               [-N dbdir] [-m mode] [-o owner] file1 ... fileN directory\n"
1154 "       install -d [-lUv] [-D dest] [-g group] [-m mode] [-N dbdir] [-o owner]\n"
1155 "               directory ...\n");
1156           exit(EX_USAGE);
1157           /* NOTREACHED */
1158 }
1159 
1160 /*
1161  * trymmap --
1162  *        return true (1) if mmap should be tried, false (0) if not.
1163  */
1164 static int
trymmap(int fd)1165 trymmap(int fd)
1166 {
1167 /*
1168  * The ifdef is for bootstrapping - f_fstypename doesn't exist in
1169  * pre-Lite2-merge systems.
1170  */
1171 #ifdef MFSNAMELEN
1172           struct statfs stfs;
1173 
1174           if (nommap || fstatfs(fd, &stfs) != 0)
1175                     return (0);
1176           if (strcmp(stfs.f_fstypename, "ufs") == 0 ||
1177               strcmp(stfs.f_fstypename, "cd9660") == 0)
1178                     return (1);
1179 #endif
1180           return (0);
1181 }
1182