VirtualBox

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

Last change on this file since 3432 was 3389, checked in by bird, 5 years ago

kmk: Avoid setting umask just to get it, store the current value in a global variable (g_fUMask). The umask(0777) call in cp.c raced other code (kmk_append) that created files and directories, leaving us with read-only files sometimes.

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