]> eyrie.org Git - kerberos/krb5-strength.git/blob - portable/mkstemp.c
New upstream version 3.1
[kerberos/krb5-strength.git] / portable / mkstemp.c
1 /*
2  * Replacement for a missing mkstemp.
3  *
4  * Provides the same functionality as the library function mkstemp for those
5  * systems that don't have it.
6  *
7  * The canonical version of this file is maintained in the rra-c-util package,
8  * which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
9  *
10  * Written by Russ Allbery <eagle@eyrie.org>
11  *
12  * The authors hereby relinquish any claim to any copyright that they may have
13  * in this work, whether granted under contract or by operation of law or
14  * international treaty, and hereby commit to the public, at large, that they
15  * shall not, at any time in the future, seek to enforce any copyright in this
16  * work against any person or entity, or prevent any person or entity from
17  * copying, publishing, distributing or creating derivative works of this
18  * work.
19  */
20
21 #include <config.h>
22 #include <portable/system.h>
23
24 #include <errno.h>
25 #include <fcntl.h>
26 #ifdef HAVE_SYS_TIME_H
27 # include <sys/time.h>
28 #endif
29 #include <time.h>
30
31 /*
32  * If we're running the test suite, rename mkstemp to avoid conflicts with the
33  * system version.  #undef it first because some systems may define it to
34  * another name.
35  */
36 #if TESTING
37 # undef mkstemp
38 # define mkstemp test_mkstemp
39 int test_mkstemp(char *);
40 #endif
41
42 /* Pick the longest available integer type. */
43 #if HAVE_LONG_LONG_INT
44 typedef unsigned long long long_int_type;
45 #else
46 typedef unsigned long long_int_type;
47 #endif
48
49 int
50 mkstemp(char *template)
51 {
52     static const char letters[] =
53         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
54     size_t length;
55     char *XXXXXX;
56     struct timeval tv;
57     long_int_type randnum, working;
58     int i, tries, fd;
59
60     /*
61      * Make sure we have a valid template and initialize p to point at the
62      * beginning of the template portion of the string.
63      */
64     length = strlen(template);
65     if (length < 6) {
66         errno = EINVAL;
67         return -1;
68     }
69     XXXXXX = template + length - 6;
70     if (strcmp(XXXXXX, "XXXXXX") != 0) {
71         errno = EINVAL;
72         return -1;
73     }
74
75     /* Get some more-or-less random information. */
76     gettimeofday(&tv, NULL);
77     randnum = ((long_int_type) tv.tv_usec << 16) ^ tv.tv_sec ^ getpid();
78
79     /*
80      * Now, try to find a working file name.  We try no more than TMP_MAX file
81      * names.
82      */
83     for (tries = 0; tries < TMP_MAX; tries++) {
84         for (working = randnum, i = 0; i < 6; i++) {
85             XXXXXX[i] = letters[working % 62];
86             working /= 62;
87         }
88         fd = open(template, O_RDWR | O_CREAT | O_EXCL, 0600);
89         if (fd >= 0 || (errno != EEXIST && errno != EISDIR))
90             return fd;
91
92         /*
93          * This is a relatively random increment.  Cut off the tail end of
94          * tv_usec since it's often predictable.
95          */
96         randnum += (tv.tv_usec >> 10) & 0xfff;
97     }
98     errno = EEXIST;
99     return -1;
100 }