xref: /openbsd/usr.bin/xinstall/xinstall.c (revision 264ca280)
1 /*	$OpenBSD: xinstall.c,v 1.65 2016/05/13 17:51:15 jmc Exp $	*/
2 /*	$NetBSD: xinstall.c,v 1.9 1995/12/20 10:25:17 jonathan Exp $	*/
3 
4 /*
5  * Copyright (c) 1987, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/param.h>	/* MAXBSIZE */
34 #include <sys/wait.h>
35 #include <sys/mman.h>
36 #include <sys/stat.h>
37 
38 #include <ctype.h>
39 #include <err.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <grp.h>
43 #include <paths.h>
44 #include <pwd.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49 #include <limits.h>
50 #include <utime.h>
51 #include <libgen.h>
52 
53 #include "pathnames.h"
54 
55 #define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
56 
57 #define	DIRECTORY	0x01		/* Tell install it's a directory. */
58 #define	SETFLAGS	0x02		/* Tell install to set flags. */
59 #define	USEFSYNC	0x04		/* Tell install to use fsync(2). */
60 #define NOCHANGEBITS	(UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)
61 #define BACKUP_SUFFIX	".old"
62 
63 struct passwd *pp;
64 struct group *gp;
65 int dobackup, docompare, dodest, dodir, dopreserve, dostrip, safecopy;
66 int mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
67 char pathbuf[PATH_MAX], tempfile[PATH_MAX];
68 char *suffix = BACKUP_SUFFIX;
69 uid_t uid;
70 gid_t gid;
71 
72 void	copy(int, char *, int, char *, off_t, int);
73 int	compare(int, const char *, off_t, int, const char *, off_t);
74 void	install(char *, char *, u_long, u_int);
75 void	install_dir(char *, int);
76 void	strip(char *);
77 void	usage(void);
78 int	create_newfile(char *, struct stat *);
79 int	create_tempfile(char *, char *, size_t);
80 int	file_write(int, char *, size_t, int *, int *, int);
81 void	file_flush(int, int);
82 
83 int
84 main(int argc, char *argv[])
85 {
86 	struct stat from_sb, to_sb;
87 	void *set;
88 	u_int32_t fset;
89 	u_int iflags;
90 	int ch, no_target;
91 	char *flags, *to_name, *group = NULL, *owner = NULL;
92 
93 	iflags = 0;
94 	while ((ch = getopt(argc, argv, "B:bCcDdFf:g:m:o:pSs")) != -1)
95 		switch(ch) {
96 		case 'C':
97 			docompare = 1;
98 			break;
99 		case 'B':
100 			suffix = optarg;
101 			/* fall through; -B implies -b */
102 		case 'b':
103 			dobackup = 1;
104 			break;
105 		case 'c':
106 			/* For backwards compatibility. */
107 			break;
108 		case 'F':
109 			iflags |= USEFSYNC;
110 			break;
111 		case 'f':
112 			flags = optarg;
113 			if (strtofflags(&flags, &fset, NULL))
114 				errx(1, "%s: invalid flag", flags);
115 			iflags |= SETFLAGS;
116 			break;
117 		case 'g':
118 			group = optarg;
119 			break;
120 		case 'm':
121 			if (!(set = setmode(optarg)))
122 				errx(1, "%s: invalid file mode", optarg);
123 			mode = getmode(set, 0);
124 			free(set);
125 			break;
126 		case 'o':
127 			owner = optarg;
128 			break;
129 		case 'p':
130 			docompare = dopreserve = 1;
131 			break;
132 		case 'S':
133 			safecopy = 1;
134 			break;
135 		case 's':
136 			dostrip = 1;
137 			break;
138 		case 'D':
139 			dodest = 1;
140 			break;
141 		case 'd':
142 			dodir = 1;
143 			break;
144 		case '?':
145 		default:
146 			usage();
147 		}
148 	argc -= optind;
149 	argv += optind;
150 
151 	/* some options make no sense when creating directories */
152 	if ((safecopy || docompare || dostrip) && dodir)
153 		usage();
154 
155 	/* must have at least two arguments, except when creating directories */
156 	if (argc < 2 && !dodir)
157 		usage();
158 
159 	/* need to make a temp copy so we can compare stripped version */
160 	if (docompare && dostrip)
161 		safecopy = 1;
162 
163 	/* get group and owner id's */
164 	if (group && !(gp = getgrnam(group)) && !isdigit((unsigned char)*group))
165 		errx(1, "unknown group %s", group);
166 	gid = (group) ? ((gp) ? gp->gr_gid : (gid_t)strtoul(group, NULL, 10)) : (gid_t)-1;
167 	if (owner && !(pp = getpwnam(owner)) && !isdigit((unsigned char)*owner))
168 		errx(1, "unknown user %s", owner);
169 	uid = (owner) ? ((pp) ? pp->pw_uid : (uid_t)strtoul(owner, NULL, 10)) : (uid_t)-1;
170 
171 	if (dodir) {
172 		for (; *argv != NULL; ++argv)
173 			install_dir(*argv, mode);
174 		exit(0);
175 		/* NOTREACHED */
176 	}
177 
178 	if (dodest) {
179 		char *dest = dirname(argv[argc - 1]);
180 		if (dest == NULL)
181 			errx(1, "cannot determine dirname");
182 		/*
183 		 * When -D is passed, do not chmod the directory with the mode set for
184 		 * the target file. If more restrictive permissions are required then
185 		 * '-d -m' ought to be used instead.
186 		 */
187 		install_dir(dest, 0755);
188 	}
189 
190 	no_target = stat(to_name = argv[argc - 1], &to_sb);
191 	if (!no_target && S_ISDIR(to_sb.st_mode)) {
192 		for (; *argv != to_name; ++argv)
193 			install(*argv, to_name, fset, iflags | DIRECTORY);
194 		exit(0);
195 		/* NOTREACHED */
196 	}
197 
198 	/* can't do file1 file2 directory/file */
199 	if (argc != 2)
200 		errx(1, "Target: %s", argv[argc-1]);
201 
202 	if (!no_target) {
203 		if (stat(*argv, &from_sb))
204 			err(1, "%s", *argv);
205 		if (!S_ISREG(to_sb.st_mode))
206 			errc(1, EFTYPE, "%s", to_name);
207 		if (to_sb.st_dev == from_sb.st_dev &&
208 		    to_sb.st_ino == from_sb.st_ino)
209 			errx(1, "%s and %s are the same file", *argv, to_name);
210 	}
211 	install(*argv, to_name, fset, iflags);
212 	exit(0);
213 	/* NOTREACHED */
214 }
215 
216 /*
217  * install --
218  *	build a path name and install the file
219  */
220 void
221 install(char *from_name, char *to_name, u_long fset, u_int flags)
222 {
223 	struct stat from_sb, to_sb;
224 	struct timespec ts[2];
225 	int devnull, from_fd, to_fd, serrno, files_match = 0;
226 	char *p;
227 
228 	(void)memset((void *)&from_sb, 0, sizeof(from_sb));
229 	(void)memset((void *)&to_sb, 0, sizeof(to_sb));
230 
231 	/* If try to install NULL file to a directory, fails. */
232 	if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) {
233 		if (stat(from_name, &from_sb))
234 			err(1, "%s", from_name);
235 		if (!S_ISREG(from_sb.st_mode))
236 			errc(1, EFTYPE, "%s", from_name);
237 		/* Build the target path. */
238 		if (flags & DIRECTORY) {
239 			(void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
240 			    to_name,
241 			    (p = strrchr(from_name, '/')) ? ++p : from_name);
242 			to_name = pathbuf;
243 		}
244 		devnull = 0;
245 	} else {
246 		devnull = 1;
247 	}
248 
249 	if (stat(to_name, &to_sb) == 0) {
250 		/* Only compare against regular files. */
251 		if (docompare && !S_ISREG(to_sb.st_mode)) {
252 			docompare = 0;
253 			warnc(EFTYPE, "%s", to_name);
254 		}
255 	} else if (docompare) {
256 		/* File does not exist so silently ignore compare flag. */
257 		docompare = 0;
258 	}
259 
260 	if (!devnull) {
261 		if ((from_fd = open(from_name, O_RDONLY, 0)) < 0)
262 			err(1, "%s", from_name);
263 	}
264 
265 	if (safecopy) {
266 		to_fd = create_tempfile(to_name, tempfile, sizeof(tempfile));
267 		if (to_fd < 0)
268 			err(1, "%s", tempfile);
269 	} else if (docompare && !dostrip) {
270 		if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
271 			err(1, "%s", to_name);
272 	} else {
273 		if ((to_fd = create_newfile(to_name, &to_sb)) < 0)
274 			err(1, "%s", to_name);
275 	}
276 
277 	if (!devnull) {
278 		if (docompare && !safecopy) {
279 			files_match = !(compare(from_fd, from_name,
280 					from_sb.st_size, to_fd,
281 					to_name, to_sb.st_size));
282 
283 			/* Truncate "to" file for copy unless we match */
284 			if (!files_match) {
285 				(void)close(to_fd);
286 				if ((to_fd = create_newfile(to_name, &to_sb)) < 0)
287 					err(1, "%s", to_name);
288 			}
289 		}
290 		if (!files_match)
291 			copy(from_fd, from_name, to_fd,
292 			     safecopy ? tempfile : to_name, from_sb.st_size,
293 			     ((off_t)from_sb.st_blocks * S_BLKSIZE < from_sb.st_size));
294 	}
295 
296 	if (dostrip) {
297 		strip(safecopy ? tempfile : to_name);
298 
299 		/*
300 		 * Re-open our fd on the target, in case we used a strip
301 		 *  that does not work in-place -- like gnu binutils strip.
302 		 */
303 		close(to_fd);
304 		if ((to_fd = open(safecopy ? tempfile : to_name, O_RDONLY,
305 		     0)) < 0)
306 			err(1, "stripping %s", to_name);
307 	}
308 
309 	/*
310 	 * Compare the (possibly stripped) temp file to the target.
311 	 */
312 	if (safecopy && docompare) {
313 		int temp_fd = to_fd;
314 		struct stat temp_sb;
315 
316 		/* Re-open to_fd using the real target name. */
317 		if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
318 			err(1, "%s", to_name);
319 
320 		if (fstat(temp_fd, &temp_sb)) {
321 			serrno = errno;
322 			(void)unlink(tempfile);
323 			errc(1, serrno, "%s", tempfile);
324 		}
325 
326 		if (compare(temp_fd, tempfile, temp_sb.st_size, to_fd,
327 			    to_name, to_sb.st_size) == 0) {
328 			/*
329 			 * If target has more than one link we need to
330 			 * replace it in order to snap the extra links.
331 			 * Need to preserve target file times, though.
332 			 */
333 			if (to_sb.st_nlink != 1) {
334 				ts[0] = to_sb.st_atim;
335 				ts[1] = to_sb.st_mtim;
336 				futimens(temp_fd, ts);
337 			} else {
338 				files_match = 1;
339 				(void)unlink(tempfile);
340 			}
341 		}
342 		(void)close(to_fd);
343 		to_fd = temp_fd;
344 	}
345 
346 	/*
347 	 * Preserve the timestamp of the source file if necessary.
348 	 */
349 	if (dopreserve && !files_match) {
350 		ts[0] = from_sb.st_atim;
351 		ts[1] = from_sb.st_mtim;
352 		futimens(to_fd, ts);
353 	}
354 
355 	/*
356 	 * Set owner, group, mode for target; do the chown first,
357 	 * chown may lose the setuid bits.
358 	 */
359 	if ((gid != (gid_t)-1 || uid != (uid_t)-1) &&
360 	    fchown(to_fd, uid, gid)) {
361 		serrno = errno;
362 		(void)unlink(safecopy ? tempfile : to_name);
363 		errx(1, "%s: chown/chgrp: %s",
364 		    safecopy ? tempfile : to_name, strerror(serrno));
365 	}
366 	if (fchmod(to_fd, mode)) {
367 		serrno = errno;
368 		(void)unlink(safecopy ? tempfile : to_name);
369 		errx(1, "%s: chmod: %s", safecopy ? tempfile : to_name,
370 		    strerror(serrno));
371 	}
372 
373 	/*
374 	 * If provided a set of flags, set them, otherwise, preserve the
375 	 * flags, except for the dump flag.
376 	 */
377 	if (fchflags(to_fd,
378 	    flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) {
379 		if (errno != EOPNOTSUPP || (from_sb.st_flags & ~UF_NODUMP) != 0)
380 			warnx("%s: chflags: %s",
381 			    safecopy ? tempfile :to_name, strerror(errno));
382 	}
383 
384 	if (flags & USEFSYNC)
385 		fsync(to_fd);
386 	(void)close(to_fd);
387 	if (!devnull)
388 		(void)close(from_fd);
389 
390 	/*
391 	 * Move the new file into place if doing a safe copy
392 	 * and the files are different (or just not compared).
393 	 */
394 	if (safecopy && !files_match) {
395 		/* Try to turn off the immutable bits. */
396 		if (to_sb.st_flags & (NOCHANGEBITS))
397 			(void)chflags(to_name, to_sb.st_flags & ~(NOCHANGEBITS));
398 		if (dobackup) {
399 			char backup[PATH_MAX];
400 			(void)snprintf(backup, PATH_MAX, "%s%s", to_name,
401 			    suffix);
402 			/* It is ok for the target file not to exist. */
403 			if (rename(to_name, backup) < 0 && errno != ENOENT) {
404 				serrno = errno;
405 				unlink(tempfile);
406 				errx(1, "rename: %s to %s: %s", to_name,
407 				     backup, strerror(serrno));
408 			}
409 		}
410 		if (rename(tempfile, to_name) < 0 ) {
411 			serrno = errno;
412 			unlink(tempfile);
413 			errx(1, "rename: %s to %s: %s", tempfile,
414 			     to_name, strerror(serrno));
415 		}
416 	}
417 }
418 
419 /*
420  * copy --
421  *	copy from one file to another
422  */
423 void
424 copy(int from_fd, char *from_name, int to_fd, char *to_name, off_t size,
425     int sparse)
426 {
427 	ssize_t nr, nw;
428 	int serrno;
429 	char *p, buf[MAXBSIZE];
430 
431 	if (size == 0)
432 		return;
433 
434 	/* Rewind file descriptors. */
435 	if (lseek(from_fd, (off_t)0, SEEK_SET) == (off_t)-1)
436 		err(1, "lseek: %s", from_name);
437 	if (lseek(to_fd, (off_t)0, SEEK_SET) == (off_t)-1)
438 		err(1, "lseek: %s", to_name);
439 
440 	/*
441 	 * Mmap and write if less than 8M (the limit is so we don't totally
442 	 * trash memory on big files.  This is really a minor hack, but it
443 	 * wins some CPU back.  Sparse files need special treatment.
444 	 */
445 	if (!sparse && size <= 8 * 1048576) {
446 		size_t siz;
447 
448 		if ((p = mmap(NULL, (size_t)size, PROT_READ, MAP_PRIVATE,
449 		    from_fd, (off_t)0)) == MAP_FAILED) {
450 			serrno = errno;
451 			(void)unlink(to_name);
452 			errc(1, serrno, "%s", from_name);
453 		}
454 		madvise(p, size, MADV_SEQUENTIAL);
455 		siz = (size_t)size;
456 		if ((nw = write(to_fd, p, siz)) != siz) {
457 			serrno = errno;
458 			(void)unlink(to_name);
459 			errx(1, "%s: %s",
460 			    to_name, strerror(nw > 0 ? EIO : serrno));
461 		}
462 		(void) munmap(p, (size_t)size);
463 	} else {
464 		int sz, rem, isem = 1;
465 		struct stat sb;
466 
467 		/*
468 		 * Pass the blocksize of the file being written to the write
469 		 * routine.  if the size is zero, use the default S_BLKSIZE.
470 		 */
471 		if (fstat(to_fd, &sb) != 0 || sb.st_blksize == 0)
472 			sz = S_BLKSIZE;
473 		else
474 			sz = sb.st_blksize;
475 		rem = sz;
476 
477 		while ((nr = read(from_fd, buf, sizeof(buf))) > 0) {
478 			if (sparse)
479 				nw = file_write(to_fd, buf, nr, &rem, &isem, sz);
480 			else
481 				nw = write(to_fd, buf, nr);
482 			if (nw != nr) {
483 				serrno = errno;
484 				(void)unlink(to_name);
485 				errx(1, "%s: %s",
486 				    to_name, strerror(nw > 0 ? EIO : serrno));
487 			}
488 		}
489 		if (sparse)
490 			file_flush(to_fd, isem);
491 		if (nr != 0) {
492 			serrno = errno;
493 			(void)unlink(to_name);
494 			errc(1, serrno, "%s", from_name);
495 		}
496 	}
497 }
498 
499 /*
500  * compare --
501  *	compare two files; non-zero means files differ
502  */
503 int
504 compare(int from_fd, const char *from_name, off_t from_len, int to_fd,
505     const char *to_name, off_t to_len)
506 {
507 	caddr_t p1, p2;
508 	size_t length;
509 	off_t from_off, to_off, remainder;
510 	int dfound;
511 
512 	if (from_len == 0 && from_len == to_len)
513 		return (0);
514 
515 	if (from_len != to_len)
516 		return (1);
517 
518 	/*
519 	 * Compare the two files being careful not to mmap
520 	 * more than 8M at a time.
521 	 */
522 	from_off = to_off = (off_t)0;
523 	remainder = from_len;
524 	do {
525 		length = MINIMUM(remainder, 8 * 1048576);
526 		remainder -= length;
527 
528 		if ((p1 = mmap(NULL, length, PROT_READ, MAP_PRIVATE,
529 		    from_fd, from_off)) == MAP_FAILED)
530 			err(1, "%s", from_name);
531 		if ((p2 = mmap(NULL, length, PROT_READ, MAP_PRIVATE,
532 		    to_fd, to_off)) == MAP_FAILED)
533 			err(1, "%s", to_name);
534 		if (length) {
535 			madvise(p1, length, MADV_SEQUENTIAL);
536 			madvise(p2, length, MADV_SEQUENTIAL);
537 		}
538 
539 		dfound = memcmp(p1, p2, length);
540 
541 		(void) munmap(p1, length);
542 		(void) munmap(p2, length);
543 
544 		from_off += length;
545 		to_off += length;
546 
547 	} while (!dfound && remainder > 0);
548 
549 	return(dfound);
550 }
551 
552 /*
553  * strip --
554  *	use strip(1) to strip the target file
555  */
556 void
557 strip(char *to_name)
558 {
559 	int serrno, status;
560 	char * volatile path_strip;
561 
562 	if (issetugid() || (path_strip = getenv("STRIP")) == NULL)
563 		path_strip = _PATH_STRIP;
564 
565 	switch (vfork()) {
566 	case -1:
567 		serrno = errno;
568 		(void)unlink(to_name);
569 		errc(1, serrno, "forks");
570 	case 0:
571 		execl(path_strip, "strip", "--", to_name, (char *)NULL);
572 		warn("%s", path_strip);
573 		_exit(1);
574 	default:
575 		if (wait(&status) == -1 || !WIFEXITED(status))
576 			(void)unlink(to_name);
577 	}
578 }
579 
580 /*
581  * install_dir --
582  *	build directory hierarchy
583  */
584 void
585 install_dir(char *path, int mode)
586 {
587 	char *p;
588 	struct stat sb;
589 	int ch;
590 
591 	for (p = path;; ++p)
592 		if (!*p || (p != path && *p  == '/')) {
593 			ch = *p;
594 			*p = '\0';
595 			if (mkdir(path, 0777)) {
596 				int mkdir_errno = errno;
597 				if (stat(path, &sb)) {
598 					/* Not there; use mkdir()s errno */
599 					errc(1, mkdir_errno, "%s",
600 					    path);
601 					/* NOTREACHED */
602 				}
603 				if (!S_ISDIR(sb.st_mode)) {
604 					/* Is there, but isn't a directory */
605 					errc(1, ENOTDIR, "%s", path);
606 					/* NOTREACHED */
607 				}
608 			}
609 			if (!(*p = ch))
610 				break;
611  		}
612 
613 	if (((gid != (gid_t)-1 || uid != (uid_t)-1) && chown(path, uid, gid)) ||
614 	    chmod(path, mode)) {
615 		warn("%s", path);
616 	}
617 }
618 
619 /*
620  * usage --
621  *	print a usage message and die
622  */
623 void
624 usage(void)
625 {
626 	(void)fprintf(stderr, "\
627 usage: install [-bCcDdFpSs] [-B suffix] [-f flags] [-g group] [-m mode] [-o owner]\n	       source ... target ...\n");
628 	exit(1);
629 	/* NOTREACHED */
630 }
631 
632 /*
633  * create_tempfile --
634  *	create a temporary file based on path and open it
635  */
636 int
637 create_tempfile(char *path, char *temp, size_t tsize)
638 {
639 	char *p;
640 
641 	strlcpy(temp, path, tsize);
642 	if ((p = strrchr(temp, '/')) != NULL)
643 		p++;
644 	else
645 		p = temp;
646 	*p = '\0';
647 	strlcat(p, "INS@XXXXXXXXXX", tsize);
648 
649 	return(mkstemp(temp));
650 }
651 
652 /*
653  * create_newfile --
654  *	create a new file, overwriting an existing one if necessary
655  */
656 int
657 create_newfile(char *path, struct stat *sbp)
658 {
659 	char backup[PATH_MAX];
660 
661 	/*
662 	 * Unlink now... avoid ETXTBSY errors later.  Try and turn
663 	 * off the append/immutable bits -- if we fail, go ahead,
664 	 * it might work.
665 	 */
666 	if (sbp->st_flags & (NOCHANGEBITS))
667 		(void)chflags(path, sbp->st_flags & ~(NOCHANGEBITS));
668 
669 	if (dobackup) {
670 		(void)snprintf(backup, PATH_MAX, "%s%s", path, suffix);
671 		/* It is ok for the target file not to exist. */
672 		if (rename(path, backup) < 0 && errno != ENOENT)
673 			err(1, "rename: %s to %s (errno %d)", path, backup, errno);
674 	} else {
675 		if (unlink(path) < 0 && errno != ENOENT)
676 			err(1, "%s", path);
677 	}
678 
679 	return(open(path, O_CREAT | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR));
680 }
681 
682 /*
683  * file_write()
684  *	Write/copy a file (during copy or archive extract). This routine knows
685  *	how to copy files with lseek holes in it. (Which are read as file
686  *	blocks containing all 0's but do not have any file blocks associated
687  *	with the data). Typical examples of these are files created by dbm
688  *	variants (.pag files). While the file size of these files are huge, the
689  *	actual storage is quite small (the files are sparse). The problem is
690  *	the holes read as all zeros so are probably stored on the archive that
691  *	way (there is no way to determine if the file block is really a hole,
692  *	we only know that a file block of all zero's can be a hole).
693  *	At this writing, no major archive format knows how to archive files
694  *	with holes. However, on extraction (or during copy, -rw) we have to
695  *	deal with these files. Without detecting the holes, the files can
696  *	consume a lot of file space if just written to disk. This replacement
697  *	for write when passed the basic allocation size of a file system block,
698  *	uses lseek whenever it detects the input data is all 0 within that
699  *	file block. In more detail, the strategy is as follows:
700  *	While the input is all zero keep doing an lseek. Keep track of when we
701  *	pass over file block boundaries. Only write when we hit a non zero
702  *	input. once we have written a file block, we continue to write it to
703  *	the end (we stop looking at the input). When we reach the start of the
704  *	next file block, start checking for zero blocks again. Working on file
705  *	block boundaries significantly reduces the overhead when copying files
706  *	that are NOT very sparse. This overhead (when compared to a write) is
707  *	almost below the measurement resolution on many systems. Without it,
708  *	files with holes cannot be safely copied. It does has a side effect as
709  *	it can put holes into files that did not have them before, but that is
710  *	not a problem since the file contents are unchanged (in fact it saves
711  *	file space). (Except on paging files for diskless clients. But since we
712  *	cannot determine one of those file from here, we ignore them). If this
713  *	ever ends up on a system where CTG files are supported and the holes
714  *	are not desired, just do a conditional test in those routines that
715  *	call file_write() and have it call write() instead. BEFORE CLOSING THE
716  *	FILE, make sure to call file_flush() when the last write finishes with
717  *	an empty block. A lot of file systems will not create an lseek hole at
718  *	the end. In this case we drop a single 0 at the end to force the
719  *	trailing 0's in the file.
720  *	---Parameters---
721  *	rem: how many bytes left in this file system block
722  *	isempt: have we written to the file block yet (is it empty)
723  *	sz: basic file block allocation size
724  *	cnt: number of bytes on this write
725  *	str: buffer to write
726  * Return:
727  *	number of bytes written, -1 on write (or lseek) error.
728  */
729 
730 int
731 file_write(int fd, char *str, size_t cnt, int *rem, int *isempt, int sz)
732 {
733 	char *pt;
734 	char *end;
735 	size_t wcnt;
736 	char *st = str;
737 
738 	/*
739 	 * while we have data to process
740 	 */
741 	while (cnt) {
742 		if (!*rem) {
743 			/*
744 			 * We are now at the start of file system block again
745 			 * (or what we think one is...). start looking for
746 			 * empty blocks again
747 			 */
748 			*isempt = 1;
749 			*rem = sz;
750 		}
751 
752 		/*
753 		 * only examine up to the end of the current file block or
754 		 * remaining characters to write, whatever is smaller
755 		 */
756 		wcnt = MINIMUM(cnt, *rem);
757 		cnt -= wcnt;
758 		*rem -= wcnt;
759 		if (*isempt) {
760 			/*
761 			 * have not written to this block yet, so we keep
762 			 * looking for zero's
763 			 */
764 			pt = st;
765 			end = st + wcnt;
766 
767 			/*
768 			 * look for a zero filled buffer
769 			 */
770 			while ((pt < end) && (*pt == '\0'))
771 				++pt;
772 
773 			if (pt == end) {
774 				/*
775 				 * skip, buf is empty so far
776 				 */
777 				if (lseek(fd, (off_t)wcnt, SEEK_CUR) < 0) {
778 					warn("lseek");
779 					return(-1);
780 				}
781 				st = pt;
782 				continue;
783 			}
784 			/*
785 			 * drat, the buf is not zero filled
786 			 */
787 			*isempt = 0;
788 		}
789 
790 		/*
791 		 * have non-zero data in this file system block, have to write
792 		 */
793 		if (write(fd, st, wcnt) != wcnt) {
794 			warn("write");
795 			return(-1);
796 		}
797 		st += wcnt;
798 	}
799 	return(st - str);
800 }
801 
802 /*
803  * file_flush()
804  *	when the last file block in a file is zero, many file systems will not
805  *	let us create a hole at the end. To get the last block with zeros, we
806  *	write the last BYTE with a zero (back up one byte and write a zero).
807  */
808 void
809 file_flush(int fd, int isempt)
810 {
811 	static char blnk[] = "\0";
812 
813 	/*
814 	 * silly test, but make sure we are only called when the last block is
815 	 * filled with all zeros.
816 	 */
817 	if (!isempt)
818 		return;
819 
820 	/*
821 	 * move back one byte and write a zero
822 	 */
823 	if (lseek(fd, (off_t)-1, SEEK_CUR) < 0) {
824 		warn("Failed seek on file");
825 		return;
826 	}
827 
828 	if (write(fd, blnk, 1) < 0)
829 		warn("Failed write to file");
830 	return;
831 }
832