[BACK]Return to mktemp.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / rsync

Annotation of src/usr.bin/rsync/mktemp.c, Revision 1.10

1.10    ! benno       1: /*     $OpenBSD: mktemp.c,v 1.9 2019/05/08 20:00:25 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.10    ! benno     286: mktemplate(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) {
1.9       benno     296:                        ERR("asprintf");
1.1       florian   297:                        *ret = NULL;
1.7       benno     298:                }
                    299:        } else if ((n = asprintf(ret, ".%s.XXXXXXXXXX", path)) < 0) {
1.9       benno     300:                ERR("asprintf");
1.7       benno     301:                *ret = NULL;
1.1       florian   302:        }
1.7       benno     303:
                    304:        return n;
1.1       florian   305: }