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