Annotation of src/usr.bin/rsync/mktemp.c, Revision 1.6
1.6 ! benno 1: /* $OpenBSD: mktemp.c,v 1.5 2019/02/18 21:34:54 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.1 florian 24: #include <errno.h>
25: #include <fcntl.h>
26: #include <limits.h>
27: #include <stdio.h>
28: #include <stdlib.h>
29: #include <string.h>
30: #include <ctype.h>
31: #include <unistd.h>
32:
1.6 ! benno 33: #define MKTEMP_NAME 0
! 34: #define MKTEMP_FILE 1
! 35: #define MKTEMP_DIR 2
! 36: #define MKTEMP_LINK 3
! 37: #define MKTEMP_FIFO 4
! 38: #define MKTEMP_NOD 5
! 39: #define MKTEMP_SOCK 6
1.1 florian 40:
41: #define TEMPCHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
42: #define NUM_CHARS (sizeof(TEMPCHARS) - 1)
43: #define MIN_X 6
44:
45: #define MKOTEMP_FLAGS (O_APPEND | O_CLOEXEC | O_DSYNC | O_RSYNC | O_SYNC)
46:
47: #ifndef nitems
1.6 ! benno 48: #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
1.1 florian 49: #endif
50:
1.6 ! benno 51: /* adapted from libc/stdio/mktemp.c */
1.1 florian 52: static int
1.6 ! benno 53: mktemp_internalat(int pfd, char *path, int slen, int mode, int flags,
! 54: const char *link, mode_t dev_type, dev_t dev)
1.1 florian 55: {
1.6 ! benno 56: char *start, *cp, *ep;
! 57: const char tempchars[] = TEMPCHARS;
! 58: unsigned int tries;
! 59: struct stat sb;
1.2 florian 60: struct sockaddr_un sun;
1.6 ! benno 61: size_t len;
! 62: int fd, saved_errno;
1.1 florian 63:
64: len = strlen(path);
65: if (len < MIN_X || slen < 0 || (size_t)slen > len - MIN_X) {
66: errno = EINVAL;
67: return(-1);
68: }
69: ep = path + len - slen;
70:
71: for (start = ep; start > path && start[-1] == 'X'; start--)
1.6 ! benno 72: ;
1.1 florian 73: if (ep - start < MIN_X) {
74: errno = EINVAL;
75: return(-1);
76: }
77:
78: if (flags & ~MKOTEMP_FLAGS) {
79: errno = EINVAL;
80: return(-1);
81: }
82: flags |= O_CREAT | O_EXCL | O_RDWR;
83:
84: tries = INT_MAX;
85: do {
86: cp = start;
87: do {
88: unsigned short rbuf[16];
89: unsigned int i;
90:
91: /*
92: * Avoid lots of arc4random() calls by using
93: * a buffer sized for up to 16 Xs at a time.
94: */
95: arc4random_buf(rbuf, sizeof(rbuf));
96: for (i = 0; i < nitems(rbuf) && cp != ep; i++)
97: *cp++ = tempchars[rbuf[i] % NUM_CHARS];
98: } while (cp != ep);
99:
100: switch (mode) {
101: case MKTEMP_NAME:
102: if (fstatat(pfd, path, &sb, AT_SYMLINK_NOFOLLOW) != 0)
103: return(errno == ENOENT ? 0 : -1);
104: break;
105: case MKTEMP_FILE:
106: fd = openat(pfd, path, flags, S_IRUSR|S_IWUSR);
107: if (fd != -1 || errno != EEXIST)
108: return(fd);
109: break;
110: case MKTEMP_DIR:
111: if (mkdirat(pfd, path, S_IRUSR|S_IWUSR|S_IXUSR) == 0)
112: return(0);
113: if (errno != EEXIST)
114: return(-1);
115: break;
116: case MKTEMP_LINK:
117: if (symlinkat(link, pfd, path) == 0)
118: return(0);
119: else if (errno != EEXIST)
120: return(-1);
121: break;
1.2 florian 122: case MKTEMP_FIFO:
123: if (mkfifoat(pfd, path, S_IRUSR|S_IWUSR) == 0)
124: return(0);
125: else if (errno != EEXIST)
126: return(-1);
127: break;
128: case MKTEMP_NOD:
129: if (!(dev_type == S_IFCHR || dev_type == S_IFBLK)) {
130: errno = EINVAL;
131: return(-1);
132: }
133: if (mknodat(pfd, path, S_IRUSR|S_IWUSR|dev_type, dev)
134: == 0)
135: return(0);
136: else if (errno != EEXIST)
137: return(-1);
138: break;
139: case MKTEMP_SOCK:
140: memset(&sun, 0, sizeof(sun));
141: sun.sun_family = AF_UNIX;
142: if ((len = strlcpy(sun.sun_path, link,
143: sizeof(sun.sun_path))) >= sizeof(sun.sun_path)) {
144: errno = EINVAL;
145: return(-1);
146: }
147: if (sun.sun_path[len] != '/') {
148: if (strlcat(sun.sun_path, "/",
149: sizeof(sun.sun_path)) >=
150: sizeof(sun.sun_path)) {
151: errno = EINVAL;
152: return(-1);
153: }
154: }
155: if (strlcat(sun.sun_path, path, sizeof(sun.sun_path)) >=
156: sizeof(sun.sun_path)) {
157: errno = EINVAL;
158: return(-1);
159: }
160: if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC |
161: SOCK_NONBLOCK, 0)) == -1)
162: return -1;
163: if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) ==
164: 0) {
165: close(fd);
166: return(0);
167: } else if (errno != EEXIST) {
168: saved_errno = errno;
169: close(fd);
170: errno = saved_errno;
171: return -1;
172: }
173: close(fd);
174: break;
1.1 florian 175: }
176: } while (--tries);
177:
178: errno = EEXIST;
179: return(-1);
180: }
181:
182: /*
183: * A combination of mkstemp(3) and openat(2).
184: * On success returns a file descriptor and trailing Xs are overwritten in
185: * path to create a unique file name.
1.6 ! benno 186: * Returns -1 on failure.
1.1 florian 187: */
188: int
189: mkstempat(int fd, char *path)
190: {
1.6 ! benno 191: return(mktemp_internalat(fd, path, 0, MKTEMP_FILE, 0, NULL, 0, 0));
1.1 florian 192: }
193:
194: /*
195: * A combination of mkstemp(3) and symlinkat(2).
196: * On success returns path with trailing Xs overwritten to create a unique
197: * file name.
1.6 ! benno 198: * Returns NULL on failure.
1.1 florian 199: */
1.6 ! benno 200: char*
1.1 florian 201: mkstemplinkat(char *link, int fd, char *path)
202: {
1.2 florian 203: if (mktemp_internalat(fd, path, 0, MKTEMP_LINK, 0, link, 0, 0) == -1)
1.6 ! benno 204: return(NULL);
! 205: return(path);
1.2 florian 206: }
207:
208: /*
209: * A combination of mkstemp(3) and mkfifoat(2).
210: * On success returns path with trailing Xs overwritten to create a unique
211: * file name.
1.6 ! benno 212: * Returns NULL on failure.
1.2 florian 213: */
1.6 ! benno 214: char*
1.2 florian 215: mkstempfifoat(int fd, char *path)
216: {
217: if (mktemp_internalat(fd, path, 0, MKTEMP_FIFO, 0, NULL, 0, 0) == -1)
1.6 ! benno 218: return(NULL);
! 219: return(path);
1.2 florian 220: }
221:
222: /*
223: * A combination of mkstemp(3) and mknodat(2).
224: * On success returns path with trailing Xs overwritten to create a unique
225: * file name.
1.6 ! benno 226: * Returns NULL on failure.
1.2 florian 227: */
1.6 ! benno 228: char*
1.2 florian 229: mkstempnodat(int fd, char *path, mode_t mode, dev_t dev)
230: {
1.6 ! benno 231: if (mktemp_internalat(fd, path, 0, MKTEMP_NOD, 0, NULL, mode, dev) ==
! 232: -1)
! 233: return(NULL);
! 234: return(path);
1.2 florian 235: }
236:
237: /*
238: * A combination of mkstemp(3) and bind(2) on a unix domain socket.
239: * On success returns path with trailing Xs overwritten to create a unique
240: * file name.
1.6 ! benno 241: * Returns NULL on failure.
1.2 florian 242: */
1.6 ! benno 243: char*
1.2 florian 244: mkstempsock(const char *root, char *path)
245: {
246: if (mktemp_internalat(0, path, 0, MKTEMP_SOCK, 0, root, 0, 0) == -1)
1.6 ! benno 247: return(NULL);
! 248: return(path);
1.1 florian 249: }
250:
251: /*
252: * Turn path into a suitable template for mkstemp*at functions and
253: * place it into the newly allocated string returned in ret.
254: * The caller must free ret.
255: * Returns -1 on failure or number of characters output to ret
256: * (excluding the final '\0').
257: */
258: int
1.6 ! benno 259: mktemplate(char **ret, const char *path, int recursive)
1.1 florian 260: {
261: int n, dirlen;
262: const char *cp;
263:
264: if (recursive && (cp = strrchr(path, '/')) != NULL) {
265: dirlen = cp - path;
1.6 ! benno 266: if ((n = asprintf(ret, "%.*s/.%s.XXXXXXXXXX", dirlen, path,
! 267: path + dirlen + 1)) == -1)
! 268: *ret = NULL;
! 269: } else {
! 270: if ((n = asprintf(ret, ".%s.XXXXXXXXXX", path)) == -1)
1.1 florian 271: *ret = NULL;
272: }
1.6 ! benno 273: return(n);
1.1 florian 274: }