PageRenderTime 104ms CodeModel.GetById 46ms RepoModel.GetById 1ms app.codeStats 0ms

/usr/src/lib/libuuid/common/uuid.c

https://github.com/richlowe/illumos-gate
C | 674 lines | 380 code | 97 blank | 197 comment | 82 complexity | e6fc80387889360a8d367f4ecfff1f50 MD5 | raw file
  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. * Copyright 2012 Milan Jurik. All rights reserved.
  25. */
  26. /*
  27. * The copyright in this file is taken from the original Leach & Salz
  28. * UUID specification, from which this implementation is derived.
  29. */
  30. /*
  31. * Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc.
  32. * Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. &
  33. * Digital Equipment Corporation, Maynard, Mass. Copyright (c) 1998
  34. * Microsoft. To anyone who acknowledges that this file is provided
  35. * "AS IS" without any express or implied warranty: permission to use,
  36. * copy, modify, and distribute this file for any purpose is hereby
  37. * granted without fee, provided that the above copyright notices and
  38. * this notice appears in all source code copies, and that none of the
  39. * names of Open Software Foundation, Inc., Hewlett-Packard Company,
  40. * or Digital Equipment Corporation be used in advertising or
  41. * publicity pertaining to distribution of the software without
  42. * specific, written prior permission. Neither Open Software
  43. * Foundation, Inc., Hewlett-Packard Company, Microsoft, nor Digital
  44. * Equipment Corporation makes any representations about the
  45. * suitability of this software for any purpose.
  46. */
  47. /*
  48. * This module is the workhorse for generating abstract
  49. * UUIDs. It delegates system-specific tasks (such
  50. * as obtaining the node identifier or system time)
  51. * to the sysdep module.
  52. */
  53. #include <ctype.h>
  54. #include <sys/param.h>
  55. #include <sys/stat.h>
  56. #include <errno.h>
  57. #include <stdio.h>
  58. #include <stdlib.h>
  59. #include <strings.h>
  60. #include <fcntl.h>
  61. #include <unistd.h>
  62. #include <synch.h>
  63. #include <sys/mman.h>
  64. #include "uuid_misc.h"
  65. shared_buffer_t *data;
  66. static uuid_node_t node_id_cache;
  67. static int node_init;
  68. static int file_type;
  69. static int fd;
  70. /*
  71. * misc routines
  72. */
  73. uint16_t get_random(void);
  74. void get_current_time(uuid_time_t *);
  75. void struct_to_string(uuid_t, struct uuid *);
  76. void string_to_struct(struct uuid *, uuid_t);
  77. int get_ethernet_address(uuid_node_t *);
  78. /*
  79. * local functions
  80. */
  81. static int map_state();
  82. static void format_uuid(struct uuid *, uint16_t, uuid_time_t,
  83. uuid_node_t);
  84. static void fill_random_bytes(uchar_t *, int);
  85. static int uuid_create(struct uuid *);
  86. static void gen_ethernet_address(uuid_node_t *);
  87. static void revalidate_data(uuid_node_t *);
  88. /*
  89. * Generates a uuid based on version 1 format.
  90. * Returns 0 on success and -1 on failure.
  91. */
  92. static int
  93. uuid_create(struct uuid *uuid)
  94. {
  95. uuid_time_t timestamp;
  96. uuid_node_t system_node;
  97. int ret, non_unique = 0;
  98. /*
  99. * Get the system MAC address and/or cache it
  100. */
  101. if (node_init) {
  102. bcopy(&node_id_cache, &system_node, sizeof (uuid_node_t));
  103. } else {
  104. gen_ethernet_address(&system_node);
  105. bcopy(&system_node, &node_id_cache, sizeof (uuid_node_t));
  106. node_init = 1;
  107. }
  108. /*
  109. * Access the state file, mmap it and initialize the shared lock.
  110. * file_type tells us whether we had access to the state file or
  111. * created a temporary one.
  112. */
  113. if (map_state() == -1)
  114. return (-1);
  115. /*
  116. * Acquire the lock
  117. */
  118. for (;;) {
  119. if ((ret = mutex_lock(&data->lock)) == 0)
  120. break;
  121. else
  122. switch (ret) {
  123. case EOWNERDEAD:
  124. revalidate_data(&system_node);
  125. (void) mutex_consistent(&data->lock);
  126. (void) mutex_unlock(&data->lock);
  127. break;
  128. case ENOTRECOVERABLE:
  129. return (ret);
  130. }
  131. }
  132. /* State file is either new or is temporary, get a random clock seq */
  133. if (data->state.clock == 0) {
  134. data->state.clock = get_random();
  135. non_unique++;
  136. }
  137. if (memcmp(&system_node, &data->state.node, sizeof (uuid_node_t)) != 0)
  138. data->state.clock++;
  139. get_current_time(&timestamp);
  140. /*
  141. * If timestamp is not set or is not in the past, bump
  142. * data->state.clock
  143. */
  144. if ((data->state.ts == 0) || (data->state.ts >= timestamp)) {
  145. data->state.clock++;
  146. data->state.ts = timestamp;
  147. }
  148. if (non_unique)
  149. system_node.nodeID[0] |= 0x80;
  150. /* Stuff fields into the UUID struct */
  151. format_uuid(uuid, data->state.clock, timestamp, system_node);
  152. (void) mutex_unlock(&data->lock);
  153. return (0);
  154. }
  155. /*
  156. * Fills system_node with Ethernet address if available,
  157. * else fills random numbers
  158. */
  159. static void
  160. gen_ethernet_address(uuid_node_t *system_node)
  161. {
  162. uchar_t node[6];
  163. if (get_ethernet_address(system_node) != 0) {
  164. fill_random_bytes(node, 6);
  165. (void) memcpy(system_node->nodeID, node, 6);
  166. /*
  167. * use 8:0:20 with the multicast bit set
  168. * to avoid namespace collisions.
  169. */
  170. system_node->nodeID[0] = 0x88;
  171. system_node->nodeID[1] = 0x00;
  172. system_node->nodeID[2] = 0x20;
  173. }
  174. }
  175. /*
  176. * Formats a UUID, given the clock_seq timestamp, and node address.
  177. * Fills in passed-in pointer with the resulting uuid.
  178. */
  179. static void
  180. format_uuid(struct uuid *uuid, uint16_t clock_seq,
  181. uuid_time_t timestamp, uuid_node_t node)
  182. {
  183. /*
  184. * First set up the first 60 bits from the timestamp
  185. */
  186. uuid->time_low = (uint32_t)(timestamp & 0xFFFFFFFF);
  187. uuid->time_mid = (uint16_t)((timestamp >> 32) & 0xFFFF);
  188. uuid->time_hi_and_version = (uint16_t)((timestamp >> 48) & 0x0FFF);
  189. /*
  190. * This is version 1, so say so in the UUID version field (4 bits)
  191. */
  192. uuid->time_hi_and_version |= (1 << 12);
  193. /*
  194. * Now do the clock sequence
  195. */
  196. uuid->clock_seq_low = clock_seq & 0xFF;
  197. /*
  198. * We must save the most-significant 2 bits for the reserved field
  199. */
  200. uuid->clock_seq_hi_and_reserved = (clock_seq & 0x3F00) >> 8;
  201. /*
  202. * The variant for this format is the 2 high bits set to 10,
  203. * so here it is
  204. */
  205. uuid->clock_seq_hi_and_reserved |= 0x80;
  206. /*
  207. * write result to passed-in pointer
  208. */
  209. (void) memcpy(&uuid->node_addr, &node, sizeof (uuid->node_addr));
  210. }
  211. /*
  212. * Opens/creates the state file, falling back to a tmp
  213. */
  214. static int
  215. map_state()
  216. {
  217. FILE *tmp;
  218. /* If file's mapped, return */
  219. if (file_type != 0)
  220. return (1);
  221. if ((fd = open(STATE_LOCATION, O_RDWR)) < 0) {
  222. file_type = TEMP_FILE;
  223. if ((tmp = tmpfile()) == NULL)
  224. return (-1);
  225. else
  226. fd = fileno(tmp);
  227. } else {
  228. file_type = STATE_FILE;
  229. }
  230. (void) ftruncate(fd, (off_t)sizeof (shared_buffer_t));
  231. /* LINTED - alignment */
  232. data = (shared_buffer_t *)mmap(NULL, sizeof (shared_buffer_t),
  233. PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
  234. if (data == MAP_FAILED)
  235. return (-1);
  236. (void) mutex_init(&data->lock, USYNC_PROCESS|LOCK_ROBUST, 0);
  237. (void) close(fd);
  238. return (1);
  239. }
  240. static void
  241. revalidate_data(uuid_node_t *node)
  242. {
  243. int i;
  244. data->state.ts = 0;
  245. for (i = 0; i < sizeof (data->state.node.nodeID); i++)
  246. data->state.node.nodeID[i] = 0;
  247. data->state.clock = 0;
  248. gen_ethernet_address(node);
  249. bcopy(node, &node_id_cache, sizeof (uuid_node_t));
  250. node_init = 1;
  251. }
  252. /*
  253. * Prints a nicely-formatted uuid to stdout.
  254. */
  255. void
  256. uuid_print(struct uuid u)
  257. {
  258. int i;
  259. (void) printf("%8.8x-%4.4x-%4.4x-%2.2x%2.2x-", u.time_low, u.time_mid,
  260. u.time_hi_and_version, u.clock_seq_hi_and_reserved,
  261. u.clock_seq_low);
  262. for (i = 0; i < 6; i++)
  263. (void) printf("%2.2x", u.node_addr[i]);
  264. (void) printf("\n");
  265. }
  266. /*
  267. * Fills buf with random numbers - nbytes is the number of bytes
  268. * to fill-in. Tries to use /dev/urandom random number generator-
  269. * if that fails for some reason, it retries MAX_RETRY times. If
  270. * it still fails then it uses srand48(3C)
  271. */
  272. static void
  273. fill_random_bytes(uchar_t *buf, int nbytes)
  274. {
  275. int i, fd, retries = 0;
  276. fd = open(URANDOM_PATH, O_RDONLY);
  277. if (fd >= 0) {
  278. while (nbytes > 0) {
  279. i = read(fd, buf, nbytes);
  280. if ((i < 0) && (errno == EINTR)) {
  281. continue;
  282. }
  283. if (i <= 0) {
  284. if (retries++ == MAX_RETRY)
  285. break;
  286. continue;
  287. }
  288. nbytes -= i;
  289. buf += i;
  290. retries = 0;
  291. }
  292. if (nbytes == 0) {
  293. (void) close(fd);
  294. return;
  295. }
  296. }
  297. for (i = 0; i < nbytes; i++) {
  298. *buf++ = get_random() & 0xFF;
  299. }
  300. if (fd >= 0) {
  301. (void) close(fd);
  302. }
  303. }
  304. /*
  305. * Unpacks the structure members in "struct uuid" to a char string "uuid_t".
  306. */
  307. void
  308. struct_to_string(uuid_t ptr, struct uuid *uu)
  309. {
  310. uint_t tmp;
  311. uchar_t *out = ptr;
  312. tmp = uu->time_low;
  313. out[3] = (uchar_t)tmp;
  314. tmp >>= 8;
  315. out[2] = (uchar_t)tmp;
  316. tmp >>= 8;
  317. out[1] = (uchar_t)tmp;
  318. tmp >>= 8;
  319. out[0] = (uchar_t)tmp;
  320. tmp = uu->time_mid;
  321. out[5] = (uchar_t)tmp;
  322. tmp >>= 8;
  323. out[4] = (uchar_t)tmp;
  324. tmp = uu->time_hi_and_version;
  325. out[7] = (uchar_t)tmp;
  326. tmp >>= 8;
  327. out[6] = (uchar_t)tmp;
  328. tmp = uu->clock_seq_hi_and_reserved;
  329. out[8] = (uchar_t)tmp;
  330. tmp = uu->clock_seq_low;
  331. out[9] = (uchar_t)tmp;
  332. (void) memcpy(out+10, uu->node_addr, 6);
  333. }
  334. /*
  335. * Packs the values in the "uuid_t" string into "struct uuid".
  336. */
  337. void
  338. string_to_struct(struct uuid *uuid, uuid_t in)
  339. {
  340. uchar_t *ptr;
  341. uint_t tmp;
  342. ptr = in;
  343. tmp = *ptr++;
  344. tmp = (tmp << 8) | *ptr++;
  345. tmp = (tmp << 8) | *ptr++;
  346. tmp = (tmp << 8) | *ptr++;
  347. uuid->time_low = tmp;
  348. tmp = *ptr++;
  349. tmp = (tmp << 8) | *ptr++;
  350. uuid->time_mid = tmp;
  351. tmp = *ptr++;
  352. tmp = (tmp << 8) | *ptr++;
  353. uuid->time_hi_and_version = tmp;
  354. tmp = *ptr++;
  355. uuid->clock_seq_hi_and_reserved = tmp;
  356. tmp = *ptr++;
  357. uuid->clock_seq_low = tmp;
  358. (void) memcpy(uuid->node_addr, ptr, 6);
  359. }
  360. /*
  361. * Generates UUID based on DCE Version 4
  362. */
  363. void
  364. uuid_generate_random(uuid_t uu)
  365. {
  366. struct uuid uuid;
  367. if (uu == NULL)
  368. return;
  369. (void) memset(uu, 0, sizeof (uuid_t));
  370. (void) memset(&uuid, 0, sizeof (struct uuid));
  371. fill_random_bytes(uu, sizeof (uuid_t));
  372. string_to_struct(&uuid, uu);
  373. /*
  374. * This is version 4, so say so in the UUID version field (4 bits)
  375. */
  376. uuid.time_hi_and_version |= (1 << 14);
  377. /*
  378. * we don't want the bit 1 to be set also which is for version 1
  379. */
  380. uuid.time_hi_and_version &= VER1_MASK;
  381. /*
  382. * The variant for this format is the 2 high bits set to 10,
  383. * so here it is
  384. */
  385. uuid.clock_seq_hi_and_reserved |= 0x80;
  386. /*
  387. * Set MSB of Ethernet address to 1 to indicate that it was generated
  388. * randomly
  389. */
  390. uuid.node_addr[0] |= 0x80;
  391. struct_to_string(uu, &uuid);
  392. }
  393. /*
  394. * Generates UUID based on DCE Version 1.
  395. */
  396. void
  397. uuid_generate_time(uuid_t uu)
  398. {
  399. struct uuid uuid;
  400. if (uu == NULL)
  401. return;
  402. if (uuid_create(&uuid) < 0) {
  403. uuid_generate_random(uu);
  404. return;
  405. }
  406. struct_to_string(uu, &uuid);
  407. }
  408. /*
  409. * Creates a new UUID. The uuid will be generated based on high-quality
  410. * randomness from /dev/urandom, if available by calling uuid_generate_random.
  411. * If it failed to generate UUID then uuid_generate will call
  412. * uuid_generate_time.
  413. */
  414. void
  415. uuid_generate(uuid_t uu)
  416. {
  417. int fd;
  418. if (uu == NULL) {
  419. return;
  420. }
  421. fd = open(URANDOM_PATH, O_RDONLY);
  422. if (fd >= 0) {
  423. (void) close(fd);
  424. uuid_generate_random(uu);
  425. } else {
  426. (void) uuid_generate_time(uu);
  427. }
  428. }
  429. /*
  430. * Copies the UUID variable src to dst.
  431. */
  432. void
  433. uuid_copy(uuid_t dst, uuid_t src)
  434. {
  435. (void) memcpy(dst, src, UUID_LEN);
  436. }
  437. /*
  438. * Sets the value of the supplied uuid variable uu, to the NULL value.
  439. */
  440. void
  441. uuid_clear(uuid_t uu)
  442. {
  443. (void) memset(uu, 0, UUID_LEN);
  444. }
  445. /*
  446. * This function converts the supplied UUID uu from the internal
  447. * binary format into a 36-byte string (plus trailing null char)
  448. * and stores this value in the character string pointed to by out.
  449. */
  450. void
  451. uuid_unparse(uuid_t uu, char *out)
  452. {
  453. struct uuid uuid;
  454. uint16_t clock_seq;
  455. char etheraddr[13];
  456. int index = 0, i;
  457. /* basic sanity checking */
  458. if (uu == NULL) {
  459. return;
  460. }
  461. /* XXX user should have allocated enough memory */
  462. /*
  463. * if (strlen(out) < UUID_PRINTABLE_STRING_LENGTH) {
  464. * return;
  465. * }
  466. */
  467. string_to_struct(&uuid, uu);
  468. clock_seq = uuid.clock_seq_hi_and_reserved;
  469. clock_seq = (clock_seq << 8) | uuid.clock_seq_low;
  470. for (i = 0; i < 6; i++) {
  471. (void) sprintf(&etheraddr[index++], "%.2x", uuid.node_addr[i]);
  472. index++;
  473. }
  474. etheraddr[index] = '\0';
  475. (void) snprintf(out, 25, "%08x-%04x-%04x-%04x-",
  476. uuid.time_low, uuid.time_mid, uuid.time_hi_and_version, clock_seq);
  477. (void) strlcat(out, etheraddr, UUID_PRINTABLE_STRING_LENGTH);
  478. }
  479. /*
  480. * The uuid_is_null function compares the value of the supplied
  481. * UUID variable uu to the NULL value. If the value is equal
  482. * to the NULL UUID, 1 is returned, otherwise 0 is returned.
  483. */
  484. int
  485. uuid_is_null(uuid_t uu)
  486. {
  487. int i;
  488. uuid_t null_uu;
  489. (void) memset(null_uu, 0, sizeof (uuid_t));
  490. i = memcmp(uu, null_uu, sizeof (uuid_t));
  491. if (i == 0) {
  492. /* uu is NULL uuid */
  493. return (1);
  494. } else {
  495. return (0);
  496. }
  497. }
  498. /*
  499. * uuid_parse converts the UUID string given by 'in' into the
  500. * internal uuid_t format. The input UUID is a string of the form
  501. * cefa7a9c-1dd2-11b2-8350-880020adbeef in printf(3C) format.
  502. * Upon successfully parsing the input string, UUID is stored
  503. * in the location pointed to by uu
  504. */
  505. int
  506. uuid_parse(char *in, uuid_t uu)
  507. {
  508. char *ptr, buf[3];
  509. int i;
  510. struct uuid uuid;
  511. uint16_t clock_seq;
  512. /* do some sanity checking */
  513. if ((strlen(in) != 36) || (uu == NULL) || (in[36] != '\0')) {
  514. return (-1);
  515. }
  516. ptr = in;
  517. for (i = 0; i < 36; i++, ptr++) {
  518. if ((i == 8) || (i == 13) || (i == 18) || (i == 23)) {
  519. if (*ptr != '-') {
  520. return (-1);
  521. }
  522. } else {
  523. if (!isxdigit(*ptr)) {
  524. return (-1);
  525. }
  526. }
  527. }
  528. uuid.time_low = strtoul(in, NULL, 16);
  529. uuid.time_mid = strtoul(in+9, NULL, 16);
  530. uuid.time_hi_and_version = strtoul(in+14, NULL, 16);
  531. clock_seq = strtoul(in+19, NULL, 16);
  532. uuid.clock_seq_hi_and_reserved = (clock_seq & 0xFF00) >> 8;
  533. uuid.clock_seq_low = (clock_seq & 0xFF);
  534. ptr = in+24;
  535. buf[2] = '\0';
  536. for (i = 0; i < 6; i++) {
  537. buf[0] = *ptr++;
  538. buf[1] = *ptr++;
  539. uuid.node_addr[i] = strtoul(buf, NULL, 16);
  540. }
  541. struct_to_string(uu, &uuid);
  542. return (0);
  543. }
  544. /*
  545. * uuid_time extracts the time at which the supplied UUID uu
  546. * was created. This function can only extract the creation
  547. * time for UUIDs created with the uuid_generate_time function.
  548. * The time at which the UUID was created, in seconds and
  549. * microseconds since the epoch is stored in the location
  550. * pointed to by ret_tv.
  551. */
  552. time_t
  553. uuid_time(uuid_t uu, struct timeval *ret_tv)
  554. {
  555. struct uuid uuid;
  556. uint_t high;
  557. struct timeval tv;
  558. u_longlong_t clock_reg;
  559. uint_t tmp;
  560. uint8_t clk;
  561. string_to_struct(&uuid, uu);
  562. tmp = (uuid.time_hi_and_version & 0xF000) >> 12;
  563. clk = uuid.clock_seq_hi_and_reserved;
  564. /* check if uu is NULL, Version = 1 of DCE and Variant = 0b10x */
  565. if ((uu == NULL) || ((tmp & 0x01) != 0x01) || ((clk & 0x80) != 0x80)) {
  566. return (-1);
  567. }
  568. high = uuid.time_mid | ((uuid.time_hi_and_version & 0xFFF) << 16);
  569. clock_reg = uuid.time_low | ((u_longlong_t)high << 32);
  570. clock_reg -= (((u_longlong_t)0x01B21DD2) << 32) + 0x13814000;
  571. tv.tv_sec = clock_reg / 10000000;
  572. tv.tv_usec = (clock_reg % 10000000) / 10;
  573. if (ret_tv) {
  574. *ret_tv = tv;
  575. }
  576. return (tv.tv_sec);
  577. }