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