Bug Summary

File:src/usr.bin/xinstall/xinstall.c
Warning:line 545, column 17
Call to function 'vfork' is insecure as it can lead to denial of service situations in the parent process. Replace calls to vfork with calls to the safer 'posix_spawn' function

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name xinstall.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/xinstall/obj -resource-dir /usr/local/lib/clang/13.0.0 -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/xinstall/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/xinstall/xinstall.c
1/* $OpenBSD: xinstall.c,v 1.76 2021/11/28 19:28:42 deraadt 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/types.h>
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(*__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 <libgen.h>
51
52#include "pathnames.h"
53
54#define _MAXBSIZE(64 * 1024) (64 * 1024)
55
56#define MINIMUM(a, b)(((a) < (b)) ? (a) : (b)) (((a) < (b)) ? (a) : (b))
57
58#define DIRECTORY0x01 0x01 /* Tell install it's a directory. */
59#define SETFLAGS0x02 0x02 /* Tell install to set flags. */
60#define USEFSYNC0x04 0x04 /* Tell install to use fsync(2). */
61#define NOCHANGEBITS(0x00000002 | 0x00000004 | 0x00020000 | 0x00040000) (UF_IMMUTABLE0x00000002 | UF_APPEND0x00000004 | SF_IMMUTABLE0x00020000 | SF_APPEND0x00040000)
62#define BACKUP_SUFFIX".old" ".old"
63
64int dobackup, docompare, dodest, dodir, dopreserve, dostrip;
65int mode = S_IRWXU0000700|S_IRGRP0000040|S_IXGRP0000010|S_IROTH0000004|S_IXOTH0000001;
66char pathbuf[PATH_MAX1024], tempfile[PATH_MAX1024];
67char *suffix = BACKUP_SUFFIX".old";
68uid_t uid = (uid_t)-1;
69gid_t gid = (gid_t)-1;
70
71void copy(int, char *, int, char *, off_t, int);
72int compare(int, const char *, off_t, int, const char *, off_t);
73void install(char *, char *, u_long, u_int);
74void install_dir(char *, int);
75void strip(char *);
76void usage(void);
77int create_tempfile(char *, char *, size_t);
78int file_write(int, char *, size_t, int *, int *, int);
79void file_flush(int, int);
80
81int
82main(int argc, char *argv[])
83{
84 struct stat from_sb, to_sb;
85 void *set;
86 u_int32_t fset;
87 u_int iflags;
88 int ch, no_target;
89 char *flags, *to_name, *group = NULL((void *)0), *owner = NULL((void *)0);
90 const char *errstr;
91
92 iflags = 0;
93 while ((ch = getopt(argc, argv, "B:bCcDdFf:g:m:o:pSs")) != -1)
94 switch(ch) {
95 case 'C':
96 docompare = 1;
97 break;
98 case 'B':
99 suffix = optarg;
100 /* fall through; -B implies -b */
101 case 'b':
102 dobackup = 1;
103 break;
104 case 'c':
105 /* For backwards compatibility. */
106 break;
107 case 'F':
108 iflags |= USEFSYNC0x04;
109 break;
110 case 'f':
111 flags = optarg;
112 if (strtofflags(&flags, &fset, NULL((void *)0)))
113 errx(1, "%s: invalid flag", flags);
114 iflags |= SETFLAGS0x02;
115 break;
116 case 'g':
117 group = optarg;
118 break;
119 case 'm':
120 if (!(set = setmode(optarg)))
121 errx(1, "%s: invalid file mode", optarg);
122 mode = getmode(set, 0);
123 free(set);
124 break;
125 case 'o':
126 owner = optarg;
127 break;
128 case 'p':
129 docompare = dopreserve = 1;
130 break;
131 case 'S':
132 /* For backwards compatibility. */
133 break;
134 case 's':
135 dostrip = 1;
136 break;
137 case 'D':
138 dodest = 1;
139 break;
140 case 'd':
141 dodir = 1;
142 break;
143 case '?':
144 default:
145 usage();
146 }
147 argc -= optind;
148 argv += optind;
149
150 /* some options make no sense when creating directories */
151 if ((docompare || dostrip) && dodir)
152 usage();
153
154 /* must have at least two arguments, except when creating directories */
155 if (argc == 0 || (argc == 1 && !dodir))
156 usage();
157
158 /* get group and owner id's */
159 if (group != NULL((void *)0) && gid_from_group(group, &gid) == -1) {
160 gid = strtonum(group, 0, GID_MAX(2147483647 *2U +1U), &errstr);
161 if (errstr != NULL((void *)0))
162 errx(1, "unknown group %s", group);
163 }
164 if (owner != NULL((void *)0) && uid_from_user(owner, &uid) == -1) {
165 uid = strtonum(owner, 0, UID_MAX(2147483647 *2U +1U), &errstr);
166 if (errstr != NULL((void *)0))
167 errx(1, "unknown user %s", owner);
168 }
169
170 if (dodir) {
171 for (; *argv != NULL((void *)0); ++argv)
172 install_dir(*argv, mode);
173 exit(0);
174 /* NOTREACHED */
175 }
176
177 if (dodest) {
178 char *dest = dirname(argv[argc - 1]);
179 if (dest == NULL((void *)0))
180 errx(1, "cannot determine dirname");
181 /*
182 * When -D is passed, do not chmod the directory with the mode set for
183 * the target file. If more restrictive permissions are required then
184 * '-d -m' ought to be used instead.
185 */
186 install_dir(dest, 0755);
187 }
188
189 no_target = stat(to_name = argv[argc - 1], &to_sb);
190 if (!no_target && S_ISDIR(to_sb.st_mode)((to_sb.st_mode & 0170000) == 0040000)) {
191 for (; *argv != to_name; ++argv)
192 install(*argv, to_name, fset, iflags | DIRECTORY0x01);
193 exit(0);
194 /* NOTREACHED */
195 }
196
197 /* can't do file1 file2 directory/file */
198 if (argc != 2)
199 errx(1, "Target: %s", argv[argc-1]);
200
201 if (!no_target) {
202 if (stat(*argv, &from_sb))
203 err(1, "%s", *argv);
204 if (!S_ISREG(to_sb.st_mode)((to_sb.st_mode & 0170000) == 0100000))
205 errc(1, EFTYPE79, "%s", to_name);
206 if (to_sb.st_dev == from_sb.st_dev &&
207 to_sb.st_ino == from_sb.st_ino)
208 errx(1, "%s and %s are the same file", *argv, to_name);
209 }
210 install(*argv, to_name, fset, iflags);
211 exit(0);
212 /* NOTREACHED */
213}
214
215/*
216 * install --
217 * build a path name and install the file
218 */
219void
220install(char *from_name, char *to_name, u_long fset, u_int flags)
221{
222 struct stat from_sb, to_sb;
223 struct timespec ts[2];
224 int devnull, from_fd, to_fd, serrno, files_match = 0;
225 char *p;
226 char *target_name = tempfile;
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 & DIRECTORY0x01 || strcmp(from_name, _PATH_DEVNULL"/dev/null")) {
233 if (stat(from_name, &from_sb))
234 err(1, "%s", from_name);
235 if (!S_ISREG(from_sb.st_mode)((from_sb.st_mode & 0170000) == 0100000))
236 errc(1, EFTYPE79, "%s", from_name);
237 /* Build the target path. */
238 if (flags & DIRECTORY0x01) {
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)((to_sb.st_mode & 0170000) == 0100000)) {
252 docompare = 0;
253 warnc(EFTYPE79, "%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_RDONLY0x0000)) == -1)
262 err(1, "%s", from_name);
263 }
264
265 to_fd = create_tempfile(to_name, tempfile, sizeof(tempfile));
266 if (to_fd < 0)
267 err(1, "%s", tempfile);
268
269 if (!devnull)
270 copy(from_fd, from_name, to_fd, tempfile, from_sb.st_size,
271 ((off_t)from_sb.st_blocks * S_BLKSIZE512 < from_sb.st_size));
272
273 if (dostrip) {
274 strip(tempfile);
275
276 /*
277 * Re-open our fd on the target, in case we used a strip
278 * that does not work in-place -- like gnu binutils strip.
279 */
280 close(to_fd);
281 if ((to_fd = open(tempfile, O_RDONLY0x0000)) == -1)
282 err(1, "stripping %s", to_name);
283 }
284
285 /*
286 * Compare the (possibly stripped) temp file to the target.
287 */
288 if (docompare) {
289 int temp_fd = to_fd;
290 struct stat temp_sb;
291
292 /* Re-open to_fd using the real target name. */
293 if ((to_fd = open(to_name, O_RDONLY0x0000)) == -1)
294 err(1, "%s", to_name);
295
296 if (fstat(temp_fd, &temp_sb)) {
297 serrno = errno(*__errno());
298 (void)unlink(tempfile);
299 errc(1, serrno, "%s", tempfile);
300 }
301
302 if (compare(temp_fd, tempfile, temp_sb.st_size, to_fd,
303 to_name, to_sb.st_size) == 0) {
304 /*
305 * If target has more than one link we need to
306 * replace it in order to snap the extra links.
307 * Need to preserve target file times, though.
308 */
309 if (to_sb.st_nlink != 1) {
310 ts[0] = to_sb.st_atim;
311 ts[1] = to_sb.st_mtim;
312 futimens(temp_fd, ts);
313 } else {
314 files_match = 1;
315 (void)unlink(tempfile);
316 target_name = to_name;
317 (void)close(temp_fd);
318 }
319 }
320 if (!files_match) {
321 (void)close(to_fd);
322 to_fd = temp_fd;
323 }
324 }
325
326 /*
327 * Preserve the timestamp of the source file if necessary.
328 */
329 if (dopreserve && !files_match) {
330 ts[0] = from_sb.st_atim;
331 ts[1] = from_sb.st_mtim;
332 futimens(to_fd, ts);
333 }
334
335 /*
336 * Set owner, group, mode for target; do the chown first,
337 * chown may lose the setuid bits.
338 */
339 if ((gid != (gid_t)-1 || uid != (uid_t)-1) &&
340 fchown(to_fd, uid, gid)) {
341 serrno = errno(*__errno());
342 if (target_name == tempfile)
343 (void)unlink(target_name);
344 errx(1, "%s: chown/chgrp: %s", target_name, strerror(serrno));
345 }
346 if (fchmod(to_fd, mode)) {
347 serrno = errno(*__errno());
348 if (target_name == tempfile)
349 (void)unlink(target_name);
350 errx(1, "%s: chmod: %s", target_name, strerror(serrno));
351 }
352
353 /*
354 * If provided a set of flags, set them, otherwise, preserve the
355 * flags, except for the dump flag.
356 */
357 if (fchflags(to_fd,
358 flags & SETFLAGS0x02 ? fset : from_sb.st_flags & ~UF_NODUMP0x00000001)) {
359 if (errno(*__errno()) != EOPNOTSUPP45 || (from_sb.st_flags & ~UF_NODUMP0x00000001) != 0)
360 warnx("%s: chflags: %s", target_name, strerror(errno(*__errno())));
361 }
362
363 if (flags & USEFSYNC0x04)
364 fsync(to_fd);
365 (void)close(to_fd);
366 if (!devnull)
367 (void)close(from_fd);
368
369 /*
370 * Move the new file into place if the files are different
371 * or were not compared.
372 */
373 if (!files_match) {
374 /* Try to turn off the immutable bits. */
375 if (to_sb.st_flags & (NOCHANGEBITS(0x00000002 | 0x00000004 | 0x00020000 | 0x00040000)))
376 (void)chflags(to_name, to_sb.st_flags & ~(NOCHANGEBITS(0x00000002 | 0x00000004 | 0x00020000 | 0x00040000)));
377 if (dobackup) {
378 char backup[PATH_MAX1024];
379 (void)snprintf(backup, PATH_MAX1024, "%s%s", to_name,
380 suffix);
381 /* It is ok for the target file not to exist. */
382 if (rename(to_name, backup) == -1 && errno(*__errno()) != ENOENT2) {
383 serrno = errno(*__errno());
384 unlink(tempfile);
385 errx(1, "rename: %s to %s: %s", to_name,
386 backup, strerror(serrno));
387 }
388 }
389 if (rename(tempfile, to_name) == -1 ) {
390 serrno = errno(*__errno());
391 unlink(tempfile);
392 errx(1, "rename: %s to %s: %s", tempfile,
393 to_name, strerror(serrno));
394 }
395 }
396}
397
398/*
399 * copy --
400 * copy from one file to another
401 */
402void
403copy(int from_fd, char *from_name, int to_fd, char *to_name, off_t size,
404 int sparse)
405{
406 ssize_t nr, nw;
407 int serrno;
408 char *p, buf[_MAXBSIZE(64 * 1024)];
409
410 if (size == 0)
411 return;
412
413 /* Rewind file descriptors. */
414 if (lseek(from_fd, (off_t)0, SEEK_SET0) == (off_t)-1)
415 err(1, "lseek: %s", from_name);
416 if (lseek(to_fd, (off_t)0, SEEK_SET0) == (off_t)-1)
417 err(1, "lseek: %s", to_name);
418
419 /*
420 * Mmap and write if less than 8M (the limit is so we don't totally
421 * trash memory on big files. This is really a minor hack, but it
422 * wins some CPU back. Sparse files need special treatment.
423 */
424 if (!sparse && size <= 8 * 1048576) {
425 size_t siz;
426
427 if ((p = mmap(NULL((void *)0), (size_t)size, PROT_READ0x01, MAP_PRIVATE0x0002,
428 from_fd, (off_t)0)) == MAP_FAILED((void *)-1)) {
429 serrno = errno(*__errno());
430 (void)unlink(to_name);
431 errc(1, serrno, "%s", from_name);
432 }
433 madvise(p, size, MADV_SEQUENTIAL2);
434 siz = (size_t)size;
435 if ((nw = write(to_fd, p, siz)) != siz) {
436 serrno = errno(*__errno());
437 (void)unlink(to_name);
438 errx(1, "%s: %s",
439 to_name, strerror(nw > 0 ? EIO5 : serrno));
440 }
441 (void) munmap(p, (size_t)size);
442 } else {
443 int sz, rem, isem = 1;
444 struct stat sb;
445
446 /*
447 * Pass the blocksize of the file being written to the write
448 * routine. if the size is zero, use the default S_BLKSIZE.
449 */
450 if (fstat(to_fd, &sb) != 0 || sb.st_blksize == 0)
451 sz = S_BLKSIZE512;
452 else
453 sz = sb.st_blksize;
454 rem = sz;
455
456 while ((nr = read(from_fd, buf, sizeof(buf))) > 0) {
457 if (sparse)
458 nw = file_write(to_fd, buf, nr, &rem, &isem, sz);
459 else
460 nw = write(to_fd, buf, nr);
461 if (nw != nr) {
462 serrno = errno(*__errno());
463 (void)unlink(to_name);
464 errx(1, "%s: %s",
465 to_name, strerror(nw > 0 ? EIO5 : serrno));
466 }
467 }
468 if (sparse)
469 file_flush(to_fd, isem);
470 if (nr != 0) {
471 serrno = errno(*__errno());
472 (void)unlink(to_name);
473 errc(1, serrno, "%s", from_name);
474 }
475 }
476}
477
478/*
479 * compare --
480 * compare two files; non-zero means files differ
481 */
482int
483compare(int from_fd, const char *from_name, off_t from_len, int to_fd,
484 const char *to_name, off_t to_len)
485{
486 caddr_t p1, p2;
487 size_t length;
488 off_t from_off, to_off, remainder;
489 int dfound;
490
491 if (from_len == 0 && from_len == to_len)
492 return (0);
493
494 if (from_len != to_len)
495 return (1);
496
497 /*
498 * Compare the two files being careful not to mmap
499 * more than 8M at a time.
500 */
501 from_off = to_off = (off_t)0;
502 remainder = from_len;
503 do {
504 length = MINIMUM(remainder, 8 * 1048576)(((remainder) < (8 * 1048576)) ? (remainder) : (8 * 1048576
))
;
505 remainder -= length;
506
507 if ((p1 = mmap(NULL((void *)0), length, PROT_READ0x01, MAP_PRIVATE0x0002,
508 from_fd, from_off)) == MAP_FAILED((void *)-1))
509 err(1, "%s", from_name);
510 if ((p2 = mmap(NULL((void *)0), length, PROT_READ0x01, MAP_PRIVATE0x0002,
511 to_fd, to_off)) == MAP_FAILED((void *)-1))
512 err(1, "%s", to_name);
513 if (length) {
514 madvise(p1, length, MADV_SEQUENTIAL2);
515 madvise(p2, length, MADV_SEQUENTIAL2);
516 }
517
518 dfound = memcmp(p1, p2, length);
519
520 (void) munmap(p1, length);
521 (void) munmap(p2, length);
522
523 from_off += length;
524 to_off += length;
525
526 } while (!dfound && remainder > 0);
527
528 return(dfound);
529}
530
531/*
532 * strip --
533 * use strip(1) to strip the target file
534 */
535void
536strip(char *to_name)
537{
538 int serrno, status;
539 char * volatile path_strip;
540 pid_t pid;
541
542 if (issetugid() || (path_strip = getenv("STRIP")) == NULL((void *)0))
543 path_strip = _PATH_STRIP"/usr/bin/strip";
544
545 switch ((pid = vfork())) {
Call to function 'vfork' is insecure as it can lead to denial of service situations in the parent process. Replace calls to vfork with calls to the safer 'posix_spawn' function
546 case -1:
547 serrno = errno(*__errno());
548 (void)unlink(to_name);
549 errc(1, serrno, "forks");
550 case 0:
551 execl(path_strip, "strip", "--", to_name, (char *)NULL((void *)0));
552 warn("%s", path_strip);
553 _exit(1);
554 default:
555 while (waitpid(pid, &status, 0) == -1) {
556 if (errno(*__errno()) != EINTR4)
557 break;
558 }
559 if (!WIFEXITED(status)(((status) & 0177) == 0))
560 (void)unlink(to_name);
561 }
562}
563
564/*
565 * install_dir --
566 * build directory hierarchy
567 */
568void
569install_dir(char *path, int mode)
570{
571 char *p;
572 struct stat sb;
573 int ch;
574
575 for (p = path;; ++p)
576 if (!*p || (p != path && *p == '/')) {
577 ch = *p;
578 *p = '\0';
579 if (mkdir(path, 0777)) {
580 int mkdir_errno = errno(*__errno());
581 if (stat(path, &sb)) {
582 /* Not there; use mkdir()s errno */
583 errc(1, mkdir_errno, "%s",
584 path);
585 /* NOTREACHED */
586 }
587 if (!S_ISDIR(sb.st_mode)((sb.st_mode & 0170000) == 0040000)) {
588 /* Is there, but isn't a directory */
589 errc(1, ENOTDIR20, "%s", path);
590 /* NOTREACHED */
591 }
592 }
593 if (!(*p = ch))
594 break;
595 }
596
597 if (((gid != (gid_t)-1 || uid != (uid_t)-1) && chown(path, uid, gid)) ||
598 chmod(path, mode)) {
599 warn("%s", path);
600 }
601}
602
603/*
604 * usage --
605 * print a usage message and die
606 */
607void
608usage(void)
609{
610 (void)fprintf(stderr(&__sF[2]), "\
611usage: install [-bCcDdFpSs] [-B suffix] [-f flags] [-g group] [-m mode] [-o owner]\n source ... target ...\n");
612 exit(1);
613 /* NOTREACHED */
614}
615
616/*
617 * create_tempfile --
618 * create a temporary file based on path and open it
619 */
620int
621create_tempfile(char *path, char *temp, size_t tsize)
622{
623 char *p;
624
625 strlcpy(temp, path, tsize);
626 if ((p = strrchr(temp, '/')) != NULL((void *)0))
627 p++;
628 else
629 p = temp;
630 *p = '\0';
631 strlcat(p, "INS@XXXXXXXXXX", tsize);
632
633 return(mkstemp(temp));
634}
635
636/*
637 * file_write()
638 * Write/copy a file (during copy or archive extract). This routine knows
639 * how to copy files with lseek holes in it. (Which are read as file
640 * blocks containing all 0's but do not have any file blocks associated
641 * with the data). Typical examples of these are files created by dbm
642 * variants (.pag files). While the file size of these files are huge, the
643 * actual storage is quite small (the files are sparse). The problem is
644 * the holes read as all zeros so are probably stored on the archive that
645 * way (there is no way to determine if the file block is really a hole,
646 * we only know that a file block of all zero's can be a hole).
647 * At this writing, no major archive format knows how to archive files
648 * with holes. However, on extraction (or during copy, -rw) we have to
649 * deal with these files. Without detecting the holes, the files can
650 * consume a lot of file space if just written to disk. This replacement
651 * for write when passed the basic allocation size of a file system block,
652 * uses lseek whenever it detects the input data is all 0 within that
653 * file block. In more detail, the strategy is as follows:
654 * While the input is all zero keep doing an lseek. Keep track of when we
655 * pass over file block boundaries. Only write when we hit a non zero
656 * input. once we have written a file block, we continue to write it to
657 * the end (we stop looking at the input). When we reach the start of the
658 * next file block, start checking for zero blocks again. Working on file
659 * block boundaries significantly reduces the overhead when copying files
660 * that are NOT very sparse. This overhead (when compared to a write) is
661 * almost below the measurement resolution on many systems. Without it,
662 * files with holes cannot be safely copied. It does has a side effect as
663 * it can put holes into files that did not have them before, but that is
664 * not a problem since the file contents are unchanged (in fact it saves
665 * file space). (Except on paging files for diskless clients. But since we
666 * cannot determine one of those file from here, we ignore them). If this
667 * ever ends up on a system where CTG files are supported and the holes
668 * are not desired, just do a conditional test in those routines that
669 * call file_write() and have it call write() instead. BEFORE CLOSING THE
670 * FILE, make sure to call file_flush() when the last write finishes with
671 * an empty block. A lot of file systems will not create an lseek hole at
672 * the end. In this case we drop a single 0 at the end to force the
673 * trailing 0's in the file.
674 * ---Parameters---
675 * rem: how many bytes left in this file system block
676 * isempt: have we written to the file block yet (is it empty)
677 * sz: basic file block allocation size
678 * cnt: number of bytes on this write
679 * str: buffer to write
680 * Return:
681 * number of bytes written, -1 on write (or lseek) error.
682 */
683
684int
685file_write(int fd, char *str, size_t cnt, int *rem, int *isempt, int sz)
686{
687 char *pt;
688 char *end;
689 size_t wcnt;
690 char *st = str;
691
692 /*
693 * while we have data to process
694 */
695 while (cnt) {
696 if (!*rem) {
697 /*
698 * We are now at the start of file system block again
699 * (or what we think one is...). start looking for
700 * empty blocks again
701 */
702 *isempt = 1;
703 *rem = sz;
704 }
705
706 /*
707 * only examine up to the end of the current file block or
708 * remaining characters to write, whatever is smaller
709 */
710 wcnt = MINIMUM(cnt, *rem)(((cnt) < (*rem)) ? (cnt) : (*rem));
711 cnt -= wcnt;
712 *rem -= wcnt;
713 if (*isempt) {
714 /*
715 * have not written to this block yet, so we keep
716 * looking for zero's
717 */
718 pt = st;
719 end = st + wcnt;
720
721 /*
722 * look for a zero filled buffer
723 */
724 while ((pt < end) && (*pt == '\0'))
725 ++pt;
726
727 if (pt == end) {
728 /*
729 * skip, buf is empty so far
730 */
731 if (lseek(fd, (off_t)wcnt, SEEK_CUR1) == -1) {
732 warn("lseek");
733 return(-1);
734 }
735 st = pt;
736 continue;
737 }
738 /*
739 * drat, the buf is not zero filled
740 */
741 *isempt = 0;
742 }
743
744 /*
745 * have non-zero data in this file system block, have to write
746 */
747 if (write(fd, st, wcnt) != wcnt) {
748 warn("write");
749 return(-1);
750 }
751 st += wcnt;
752 }
753 return(st - str);
754}
755
756/*
757 * file_flush()
758 * when the last file block in a file is zero, many file systems will not
759 * let us create a hole at the end. To get the last block with zeros, we
760 * write the last BYTE with a zero (back up one byte and write a zero).
761 */
762void
763file_flush(int fd, int isempt)
764{
765 static char blnk[] = "\0";
766
767 /*
768 * silly test, but make sure we are only called when the last block is
769 * filled with all zeros.
770 */
771 if (!isempt)
772 return;
773
774 /*
775 * move back one byte and write a zero
776 */
777 if (lseek(fd, (off_t)-1, SEEK_CUR1) == -1) {
778 warn("Failed seek on file");
779 return;
780 }
781
782 if (write(fd, blnk, 1) == -1)
783 warn("Failed write to file");
784 return;
785}