Annotation of src/usr.bin/rsync/mktemp.c, Revision 1.1
1.1 ! florian 1: /* $OpenBSD: mktemp.c,v 1.39 2017/11/28 06:55:49 tb Exp $ */
! 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>
! 21: #include <errno.h>
! 22: #include <fcntl.h>
! 23: #include <limits.h>
! 24: #include <stdio.h>
! 25: #include <stdlib.h>
! 26: #include <string.h>
! 27: #include <ctype.h>
! 28: #include <unistd.h>
! 29:
! 30: #define MKTEMP_NAME 0
! 31: #define MKTEMP_FILE 1
! 32: #define MKTEMP_DIR 2
! 33: #define MKTEMP_LINK 3
! 34:
! 35: #define TEMPCHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
! 36: #define NUM_CHARS (sizeof(TEMPCHARS) - 1)
! 37: #define MIN_X 6
! 38:
! 39: #define MKOTEMP_FLAGS (O_APPEND | O_CLOEXEC | O_DSYNC | O_RSYNC | O_SYNC)
! 40:
! 41: #ifndef nitems
! 42: #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
! 43: #endif
! 44:
! 45: /* adapted from libc/stdio/mktemp.c */
! 46: static int
! 47: mktemp_internalat(int pfd, char *path, int slen, int mode, int flags,
! 48: char *link)
! 49: {
! 50: char *start, *cp, *ep;
! 51: const char tempchars[] = TEMPCHARS;
! 52: unsigned int tries;
! 53: struct stat sb;
! 54: size_t len;
! 55: int fd;
! 56:
! 57: len = strlen(path);
! 58: if (len < MIN_X || slen < 0 || (size_t)slen > len - MIN_X) {
! 59: errno = EINVAL;
! 60: return(-1);
! 61: }
! 62: ep = path + len - slen;
! 63:
! 64: for (start = ep; start > path && start[-1] == 'X'; start--)
! 65: ;
! 66: if (ep - start < MIN_X) {
! 67: errno = EINVAL;
! 68: return(-1);
! 69: }
! 70:
! 71: if (flags & ~MKOTEMP_FLAGS) {
! 72: errno = EINVAL;
! 73: return(-1);
! 74: }
! 75: flags |= O_CREAT | O_EXCL | O_RDWR;
! 76:
! 77: tries = INT_MAX;
! 78: do {
! 79: cp = start;
! 80: do {
! 81: unsigned short rbuf[16];
! 82: unsigned int i;
! 83:
! 84: /*
! 85: * Avoid lots of arc4random() calls by using
! 86: * a buffer sized for up to 16 Xs at a time.
! 87: */
! 88: arc4random_buf(rbuf, sizeof(rbuf));
! 89: for (i = 0; i < nitems(rbuf) && cp != ep; i++)
! 90: *cp++ = tempchars[rbuf[i] % NUM_CHARS];
! 91: } while (cp != ep);
! 92:
! 93: switch (mode) {
! 94: case MKTEMP_NAME:
! 95: if (fstatat(pfd, path, &sb, AT_SYMLINK_NOFOLLOW) != 0)
! 96: return(errno == ENOENT ? 0 : -1);
! 97: break;
! 98: case MKTEMP_FILE:
! 99: fd = openat(pfd, path, flags, S_IRUSR|S_IWUSR);
! 100: if (fd != -1 || errno != EEXIST)
! 101: return(fd);
! 102: break;
! 103: case MKTEMP_DIR:
! 104: if (mkdirat(pfd, path, S_IRUSR|S_IWUSR|S_IXUSR) == 0)
! 105: return(0);
! 106: if (errno != EEXIST)
! 107: return(-1);
! 108: break;
! 109: case MKTEMP_LINK:
! 110: if (symlinkat(link, pfd, path) == 0)
! 111: return(0);
! 112: else if (errno != EEXIST)
! 113: return(-1);
! 114: break;
! 115: }
! 116: } while (--tries);
! 117:
! 118: errno = EEXIST;
! 119: return(-1);
! 120: }
! 121:
! 122: /*
! 123: * A combination of mkstemp(3) and openat(2).
! 124: * On success returns a file descriptor and trailing Xs are overwritten in
! 125: * path to create a unique file name.
! 126: * Returns -1 on failure.
! 127: */
! 128: int
! 129: mkstempat(int fd, char *path)
! 130: {
! 131: return(mktemp_internalat(fd, path, 0, MKTEMP_FILE, 0, NULL));
! 132: }
! 133:
! 134: /*
! 135: * A combination of mkstemp(3) and symlinkat(2).
! 136: * On success returns path with trailing Xs overwritten to create a unique
! 137: * file name.
! 138: * Returns NULL on failure.
! 139: */
! 140: char*
! 141: mkstemplinkat(char *link, int fd, char *path)
! 142: {
! 143: if (mktemp_internalat(fd, path, 0, MKTEMP_LINK, 0, link) == -1)
! 144: return(NULL);
! 145: return(path);
! 146: }
! 147:
! 148: /*
! 149: * Turn path into a suitable template for mkstemp*at functions and
! 150: * place it into the newly allocated string returned in ret.
! 151: * The caller must free ret.
! 152: * Returns -1 on failure or number of characters output to ret
! 153: * (excluding the final '\0').
! 154: */
! 155: int
! 156: mktemplate(char **ret, const char *path, int recursive)
! 157: {
! 158: int n, dirlen;
! 159: const char *cp;
! 160:
! 161: if (recursive && (cp = strrchr(path, '/')) != NULL) {
! 162: dirlen = cp - path;
! 163: if ((n = asprintf(ret, "%.*s/.%s.XXXXXXXXXX", dirlen, path,
! 164: path + dirlen + 1)) == -1)
! 165: *ret = NULL;
! 166: } else {
! 167: if ((n = asprintf(ret, ".%s.XXXXXXXXXX", path)) == -1)
! 168: *ret = NULL;
! 169: }
! 170: return(n);
! 171: }