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