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

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

kmk_install: added --dos2unix and --unix2dos flags.

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