xinstall.c revision 1.107 1 /* $NetBSD: xinstall.c,v 1.107 2009/04/14 08:54:59 lukem Exp $ */
2
3 /*
4 * Copyright (c) 1987, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #if HAVE_NBTOOL_CONFIG_H
33 #include "nbtool_config.h"
34 #else
35 #define HAVE_FUTIMES 1
36 #define HAVE_STRUCT_STAT_ST_FLAGS 1
37 #endif
38
39 #include <sys/cdefs.h>
40 #if defined(__COPYRIGHT) && !defined(lint)
41 __COPYRIGHT("@(#) Copyright (c) 1987, 1993\
42 The Regents of the University of California. All rights reserved.");
43 #endif /* not lint */
44
45 #if defined(__RCSID) && !defined(lint)
46 #if 0
47 static char sccsid[] = "@(#)xinstall.c 8.1 (Berkeley) 7/21/93";
48 #else
49 __RCSID("$NetBSD: xinstall.c,v 1.107 2009/04/14 08:54:59 lukem Exp $");
50 #endif
51 #endif /* not lint */
52
53 #define __MKTEMP_OK__ /* All uses of mktemp have been checked */
54 #include <sys/param.h>
55 #include <sys/mman.h>
56 #include <sys/stat.h>
57 #include <sys/wait.h>
58 #include <sys/time.h>
59
60 #include <ctype.h>
61 #include <err.h>
62 #include <errno.h>
63 #include <fcntl.h>
64 #include <grp.h>
65 #include <libgen.h>
66 #include <paths.h>
67 #include <pwd.h>
68 #include <stdio.h>
69 #include <stdlib.h>
70 #include <string.h>
71 #include <unistd.h>
72 #include <util.h>
73 #include <vis.h>
74
75 #include <md5.h>
76 #include <rmd160.h>
77 #include <sha1.h>
78
79 #include "pathnames.h"
80 #include "mtree.h"
81
82 #define STRIP_ARGS_MAX 32
83 #define BACKUP_SUFFIX ".old"
84
85 int dobackup, dodir, dostrip, dolink, dopreserve, dorename, dounpriv;
86 int haveopt_f, haveopt_g, haveopt_m, haveopt_o;
87 int numberedbackup;
88 int mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
89 char pathbuf[MAXPATHLEN];
90 id_t uid = -1, gid = -1;
91 char *group, *owner, *fflags, *tags;
92 FILE *metafp;
93 char *metafile;
94 u_long fileflags;
95 char *stripArgs;
96 char *afterinstallcmd;
97 const char *suffix = BACKUP_SUFFIX;
98 char *destdir;
99
100 enum {
101 DIGEST_NONE = 0,
102 DIGEST_MD5,
103 DIGEST_RMD160,
104 DIGEST_SHA1,
105 } digesttype = DIGEST_NONE;
106 char *digest;
107
108 #define LN_ABSOLUTE 0x01
109 #define LN_RELATIVE 0x02
110 #define LN_HARD 0x04
111 #define LN_SYMBOLIC 0x08
112 #define LN_MIXED 0x10
113
114 #define DIRECTORY 0x01 /* Tell install it's a directory. */
115 #define SETFLAGS 0x02 /* Tell install to set flags. */
116 #define HASUID 0x04 /* Tell install the uid was given */
117 #define HASGID 0x08 /* Tell install the gid was given */
118
119 void afterinstall(const char *, const char *, int);
120 void backup(const char *);
121 char *copy(int, char *, int, char *, off_t);
122 int do_link(char *, char *);
123 void do_symlink(char *, char *);
124 void install(char *, char *, u_int);
125 void install_dir(char *, u_int);
126 int main(int, char *[]);
127 void makelink(char *, char *);
128 void metadata_log(const char *, const char *, struct timeval *,
129 const char *, const char *, off_t);
130 int parseid(char *, id_t *);
131 void strip(char *);
132 void usage(void);
133 char *xbasename(char *);
134 char *xdirname(char *);
135
136 int
137 main(int argc, char *argv[])
138 {
139 struct stat from_sb, to_sb;
140 void *set;
141 u_int iflags;
142 int ch, no_target;
143 char *p, *to_name;
144
145 setprogname(argv[0]);
146
147 iflags = 0;
148 while ((ch = getopt(argc, argv, "a:cbB:dD:f:g:h:l:m:M:N:o:prsS:T:U"))
149 != -1)
150 switch((char)ch) {
151 case 'a':
152 afterinstallcmd = strdup(optarg);
153 if (afterinstallcmd == NULL)
154 errx(1, "%s", strerror(ENOMEM));
155 break;
156 case 'B':
157 suffix = optarg;
158 numberedbackup = 0;
159 {
160 /* Check if given suffix really generates
161 different suffixes - catch e.g. ".%" */
162 char suffix_expanded0[FILENAME_MAX],
163 suffix_expanded1[FILENAME_MAX];
164 (void)snprintf(suffix_expanded0, FILENAME_MAX,
165 suffix, 0);
166 (void)snprintf(suffix_expanded1, FILENAME_MAX,
167 suffix, 1);
168 if (strcmp(suffix_expanded0, suffix_expanded1)
169 != 0)
170 numberedbackup = 1;
171 }
172 /* fall through; -B implies -b */
173 /*FALLTHROUGH*/
174 case 'b':
175 dobackup = 1;
176 break;
177 case 'c':
178 /* ignored; was "docopy" which is now the default. */
179 break;
180 case 'd':
181 dodir = 1;
182 break;
183 case 'D':
184 destdir = optarg;
185 break;
186 #if ! HAVE_NBTOOL_CONFIG_H
187 case 'f':
188 haveopt_f = 1;
189 fflags = optarg;
190 break;
191 #endif
192 case 'g':
193 haveopt_g = 1;
194 group = optarg;
195 break;
196 case 'h':
197 digest = optarg;
198 break;
199 case 'l':
200 for (p = optarg; *p; p++)
201 switch (*p) {
202 case 's':
203 dolink &= ~(LN_HARD|LN_MIXED);
204 dolink |= LN_SYMBOLIC;
205 break;
206 case 'h':
207 dolink &= ~(LN_SYMBOLIC|LN_MIXED);
208 dolink |= LN_HARD;
209 break;
210 case 'm':
211 dolink &= ~(LN_SYMBOLIC|LN_HARD);
212 dolink |= LN_MIXED;
213 break;
214 case 'a':
215 dolink &= ~LN_RELATIVE;
216 dolink |= LN_ABSOLUTE;
217 break;
218 case 'r':
219 dolink &= ~LN_ABSOLUTE;
220 dolink |= LN_RELATIVE;
221 break;
222 default:
223 errx(1, "%c: invalid link type", *p);
224 /* NOTREACHED */
225 }
226 break;
227 case 'm':
228 haveopt_m = 1;
229 if (!(set = setmode(optarg)))
230 err(1, "Cannot set file mode `%s'", optarg);
231 mode = getmode(set, 0);
232 free(set);
233 break;
234 case 'M':
235 metafile = optarg;
236 break;
237 case 'N':
238 if (! setup_getid(optarg))
239 errx(1,
240 "Unable to use user and group databases in `%s'",
241 optarg);
242 break;
243 case 'o':
244 haveopt_o = 1;
245 owner = optarg;
246 break;
247 case 'p':
248 dopreserve = 1;
249 break;
250 case 'r':
251 dorename = 1;
252 break;
253 case 'S':
254 stripArgs = strdup(optarg);
255 if (stripArgs == NULL)
256 errx(1, "%s", strerror(ENOMEM));
257 /* fall through; -S implies -s */
258 /*FALLTHROUGH*/
259 case 's':
260 dostrip = 1;
261 break;
262 case 'T':
263 tags = optarg;
264 break;
265 case 'U':
266 dounpriv = 1;
267 break;
268 case '?':
269 default:
270 usage();
271 }
272 argc -= optind;
273 argv += optind;
274
275 /* strip and link options make no sense when creating directories */
276 if ((dostrip || dolink) && dodir)
277 usage();
278
279 /* strip and flags make no sense with links */
280 if ((dostrip || fflags) && dolink)
281 usage();
282
283 /* must have at least two arguments, except when creating directories */
284 if (argc < 2 && !dodir)
285 usage();
286
287 if (digest) {
288 if (0) {
289 } else if (strcmp(digest, "none") == 0) {
290 digesttype = DIGEST_NONE;
291 } else if (strcmp(digest, "md5") == 0) {
292 digesttype = DIGEST_MD5;
293 } else if (strcmp(digest, "rmd160") == 0) {
294 digesttype = DIGEST_RMD160;
295 } else if (strcmp(digest, "sha1") == 0) {
296 digesttype = DIGEST_SHA1;
297 } else {
298 warnx("unknown digest `%s'", digest);
299 usage();
300 }
301 }
302
303 /* get group and owner id's */
304 if (group && !dounpriv) {
305 if (gid_from_group(group, &gid) == -1 && ! parseid(group, &gid))
306 errx(1, "unknown group %s", group);
307 iflags |= HASGID;
308 }
309 if (owner && !dounpriv) {
310 if (uid_from_user(owner, &uid) == -1 && ! parseid(owner, &uid))
311 errx(1, "unknown user %s", owner);
312 iflags |= HASUID;
313 }
314
315 #if ! HAVE_NBTOOL_CONFIG_H
316 if (fflags && !dounpriv) {
317 if (string_to_flags(&fflags, &fileflags, NULL))
318 errx(1, "%s: invalid flag", fflags);
319 /* restore fflags since string_to_flags() changed it */
320 fflags = flags_to_string(fileflags, "-");
321 iflags |= SETFLAGS;
322 }
323 #endif
324
325 if (metafile) {
326 if ((metafp = fopen(metafile, "a")) == NULL)
327 warn("open %s", metafile);
328 } else
329 digesttype = DIGEST_NONE;
330
331 if (dodir) {
332 for (; *argv != NULL; ++argv)
333 install_dir(*argv, iflags);
334 exit (0);
335 }
336
337 no_target = stat(to_name = argv[argc - 1], &to_sb);
338 if (!no_target && S_ISDIR(to_sb.st_mode)) {
339 for (; *argv != to_name; ++argv)
340 install(*argv, to_name, iflags | DIRECTORY);
341 exit(0);
342 }
343
344 /* can't do file1 file2 directory/file */
345 if (argc != 2) {
346 errx(EXIT_FAILURE, "the last argument (%s) "
347 "must name an existing directory", argv[argc - 1]);
348 /* NOTREACHED */
349 }
350
351 if (!no_target) {
352 /* makelink() handles checks for links */
353 if (!dolink) {
354 if (stat(*argv, &from_sb))
355 err(1, "%s: stat", *argv);
356 if (!S_ISREG(to_sb.st_mode))
357 errx(1, "%s: not a regular file", to_name);
358 if (to_sb.st_dev == from_sb.st_dev &&
359 to_sb.st_ino == from_sb.st_ino)
360 errx(1, "%s and %s are the same file", *argv,
361 to_name);
362 }
363 /*
364 * Unlink now... avoid ETXTBSY errors later. Try and turn
365 * off the append/immutable bits -- if we fail, go ahead,
366 * it might work.
367 */
368 #if ! HAVE_NBTOOL_CONFIG_H
369 #define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)
370 if (to_sb.st_flags & NOCHANGEBITS)
371 (void)chflags(to_name,
372 to_sb.st_flags & ~(NOCHANGEBITS));
373 #endif
374 if (dobackup)
375 backup(to_name);
376 else if (!dorename)
377 (void)unlink(to_name);
378 }
379 install(*argv, to_name, iflags);
380 exit(0);
381 }
382
383 /*
384 * parseid --
385 * parse uid or gid from arg into id, returning non-zero if successful
386 */
387 int
388 parseid(char *name, id_t *id)
389 {
390 char *ep;
391
392 errno = 0;
393 *id = (id_t)strtoul(name, &ep, 10);
394 if (errno || *ep != '\0')
395 return (0);
396 return (1);
397 }
398
399 /*
400 * do_link --
401 * make a hard link, obeying dorename if set
402 * return -1 on failure
403 */
404 int
405 do_link(char *from_name, char *to_name)
406 {
407 char tmpl[MAXPATHLEN];
408 int ret;
409
410 if (dorename) {
411 (void)snprintf(tmpl, sizeof(tmpl), "%s/inst.XXXXXX",
412 xdirname(to_name));
413 /* This usage is safe. */
414 if (mktemp(tmpl) == NULL)
415 err(1, "%s: mktemp", tmpl);
416 ret = link(from_name, tmpl);
417 if (ret == 0) {
418 ret = rename(tmpl, to_name);
419 /* If rename has posix semantics, then the temporary
420 * file may still exist when from_name and to_name point
421 * to the same file, so unlink it unconditionally.
422 */
423 (void)unlink(tmpl);
424 }
425 return (ret);
426 } else
427 return (link(from_name, to_name));
428 }
429
430 /*
431 * do_symlink --
432 * make a symbolic link, obeying dorename if set
433 * exit on failure
434 */
435 void
436 do_symlink(char *from_name, char *to_name)
437 {
438 char tmpl[MAXPATHLEN];
439
440 if (dorename) {
441 (void)snprintf(tmpl, sizeof(tmpl), "%s/inst.XXXXXX",
442 xdirname(to_name));
443 /* This usage is safe. */
444 if (mktemp(tmpl) == NULL)
445 err(1, "%s: mktemp", tmpl);
446
447 if (symlink(from_name, tmpl) == -1)
448 err(1, "symlink %s -> %s", from_name, tmpl);
449 if (rename(tmpl, to_name) == -1) {
450 /* remove temporary link before exiting */
451 (void)unlink(tmpl);
452 err(1, "%s: rename", to_name);
453 }
454 } else {
455 if (symlink(from_name, to_name) == -1)
456 err(1, "symlink %s -> %s", from_name, to_name);
457 }
458 }
459
460 /*
461 * makelink --
462 * make a link from source to destination
463 */
464 void
465 makelink(char *from_name, char *to_name)
466 {
467 char src[MAXPATHLEN], dst[MAXPATHLEN], lnk[MAXPATHLEN];
468 struct stat to_sb;
469
470 /* Try hard links first */
471 if (dolink & (LN_HARD|LN_MIXED)) {
472 if (do_link(from_name, to_name) == -1) {
473 if ((dolink & LN_HARD) || errno != EXDEV)
474 err(1, "link %s -> %s", from_name, to_name);
475 } else {
476 if (stat(to_name, &to_sb))
477 err(1, "%s: stat", to_name);
478 if (S_ISREG(to_sb.st_mode)) {
479 /* XXX: hard links to anything
480 * other than plain files are not
481 * metalogged
482 */
483 int omode;
484 char *oowner, *ogroup, *offlags;
485 char *dres;
486
487 /* XXX: use underlying perms,
488 * unless overridden on command line.
489 */
490 omode = mode;
491 if (!haveopt_m)
492 mode = (to_sb.st_mode & 0777);
493 oowner = owner;
494 if (!haveopt_o)
495 owner = NULL;
496 ogroup = group;
497 if (!haveopt_g)
498 group = NULL;
499 offlags = fflags;
500 if (!haveopt_f)
501 fflags = NULL;
502 switch (digesttype) {
503 case DIGEST_MD5:
504 dres = MD5File(from_name, NULL);
505 break;
506 case DIGEST_RMD160:
507 dres = RMD160File(from_name, NULL);
508 break;
509 case DIGEST_SHA1:
510 dres = SHA1File(from_name, NULL);
511 break;
512 default:
513 dres = NULL;
514 }
515 metadata_log(to_name, "file", NULL, NULL,
516 dres, to_sb.st_size);
517 free(dres);
518 mode = omode;
519 owner = oowner;
520 group = ogroup;
521 fflags = offlags;
522 }
523 return;
524 }
525 }
526
527 /* Symbolic links */
528 if (dolink & LN_ABSOLUTE) {
529 /* Convert source path to absolute */
530 if (realpath(from_name, src) == NULL)
531 err(1, "%s: realpath", from_name);
532 do_symlink(src, to_name);
533 /* XXX: src may point outside of destdir */
534 metadata_log(to_name, "link", NULL, src, NULL, 0);
535 return;
536 }
537
538 if (dolink & LN_RELATIVE) {
539 char *cp, *d, *s;
540
541 /* Resolve pathnames */
542 if (realpath(from_name, src) == NULL)
543 err(1, "%s: realpath", from_name);
544
545 /*
546 * The last component of to_name may be a symlink,
547 * so use realpath to resolve only the directory.
548 */
549 cp = xdirname(to_name);
550 if (realpath(cp, dst) == NULL)
551 err(1, "%s: realpath", cp);
552 /* .. and add the last component */
553 if (strcmp(dst, "/") != 0) {
554 if (strlcat(dst, "/", sizeof(dst)) > sizeof(dst))
555 errx(1, "resolved pathname too long");
556 }
557 cp = xbasename(to_name);
558 if (strlcat(dst, cp, sizeof(dst)) > sizeof(dst))
559 errx(1, "resolved pathname too long");
560
561 /* trim common path components */
562 for (s = src, d = dst; *s == *d; s++, d++)
563 continue;
564 while (*s != '/')
565 s--, d--;
566
567 /* count the number of directories we need to backtrack */
568 for (++d, lnk[0] = '\0'; *d; d++)
569 if (*d == '/')
570 (void)strlcat(lnk, "../", sizeof(lnk));
571
572 (void)strlcat(lnk, ++s, sizeof(lnk));
573
574 do_symlink(lnk, to_name);
575 /* XXX: lnk may point outside of destdir */
576 metadata_log(to_name, "link", NULL, lnk, NULL, 0);
577 return;
578 }
579
580 /*
581 * If absolute or relative was not specified,
582 * try the names the user provided
583 */
584 do_symlink(from_name, to_name);
585 /* XXX: from_name may point outside of destdir */
586 metadata_log(to_name, "link", NULL, from_name, NULL, 0);
587 }
588
589 /*
590 * install --
591 * build a path name and install the file
592 */
593 void
594 install(char *from_name, char *to_name, u_int flags)
595 {
596 struct stat from_sb;
597 #if ! HAVE_NBTOOL_CONFIG_H
598 struct stat to_sb;
599 #endif
600 struct timeval tv[2];
601 int devnull, from_fd, to_fd, serrno, tmpmode;
602 char *p, tmpl[MAXPATHLEN], *oto_name, *digestresult;
603
604 if (!dolink) {
605 /* ensure that from_sb & tv are sane if !dolink */
606 if (stat(from_name, &from_sb))
607 err(1, "%s: stat", from_name);
608 #if BSD4_4 && !HAVE_NBTOOL_CONFIG_H
609 TIMESPEC_TO_TIMEVAL(&tv[0], &from_sb.st_atimespec);
610 TIMESPEC_TO_TIMEVAL(&tv[1], &from_sb.st_mtimespec);
611 #else
612 tv[0].tv_sec = from_sb.st_atime;
613 tv[0].tv_usec = 0;
614 tv[1].tv_sec = from_sb.st_mtime;
615 tv[1].tv_usec = 0;
616 #endif
617 }
618
619 if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) {
620 if (!dolink) {
621 if (!S_ISREG(from_sb.st_mode))
622 errx(1, "%s: not a regular file", from_name);
623 }
624 /* Build the target path. */
625 if (flags & DIRECTORY) {
626 (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
627 to_name,
628 (p = strrchr(from_name, '/')) ? ++p : from_name);
629 to_name = pathbuf;
630 }
631 devnull = 0;
632 } else {
633 #if HAVE_STRUCT_STAT_ST_FLAGS
634 from_sb.st_flags = 0; /* XXX */
635 #endif
636 devnull = 1;
637 }
638
639 /*
640 * Unlink now... avoid ETXTBSY errors later. Try and turn
641 * off the append/immutable bits -- if we fail, go ahead,
642 * it might work.
643 */
644 #if ! HAVE_NBTOOL_CONFIG_H
645 if (stat(to_name, &to_sb) == 0 &&
646 to_sb.st_flags & (NOCHANGEBITS))
647 (void)chflags(to_name, to_sb.st_flags & ~(NOCHANGEBITS));
648 #endif
649 if (dorename) {
650 (void)snprintf(tmpl, sizeof(tmpl), "%s/inst.XXXXXX",
651 xdirname(to_name));
652 oto_name = to_name;
653 to_name = tmpl;
654 } else {
655 oto_name = NULL; /* pacify gcc */
656 if (dobackup)
657 backup(to_name);
658 else
659 (void)unlink(to_name);
660 }
661
662 if (dolink) {
663 makelink(from_name, dorename ? oto_name : to_name);
664 return;
665 }
666
667 /* Create target. */
668 if (dorename) {
669 if ((to_fd = mkstemp(to_name)) == -1)
670 err(1, "%s: mkstemp", to_name);
671 } else {
672 if ((to_fd = open(to_name,
673 O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)) < 0)
674 err(1, "%s: open", to_name);
675 }
676 digestresult = NULL;
677 if (!devnull) {
678 if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) {
679 (void)unlink(to_name);
680 err(1, "%s: open", from_name);
681 }
682 digestresult =
683 copy(from_fd, from_name, to_fd, to_name, from_sb.st_size);
684 (void)close(from_fd);
685 }
686
687 if (dostrip) {
688 strip(to_name);
689
690 /*
691 * Re-open our fd on the target, in case we used a strip
692 * that does not work in-place -- like gnu binutils strip.
693 */
694 close(to_fd);
695 if ((to_fd = open(to_name, O_RDONLY, S_IRUSR | S_IWUSR)) < 0)
696 err(1, "stripping %s", to_name);
697 }
698
699 if (afterinstallcmd != NULL) {
700 afterinstall(afterinstallcmd, to_name, 1);
701
702 /*
703 * Re-open our fd on the target, in case we used an
704 * after-install command that does not work in-place
705 */
706 close(to_fd);
707 if ((to_fd = open(to_name, O_RDONLY, S_IRUSR | S_IWUSR)) < 0)
708 err(1, "running after install command on %s", to_name);
709 }
710
711 /*
712 * Set owner, group, mode for target; do the chown first,
713 * chown may lose the setuid bits.
714 */
715 if (!dounpriv &&
716 (flags & (HASUID | HASGID)) && fchown(to_fd, uid, gid) == -1) {
717 serrno = errno;
718 (void)unlink(to_name);
719 errx(1, "%s: chown/chgrp: %s", to_name, strerror(serrno));
720 }
721 tmpmode = mode;
722 if (dounpriv)
723 tmpmode &= S_IRWXU|S_IRWXG|S_IRWXO;
724 if (fchmod(to_fd, tmpmode) == -1) {
725 serrno = errno;
726 (void)unlink(to_name);
727 errx(1, "%s: chmod: %s", to_name, strerror(serrno));
728 }
729
730 /*
731 * Preserve the date of the source file.
732 */
733 if (dopreserve) {
734 #if HAVE_FUTIMES
735 if (futimes(to_fd, tv) == -1)
736 warn("%s: futimes", to_name);
737 #else
738 if (utimes(to_name, tv) == -1)
739 warn("%s: utimes", to_name);
740 #endif
741 }
742
743 (void)close(to_fd);
744
745 if (dorename) {
746 if (rename(to_name, oto_name) == -1)
747 err(1, "%s: rename", to_name);
748 to_name = oto_name;
749 }
750
751 /*
752 * If provided a set of flags, set them, otherwise, preserve the
753 * flags, except for the dump flag.
754 */
755 #if ! HAVE_NBTOOL_CONFIG_H
756 if (!dounpriv && chflags(to_name,
757 flags & SETFLAGS ? fileflags : from_sb.st_flags & ~UF_NODUMP) == -1)
758 {
759 if (errno != EOPNOTSUPP || (from_sb.st_flags & ~UF_NODUMP) != 0)
760 warn("%s: chflags", to_name);
761 }
762 #endif
763
764 metadata_log(to_name, "file", tv, NULL, digestresult,
765 (devnull ? 0 : from_sb.st_size));
766 free(digestresult);
767 }
768
769 /*
770 * copy --
771 * copy from one file to another
772 */
773 char *
774 copy(int from_fd, char *from_name, int to_fd, char *to_name, off_t size)
775 {
776 ssize_t nr, nw;
777 int serrno;
778 u_char *p;
779 u_char buf[MAXBSIZE];
780 MD5_CTX ctxMD5;
781 RMD160_CTX ctxRMD160;
782 SHA1_CTX ctxSHA1;
783
784 switch (digesttype) {
785 case DIGEST_MD5:
786 MD5Init(&ctxMD5);
787 break;
788 case DIGEST_RMD160:
789 RMD160Init(&ctxRMD160);
790 break;
791 case DIGEST_SHA1:
792 SHA1Init(&ctxSHA1);
793 break;
794 case DIGEST_NONE:
795 default:
796 break;
797 }
798 /*
799 * There's no reason to do anything other than close the file
800 * now if it's empty, so let's not bother.
801 */
802 if (size > 0) {
803
804 /*
805 * Mmap and write if less than 8M (the limit is so we
806 * don't totally trash memory on big files). This is
807 * really a minor hack, but it wins some CPU back.
808 */
809
810 if (size <= 8 * 1048576) {
811 if ((p = mmap(NULL, (size_t)size, PROT_READ,
812 MAP_FILE|MAP_SHARED, from_fd, (off_t)0))
813 == MAP_FAILED) {
814 goto mmap_failed;
815 }
816 #if defined(MADV_SEQUENTIAL) && !defined(__APPLE__)
817 if (madvise(p, (size_t)size, MADV_SEQUENTIAL) == -1
818 && errno != EOPNOTSUPP)
819 warnx("madvise: %s", strerror(errno));
820 #endif
821
822 if (write(to_fd, p, size) != size) {
823 serrno = errno;
824 (void)unlink(to_name);
825 errx(1, "%s: write: %s",
826 to_name, strerror(serrno));
827 }
828 switch (digesttype) {
829 case DIGEST_MD5:
830 MD5Update(&ctxMD5, p, size);
831 break;
832 case DIGEST_RMD160:
833 RMD160Update(&ctxRMD160, p, size);
834 break;
835 case DIGEST_SHA1:
836 SHA1Update(&ctxSHA1, p, size);
837 break;
838 default:
839 break;
840 }
841 (void)munmap(p, size);
842 } else {
843 mmap_failed:
844 while ((nr = read(from_fd, buf, sizeof(buf))) > 0) {
845 if ((nw = write(to_fd, buf, nr)) != nr) {
846 serrno = errno;
847 (void)unlink(to_name);
848 errx(1, "%s: write: %s", to_name,
849 strerror(nw > 0 ? EIO : serrno));
850 }
851 switch (digesttype) {
852 case DIGEST_MD5:
853 MD5Update(&ctxMD5, buf, nr);
854 break;
855 case DIGEST_RMD160:
856 RMD160Update(&ctxRMD160, buf, nr);
857 break;
858 case DIGEST_SHA1:
859 SHA1Update(&ctxSHA1, buf, nr);
860 break;
861 default:
862 break;
863 }
864 }
865 if (nr != 0) {
866 serrno = errno;
867 (void)unlink(to_name);
868 errx(1, "%s: read: %s", from_name, strerror(serrno));
869 }
870 }
871 }
872 switch (digesttype) {
873 case DIGEST_MD5:
874 return MD5End(&ctxMD5, NULL);
875 case DIGEST_RMD160:
876 return RMD160End(&ctxRMD160, NULL);
877 case DIGEST_SHA1:
878 return SHA1End(&ctxSHA1, NULL);
879 default:
880 return NULL;
881 }
882 }
883
884 /*
885 * strip --
886 * use strip(1) to strip the target file
887 */
888 void
889 strip(char *to_name)
890 {
891 static const char exec_failure[] = ": exec of strip failed: ";
892 int serrno, status;
893 const char * volatile stripprog, *progname;
894 char *cmd;
895
896 if ((stripprog = getenv("STRIP")) == NULL) {
897 #ifdef TARGET_STRIP
898 stripprog = TARGET_STRIP;
899 #else
900 stripprog = _PATH_STRIP;
901 #endif
902 }
903
904 cmd = NULL;
905
906 if (stripArgs) {
907 /*
908 * Build up a command line and let /bin/sh
909 * parse the arguments.
910 */
911 int ret = asprintf(&cmd, "%s %s %s", stripprog, stripArgs,
912 to_name);
913
914 if (ret == -1 || cmd == NULL)
915 err(1, "asprintf failed");
916 }
917
918 switch (vfork()) {
919 case -1:
920 serrno = errno;
921 (void)unlink(to_name);
922 errx(1, "vfork: %s", strerror(serrno));
923 /*NOTREACHED*/
924 case 0:
925
926 if (stripArgs)
927 execl(_PATH_BSHELL, "sh", "-c", cmd, NULL);
928 else
929 execlp(stripprog, "strip", to_name, NULL);
930
931 progname = getprogname();
932 write(STDERR_FILENO, progname, strlen(progname));
933 write(STDERR_FILENO, exec_failure, strlen(exec_failure));
934 write(STDERR_FILENO, stripprog, strlen(stripprog));
935 write(STDERR_FILENO, "\n", 1);
936 _exit(1);
937 /*NOTREACHED*/
938 default:
939 if (wait(&status) == -1 || status)
940 (void)unlink(to_name);
941 }
942
943 free(cmd);
944 }
945
946 /*
947 * afterinstall --
948 * run provided command on the target file or directory after it's been
949 * installed and stripped, but before permissions are set or it's renamed
950 */
951 void
952 afterinstall(const char *command, const char *to_name, int errunlink)
953 {
954 int serrno, status;
955 char *cmd;
956
957 switch (vfork()) {
958 case -1:
959 serrno = errno;
960 if (errunlink)
961 (void)unlink(to_name);
962 errx(1, "vfork: %s", strerror(serrno));
963 /*NOTREACHED*/
964 case 0:
965 /*
966 * build up a command line and let /bin/sh
967 * parse the arguments
968 */
969 cmd = (char*)malloc(sizeof(char)*
970 (2+strlen(command)+
971 strlen(to_name)));
972
973 if (cmd == NULL)
974 errx(1, "%s", strerror(ENOMEM));
975
976 sprintf(cmd, "%s %s", command, to_name);
977
978 execl(_PATH_BSHELL, "sh", "-c", cmd, NULL);
979
980 warn("%s: exec of after install command", command);
981 _exit(1);
982 /*NOTREACHED*/
983 default:
984 if ((wait(&status) == -1 || status) && errunlink)
985 (void)unlink(to_name);
986 }
987 }
988
989 /*
990 * backup --
991 * backup file "to_name" to to_name.suffix
992 * if suffix contains a "%", it's taken as a printf(3) pattern
993 * used for a numbered backup.
994 */
995 void
996 backup(const char *to_name)
997 {
998 char bname[FILENAME_MAX];
999
1000 if (numberedbackup) {
1001 /* Do numbered backup */
1002 int cnt;
1003 char suffix_expanded[FILENAME_MAX];
1004
1005 cnt=0;
1006 do {
1007 (void)snprintf(suffix_expanded, FILENAME_MAX, suffix,
1008 cnt);
1009 (void)snprintf(bname, FILENAME_MAX, "%s%s", to_name,
1010 suffix_expanded);
1011 cnt++;
1012 } while (access(bname, F_OK) == 0);
1013 } else {
1014 /* Do simple backup */
1015 (void)snprintf(bname, FILENAME_MAX, "%s%s", to_name, suffix);
1016 }
1017
1018 (void)rename(to_name, bname);
1019 }
1020
1021 /*
1022 * install_dir --
1023 * build directory hierarchy
1024 */
1025 void
1026 install_dir(char *path, u_int flags)
1027 {
1028 char *p;
1029 struct stat sb;
1030 int ch;
1031
1032 for (p = path;; ++p)
1033 if (!*p || (p != path && *p == '/')) {
1034 ch = *p;
1035 *p = '\0';
1036 if (stat(path, &sb)) {
1037 if (errno != ENOENT || mkdir(path, 0777) < 0) {
1038 err(1, "%s: mkdir", path);
1039 }
1040 }
1041 else if (!S_ISDIR(sb.st_mode)) {
1042 errx(1, "%s exists but is not a directory", path);
1043 }
1044 if (!(*p = ch))
1045 break;
1046 }
1047
1048 if (afterinstallcmd != NULL)
1049 afterinstall(afterinstallcmd, path, 0);
1050
1051 if (!dounpriv && (
1052 ((flags & (HASUID | HASGID)) && chown(path, uid, gid) == -1)
1053 || chmod(path, mode) == -1 )) {
1054 warn("%s: chown/chmod", path);
1055 }
1056 metadata_log(path, "dir", NULL, NULL, NULL, 0);
1057 }
1058
1059 /*
1060 * metadata_log --
1061 * if metafp is not NULL, output mtree(8) full path name and settings to
1062 * metafp, to allow permissions to be set correctly by other tools,
1063 * or to allow integrity checks to be performed.
1064 */
1065 void
1066 metadata_log(const char *path, const char *type, struct timeval *tv,
1067 const char *slink, const char *digestresult, off_t size)
1068 {
1069 static const char extra[] = { ' ', '\t', '\n', '\\', '#', '\0' };
1070 const char *p;
1071 char *buf;
1072 size_t destlen;
1073 struct flock metalog_lock;
1074
1075 if (!metafp)
1076 return;
1077 buf = (char *)malloc(4 * strlen(path) + 1); /* buf for strsvis(3) */
1078 if (buf == NULL) {
1079 warnx("%s", strerror(ENOMEM));
1080 return;
1081 }
1082 /* lock log file */
1083 metalog_lock.l_start = 0;
1084 metalog_lock.l_len = 0;
1085 metalog_lock.l_whence = SEEK_SET;
1086 metalog_lock.l_type = F_WRLCK;
1087 if (fcntl(fileno(metafp), F_SETLKW, &metalog_lock) == -1) {
1088 warn("can't lock %s", metafile);
1089 free(buf);
1090 return;
1091 }
1092
1093 p = path; /* remove destdir */
1094 if (destdir) {
1095 destlen = strlen(destdir);
1096 if (strncmp(p, destdir, destlen) == 0 &&
1097 (p[destlen] == '/' || p[destlen] == '\0'))
1098 p += destlen;
1099 }
1100 while (*p && *p == '/') /* remove leading /s */
1101 p++;
1102 strsvis(buf, p, VIS_CSTYLE, extra); /* encode name */
1103 p = buf;
1104 /* print details */
1105 fprintf(metafp, ".%s%s type=%s", *p ? "/" : "", p, type);
1106 if (owner)
1107 fprintf(metafp, " uname=%s", owner);
1108 if (group)
1109 fprintf(metafp, " gname=%s", group);
1110 fprintf(metafp, " mode=%#o", mode);
1111 if (slink) {
1112 strsvis(buf, slink, VIS_CSTYLE, extra); /* encode link */
1113 fprintf(metafp, " link=%s", buf);
1114 }
1115 if (*type == 'f') /* type=file */
1116 fprintf(metafp, " size=%lld", (long long)size);
1117 if (tv != NULL && dopreserve)
1118 fprintf(metafp, " time=%lld.%ld",
1119 (long long)tv[1].tv_sec, (long)tv[1].tv_usec);
1120 if (digestresult && digest)
1121 fprintf(metafp, " %s=%s", digest, digestresult);
1122 if (fflags)
1123 fprintf(metafp, " flags=%s", fflags);
1124 if (tags)
1125 fprintf(metafp, " tags=%s", tags);
1126 fputc('\n', metafp);
1127 fflush(metafp); /* flush output */
1128 /* unlock log file */
1129 metalog_lock.l_type = F_UNLCK;
1130 if (fcntl(fileno(metafp), F_SETLKW, &metalog_lock) == -1) {
1131 warn("can't unlock %s", metafile);
1132 }
1133 free(buf);
1134 }
1135
1136 /*
1137 * xbasename --
1138 * libc basename(3) that returns a pointer to a static buffer
1139 * instead of overwriting that passed-in string.
1140 */
1141 char *
1142 xbasename(char *path)
1143 {
1144 static char tmp[MAXPATHLEN];
1145
1146 (void)strlcpy(tmp, path, sizeof(tmp));
1147 return (basename(tmp));
1148 }
1149
1150 /*
1151 * xdirname --
1152 * libc dirname(3) that returns a pointer to a static buffer
1153 * instead of overwriting that passed-in string.
1154 */
1155 char *
1156 xdirname(char *path)
1157 {
1158 static char tmp[MAXPATHLEN];
1159
1160 (void)strlcpy(tmp, path, sizeof(tmp));
1161 return (dirname(tmp));
1162 }
1163
1164 /*
1165 * usage --
1166 * print a usage message and die
1167 */
1168 void
1169 usage(void)
1170 {
1171 const char *prog;
1172
1173 prog = getprogname();
1174
1175 (void)fprintf(stderr,
1176 "usage: %s [-Ubcprs] [-M log] [-D dest] [-T tags] [-B suffix]\n"
1177 " [-a aftercmd] [-f flags] [-m mode] [-N dbdir] [-o owner] [-g group] \n"
1178 " [-l linkflags] [-h hash] [-S stripflags] file1 file2\n"
1179 " %s [-Ubcprs] [-M log] [-D dest] [-T tags] [-B suffix]\n"
1180 " [-a aftercmd] [-f flags] [-m mode] [-N dbdir] [-o owner] [-g group]\n"
1181 " [-l linkflags] [-h hash] [-S stripflags] file1 ... fileN directory\n"
1182 " %s -d [-Up] [-M log] [-D dest] [-T tags] [-a aftercmd] [-m mode]\n"
1183 " [-N dbdir] [-o owner] [-g group] directory ...\n",
1184 prog, prog, prog);
1185 exit(1);
1186 }
1187