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 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