VirtualBox

source: kBuild/trunk/src/gmake/kmkbuiltin/install.c@ 775

Last change on this file since 775 was 758, checked in by bird, 18 years ago

use waitpid(pid) since wait() interfers with any parallel jobs gnu make might be executing.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette