Bug Summary

File:src/usr.bin/xinstall/xinstall.c
Warning:line 552, column 3
This function call is prohibited after a successful vfork

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)
1
Assuming the condition is true
2
Loop condition is true. Entering loop body
7
Assuming the condition is false
8
Loop condition is false. Execution continues on line 147
94 switch(ch) {
3
Control jumps to 'case 102:' at line 110
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)))
4
Assuming the condition is false
5
Taking false branch
113 errx(1, "%s: invalid flag", flags);
114 iflags |= SETFLAGS0x02;
115 break;
6
Execution continues on line 93
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)
9
Assuming 'docompare' is 0
10
Assuming 'dostrip' is 0
152 usage();
153
154 /* must have at least two arguments, except when creating directories */
155 if (argc == 0 || (argc == 1 && !dodir))
11
Assuming 'argc' is not equal to 0
12
Assuming 'argc' is not equal to 1
156 usage();
157
158 /* get group and owner id's */
159 if (group
12.1
'group' is equal to NULL
!= 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
12.2
'owner' is equal to NULL
!= 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) {
13
Assuming 'dodir' is 0
14
Taking false branch
171 for (; *argv != NULL((void *)0); ++argv)
172 install_dir(*argv, mode);
173 exit(0);
174 /* NOTREACHED */
175 }
176
177 if (dodest) {
15
Assuming 'dodest' is 0
16
Taking false branch
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)) {
17
Assuming 'no_target' is 0
18
Assuming the condition is true
19
Taking true branch
191 for (; *argv != to_name; ++argv)
20
Loop condition is true. Entering loop body
192 install(*argv, to_name, fset, iflags | DIRECTORY0x01);
21
Calling 'install'
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))
22
Assuming the condition is false
23
Taking false branch
234 err(1, "%s", from_name);
235 if (!S_ISREG(from_sb.st_mode)((from_sb.st_mode & 0170000) == 0100000))
24
Assuming the condition is true
25
Taking false branch
236 errc(1, EFTYPE79, "%s", from_name);
237 /* Build the target path. */
238 if (flags & DIRECTORY0x01) {
26
Taking true branch
239 (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
240 to_name,
241 (p = strrchr(from_name, '/')) ? ++p : from_name);
27
Assuming 'p' is null
28
'?' condition is false
242 to_name = pathbuf;
243 }
244 devnull = 0;
245 } else {
246 devnull = 1;
247 }
248
249 if (stat(to_name, &to_sb) == 0) {
29
Assuming the condition is false
30
Taking false branch
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
30.1
'docompare' is 0
) {
31
Taking false branch
256 /* File does not exist so silently ignore compare flag. */
257 docompare = 0;
258 }
259
260 if (!devnull
31.1
'devnull' is 0
) {
32
Taking true branch
261 if ((from_fd = open(from_name, O_RDONLY0x0000)) == -1)
33
Assuming the condition is false
34
Taking false branch
262 err(1, "%s", from_name);
263 }
264
265 to_fd = create_tempfile(to_name, tempfile, sizeof(tempfile));
266 if (to_fd < 0)
35
Assuming 'to_fd' is >= 0
36
Taking false branch
267 err(1, "%s", tempfile);
268
269 if (!devnull
36.1
'devnull' is 0
)
37
Taking true branch
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));
38
Assuming the condition is true
272
273 if (dostrip) {
39
Assuming 'dostrip' is not equal to 0
40
Taking true branch
274 strip(tempfile);
41
Calling 'strip'
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))
42
Assuming the condition is true
543 path_strip = _PATH_STRIP"/usr/bin/strip";
544
545 switch ((pid = vfork())) {
43
Control jumps to 'case 0:' at line 550
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);
44
This function call is prohibited after a successful vfork
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}