VirtualBox

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

Last change on this file since 3387 was 3241, checked in by bird, 5 years ago

kmk_install: Fixed create_tempfile (glibc wants exactly 6 X'es) and did proper overflow handling.

  • 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
398int main(int argc, char **argv, char **envp)
399{
400 KMKBUILTINCTX Ctx = { "kmk_install", NULL };
401 return kmk_builtin_install(argc, argv, envp, &Ctx);
402}
403#endif
404
405static u_long
406numeric_id(PINSTALLINSTANCE pThis, const char *name, const char *type)
407{
408 u_long val;
409 char *ep;
410
411 /*
412 * XXX
413 * We know that uid_t's and gid_t's are unsigned longs.
414 */
415 errno = 0;
416 val = strtoul(name, &ep, 10);
417 if (errno)
418 return err(pThis->pCtx, -1, "%s", name);
419 if (*ep != '\0')
420 return errx(pThis->pCtx, -1, "unknown %s %s", type, name);
421 return (val);
422}
423
424/*
425 * install --
426 * build a path name and install the file
427 */
428static int
429install(PINSTALLINSTANCE pThis, const char *from_name, const char *to_name, u_long fset, u_int flags)
430{
431 struct stat from_sb, temp_sb, to_sb;
432 struct timeval tvb[2];
433 int devnull, files_match, from_fd, serrno, target;
434 int tempcopy, temp_fd, to_fd;
435 char backup[MAXPATHLEN], *p, pathbuf[MAXPATHLEN], tempfile[MAXPATHLEN];
436 int rc = EX_OK;
437
438 files_match = 0;
439 from_fd = -1;
440 to_fd = -1;
441 temp_fd = -1;
442
443 /* If try to install NULL file to a directory, fails. */
444 if (flags & DIRECTORY
445#if defined(__EMX__) || defined(_MSC_VER)
446 || ( stricmp(from_name, _PATH_DEVNULL)
447 && stricmp(from_name, "nul")
448# ifdef __EMX__
449 && stricmp(from_name, "/dev/nul")
450# endif
451 )
452#else
453 || strcmp(from_name, _PATH_DEVNULL)
454#endif
455 ) {
456 if (stat(from_name, &from_sb))
457 return err(pThis->pCtx, EX_OSERR, "%s", from_name);
458 if (!S_ISREG(from_sb.st_mode)) {
459 errno = EFTYPE;
460 return err(pThis->pCtx, EX_OSERR, "%s", from_name);
461 }
462 /* Build the target path. */
463 if (flags & DIRECTORY) {
464 (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
465 to_name,
466 (p = last_slash(from_name)) ? ++p : from_name);
467 to_name = pathbuf;
468 }
469 devnull = 0;
470 } else {
471 devnull = 1;
472 }
473
474 target = stat(to_name, &to_sb) == 0;
475
476 /* Only install to regular files. */
477 if (target && !S_ISREG(to_sb.st_mode)) {
478 errno = EFTYPE;
479 warn(pThis->pCtx, "%s", to_name);
480 return EX_OK;
481 }
482
483 /* Only copy safe if the target exists. */
484 tempcopy = pThis->safecopy && target;
485
486 /* Try hard linking if wanted and possible. */
487 if (pThis->hard_link_files_when_possible)
488 {
489#ifdef KBUILD_OS_OS2
490 const char *why_not = "not supported on OS/2";
491#else
492 const char *why_not = NULL;
493 if (devnull) {
494 why_not = "/dev/null";
495 } else if (pThis->dostrip) {
496 why_not = "strip (-s)";
497 } else if (pThis->docompare) {
498 why_not = "compare (-C)";
499 } else if (pThis->dobackup) {
500 why_not = "backup (-b/-B)";
501 } else if (pThis->safecopy) {
502 why_not = "safe copy (-S)";
503 } else if (lstat(from_name, &temp_sb)) {
504 why_not = "lstat on source failed";
505 } else if (S_ISLNK(temp_sb.st_mode)) {
506 why_not = "symlink";
507 } else if (!S_ISREG(temp_sb.st_mode)) {
508 why_not = "not regular file";
509# if defined(KBUILD_OS_WINDOWS) || defined(KBUILD_OS_OS2)
510 } else if ((pThis->mode & S_IWUSR) != (from_sb.st_mode & S_IWUSR)) {
511# else
512 } else if (pThis->mode != (from_sb.st_mode & ALLPERMS)) {
513# endif
514 kmk_builtin_ctx_printf(pThis->pCtx, 0,
515 "install: warning: Not hard linking, mode differs: 0%03o, desires 0%03o\n"
516 "install: src path '%s'\n"
517 "install: dst path '%s'\n",
518 (from_sb.st_mode & ALLPERMS), pThis->mode, from_name, to_name);
519 why_not = NULL;
520 } else if (pThis->uid != (uid_t)-1 && pThis->uid != from_sb.st_uid) {
521 why_not = "uid mismatch";
522 } else if (pThis->gid != (gid_t)-1 && pThis->gid != from_sb.st_gid) {
523 why_not = "gid mismatch";
524 } else if (pThis->dos2unix > 0 && needs_dos2unix_conversion(from_name)) {
525 why_not = "dos2unix";
526 } else if (pThis->dos2unix < 0 && needs_unix2dos_conversion(from_name)) {
527 why_not = "unix2dos";
528 } else {
529 int rcLink = link(from_name, to_name);
530 if (rcLink != 0 && errno == EEXIST) {
531 unlink(to_name);
532 rcLink = link(from_name, to_name);
533 }
534 if (rcLink == 0) {
535 if (pThis->verbose)
536 kmk_builtin_ctx_printf(pThis->pCtx, 0,
537 "install: %s -> %s (hardlinked)\n", from_name, to_name);
538 goto l_done;
539 }
540 if (pThis->verbose)
541 kmk_builtin_ctx_printf(pThis->pCtx, 0, "install: hard linking '%s' to '%s' failed: %s\n",
542 to_name, from_name, strerror(errno));
543 why_not = NULL;
544 }
545#endif
546 if (pThis->verbose && why_not)
547 kmk_builtin_ctx_printf(pThis->pCtx, 0, "install: not hard linking '%s' to '%s' because: %s\n",
548 to_name, from_name, why_not);
549
550 /* Can't hard link or we failed, continue as nothing happend. */
551 }
552
553 if (!devnull && (from_fd = open(from_name, O_RDONLY | O_BINARY | KMK_OPEN_NO_INHERIT, 0)) < 0)
554 return err(pThis->pCtx, EX_OSERR, "%s", from_name);
555
556 /* If we don't strip, we can compare first. */
557 if (pThis->docompare && !pThis->dostrip && target) {
558 if ((to_fd = open(to_name, O_RDONLY | O_BINARY | KMK_OPEN_NO_INHERIT, 0)) < 0) {
559 rc = err(pThis->pCtx, EX_OSERR, "%s", to_name);
560 goto l_done;
561 }
562 if (devnull)
563 files_match = to_sb.st_size == 0;
564 else
565 files_match = !compare(from_fd, (size_t)from_sb.st_size,
566 to_fd, (size_t)to_sb.st_size);
567
568 /* Close "to" file unless we match. */
569 if (!files_match) {
570 (void)close(to_fd);
571 to_fd = -1;
572 }
573 }
574
575 if (!files_match) {
576 if (tempcopy) {
577 to_fd = create_tempfile(to_name, tempfile,
578 sizeof(tempfile));
579 if (to_fd < 0) {
580 rc = err(pThis->pCtx, EX_OSERR, "%s", tempfile);
581 goto l_done;
582 }
583 } else {
584 if ((to_fd = create_newfile(pThis, to_name, target, &to_sb)) < 0) {
585 rc = err(pThis->pCtx, EX_OSERR, "%s", to_name);
586 goto l_done;
587 }
588 if (pThis->verbose)
589 kmk_builtin_ctx_printf(pThis->pCtx, 0, "install: %s -> %s\n", from_name, to_name);
590 }
591 if (!devnull) {
592 rc = copy(pThis, from_fd, from_name, &to_fd, tempcopy ? tempfile : to_name);
593 if (rc)
594 goto l_done;
595 }
596 }
597
598 if (pThis->dostrip) {
599#if defined(__EMX__) || defined(_MSC_VER)
600 /* close before we strip. */
601 close(to_fd);
602 to_fd = -1;
603#endif
604 rc = strip(pThis, tempcopy ? tempfile : to_name);
605 if (rc)
606 goto l_done;
607
608 /*
609 * Re-open our fd on the target, in case we used a strip
610 * that does not work in-place -- like GNU binutils strip.
611 */
612#if !defined(__EMX__) && !defined(_MSC_VER)
613 close(to_fd);
614#endif
615 to_fd = open(tempcopy ? tempfile : to_name, O_RDONLY | O_BINARY | KMK_OPEN_NO_INHERIT, 0);
616 if (to_fd < 0) {
617 rc = err(pThis->pCtx, EX_OSERR, "stripping %s", to_name);
618 goto l_done;
619 }
620 }
621
622 /*
623 * Compare the stripped temp file with the target.
624 */
625 if (pThis->docompare && pThis->dostrip && target) {
626 temp_fd = to_fd;
627
628 /* Re-open to_fd using the real target name. */
629 if ((to_fd = open(to_name, O_RDONLY | O_BINARY | KMK_OPEN_NO_INHERIT, 0)) < 0) {
630 rc = err(pThis->pCtx, EX_OSERR, "%s", to_name);
631 goto l_done;
632 }
633
634 if (fstat(temp_fd, &temp_sb)) {
635 serrno = errno;
636 (void)unlink(tempfile);
637 errno = serrno;
638 rc = err(pThis->pCtx, EX_OSERR, "%s", tempfile);
639 goto l_done;
640 }
641
642 if (compare(temp_fd, (size_t)temp_sb.st_size,
643 to_fd, (size_t)to_sb.st_size) == 0) {
644 /*
645 * If target has more than one link we need to
646 * replace it in order to snap the extra links.
647 * Need to preserve target file times, though.
648 */
649#if !defined(_MSC_VER) && !defined(__EMX__)
650 if (to_sb.st_nlink != 1) {
651 tvb[0].tv_sec = to_sb.st_atime;
652 tvb[0].tv_usec = 0;
653 tvb[1].tv_sec = to_sb.st_mtime;
654 tvb[1].tv_usec = 0;
655 (void)utimes(tempfile, tvb);
656 } else
657#endif
658 {
659
660 files_match = 1;
661 (void)unlink(tempfile);
662 }
663 (void) close(temp_fd);
664 temp_fd = -1;
665 }
666 }
667
668 /*
669 * Move the new file into place if doing a safe copy
670 * and the files are different (or just not compared).
671 */
672 if (tempcopy && !files_match) {
673#ifdef UF_IMMUTABLE
674 /* Try to turn off the immutable bits. */
675 if (to_sb.st_flags & NOCHANGEBITS)
676 (void)chflags(to_name, to_sb.st_flags & ~NOCHANGEBITS);
677#endif
678 if (pThis->dobackup) {
679 if ( (size_t)snprintf(backup, MAXPATHLEN, "%s%s", to_name, pThis->suffix)
680 != strlen(to_name) + strlen(pThis->suffix)) {
681 unlink(tempfile);
682 rc = errx(pThis->pCtx, EX_OSERR, "%s: backup filename too long",
683 to_name);
684 goto l_done;
685 }
686 if (pThis->verbose)
687 kmk_builtin_ctx_printf(pThis->pCtx, 0, "install: %s -> %s\n", to_name, backup);
688 if (rename(to_name, backup) < 0) {
689 serrno = errno;
690 unlink(tempfile);
691 errno = serrno;
692 rc = err(pThis->pCtx, EX_OSERR, "rename: %s to %s", to_name,
693 backup);
694 goto l_done;
695 }
696 }
697 if (pThis->verbose)
698 kmk_builtin_ctx_printf(pThis->pCtx, 0, "install: %s -> %s\n", from_name, to_name);
699 if (rename(tempfile, to_name) < 0) {
700 serrno = errno;
701 unlink(tempfile);
702 errno = serrno;
703 rc = err(pThis->pCtx, EX_OSERR, "rename: %s to %s", tempfile, to_name);
704 goto l_done;
705 }
706
707 /* Re-open to_fd so we aren't hosed by the rename(2). */
708 (void) close(to_fd);
709 if ((to_fd = open(to_name, O_RDONLY | O_BINARY | KMK_OPEN_NO_INHERIT, 0)) < 0) {
710 rc = err(pThis->pCtx, EX_OSERR, "%s", to_name);
711 goto l_done;
712 }
713 }
714
715 /*
716 * Preserve the timestamp of the source file if necessary.
717 */
718 if (pThis->dopreserve && !files_match && !devnull) {
719 tvb[0].tv_sec = from_sb.st_atime;
720 tvb[0].tv_usec = 0;
721 tvb[1].tv_sec = from_sb.st_mtime;
722 tvb[1].tv_usec = 0;
723 (void)utimes(to_name, tvb);
724 }
725
726 if (fstat(to_fd, &to_sb) == -1) {
727 serrno = errno;
728 (void)unlink(to_name);
729 errno = serrno;
730 rc = err(pThis->pCtx, EX_OSERR, "%s", to_name);
731 goto l_done;
732 }
733
734 /*
735 * Set owner, group, mode for target; do the chown first,
736 * chown may lose the setuid bits.
737 */
738#ifdef UF_IMMUTABLE
739 if ((pThis->gid != (gid_t)-1 && pThis->gid != to_sb.st_gid) ||
740 (pThis->uid != (uid_t)-1 && pThis->uid != to_sb.st_uid) ||
741 (pThis->mode != (to_sb.st_mode & ALLPERMS))) {
742 /* Try to turn off the immutable bits. */
743 if (to_sb.st_flags & NOCHANGEBITS)
744 (void)fchflags(to_fd, to_sb.st_flags & ~NOCHANGEBITS);
745 }
746#endif
747
748 if ((pThis->gid != (gid_t)-1 && pThis->gid != to_sb.st_gid) ||
749 (pThis->uid != (uid_t)-1 && pThis->uid != to_sb.st_uid))
750 if (fchown(to_fd, pThis->uid, pThis->gid) == -1) {
751 if (errno == EPERM && pThis->ignore_perm_errors) {
752 warn(pThis->pCtx, "%s: ignoring chown uid=%d gid=%d failure",
753 to_name, (int)pThis->uid, (int)pThis->gid);
754 } else {
755 serrno = errno;
756 (void)unlink(to_name);
757 errno = serrno;
758 rc = err(pThis->pCtx, EX_OSERR,"%s: chown/chgrp", to_name);
759 goto l_done;
760 }
761 }
762
763 if (pThis->mode != (to_sb.st_mode & ALLPERMS))
764 if (fchmod(to_fd, pThis->mode)) {
765 serrno = errno;
766 if (serrno == EPERM && pThis->ignore_perm_errors) {
767 fchmod(to_fd, pThis->mode & (ALLPERMS & ~0007000));
768 errno = errno;
769 warn(pThis->pCtx, "%s: ignoring chmod 0%o failure", to_name, (int)(pThis->mode & ALLPERMS));
770 } else {
771 serrno = errno;
772 (void)unlink(to_name);
773 errno = serrno;
774 rc = err(pThis->pCtx, EX_OSERR, "%s: chmod", to_name);
775 goto l_done;
776 }
777 }
778
779 /*
780 * If provided a set of flags, set them, otherwise, preserve the
781 * flags, except for the dump flag.
782 * NFS does not support flags. Ignore EOPNOTSUPP flags if we're just
783 * trying to turn off UF_NODUMP. If we're trying to set real flags,
784 * then warn if the the fs doesn't support it, otherwise fail.
785 */
786#ifdef UF_IMMUTABLE
787 if ( !devnull
788 && (flags & SETFLAGS || (from_sb.st_flags & ~UF_NODUMP) != to_sb.st_flags)
789 && fchflags(to_fd, flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) {
790 if (flags & SETFLAGS) {
791 if (errno == EOPNOTSUPP)
792 warn(pThis->pCtx, "%s: chflags", to_name);
793 else {
794 serrno = errno;
795 (void)unlink(to_name);
796 errno = serrno;
797 rc = err(pThis->pCtx, EX_OSERR, "%s: chflags", to_name);
798 goto l_done;
799 }
800 }
801 }
802#endif
803
804l_done:
805 if (to_fd >= 0)
806 (void)close(to_fd);
807 if (temp_fd >= 0)
808 (void)close(temp_fd);
809 if (from_fd >= 0 && !devnull)
810 (void)close(from_fd);
811 return rc;
812}
813
814/*
815 * compare --
816 * compare two files; non-zero means files differ
817 */
818static int
819compare(int from_fd, size_t from_len, int to_fd, size_t to_len)
820{
821 char buf1[MAXBSIZE];
822 char buf2[MAXBSIZE];
823 int n1, n2;
824 int rv;
825
826 if (from_len != to_len)
827 return 1;
828
829 if (from_len <= MAX_CMP_SIZE) {
830 rv = 0;
831 lseek(from_fd, 0, SEEK_SET);
832 lseek(to_fd, 0, SEEK_SET);
833 while (rv == 0) {
834 n1 = read(from_fd, buf1, sizeof(buf1));
835 if (n1 == 0)
836 break; /* EOF */
837 else if (n1 > 0) {
838 n2 = read(to_fd, buf2, n1);
839 if (n2 == n1)
840 rv = memcmp(buf1, buf2, n1);
841 else
842 rv = 1; /* out of sync */
843 } else
844 rv = 1; /* read failure */
845 }
846 lseek(from_fd, 0, SEEK_SET);
847 lseek(to_fd, 0, SEEK_SET);
848 } else
849 rv = 1; /* don't bother in this case */
850
851 return rv;
852}
853
854/*
855 * create_tempfile --
856 * create a temporary file based on path and open it
857 */
858int
859create_tempfile(const char *path, char *temp, size_t tsize)
860{
861 static char s_szTemplate[] = "INS@XXXXXX";
862 const char *p = last_slash(path);
863 if (p) {
864 size_t cchDir = ++p - path;
865 if (cchDir + sizeof(s_szTemplate) <= tsize) {
866 memcpy(temp, path, cchDir);
867 memcpy(&temp[cchDir], s_szTemplate, sizeof(s_szTemplate));
868 } else
869 return EOVERFLOW;
870 } else if (tsize >= sizeof(s_szTemplate))
871 memcpy(temp, s_szTemplate, sizeof(s_szTemplate));
872 else
873 return EOVERFLOW;
874
875 return (mkstemp(temp));
876}
877
878/*
879 * create_newfile --
880 * create a new file, overwriting an existing one if necessary
881 */
882int
883create_newfile(PINSTALLINSTANCE pThis, const char *path, int target, struct stat *sbp)
884{
885 char backup[MAXPATHLEN];
886 int saved_errno = 0;
887 int newfd;
888
889 if (target) {
890 /*
891 * Unlink now... avoid ETXTBSY errors later. Try to turn
892 * off the append/immutable bits -- if we fail, go ahead,
893 * it might work.
894 */
895#ifdef UF_IMMUTABLE
896 if (sbp->st_flags & NOCHANGEBITS)
897 (void)chflags(path, sbp->st_flags & ~NOCHANGEBITS);
898#endif
899
900 if (pThis->dobackup) {
901 if ( (size_t)snprintf(backup, MAXPATHLEN, "%s%s", path, pThis->suffix)
902 != strlen(path) + strlen(pThis->suffix)) {
903 errx(pThis->pCtx, EX_OSERR, "%s: backup filename too long", path);
904 errno = ENAMETOOLONG;
905 return -1;
906 }
907 (void)snprintf(backup, MAXPATHLEN, "%s%s", path, pThis->suffix);
908 if (pThis->verbose)
909 kmk_builtin_ctx_printf(pThis->pCtx, 0, "install: %s -> %s\n", path, backup);
910 if (rename(path, backup) < 0) {
911 err(pThis->pCtx, EX_OSERR, "rename: %s to %s", path, backup);
912 return -1;
913 }
914 } else
915 if (unlink(path) < 0)
916 saved_errno = errno;
917 }
918
919 newfd = open(path, O_CREAT | O_RDWR | O_TRUNC | O_BINARY | KMK_OPEN_NO_INHERIT, S_IRUSR | S_IWUSR);
920 if (newfd < 0 && saved_errno != 0)
921 errno = saved_errno;
922 return newfd;
923}
924
925/*
926 * Write error handler.
927 */
928static int write_error(PINSTALLINSTANCE pThis, int *ptr_to_fd, const char *to_name, int nw)
929{
930 int serrno = errno;
931 (void)close(*ptr_to_fd);
932 *ptr_to_fd = -1;
933 (void)unlink(to_name);
934 errno = nw > 0 ? EIO : serrno;
935 return err(pThis->pCtx, EX_OSERR, "%s", to_name);
936}
937
938/*
939 * Read error handler.
940 */
941static int read_error(PINSTALLINSTANCE pThis, const char *from_name, int *ptr_to_fd, const char *to_name)
942{
943 int serrno = errno;
944 (void)close(*ptr_to_fd);
945 *ptr_to_fd = -1;
946 (void)unlink(to_name);
947 errno = serrno;
948 return err(pThis->pCtx, EX_OSERR, "%s", from_name);
949}
950
951/*
952 * copy --
953 * copy from one file to another
954 */
955static int
956copy(PINSTALLINSTANCE pThis, int from_fd, const char *from_name, int *ptr_to_fd, const char *to_name)
957{
958 KBOOL fPendingCr = K_FALSE;
959 KSIZE cchDst;
960 int nr, nw;
961 char buf[MAXBSIZE];
962 int to_fd = *ptr_to_fd;
963
964 /* Rewind file descriptors. */
965 if (lseek(from_fd, (off_t)0, SEEK_SET) == (off_t)-1)
966 return err(pThis->pCtx, EX_OSERR, "lseek: %s", from_name);
967 if (lseek(to_fd, (off_t)0, SEEK_SET) == (off_t)-1)
968 return err(pThis->pCtx, EX_OSERR, "lseek: %s", to_name);
969
970 if (pThis->dos2unix == 0) {
971 /*
972 * Copy bytes, no conversion.
973 */
974 while ((nr = read(from_fd, buf, sizeof(buf))) > 0)
975 if ((nw = write(to_fd, buf, nr)) != nr)
976 return write_error(pThis, ptr_to_fd, to_name, nw);
977 } else if (pThis->dos2unix > 0) {
978 /*
979 * CRLF -> LF is a reduction, so we can work with full buffers.
980 */
981 while ((nr = read(from_fd, buf, sizeof(buf))) > 0) {
982 if ( fPendingCr
983 && buf[0] != '\n'
984 && (nw = write(to_fd, "\r", 1)) != 1)
985 return write_error(pThis, ptr_to_fd, to_name, nw);
986
987 fPendingCr = dos2unix_convert_to_unix(buf, nr, buf, &cchDst);
988
989 nw = write(to_fd, buf, cchDst);
990 if (nw != (int)cchDst)
991 return write_error(pThis, ptr_to_fd, to_name, nw);
992 }
993 } else {
994 /*
995 * LF -> CRLF is an expansion, so we work with half buffers, reading
996 * into the upper half of the buffer and expanding into the full buffer.
997 * The conversion will never expand to more than the double size.
998 *
999 * Note! We do not convert valid CRLF line endings. This gives us
1000 * valid DOS text, but no round-trip conversion.
1001 */
1002 char * const pchSrc = &buf[sizeof(buf) / 2];
1003 while ((nr = read(from_fd, pchSrc, sizeof(buf) / 2)) > 0) {
1004 if ( fPendingCr
1005 && pchSrc[0] != '\n'
1006 && (nw = write(to_fd, "\r", 1))!= 1)
1007 return write_error(pThis, ptr_to_fd, to_name, nw);
1008
1009 fPendingCr = dos2unix_convert_to_dos(pchSrc, nr, buf, &cchDst);
1010
1011 nw = write(to_fd, buf, cchDst);
1012 if (nw != (int)cchDst)
1013 return write_error(pThis, ptr_to_fd, to_name, nw);
1014 }
1015 }
1016
1017 /* Check for read error. */
1018 if (nr != 0)
1019 return read_error(pThis, from_name, ptr_to_fd, to_name);
1020
1021 /* When converting, we might have a pending final CR to write. */
1022 if ( fPendingCr
1023 && (nw = write(to_fd, "\r", 1))!= 1)
1024 return write_error(pThis, ptr_to_fd, to_name, nw);
1025
1026 return EX_OK;
1027}
1028
1029/*
1030 * strip --
1031 * use strip(1) to strip the target file
1032 */
1033static int
1034strip(PINSTALLINSTANCE pThis, const char *to_name)
1035{
1036#if defined(__EMX__) || defined(_MSC_VER)
1037 const char *stripbin = getenv("STRIPBIN");
1038 if (stripbin == NULL)
1039 stripbin = "strip";
1040 (void)pThis;
1041 return spawnlp(P_WAIT, stripbin, stripbin, to_name, NULL);
1042#else
1043 const char *stripbin;
1044 int serrno, status;
1045 pid_t pid;
1046
1047 pid = fork();
1048 switch (pid) {
1049 case -1:
1050 serrno = errno;
1051 (void)unlink(to_name);
1052 errno = serrno;
1053 return err(pThis->pCtx, EX_TEMPFAIL, "fork");
1054 case 0:
1055 stripbin = getenv("STRIPBIN");
1056 if (stripbin == NULL)
1057 stripbin = "strip";
1058 execlp(stripbin, stripbin, to_name, (char *)NULL);
1059 err(pThis->pCtx, EX_OSERR, "exec(%s)", stripbin);
1060 exit(EX_OSERR);
1061 default:
1062 if (waitpid(pid, &status, 0) == -1 || status) {
1063 serrno = errno;
1064 (void)unlink(to_name);
1065 errno = serrno;
1066 return err(pThis->pCtx, EX_SOFTWARE, "waitpid");
1067 /* NOTREACHED */
1068 }
1069 }
1070 return 0;
1071#endif
1072}
1073
1074/*
1075 * install_dir --
1076 * build directory heirarchy
1077 */
1078static int
1079install_dir(PINSTALLINSTANCE pThis, char *path)
1080{
1081 char *p;
1082 struct stat sb;
1083 int ch;
1084
1085 for (p = path;; ++p)
1086 if ( !*p
1087 || ( p != path
1088 && IS_SLASH(*p)
1089#if defined(_MSC_VER) /* stat("C:") fails (VC++ v10). Just skip it since it's unnecessary. */
1090 && (p - path != 2 || p[-1] != ':')
1091#endif
1092 )) {
1093 ch = *p;
1094 *p = '\0';
1095 if (stat(path, &sb)) {
1096 if (errno != ENOENT || mkdir(path, 0755) < 0) {
1097 return err(pThis->pCtx, EX_OSERR, "mkdir %s", path);
1098 /* NOTREACHED */
1099 } else if (pThis->verbose)
1100 kmk_builtin_ctx_printf(pThis->pCtx, 0, "install: mkdir %s\n", path);
1101 } else if (!S_ISDIR(sb.st_mode))
1102 return errx(pThis->pCtx, EX_OSERR, "%s exists but is not a directory", path);
1103 if (!(*p = ch))
1104 break;
1105 }
1106
1107 if ((pThis->gid != (gid_t)-1 || pThis->uid != (uid_t)-1) && chown(path, pThis->uid, pThis->gid))
1108 warn(pThis->pCtx, "chown %u:%u %s", pThis->uid, pThis->gid, path);
1109 if (chmod(path, pThis->mode))
1110 warn(pThis->pCtx, "chmod %o %s", pThis->mode, path);
1111 return EX_OK;
1112}
1113
1114/*
1115 * usage --
1116 * print a usage message and die
1117 */
1118static int
1119usage(PKMKBUILTINCTX pCtx, int fIsErr)
1120{
1121 kmk_builtin_ctx_printf(pCtx, fIsErr,
1122"usage: %s [-bCcpSsv] [--[no-]hard-link-files-when-possible]\n"
1123" [--[no-]ignore-perm-errors] [-B suffix] [-f flags] [-g group]\n"
1124" [-m mode] [-o owner] [--dos2unix|--unix2dos] file1 file2\n"
1125" or: %s [-bCcpSsv] [--[no-]ignore-perm-errors] [-B suffix] [-f flags]\n"
1126" [-g group] [-m mode] [-o owner] file1 ... fileN directory\n"
1127" or: %s -d [-v] [-g group] [-m mode] [-o owner] directory ...\n"
1128" or: %s --help\n"
1129" or: %s --version\n",
1130 pCtx->pszProgName, pCtx->pszProgName, pCtx->pszProgName,
1131 pCtx->pszProgName, pCtx->pszProgName);
1132 return EX_USAGE;
1133}
1134
1135/* figures out where the last slash or colon is. */
1136static char *
1137last_slash(const char *path)
1138{
1139#if defined(__WIN32__) || defined(__WIN64__) || defined(__OS2__)
1140 char *p = (char *)strrchr(path, '/');
1141 if (p)
1142 {
1143 char *p2 = strrchr(p, '\\');
1144 if (p2)
1145 p = p2;
1146 }
1147 else
1148 {
1149 p = (char *)strrchr(path, '\\');
1150 if (!p && isalpha(path[0]) && path[1] == ':')
1151 p = (char *)&path[1];
1152 }
1153 return p;
1154#else
1155 return strrchr(path, '/');
1156#endif
1157}
1158
1159/**
1160 * Checks if @a pszFilename actually needs dos2unix conversion.
1161 *
1162 * @returns boolean.
1163 * @param pszFilename The name of the file to check.
1164 */
1165static KBOOL needs_dos2unix_conversion(const char *pszFilename)
1166{
1167 KU32 fStyle = 0;
1168 int iErr = dos2unix_analyze_file(pszFilename, &fStyle, NULL, NULL);
1169 return iErr != 0
1170 || (fStyle & (DOS2UNIX_STYLE_MASK | DOS2UNIX_F_BINARY)) != DOS2UNIX_STYLE_UNIX;
1171}
1172
1173/**
1174 * Checks if @a pszFilename actually needs unix2dos conversion.
1175 *
1176 * @returns boolean.
1177 * @param pszFilename The name of the file to check.
1178 */
1179static KBOOL needs_unix2dos_conversion(const char *pszFilename)
1180{
1181 KU32 fStyle = 0;
1182 int iErr = dos2unix_analyze_file(pszFilename, &fStyle, NULL, NULL);
1183 return iErr != 0
1184 || (fStyle & (DOS2UNIX_STYLE_MASK | DOS2UNIX_F_BINARY)) != DOS2UNIX_STYLE_DOS;
1185}
1186
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use