OSDN Git Service

Ok, this commit is _huge_ and its gonna change the world. I've
[uclinux-h8/uClibc.git] / libc / misc / internals / tempname.c
1 /* Copyright (C) 1991,92,93,94,95,96,97,98,99 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Library General Public License as
6    published by the Free Software Foundation; either version 2 of the
7    License, or (at your option) any later version.
8
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Library General Public License for more details.
13
14    You should have received a copy of the GNU Library General Public
15    License along with the GNU C Library; see the file COPYING.LIB.  If not,
16    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17    Boston, MA 02111-1307, USA.  */
18
19 /* March 11, 2002       Manuel Novoa III
20  *
21  * Modify code to remove dependency on libgcc long long arith support funcs.
22  */
23
24 #include <stddef.h>
25 #include <stdint.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <assert.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <sys/time.h>
36 #include "tempname.h"
37
38
39 /* Return nonzero if DIR is an existent directory.  */
40 static int direxists (const char *dir)
41 {
42     struct stat buf;
43     return stat(dir, &buf) == 0 && S_ISDIR (buf.st_mode);
44 }
45
46 /* Path search algorithm, for tmpnam, tmpfile, etc.  If DIR is
47    non-null and exists, uses it; otherwise uses the first of $TMPDIR,
48    P_tmpdir, /tmp that exists.  Copies into TMPL a template suitable
49    for use with mk[s]temp.  Will fail (-1) if DIR is non-null and
50    doesn't exist, none of the searched dirs exists, or there's not
51    enough space in TMPL. */
52 int __path_search (char *tmpl, size_t tmpl_len, const char *dir, 
53         const char *pfx, int try_tmpdir)
54 {
55     //const char *d;
56     size_t dlen, plen;
57
58     if (!pfx || !pfx[0])
59     {
60         pfx = "file";
61         plen = 4;
62     }
63     else
64     {
65         plen = strlen (pfx);
66         if (plen > 5)
67             plen = 5;
68     }
69
70 #if 0
71     if (try_tmpdir)
72     {
73         d = __secure_getenv ("TMPDIR");
74         if (d != NULL && direxists (d))
75             dir = d;
76         else if (dir != NULL && direxists (dir))
77             /* nothing */ ;
78         else
79             dir = NULL;
80     }
81 #endif  
82     if (dir == NULL)
83     {
84         if (direxists (P_tmpdir))
85             dir = P_tmpdir;
86         else if (strcmp (P_tmpdir, "/tmp") != 0 && direxists ("/tmp"))
87             dir = "/tmp";
88         else
89         {
90             __set_errno (ENOENT);
91             return -1;
92         }
93     }
94
95     dlen = strlen (dir);
96     while (dlen > 1 && dir[dlen - 1] == '/')
97         dlen--;                 /* remove trailing slashes */
98
99     /* check we have room for "${dir}/${pfx}XXXXXX\0" */
100     if (tmpl_len < dlen + 1 + plen + 6 + 1)
101     {
102         __set_errno (EINVAL);
103         return -1;
104     }
105
106     sprintf (tmpl, "%.*s/%.*sXXXXXX", (int) dlen, dir, (int) plen, pfx);
107     return 0;
108 }
109
110 /* These are the characters used in temporary filenames.  */
111 static const char letters[] =
112 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
113
114 /* Generate a temporary file name based on TMPL.  TMPL must match the
115    rules for mk[s]temp (i.e. end in "XXXXXX").  The name constructed
116    does not exist at the time of the call to __gen_tempname.  TMPL is
117    overwritten with the result.  
118
119    KIND may be one of:
120    __GT_NOCREATE:       simply verify that the name does not exist
121                         at the time of the call.
122    __GT_FILE:           create the file using open(O_CREAT|O_EXCL)
123                         and return a read-write fd.  The file is mode 0600.
124    __GT_BIGFILE:        same as __GT_FILE but use open64().
125    __GT_DIR:            create a directory, which will be mode 0700.
126
127    We use a clever algorithm to get hard-to-predict names. */
128 int __gen_tempname (char *tmpl, int kind)
129 {
130     char *XXXXXX;
131     struct timeval tv;
132     uint32_t high, low, rh;
133     unsigned int k;
134     int len, i, count, fd, save_errno = errno;
135     static uint64_t value; /* Do not initialize this, 
136                               or lock it for multi-threaded
137                               apps -- the messier the better */
138
139     len = strlen (tmpl);
140     if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX"))
141     {
142         __set_errno (EINVAL);
143         return -1;
144     }
145
146     /* This is where the Xs start.  */
147     XXXXXX = &tmpl[len - 6];
148
149     /* Get some more or less random data.  */
150     gettimeofday (&tv, NULL);
151     value += ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec ^ getpid ();
152
153     for (count = 0; count < TMP_MAX; value += 7777, ++count)
154     {
155         low = value & UINT32_MAX;
156         high = value >> 32;
157
158         for (i = 0 ; i < 6 ; i++) {
159             rh = high % 62;
160             high /= 62;
161 #define L ((UINT32_MAX % 62 + 1) % 62)
162             k = (low % 62) + (L * rh);
163 #undef L
164 #define H ((UINT32_MAX / 62) + ((UINT32_MAX % 62 + 1) / 62))
165             low = (low / 62) + (H * rh) + (k / 62);
166 #undef H
167             k %= 62;
168             XXXXXX[i] = letters[k];
169         }
170
171         switch(kind) {
172             case __GT_NOCREATE:
173                 {
174                     struct stat st;
175                     if (stat (tmpl, &st) < 0)
176                     {
177                         if (errno == ENOENT)
178                         {
179                             __set_errno (save_errno);
180                             return 0;
181                         }
182                         else
183                             /* Give up now. */
184                             return -1;
185                     }
186                     else
187                         continue;
188                 }
189             case __GT_FILE:
190                 fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
191                 break;
192 #if defined __UCLIBC_HAS_LFS__
193             case __GT_BIGFILE:
194                 fd = open64 (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
195                 break;
196 #endif
197             case __GT_DIR:
198                 fd = mkdir (tmpl, S_IRUSR | S_IWUSR | S_IXUSR);
199                 break;
200             default:
201                 fd = -1;
202                 assert (! "invalid KIND in __gen_tempname");
203         }
204
205         if (fd >= 0) {
206             __set_errno (save_errno);
207             return fd;
208         }
209         else if (errno != EEXIST)
210             /* Any other error will apply also to other names we might
211                try, and there are 2^32 or so of them, so give up now. */
212             return -1;
213     }
214
215     /* We got out of the loop because we ran out of combinations to try.  */
216     __set_errno (EEXIST);
217     return -1;
218 }