[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.4

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