QOF  0.8.7
guid.c
1 /********************************************************************\
2  * guid.c -- globally unique ID implementation *
3  * Copyright (C) 2000 Dave Peticolas <peticola@cs.ucdavis.edu> *
4  * *
5  * This program is free software; you can redistribute it and/or *
6  * modify it under the terms of the GNU General Public License as *
7  * published by the Free Software Foundation; either version 2 of *
8  * the License, or (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License*
16  * along with this program; if not, contact: *
17  * *
18  * Free Software Foundation Voice: +1-617-542-5942 *
19  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
20  * Boston, MA 02110-1301, USA gnu@gnu.org *
21  * *
22 \********************************************************************/
23 
24 # include <config.h>
25 
26 #ifdef HAVE_SYS_TYPES_H
27 # include <sys/types.h>
28 #endif
29 #include <ctype.h>
30 #include <dirent.h>
31 #include <glib.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <sys/stat.h>
36 #ifdef HAVE_SYS_TIMES_H
37 # include <sys/times.h>
38 #endif
39 #include <time.h>
40 #include <unistd.h>
41 #include "qof.h"
42 #include "md5.h"
43 
44 # ifndef P_tmpdir
45 # define P_tmpdir "/tmp"
46 # endif
47 
48 /* Constants *******************************************************/
49 #define DEBUG_GUID 0
50 #define BLOCKSIZE 4096
51 #define THRESHOLD (2 * BLOCKSIZE)
52 
53 
54 /* Static global variables *****************************************/
55 static gboolean guid_initialized = FALSE;
56 static struct md5_ctx guid_context;
57 
58 /* This static indicates the debugging module that this .o belongs to. */
59 static QofLogModule log_module = QOF_MOD_ENGINE;
60 
61 /* Memory management routines ***************************************/
62 
63 GUID *
65 {
66  return g_slice_new (GUID);
67 }
68 
69 void
70 guid_free (GUID * guid)
71 {
72  if (!guid)
73  return;
74 
75  g_slice_free (GUID, guid);
76 }
77 
78 const GUID *
79 guid_null (void)
80 {
81  static int null_inited = 0;
82  static GUID null_guid;
83 
84  if (!null_inited)
85  {
86  int i;
87  char *tmp = "NULLGUID.EMPTY.";
88 
89  /* 16th space for '\O' */
90  for (i = 0; i < GUID_DATA_SIZE; i++)
91  null_guid.data[i] = tmp[i];
92 
93  null_inited = 1;
94  }
95 
96  return &null_guid;
97 }
98 
99 /* Function implementations ****************************************/
100 
101 /* This code is based on code in md5.c in GNU textutils. */
102 static size_t
103 init_from_stream (FILE * stream, size_t max_size)
104 {
105  char buffer[BLOCKSIZE + 72];
106  size_t sum, block_size, total;
107 
108  if (max_size <= 0)
109  return 0;
110 
111  total = 0;
112 
113  /* Iterate over file contents. */
114  while (1)
115  {
116  /* We read the file in blocks of BLOCKSIZE bytes. One call of the
117  * computation function processes the whole buffer so that with the
118  * next round of the loop another block can be read. */
119  size_t n;
120  sum = 0;
121 
122  if (max_size < BLOCKSIZE)
123  block_size = max_size;
124  else
125  block_size = BLOCKSIZE;
126 
127  /* Read block. Take care for partial reads. */
128  do
129  {
130  n = fread (buffer + sum, 1, block_size - sum, stream);
131 
132  sum += n;
133  }
134  while (sum < block_size && n != 0);
135 
136  max_size -= sum;
137 
138  if (n == 0 && ferror (stream))
139  return total;
140 
141  /* If end of file or max_size is reached, end the loop. */
142  if ((n == 0) || (max_size == 0))
143  break;
144 
145  /* Process buffer with BLOCKSIZE bytes. Note that
146  * BLOCKSIZE % 64 == 0 */
147  md5_process_block (buffer, BLOCKSIZE, &guid_context);
148 
149  total += sum;
150  }
151 
152  /* Add the last bytes if necessary. */
153  if (sum > 0)
154  {
155  md5_process_bytes (buffer, sum, &guid_context);
156  total += sum;
157  }
158 
159  return total;
160 }
161 
162 static size_t
163 init_from_file (const char *filename, size_t max_size)
164 {
165  struct stat stats;
166  size_t total = 0;
167  size_t file_bytes;
168  FILE *fp;
169 
170  memset (&stats, 0, sizeof (stats));
171  if (stat (filename, &stats) != 0)
172  return 0;
173 
174  md5_process_bytes (&stats, sizeof (stats), &guid_context);
175  total += sizeof (stats);
176 
177  if (max_size <= 0)
178  return total;
179 
180  fp = fopen (filename, "r");
181  if (fp == NULL)
182  return total;
183 
184  file_bytes = init_from_stream (fp, max_size);
185 
186  PINFO ("guid_init got %llu bytes from %s",
187  (unsigned long long int) file_bytes, filename);
188 
189  total += file_bytes;
190 
191  fclose (fp);
192 
193  return total;
194 }
195 
196 static size_t
197 init_from_dir (const char *dirname, unsigned int max_files)
198 {
199  char filename[1024];
200  struct dirent *de;
201  struct stat stats;
202  size_t total;
203  int result;
204  DIR *dir;
205 
206  if (max_files <= 0)
207  return 0;
208 
209  dir = opendir (dirname);
210  if (dir == NULL)
211  return 0;
212 
213  total = 0;
214 
215  do
216  {
217  de = readdir (dir);
218  if (de == NULL)
219  break;
220 
221  md5_process_bytes (de->d_name, strlen (de->d_name), &guid_context);
222  total += strlen (de->d_name);
223 
224  result = snprintf (filename, sizeof (filename),
225  "%s/%s", dirname, de->d_name);
226  if ((result < 0) || (result >= (int) sizeof (filename)))
227  continue;
228 
229  memset (&stats, 0, sizeof (stats));
230  if (stat (filename, &stats) != 0)
231  continue;
232  md5_process_bytes (&stats, sizeof (stats), &guid_context);
233  total += sizeof (stats);
234 
235  max_files--;
236  }
237  while (max_files > 0);
238 
239  closedir (dir);
240 
241  return total;
242 }
243 
244 static size_t
245 init_from_time (void)
246 {
247  size_t total;
248  time_t t_time;
249 #ifdef HAVE_SYS_TIMES_H
250  clock_t clocks;
251  struct tms tms_buf;
252 #endif
253 
254  total = 0;
255 
256  t_time = time (NULL);
257  md5_process_bytes (&t_time, sizeof (t_time), &guid_context);
258  total += sizeof (t_time);
259 
260 #ifdef HAVE_SYS_TIMES_H
261  clocks = times (&tms_buf);
262  md5_process_bytes (&clocks, sizeof (clocks), &guid_context);
263  md5_process_bytes (&tms_buf, sizeof (tms_buf), &guid_context);
264  total += sizeof (clocks) + sizeof (tms_buf);
265 #endif
266 
267  return total;
268 }
269 
270 static size_t
271 init_from_int (int val)
272 {
273  md5_process_bytes (&val, sizeof (val), &guid_context);
274  return sizeof (int);
275 }
276 
277 static size_t
278 init_from_buff (unsigned char *buf, size_t buflen)
279 {
280  md5_process_bytes (buf, buflen, &guid_context);
281  return buflen;
282 }
283 
284 void
285 guid_init (void)
286 {
287  size_t bytes = 0;
288 
289  /* Not needed; taken care of on first malloc.
290  * guid_memchunk_init(); */
291 
292  md5_init_ctx (&guid_context);
293 
294  /* entropy pool */
295  bytes += init_from_file ("/dev/urandom", 512);
296 
297  /* files */
298  {
299  const char *files[] = { "/etc/passwd",
300  "/proc/loadavg",
301  "/proc/meminfo",
302  "/proc/net/dev",
303  "/proc/rtc",
304  "/proc/self/environ",
305  "/proc/self/stat",
306  "/proc/stat",
307  "/proc/uptime",
308  NULL
309  };
310  int i;
311 
312  for (i = 0; files[i] != NULL; i++)
313  bytes += init_from_file (files[i], BLOCKSIZE);
314  }
315 
316  /* directories */
317  {
318  const char *dirname;
319  const char *dirs[] = {
320  "/proc",
321  P_tmpdir,
322  "/var/lock",
323  "/var/log",
324  "/var/mail",
325  "/var/spool/mail",
326  "/var/run",
327  NULL
328  };
329  int i;
330 
331  for (i = 0; dirs[i] != NULL; i++)
332  bytes += init_from_dir (dirs[i], 32);
333 
334  dirname = g_get_home_dir ();
335  if (dirname != NULL)
336  bytes += init_from_dir (dirname, 32);
337  }
338 
339  /* process and parent ids */
340  {
341  pid_t pid;
342 
343  pid = getpid ();
344  md5_process_bytes (&pid, sizeof (pid), &guid_context);
345  bytes += sizeof (pid);
346 
347 #ifdef HAVE_GETPPID
348  pid = getppid ();
349  md5_process_bytes (&pid, sizeof (pid), &guid_context);
350  bytes += sizeof (pid);
351 #endif
352  }
353 
354  /* user info */
355  {
356 #ifdef HAVE_GETUID
357  uid_t uid;
358  gid_t gid;
359  char *s;
360 
361  s = getlogin ();
362  if (s != NULL)
363  {
364  md5_process_bytes (s, strlen (s), &guid_context);
365  bytes += strlen (s);
366  }
367 
368  uid = getuid ();
369  md5_process_bytes (&uid, sizeof (uid), &guid_context);
370  bytes += sizeof (uid);
371 
372  gid = getgid ();
373  md5_process_bytes (&gid, sizeof (gid), &guid_context);
374  bytes += sizeof (gid);
375 #endif
376  }
377 
378  /* host info */
379  {
380 #ifdef HAVE_GETHOSTNAME
381  char string[1024];
382 
383  memset (string, 0, sizeof (string));
384  gethostname (string, sizeof (string));
385  md5_process_bytes (string, sizeof (string), &guid_context);
386  bytes += sizeof (string);
387 #endif
388  }
389 
390  /* plain old random */
391  {
392  int n, i;
393 
394  srand ((unsigned int) time (NULL));
395 
396  for (i = 0; i < 32; i++)
397  {
398  n = rand ();
399 
400  md5_process_bytes (&n, sizeof (n), &guid_context);
401  bytes += sizeof (n);
402  }
403  }
404 
405  /* time in secs and clock ticks */
406  bytes += init_from_time ();
407 
408  PINFO ("got %llu bytes", (unsigned long long int) bytes);
409 
410  if (bytes < THRESHOLD)
411  PWARN ("only got %llu bytes.\n"
412  "The identifiers might not be very random.\n",
413  (unsigned long long int) bytes);
414 
415  guid_initialized = TRUE;
416 }
417 
418 void
419 guid_init_with_salt (const void *salt, size_t salt_len)
420 {
421  guid_init ();
422 
423  md5_process_bytes (salt, salt_len, &guid_context);
424 }
425 
426 void
427 guid_init_only_salt (const void *salt, size_t salt_len)
428 {
429  md5_init_ctx (&guid_context);
430 
431  md5_process_bytes (salt, salt_len, &guid_context);
432 
433  guid_initialized = TRUE;
434 }
435 
436 void
438 {
439 }
440 
441 #define GUID_PERIOD 5000
442 
443 void
444 guid_new (GUID * guid)
445 {
446  static int counter = 0;
447  struct md5_ctx ctx;
448 
449  if (guid == NULL)
450  return;
451 
452  if (!guid_initialized)
453  guid_init ();
454 
455  /* make the id */
456  ctx = guid_context;
457  md5_finish_ctx (&ctx, guid->data);
458 
459  /* update the global context */
460  init_from_time ();
461 
462  /* Make it a little extra salty. I think init_from_time was buggy,
463  * or something, since duplicate id's actually happened. Or something
464  * like that. I think this is because init_from_time kept returning
465  * the same values too many times in a row. So we'll do some 'block
466  * chaining', and feed in the old guid as new random data.
467  *
468  * Anyway, I think the whole fact that I saw a bunch of duplicate
469  * id's at one point, but can't reproduce the bug is rather alarming.
470  * Something must be broken somewhere, and merely adding more salt
471  * is just hiding the problem, not fixing it.
472  */
473  init_from_int (433781 * counter);
474  init_from_buff (guid->data, GUID_DATA_SIZE);
475 
476  if (counter == 0)
477  {
478  FILE *fp;
479 
480  fp = fopen ("/dev/urandom", "r");
481  if (fp == NULL)
482  return;
483 
484  init_from_stream (fp, 32);
485 
486  fclose (fp);
487 
488  counter = GUID_PERIOD;
489  }
490 
491  counter--;
492 }
493 
494 GUID
496 {
497  GUID guid;
498 
499  guid_new (&guid);
500 
501  return guid;
502 }
503 
504 /* needs 32 bytes exactly, doesn't print a null char */
505 static void
506 encode_md5_data (const unsigned char *data, char *buffer)
507 {
508  size_t count;
509 
510  for (count = 0; count < GUID_DATA_SIZE; count++, buffer += 2)
511  sprintf (buffer, "%02x", data[count]);
512 }
513 
514 /* returns true if the first 32 bytes of buffer encode
515  * a hex number. returns false otherwise. Decoded number
516  * is packed into data in little endian order. */
517 static gboolean
518 decode_md5_string (const unsigned char *string, unsigned char *data)
519 {
520  unsigned char n1, n2;
521  size_t count = -1;
522  unsigned char c1, c2;
523 
524  if (NULL == data)
525  return FALSE;
526  if (NULL == string)
527  goto badstring;
528 
529  for (count = 0; count < GUID_DATA_SIZE; count++)
530  {
531  /* check for a short string e.g. null string ... */
532  if ((0 == string[2 * count]) || (0 == string[2 * count + 1]))
533  goto badstring;
534 
535  c1 = tolower (string[2 * count]);
536  if (!isxdigit (c1))
537  goto badstring;
538 
539  c2 = tolower (string[2 * count + 1]);
540  if (!isxdigit (c2))
541  goto badstring;
542 
543  if (isdigit (c1))
544  n1 = c1 - '0';
545  else
546  n1 = c1 - 'a' + 10;
547 
548  if (isdigit (c2))
549  n2 = c2 - '0';
550  else
551  n2 = c2 - 'a' + 10;
552 
553  data[count] = (n1 << 4) | n2;
554  }
555  return TRUE;
556 
557  badstring:
558  for (count = 0; count < GUID_DATA_SIZE; count++)
559  {
560  data[count] = 0;
561  }
562  return FALSE;
563 }
564 
565 /* Allocate the key */
566 
567 const char *
568 guid_to_string (const GUID * guid)
569 {
570 #ifdef G_THREADS_ENABLED
571 #ifdef GLIB_DEPRECATED_IN_2_32
572  static GPrivate guid_buffer_key = G_PRIVATE_INIT (g_free);
573 #else
574  static GStaticPrivate guid_buffer_key = G_STATIC_PRIVATE_INIT;
575 #endif
576  gchar *string;
577 #ifdef GLIB_DEPRECATED_IN_2_32
578  string = g_private_get (&guid_buffer_key);
579 #else
580  string = g_static_private_get (&guid_buffer_key);
581 #endif
582  if (string == NULL)
583  {
584  string = malloc (GUID_ENCODING_LENGTH + 1);
585 #ifdef GLIB_DEPRECATED_IN_2_32
586  g_private_set (&guid_buffer_key, string);
587 #else
588  g_static_private_set (&guid_buffer_key, string, g_free);
589 #endif
590  }
591 #else
592  static char string[64];
593 #endif
594 
595  encode_md5_data (guid->data, string);
596  string[GUID_ENCODING_LENGTH] = '\0';
597 
598  return string;
599 }
600 
601 char *
602 guid_to_string_buff (const GUID * guid, char *string)
603 {
604  if (!string || !guid)
605  return NULL;
606 
607  encode_md5_data (guid->data, string);
608 
609  string[GUID_ENCODING_LENGTH] = '\0';
610  return &string[GUID_ENCODING_LENGTH];
611 }
612 
613 gboolean
614 string_to_guid (const char *string, GUID * guid)
615 {
616  return decode_md5_string ((const guchar *)string, (guid != NULL) ? guid->data : NULL);
617 }
618 
619 gboolean
620 guid_equal (const GUID * guid_1, const GUID * guid_2)
621 {
622  if (guid_1 && guid_2)
623  return (memcmp (guid_1, guid_2, GUID_DATA_SIZE) == 0);
624  else
625  return FALSE;
626 }
627 
628 gint
629 guid_compare (const GUID * guid_1, const GUID * guid_2)
630 {
631  if (guid_1 == guid_2)
632  return 0;
633 
634  /* nothing is always less than something */
635  if (!guid_1 && guid_2)
636  return -1;
637 
638  if (guid_1 && !guid_2)
639  return 1;
640 
641  return memcmp (guid_1, guid_2, GUID_DATA_SIZE);
642 }
643 
644 guint
645 guid_hash_to_guint (gconstpointer ptr)
646 {
647  const GUID *guid = ptr;
648  guint hash = 0;
649  unsigned int i, j;
650 
651  if (!guid)
652  {
653  PERR ("received NULL guid pointer.");
654  return 0;
655  }
656 
657  for (i = 0, j = 0; i < sizeof (guint); i++, j++)
658  {
659  if (j == GUID_DATA_SIZE)
660  j = 0;
661 
662  hash <<= 4;
663  hash |= guid->data[j];
664  }
665 
666  return hash;
667 }
668 
669 static gint
670 guid_g_hash_table_equal (gconstpointer guid_a, gconstpointer guid_b)
671 {
672  return guid_equal (guid_a, guid_b);
673 }
674 
675 GHashTable *
676 guid_hash_table_new (void)
677 {
678  return g_hash_table_new (guid_hash_to_guint, guid_g_hash_table_equal);
679 }
gboolean guid_equal(const GUID *guid_1, const GUID *guid_2)
Definition: guid.c:620
#define PERR(format, args...)
Definition: qoflog.h:183
GUID guid_new_return(void)
Definition: guid.c:495
const GUID * guid_null(void)
Definition: guid.c:79
#define PINFO(format, args...)
Definition: qoflog.h:199
GUID * guid_malloc(void)
Definition: guid.c:64
void guid_init_with_salt(const void *salt, size_t salt_len)
Definition: guid.c:419
gboolean string_to_guid(const gchar *string, GUID *guid)
#define GUID_ENCODING_LENGTH
Definition: guid.h:64
void guid_shutdown(void)
Definition: guid.c:437
#define GUID_DATA_SIZE
Definition: guid.h:52
const char * guid_to_string(const GUID *guid)
Definition: guid.c:568
gchar * guid_to_string_buff(const GUID *guid, gchar *buff)
guint guid_hash_to_guint(gconstpointer ptr)
Definition: guid.c:645
Definition: guid.h:53
void guid_new(GUID *guid)
Definition: guid.c:444
void guid_init_only_salt(const void *salt, size_t salt_len)
Definition: guid.c:427
#define PWARN(format, args...)
Definition: qoflog.h:191
void guid_init(void)
Definition: guid.c:285
const gchar * QofLogModule
Definition: qofid.h:85
Definition: md5.h:80