mkstemp.c 2.82 KB
Newer Older
1
2
3
4
5
6
/*
 * Replacement for a missing mkstemp.
 *
 * Provides the same functionality as the library function mkstemp for those
 * systems that don't have it.
 *
7
8
9
 * The canonical version of this file is maintained in the rra-c-util package,
 * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
 *
10
 * Written by Russ Allbery <eagle@eyrie.org>
11
12
13
14
15
16
17
18
 *
 * The authors hereby relinquish any claim to any copyright that they may have
 * in this work, whether granted under contract or by operation of law or
 * international treaty, and hereby commit to the public, at large, that they
 * shall not, at any time in the future, seek to enforce any copyright in this
 * work against any person or entity, or prevent any person or entity from
 * copying, publishing, distributing or creating derivative works of this
 * work.
19
20
21
22
23
24
25
 */

#include <config.h>
#include <portable/system.h>

#include <errno.h>
#include <fcntl.h>
26
27
28
29
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#include <time.h>
30
31
32
33
34
35
36
37
38
39
40
41
42

/*
 * If we're running the test suite, rename mkstemp to avoid conflicts with the
 * system version.  #undef it first because some systems may define it to
 * another name.
 */
#if TESTING
# undef mkstemp
# define mkstemp test_mkstemp
int test_mkstemp(char *);
#endif

/* Pick the longest available integer type. */
43
#if HAVE_LONG_LONG_INT
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
typedef unsigned long long long_int_type;
#else
typedef unsigned long long_int_type;
#endif

int
mkstemp(char *template)
{
    static const char letters[] =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    size_t length;
    char *XXXXXX;
    struct timeval tv;
    long_int_type randnum, working;
    int i, tries, fd;

    /*
     * Make sure we have a valid template and initialize p to point at the
     * beginning of the template portion of the string.
     */
    length = strlen(template);
    if (length < 6) {
        errno = EINVAL;
        return -1;
    }
    XXXXXX = template + length - 6;
    if (strcmp(XXXXXX, "XXXXXX") != 0) {
        errno = EINVAL;
        return -1;
    }

    /* Get some more-or-less random information. */
    gettimeofday(&tv, NULL);
    randnum = ((long_int_type) tv.tv_usec << 16) ^ tv.tv_sec ^ getpid();

    /*
     * Now, try to find a working file name.  We try no more than TMP_MAX file
     * names.
     */
    for (tries = 0; tries < TMP_MAX; tries++) {
        for (working = randnum, i = 0; i < 6; i++) {
            XXXXXX[i] = letters[working % 62];
            working /= 62;
        }
        fd = open(template, O_RDWR | O_CREAT | O_EXCL, 0600);
        if (fd >= 0 || (errno != EEXIST && errno != EISDIR))
            return fd;

        /*
         * This is a relatively random increment.  Cut off the tail end of
         * tv_usec since it's often predictable.
         */
        randnum += (tv.tv_usec >> 10) & 0xfff;
    }
    errno = EEXIST;
    return -1;
}