GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: xinstall.c,v 1.66 2017/08/21 21:41:13 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/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 |
14076 |
struct stat from_sb, to_sb; |
|
87 |
void *set; |
||
88 |
7038 |
u_int32_t fset; |
|
89 |
u_int iflags; |
||
90 |
int ch, no_target; |
||
91 |
7038 |
char *flags, *to_name, *group = NULL, *owner = NULL; |
|
92 |
|||
93 |
iflags = 0; |
||
94 |
✓✓ | 76860 |
while ((ch = getopt(argc, argv, "B:bCcDdFf:g:m:o:pSs")) != -1) |
95 |
✗✗✗✗ ✗✓✓✓ ✗✓✓✓ ✓✗✓ |
55779 |
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 |
7022 |
group = optarg; |
|
119 |
7022 |
break; |
|
120 |
case 'm': |
||
121 |
✗✓ | 7036 |
if (!(set = setmode(optarg))) |
122 |
errx(1, "%s: invalid file mode", optarg); |
||
123 |
7036 |
mode = getmode(set, 0); |
|
124 |
7036 |
free(set); |
|
125 |
7036 |
break; |
|
126 |
case 'o': |
||
127 |
7038 |
owner = optarg; |
|
128 |
7038 |
break; |
|
129 |
case 'p': |
||
130 |
docompare = dopreserve = 1; |
||
131 |
break; |
||
132 |
case 'S': |
||
133 |
370 |
safecopy = 1; |
|
134 |
370 |
break; |
|
135 |
case 's': |
||
136 |
186 |
dostrip = 1; |
|
137 |
186 |
break; |
|
138 |
case 'D': |
||
139 |
2723 |
dodest = 1; |
|
140 |
2723 |
break; |
|
141 |
case 'd': |
||
142 |
12 |
dodir = 1; |
|
143 |
12 |
break; |
|
144 |
case '?': |
||
145 |
default: |
||
146 |
usage(); |
||
147 |
} |
||
148 |
7038 |
argc -= optind; |
|
149 |
7038 |
argv += optind; |
|
150 |
|||
151 |
/* some options make no sense when creating directories */ |
||
152 |
✗✓ | 7038 |
if ((safecopy || docompare || dostrip) && dodir) |
153 |
usage(); |
||
154 |
|||
155 |
/* must have at least two arguments, except when creating directories */ |
||
156 |
✗✓ | 7038 |
if (argc < 2 && !dodir) |
157 |
usage(); |
||
158 |
|||
159 |
/* need to make a temp copy so we can compare stripped version */ |
||
160 |
✗✓ | 7038 |
if (docompare && dostrip) |
161 |
safecopy = 1; |
||
162 |
|||
163 |
/* get group and owner id's */ |
||
164 |
✓✓✗✓ ✗✗ |
14060 |
if (group && !(gp = getgrnam(group)) && !isdigit((unsigned char)*group)) |
165 |
errx(1, "unknown group %s", group); |
||
166 |
✓✓✓✗ |
28120 |
gid = (group) ? ((gp) ? gp->gr_gid : (gid_t)strtoul(group, NULL, 10)) : (gid_t)-1; |
167 |
✓✗✗✓ ✗✗ |
14076 |
if (owner && !(pp = getpwnam(owner)) && !isdigit((unsigned char)*owner)) |
168 |
errx(1, "unknown user %s", owner); |
||
169 |
✓✗✓✗ |
28152 |
uid = (owner) ? ((pp) ? pp->pw_uid : (uid_t)strtoul(owner, NULL, 10)) : (uid_t)-1; |
170 |
|||
171 |
✓✓ | 7038 |
if (dodir) { |
172 |
✓✓ | 48 |
for (; *argv != NULL; ++argv) |
173 |
12 |
install_dir(*argv, mode); |
|
174 |
exit(0); |
||
175 |
/* NOTREACHED */ |
||
176 |
} |
||
177 |
|||
178 |
✓✓ | 7026 |
if (dodest) { |
179 |
2723 |
char *dest = dirname(argv[argc - 1]); |
|
180 |
✗✓ | 2723 |
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 |
2723 |
install_dir(dest, 0755); |
|
188 |
2723 |
} |
|
189 |
|||
190 |
7026 |
no_target = stat(to_name = argv[argc - 1], &to_sb); |
|
191 |
✓✓✓✓ |
14050 |
if (!no_target && S_ISDIR(to_sb.st_mode)) { |
192 |
✓✓ | 700 |
for (; *argv != to_name; ++argv) |
193 |
210 |
install(*argv, to_name, fset, iflags | DIRECTORY); |
|
194 |
exit(0); |
||
195 |
/* NOTREACHED */ |
||
196 |
} |
||
197 |
|||
198 |
/* can't do file1 file2 directory/file */ |
||
199 |
✗✓ | 6886 |
if (argc != 2) |
200 |
errx(1, "Target: %s", argv[argc-1]); |
||
201 |
|||
202 |
✓✓ | 6886 |
if (!no_target) { |
203 |
✓✓ | 6884 |
if (stat(*argv, &from_sb)) |
204 |
err(1, "%s", *argv); |
||
205 |
✗✓ | 6881 |
if (!S_ISREG(to_sb.st_mode)) |
206 |
errc(1, EFTYPE, "%s", to_name); |
||
207 |
✓✓✗✓ |
6895 |
if (to_sb.st_dev == from_sb.st_dev && |
208 |
14 |
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 |
14186 |
struct stat from_sb, to_sb; |
|
224 |
7093 |
struct timespec ts[2]; |
|
225 |
int devnull, from_fd, to_fd, serrno, files_match = 0; |
||
226 |
char *p; |
||
227 |
|||
228 |
7093 |
(void)memset((void *)&from_sb, 0, sizeof(from_sb)); |
|
229 |
7093 |
(void)memset((void *)&to_sb, 0, sizeof(to_sb)); |
|
230 |
|||
231 |
/* If try to install NULL file to a directory, fails. */ |
||
232 |
✓✓✓✗ |
13976 |
if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) { |
233 |
✗✓ | 7093 |
if (stat(from_name, &from_sb)) |
234 |
err(1, "%s", from_name); |
||
235 |
✗✓ | 7093 |
if (!S_ISREG(from_sb.st_mode)) |
236 |
errc(1, EFTYPE, "%s", from_name); |
||
237 |
/* Build the target path. */ |
||
238 |
✓✓ | 7093 |
if (flags & DIRECTORY) { |
239 |
210 |
(void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s", |
|
240 |
to_name, |
||
241 |
✓✓ | 630 |
(p = strrchr(from_name, '/')) ? ++p : from_name); |
242 |
to_name = pathbuf; |
||
243 |
210 |
} |
|
244 |
devnull = 0; |
||
245 |
7093 |
} else { |
|
246 |
devnull = 1; |
||
247 |
} |
||
248 |
|||
249 |
✓✓ | 7093 |
if (stat(to_name, &to_sb) == 0) { |
250 |
/* Only compare against regular files. */ |
||
251 |
✗✓✗✗ |
7091 |
if (docompare && !S_ISREG(to_sb.st_mode)) { |
252 |
docompare = 0; |
||
253 |
warnc(EFTYPE, "%s", to_name); |
||
254 |
} |
||
255 |
✗✓ | 2 |
} else if (docompare) { |
256 |
/* File does not exist so silently ignore compare flag. */ |
||
257 |
docompare = 0; |
||
258 |
} |
||
259 |
|||
260 |
✓✗ | 7093 |
if (!devnull) { |
261 |
✗✓ | 7093 |
if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) |
262 |
err(1, "%s", from_name); |
||
263 |
} |
||
264 |
|||
265 |
✓✓ | 7093 |
if (safecopy) { |
266 |
379 |
to_fd = create_tempfile(to_name, tempfile, sizeof(tempfile)); |
|
267 |
✗✓ | 379 |
if (to_fd < 0) |
268 |
err(1, "%s", tempfile); |
||
269 |
✗✓ | 6714 |
} else if (docompare && !dostrip) { |
270 |
if ((to_fd = open(to_name, O_RDONLY, 0)) < 0) |
||
271 |
err(1, "%s", to_name); |
||
272 |
} else { |
||
273 |
✗✓ | 6714 |
if ((to_fd = create_newfile(to_name, &to_sb)) < 0) |
274 |
err(1, "%s", to_name); |
||
275 |
} |
||
276 |
|||
277 |
✓✗ | 7093 |
if (!devnull) { |
278 |
✗✓ | 7093 |
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 |
✓✗ | 7093 |
if (!files_match) |
291 |
7093 |
copy(from_fd, from_name, to_fd, |
|
292 |
7093 |
safecopy ? tempfile : to_name, from_sb.st_size, |
|
293 |
7093 |
((off_t)from_sb.st_blocks * S_BLKSIZE < from_sb.st_size)); |
|
294 |
} |
||
295 |
|||
296 |
✓✓ | 7093 |
if (dostrip) { |
297 |
185 |
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 |
185 |
close(to_fd); |
|
304 |
✗✓ | 370 |
if ((to_fd = open(safecopy ? tempfile : to_name, O_RDONLY, |
305 |
185 |
0)) < 0) |
|
306 |
err(1, "stripping %s", to_name); |
||
307 |
} |
||
308 |
|||
309 |
/* |
||
310 |
* Compare the (possibly stripped) temp file to the target. |
||
311 |
*/ |
||
312 |
✗✓ | 7093 |
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 |
✗✓ | 7093 |
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 |
✓✗✗✓ |
14186 |
if ((gid != (gid_t)-1 || uid != (uid_t)-1) && |
360 |
7093 |
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 |
✗✓ | 7093 |
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 |
✗✓ | 7093 |
if (fchflags(to_fd, |
378 |
7093 |
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 |
✗✓ | 7093 |
if (flags & USEFSYNC) |
385 |
fsync(to_fd); |
||
386 |
7093 |
(void)close(to_fd); |
|
387 |
✓✗ | 7093 |
if (!devnull) |
388 |
7093 |
(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 |
✓✓ | 7093 |
if (safecopy && !files_match) { |
395 |
/* Try to turn off the immutable bits. */ |
||
396 |
✗✓ | 379 |
if (to_sb.st_flags & (NOCHANGEBITS)) |
397 |
(void)chflags(to_name, to_sb.st_flags & ~(NOCHANGEBITS)); |
||
398 |
✗✓ | 379 |
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 |
✗✓ | 379 |
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 |
7093 |
} |
|
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 |
14186 |
char *p, buf[MAXBSIZE]; |
|
430 |
|||
431 |
✗✓ | 7093 |
if (size == 0) |
432 |
return; |
||
433 |
|||
434 |
/* Rewind file descriptors. */ |
||
435 |
✗✓ | 7093 |
if (lseek(from_fd, (off_t)0, SEEK_SET) == (off_t)-1) |
436 |
err(1, "lseek: %s", from_name); |
||
437 |
✗✓ | 7093 |
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 |
✓✓ | 7093 |
if (!sparse && size <= 8 * 1048576) { |
446 |
size_t siz; |
||
447 |
|||
448 |
✗✓ | 14154 |
if ((p = mmap(NULL, (size_t)size, PROT_READ, MAP_PRIVATE, |
449 |
7077 |
from_fd, (off_t)0)) == MAP_FAILED) { |
|
450 |
serrno = errno; |
||
451 |
(void)unlink(to_name); |
||
452 |
errc(1, serrno, "%s", from_name); |
||
453 |
} |
||
454 |
7077 |
madvise(p, size, MADV_SEQUENTIAL); |
|
455 |
siz = (size_t)size; |
||
456 |
✗✓ | 7077 |
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 |
7077 |
(void) munmap(p, (size_t)size); |
|
463 |
7077 |
} else { |
|
464 |
16 |
int sz, rem, isem = 1; |
|
465 |
16 |
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 |
✓✗✗✓ |
32 |
if (fstat(to_fd, &sb) != 0 || sb.st_blksize == 0) |
472 |
sz = S_BLKSIZE; |
||
473 |
else |
||
474 |
sz = sb.st_blksize; |
||
475 |
16 |
rem = sz; |
|
476 |
|||
477 |
✓✓ | 6468 |
while ((nr = read(from_fd, buf, sizeof(buf))) > 0) { |
478 |
✗✓ | 3218 |
if (sparse) |
479 |
nw = file_write(to_fd, buf, nr, &rem, &isem, sz); |
||
480 |
else |
||
481 |
3218 |
nw = write(to_fd, buf, nr); |
|
482 |
✗✓ | 3218 |
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 |
✗✓ | 16 |
if (sparse) |
490 |
file_flush(to_fd, isem); |
||
491 |
✗✓ | 16 |
if (nr != 0) { |
492 |
serrno = errno; |
||
493 |
(void)unlink(to_name); |
||
494 |
errc(1, serrno, "%s", from_name); |
||
495 |
} |
||
496 |
16 |
} |
|
497 |
14186 |
} |
|
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 |
370 |
int serrno, status; |
|
560 |
185 |
char * volatile path_strip; |
|
561 |
pid_t pid; |
||
562 |
|||
563 |
✓✗✓✗ |
370 |
if (issetugid() || (path_strip = getenv("STRIP")) == NULL) |
564 |
185 |
path_strip = _PATH_STRIP; |
|
565 |
|||
566 |
✗✗✓ | 185 |
switch ((pid = vfork())) { |
567 |
case -1: |
||
568 |
serrno = errno; |
||
569 |
(void)unlink(to_name); |
||
570 |
errc(1, serrno, "forks"); |
||
571 |
case 0: |
||
572 |
execl(path_strip, "strip", "--", to_name, (char *)NULL); |
||
573 |
warn("%s", path_strip); |
||
574 |
_exit(1); |
||
575 |
default: |
||
576 |
✗✓ | 370 |
while (waitpid(pid, &status, 0) == -1) { |
577 |
if (errno != EINTR) |
||
578 |
break; |
||
579 |
} |
||
580 |
✗✓ | 185 |
if (!WIFEXITED(status)) |
581 |
(void)unlink(to_name); |
||
582 |
} |
||
583 |
185 |
} |
|
584 |
|||
585 |
/* |
||
586 |
* install_dir -- |
||
587 |
* build directory hierarchy |
||
588 |
*/ |
||
589 |
void |
||
590 |
install_dir(char *path, int mode) |
||
591 |
{ |
||
592 |
char *p; |
||
593 |
5470 |
struct stat sb; |
|
594 |
int ch; |
||
595 |
|||
596 |
60262 |
for (p = path;; ++p) |
|
597 |
✓✓✓✓ ✓✓ |
172581 |
if (!*p || (p != path && *p == '/')) { |
598 |
10951 |
ch = *p; |
|
599 |
10951 |
*p = '\0'; |
|
600 |
✓✗ | 10951 |
if (mkdir(path, 0777)) { |
601 |
10951 |
int mkdir_errno = errno; |
|
602 |
✗✓ | 10951 |
if (stat(path, &sb)) { |
603 |
/* Not there; use mkdir()s errno */ |
||
604 |
errc(1, mkdir_errno, "%s", |
||
605 |
path); |
||
606 |
/* NOTREACHED */ |
||
607 |
} |
||
608 |
✗✓ | 10951 |
if (!S_ISDIR(sb.st_mode)) { |
609 |
/* Is there, but isn't a directory */ |
||
610 |
errc(1, ENOTDIR, "%s", path); |
||
611 |
/* NOTREACHED */ |
||
612 |
} |
||
613 |
10951 |
} |
|
614 |
✓✓ | 10951 |
if (!(*p = ch)) |
615 |
break; |
||
616 |
} |
||
617 |
|||
618 |
✓✗✓✗ ✗✓ |
8205 |
if (((gid != (gid_t)-1 || uid != (uid_t)-1) && chown(path, uid, gid)) || |
619 |
2735 |
chmod(path, mode)) { |
|
620 |
warn("%s", path); |
||
621 |
} |
||
622 |
2735 |
} |
|
623 |
|||
624 |
/* |
||
625 |
* usage -- |
||
626 |
* print a usage message and die |
||
627 |
*/ |
||
628 |
void |
||
629 |
usage(void) |
||
630 |
{ |
||
631 |
(void)fprintf(stderr, "\ |
||
632 |
usage: install [-bCcDdFpSs] [-B suffix] [-f flags] [-g group] [-m mode] [-o owner]\n source ... target ...\n"); |
||
633 |
exit(1); |
||
634 |
/* NOTREACHED */ |
||
635 |
} |
||
636 |
|||
637 |
/* |
||
638 |
* create_tempfile -- |
||
639 |
* create a temporary file based on path and open it |
||
640 |
*/ |
||
641 |
int |
||
642 |
create_tempfile(char *path, char *temp, size_t tsize) |
||
643 |
{ |
||
644 |
char *p; |
||
645 |
|||
646 |
758 |
strlcpy(temp, path, tsize); |
|
647 |
✓✗ | 379 |
if ((p = strrchr(temp, '/')) != NULL) |
648 |
379 |
p++; |
|
649 |
else |
||
650 |
p = temp; |
||
651 |
379 |
*p = '\0'; |
|
652 |
379 |
strlcat(p, "INS@XXXXXXXXXX", tsize); |
|
653 |
|||
654 |
379 |
return(mkstemp(temp)); |
|
655 |
} |
||
656 |
|||
657 |
/* |
||
658 |
* create_newfile -- |
||
659 |
* create a new file, overwriting an existing one if necessary |
||
660 |
*/ |
||
661 |
int |
||
662 |
create_newfile(char *path, struct stat *sbp) |
||
663 |
{ |
||
664 |
13428 |
char backup[PATH_MAX]; |
|
665 |
|||
666 |
/* |
||
667 |
* Unlink now... avoid ETXTBSY errors later. Try and turn |
||
668 |
* off the append/immutable bits -- if we fail, go ahead, |
||
669 |
* it might work. |
||
670 |
*/ |
||
671 |
✗✓ | 6714 |
if (sbp->st_flags & (NOCHANGEBITS)) |
672 |
(void)chflags(path, sbp->st_flags & ~(NOCHANGEBITS)); |
||
673 |
|||
674 |
✗✓ | 6714 |
if (dobackup) { |
675 |
(void)snprintf(backup, PATH_MAX, "%s%s", path, suffix); |
||
676 |
/* It is ok for the target file not to exist. */ |
||
677 |
if (rename(path, backup) < 0 && errno != ENOENT) |
||
678 |
err(1, "rename: %s to %s (errno %d)", path, backup, errno); |
||
679 |
} else { |
||
680 |
✓✓✗✓ |
6716 |
if (unlink(path) < 0 && errno != ENOENT) |
681 |
err(1, "%s", path); |
||
682 |
} |
||
683 |
|||
684 |
13428 |
return(open(path, O_CREAT | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR)); |
|
685 |
6714 |
} |
|
686 |
|||
687 |
/* |
||
688 |
* file_write() |
||
689 |
* Write/copy a file (during copy or archive extract). This routine knows |
||
690 |
* how to copy files with lseek holes in it. (Which are read as file |
||
691 |
* blocks containing all 0's but do not have any file blocks associated |
||
692 |
* with the data). Typical examples of these are files created by dbm |
||
693 |
* variants (.pag files). While the file size of these files are huge, the |
||
694 |
* actual storage is quite small (the files are sparse). The problem is |
||
695 |
* the holes read as all zeros so are probably stored on the archive that |
||
696 |
* way (there is no way to determine if the file block is really a hole, |
||
697 |
* we only know that a file block of all zero's can be a hole). |
||
698 |
* At this writing, no major archive format knows how to archive files |
||
699 |
* with holes. However, on extraction (or during copy, -rw) we have to |
||
700 |
* deal with these files. Without detecting the holes, the files can |
||
701 |
* consume a lot of file space if just written to disk. This replacement |
||
702 |
* for write when passed the basic allocation size of a file system block, |
||
703 |
* uses lseek whenever it detects the input data is all 0 within that |
||
704 |
* file block. In more detail, the strategy is as follows: |
||
705 |
* While the input is all zero keep doing an lseek. Keep track of when we |
||
706 |
* pass over file block boundaries. Only write when we hit a non zero |
||
707 |
* input. once we have written a file block, we continue to write it to |
||
708 |
* the end (we stop looking at the input). When we reach the start of the |
||
709 |
* next file block, start checking for zero blocks again. Working on file |
||
710 |
* block boundaries significantly reduces the overhead when copying files |
||
711 |
* that are NOT very sparse. This overhead (when compared to a write) is |
||
712 |
* almost below the measurement resolution on many systems. Without it, |
||
713 |
* files with holes cannot be safely copied. It does has a side effect as |
||
714 |
* it can put holes into files that did not have them before, but that is |
||
715 |
* not a problem since the file contents are unchanged (in fact it saves |
||
716 |
* file space). (Except on paging files for diskless clients. But since we |
||
717 |
* cannot determine one of those file from here, we ignore them). If this |
||
718 |
* ever ends up on a system where CTG files are supported and the holes |
||
719 |
* are not desired, just do a conditional test in those routines that |
||
720 |
* call file_write() and have it call write() instead. BEFORE CLOSING THE |
||
721 |
* FILE, make sure to call file_flush() when the last write finishes with |
||
722 |
* an empty block. A lot of file systems will not create an lseek hole at |
||
723 |
* the end. In this case we drop a single 0 at the end to force the |
||
724 |
* trailing 0's in the file. |
||
725 |
* ---Parameters--- |
||
726 |
* rem: how many bytes left in this file system block |
||
727 |
* isempt: have we written to the file block yet (is it empty) |
||
728 |
* sz: basic file block allocation size |
||
729 |
* cnt: number of bytes on this write |
||
730 |
* str: buffer to write |
||
731 |
* Return: |
||
732 |
* number of bytes written, -1 on write (or lseek) error. |
||
733 |
*/ |
||
734 |
|||
735 |
int |
||
736 |
file_write(int fd, char *str, size_t cnt, int *rem, int *isempt, int sz) |
||
737 |
{ |
||
738 |
char *pt; |
||
739 |
char *end; |
||
740 |
size_t wcnt; |
||
741 |
char *st = str; |
||
742 |
|||
743 |
/* |
||
744 |
* while we have data to process |
||
745 |
*/ |
||
746 |
while (cnt) { |
||
747 |
if (!*rem) { |
||
748 |
/* |
||
749 |
* We are now at the start of file system block again |
||
750 |
* (or what we think one is...). start looking for |
||
751 |
* empty blocks again |
||
752 |
*/ |
||
753 |
*isempt = 1; |
||
754 |
*rem = sz; |
||
755 |
} |
||
756 |
|||
757 |
/* |
||
758 |
* only examine up to the end of the current file block or |
||
759 |
* remaining characters to write, whatever is smaller |
||
760 |
*/ |
||
761 |
wcnt = MINIMUM(cnt, *rem); |
||
762 |
cnt -= wcnt; |
||
763 |
*rem -= wcnt; |
||
764 |
if (*isempt) { |
||
765 |
/* |
||
766 |
* have not written to this block yet, so we keep |
||
767 |
* looking for zero's |
||
768 |
*/ |
||
769 |
pt = st; |
||
770 |
end = st + wcnt; |
||
771 |
|||
772 |
/* |
||
773 |
* look for a zero filled buffer |
||
774 |
*/ |
||
775 |
while ((pt < end) && (*pt == '\0')) |
||
776 |
++pt; |
||
777 |
|||
778 |
if (pt == end) { |
||
779 |
/* |
||
780 |
* skip, buf is empty so far |
||
781 |
*/ |
||
782 |
if (lseek(fd, (off_t)wcnt, SEEK_CUR) < 0) { |
||
783 |
warn("lseek"); |
||
784 |
return(-1); |
||
785 |
} |
||
786 |
st = pt; |
||
787 |
continue; |
||
788 |
} |
||
789 |
/* |
||
790 |
* drat, the buf is not zero filled |
||
791 |
*/ |
||
792 |
*isempt = 0; |
||
793 |
} |
||
794 |
|||
795 |
/* |
||
796 |
* have non-zero data in this file system block, have to write |
||
797 |
*/ |
||
798 |
if (write(fd, st, wcnt) != wcnt) { |
||
799 |
warn("write"); |
||
800 |
return(-1); |
||
801 |
} |
||
802 |
st += wcnt; |
||
803 |
} |
||
804 |
return(st - str); |
||
805 |
} |
||
806 |
|||
807 |
/* |
||
808 |
* file_flush() |
||
809 |
* when the last file block in a file is zero, many file systems will not |
||
810 |
* let us create a hole at the end. To get the last block with zeros, we |
||
811 |
* write the last BYTE with a zero (back up one byte and write a zero). |
||
812 |
*/ |
||
813 |
void |
||
814 |
file_flush(int fd, int isempt) |
||
815 |
{ |
||
816 |
static char blnk[] = "\0"; |
||
817 |
|||
818 |
/* |
||
819 |
* silly test, but make sure we are only called when the last block is |
||
820 |
* filled with all zeros. |
||
821 |
*/ |
||
822 |
if (!isempt) |
||
823 |
return; |
||
824 |
|||
825 |
/* |
||
826 |
* move back one byte and write a zero |
||
827 |
*/ |
||
828 |
if (lseek(fd, (off_t)-1, SEEK_CUR) < 0) { |
||
829 |
warn("Failed seek on file"); |
||
830 |
return; |
||
831 |
} |
||
832 |
|||
833 |
if (write(fd, blnk, 1) < 0) |
||
834 |
warn("Failed write to file"); |
||
835 |
return; |
||
836 |
} |
Generated by: GCOVR (Version 3.3) |