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