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