Annotation of src/usr.bin/xinstall/xinstall.c, Revision 1.4
1.4 ! millert 1: /* $OpenBSD: xinstall.c,v 1.3 1996/06/26 05:44:05 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.4 ! millert 47: static char rcsid[] = "$OpenBSD: xinstall.c,v 1.3 1996/06/26 05:44:05 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:
83: void copy __P((int, char *, int, char *, off_t));
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.1 deraadt 92:
93: int
94: main(argc, argv)
95: int argc;
96: char *argv[];
97: {
98: struct stat from_sb, to_sb;
99: mode_t *set;
100: u_long fset;
101: u_int iflags;
102: int ch, no_target;
103: char *flags, *to_name, *group = NULL, *owner = NULL;
104:
105: iflags = 0;
1.4 ! millert 106: while ((ch = getopt(argc, argv, "Ccdf:g:m:o:pSs")) != EOF)
1.1 deraadt 107: switch((char)ch) {
1.4 ! millert 108: case 'C':
! 109: docompare = 1;
! 110: break;
1.1 deraadt 111: case 'c':
1.4 ! millert 112: /* For backwards compatibility. */
1.1 deraadt 113: break;
114: case 'f':
115: flags = optarg;
116: if (string_to_flags(&flags, &fset, NULL))
1.4 ! millert 117: errx(EX_USAGE, "%s: invalid flag", flags);
1.1 deraadt 118: iflags |= SETFLAGS;
119: break;
120: case 'g':
121: group = optarg;
122: break;
123: case 'm':
124: if (!(set = setmode(optarg)))
1.4 ! millert 125: errx(EX_USAGE, "%s: invalid file mode", optarg);
1.1 deraadt 126: mode = getmode(set, 0);
127: break;
128: case 'o':
129: owner = optarg;
130: break;
1.4 ! millert 131: case 'p':
! 132: docompare = dopreserve = 1;
! 133: break;
! 134: case 'S':
! 135: safecopy = 1;
! 136: break;
1.1 deraadt 137: case 's':
138: dostrip = 1;
139: break;
140: case 'd':
141: dodir = 1;
142: break;
143: case '?':
144: default:
145: usage();
146: }
147: argc -= optind;
148: argv += optind;
149:
1.4 ! millert 150: /* some options make no sense when creating directories */
! 151: if ((safecopy || docompare || dostrip) && dodir)
1.1 deraadt 152: usage();
153:
154: /* must have at least two arguments, except when creating directories */
155: if (argc < 2 && !dodir)
156: usage();
157:
1.4 ! millert 158: /* need to make a temp copy so we can compare stripped version */
! 159: if (docompare && dostrip)
! 160: safecopy = 1;
! 161:
1.1 deraadt 162: /* get group and owner id's */
163: if (group && !(gp = getgrnam(group)) && !isdigit(*group))
1.4 ! millert 164: errx(EX_NOUSER, "unknown group %s", group);
! 165: gid = (group) ? ((gp) ? gp->gr_gid : (gid_t)strtoul(group, NULL, 10)) : (gid_t)-1;
1.1 deraadt 166: if (owner && !(pp = getpwnam(owner)) && !isdigit(*owner))
1.4 ! millert 167: errx(EX_NOUSER, "unknown user %s", owner);
! 168: uid = (owner) ? ((pp) ? pp->pw_uid : (uid_t)strtoul(owner, NULL, 10)) : (uid_t)-1;
1.1 deraadt 169:
170: if (dodir) {
171: for (; *argv != NULL; ++argv)
172: install_dir(*argv);
1.4 ! millert 173: exit(EX_OK);
1.1 deraadt 174: /* NOTREACHED */
175: }
176:
177: no_target = stat(to_name = argv[argc - 1], &to_sb);
178: if (!no_target && S_ISDIR(to_sb.st_mode)) {
179: for (; *argv != to_name; ++argv)
180: install(*argv, to_name, fset, iflags | DIRECTORY);
1.4 ! millert 181: exit(EX_OK);
! 182: /* NOTREACHED */
1.1 deraadt 183: }
184:
185: /* can't do file1 file2 directory/file */
186: if (argc != 2)
187: usage();
188:
189: if (!no_target) {
190: if (stat(*argv, &from_sb))
1.4 ! millert 191: err(EX_OSERR, "%s", *argv);
1.1 deraadt 192: if (!S_ISREG(to_sb.st_mode))
1.4 ! millert 193: errx(EX_OSERR, "%s: %s", to_name, strerror(EFTYPE));
1.1 deraadt 194: if (to_sb.st_dev == from_sb.st_dev &&
195: to_sb.st_ino == from_sb.st_ino)
1.4 ! millert 196: errx(EX_USAGE, "%s and %s are the same file", *argv, to_name);
1.1 deraadt 197: }
198: install(*argv, to_name, fset, iflags);
1.4 ! millert 199: exit(EX_OK);
! 200: /* NOTREACHED */
1.1 deraadt 201: }
202:
203: /*
204: * install --
205: * build a path name and install the file
206: */
207: void
208: install(from_name, to_name, fset, flags)
209: char *from_name, *to_name;
210: u_long fset;
211: u_int flags;
212: {
213: struct stat from_sb, to_sb;
1.4 ! millert 214: struct utimbuf utb;
! 215: int devnull, from_fd, to_fd, serrno, files_match = 0;
1.1 deraadt 216: char *p;
217:
1.4 ! millert 218: (void)memset((void *)&from_sb, 0, sizeof(from_sb));
! 219: (void)memset((void *)&to_sb, 0, sizeof(to_sb));
! 220:
1.1 deraadt 221: /* If try to install NULL file to a directory, fails. */
222: if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) {
223: if (stat(from_name, &from_sb))
1.4 ! millert 224: err(EX_OSERR, "%s", from_name);
1.1 deraadt 225: if (!S_ISREG(from_sb.st_mode))
1.4 ! millert 226: errx(EX_OSERR, "%s: %s", from_name, strerror(EFTYPE));
1.1 deraadt 227: /* Build the target path. */
228: if (flags & DIRECTORY) {
229: (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
230: to_name,
231: (p = rindex(from_name, '/')) ? ++p : from_name);
232: to_name = pathbuf;
233: }
234: devnull = 0;
235: } else {
236: devnull = 1;
237: }
238:
1.4 ! millert 239: if (stat(to_name, &to_sb) == 0) {
! 240: /* Only compare against regular files. */
! 241: if (docompare && !S_ISREG(to_sb.st_mode)) {
! 242: docompare = 0;
! 243: warnx("%s: %s", to_name, strerror(EFTYPE));
! 244: }
! 245: } else if (docompare) {
! 246: /* File does not exist so silently ignore compare flag. */
! 247: docompare = 0;
! 248: }
! 249:
! 250: if (safecopy) {
! 251: to_fd = create_tempfile(to_name, tempfile, sizeof(tempfile));
! 252: if (to_fd < 0)
! 253: err(EX_OSERR, "%s", tempfile);
! 254: } else if (docompare && !dostrip) {
! 255: if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
! 256: err(EX_OSERR, "%s", to_name);
! 257: } else {
! 258: if ((to_fd = create_newfile(to_name, &to_sb)) < 0)
! 259: err(EX_OSERR, "%s", to_name);
! 260: }
! 261:
1.1 deraadt 262: if (!devnull) {
263: if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) {
1.4 ! millert 264: serrno = errno;
! 265: (void)unlink(safecopy ? tempfile : to_name);
! 266: errx(EX_OSERR, "%s: %s", from_name, strerror(serrno));
! 267: }
! 268:
! 269: if (docompare && !safecopy) {
! 270: files_match = !(compare(from_fd, from_name,
! 271: (size_t)from_sb.st_size, to_fd,
! 272: to_name, (size_t)to_sb.st_size));
! 273:
! 274: /* Truncate "to" file for copy unless we match */
! 275: if (!files_match) {
! 276: (void)close(to_fd);
! 277: if ((to_fd = create_newfile(to_name, &to_sb)) < 0)
! 278: err(EX_OSERR, "%s", to_name);
! 279: }
1.1 deraadt 280: }
1.4 ! millert 281: if (!files_match)
! 282: copy(from_fd, from_name, to_fd,
! 283: safecopy ? tempfile : to_name, from_sb.st_size);
1.1 deraadt 284: }
1.2 deraadt 285:
286: if (dostrip) {
1.4 ! millert 287: strip(safecopy ? tempfile : to_name);
1.2 deraadt 288:
289: /*
290: * Re-open our fd on the target, in case we used a strip
291: * that does not work in-place -- like gnu binutils strip.
292: */
293: close(to_fd);
1.4 ! millert 294: if ((to_fd = open(safecopy ? tempfile : to_name, O_RDONLY,
! 295: 0)) < 0)
! 296: err(EX_OSERR, "stripping %s", to_name);
! 297: }
! 298:
! 299: /*
! 300: * Compare the (possibly stripped) temp file to the target.
! 301: */
! 302: if (safecopy && docompare) {
! 303: int temp_fd = to_fd;
! 304: struct stat temp_sb;
! 305:
! 306: /* Re-open to_fd using the real target name. */
! 307: if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
! 308: err(EX_OSERR, "%s", to_name);
! 309:
! 310: if (fstat(temp_fd, &temp_sb)) {
! 311: serrno = errno;
! 312: (void)unlink(tempfile);
! 313: errx(EX_OSERR, "%s: %s", tempfile, strerror(serrno));
! 314: }
! 315:
! 316: if (compare(temp_fd, tempfile, (size_t)temp_sb.st_size, to_fd,
! 317: to_name, (size_t)to_sb.st_size) == 0) {
! 318: /*
! 319: * If target has more than one link we need to
! 320: * replace it in order to snap the extra links.
! 321: * Need to preserve target file times, though.
! 322: */
! 323: if (to_sb.st_nlink != 1) {
! 324: utb.actime = to_sb.st_atime;
! 325: utb.modtime = to_sb.st_mtime;
! 326: (void)utime(tempfile, &utb);
! 327: } else {
! 328: files_match = 1;
! 329: (void)unlink(tempfile);
! 330: }
! 331: (void) close(temp_fd);
! 332: }
! 333: }
! 334:
! 335: /*
! 336: * Move the new file into place if doing a safe copy
! 337: * and the files are different (or just not compared).
! 338: */
! 339: if (safecopy && !files_match) {
! 340: /* Try to turn off the immutable bits. */
! 341: if (to_sb.st_flags & (NOCHANGEBITS))
! 342: (void)chflags(to_name, to_sb.st_flags & ~(NOCHANGEBITS));
! 343: if (rename(tempfile, to_name) < 0 ) {
! 344: serrno = errno;
! 345: unlink(tempfile);
! 346: errx(EX_OSERR, "rename: %s to %s: %s", tempfile,
! 347: to_name, strerror(serrno));
! 348: }
! 349:
! 350: /* Re-open to_fd so we aren't hosed by the rename(2). */
! 351: (void) close(to_fd);
! 352: if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
! 353: err(EX_OSERR, "%s", to_name);
! 354: }
! 355:
! 356: /*
! 357: * Preserve the timestamp of the source file if necesary.
! 358: */
! 359: if (dopreserve && !files_match) {
! 360: utb.actime = from_sb.st_atime;
! 361: utb.modtime = from_sb.st_mtime;
! 362: (void)utime(to_name, &utb);
1.2 deraadt 363: }
364:
1.1 deraadt 365: /*
366: * Set owner, group, mode for target; do the chown first,
367: * chown may lose the setuid bits.
368: */
1.4 ! millert 369: if ((gid != (gid_t)-1 || uid != (uid_t)-1) && fchown(to_fd, uid, gid)) {
1.1 deraadt 370: serrno = errno;
371: (void)unlink(to_name);
1.4 ! millert 372: errx(EX_OSERR, "%s: chown/chgrp: %s", to_name, strerror(serrno));
1.1 deraadt 373: }
374: if (fchmod(to_fd, mode)) {
375: serrno = errno;
376: (void)unlink(to_name);
1.4 ! millert 377: errx(EX_OSERR, "%s: chmod: %s", to_name, strerror(serrno));
1.1 deraadt 378: }
379:
380: /*
381: * If provided a set of flags, set them, otherwise, preserve the
382: * flags, except for the dump flag.
383: */
384: if (fchflags(to_fd,
385: flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) {
386: serrno = errno;
387: (void)unlink(to_name);
1.4 ! millert 388: errx(EX_OSERR, "%s: chflags: %s", to_name, strerror(serrno));
1.1 deraadt 389: }
390:
391: (void)close(to_fd);
1.4 ! millert 392: (void)close(from_fd);
1.1 deraadt 393: }
394:
395: /*
396: * copy --
397: * copy from one file to another
398: */
399: void
400: copy(from_fd, from_name, to_fd, to_name, size)
401: register int from_fd, to_fd;
402: char *from_name, *to_name;
403: off_t size;
404: {
405: register int nr, nw;
406: int serrno;
407: char *p, buf[MAXBSIZE];
1.3 deraadt 408: volatile size_t siz;
1.1 deraadt 409:
410: /*
411: * Mmap and write if less than 8M (the limit is so we don't totally
412: * trash memory on big files. This is really a minor hack, but it
413: * wins some CPU back.
414: */
415: if (size <= 8 * 1048576) {
416: if ((p = mmap(NULL, (size_t)size, PROT_READ,
417: 0, from_fd, (off_t)0)) == (char *)-1)
1.4 ! millert 418: err(EX_OSERR, "%s", from_name);
1.3 deraadt 419: siz = (size_t)size;
420: if (write(to_fd, p, siz) != siz)
1.4 ! millert 421: err(EX_OSERR, "%s", to_name);
! 422: (void) munmap(p, (size_t)size);
1.1 deraadt 423: } else {
424: while ((nr = read(from_fd, buf, sizeof(buf))) > 0)
425: if ((nw = write(to_fd, buf, nr)) != nr) {
426: serrno = errno;
427: (void)unlink(to_name);
1.4 ! millert 428: errx(EX_OSERR, "%s: %s",
1.1 deraadt 429: to_name, strerror(nw > 0 ? EIO : serrno));
430: }
431: if (nr != 0) {
432: serrno = errno;
433: (void)unlink(to_name);
1.4 ! millert 434: errx(EX_OSERR, "%s: %s", from_name, strerror(serrno));
1.1 deraadt 435: }
436: }
437: }
438:
439: /*
1.4 ! millert 440: * compare --
! 441: * compare two files; non-zero means files differ
! 442: */
! 443: int
! 444: compare(from_fd, from_name, from_len, to_fd, to_name, to_len)
! 445: int from_fd;
! 446: const char *from_name;
! 447: size_t from_len;
! 448: int to_fd;
! 449: const char *to_name;
! 450: size_t to_len;
! 451: {
! 452: caddr_t p1, p2;
! 453: register size_t length, remainder;
! 454: int dfound;
! 455:
! 456: if (from_len != to_len)
! 457: return(1);
! 458:
! 459: /* Rewind file descriptors. */
! 460: if (lseek(from_fd, (off_t)0, SEEK_SET) == (off_t)-1)
! 461: err(EX_OSERR, "lseek: %s", from_name);
! 462: if (lseek(to_fd, (off_t)0, SEEK_SET) == (off_t)-1)
! 463: err(EX_OSERR, "lseek: %s", to_name);
! 464:
! 465: /*
! 466: * Compare the two files being careful not to mmap
! 467: * more than 8M at a time.
! 468: */
! 469: remainder = from_len;
! 470: do {
! 471: length = MIN(remainder, 8 * 1048576);
! 472: remainder -= length;
! 473:
! 474: if ((p1 = mmap(NULL, length, PROT_READ, 0, from_fd, (off_t)0))
! 475: == (caddr_t)-1)
! 476: err(EX_OSERR, "%s", from_name);
! 477: if ((p2 = mmap(NULL, length, PROT_READ, 0, to_fd, (off_t)0))
! 478: == (caddr_t)-1)
! 479: err(EX_OSERR, "%s", to_name);
! 480:
! 481: dfound = memcmp(p1, p2, length);
! 482:
! 483: (void) munmap(p1, length);
! 484: (void) munmap(p2, length);
! 485:
! 486: if (dfound)
! 487: break;
! 488:
! 489: } while (remainder > 0);
! 490:
! 491: return(dfound);
! 492: }
! 493:
! 494: /*
1.1 deraadt 495: * strip --
496: * use strip(1) to strip the target file
497: */
498: void
499: strip(to_name)
500: char *to_name;
501: {
502: int serrno, status;
503:
504: switch (vfork()) {
505: case -1:
506: serrno = errno;
507: (void)unlink(to_name);
1.4 ! millert 508: errx(EX_TEMPFAIL, "forks: %s", strerror(serrno));
1.1 deraadt 509: case 0:
510: execl(_PATH_STRIP, "strip", to_name, NULL);
1.4 ! millert 511: err(EX_OSERR, "%s", _PATH_STRIP);
1.1 deraadt 512: default:
513: if (wait(&status) == -1 || status)
514: (void)unlink(to_name);
515: }
516: }
517:
518: /*
519: * install_dir --
520: * build directory heirarchy
521: */
522: void
523: install_dir(path)
524: char *path;
525: {
526: register char *p;
527: struct stat sb;
528: int ch;
529:
530: for (p = path;; ++p)
531: if (!*p || (p != path && *p == '/')) {
532: ch = *p;
533: *p = '\0';
534: if (stat(path, &sb)) {
535: if (errno != ENOENT || mkdir(path, 0777) < 0) {
1.4 ! millert 536: err(EX_OSERR, "%s", path);
1.1 deraadt 537: /* NOTREACHED */
538: }
539: }
540: if (!(*p = ch))
541: break;
542: }
543:
1.4 ! millert 544: if (((gid != (gid_t)-1 || uid != (uid_t)-1) && chown(path, uid, gid)) ||
1.1 deraadt 545: chmod(path, mode)) {
546: warn("%s", path);
547: }
548: }
549:
550: /*
551: * usage --
552: * print a usage message and die
553: */
554: void
555: usage()
556: {
557: (void)fprintf(stderr, "\
558: usage: install [-cs] [-f flags] [-g group] [-m mode] [-o owner] file1 file2\n\
559: install [-cs] [-f flags] [-g group] [-m mode] [-o owner] file1 ... fileN directory\n\
560: install -d [-g group] [-m mode] [-o owner] directory ...\n");
1.4 ! millert 561: exit(EX_USAGE);
! 562: /* NOTREACHED */
! 563: }
! 564:
! 565: /*
! 566: * create_tempfile --
! 567: * create a temporary file based on path and open it
! 568: */
! 569: int
! 570: create_tempfile(path, temp, tsize)
! 571: char *path;
! 572: char *temp;
! 573: size_t tsize;
! 574: {
! 575: char *p;
! 576:
! 577: (void)strncpy(temp, path, tsize);
! 578: temp[tsize - 1] = '\0';
! 579: if ((p = strrchr(temp, '/')))
! 580: p++;
! 581: else
! 582: p = temp;
! 583: (void)strncpy(p, "INS@XXXXXX", &temp[tsize - 1] - p);
! 584: temp[tsize - 1] = '\0';
! 585:
! 586: return(mkstemp(temp));
! 587: }
! 588:
! 589: /*
! 590: * create_newfile --
! 591: * create a new file, overwriting an existing one if necesary
! 592: */
! 593: int
! 594: create_newfile(path, sbp)
! 595: char *path;
! 596: struct stat *sbp;
! 597: {
! 598: /*
! 599: * Unlink now... avoid ETXTBSY errors later. Try and turn
! 600: * off the append/immutable bits -- if we fail, go ahead,
! 601: * it might work.
! 602: */
! 603: if (sbp->st_flags & (NOCHANGEBITS))
! 604: (void)chflags(path, sbp->st_flags & ~(NOCHANGEBITS));
! 605:
! 606: (void)unlink(path);
! 607:
! 608: return(open(path, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR));
1.1 deraadt 609: }