source: trunk/src/kmk/kmkbuiltin/install.c@ 3062

Last change on this file since 3062 was 3062, checked in by bird, 8 years ago

netbsd patches from Uwe. Messing around on arm (arm -> arm32, adding arm64); Adding GNU/kFreeBSD and GNU/kNetBSD to the OS list. Added SuperH to archs. Recognize ppc64le as ppc64 in env.sh. Misc.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.5 KB
Line 
1/*
2 * Copyright (c) 1987, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static const char copyright[] =
36"@(#) Copyright (c) 1987, 1993\n\
37 The Regents of the University of California. All rights reserved.\n";
38#endif /* not lint */
39
40#if 0
41#ifndef lint
42static char sccsid[] = "@(#)xinstall.c 8.1 (Berkeley) 7/21/93";
43#endif /* not lint */
44
45#include <sys/cdefs.h>
46__FBSDID("$FreeBSD: src/usr.bin/xinstall/xinstall.c,v 1.66 2005/01/25 14:34:57 ssouhlal Exp $");
47#endif
48
49#include "config.h"
50#ifndef _MSC_VER
51# include <sys/param.h>
52# ifdef USE_MMAP
53# include <sys/mman.h>
54# endif
55# ifndef __HAIKU__
56# include <sys/mount.h>
57# endif
58# include <sys/wait.h>
59# include <sys/time.h>
60#endif /* !_MSC_VER */
61#include <sys/stat.h>
62
63#include <ctype.h>
64#include "err.h"
65#include <errno.h>
66#include <fcntl.h>
67#include <grp.h>
68#include <paths.h>
69#include <pwd.h>
70#include <stdio.h>
71#include <stdlib.h>
72#include <string.h>
73#ifndef __HAIKU__
74# include <sysexits.h>
75#endif
76#ifdef __NetBSD__
77# include <util.h>
78# define strtofflags(a, b, c) string_to_flags(a, b, c)
79#endif
80#include <unistd.h>
81#if defined(__EMX__) || defined(_MSC_VER)
82# include <process.h>
83#endif
84#include "getopt.h"
85#ifdef __sun__
86# include "solfakes.h"
87#endif
88#ifdef _MSC_VER
89# include "mscfakes.h"
90#endif
91#ifdef __HAIKU__
92# include "haikufakes.h"
93#endif
94#include "kmkbuiltin.h"
95
96
97extern void * bsd_setmode(const char *p);
98extern mode_t bsd_getmode(const void *bbox, mode_t omode);
99
100#ifndef __unused
101# define __unused
102#endif
103
104#ifndef MAXBSIZE
105# define MAXBSIZE 0x20000
106#endif
107
108/* Bootstrap aid - this doesn't exist in most older releases */
109#ifndef MAP_FAILED
110#define MAP_FAILED ((void *)-1) /* from <sys/mman.h> */
111#endif
112
113#define MAX_CMP_SIZE (16 * 1024 * 1024)
114
115#define DIRECTORY 0x01 /* Tell install it's a directory. */
116#define SETFLAGS 0x02 /* Tell install to set flags. */
117#define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)
118#define BACKUP_SUFFIX ".old"
119
120#ifndef O_BINARY
121# define O_BINARY 0
122#endif
123
124#ifndef EFTYPE
125# define EFTYPE EINVAL
126#endif
127
128#if defined(__WIN32__) || defined(__WIN64__) || defined(__OS2__)
129# define IS_SLASH(ch) ((ch) == '/' || (ch) == '\\')
130#else
131# define IS_SLASH(ch) ((ch) == '/')
132#endif
133
134static gid_t gid;
135static uid_t uid;
136static int dobackup, docompare, dodir, dopreserve, dostrip, nommap, safecopy, verbose, mode_given;
137static mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
138static const char *suffix = BACKUP_SUFFIX;
139static int ignore_perm_errors;
140static int hard_link_files_when_possible;
141
142static struct option long_options[] =
143{
144 { "help", no_argument, 0, 261 },
145 { "version", no_argument, 0, 262 },
146 { "ignore-perm-errors", no_argument, 0, 263 },
147 { "no-ignore-perm-errors", no_argument, 0, 264 },
148 { "hard-link-files-when-possible", no_argument, 0, 265 },
149 { "no-hard-link-files-when-possible", no_argument, 0, 266 },
150 { 0, 0, 0, 0 },
151};
152
153
154static int copy(int, const char *, int, const char *, off_t);
155static int compare(int, const char *, size_t, int, const char *, size_t);
156static int create_newfile(const char *, int, struct stat *);
157static int create_tempfile(const char *, char *, size_t);
158static int install(const char *, const char *, u_long, u_int);
159static int install_dir(char *);
160static u_long numeric_id(const char *, const char *);
161static int strip(const char *);
162#ifdef USE_MMAP
163static int trymmap(int);
164#endif
165static int usage(FILE *);
166static char *last_slash(const char *);
167
168int
169kmk_builtin_install(int argc, char *argv[], char ** envp)
170{
171 struct stat from_sb, to_sb;
172 mode_t *set;
173 u_long fset = 0;
174 int ch, no_target;
175 u_int iflags;
176 char *flags;
177 const char *group, *owner, *to_name;
178 (void)envp;
179
180 /* reinitialize globals */
181 mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
182 suffix = BACKUP_SUFFIX;
183 gid = 0;
184 uid = 0;
185 dobackup = docompare = dodir = dopreserve = dostrip = nommap = safecopy = verbose = mode_given = 0;
186 ignore_perm_errors = geteuid() != 0;
187 hard_link_files_when_possible = 0;
188
189 /* reset getopt and set progname. */
190 g_progname = argv[0];
191 opterr = 1;
192 optarg = NULL;
193 optopt = 0;
194 optind = 0; /* init */
195
196 iflags = 0;
197 group = owner = NULL;
198 while ((ch = getopt_long(argc, argv, "B:bCcdf:g:Mm:o:pSsv", long_options, NULL)) != -1)
199 switch(ch) {
200 case 'B':
201 suffix = optarg;
202 /* FALLTHROUGH */
203 case 'b':
204 dobackup = 1;
205 break;
206 case 'C':
207 docompare = 1;
208 break;
209 case 'c':
210 /* For backwards compatibility. */
211 break;
212 case 'd':
213 dodir = 1;
214 break;
215 case 'f':
216#ifdef UF_IMMUTABLE
217 flags = optarg;
218 if (strtofflags(&flags, &fset, NULL))
219 return errx(EX_USAGE, "%s: invalid flag", flags);
220 iflags |= SETFLAGS;
221#else
222 (void)flags;
223#endif
224 break;
225 case 'g':
226 group = optarg;
227 break;
228 case 'M':
229 nommap = 1;
230 break;
231 case 'm':
232 if (!(set = bsd_setmode(optarg)))
233 return errx(EX_USAGE, "invalid file mode: %s",
234 optarg);
235 mode = bsd_getmode(set, 0);
236 free(set);
237 mode_given = 1;
238 break;
239 case 'o':
240 owner = optarg;
241 break;
242 case 'p':
243 docompare = dopreserve = 1;
244 break;
245 case 'S':
246 safecopy = 1;
247 break;
248 case 's':
249 dostrip = 1;
250 break;
251 case 'v':
252 verbose = 1;
253 break;
254 case 261:
255 usage(stdout);
256 return 0;
257 case 262:
258 return kbuild_version(argv[0]);
259 case 263:
260 ignore_perm_errors = 1;
261 break;
262 case 264:
263 ignore_perm_errors = 0;
264 break;
265 case 265:
266 hard_link_files_when_possible = 1;
267 break;
268 case 266:
269 hard_link_files_when_possible = 0;
270 break;
271 case '?':
272 default:
273 return usage(stderr);
274 }
275 argc -= optind;
276 argv += optind;
277
278 /* some options make no sense when creating directories */
279 if (dostrip && dodir) {
280 warnx("-d and -s may not be specified together");
281 return usage(stderr);
282 }
283
284 /* must have at least two arguments, except when creating directories */
285 if (argc == 0 || (argc == 1 && !dodir))
286 return usage(stderr);
287
288 /* need to make a temp copy so we can compare stripped version */
289 if (docompare && dostrip)
290 safecopy = 1;
291
292 /* get group and owner id's */
293 if (group != NULL) {
294#ifndef _MSC_VER
295 struct group *gp;
296 if ((gp = getgrnam(group)) != NULL)
297 gid = gp->gr_gid;
298 else
299#endif
300 {
301 gid = (gid_t)numeric_id(group, "group");
302 if (gid == (gid_t)-1)
303 return 1;
304 }
305 } else
306 gid = (gid_t)-1;
307
308 if (owner != NULL) {
309#ifndef _MSC_VER
310 struct passwd *pp;
311 if ((pp = getpwnam(owner)) != NULL)
312 uid = pp->pw_uid;
313 else
314#endif
315 {
316 uid = (uid_t)numeric_id(owner, "user");
317 if (uid == (uid_t)-1)
318 return 1;
319 }
320 } else
321 uid = (uid_t)-1;
322
323 if (dodir) {
324 for (; *argv != NULL; ++argv) {
325 int rc = install_dir(*argv);
326 if (rc)
327 return rc;
328 }
329 return EX_OK;
330 /* NOTREACHED */
331 }
332
333 no_target = stat(to_name = argv[argc - 1], &to_sb);
334 if (!no_target && S_ISDIR(to_sb.st_mode)) {
335 for (; *argv != to_name; ++argv) {
336 int rc = install(*argv, to_name, fset, iflags | DIRECTORY);
337 if (rc)
338 return rc;
339 }
340 return EX_OK;
341 }
342
343 /* can't do file1 file2 directory/file */
344 if (argc != 2) {
345 warnx("wrong number or types of arguments");
346 return usage(stderr);
347 }
348
349 if (!no_target) {
350 if (stat(*argv, &from_sb))
351 return err(EX_OSERR, "%s", *argv);
352 if (!S_ISREG(to_sb.st_mode)) {
353 errno = EFTYPE;
354 return err(EX_OSERR, "%s", to_name);
355 }
356 if (to_sb.st_dev == from_sb.st_dev &&
357 to_sb.st_dev != 0 &&
358 to_sb.st_ino == from_sb.st_ino &&
359 to_sb.st_ino != 0 &&
360 !hard_link_files_when_possible)
361 return errx(EX_USAGE,
362 "%s and %s are the same file", *argv, to_name);
363 }
364 return install(*argv, to_name, fset, iflags);
365}
366
367static u_long
368numeric_id(const char *name, const char *type)
369{
370 u_long val;
371 char *ep;
372
373 /*
374 * XXX
375 * We know that uid_t's and gid_t's are unsigned longs.
376 */
377 errno = 0;
378 val = strtoul(name, &ep, 10);
379 if (errno)
380 return err(-1, "%s", name);
381 if (*ep != '\0')
382 return errx(-1, "unknown %s %s", type, name);
383 return (val);
384}
385
386/*
387 * install --
388 * build a path name and install the file
389 */
390static int
391install(const char *from_name, const char *to_name, u_long fset, u_int flags)
392{
393 struct stat from_sb, temp_sb, to_sb;
394 struct timeval tvb[2];
395 int devnull, files_match, from_fd, serrno, target;
396 int tempcopy, temp_fd, to_fd;
397 char backup[MAXPATHLEN], *p, pathbuf[MAXPATHLEN], tempfile[MAXPATHLEN];
398 int rc = EX_OK;
399
400 files_match = 0;
401 from_fd = -1;
402 to_fd = -1;
403 temp_fd = -1;
404
405 /* If try to install NULL file to a directory, fails. */
406 if (flags & DIRECTORY
407#if defined(__EMX__) || defined(_MSC_VER)
408 || ( stricmp(from_name, _PATH_DEVNULL)
409 && stricmp(from_name, "nul")
410# ifdef __EMX__
411 && stricmp(from_name, "/dev/nul")
412# endif
413 )
414#else
415 || strcmp(from_name, _PATH_DEVNULL)
416#endif
417 ) {
418 if (stat(from_name, &from_sb))
419 return err(EX_OSERR, "%s", from_name);
420 if (!S_ISREG(from_sb.st_mode)) {
421 errno = EFTYPE;
422 return err(EX_OSERR, "%s", from_name);
423 }
424 /* Build the target path. */
425 if (flags & DIRECTORY) {
426 (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
427 to_name,
428 (p = last_slash(from_name)) ? ++p : from_name);
429 to_name = pathbuf;
430 }
431 devnull = 0;
432 } else {
433 devnull = 1;
434 }
435
436 target = stat(to_name, &to_sb) == 0;
437
438 /* Only install to regular files. */
439 if (target && !S_ISREG(to_sb.st_mode)) {
440 errno = EFTYPE;
441 warn("%s", to_name);
442 return EX_OK;
443 }
444
445 /* Only copy safe if the target exists. */
446 tempcopy = safecopy && target;
447
448 /* Try hard linking if wanted and possible. */
449 if (hard_link_files_when_possible)
450 {
451#ifdef KBUILD_OS_OS2
452 const char *why_not = "not supported on OS/2";
453#else
454 const char *why_not = NULL;
455 if (devnull) {
456 why_not = "/dev/null";
457 } else if (dostrip) {
458 why_not = "strip (-s)";
459 } else if (docompare) {
460 why_not = "compare (-C)";
461 } else if (dobackup) {
462 why_not = "backup (-b/-B)";
463 } else if (safecopy) {
464 why_not = "safe copy (-S)";
465 } else if (lstat(from_name, &temp_sb)) {
466 why_not = "lstat on source failed";
467 } else if (S_ISLNK(temp_sb.st_mode)) {
468 why_not = "symlink";
469 } else if (!S_ISREG(temp_sb.st_mode)) {
470 why_not = "not regular file";
471# if defined(KBUILD_OS_WINDOWS) || defined(KBUILD_OS_OS2)
472 } else if ((mode & S_IWUSR) != (from_sb.st_mode & S_IWUSR)) {
473# else
474 } else if (mode != (from_sb.st_mode & ALLPERMS)) {
475# endif
476 printf("install: warning: Not hard linking, mode differs: 0%03o, desires 0%03o\n"
477 "install: src path '%s'\n"
478 "install: dst path '%s'\n",
479 (from_sb.st_mode & ALLPERMS), mode, from_name, to_name);
480 why_not = NULL;
481 } else if (uid != (uid_t)-1 && gid != from_sb.st_uid) {
482 why_not = "uid mismatch";
483 } else if (gid != (gid_t)-1 && gid != from_sb.st_gid) {
484 why_not = "gid mismatch";
485 } else {
486 int rcLink = link(from_name, to_name);
487 if (rcLink != 0 && errno == EEXIST) {
488 unlink(to_name);
489 rcLink = link(from_name, to_name);
490 }
491 if (rcLink == 0) {
492 if (verbose)
493 printf("install: %s -> %s (hardlinked)\n", from_name, to_name);
494 goto l_done;
495 }
496 if (verbose)
497 printf("install: hard linking '%s' to '%s' failed: %s\n",
498 to_name, from_name, strerror(errno));
499 why_not = NULL;
500 }
501#endif
502 if (verbose && why_not)
503 printf("install: not hard linking '%s' to '%s' because: %s\n",
504 to_name, from_name, why_not);
505
506 /* Can't hard link or we failed, continue as nothing happend. */
507 }
508
509 if (!devnull && (from_fd = open(from_name, O_RDONLY | O_BINARY, 0)) < 0)
510 return err(EX_OSERR, "%s", from_name);
511
512 /* If we don't strip, we can compare first. */
513 if (docompare && !dostrip && target) {
514 if ((to_fd = open(to_name, O_RDONLY | O_BINARY, 0)) < 0) {
515 rc = err(EX_OSERR, "%s", to_name);
516 goto l_done;
517 }
518 if (devnull)
519 files_match = to_sb.st_size == 0;
520 else
521 files_match = !(compare(from_fd, from_name,
522 (size_t)from_sb.st_size, to_fd,
523 to_name, (size_t)to_sb.st_size));
524
525 /* Close "to" file unless we match. */
526 if (!files_match) {
527 (void)close(to_fd);
528 to_fd = -1;
529 }
530 }
531
532 if (!files_match) {
533 if (tempcopy) {
534 to_fd = create_tempfile(to_name, tempfile,
535 sizeof(tempfile));
536 if (to_fd < 0) {
537 rc = err(EX_OSERR, "%s", tempfile);
538 goto l_done;
539 }
540 } else {
541 if ((to_fd = create_newfile(to_name, target,
542 &to_sb)) < 0) {
543 rc = err(EX_OSERR, "%s", to_name);
544 goto l_done;
545 }
546 if (verbose)
547 (void)printf("install: %s -> %s\n",
548 from_name, to_name);
549 }
550 if (!devnull) {
551 rc = copy(from_fd, from_name, to_fd,
552 tempcopy ? tempfile : to_name, from_sb.st_size);
553 if (rc)
554 goto l_done;
555 }
556 }
557
558 if (dostrip) {
559#if defined(__EMX__) || defined(_MSC_VER)
560 /* close before we strip. */
561 close(to_fd);
562 to_fd = -1;
563#endif
564 rc = strip(tempcopy ? tempfile : to_name);
565 if (rc)
566 goto l_done;
567
568 /*
569 * Re-open our fd on the target, in case we used a strip
570 * that does not work in-place -- like GNU binutils strip.
571 */
572#if !defined(__EMX__) && !defined(_MSC_VER)
573 close(to_fd);
574#endif
575 to_fd = open(tempcopy ? tempfile : to_name, O_RDONLY | O_BINARY, 0);
576 if (to_fd < 0) {
577 rc = err(EX_OSERR, "stripping %s", to_name);
578 goto l_done;
579 }
580 }
581
582 /*
583 * Compare the stripped temp file with the target.
584 */
585 if (docompare && dostrip && target) {
586 temp_fd = to_fd;
587
588 /* Re-open to_fd using the real target name. */
589 if ((to_fd = open(to_name, O_RDONLY | O_BINARY, 0)) < 0) {
590 rc = err(EX_OSERR, "%s", to_name);
591 goto l_done;
592 }
593
594 if (fstat(temp_fd, &temp_sb)) {
595 serrno = errno;
596 (void)unlink(tempfile);
597 errno = serrno;
598 rc = err(EX_OSERR, "%s", tempfile);
599 goto l_done;
600 }
601
602 if (compare(temp_fd, tempfile, (size_t)temp_sb.st_size, to_fd,
603 to_name, (size_t)to_sb.st_size) == 0) {
604 /*
605 * If target has more than one link we need to
606 * replace it in order to snap the extra links.
607 * Need to preserve target file times, though.
608 */
609#if !defined(_MSC_VER) && !defined(__EMX__)
610 if (to_sb.st_nlink != 1) {
611 tvb[0].tv_sec = to_sb.st_atime;
612 tvb[0].tv_usec = 0;
613 tvb[1].tv_sec = to_sb.st_mtime;
614 tvb[1].tv_usec = 0;
615 (void)utimes(tempfile, tvb);
616 } else
617#endif
618 {
619
620 files_match = 1;
621 (void)unlink(tempfile);
622 }
623 (void) close(temp_fd);
624 temp_fd = -1;
625 }
626 }
627
628 /*
629 * Move the new file into place if doing a safe copy
630 * and the files are different (or just not compared).
631 */
632 if (tempcopy && !files_match) {
633#ifdef UF_IMMUTABLE
634 /* Try to turn off the immutable bits. */
635 if (to_sb.st_flags & NOCHANGEBITS)
636 (void)chflags(to_name, to_sb.st_flags & ~NOCHANGEBITS);
637#endif
638 if (dobackup) {
639 if ((size_t)snprintf(backup, MAXPATHLEN, "%s%s", to_name,
640 suffix) != strlen(to_name) + strlen(suffix)) {
641 unlink(tempfile);
642 rc = errx(EX_OSERR, "%s: backup filename too long",
643 to_name);
644 goto l_done;
645 }
646 if (verbose)
647 (void)printf("install: %s -> %s\n", to_name, backup);
648 if (rename(to_name, backup) < 0) {
649 serrno = errno;
650 unlink(tempfile);
651 errno = serrno;
652 rc = err(EX_OSERR, "rename: %s to %s", to_name,
653 backup);
654 goto l_done;
655 }
656 }
657 if (verbose)
658 (void)printf("install: %s -> %s\n", from_name, to_name);
659 if (rename(tempfile, to_name) < 0) {
660 serrno = errno;
661 unlink(tempfile);
662 errno = serrno;
663 rc = err(EX_OSERR, "rename: %s to %s",
664 tempfile, to_name);
665 goto l_done;
666 }
667
668 /* Re-open to_fd so we aren't hosed by the rename(2). */
669 (void) close(to_fd);
670 if ((to_fd = open(to_name, O_RDONLY | O_BINARY, 0)) < 0) {
671 rc = err(EX_OSERR, "%s", to_name);
672 goto l_done;
673 }
674 }
675
676 /*
677 * Preserve the timestamp of the source file if necessary.
678 */
679 if (dopreserve && !files_match && !devnull) {
680 tvb[0].tv_sec = from_sb.st_atime;
681 tvb[0].tv_usec = 0;
682 tvb[1].tv_sec = from_sb.st_mtime;
683 tvb[1].tv_usec = 0;
684 (void)utimes(to_name, tvb);
685 }
686
687 if (fstat(to_fd, &to_sb) == -1) {
688 serrno = errno;
689 (void)unlink(to_name);
690 errno = serrno;
691 rc = err(EX_OSERR, "%s", to_name);
692 goto l_done;
693 }
694
695 /*
696 * Set owner, group, mode for target; do the chown first,
697 * chown may lose the setuid bits.
698 */
699#ifdef UF_IMMUTABLE
700 if ((gid != (gid_t)-1 && gid != to_sb.st_gid) ||
701 (uid != (uid_t)-1 && uid != to_sb.st_uid) ||
702 (mode != (to_sb.st_mode & ALLPERMS))) {
703 /* Try to turn off the immutable bits. */
704 if (to_sb.st_flags & NOCHANGEBITS)
705 (void)fchflags(to_fd, to_sb.st_flags & ~NOCHANGEBITS);
706 }
707#endif
708
709 if ((gid != (gid_t)-1 && gid != to_sb.st_gid) ||
710 (uid != (uid_t)-1 && uid != to_sb.st_uid))
711 if (fchown(to_fd, uid, gid) == -1) {
712 if (errno == EPERM && ignore_perm_errors) {
713 warn("%s: ignoring chown uid=%d gid=%d failure", to_name, (int)uid, (int)gid);
714 } else {
715 serrno = errno;
716 (void)unlink(to_name);
717 errno = serrno;
718 rc = err(EX_OSERR,"%s: chown/chgrp", to_name);
719 goto l_done;
720 }
721 }
722
723 if (mode != (to_sb.st_mode & ALLPERMS))
724 if (fchmod(to_fd, mode)) {
725 serrno = errno;
726 if (serrno == EPERM && ignore_perm_errors) {
727 fchmod(to_fd, mode & (ALLPERMS & ~0007000));
728 errno = errno;
729 warn("%s: ignoring chmod 0%o failure", to_name, (int)(mode & ALLPERMS));
730 } else {
731 serrno = errno;
732 (void)unlink(to_name);
733 errno = serrno;
734 rc = err(EX_OSERR, "%s: chmod", to_name);
735 goto l_done;
736 }
737 }
738
739 /*
740 * If provided a set of flags, set them, otherwise, preserve the
741 * flags, except for the dump flag.
742 * NFS does not support flags. Ignore EOPNOTSUPP flags if we're just
743 * trying to turn off UF_NODUMP. If we're trying to set real flags,
744 * then warn if the the fs doesn't support it, otherwise fail.
745 */
746#ifdef UF_IMMUTABLE
747 if (!devnull && (flags & SETFLAGS ||
748 (from_sb.st_flags & ~UF_NODUMP) != to_sb.st_flags) &&
749 fchflags(to_fd,
750 flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) {
751 if (flags & SETFLAGS) {
752 if (errno == EOPNOTSUPP)
753 warn("%s: chflags", to_name);
754 else {
755 serrno = errno;
756 (void)unlink(to_name);
757 errno = serrno;
758 rc = err(EX_OSERR, "%s: chflags", to_name);
759 goto l_done;
760 }
761 }
762 }
763#endif
764
765l_done:
766 if (to_fd >= 0)
767 (void)close(to_fd);
768 if (temp_fd >= 0)
769 (void)close(temp_fd);
770 if (from_fd >= 0 && !devnull)
771 (void)close(from_fd);
772 return rc;
773}
774
775/*
776 * compare --
777 * compare two files; non-zero means files differ
778 */
779static int
780compare(int from_fd, const char *from_name __unused, size_t from_len,
781 int to_fd, const char *to_name __unused, size_t to_len)
782{
783 char *p, *q;
784 int rv;
785 int done_compare;
786
787 rv = 0;
788 if (from_len != to_len)
789 return 1;
790
791 if (from_len <= MAX_CMP_SIZE) {
792#ifdef USE_MMAP
793 done_compare = 0;
794 if (trymmap(from_fd) && trymmap(to_fd)) {
795 p = mmap(NULL, from_len, PROT_READ, MAP_SHARED, from_fd, (off_t)0);
796 if (p == (char *)MAP_FAILED)
797 goto out;
798 q = mmap(NULL, from_len, PROT_READ, MAP_SHARED, to_fd, (off_t)0);
799 if (q == (char *)MAP_FAILED) {
800 munmap(p, from_len);
801 goto out;
802 }
803
804 rv = memcmp(p, q, from_len);
805 munmap(p, from_len);
806 munmap(q, from_len);
807 done_compare = 1;
808 }
809 out:
810#else
811 (void)p; (void)q;
812 done_compare = 0;
813#endif
814 if (!done_compare) {
815 char buf1[MAXBSIZE];
816 char buf2[MAXBSIZE];
817 int n1, n2;
818
819 rv = 0;
820 lseek(from_fd, 0, SEEK_SET);
821 lseek(to_fd, 0, SEEK_SET);
822 while (rv == 0) {
823 n1 = read(from_fd, buf1, sizeof(buf1));
824 if (n1 == 0)
825 break; /* EOF */
826 else if (n1 > 0) {
827 n2 = read(to_fd, buf2, n1);
828 if (n2 == n1)
829 rv = memcmp(buf1, buf2, n1);
830 else
831 rv = 1; /* out of sync */
832 } else
833 rv = 1; /* read failure */
834 }
835 lseek(from_fd, 0, SEEK_SET);
836 lseek(to_fd, 0, SEEK_SET);
837 }
838 } else
839 rv = 1; /* don't bother in this case */
840
841 return rv;
842}
843
844/*
845 * create_tempfile --
846 * create a temporary file based on path and open it
847 */
848int
849create_tempfile(const char *path, char *temp, size_t tsize)
850{
851 char *p;
852
853 (void)strncpy(temp, path, tsize);
854 temp[tsize - 1] = '\0';
855 if ((p = last_slash(temp)) != NULL)
856 p++;
857 else
858 p = temp;
859 (void)strncpy(p, "INS@XXXX", &temp[tsize - 1] - p);
860 temp[tsize - 1] = '\0';
861 return (mkstemp(temp));
862}
863
864/*
865 * create_newfile --
866 * create a new file, overwriting an existing one if necessary
867 */
868int
869create_newfile(const char *path, int target, struct stat *sbp)
870{
871 char backup[MAXPATHLEN];
872 int saved_errno = 0;
873 int newfd;
874
875 if (target) {
876 /*
877 * Unlink now... avoid ETXTBSY errors later. Try to turn
878 * off the append/immutable bits -- if we fail, go ahead,
879 * it might work.
880 */
881#ifdef UF_IMMUTABLE
882 if (sbp->st_flags & NOCHANGEBITS)
883 (void)chflags(path, sbp->st_flags & ~NOCHANGEBITS);
884#endif
885
886 if (dobackup) {
887 if ((size_t)snprintf(backup, MAXPATHLEN, "%s%s",
888 path, suffix) != strlen(path) + strlen(suffix)) {
889 errx(EX_OSERR, "%s: backup filename too long",
890 path);
891 errno = ENAMETOOLONG;
892 return -1;
893 }
894 (void)snprintf(backup, MAXPATHLEN, "%s%s",
895 path, suffix);
896 if (verbose)
897 (void)printf("install: %s -> %s\n",
898 path, backup);
899 if (rename(path, backup) < 0) {
900 err(EX_OSERR, "rename: %s to %s", path, backup);
901 return -1;
902 }
903 } else
904 if (unlink(path) < 0)
905 saved_errno = errno;
906 }
907
908 newfd = open(path, O_CREAT | O_RDWR | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR);
909 if (newfd < 0 && saved_errno != 0)
910 errno = saved_errno;
911 return newfd;
912}
913
914/*
915 * copy --
916 * copy from one file to another
917 */
918static int
919copy(int from_fd, const char *from_name, int to_fd, const char *to_name,
920 off_t size)
921{
922 int nr, nw;
923 int serrno;
924 char *p, buf[MAXBSIZE];
925 int done_copy;
926
927 /* Rewind file descriptors. */
928 if (lseek(from_fd, (off_t)0, SEEK_SET) == (off_t)-1)
929 return err(EX_OSERR, "lseek: %s", from_name);
930 if (lseek(to_fd, (off_t)0, SEEK_SET) == (off_t)-1)
931 return err(EX_OSERR, "lseek: %s", to_name);
932
933 /*
934 * Mmap and write if less than 8M (the limit is so we don't totally
935 * trash memory on big files. This is really a minor hack, but it
936 * wins some CPU back.
937 */
938 done_copy = 0;
939#ifdef USE_MMAP
940 if (size <= 8 * 1048576 && trymmap(from_fd) &&
941 (p = mmap(NULL, (size_t)size, PROT_READ, MAP_SHARED,
942 from_fd, (off_t)0)) != (char *)MAP_FAILED) {
943 if ((nw = write(to_fd, p, size)) != size) {
944 serrno = errno;
945 (void)unlink(to_name);
946 errno = nw > 0 ? EIO : serrno;
947 err(EX_OSERR, "%s", to_name);
948 }
949 done_copy = 1;
950 }
951#else
952 (void)p; (void)size;
953#endif
954 if (!done_copy) {
955 while ((nr = read(from_fd, buf, sizeof(buf))) > 0)
956 if ((nw = write(to_fd, buf, nr)) != nr) {
957 serrno = errno;
958 (void)unlink(to_name);
959 errno = nw > 0 ? EIO : serrno;
960 return err(EX_OSERR, "%s", to_name);
961 }
962 if (nr != 0) {
963 serrno = errno;
964 (void)unlink(to_name);
965 errno = serrno;
966 return err(EX_OSERR, "%s", from_name);
967 }
968 }
969 return EX_OK;
970}
971
972/*
973 * strip --
974 * use strip(1) to strip the target file
975 */
976static int
977strip(const char *to_name)
978{
979#if defined(__EMX__) || defined(_MSC_VER)
980 const char *stripbin = getenv("STRIPBIN");
981 if (stripbin == NULL)
982 stripbin = "strip";
983 return spawnlp(P_WAIT, stripbin, stripbin, to_name, NULL);
984#else
985 const char *stripbin;
986 int serrno, status;
987 pid_t pid;
988
989 pid = fork();
990 switch (pid) {
991 case -1:
992 serrno = errno;
993 (void)unlink(to_name);
994 errno = serrno;
995 return err(EX_TEMPFAIL, "fork");
996 case 0:
997 stripbin = getenv("STRIPBIN");
998 if (stripbin == NULL)
999 stripbin = "strip";
1000 execlp(stripbin, stripbin, to_name, (char *)NULL);
1001 err(EX_OSERR, "exec(%s)", stripbin);
1002 exit(EX_OSERR);
1003 default:
1004 if (waitpid(pid, &status, 0) == -1 || status) {
1005 serrno = errno;
1006 (void)unlink(to_name);
1007 errno = serrno;
1008 return err(EX_SOFTWARE, "waitpid");
1009 /* NOTREACHED */
1010 }
1011 }
1012 return 0;
1013#endif
1014}
1015
1016/*
1017 * install_dir --
1018 * build directory heirarchy
1019 */
1020static int
1021install_dir(char *path)
1022{
1023 char *p;
1024 struct stat sb;
1025 int ch;
1026
1027 for (p = path;; ++p)
1028 if ( !*p
1029 || ( p != path
1030 && IS_SLASH(*p)
1031#if defined(_MSC_VER) /* stat("C:") fails (VC++ v10). Just skip it since it's unnecessary. */
1032 && (p - path != 2 || p[-1] != ':')
1033#endif
1034 )) {
1035 ch = *p;
1036 *p = '\0';
1037 if (stat(path, &sb)) {
1038 if (errno != ENOENT || mkdir(path, 0755) < 0) {
1039 return err(EX_OSERR, "mkdir %s", path);
1040 /* NOTREACHED */
1041 } else if (verbose)
1042 (void)printf("install: mkdir %s\n",
1043 path);
1044 } else if (!S_ISDIR(sb.st_mode))
1045 return errx(EX_OSERR, "%s exists but is not a directory", path);
1046 if (!(*p = ch))
1047 break;
1048 }
1049
1050 if ((gid != (gid_t)-1 || uid != (uid_t)-1) && chown(path, uid, gid))
1051 warn("chown %u:%u %s", uid, gid, path);
1052 if (chmod(path, mode))
1053 warn("chmod %o %s", mode, path);
1054 return EX_OK;
1055}
1056
1057/*
1058 * usage --
1059 * print a usage message and die
1060 */
1061static int
1062usage(FILE *pf)
1063{
1064 fprintf(pf,
1065"usage: %s [-bCcpSsv] [--[no-]hard-link-files-when-possible]\n"
1066" [--[no-]ignore-perm-errors] [-B suffix] [-f flags]\n"
1067" [-g group] [-m mode] [-o owner] file1 file2\n"
1068" or: %s [-bCcpSsv] [--[no-]ignore-perm-errors] [-B suffix] [-f flags]\n"
1069" [-g group] [-m mode] [-o owner] file1 ... fileN directory\n"
1070" or: %s -d [-v] [-g group] [-m mode] [-o owner] directory ...\n"
1071" or: %s --help\n"
1072" or: %s --version\n",
1073 g_progname, g_progname, g_progname, g_progname, g_progname);
1074 return EX_USAGE;
1075}
1076
1077#ifdef USE_MMAP
1078/*
1079 * trymmap --
1080 * return true (1) if mmap should be tried, false (0) if not.
1081 */
1082static int
1083trymmap(int fd)
1084{
1085/*
1086 * The ifdef is for bootstrapping - f_fstypename doesn't exist in
1087 * pre-Lite2-merge systems.
1088 */
1089#ifdef MFSNAMELEN
1090 struct statfs stfs;
1091
1092 if (nommap || fstatfs(fd, &stfs) != 0)
1093 return (0);
1094 if (strcmp(stfs.f_fstypename, "ufs") == 0 ||
1095 strcmp(stfs.f_fstypename, "cd9660") == 0)
1096 return (1);
1097#endif
1098 return (0);
1099}
1100#endif
1101
1102/* figures out where the last slash or colon is. */
1103static char *
1104last_slash(const char *path)
1105{
1106#if defined(__WIN32__) || defined(__WIN64__) || defined(__OS2__)
1107 char *p = (char *)strrchr(path, '/');
1108 if (p)
1109 {
1110 char *p2 = strrchr(p, '\\');
1111 if (p2)
1112 p = p2;
1113 }
1114 else
1115 {
1116 p = (char *)strrchr(path, '\\');
1117 if (!p && isalpha(path[0]) && path[1] == ':')
1118 p = (char *)&path[1];
1119 }
1120 return p;
1121#else
1122 return strrchr(path, '/');
1123#endif
1124}
1125
Note: See TracBrowser for help on using the repository browser.