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 * Copyright 2009, 2011, 2014
12 * The Board of Trustees of the Leland Stanford Junior University
13 *
14 * Copying and distribution of this file, with or without modification, are
15 * permitted in any medium without royalty provided the copyright notice and
16 * this notice are preserved. This file is offered as-is, without any
17 * warranty.
18 *
19 * SPDX-License-Identifier: FSFAP
20 */
21
22 #include <config.h>
23 #include <portable/system.h>
24
25 #include <errno.h>
26 #include <fcntl.h>
27 #ifdef HAVE_SYS_TIME_H
28 # include <sys/time.h>
29 #endif
30 #include <time.h>
31
32 /*
33 * If we're running the test suite, rename mkstemp to avoid conflicts with the
34 * system version. #undef it first because some systems may define it to
35 * another name.
36 */
37 #if TESTING
38 # undef mkstemp
39 # define mkstemp test_mkstemp
40 int test_mkstemp(char *);
41 #endif
42
43 /* Pick the longest available integer type. */
44 #if HAVE_LONG_LONG_INT
45 typedef unsigned long long long_int_type;
46 #else
47 typedef unsigned long long_int_type;
48 #endif
49
50 int
mkstemp(char * template)51 mkstemp(char *template)
52 {
53 static const char letters[] =
54 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
55 size_t length;
56 char *XXXXXX;
57 struct timeval tv;
58 long_int_type randnum, working;
59 int i, tries, fd;
60
61 /*
62 * Make sure we have a valid template and initialize p to point at the
63 * beginning of the template portion of the string.
64 */
65 length = strlen(template);
66 if (length < 6) {
67 errno = EINVAL;
68 return -1;
69 }
70 XXXXXX = template + length - 6;
71 if (strcmp(XXXXXX, "XXXXXX") != 0) {
72 errno = EINVAL;
73 return -1;
74 }
75
76 /* Get some more-or-less random information. */
77 gettimeofday(&tv, NULL);
78 randnum = ((long_int_type) tv.tv_usec << 16) ^ tv.tv_sec ^ getpid();
79
80 /*
81 * Now, try to find a working file name. We try no more than TMP_MAX file
82 * names.
83 */
84 for (tries = 0; tries < TMP_MAX; tries++) {
85 for (working = randnum, i = 0; i < 6; i++) {
86 XXXXXX[i] = letters[working % 62];
87 working /= 62;
88 }
89 fd = open(template, O_RDWR | O_CREAT | O_EXCL, 0600);
90 if (fd >= 0 || (errno != EEXIST && errno != EISDIR))
91 return fd;
92
93 /*
94 * This is a relatively random increment. Cut off the tail end of
95 * tv_usec since it's often predictable.
96 */
97 randnum += (tv.tv_usec >> 10) & 0xfff;
98 }
99 errno = EEXIST;
100 return -1;
101 }
102