]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blob - lib/uuid/gen_uuid.c
gen_uuid.c (uuid_generate_time): Fix bug pointed out by Ralf
[thirdparty/e2fsprogs.git] / lib / uuid / gen_uuid.c
1 /*
2 * gen_uuid.c --- generate a DCE-compatible uuid
3 *
4 * Copyright (C) 1996, 1997, 1998, 1999 Theodore Ts'o.
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU
8 * Library General Public License.
9 * %End-Header%
10 */
11
12 /*
13 * Force inclusion of SVID stuff since we need it if we're compiling in
14 * gcc-wall wall mode
15 */
16 #define _SVID_SOURCE
17
18 #ifdef HAVE_UNISTD_H
19 #include <unistd.h>
20 #endif
21 #ifdef HAVE_STDLIB_H
22 #include <stdlib.h>
23 #endif
24 #include <string.h>
25 #include <fcntl.h>
26 #include <errno.h>
27 #include <sys/types.h>
28 #include <sys/time.h>
29 #include <sys/stat.h>
30 #include <sys/file.h>
31 #ifdef HAVE_SYS_IOCTL_H
32 #include <sys/ioctl.h>
33 #endif
34 #ifdef HAVE_SYS_SOCKET_H
35 #include <sys/socket.h>
36 #endif
37 #ifdef HAVE_SYS_SOCKIO_H
38 #include <sys/sockio.h>
39 #endif
40 #ifdef HAVE_NET_IF_H
41 #include <net/if.h>
42 #endif
43 #ifdef HAVE_NETINET_IN_H
44 #include <netinet/in.h>
45 #endif
46
47 #include "uuidP.h"
48
49 #ifdef HAVE_SRANDOM
50 #define srand(x) srandom(x)
51 #define rand() random()
52 #endif
53
54 static int get_random_fd(void)
55 {
56 struct timeval tv;
57 static int fd = -2;
58 int i;
59
60 if (fd == -2) {
61 gettimeofday(&tv, 0);
62 fd = open("/dev/urandom", O_RDONLY);
63 if (fd == -1)
64 fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
65 srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec);
66 }
67 /* Crank the random number generator a few times */
68 gettimeofday(&tv, 0);
69 for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--)
70 rand();
71 return fd;
72 }
73
74
75 /*
76 * Generate a series of random bytes. Use /dev/urandom if possible,
77 * and if not, use srandom/random.
78 */
79 static void get_random_bytes(void *buf, int nbytes)
80 {
81 int i, n = nbytes, fd = get_random_fd();
82 int lose_counter = 0;
83 unsigned char *cp = (unsigned char *) buf;
84
85 if (fd >= 0) {
86 while (n > 0) {
87 i = read(fd, cp, n);
88 if (i <= 0) {
89 if (lose_counter++ > 16)
90 break;
91 continue;
92 }
93 n -= i;
94 cp += i;
95 lose_counter = 0;
96 }
97 }
98
99 /*
100 * We do this all the time, but this is the only source of
101 * randomness if /dev/random/urandom is out to lunch.
102 */
103 for (cp = buf, i = 0; i < nbytes; i++)
104 *cp++ ^= (rand() >> 7) & 0xFF;
105 return;
106 }
107
108 /*
109 * Get the ethernet hardware address, if we can find it...
110 */
111 static int get_node_id(unsigned char *node_id)
112 {
113 #ifdef HAVE_NET_IF_H
114 int sd;
115 struct ifreq ifr, *ifrp;
116 struct ifconf ifc;
117 char buf[1024];
118 int n, i;
119 unsigned char *a;
120
121 /*
122 * BSD 4.4 defines the size of an ifreq to be
123 * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
124 * However, under earlier systems, sa_len isn't present, so the size is
125 * just sizeof(struct ifreq)
126 */
127 #ifdef HAVE_SA_LEN
128 #ifndef max
129 #define max(a,b) ((a) > (b) ? (a) : (b))
130 #endif
131 #define ifreq_size(i) max(sizeof(struct ifreq),\
132 sizeof((i).ifr_name)+(i).ifr_addr.sa_len)
133 #else
134 #define ifreq_size(i) sizeof(struct ifreq)
135 #endif /* HAVE_SA_LEN*/
136
137 sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
138 if (sd < 0) {
139 return -1;
140 }
141 memset(buf, 0, sizeof(buf));
142 ifc.ifc_len = sizeof(buf);
143 ifc.ifc_buf = buf;
144 if (ioctl (sd, SIOCGIFCONF, (char *)&ifc) < 0) {
145 close(sd);
146 return -1;
147 }
148 n = ifc.ifc_len;
149 for (i = 0; i < n; i+= ifreq_size(*ifr) ) {
150 ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i);
151 strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ);
152 #ifdef SIOCGIFHWADDR
153 if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0)
154 continue;
155 a = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
156 #else
157 #ifdef SIOCGENADDR
158 if (ioctl(sd, SIOCGENADDR, &ifr) < 0)
159 continue;
160 a = (unsigned char *) ifr.ifr_enaddr;
161 #else
162 /*
163 * XXX we don't have a way of getting the hardware
164 * address
165 */
166 close(sd);
167 return 0;
168 #endif /* SIOCGENADDR */
169 #endif /* SIOCGIFHWADDR */
170 if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5])
171 continue;
172 if (node_id) {
173 memcpy(node_id, a, 6);
174 close(sd);
175 return 1;
176 }
177 }
178 close(sd);
179 #endif
180 return 0;
181 }
182
183 /* Assume that the gettimeofday() has microsecond granularity */
184 #define MAX_ADJUSTMENT 10
185
186 static int get_clock(__u32 *clock_high, __u32 *clock_low, __u16 *ret_clock_seq)
187 {
188 static int adjustment = 0;
189 static struct timeval last = {0, 0};
190 static __u16 clock_seq;
191 struct timeval tv;
192 unsigned long long clock_reg;
193
194 try_again:
195 gettimeofday(&tv, 0);
196 if ((last.tv_sec == 0) && (last.tv_usec == 0)) {
197 get_random_bytes(&clock_seq, sizeof(clock_seq));
198 clock_seq &= 0x1FFF;
199 last = tv;
200 last.tv_sec--;
201 }
202 if ((tv.tv_sec < last.tv_sec) ||
203 ((tv.tv_sec == last.tv_sec) &&
204 (tv.tv_usec < last.tv_usec))) {
205 clock_seq = (clock_seq+1) & 0x1FFF;
206 adjustment = 0;
207 last = tv;
208 } else if ((tv.tv_sec == last.tv_sec) &&
209 (tv.tv_usec == last.tv_usec)) {
210 if (adjustment >= MAX_ADJUSTMENT)
211 goto try_again;
212 adjustment++;
213 } else {
214 adjustment = 0;
215 last = tv;
216 }
217
218 clock_reg = tv.tv_usec*10 + adjustment;
219 clock_reg += ((unsigned long long) tv.tv_sec)*10000000;
220 clock_reg += (((unsigned long long) 0x01B21DD2) << 32) + 0x13814000;
221
222 *clock_high = clock_reg >> 32;
223 *clock_low = clock_reg;
224 *ret_clock_seq = clock_seq;
225 return 0;
226 }
227
228 void uuid_generate_time(uuid_t out)
229 {
230 static unsigned char node_id[6];
231 static int has_init = 0;
232 struct uuid uu;
233 __u32 clock_mid;
234
235 if (!has_init) {
236 if (get_node_id(node_id) <= 0) {
237 get_random_bytes(node_id, 6);
238 /*
239 * Set multicast bit, to prevent conflicts
240 * with IEEE 802 addresses obtained from
241 * network cards
242 */
243 node_id[0] |= 0x01;
244 }
245 has_init = 1;
246 }
247 get_clock(&clock_mid, &uu.time_low, &uu.clock_seq);
248 uu.clock_seq |= 0x8000;
249 uu.time_mid = (__u16) clock_mid;
250 uu.time_hi_and_version = (clock_mid >> 16) | 0x1000;
251 memcpy(uu.node, node_id, 6);
252 uuid_pack(&uu, out);
253 }
254
255 void uuid_generate_random(uuid_t out)
256 {
257 uuid_t buf;
258 struct uuid uu;
259
260 get_random_bytes(buf, sizeof(buf));
261 uuid_unpack(buf, &uu);
262
263 uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000;
264 uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) | 0x4000;
265 uuid_pack(&uu, out);
266 }
267
268 /*
269 * This is the generic front-end to uuid_generate_random and
270 * uuid_generate_time. It uses uuid_generate_random only if
271 * /dev/urandom is available, since otherwise we won't have
272 * high-quality randomness.
273 */
274 void uuid_generate(uuid_t out)
275 {
276 if (get_random_fd() >= 0)
277 uuid_generate_random(out);
278 else
279 uuid_generate_time(out);
280 }