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