Annotation of src/usr.bin/rsync/mktemp.c, Revision 1.5
1.5 ! benno 1: /* $OpenBSD: mktemp.c,v 1.4 2019/02/16 17:59:33 deraadt 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: */
19: #include <sys/types.h>
20: #include <sys/stat.h>
1.2 florian 21: #include <sys/socket.h>
1.4 deraadt 22: #include <sys/un.h>
1.5 ! benno 23:
1.1 florian 24: #include <errno.h>
25: #include <fcntl.h>
26: #include <limits.h>
27: #include <stdio.h>
1.5 ! benno 28: #include <stdint.h>
1.1 florian 29: #include <stdlib.h>
30: #include <string.h>
31: #include <ctype.h>
32: #include <unistd.h>
33:
1.5 ! benno 34: #include "extern.h"
! 35:
! 36: /*
! 37: * The type of temporary files we can create.
! 38: */
! 39: enum tmpmode {
! 40: MKTEMP_NAME,
! 41: MKTEMP_FILE,
! 42: MKTEMP_DIR,
! 43: MKTEMP_LINK,
! 44: MKTEMP_FIFO,
! 45: MKTEMP_NOD,
! 46: MKTEMP_SOCK
! 47: };
1.1 florian 48:
1.5 ! benno 49: /*
! 50: * Characters we'll use for replacement in the template string.
! 51: */
1.1 florian 52: #define TEMPCHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
53: #define NUM_CHARS (sizeof(TEMPCHARS) - 1)
1.5 ! benno 54:
! 55: /*
! 56: * The number of template replacement values (foo.XXXXXX = 6) that we
! 57: * require as a minimum for the filename.
! 58: */
1.1 florian 59: #define MIN_X 6
60:
1.5 ! benno 61: /*
! 62: * The only flags we'll accept for creation of the temporary file.
! 63: */
1.1 florian 64: #define MKOTEMP_FLAGS (O_APPEND | O_CLOEXEC | O_DSYNC | O_RSYNC | O_SYNC)
65:
66: #ifndef nitems
1.5 ! benno 67: # define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
1.1 florian 68: #endif
69:
1.5 ! benno 70: /*
! 71: * Adapted from libc/stdio/mktemp.c.
! 72: */
1.1 florian 73: static int
1.5 ! benno 74: mktemp_internalat(int pfd, char *path, int slen, enum tmpmode mode,
! 75: int flags, const char *link, mode_t dev_type, dev_t dev)
1.1 florian 76: {
1.5 ! benno 77: char *start, *cp, *ep;
! 78: const char tempchars[] = TEMPCHARS;
! 79: unsigned int tries;
! 80: struct stat sb;
1.2 florian 81: struct sockaddr_un sun;
1.5 ! benno 82: size_t len;
! 83: int fd, saved_errno;
1.1 florian 84:
85: len = strlen(path);
86: if (len < MIN_X || slen < 0 || (size_t)slen > len - MIN_X) {
87: errno = EINVAL;
88: return(-1);
89: }
90: ep = path + len - slen;
91:
92: for (start = ep; start > path && start[-1] == 'X'; start--)
1.5 ! benno 93: /* continue */ ;
! 94:
1.1 florian 95: if (ep - start < MIN_X) {
96: errno = EINVAL;
97: return(-1);
98: }
99:
100: if (flags & ~MKOTEMP_FLAGS) {
101: errno = EINVAL;
102: return(-1);
103: }
104: flags |= O_CREAT | O_EXCL | O_RDWR;
105:
106: tries = INT_MAX;
107: do {
108: cp = start;
109: do {
110: unsigned short rbuf[16];
111: unsigned int i;
112:
113: /*
114: * Avoid lots of arc4random() calls by using
115: * a buffer sized for up to 16 Xs at a time.
116: */
117: arc4random_buf(rbuf, sizeof(rbuf));
118: for (i = 0; i < nitems(rbuf) && cp != ep; i++)
119: *cp++ = tempchars[rbuf[i] % NUM_CHARS];
120: } while (cp != ep);
121:
122: switch (mode) {
123: case MKTEMP_NAME:
124: if (fstatat(pfd, path, &sb, AT_SYMLINK_NOFOLLOW) != 0)
125: return(errno == ENOENT ? 0 : -1);
126: break;
127: case MKTEMP_FILE:
128: fd = openat(pfd, path, flags, S_IRUSR|S_IWUSR);
129: if (fd != -1 || errno != EEXIST)
130: return(fd);
131: break;
132: case MKTEMP_DIR:
133: if (mkdirat(pfd, path, S_IRUSR|S_IWUSR|S_IXUSR) == 0)
134: return(0);
135: if (errno != EEXIST)
136: return(-1);
137: break;
138: case MKTEMP_LINK:
139: if (symlinkat(link, pfd, path) == 0)
140: return(0);
141: else if (errno != EEXIST)
142: return(-1);
143: break;
1.2 florian 144: case MKTEMP_FIFO:
145: if (mkfifoat(pfd, path, S_IRUSR|S_IWUSR) == 0)
146: return(0);
147: else if (errno != EEXIST)
148: return(-1);
149: break;
150: case MKTEMP_NOD:
151: if (!(dev_type == S_IFCHR || dev_type == S_IFBLK)) {
152: errno = EINVAL;
153: return(-1);
154: }
155: if (mknodat(pfd, path, S_IRUSR|S_IWUSR|dev_type, dev)
156: == 0)
157: return(0);
158: else if (errno != EEXIST)
159: return(-1);
160: break;
161: case MKTEMP_SOCK:
162: memset(&sun, 0, sizeof(sun));
163: sun.sun_family = AF_UNIX;
164: if ((len = strlcpy(sun.sun_path, link,
165: sizeof(sun.sun_path))) >= sizeof(sun.sun_path)) {
166: errno = EINVAL;
167: return(-1);
168: }
169: if (sun.sun_path[len] != '/') {
170: if (strlcat(sun.sun_path, "/",
171: sizeof(sun.sun_path)) >=
172: sizeof(sun.sun_path)) {
173: errno = EINVAL;
174: return(-1);
175: }
176: }
177: if (strlcat(sun.sun_path, path, sizeof(sun.sun_path)) >=
178: sizeof(sun.sun_path)) {
179: errno = EINVAL;
180: return(-1);
181: }
182: if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC |
183: SOCK_NONBLOCK, 0)) == -1)
184: return -1;
185: if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) ==
186: 0) {
187: close(fd);
188: return(0);
189: } else if (errno != EEXIST) {
190: saved_errno = errno;
191: close(fd);
192: errno = saved_errno;
193: return -1;
194: }
195: close(fd);
196: break;
1.1 florian 197: }
198: } while (--tries);
199:
200: errno = EEXIST;
201: return(-1);
202: }
203:
204: /*
205: * A combination of mkstemp(3) and openat(2).
206: * On success returns a file descriptor and trailing Xs are overwritten in
207: * path to create a unique file name.
1.5 ! benno 208: * Returns -1 on failure and sets errno.
1.1 florian 209: */
210: int
211: mkstempat(int fd, char *path)
212: {
1.5 ! benno 213:
! 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.5 ! benno 221: * Returns NULL on failure and sets errno.
1.1 florian 222: */
1.5 ! benno 223: char *
1.1 florian 224: mkstemplinkat(char *link, int fd, char *path)
225: {
1.5 ! benno 226:
1.2 florian 227: if (mktemp_internalat(fd, path, 0, MKTEMP_LINK, 0, link, 0, 0) == -1)
1.5 ! 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.5 ! benno 236: * Returns NULL on failure and sets errno.
1.2 florian 237: */
1.5 ! benno 238: char *
1.2 florian 239: mkstempfifoat(int fd, char *path)
240: {
1.5 ! benno 241:
1.2 florian 242: if (mktemp_internalat(fd, path, 0, MKTEMP_FIFO, 0, NULL, 0, 0) == -1)
1.5 ! 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.5 ! benno 251: * Returns NULL on failure and sets errno.
1.2 florian 252: */
1.5 ! benno 253: char *
1.2 florian 254: mkstempnodat(int fd, char *path, mode_t mode, dev_t dev)
255: {
1.5 ! 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.5 ! benno 267: * Returns NULL on failure and sets errno.
1.2 florian 268: */
1.5 ! benno 269: char *
1.2 florian 270: mkstempsock(const char *root, char *path)
271: {
1.5 ! benno 272:
1.2 florian 273: if (mktemp_internalat(0, path, 0, MKTEMP_SOCK, 0, root, 0, 0) == -1)
1.5 ! 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.5 ! 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.5 ! 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.5 ! benno 298: }
! 299: } else if ((n = asprintf(ret, ".%s.XXXXXXXXXX", path)) < 0) {
! 300: ERR(sess, "asprintf");
! 301: *ret = NULL;
1.1 florian 302: }
1.5 ! benno 303:
! 304: return n;
1.1 florian 305: }