[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.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: }