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