VirtualBox

source: kBuild/trunk/src/kmk/kmkbuiltin/install.c@ 3173

Last change on this file since 3173 was 3145, checked in by bird, 7 years ago

kmk: warnings found by gcc 7.3.0

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

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