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