1 /* $OpenBSD: xinstall.c,v 1.67 2018/09/16 02:44:07 millert Exp $ */
2 /* $NetBSD: xinstall.c,v 1.9 1995/12/20 10:25:17 jonathan Exp $ */
5 * Copyright (c) 1987, 1993
6 * The Regents of the University of California. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
33 #include <sys/param.h> /* MAXBSIZE */
53 #include "pathnames.h"
56 #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
58 #define DIRECTORY 0x01 /* Tell install it's a directory. */
59 #define SETFLAGS 0x02 /* Tell install to set flags. */
60 #define USEFSYNC 0x04 /* Tell install to use fsync(2). */
61 #define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)
62 #define BACKUP_SUFFIX ".old"
66 int dobackup
, docompare
, dodest
, dodir
, dopreserve
, dostrip
;
67 int mode
= S_IRWXU
|S_IRGRP
|S_IXGRP
|S_IROTH
|S_IXOTH
;
68 char pathbuf
[PATH_MAX
], tempfile
[PATH_MAX
];
69 char *suffix
= BACKUP_SUFFIX
;
73 void copy(int, char *, int, char *, off_t
, int);
74 int compare(int, const char *, off_t
, int, const char *, off_t
);
75 void install(char *, char *, u_long
, u_int
);
76 void install_dir(char *, int);
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);
84 main(int argc
, char *argv
[])
86 struct stat from_sb
, to_sb
;
91 char *flags
, *to_name
, *group
= NULL
, *owner
= NULL
;
94 while ((ch
= getopt(argc
, argv
, "B:bCcDdFf:g:m:o:pSs")) != -1)
101 /* fall through; -B implies -b */
106 /* For backwards compatibility. */
115 if (!(set
= setmode(optarg
)))
116 errx(1, "%s: invalid file mode", optarg
);
117 mode
= getmode(set
, 0);
124 docompare
= dopreserve
= 1;
144 /* some options make no sense when creating directories */
145 if ((docompare
|| dostrip
) && dodir
)
148 /* must have at least two arguments, except when creating directories */
149 if (argc
< 2 && !dodir
)
152 /* get group and owner id's */
153 if (group
&& !(gp
= getgrnam(group
)) && !isdigit((unsigned char)*group
))
154 errx(1, "unknown group %s", group
);
155 gid
= (group
) ? ((gp
) ? gp
->gr_gid
: (gid_t
)strtoul(group
, NULL
, 10)) : (gid_t
)-1;
156 if (owner
&& !(pp
= getpwnam(owner
)) && !isdigit((unsigned char)*owner
))
157 errx(1, "unknown user %s", owner
);
158 uid
= (owner
) ? ((pp
) ? pp
->pw_uid
: (uid_t
)strtoul(owner
, NULL
, 10)) : (uid_t
)-1;
161 for (; *argv
!= NULL
; ++argv
)
162 install_dir(*argv
, mode
);
168 char *dest
= dirname(argv
[argc
- 1]);
170 errx(1, "cannot determine dirname");
172 * When -D is passed, do not chmod the directory with the mode set for
173 * the target file. If more restrictive permissions are required then
174 * '-d -m' ought to be used instead.
176 install_dir(dest
, 0755);
179 no_target
= stat(to_name
= argv
[argc
- 1], &to_sb
);
180 if (!no_target
&& S_ISDIR(to_sb
.st_mode
)) {
181 for (; *argv
!= to_name
; ++argv
)
182 install(*argv
, to_name
, fset
, iflags
| DIRECTORY
);
187 /* can't do file1 file2 directory/file */
189 errx(1, "Target: %s", argv
[argc
-1]);
192 if (stat(*argv
, &from_sb
))
194 if (!S_ISREG(to_sb
.st_mode
))
195 errc(1, EFTYPE
, "%s", to_name
);
196 if (to_sb
.st_dev
== from_sb
.st_dev
&&
197 to_sb
.st_ino
== from_sb
.st_ino
)
198 errx(1, "%s and %s are the same file", *argv
, to_name
);
200 install(*argv
, to_name
, fset
, iflags
);
207 * build a path name and install the file
210 install(char *from_name
, char *to_name
, u_long fset
, u_int flags
)
212 struct stat from_sb
, to_sb
;
213 struct timespec ts
[2];
214 int devnull
, from_fd
, to_fd
, serrno
, files_match
= 0;
217 (void)memset((void *)&from_sb
, 0, sizeof(from_sb
));
218 (void)memset((void *)&to_sb
, 0, sizeof(to_sb
));
220 /* If try to install NULL file to a directory, fails. */
221 if (flags
& DIRECTORY
|| strcmp(from_name
, _PATH_DEVNULL
)) {
222 if (stat(from_name
, &from_sb
))
223 err(1, "%s", from_name
);
224 if (!S_ISREG(from_sb
.st_mode
))
225 errc(1, EFTYPE
, "%s", from_name
);
226 /* Build the target path. */
227 if (flags
& DIRECTORY
) {
228 (void)snprintf(pathbuf
, sizeof(pathbuf
), "%s/%s",
230 (p
= strrchr(from_name
, '/')) ? ++p
: from_name
);
238 if (stat(to_name
, &to_sb
) == 0) {
239 /* Only compare against regular files. */
240 if (docompare
&& !S_ISREG(to_sb
.st_mode
)) {
242 warnc(EFTYPE
, "%s", to_name
);
244 } else if (docompare
) {
245 /* File does not exist so silently ignore compare flag. */
250 if ((from_fd
= open(from_name
, O_RDONLY
, 0)) < 0)
251 err(1, "%s", from_name
);
254 to_fd
= create_tempfile(to_name
, tempfile
, sizeof(tempfile
));
256 err(1, "%s", tempfile
);
259 copy(from_fd
, from_name
, to_fd
, tempfile
, from_sb
.st_size
,
260 ((off_t
)from_sb
.st_blocks
* S_BLKSIZE
< from_sb
.st_size
));
266 * Re-open our fd on the target, in case we used a strip
267 * that does not work in-place -- like gnu binutils strip.
270 if ((to_fd
= open(tempfile
, O_RDONLY
, 0)) < 0)
271 err(1, "stripping %s", to_name
);
275 * Compare the (possibly stripped) temp file to the target.
281 /* Re-open to_fd using the real target name. */
282 if ((to_fd
= open(to_name
, O_RDONLY
, 0)) < 0)
283 err(1, "%s", to_name
);
285 if (fstat(temp_fd
, &temp_sb
)) {
287 (void)unlink(tempfile
);
288 errc(1, serrno
, "%s", tempfile
);
291 if (compare(temp_fd
, tempfile
, temp_sb
.st_size
, to_fd
,
292 to_name
, to_sb
.st_size
) == 0) {
294 * If target has more than one link we need to
295 * replace it in order to snap the extra links.
296 * Need to preserve target file times, though.
298 if (to_sb
.st_nlink
!= 1) {
299 ts
[0] = to_sb
.st_atim
;
300 ts
[1] = to_sb
.st_mtim
;
301 futimens(temp_fd
, ts
);
304 (void)unlink(tempfile
);
312 * Preserve the timestamp of the source file if necessary.
314 if (dopreserve
&& !files_match
) {
315 ts
[0] = from_sb
.st_atim
;
316 ts
[1] = from_sb
.st_mtim
;
321 * Set owner, group, mode for target; do the chown first,
322 * chown may lose the setuid bits.
324 if ((gid
!= (gid_t
)-1 || uid
!= (uid_t
)-1) &&
325 fchown(to_fd
, uid
, gid
)) {
327 (void)unlink(tempfile
);
328 errx(1, "%s: chown/chgrp: %s", tempfile
, strerror(serrno
));
330 if (fchmod(to_fd
, mode
)) {
332 (void)unlink(tempfile
);
333 errx(1, "%s: chmod: %s", tempfile
, strerror(serrno
));
336 if (flags
& USEFSYNC
)
340 (void)close(from_fd
);
343 * Move the new file into place if the files are different (or just not
348 char backup
[PATH_MAX
];
349 (void)snprintf(backup
, PATH_MAX
, "%s%s", to_name
,
351 /* It is ok for the target file not to exist. */
352 if (rename(to_name
, backup
) < 0 && errno
!= ENOENT
) {
355 errx(1, "rename: %s to %s: %s", to_name
,
356 backup
, strerror(serrno
));
359 if (rename(tempfile
, to_name
) < 0 ) {
362 errx(1, "rename: %s to %s: %s", tempfile
,
363 to_name
, strerror(serrno
));
370 * copy from one file to another
373 copy(int from_fd
, char *from_name
, int to_fd
, char *to_name
, off_t size
,
378 char *p
, buf
[MAXBSIZE
];
383 /* Rewind file descriptors. */
384 if (lseek(from_fd
, (off_t
)0, SEEK_SET
) == (off_t
)-1)
385 err(1, "lseek: %s", from_name
);
386 if (lseek(to_fd
, (off_t
)0, SEEK_SET
) == (off_t
)-1)
387 err(1, "lseek: %s", to_name
);
390 * Mmap and write if less than 8M (the limit is so we don't totally
391 * trash memory on big files. This is really a minor hack, but it
392 * wins some CPU back. Sparse files need special treatment.
394 if (!sparse
&& size
<= 8 * 1048576) {
397 if ((p
= mmap(NULL
, (size_t)size
, PROT_READ
, MAP_PRIVATE
,
398 from_fd
, (off_t
)0)) == MAP_FAILED
) {
400 (void)unlink(to_name
);
401 errc(1, serrno
, "%s", from_name
);
403 madvise(p
, size
, MADV_SEQUENTIAL
);
405 if ((nw
= write(to_fd
, p
, siz
)) != siz
) {
407 (void)unlink(to_name
);
409 to_name
, strerror(nw
> 0 ? EIO
: serrno
));
411 (void) munmap(p
, (size_t)size
);
413 int sz
, rem
, isem
= 1;
417 * Pass the blocksize of the file being written to the write
418 * routine. if the size is zero, use the default S_BLKSIZE.
420 if (fstat(to_fd
, &sb
) != 0 || sb
.st_blksize
== 0)
426 while ((nr
= read(from_fd
, buf
, sizeof(buf
))) > 0) {
428 nw
= file_write(to_fd
, buf
, nr
, &rem
, &isem
, sz
);
430 nw
= write(to_fd
, buf
, nr
);
433 (void)unlink(to_name
);
435 to_name
, strerror(nw
> 0 ? EIO
: serrno
));
439 file_flush(to_fd
, isem
);
442 (void)unlink(to_name
);
443 errc(1, serrno
, "%s", from_name
);
450 * compare two files; non-zero means files differ
453 compare(int from_fd
, const char *from_name
, off_t from_len
, int to_fd
,
454 const char *to_name
, off_t to_len
)
458 off_t from_off
, to_off
, remainder
;
461 if (from_len
== 0 && from_len
== to_len
)
464 if (from_len
!= to_len
)
468 * Compare the two files being careful not to mmap
469 * more than 8M at a time.
471 from_off
= to_off
= (off_t
)0;
472 remainder
= from_len
;
474 length
= MINIMUM(remainder
, 8 * 1048576);
477 if ((p1
= mmap(NULL
, length
, PROT_READ
, MAP_PRIVATE
,
478 from_fd
, from_off
)) == MAP_FAILED
)
479 err(1, "%s", from_name
);
480 if ((p2
= mmap(NULL
, length
, PROT_READ
, MAP_PRIVATE
,
481 to_fd
, to_off
)) == MAP_FAILED
)
482 err(1, "%s", to_name
);
484 madvise(p1
, length
, MADV_SEQUENTIAL
);
485 madvise(p2
, length
, MADV_SEQUENTIAL
);
488 dfound
= memcmp(p1
, p2
, length
);
490 (void) munmap(p1
, length
);
491 (void) munmap(p2
, length
);
496 } while (!dfound
&& remainder
> 0);
503 * use strip(1) to strip the target file
509 char * volatile path_strip
;
512 if (issetugid() || (path_strip
= getenv("STRIP")) == NULL
)
513 path_strip
= _PATH_STRIP
;
515 switch ((pid
= vfork())) {
518 (void)unlink(to_name
);
519 errc(1, serrno
, "forks");
521 execl(path_strip
, "strip", "--", to_name
, (char *)NULL
);
522 warn("%s", path_strip
);
525 while (waitpid(pid
, &status
, 0) == -1) {
529 if (!WIFEXITED(status
))
530 (void)unlink(to_name
);
536 * build directory hierarchy
539 install_dir(char *path
, int mode
)
546 if (!*p
|| (p
!= path
&& *p
== '/')) {
549 if (mkdir(path
, 0777)) {
550 int mkdir_errno
= errno
;
551 if (stat(path
, &sb
)) {
552 /* Not there; use mkdir()s errno */
553 errc(1, mkdir_errno
, "%s",
557 if (!S_ISDIR(sb
.st_mode
)) {
558 /* Is there, but isn't a directory */
559 errc(1, ENOTDIR
, "%s", path
);
567 if (((gid
!= (gid_t
)-1 || uid
!= (uid_t
)-1) && chown(path
, uid
, gid
)) ||
575 * print a usage message and die
580 (void)fprintf(stderr
, "\
581 usage: install [-bCcDdFps] [-B suffix] [-f flags] [-g group] [-m mode] [-o owner]\n source ... target ...\n");
588 * create a temporary file based on path and open it
591 create_tempfile(char *path
, char *temp
, size_t tsize
)
595 strlcpy(temp
, path
, tsize
);
596 if ((p
= strrchr(temp
, '/')) != NULL
)
601 strlcat(p
, "INS@XXXXXXXXXX", tsize
);
603 return(mkstemp(temp
));
608 * Write/copy a file (during copy or archive extract). This routine knows
609 * how to copy files with lseek holes in it. (Which are read as file
610 * blocks containing all 0's but do not have any file blocks associated
611 * with the data). Typical examples of these are files created by dbm
612 * variants (.pag files). While the file size of these files are huge, the
613 * actual storage is quite small (the files are sparse). The problem is
614 * the holes read as all zeros so are probably stored on the archive that
615 * way (there is no way to determine if the file block is really a hole,
616 * we only know that a file block of all zero's can be a hole).
617 * At this writing, no major archive format knows how to archive files
618 * with holes. However, on extraction (or during copy, -rw) we have to
619 * deal with these files. Without detecting the holes, the files can
620 * consume a lot of file space if just written to disk. This replacement
621 * for write when passed the basic allocation size of a file system block,
622 * uses lseek whenever it detects the input data is all 0 within that
623 * file block. In more detail, the strategy is as follows:
624 * While the input is all zero keep doing an lseek. Keep track of when we
625 * pass over file block boundaries. Only write when we hit a non zero
626 * input. once we have written a file block, we continue to write it to
627 * the end (we stop looking at the input). When we reach the start of the
628 * next file block, start checking for zero blocks again. Working on file
629 * block boundaries significantly reduces the overhead when copying files
630 * that are NOT very sparse. This overhead (when compared to a write) is
631 * almost below the measurement resolution on many systems. Without it,
632 * files with holes cannot be safely copied. It does has a side effect as
633 * it can put holes into files that did not have them before, but that is
634 * not a problem since the file contents are unchanged (in fact it saves
635 * file space). (Except on paging files for diskless clients. But since we
636 * cannot determine one of those file from here, we ignore them). If this
637 * ever ends up on a system where CTG files are supported and the holes
638 * are not desired, just do a conditional test in those routines that
639 * call file_write() and have it call write() instead. BEFORE CLOSING THE
640 * FILE, make sure to call file_flush() when the last write finishes with
641 * an empty block. A lot of file systems will not create an lseek hole at
642 * the end. In this case we drop a single 0 at the end to force the
643 * trailing 0's in the file.
645 * rem: how many bytes left in this file system block
646 * isempt: have we written to the file block yet (is it empty)
647 * sz: basic file block allocation size
648 * cnt: number of bytes on this write
649 * str: buffer to write
651 * number of bytes written, -1 on write (or lseek) error.
655 file_write(int fd
, char *str
, size_t cnt
, int *rem
, int *isempt
, int sz
)
663 * while we have data to process
668 * We are now at the start of file system block again
669 * (or what we think one is...). start looking for
677 * only examine up to the end of the current file block or
678 * remaining characters to write, whatever is smaller
680 wcnt
= MINIMUM(cnt
, *rem
);
685 * have not written to this block yet, so we keep
692 * look for a zero filled buffer
694 while ((pt
< end
) && (*pt
== '\0'))
699 * skip, buf is empty so far
701 if (lseek(fd
, (off_t
)wcnt
, SEEK_CUR
) < 0) {
709 * drat, the buf is not zero filled
715 * have non-zero data in this file system block, have to write
717 if (write(fd
, st
, wcnt
) != wcnt
) {
728 * when the last file block in a file is zero, many file systems will not
729 * let us create a hole at the end. To get the last block with zeros, we
730 * write the last BYTE with a zero (back up one byte and write a zero).
733 file_flush(int fd
, int isempt
)
735 static char blnk
[] = "\0";
738 * silly test, but make sure we are only called when the last block is
739 * filled with all zeros.
745 * move back one byte and write a zero
747 if (lseek(fd
, (off_t
)-1, SEEK_CUR
) < 0) {
748 warn("Failed seek on file");
752 if (write(fd
, blnk
, 1) < 0)
753 warn("Failed write to file");