prepare to upload
[debian/sudo] / compat / mktemp.c
1 /*
2  * Copyright (c) 2001, 2003, 2004, 2008-2011
3  *      Todd C. Miller <Todd.Miller@courtesan.com>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 #include <config.h>
19
20 #include <sys/types.h>
21 #include <sys/time.h>
22 #include <sys/stat.h>
23
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <limits.h>
27 #include <stdio.h>
28 #ifdef HAVE_STDLIB_H
29 # include <stdlib.h>
30 #endif /* HAVE_STDLIB_H */
31 #include <ctype.h>
32 #ifdef HAVE_UNISTD_H
33 # include <unistd.h>
34 #endif /* HAVE_UNISTD_H */
35 #if TIME_WITH_SYS_TIME
36 # include <time.h>
37 #endif
38
39 #include "missing.h"
40
41 #define MKTEMP_FILE     1
42 #define MKTEMP_DIR      2
43
44 #define TEMPCHARS       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
45 #define NUM_CHARS       (sizeof(TEMPCHARS) - 1)
46
47 #ifndef INT_MAX
48 #define INT_MAX 0x7fffffff
49 #endif
50
51 #ifdef HAVE_RANDOM
52 # define RAND           random
53 # define SRAND          srandom
54 # define SEED_T         unsigned int
55 #else
56 # ifdef HAVE_LRAND48
57 #  define RAND          lrand48
58 #  define SRAND         srand48
59 #  define SEED_T        long
60 # else
61 #  define RAND          rand
62 #  define SRAND         srand
63 #  define SEED_T        unsigned int
64 # endif
65 #endif
66
67 static void
68 seed_random(void)
69 {
70         SEED_T seed;
71         struct timeval tv;
72
73         /*
74          * Seed from time of day and process id multiplied by small primes.
75          */
76         (void) gettimeofday(&tv, NULL);
77         seed = (tv.tv_sec % 10000) * 523 + tv.tv_usec * 13 +
78             (getpid() % 1000) * 983;
79         SRAND(seed);
80 }
81
82 static unsigned int
83 get_random(void)
84 {
85         static int initialized;
86
87         if (!initialized) {
88                 seed_random();
89                 initialized = 1;
90         }
91
92         return RAND() & 0xffffffff;
93 }
94
95 static int
96 mktemp_internal(char *path, int slen, int mode)
97 {
98         char *start, *cp, *ep;
99         const char *tempchars = TEMPCHARS;
100         unsigned int r, tries;
101         int fd;
102
103         for (ep = path; *ep; ep++)
104                 ;
105         if (path + slen >= ep) {
106                 errno = EINVAL;
107                 return -1;
108         }
109         ep -= slen;
110
111         tries = 1;
112         for (start = ep; start > path && start[-1] == 'X'; start--) {
113                 if (tries < INT_MAX / NUM_CHARS)
114                         tries *= NUM_CHARS;
115         }
116         tries *= 2;
117
118         do {
119                 for (cp = start; *cp; cp++) {
120                         r = get_random() % NUM_CHARS;
121                         *cp = tempchars[r];
122                 }
123
124                 switch (mode) {
125                 case MKTEMP_FILE:
126                         fd = open(path, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR);
127                         if (fd != -1 || errno != EEXIST)
128                                 return fd;
129                         break;
130                 case MKTEMP_DIR:
131                         if (mkdir(path, S_IRWXU) == 0)
132                                 return 0;
133                         if (errno != EEXIST)
134                                 return -1;
135                         break;
136                 }
137         } while (--tries);
138
139         errno = EEXIST;
140         return -1;
141 }
142
143 #ifndef HAVE_MKSTEMPS
144 int
145 mkstemps(char *path, int slen)
146 {
147         return mktemp_internal(path, slen, MKTEMP_FILE);
148 }
149 #endif /* HAVE_MKSTEMPS */
150
151 #ifndef HAVE_MKDTEMP
152 char *
153 mkdtemp(char *path)
154 {
155         if (mktemp_internal(path, 0, MKTEMP_DIR) == -1)
156                 return NULL;
157         return path;
158 }
159 #endif /* HAVE_MKDTEMP */