VirtualBox

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

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

kmkbuiltin: funnel output thru output.c (usually via err.c).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.0 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
124/*********************************************************************************************************************************
125* Structures and Typedefs *
126*********************************************************************************************************************************/
127typedef struct INSTALLINSTANCE
128{
129 PKMKBUILTINCTX pCtx;
130
131 gid_t gid;
132 uid_t uid;
133 int dobackup, docompare, dodir, dopreserve, dostrip, nommap, safecopy, verbose, mode_given;
134 mode_t mode;
135 const char *suffix;
136 int ignore_perm_errors;
137 int hard_link_files_when_possible;
138 int dos2unix;
139} INSTALLINSTANCE;
140typedef INSTALLINSTANCE *PINSTALLINSTANCE;
141
142
143/*********************************************************************************************************************************
144* Global Variables *
145*********************************************************************************************************************************/
146static struct option long_options[] =
147{
148 { "help", no_argument, 0, 261 },
149 { "version", no_argument, 0, 262 },
150 { "ignore-perm-errors", no_argument, 0, 263 },
151 { "no-ignore-perm-errors", no_argument, 0, 264 },
152 { "hard-link-files-when-possible", no_argument, 0, 265 },
153 { "no-hard-link-files-when-possible", no_argument, 0, 266 },
154 { "dos2unix", no_argument, 0, 267 },
155 { "unix2dos", no_argument, 0, 268 },
156 { 0, 0, 0, 0 },
157};
158
159
160static int copy(PINSTALLINSTANCE, int, const char *, int *, const char *);
161static int compare(int, size_t, int, size_t);
162static int create_newfile(PINSTALLINSTANCE, const char *, int, struct stat *);
163static int create_tempfile(const char *, char *, size_t);
164static int install(PINSTALLINSTANCE, const char *, const char *, u_long, u_int);
165static int install_dir(PINSTALLINSTANCE, char *);
166static u_long numeric_id(PINSTALLINSTANCE, const char *, const char *);
167static int strip(PINSTALLINSTANCE, const char *);
168static int usage(PKMKBUILTINCTX, int);
169static char *last_slash(const char *);
170static KBOOL needs_dos2unix_conversion(const char *pszFilename);
171static KBOOL needs_unix2dos_conversion(const char *pszFilename);
172
173int
174kmk_builtin_install(int argc, char *argv[], char ** envp, PKMKBUILTINCTX pCtx)
175{
176 INSTALLINSTANCE This;
177 struct stat from_sb, to_sb;
178 mode_t *set;
179 u_long fset = 0;
180 int ch, no_target;
181 u_int iflags;
182 char *flags;
183 const char *group, *owner, *to_name;
184 (void)envp;
185
186 /* Initialize global instance data. */
187 This.pCtx = pCtx;
188 This.mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
189 This.suffix = BACKUP_SUFFIX;
190 This.gid = 0;
191 This.uid = 0;
192 This.dobackup = 0;
193 This.docompare = 0;
194 This.dodir = 0;
195 This.dopreserve = 0;
196 This.dostrip = 0;
197 This.nommap = 0;
198 This.safecopy = 0;
199 This.verbose = 0;
200 This.mode_given = 0;
201 This.ignore_perm_errors = geteuid() != 0;
202 This.hard_link_files_when_possible = 0;
203 This.dos2unix = 0;
204
205 /* reset getopt and set progname. */
206 opterr = 1;
207 optarg = NULL;
208 optopt = 0;
209 optind = 0; /* init */
210
211 iflags = 0;
212 group = owner = NULL;
213 while ((ch = getopt_long(argc, argv, "B:bCcdf:g:Mm:o:pSsv", long_options, NULL)) != -1)
214 switch(ch) {
215 case 'B':
216 This.suffix = optarg;
217 /* FALLTHROUGH */
218 case 'b':
219 This.dobackup = 1;
220 break;
221 case 'C':
222 This.docompare = 1;
223 break;
224 case 'c':
225 /* For backwards compatibility. */
226 break;
227 case 'd':
228 This.dodir = 1;
229 break;
230 case 'f':
231#if defined(UF_IMMUTABLE) && K_OS != K_OS_GNU_KFBSD && K_OS != K_OS_GNU_HURD
232 flags = optarg;
233 if (strtofflags(&flags, &fset, NULL))
234 return errx(pCtx, EX_USAGE, "%s: invalid flag", flags);
235 iflags |= SETFLAGS;
236#else
237 (void)flags;
238#endif
239 break;
240 case 'g':
241 group = optarg;
242 break;
243 case 'M':
244 This.nommap = 1;
245 break;
246 case 'm':
247 if (!(set = bsd_setmode(optarg)))
248 return errx(pCtx, EX_USAGE, "invalid file mode: %s", optarg);
249 This.mode = bsd_getmode(set, 0);
250 free(set);
251 This.mode_given = 1;
252 break;
253 case 'o':
254 owner = optarg;
255 break;
256 case 'p':
257 This.docompare = This.dopreserve = 1;
258 break;
259 case 'S':
260 This.safecopy = 1;
261 break;
262 case 's':
263 This.dostrip = 1;
264 break;
265 case 'v':
266 This.verbose = 1;
267 break;
268 case 261:
269 usage(pCtx, 0);
270 return 0;
271 case 262:
272 return kbuild_version(argv[0]);
273 case 263:
274 This.ignore_perm_errors = 1;
275 break;
276 case 264:
277 This.ignore_perm_errors = 0;
278 break;
279 case 265:
280 This.hard_link_files_when_possible = 1;
281 break;
282 case 266:
283 This.hard_link_files_when_possible = 0;
284 break;
285 case 267:
286 This.dos2unix = 1;
287 break;
288 case 268:
289 This.dos2unix = -1;
290 break;
291 case '?':
292 default:
293 return usage(pCtx, 1);
294 }
295 argc -= optind;
296 argv += optind;
297
298 /* some options make no sense when creating directories */
299 if (This.dostrip && This.dodir) {
300 warnx(pCtx, "-d and -s may not be specified together");
301 return usage(pCtx, 1);
302 }
303
304 /* must have at least two arguments, except when creating directories */
305 if (argc == 0 || (argc == 1 && !This.dodir))
306 return usage(pCtx, 1);
307
308 /* and unix2dos doesn't combine well with a couple of other options. */
309 if (This.dos2unix != 0) {
310 if (This.docompare) {
311 warnx(pCtx, "-C/-p and --dos2unix/unix2dos may not be specified together");
312 return usage(pCtx, 1);
313 }
314 if (This.dostrip) {
315 warnx(pCtx, "-s and --dos2unix/unix2dos may not be specified together");
316 return usage(pCtx, 1);
317 }
318 }
319
320 /* need to make a temp copy so we can compare stripped version */
321 if (This.docompare && This.dostrip)
322 This.safecopy = 1;
323
324 /* get group and owner id's */
325 if (group != NULL) {
326#ifndef _MSC_VER
327 struct group *gp;
328 if ((gp = getgrnam(group)) != NULL)
329 gid = gp->gr_gid;
330 else
331#endif
332 {
333 This.gid = (gid_t)numeric_id(&This, group, "group");
334 if (This.gid == (gid_t)-1)
335 return 1;
336 }
337 } else
338 This.gid = (gid_t)-1;
339
340 if (owner != NULL) {
341#ifndef _MSC_VER
342 struct passwd *pp;
343 if ((pp = getpwnam(owner)) != NULL)
344 uid = pp->pw_uid;
345 else
346#endif
347 {
348 This.uid = (uid_t)numeric_id(&This, owner, "user");
349 if (This.uid == (uid_t)-1)
350 return 1;
351 }
352 } else
353 This.uid = (uid_t)-1;
354
355 if (This.dodir) {
356 for (; *argv != NULL; ++argv) {
357 int rc = install_dir(&This, *argv);
358 if (rc)
359 return rc;
360 }
361 return EX_OK;
362 /* NOTREACHED */
363 }
364
365 no_target = stat(to_name = argv[argc - 1], &to_sb);
366 if (!no_target && S_ISDIR(to_sb.st_mode)) {
367 for (; *argv != to_name; ++argv) {
368 int rc = install(&This, *argv, to_name, fset, iflags | DIRECTORY);
369 if (rc)
370 return rc;
371 }
372 return EX_OK;
373 }
374
375 /* can't do file1 file2 directory/file */
376 if (argc != 2) {
377 warnx(pCtx, "wrong number or types of arguments");
378 return usage(pCtx, 1);
379 }
380
381 if (!no_target) {
382 if (stat(*argv, &from_sb))
383 return err(pCtx, EX_OSERR, "%s", *argv);
384 if (!S_ISREG(to_sb.st_mode)) {
385 errno = EFTYPE;
386 return err(pCtx, EX_OSERR, "%s", to_name);
387 }
388 if (to_sb.st_dev == from_sb.st_dev &&
389 to_sb.st_dev != 0 &&
390 to_sb.st_ino == from_sb.st_ino &&
391 to_sb.st_ino != 0 &&
392 !This.hard_link_files_when_possible)
393 return errx(pCtx, EX_USAGE,
394 "%s and %s are the same file", *argv, to_name);
395 }
396 return install(&This, *argv, to_name, fset, iflags);
397}
398
399#ifdef KMK_BUILTIN_STANDALONE
400int main(int argc, char **argv, char **envp)
401{
402 KMKBUILTINCTX Ctx = { "kmk_install", NULL };
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, 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, 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, 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, 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, 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 ((gid != (gid_t)-1 && gid != to_sb.st_gid) ||
742 (uid != (uid_t)-1 && uid != to_sb.st_uid) ||
743 (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 char *p;
864
865 (void)strncpy(temp, path, tsize);
866 temp[tsize - 1] = '\0';
867 if ((p = last_slash(temp)) != NULL)
868 p++;
869 else
870 p = temp;
871 (void)strncpy(p, "INS@XXXX", &temp[tsize - 1] - p);
872 temp[tsize - 1] = '\0';
873 return (mkstemp(temp));
874}
875
876/*
877 * create_newfile --
878 * create a new file, overwriting an existing one if necessary
879 */
880int
881create_newfile(PINSTALLINSTANCE pThis, const char *path, int target, struct stat *sbp)
882{
883 char backup[MAXPATHLEN];
884 int saved_errno = 0;
885 int newfd;
886
887 if (target) {
888 /*
889 * Unlink now... avoid ETXTBSY errors later. Try to turn
890 * off the append/immutable bits -- if we fail, go ahead,
891 * it might work.
892 */
893#ifdef UF_IMMUTABLE
894 if (sbp->st_flags & NOCHANGEBITS)
895 (void)chflags(path, sbp->st_flags & ~NOCHANGEBITS);
896#endif
897
898 if (pThis->dobackup) {
899 if ( (size_t)snprintf(backup, MAXPATHLEN, "%s%s", path, pThis->suffix)
900 != strlen(path) + strlen(pThis->suffix)) {
901 errx(pThis->pCtx, EX_OSERR, "%s: backup filename too long", path);
902 errno = ENAMETOOLONG;
903 return -1;
904 }
905 (void)snprintf(backup, MAXPATHLEN, "%s%s", path, pThis->suffix);
906 if (pThis->verbose)
907 kmk_builtin_ctx_printf(pThis->pCtx, 0, "install: %s -> %s\n", path, backup);
908 if (rename(path, backup) < 0) {
909 err(pThis->pCtx, EX_OSERR, "rename: %s to %s", path, backup);
910 return -1;
911 }
912 } else
913 if (unlink(path) < 0)
914 saved_errno = errno;
915 }
916
917 newfd = open(path, O_CREAT | O_RDWR | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR);
918 if (newfd < 0 && saved_errno != 0)
919 errno = saved_errno;
920 return newfd;
921}
922
923/*
924 * Write error handler.
925 */
926static int write_error(PINSTALLINSTANCE pThis, int *ptr_to_fd, const char *to_name, int nw)
927{
928 int serrno = errno;
929 (void)close(*ptr_to_fd);
930 *ptr_to_fd = -1;
931 (void)unlink(to_name);
932 errno = nw > 0 ? EIO : serrno;
933 return err(pThis->pCtx, EX_OSERR, "%s", to_name);
934}
935
936/*
937 * Read error handler.
938 */
939static int read_error(PINSTALLINSTANCE pThis, const char *from_name, int *ptr_to_fd, const char *to_name)
940{
941 int serrno = errno;
942 (void)close(*ptr_to_fd);
943 *ptr_to_fd = -1;
944 (void)unlink(to_name);
945 errno = serrno;
946 return err(pThis->pCtx, EX_OSERR, "%s", from_name);
947}
948
949/*
950 * copy --
951 * copy from one file to another
952 */
953static int
954copy(PINSTALLINSTANCE pThis, int from_fd, const char *from_name, int *ptr_to_fd, const char *to_name)
955{
956 KBOOL fPendingCr = K_FALSE;
957 KSIZE cchDst;
958 int nr, nw;
959 char buf[MAXBSIZE];
960 int to_fd = *ptr_to_fd;
961
962 /* Rewind file descriptors. */
963 if (lseek(from_fd, (off_t)0, SEEK_SET) == (off_t)-1)
964 return err(pThis->pCtx, EX_OSERR, "lseek: %s", from_name);
965 if (lseek(to_fd, (off_t)0, SEEK_SET) == (off_t)-1)
966 return err(pThis->pCtx, EX_OSERR, "lseek: %s", to_name);
967
968 if (pThis->dos2unix == 0) {
969 /*
970 * Copy bytes, no conversion.
971 */
972 while ((nr = read(from_fd, buf, sizeof(buf))) > 0)
973 if ((nw = write(to_fd, buf, nr)) != nr)
974 return write_error(pThis, ptr_to_fd, to_name, nw);
975 } else if (pThis->dos2unix > 0) {
976 /*
977 * CRLF -> LF is a reduction, so we can work with full buffers.
978 */
979 while ((nr = read(from_fd, buf, sizeof(buf))) > 0) {
980 if ( fPendingCr
981 && buf[0] != '\n'
982 && (nw = write(to_fd, "\r", 1)) != 1)
983 return write_error(pThis, ptr_to_fd, to_name, nw);
984
985 fPendingCr = dos2unix_convert_to_unix(buf, nr, buf, &cchDst);
986
987 nw = write(to_fd, buf, cchDst);
988 if (nw != (int)cchDst)
989 return write_error(pThis, ptr_to_fd, to_name, nw);
990 }
991 } else {
992 /*
993 * LF -> CRLF is an expansion, so we work with half buffers, reading
994 * into the upper half of the buffer and expanding into the full buffer.
995 * The conversion will never expand to more than the double size.
996 *
997 * Note! We do not convert valid CRLF line endings. This gives us
998 * valid DOS text, but no round-trip conversion.
999 */
1000 char * const pchSrc = &buf[sizeof(buf) / 2];
1001 while ((nr = read(from_fd, pchSrc, sizeof(buf) / 2)) > 0) {
1002 if ( fPendingCr
1003 && pchSrc[0] != '\n'
1004 && (nw = write(to_fd, "\r", 1))!= 1)
1005 return write_error(pThis, ptr_to_fd, to_name, nw);
1006
1007 fPendingCr = dos2unix_convert_to_dos(pchSrc, nr, buf, &cchDst);
1008
1009 nw = write(to_fd, buf, cchDst);
1010 if (nw != (int)cchDst)
1011 return write_error(pThis, ptr_to_fd, to_name, nw);
1012 }
1013 }
1014
1015 /* Check for read error. */
1016 if (nr != 0)
1017 return read_error(pThis, from_name, ptr_to_fd, to_name);
1018
1019 /* When converting, we might have a pending final CR to write. */
1020 if ( fPendingCr
1021 && (nw = write(to_fd, "\r", 1))!= 1)
1022 return write_error(pThis, ptr_to_fd, to_name, nw);
1023
1024 return EX_OK;
1025}
1026
1027/*
1028 * strip --
1029 * use strip(1) to strip the target file
1030 */
1031static int
1032strip(PINSTALLINSTANCE pThis, const char *to_name)
1033{
1034#if defined(__EMX__) || defined(_MSC_VER)
1035 const char *stripbin = getenv("STRIPBIN");
1036 if (stripbin == NULL)
1037 stripbin = "strip";
1038 (void)pThis;
1039 return spawnlp(P_WAIT, stripbin, stripbin, to_name, NULL);
1040#else
1041 const char *stripbin;
1042 int serrno, status;
1043 pid_t pid;
1044
1045 pid = fork();
1046 switch (pid) {
1047 case -1:
1048 serrno = errno;
1049 (void)unlink(to_name);
1050 errno = serrno;
1051 return err(pThis->pCtx, EX_TEMPFAIL, "fork");
1052 case 0:
1053 stripbin = getenv("STRIPBIN");
1054 if (stripbin == NULL)
1055 stripbin = "strip";
1056 execlp(stripbin, stripbin, to_name, (char *)NULL);
1057 err(pThis->pCtx, EX_OSERR, "exec(%s)", stripbin);
1058 exit(EX_OSERR);
1059 default:
1060 if (waitpid(pid, &status, 0) == -1 || status) {
1061 serrno = errno;
1062 (void)unlink(to_name);
1063 errno = serrno;
1064 return err(pThis->pCtx, EX_SOFTWARE, "waitpid");
1065 /* NOTREACHED */
1066 }
1067 }
1068 return 0;
1069#endif
1070}
1071
1072/*
1073 * install_dir --
1074 * build directory heirarchy
1075 */
1076static int
1077install_dir(PINSTALLINSTANCE pThis, char *path)
1078{
1079 char *p;
1080 struct stat sb;
1081 int ch;
1082
1083 for (p = path;; ++p)
1084 if ( !*p
1085 || ( p != path
1086 && IS_SLASH(*p)
1087#if defined(_MSC_VER) /* stat("C:") fails (VC++ v10). Just skip it since it's unnecessary. */
1088 && (p - path != 2 || p[-1] != ':')
1089#endif
1090 )) {
1091 ch = *p;
1092 *p = '\0';
1093 if (stat(path, &sb)) {
1094 if (errno != ENOENT || mkdir(path, 0755) < 0) {
1095 return err(pThis->pCtx, EX_OSERR, "mkdir %s", path);
1096 /* NOTREACHED */
1097 } else if (pThis->verbose)
1098 kmk_builtin_ctx_printf(pThis->pCtx, 0, "install: mkdir %s\n", path);
1099 } else if (!S_ISDIR(sb.st_mode))
1100 return errx(pThis->pCtx, EX_OSERR, "%s exists but is not a directory", path);
1101 if (!(*p = ch))
1102 break;
1103 }
1104
1105 if ((pThis->gid != (gid_t)-1 || pThis->uid != (uid_t)-1) && chown(path, pThis->uid, pThis->gid))
1106 warn(pThis->pCtx, "chown %u:%u %s", pThis->uid, pThis->gid, path);
1107 if (chmod(path, pThis->mode))
1108 warn(pThis->pCtx, "chmod %o %s", pThis->mode, path);
1109 return EX_OK;
1110}
1111
1112/*
1113 * usage --
1114 * print a usage message and die
1115 */
1116static int
1117usage(PKMKBUILTINCTX pCtx, int fIsErr)
1118{
1119 kmk_builtin_ctx_printf(pCtx, fIsErr,
1120"usage: %s [-bCcpSsv] [--[no-]hard-link-files-when-possible]\n"
1121" [--[no-]ignore-perm-errors] [-B suffix] [-f flags] [-g group]\n"
1122" [-m mode] [-o owner] [--dos2unix|--unix2dos] file1 file2\n"
1123" or: %s [-bCcpSsv] [--[no-]ignore-perm-errors] [-B suffix] [-f flags]\n"
1124" [-g group] [-m mode] [-o owner] file1 ... fileN directory\n"
1125" or: %s -d [-v] [-g group] [-m mode] [-o owner] directory ...\n"
1126" or: %s --help\n"
1127" or: %s --version\n",
1128 pCtx->pszProgName, pCtx->pszProgName, pCtx->pszProgName,
1129 pCtx->pszProgName, pCtx->pszProgName);
1130 return EX_USAGE;
1131}
1132
1133/* figures out where the last slash or colon is. */
1134static char *
1135last_slash(const char *path)
1136{
1137#if defined(__WIN32__) || defined(__WIN64__) || defined(__OS2__)
1138 char *p = (char *)strrchr(path, '/');
1139 if (p)
1140 {
1141 char *p2 = strrchr(p, '\\');
1142 if (p2)
1143 p = p2;
1144 }
1145 else
1146 {
1147 p = (char *)strrchr(path, '\\');
1148 if (!p && isalpha(path[0]) && path[1] == ':')
1149 p = (char *)&path[1];
1150 }
1151 return p;
1152#else
1153 return strrchr(path, '/');
1154#endif
1155}
1156
1157/**
1158 * Checks if @a pszFilename actually needs dos2unix conversion.
1159 *
1160 * @returns boolean.
1161 * @param pszFilename The name of the file to check.
1162 */
1163static KBOOL needs_dos2unix_conversion(const char *pszFilename)
1164{
1165 KU32 fStyle = 0;
1166 int iErr = dos2unix_analyze_file(pszFilename, &fStyle, NULL, NULL);
1167 return iErr != 0
1168 || (fStyle & (DOS2UNIX_STYLE_MASK | DOS2UNIX_F_BINARY)) != DOS2UNIX_STYLE_UNIX;
1169}
1170
1171/**
1172 * Checks if @a pszFilename actually needs unix2dos conversion.
1173 *
1174 * @returns boolean.
1175 * @param pszFilename The name of the file to check.
1176 */
1177static KBOOL needs_unix2dos_conversion(const char *pszFilename)
1178{
1179 KU32 fStyle = 0;
1180 int iErr = dos2unix_analyze_file(pszFilename, &fStyle, NULL, NULL);
1181 return iErr != 0
1182 || (fStyle & (DOS2UNIX_STYLE_MASK | DOS2UNIX_F_BINARY)) != DOS2UNIX_STYLE_DOS;
1183}
1184
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