source: trunk/src/gmake/kmkbuiltin/install.c@ 374

Last change on this file since 374 was 374, checked in by bird, 20 years ago

Made it build on OS/2.

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