Annotation of src/usr.bin/rsync/mktemp.c, Revision 1.8
1.8 ! deraadt 1: /* $OpenBSD: mktemp.c,v 1.7 2019/02/18 22:47:34 benno Exp $ */
1.1 florian 2: /*
3: * Copyright (c) 1996-1998, 2008 Theo de Raadt
4: * Copyright (c) 1997, 2008-2009 Todd C. Miller
1.3 florian 5: * Copyright (c) 2019 Florian Obser <florian@openbsd.org>
1.1 florian 6: *
7: * Permission to use, copy, modify, and distribute this software for any
8: * purpose with or without fee is hereby granted, provided that the above
9: * copyright notice and this permission notice appear in all copies.
10: *
11: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18: */
1.6 benno 19:
1.1 florian 20: #include <sys/types.h>
21: #include <sys/stat.h>
1.2 florian 22: #include <sys/socket.h>
1.4 deraadt 23: #include <sys/un.h>
1.7 benno 24:
1.1 florian 25: #include <errno.h>
26: #include <fcntl.h>
27: #include <limits.h>
28: #include <stdio.h>
1.7 benno 29: #include <stdint.h>
1.1 florian 30: #include <stdlib.h>
31: #include <string.h>
32: #include <ctype.h>
33: #include <unistd.h>
34:
1.7 benno 35: #include "extern.h"
36:
37: /*
38: * The type of temporary files we can create.
39: */
40: enum tmpmode {
41: MKTEMP_NAME,
42: MKTEMP_FILE,
43: MKTEMP_DIR,
44: MKTEMP_LINK,
45: MKTEMP_FIFO,
46: MKTEMP_NOD,
47: MKTEMP_SOCK
48: };
1.1 florian 49:
1.7 benno 50: /*
51: * Characters we'll use for replacement in the template string.
52: */
1.1 florian 53: #define TEMPCHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
54: #define NUM_CHARS (sizeof(TEMPCHARS) - 1)
1.7 benno 55:
56: /*
57: * The number of template replacement values (foo.XXXXXX = 6) that we
58: * require as a minimum for the filename.
59: */
1.1 florian 60: #define MIN_X 6
61:
1.7 benno 62: /*
63: * The only flags we'll accept for creation of the temporary file.
64: */
1.1 florian 65: #define MKOTEMP_FLAGS (O_APPEND | O_CLOEXEC | O_DSYNC | O_RSYNC | O_SYNC)
66:
67: #ifndef nitems
1.6 benno 68: #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
1.1 florian 69: #endif
70:
1.7 benno 71: /*
72: * Adapted from libc/stdio/mktemp.c.
73: */
1.1 florian 74: static int
1.7 benno 75: mktemp_internalat(int pfd, char *path, int slen, enum tmpmode mode,
76: int flags, const char *link, mode_t dev_type, dev_t dev)
1.1 florian 77: {
1.8 ! deraadt 78: char *start, *cp, *ep;
! 79: const char tempchars[] = TEMPCHARS;
! 80: unsigned int tries;
! 81: struct stat sb;
1.2 florian 82: struct sockaddr_un sun;
1.8 ! deraadt 83: size_t len;
! 84: int fd, saved_errno;
1.1 florian 85:
86: len = strlen(path);
87: if (len < MIN_X || slen < 0 || (size_t)slen > len - MIN_X) {
88: errno = EINVAL;
89: return(-1);
90: }
91: ep = path + len - slen;
92:
93: for (start = ep; start > path && start[-1] == 'X'; start--)
1.7 benno 94: /* continue */ ;
95:
1.1 florian 96: if (ep - start < MIN_X) {
97: errno = EINVAL;
98: return(-1);
99: }
100:
101: if (flags & ~MKOTEMP_FLAGS) {
102: errno = EINVAL;
103: return(-1);
104: }
105: flags |= O_CREAT | O_EXCL | O_RDWR;
106:
107: tries = INT_MAX;
108: do {
109: cp = start;
110: do {
111: unsigned short rbuf[16];
112: unsigned int i;
113:
114: /*
115: * Avoid lots of arc4random() calls by using
116: * a buffer sized for up to 16 Xs at a time.
117: */
118: arc4random_buf(rbuf, sizeof(rbuf));
119: for (i = 0; i < nitems(rbuf) && cp != ep; i++)
120: *cp++ = tempchars[rbuf[i] % NUM_CHARS];
121: } while (cp != ep);
122:
123: switch (mode) {
124: case MKTEMP_NAME:
125: if (fstatat(pfd, path, &sb, AT_SYMLINK_NOFOLLOW) != 0)
126: return(errno == ENOENT ? 0 : -1);
127: break;
128: case MKTEMP_FILE:
129: fd = openat(pfd, path, flags, S_IRUSR|S_IWUSR);
130: if (fd != -1 || errno != EEXIST)
131: return(fd);
132: break;
133: case MKTEMP_DIR:
134: if (mkdirat(pfd, path, S_IRUSR|S_IWUSR|S_IXUSR) == 0)
135: return(0);
136: if (errno != EEXIST)
137: return(-1);
138: break;
139: case MKTEMP_LINK:
140: if (symlinkat(link, pfd, path) == 0)
141: return(0);
142: else if (errno != EEXIST)
143: return(-1);
144: break;
1.2 florian 145: case MKTEMP_FIFO:
146: if (mkfifoat(pfd, path, S_IRUSR|S_IWUSR) == 0)
147: return(0);
148: else if (errno != EEXIST)
149: return(-1);
150: break;
151: case MKTEMP_NOD:
152: if (!(dev_type == S_IFCHR || dev_type == S_IFBLK)) {
153: errno = EINVAL;
154: return(-1);
155: }
156: if (mknodat(pfd, path, S_IRUSR|S_IWUSR|dev_type, dev)
157: == 0)
158: return(0);
159: else if (errno != EEXIST)
160: return(-1);
161: break;
162: case MKTEMP_SOCK:
163: memset(&sun, 0, sizeof(sun));
164: sun.sun_family = AF_UNIX;
165: if ((len = strlcpy(sun.sun_path, link,
166: sizeof(sun.sun_path))) >= sizeof(sun.sun_path)) {
167: errno = EINVAL;
168: return(-1);
169: }
170: if (sun.sun_path[len] != '/') {
171: if (strlcat(sun.sun_path, "/",
172: sizeof(sun.sun_path)) >=
173: sizeof(sun.sun_path)) {
174: errno = EINVAL;
175: return(-1);
176: }
177: }
178: if (strlcat(sun.sun_path, path, sizeof(sun.sun_path)) >=
179: sizeof(sun.sun_path)) {
180: errno = EINVAL;
181: return(-1);
182: }
183: if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC |
184: SOCK_NONBLOCK, 0)) == -1)
185: return -1;
186: if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) ==
187: 0) {
188: close(fd);
189: return(0);
190: } else if (errno != EEXIST) {
191: saved_errno = errno;
192: close(fd);
193: errno = saved_errno;
194: return -1;
195: }
196: close(fd);
197: break;
1.1 florian 198: }
199: } while (--tries);
200:
201: errno = EEXIST;
202: return(-1);
203: }
204:
205: /*
206: * A combination of mkstemp(3) and openat(2).
207: * On success returns a file descriptor and trailing Xs are overwritten in
208: * path to create a unique file name.
1.7 benno 209: * Returns -1 on failure and sets errno.
1.1 florian 210: */
211: int
212: mkstempat(int fd, char *path)
213: {
1.7 benno 214: return mktemp_internalat(fd, path, 0, MKTEMP_FILE, 0, NULL, 0, 0);
1.1 florian 215: }
216:
217: /*
218: * A combination of mkstemp(3) and symlinkat(2).
219: * On success returns path with trailing Xs overwritten to create a unique
220: * file name.
1.7 benno 221: * Returns NULL on failure and sets errno.
1.1 florian 222: */
1.7 benno 223: char *
1.1 florian 224: mkstemplinkat(char *link, int fd, char *path)
225: {
1.7 benno 226:
1.2 florian 227: if (mktemp_internalat(fd, path, 0, MKTEMP_LINK, 0, link, 0, 0) == -1)
1.7 benno 228: return NULL;
229: return path;
1.2 florian 230: }
231:
232: /*
233: * A combination of mkstemp(3) and mkfifoat(2).
234: * On success returns path with trailing Xs overwritten to create a unique
235: * file name.
1.7 benno 236: * Returns NULL on failure and sets errno.
1.2 florian 237: */
1.7 benno 238: char *
1.2 florian 239: mkstempfifoat(int fd, char *path)
240: {
1.7 benno 241:
1.2 florian 242: if (mktemp_internalat(fd, path, 0, MKTEMP_FIFO, 0, NULL, 0, 0) == -1)
1.7 benno 243: return NULL;
244: return path;
1.2 florian 245: }
246:
247: /*
248: * A combination of mkstemp(3) and mknodat(2).
249: * On success returns path with trailing Xs overwritten to create a unique
250: * file name.
1.7 benno 251: * Returns NULL on failure and sets errno.
1.2 florian 252: */
1.7 benno 253: char *
1.2 florian 254: mkstempnodat(int fd, char *path, mode_t mode, dev_t dev)
255: {
1.7 benno 256:
257: if (mktemp_internalat(fd, path, 0,
258: MKTEMP_NOD, 0, NULL, mode, dev) == -1)
259: return NULL;
260: return path;
1.2 florian 261: }
262:
263: /*
264: * A combination of mkstemp(3) and bind(2) on a unix domain socket.
265: * On success returns path with trailing Xs overwritten to create a unique
266: * file name.
1.7 benno 267: * Returns NULL on failure and sets errno.
1.2 florian 268: */
1.7 benno 269: char *
1.2 florian 270: mkstempsock(const char *root, char *path)
271: {
1.7 benno 272:
1.2 florian 273: if (mktemp_internalat(0, path, 0, MKTEMP_SOCK, 0, root, 0, 0) == -1)
1.7 benno 274: return NULL;
275: return path;
1.1 florian 276: }
277:
278: /*
279: * Turn path into a suitable template for mkstemp*at functions and
280: * place it into the newly allocated string returned in ret.
281: * The caller must free ret.
282: * Returns -1 on failure or number of characters output to ret
283: * (excluding the final '\0').
284: */
285: int
1.7 benno 286: mktemplate(struct sess *sess, char **ret, const char *path, int recursive)
1.1 florian 287: {
288: int n, dirlen;
289: const char *cp;
290:
291: if (recursive && (cp = strrchr(path, '/')) != NULL) {
292: dirlen = cp - path;
1.7 benno 293: n = asprintf(ret, "%.*s/.%s.XXXXXXXXXX",
294: dirlen, path, path + dirlen + 1);
295: if (n < 0) {
296: ERR(sess, "asprintf");
1.1 florian 297: *ret = NULL;
1.7 benno 298: }
299: } else if ((n = asprintf(ret, ".%s.XXXXXXXXXX", path)) < 0) {
300: ERR(sess, "asprintf");
301: *ret = NULL;
1.1 florian 302: }
1.7 benno 303:
304: return n;
1.1 florian 305: }