xref: /illumos-gate/usr/src/lib/libuuid/common/uuid.c (revision e7cbe64f7a72dae5cb44f100db60ca88f3313c65)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * The copyright in this file is taken from the original Leach & Salz
30  * UUID specification, from which this implementation is derived.
31  */
32 
33 /*
34  * Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc.
35  * Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. &
36  * Digital Equipment Corporation, Maynard, Mass.  Copyright (c) 1998
37  * Microsoft.  To anyone who acknowledges that this file is provided
38  * "AS IS" without any express or implied warranty: permission to use,
39  * copy, modify, and distribute this file for any purpose is hereby
40  * granted without fee, provided that the above copyright notices and
41  * this notice appears in all source code copies, and that none of the
42  * names of Open Software Foundation, Inc., Hewlett-Packard Company,
43  * or Digital Equipment Corporation be used in advertising or
44  * publicity pertaining to distribution of the software without
45  * specific, written prior permission.  Neither Open Software
46  * Foundation, Inc., Hewlett-Packard Company, Microsoft, nor Digital
47  * Equipment Corporation makes any representations about the
48  * suitability of this software for any purpose.
49  */
50 
51 /*
52  * Module:		uuid.c
53  *
54  * Description:		This module is the workhorse for generating abstract
55  *			UUIDs.  It delegates system-specific tasks (such
56  *			as obtaining the node identifier or system time)
57  *			to the sysdep module.
58  */
59 
60 #include <ctype.h>
61 #include <sys/param.h>
62 #include <sys/stat.h>
63 #include <errno.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <strings.h>
67 #include <fcntl.h>
68 #include <unistd.h>
69 #include <uuid/uuid.h>
70 #include <thread.h>
71 #include <synch.h>
72 #include "uuid_misc.h"
73 
74 #define	STATE_LOCATION		"/var/sadm/system/uuid_state"
75 #define	URANDOM_PATH		"/dev/urandom"
76 #define	MAX_RETRY		8
77 #define	VER1_MASK		0xefff
78 
79 static	mutex_t			ulock = DEFAULTMUTEX;
80 
81 uint16_t	_get_random(void);
82 void		_get_current_time(uuid_time_t *);
83 void		struct_to_string(uuid_t, struct uuid *);
84 void		string_to_struct(struct uuid *, uuid_t);
85 int		get_ethernet_address(uuid_node_t *);
86 
87 /*
88  * local functions
89  */
90 static	int	_lock_state(char *);
91 static	void	_unlock_state(int);
92 static	void	_read_state(int, uint16_t *, uuid_time_t *,
93 		    uuid_node_t *);
94 static	int	_write_state(int, uint16_t, uuid_time_t, uuid_node_t);
95 static	void 	_format_uuid(struct uuid *, uint16_t, uuid_time_t,
96 		    uuid_node_t);
97 static	void	fill_random_bytes(uchar_t *, int);
98 static	int	uuid_create(struct uuid *);
99 
100 static	void	gen_ethernet_address(uuid_node_t *);
101 /*
102  * Name:		uuid_create.
103  *
104  * Description:	Generates a uuid based on Version 1 format
105  *
106  * Returns:	0 on success, -1 on Error
107  */
108 static int
109 uuid_create(struct uuid *uuid)
110 {
111 	uuid_time_t	timestamp, last_time;
112 	uint16_t	clockseq = 0;
113 	uuid_node_t	last_node;
114 	uuid_node_t	system_node;
115 	int		locked_state_fd;
116 	int		non_unique = 0;
117 
118 	if (mutex_lock(&ulock) != 0) {
119 	    return (-1);
120 	}
121 
122 	gen_ethernet_address(&system_node);
123 	/*
124 	 * acquire system wide lock so we're alone
125 	 */
126 	locked_state_fd = _lock_state(STATE_LOCATION);
127 	if (locked_state_fd < 0) {
128 	    /* couldn't create and/or lock state; don't have access */
129 	    non_unique++;
130 	} else {
131 	    /* read saved state from disk */
132 	    _read_state(locked_state_fd, &clockseq, &last_time,
133 			&last_node);
134 	}
135 
136 	if (clockseq == 0) {
137 	    /* couldn't read clock sequence; generate a random one */
138 	    clockseq = _get_random();
139 	    non_unique++;
140 	}
141 	if (memcmp(&system_node, &last_node, sizeof (uuid_node_t)) != 0) {
142 	    clockseq++;
143 	}
144 
145 	/*
146 	 * get current time
147 	 */
148 	_get_current_time(&timestamp);
149 
150 	/*
151 	 * If timestamp is not set or is not in the past,
152 	 * increment clock sequence.
153 	 */
154 	if ((last_time == 0) || (last_time >= timestamp)) {
155 	    clockseq++;
156 	    last_time = timestamp;
157 	}
158 
159 	if (non_unique)
160 		system_node.nodeID[0] |= 0x80;
161 	/*
162 	 * stuff fields into the UUID
163 	 */
164 	_format_uuid(uuid, clockseq, timestamp, system_node);
165 	if ((locked_state_fd >= 0) &&
166 		(_write_state(locked_state_fd, clockseq, timestamp,
167 		system_node) == -1)) {
168 	    _unlock_state(locked_state_fd);
169 	    (void) mutex_unlock(&ulock);
170 	    return (-1);
171 	}
172 	/*
173 	 * Unlock system-wide lock
174 	 */
175 	_unlock_state(locked_state_fd);
176 	(void) mutex_unlock(&ulock);
177 	return (0);
178 }
179 
180 /*
181  * Name:	gen_ethernet_address
182  *
183  * Description: Fills system_node with Ethernet address if available,
184  *		else fills random numbers
185  *
186  * Returns:	Nothing
187  */
188 static void
189 gen_ethernet_address(uuid_node_t *system_node)
190 {
191 	uchar_t		node[6];
192 
193 	if (get_ethernet_address(system_node) != 0) {
194 		fill_random_bytes(node, 6);
195 		(void) memcpy(system_node->nodeID, node, 6);
196 		/*
197 		 * use 8:0:20 with the multicast bit set
198 		 * to avoid namespace collisions.
199 		 */
200 		system_node->nodeID[0] = 0x88;
201 		system_node->nodeID[1] = 0x00;
202 		system_node->nodeID[2] = 0x20;
203 	}
204 }
205 
206 /*
207  * Name:	_format_uuid
208  *
209  * Description: Formats a UUID, given the clock_seq timestamp,
210  * 		and node address.  Fills in passed-in pointer with
211  *		the resulting uuid.
212  *
213  * Returns:	None.
214  */
215 static void
216 _format_uuid(struct uuid *uuid, uint16_t clock_seq,
217     uuid_time_t timestamp, uuid_node_t node)
218 {
219 
220 	/*
221 	 * First set up the first 60 bits from the timestamp
222 	 */
223 	uuid->time_low = (uint32_t)(timestamp & 0xFFFFFFFF);
224 	uuid->time_mid = (uint16_t)((timestamp >> 32) & 0xFFFF);
225 	uuid->time_hi_and_version = (uint16_t)((timestamp >> 48) &
226 	    0x0FFF);
227 
228 	/*
229 	 * This is version 1, so say so in the UUID version field (4 bits)
230 	 */
231 	uuid->time_hi_and_version |= (1 << 12);
232 
233 	/*
234 	 * Now do the clock sequence
235 	 */
236 	uuid->clock_seq_low = clock_seq & 0xFF;
237 
238 	/*
239 	 * We must save the most-significant 2 bits for the reserved field
240 	 */
241 	uuid->clock_seq_hi_and_reserved = (clock_seq & 0x3F00) >> 8;
242 
243 	/*
244 	 * The variant for this format is the 2 high bits set to 10,
245 	 * so here it is
246 	 */
247 	uuid->clock_seq_hi_and_reserved |= 0x80;
248 
249 	/*
250 	 * write result to passed-in pointer
251 	 */
252 	(void) memcpy(&uuid->node_addr, &node, sizeof (uuid->node_addr));
253 }
254 
255 /*
256  * Name:	_read_state
257  *
258  * Description: Reads non-volatile state from a (possibly) saved statefile.
259  * 		For each non-null pointer passed-in, the corresponding
260  *		information from the statefile is filled in.
261  *		the resulting uuid.
262  *
263  * Returns:	Nothing.
264  */
265 static void
266 _read_state(int fd, uint16_t *clockseq,
267     uuid_time_t *timestamp, uuid_node_t *node)
268 {
269 	uuid_state_t	vol_state;
270 
271 	bzero(node, sizeof (uuid_node_t));
272 	*timestamp = 0;
273 	*clockseq = 0;
274 
275 	if (read(fd, &vol_state, sizeof (vol_state)) < sizeof (vol_state)) {
276 		/* This file is being accessed the first time */
277 		return;
278 	}
279 
280 	*node = vol_state.node;
281 	*timestamp = vol_state.ts;
282 	*clockseq = vol_state.cs;
283 }
284 
285 
286 /*
287  * Name:	_write_state
288  *
289  * Description: Writes non-volatile state from the passed-in information.
290  *
291  * Returns:	-1 on error, 0 otherwise.
292  */
293 static int
294 _write_state(int fd, uint16_t clockseq,
295     uuid_time_t timestamp, uuid_node_t node)
296 {
297 	uuid_state_t	vol_state;
298 
299 	vol_state.cs = clockseq;
300 	vol_state.ts = timestamp;
301 	vol_state.node = node;
302 	/*
303 	 * seek to beginning of file and write data
304 	 */
305 	if (lseek(fd, 0, SEEK_SET) != -1) {
306 	    if (write(fd, &vol_state, sizeof (uuid_state_t)) != -1) {
307 		return (0);
308 	    }
309 	}
310 	return (-1);
311 }
312 
313 
314 
315 /*
316  * Name:	_uuid_print
317  *
318  * Description:	Prints a nicely-formatted uuid to stdout.
319  *
320  * Returns:	None.
321  *
322  */
323 void
324 uuid_print(struct uuid u)
325 {
326 	int i;
327 
328 	(void) printf("%8.8x-%4.4x-%4.4x-%2.2x%2.2x-", u.time_low, u.time_mid,
329 	    u.time_hi_and_version, u.clock_seq_hi_and_reserved,
330 	    u.clock_seq_low);
331 	for (i = 0; i < 6; i++)
332 		(void) printf("%2.2x", u.node_addr[i]);
333 	(void) printf("\n");
334 }
335 
336 /*
337  * Name:	_lock_state
338  *
339  * Description:	Locks down the statefile, by first creating the file
340  *		if it doesn't exist.
341  *
342  * Returns:	A non-negative file descriptor referring to the locked
343  *		state file, if it was able to be created and/or locked,
344  *		or -1 otherwise.
345  */
346 static int
347 _lock_state(char *loc)
348 {
349 	int fd;
350 	struct flock lock;
351 
352 	fd = open(loc, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
353 
354 	if (fd < 0) {
355 		return (-1);
356 	}
357 
358 	lock.l_type = F_WRLCK;
359 	lock.l_start = 0;
360 	lock.l_whence = SEEK_SET;
361 	lock.l_len = 0;
362 
363 	if (fcntl(fd, F_SETLKW, &lock) == -1) {
364 		/*
365 		 * File could not be locked, bail
366 		 */
367 		(void) close(fd);
368 		return (-1);
369 	}
370 	return (fd);
371 }
372 
373 /*
374  * Name:	_unlock_state
375  *
376  * Description:	Unlocks a locked statefile, and close()'s the file.
377  *
378  * Returns:	Nothing.
379  */
380 void
381 _unlock_state(int fd)
382 {
383 	struct flock lock;
384 
385 	lock.l_type = F_UNLCK;
386 	lock.l_start = 0;
387 	lock.l_whence = SEEK_SET;
388 	lock.l_len = 0;
389 
390 	(void) fcntl(fd, F_SETLK, &lock);
391 	(void) close(fd);
392 }
393 
394 /*
395  * Name:	fill_random_bytes
396  *
397  * Description:	fills buf with random numbers - nbytes is the number of bytes
398  *		to fill-in. Tries to use /dev/urandom random number generator-
399  *		if that fails for some reason, it retries MAX_RETRY times. If
400  *		it still fails then it uses srand48(3C)
401  *
402  * Returns:	Nothing.
403  */
404 static void
405 fill_random_bytes(uchar_t *buf, int nbytes)
406 {
407 	int i, fd, retries = 0;
408 
409 	fd = open(URANDOM_PATH, O_RDONLY);
410 	if (fd >= 0) {
411 	    while (nbytes > 0) {
412 		i = read(fd, buf, nbytes);
413 		if ((i < 0) && (errno == EINTR)) {
414 		    continue;
415 		}
416 		if (i <= 0) {
417 		    if (retries++ == MAX_RETRY)
418 			break;
419 		    continue;
420 		}
421 		nbytes -= i;
422 		buf += i;
423 		retries = 0;
424 	    }
425 	    if (nbytes == 0) {
426 		(void) close(fd);
427 		return;
428 	    }
429 	}
430 	for (i = 0; i < nbytes; i++) {
431 	    *buf++ = _get_random() & 0xFF;
432 	}
433 	if (fd >= 0) {
434 	    (void) close(fd);
435 	}
436 }
437 
438 /*
439  * Name:	struct_to_string
440  *
441  * Description:	Unpacks the structure members in "struct uuid" to a char
442  *		string "uuid_t".
443  *
444  * Returns:	Nothing.
445  */
446 void
447 struct_to_string(uuid_t ptr, struct uuid *uu)
448 {
449 	uint_t		tmp;
450 	uchar_t		*out = ptr;
451 
452 	tmp = uu->time_low;
453 	out[3] = (uchar_t)tmp;
454 	tmp >>= 8;
455 	out[2] = (uchar_t)tmp;
456 	tmp >>= 8;
457 	out[1] = (uchar_t)tmp;
458 	tmp >>= 8;
459 	out[0] = (uchar_t)tmp;
460 
461 	tmp = uu->time_mid;
462 	out[5] = (uchar_t)tmp;
463 	tmp >>= 8;
464 	out[4] = (uchar_t)tmp;
465 
466 	tmp = uu->time_hi_and_version;
467 	out[7] = (uchar_t)tmp;
468 	tmp >>= 8;
469 	out[6] = (uchar_t)tmp;
470 
471 	tmp = uu->clock_seq_hi_and_reserved;
472 	out[8] = (uchar_t)tmp;
473 	tmp = uu->clock_seq_low;
474 	out[9] = (uchar_t)tmp;
475 
476 	(void) memcpy(out+10, uu->node_addr, 6);
477 
478 }
479 
480 /*
481  * Name:	string_to_struct
482  *
483  * Description:	Packs the values in the "uuid_t" string into "struct uuid".
484  *
485  * Returns:	Nothing
486  */
487 void
488 string_to_struct(struct uuid *uuid, uuid_t in)
489 {
490 
491 	uchar_t 	*ptr;
492 	uint_t		tmp;
493 
494 	ptr = in;
495 
496 	tmp = *ptr++;
497 	tmp = (tmp << 8) | *ptr++;
498 	tmp = (tmp << 8) | *ptr++;
499 	tmp = (tmp << 8) | *ptr++;
500 	uuid->time_low = tmp;
501 
502 	tmp = *ptr++;
503 	tmp = (tmp << 8) | *ptr++;
504 	uuid->time_mid = tmp;
505 
506 	tmp = *ptr++;
507 	tmp = (tmp << 8) | *ptr++;
508 	uuid->time_hi_and_version = tmp;
509 
510 	tmp = *ptr++;
511 	uuid->clock_seq_hi_and_reserved = tmp;
512 
513 	tmp = *ptr++;
514 	uuid->clock_seq_low = tmp;
515 
516 	(void) memcpy(uuid->node_addr, ptr, 6);
517 
518 }
519 
520 /*
521  * Name:	uuid_generate_random
522  *
523  * Description:	Generates UUID based on DCE Version 4
524  *
525  * Returns:	Nothing. uu contains the newly generated UUID
526  */
527 void
528 uuid_generate_random(uuid_t uu)
529 {
530 
531 	struct uuid	uuid;
532 
533 	if (uu == NULL)
534 	    return;
535 
536 	(void) memset(uu, 0, sizeof (uuid_t));
537 	(void) memset(&uuid, 0, sizeof (struct uuid));
538 
539 	fill_random_bytes(uu, sizeof (uuid_t));
540 	string_to_struct(&uuid, uu);
541 	/*
542 	 * This is version 4, so say so in the UUID version field (4 bits)
543 	 */
544 	uuid.time_hi_and_version |= (1 << 14);
545 	/*
546 	 * we don't want the bit 1 to be set also which is for version 1
547 	 */
548 	uuid.time_hi_and_version &= VER1_MASK;
549 
550 	/*
551 	 * The variant for this format is the 2 high bits set to 10,
552 	 * so here it is
553 	 */
554 	uuid.clock_seq_hi_and_reserved |= 0x80;
555 
556 	/*
557 	 * Set MSB of Ethernet address to 1 to indicate that it was generated
558 	 * randomly
559 	 */
560 	uuid.node_addr[0] |= 0x80;
561 	struct_to_string(uu, &uuid);
562 }
563 
564 /*
565  * Name:	uuid_generate_time
566  *
567  * Description:	Generates UUID based on DCE Version 1.
568  *
569  * Returns:	Nothing. uu contains the newly generated UUID.
570  */
571 void
572 uuid_generate_time(uuid_t uu)
573 {
574 	struct uuid uuid;
575 
576 	if (uu == NULL)
577 	    return;
578 
579 	if (uuid_create(&uuid) == -1) {
580 	    uuid_generate_random(uu);
581 	    return;
582 	}
583 	struct_to_string(uu, &uuid);
584 }
585 
586 /*
587  * Name:	uuid_generate
588  *
589  * Description:	Creates a new UUID. The uuid will be generated based on
590  *		high-quality randomness from /dev/urandom, if available by
591  *		calling uuid_generate_random. If it failed to generate UUID
592  *		then uuid_generate will call uuid_generate_time.
593  *
594  * Returns:	Nothing. uu contains the newly generated UUID.
595  */
596 void
597 uuid_generate(uuid_t uu)
598 {
599 	int fd;
600 
601 	if (uu == NULL) {
602 	    return;
603 	}
604 	fd = open(URANDOM_PATH, O_RDONLY);
605 	if (fd >= 0) {
606 	    (void) close(fd);
607 	    uuid_generate_random(uu);
608 	} else {
609 	    (void) uuid_generate_time(uu);
610 	}
611 }
612 
613 /*
614  * Name:	uuid_copy
615  *
616  * Description:	The uuid_copy function copies the UUID variable src to dst
617  *
618  * Returns:	Nothing
619  */
620 void
621 uuid_copy(uuid_t dst, uuid_t src)
622 {
623 
624 	(void) memcpy(dst, src, UUID_LEN);
625 }
626 
627 /*
628  * Name:	uuid_clear
629  *
630  * Description:	The uuid_clear function sets the value of the supplied uuid
631  *		variable uu, to the NULL value.
632  *
633  * Returns:	Nothing
634  */
635 void
636 uuid_clear(uuid_t uu)
637 {
638 	(void) memset(uu, 0, UUID_LEN);
639 }
640 
641 /*
642  * Name:	uuid_unparse
643  *
644  * Description:	This function converts the supplied UUID uu from the internal
645  *		binary format into a 36-byte string (plus trailing null char)
646  *		and stores this value in the character string pointed to by out
647  *
648  * Returns:	Nothing.
649  */
650 void
651 uuid_unparse(uuid_t uu, char *out)
652 {
653 	struct uuid 	uuid;
654 	uint16_t	clock_seq;
655 	char		etheraddr[13];
656 	int		index = 0, i;
657 
658 	/* basic sanity checking */
659 	if (uu == NULL) {
660 	    return;
661 	}
662 
663 	/* XXX user should have allocated enough memory */
664 	/*
665 	 * if (strlen(out) < UUID_PRINTABLE_STRING_LENGTH) {
666 	 * return;
667 	 * }
668 	 */
669 	string_to_struct(&uuid, uu);
670 	clock_seq = uuid.clock_seq_hi_and_reserved;
671 	clock_seq = (clock_seq  << 8) | uuid.clock_seq_low;
672 	for (i = 0; i < 6; i++) {
673 	    (void) sprintf(&etheraddr[index++], "%.2x", uuid.node_addr[i]);
674 	    index++;
675 	}
676 	etheraddr[index] = '\0';
677 
678 	(void) snprintf(out, 25, "%08x-%04x-%04x-%04x-",
679 	    uuid.time_low, uuid.time_mid, uuid.time_hi_and_version,
680 		clock_seq);
681 	(void) strlcat(out, etheraddr, UUID_PRINTABLE_STRING_LENGTH);
682 }
683 
684 /*
685  * Name:	uuid_is_null
686  *
687  * Description:	The uuid_is_null function compares the value of the supplied
688  *		UUID variable uu to the NULL value. If the value is equal
689  *		to the NULL UUID, 1 is returned, otherwise 0 is returned.
690  *
691  * Returns:	0 if uu is NOT null, 1 if uu is NULL.
692  */
693 int
694 uuid_is_null(uuid_t uu)
695 {
696 	int		i;
697 	uuid_t		null_uu;
698 
699 	(void) memset(null_uu, 0, sizeof (uuid_t));
700 	i = memcmp(uu, null_uu, sizeof (uuid_t));
701 	if (i == 0) {
702 	/* uu is NULL uuid */
703 	    return (1);
704 	} else {
705 	    return (0);
706 	}
707 }
708 
709 /*
710  * Name:	uuid_parse
711  *
712  * Description:	uuid_parse converts the UUID string given by 'in' into the
713  *		internal uuid_t format. The input UUID is a string of the form
714  *		cefa7a9c-1dd2-11b2-8350-880020adbeef in printf(3C) format.
715  *		Upon successfully parsing the input string, UUID is stored
716  *		in the location pointed to by uu
717  *
718  * Returns:	0 if the UUID is successfully stored, -1 otherwise.
719  */
720 int
721 uuid_parse(char *in, uuid_t uu)
722 {
723 
724 	char		*ptr, buf[3];
725 	int		i;
726 	struct uuid	uuid;
727 	uint16_t	clock_seq;
728 
729 	/* do some sanity checking */
730 	if ((strlen(in) != 36) || (uu == NULL) || (in[36] != '\0')) {
731 	    return (-1);
732 	}
733 
734 	ptr = in;
735 	for (i = 0; i < 36; i++, ptr++) {
736 	    if ((i == 8) || (i == 13) || (i == 18) || (i == 23)) {
737 		if (*ptr != '-') {
738 		    return (-1);
739 		}
740 	    } else {
741 		if (!isxdigit(*ptr)) {
742 		    return (-1);
743 		}
744 	    }
745 	}
746 
747 	uuid.time_low = strtoul(in, NULL, 16);
748 	uuid.time_mid = strtoul(in+9, NULL, 16);
749 	uuid.time_hi_and_version = strtoul(in+14, NULL, 16);
750 	clock_seq = strtoul(in+19, NULL, 16);
751 	uuid.clock_seq_hi_and_reserved = (clock_seq & 0xFF00) >> 8;
752 	uuid.clock_seq_low = (clock_seq & 0xFF);
753 
754 	ptr = in+24;
755 	buf[2] = '\0';
756 	for (i = 0; i < 6; i++) {
757 	    buf[0] = *ptr++;
758 	    buf[1] = *ptr++;
759 	    uuid.node_addr[i] = strtoul(buf, NULL, 16);
760 	}
761 	struct_to_string(uu, &uuid);
762 	return (0);
763 }
764 
765 /*
766  * Name:	uuid_time
767  *
768  * Description:	uuid_time extracts the time at which the supplied UUID uu
769  *		was created. This function can only extract the creation
770  *		time for UUIDs created with the uuid_generate_time function.
771  *		The time at which the UUID was created, in seconds and
772  *		microseconds since the epoch is stored in the location
773  *		pointed to by ret_tv.
774  *
775  * Returns:	The time at which the UUID was created, in seconds since
776  *		January  1, 1970 GMT (the epoch). -1 otherwise.
777  */
778 time_t
779 uuid_time(uuid_t uu, struct timeval *ret_tv)
780 {
781 	struct uuid	uuid;
782 	uint_t		high;
783 	struct timeval	tv;
784 	u_longlong_t	clock_reg;
785 	uint_t		tmp;
786 	uint8_t		clk;
787 
788 	string_to_struct(&uuid, uu);
789 	tmp = (uuid.time_hi_and_version & 0xF000) >> 12;
790 	clk = uuid.clock_seq_hi_and_reserved;
791 
792 	/* check if uu is NULL, Version = 1 of DCE and Variant = 0b10x */
793 	if ((uu == NULL) || ((tmp & 0x01) != 0x01) || ((clk & 0x80) != 0x80)) {
794 	    return (-1);
795 	}
796 	high = uuid.time_mid | ((uuid.time_hi_and_version & 0xFFF) << 16);
797 	clock_reg = uuid.time_low | ((u_longlong_t)high << 32);
798 
799 	clock_reg -= (((u_longlong_t)0x01B21DD2) << 32) + 0x13814000;
800 	tv.tv_sec = clock_reg / 10000000;
801 	tv.tv_usec = (clock_reg % 10000000) / 10;
802 
803 	if (ret_tv) {
804 	    *ret_tv = tv;
805 	}
806 
807 	return (tv.tv_sec);
808 }
809