hivex: Send all debug messages to stderr.
[libguestfs.git] / hivex / hivex.c
index 85d6c7b..36c6b0e 100644 (file)
@@ -30,7 +30,6 @@
 #include <iconv.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
-#include <assert.h>
 #ifdef HAVE_ENDIAN_H
 #include <endian.h>
 #endif
 #include <byteswap.h>
 #endif
 
+#define STREQ(a,b) (strcmp((a),(b)) == 0)
+#define STRCASEEQ(a,b) (strcasecmp((a),(b)) == 0)
+//#define STRNEQ(a,b) (strcmp((a),(b)) != 0)
+//#define STRCASENEQ(a,b) (strcasecmp((a),(b)) != 0)
+#define STREQLEN(a,b,n) (strncmp((a),(b),(n)) == 0)
+//#define STRCASEEQLEN(a,b,n) (strncasecmp((a),(b),(n)) == 0)
+//#define STRNEQLEN(a,b,n) (strncmp((a),(b),(n)) != 0)
+//#define STRCASENEQLEN(a,b,n) (strncasecmp((a),(b),(n)) != 0)
+//#define STRPREFIX(a,b) (strncmp((a),(b),strlen((b))) == 0)
+
 #if __BYTE_ORDER == __LITTLE_ENDIAN
 #ifndef be32toh
 #define be32toh(x) __bswap_32 (x)
 #ifndef be64toh
 #define be64toh(x) __bswap_64 (x)
 #endif
+#ifndef le16toh
+#define le16toh(x) (x)
+#endif
 #ifndef le32toh
 #define le32toh(x) (x)
 #endif
+#ifndef le64toh
+#define le64toh(x) (x)
+#endif
 #else
 #ifndef be32toh
 #define be32toh(x) (x)
 #ifndef be64toh
 #define be64toh(x) (x)
 #endif
+#ifndef le16toh
+#define le16toh(x) __bswap_16 (x)
+#endif
 #ifndef le32toh
 #define le32toh(x) __bswap_32 (x)
 #endif
+#ifndef le64toh
+#define le64toh(x) __bswap_64 (x)
+#endif
 #endif
 
 #include "hivex.h"
@@ -84,7 +105,7 @@ struct hive_h {
 #define BITMAP_SET(bitmap,off) (bitmap[(off)>>5] |= 1 << (((off)>>2)&7))
 #define BITMAP_CLR(bitmap,off) (bitmap[(off)>>5] &= ~ (1 << (((off)>>2)&7)))
 #define BITMAP_TST(bitmap,off) (bitmap[(off)>>5] & (1 << (((off)>>2)&7)))
-#define IS_VALID_BLOCK(h,off)                 \
+#define IS_VALID_BLOCK(h,off)               \
   (((off) & 3) == 0 &&                      \
    (off) >= 0x1000 &&                       \
    (off) < (h)->size &&                     \
@@ -132,7 +153,7 @@ struct ntreg_hbin_block {
 } __attribute__((__packed__));
 
 #define BLOCK_ID_EQ(h,offs,eqid) \
-  (strncmp (((struct ntreg_hbin_block *)((h)->addr + (offs)))->id, (eqid), 2) == 0)
+  (STREQLEN (((struct ntreg_hbin_block *)((h)->addr + (offs)))->id, (eqid), 2))
 
 static size_t
 block_len (hive_h *h, size_t blkoff, int *used)
@@ -224,11 +245,11 @@ hivex_open (const char *filename, int flags)
   h->msglvl = flags & HIVEX_OPEN_MSGLVL_MASK;
 
   const char *debug = getenv ("HIVEX_DEBUG");
-  if (debug && strcmp (debug, "1") == 0)
+  if (debug && STREQ (debug, "1"))
     h->msglvl = 2;
 
   if (h->msglvl >= 2)
-    printf ("hivex_open: created handle %p\n", h);
+    fprintf (stderr, "hivex_open: created handle %p\n", h);
 
   h->fd = open (filename, O_RDONLY);
   if (h->fd == -1)
@@ -245,7 +266,7 @@ hivex_open (const char *filename, int flags)
     goto error;
 
   if (h->msglvl >= 2)
-    printf ("hivex_open: mapped file at %p\n", h->addr);
+    fprintf (stderr, "hivex_open: mapped file at %p\n", h->addr);
 
   /* Check header. */
   if (h->hdr->magic[0] != 'r' ||
@@ -259,6 +280,8 @@ hivex_open (const char *filename, int flags)
   }
 
   h->bitmap = calloc (1 + h->size / 32, 1);
+  if (h->bitmap == NULL)
+    goto error;
 
 #if 0                           /* Doesn't work. */
   /* Header checksum. */
@@ -279,7 +302,7 @@ hivex_open (const char *filename, int flags)
   h->rootoffs = le32toh (h->hdr->offset) + 0x1000;
 
   if (h->msglvl >= 2)
-    printf ("hivex_open: root offset = %zu\n", h->rootoffs);
+    fprintf (stderr, "hivex_open: root offset = %zu\n", h->rootoffs);
 
   /* We'll set this flag when we see a block with the root offset (ie.
    * the root block).
@@ -302,16 +325,15 @@ hivex_open (const char *filename, int flags)
         page->magic[1] != 'b' ||
         page->magic[2] != 'i' ||
         page->magic[3] != 'n') {
-      /* This error is seemingly common in uncorrupt registry files. */
-      /*
-      fprintf (stderr, "hivex: %s: ignoring trailing garbage at end of file (at %zu, after %zu pages)\n",
-               filename, off, h->pages);
-      */
+      /* NB: This error is seemingly common in uncorrupt registry files. */
+      if (h->msglvl >= 2)
+        fprintf (stderr, "hivex: %s: ignoring trailing garbage at end of file (at %zu, after %zu pages)\n",
+                 filename, off, h->pages);
       break;
     }
 
     if (h->msglvl >= 2)
-      printf ("hivex_open: page at %zu\n", off);
+      fprintf (stderr, "hivex_open: page at %zu\n", off);
 
     if (le32toh (page->offset_next) <= sizeof (struct ntreg_hbin_page) ||
         (le32toh (page->offset_next) & 3) != 0) {
@@ -345,9 +367,9 @@ hivex_open (const char *filename, int flags)
       }
 
       if (h->msglvl >= 2)
-        printf ("hivex_open: %s block id %d,%d at %zu%s\n",
-                used ? "used" : "free", block->id[0], block->id[1], blkoff,
-                is_root ? " (root)" : "");
+        fprintf (stderr, "hivex_open: %s block id %d,%d at %zu%s\n",
+                 used ? "used" : "free", block->id[0], block->id[1], blkoff,
+                 is_root ? " (root)" : "");
 
       if (is_root && !used)
         bad_root_block = 1;
@@ -379,12 +401,13 @@ hivex_open (const char *filename, int flags)
   }
 
   if (h->msglvl >= 1)
-    printf ("hivex_open: successfully read Windows Registry hive file:\n"
-            "  pages:                  %zu\n"
-            "  blocks:                 %zu\n"
-            "  blocks used:            %zu\n"
-            "  bytes used:             %zu\n",
-            h->pages, h->blocks, h->used_blocks, h->used_size);
+    fprintf (stderr,
+             "hivex_open: successfully read Windows Registry hive file:\n"
+             "  pages:                  %zu\n"
+             "  blocks:                 %zu\n"
+             "  blocks used:            %zu\n"
+             "  bytes used:             %zu\n",
+             h->pages, h->blocks, h->used_blocks, h->used_size);
 
   return h;
 
@@ -448,7 +471,7 @@ hivex_node_name (hive_h *h, hive_node_h node)
   size_t seg_len = block_len (h, node, NULL);
   if (sizeof (struct ntreg_nk_record) + len - 1 > seg_len) {
     if (h->msglvl >= 2)
-      printf ("hivex_node_name: returning EFAULT because node name is too long (%zu, %zu)\n",
+      fprintf (stderr, "hivex_node_name: returning EFAULT because node name is too long (%zu, %zu)\n",
               len, seg_len);
     errno = EFAULT;
     return NULL;
@@ -544,8 +567,8 @@ hivex_node_children (hive_h *h, hive_node_h node)
   subkey_lf += 0x1000;
   if (!IS_VALID_BLOCK (h, subkey_lf)) {
     if (h->msglvl >= 2)
-      printf ("hivex_node_children: returning EFAULT because subkey_lf is not a valid block (%zu)\n",
-              subkey_lf);
+      fprintf (stderr, "hivex_node_children: returning EFAULT because subkey_lf is not a valid block (%zu)\n",
+               subkey_lf);
     errno = EFAULT;
     return NULL;
   }
@@ -565,8 +588,8 @@ hivex_node_children (hive_h *h, hive_node_h node)
     size_t nr_subkeys_in_lf = le16toh (lf->nr_keys);
 
     if (h->msglvl >= 2)
-      printf ("hivex_node_children: nr_subkeys_in_nk = %zu, nr_subkeys_in_lf = %zu\n",
-              nr_subkeys_in_nk, nr_subkeys_in_lf);
+      fprintf (stderr, "hivex_node_children: nr_subkeys_in_nk = %zu, nr_subkeys_in_lf = %zu\n",
+               nr_subkeys_in_nk, nr_subkeys_in_lf);
 
     if (nr_subkeys_in_nk != nr_subkeys_in_lf) {
       errno = ENOTSUP;
@@ -576,8 +599,8 @@ hivex_node_children (hive_h *h, hive_node_h node)
     size_t len = block_len (h, subkey_lf, NULL);
     if (8 + nr_subkeys_in_lf * 8 > len) {
       if (h->msglvl >= 2)
-        printf ("hivex_node_children: returning EFAULT because too many subkeys (%zu, %zu)\n",
-                nr_subkeys_in_lf, len);
+        fprintf (stderr, "hivex_node_children: returning EFAULT because too many subkeys (%zu, %zu)\n",
+                 nr_subkeys_in_lf, len);
       errno = EFAULT;
       return NULL;
     }
@@ -595,8 +618,8 @@ hivex_node_children (hive_h *h, hive_node_h node)
       subkey += 0x1000;
       if (!IS_VALID_BLOCK (h, subkey)) {
         if (h->msglvl >= 2)
-          printf ("hivex_node_children: returning EFAULT because subkey is not a valid block (%zu)\n",
-                  subkey);
+          fprintf (stderr, "hivex_node_children: returning EFAULT because subkey is not a valid block (%zu)\n",
+                   subkey);
         errno = EFAULT;
         free (ret);
         return NULL;
@@ -619,8 +642,8 @@ hivex_node_children (hive_h *h, hive_node_h node)
       offset += 0x1000;
       if (!IS_VALID_BLOCK (h, offset)) {
         if (h->msglvl >= 2)
-          printf ("hivex_node_children: returning EFAULT because ri-offset is not a valid block (%zu)\n",
-                  offset);
+          fprintf (stderr, "hivex_node_children: returning EFAULT because ri-offset is not a valid block (%zu)\n",
+                   offset);
         errno = EFAULT;
         return NULL;
       }
@@ -636,8 +659,8 @@ hivex_node_children (hive_h *h, hive_node_h node)
     }
 
     if (h->msglvl >= 2)
-      printf ("hivex_node_children: nr_subkeys_in_nk = %zu, counted = %zu\n",
-              nr_subkeys_in_nk, count);
+      fprintf (stderr, "hivex_node_children: nr_subkeys_in_nk = %zu, counted = %zu\n",
+               nr_subkeys_in_nk, count);
 
     if (nr_subkeys_in_nk != count) {
       errno = ENOTSUP;
@@ -657,8 +680,8 @@ hivex_node_children (hive_h *h, hive_node_h node)
       offset += 0x1000;
       if (!IS_VALID_BLOCK (h, offset)) {
         if (h->msglvl >= 2)
-          printf ("hivex_node_children: returning EFAULT because ri-offset is not a valid block (%zu)\n",
-                  offset);
+          fprintf (stderr, "hivex_node_children: returning EFAULT because ri-offset is not a valid block (%zu)\n",
+                   offset);
         errno = EFAULT;
         return NULL;
       }
@@ -676,8 +699,8 @@ hivex_node_children (hive_h *h, hive_node_h node)
         subkey += 0x1000;
         if (!IS_VALID_BLOCK (h, subkey)) {
           if (h->msglvl >= 2)
-            printf ("hivex_node_children: returning EFAULT because indirect subkey is not a valid block (%zu)\n",
-                    subkey);
+            fprintf (stderr, "hivex_node_children: returning EFAULT because indirect subkey is not a valid block (%zu)\n",
+                     subkey);
           errno = EFAULT;
           free (ret);
           return NULL;
@@ -712,7 +735,7 @@ hivex_node_get_child (hive_h *h, hive_node_h node, const char *nname)
   for (i = 0; children[i] != 0; ++i) {
     name = hivex_node_name (h, children[i]);
     if (!name) goto error;
-    if (strcasecmp (name, nname) == 0) {
+    if (STRCASEEQ (name, nname)) {
       ret = children[i];
       break;
     }
@@ -737,10 +760,9 @@ hivex_node_parent (hive_h *h, hive_node_h node)
 
   hive_node_h ret = le32toh (nk->parent);
   ret += 0x1000;
-  printf ("parent = %zu\n", ret);
   if (!IS_VALID_BLOCK (h, ret)) {
     if (h->msglvl >= 2)
-      printf ("hivex_node_parent: returning EFAULT because parent is not a valid block (%zu)\n",
+      fprintf (stderr, "hivex_node_parent: returning EFAULT because parent is not a valid block (%zu)\n",
               ret);
     errno = EFAULT;
     return 0;
@@ -761,7 +783,7 @@ hivex_node_values (hive_h *h, hive_node_h node)
   size_t nr_values = le32toh (nk->nr_values);
 
   if (h->msglvl >= 2)
-    printf ("hivex_node_values: nr_values = %zu\n", nr_values);
+    fprintf (stderr, "hivex_node_values: nr_values = %zu\n", nr_values);
 
   /* Deal with the common "no values" case quickly. */
   hive_node_h *ret;
@@ -784,8 +806,8 @@ hivex_node_values (hive_h *h, hive_node_h node)
   vlist_offset += 0x1000;
   if (!IS_VALID_BLOCK (h, vlist_offset)) {
     if (h->msglvl >= 2)
-      printf ("hivex_node_values: returning EFAULT because value list is not a valid block (%zu)\n",
-              vlist_offset);
+      fprintf (stderr, "hivex_node_values: returning EFAULT because value list is not a valid block (%zu)\n",
+               vlist_offset);
     errno = EFAULT;
     return NULL;
   }
@@ -796,8 +818,8 @@ hivex_node_values (hive_h *h, hive_node_h node)
   size_t len = block_len (h, vlist_offset, NULL);
   if (4 + nr_values * 4 > len) {
     if (h->msglvl >= 2)
-      printf ("hivex_node_values: returning EFAULT because value list is too long (%zu, %zu)\n",
-              nr_values, len);
+      fprintf (stderr, "hivex_node_values: returning EFAULT because value list is too long (%zu, %zu)\n",
+               nr_values, len);
     errno = EFAULT;
     return NULL;
   }
@@ -813,8 +835,8 @@ hivex_node_values (hive_h *h, hive_node_h node)
     value += 0x1000;
     if (!IS_VALID_BLOCK (h, value)) {
       if (h->msglvl >= 2)
-        printf ("hivex_node_values: returning EFAULT because value is not a valid block (%zu)\n",
-                value);
+        fprintf (stderr, "hivex_node_values: returning EFAULT because value is not a valid block (%zu)\n",
+                 value);
       errno = EFAULT;
       free (ret);
       return NULL;
@@ -843,7 +865,7 @@ hivex_node_get_value (hive_h *h, hive_node_h node, const char *key)
   for (i = 0; values[i] != 0; ++i) {
     name = hivex_value_key (h, values[i]);
     if (!name) goto error;
-    if (strcasecmp (name, key) == 0) {
+    if (STRCASEEQ (name, key)) {
       ret = values[i];
       break;
     }
@@ -877,8 +899,8 @@ hivex_value_key (hive_h *h, hive_value_h value)
   size_t seg_len = block_len (h, value, NULL);
   if (sizeof (struct ntreg_vk_record) + len - 1 > seg_len) {
     if (h->msglvl >= 2)
-      printf ("hivex_value_key: returning EFAULT because key length is too long (%zu, %zu)\n",
-              len, seg_len);
+      fprintf (stderr, "hivex_value_key: returning EFAULT because key length is too long (%zu, %zu)\n",
+               len, seg_len);
     errno = EFAULT;
     return NULL;
   }
@@ -940,8 +962,8 @@ hivex_value_value (hive_h *h, hive_value_h value,
   len &= 0x7fffffff;
 
   if (h->msglvl >= 2)
-    printf ("hivex_value_value: value=%zu, t=%d, len=%zu\n",
-            value, t, len);
+    fprintf (stderr, "hivex_value_value: value=%zu, t=%d, len=%zu\n",
+             value, t, len);
 
   if (t_rtn)
     *t_rtn = t;
@@ -968,8 +990,8 @@ hivex_value_value (hive_h *h, hive_value_h value,
   data_offset += 0x1000;
   if (!IS_VALID_BLOCK (h, data_offset)) {
     if (h->msglvl >= 2)
-      printf ("hivex_value_value: returning EFAULT because data offset is not a valid block (%zu)\n",
-              data_offset);
+      fprintf (stderr, "hivex_value_value: returning EFAULT because data offset is not a valid block (%zu)\n",
+               data_offset);
     errno = EFAULT;
     free (ret);
     return NULL;
@@ -979,8 +1001,8 @@ hivex_value_value (hive_h *h, hive_value_h value,
   size_t blen = block_len (h, data_offset, NULL);
   if (blen < len) {
     if (h->msglvl >= 2)
-      printf ("hivex_value_value: returning EFAULT because data is longer than its block (%zu, %zu)\n",
-              blen, len);
+      fprintf (stderr, "hivex_value_value: returning EFAULT because data is longer than its block (%zu, %zu)\n",
+               blen, len);
     errno = EFAULT;
     free (ret);
     return NULL;
@@ -1019,9 +1041,12 @@ windows_utf16_to_utf8 (/* const */ char *input, size_t len)
   size_t r = iconv (ic, &inp, &inlen, &outp, &outlen);
   if (r == (size_t) -1) {
     if (errno == E2BIG) {
+      size_t prev = outalloc;
       /* Try again with a larger output buffer. */
       free (out);
       outalloc *= 2;
+      if (outalloc < prev)
+        return NULL;
       goto again;
     }
     else {
@@ -1249,8 +1274,8 @@ hivex__visit_node (hive_h *h, hive_node_h node,
 
   if (!BITMAP_TST (unvisited, node)) {
     if (h->msglvl >= 2)
-      printf ("hivex__visit_node: contains cycle: visited node %zu already\n",
-              node);
+      fprintf (stderr, "hivex__visit_node: contains cycle: visited node %zu already\n",
+               node);
 
     errno = ELOOP;
     return skip_bad ? 0 : -1;
@@ -1404,8 +1429,8 @@ hivex__visit_node (hive_h *h, hive_node_h node,
 
   for (i = 0; children[i] != 0; ++i) {
     if (h->msglvl >= 2)
-      printf ("hivex__visit_node: %s: visiting subkey %d (%zu)\n",
-              name, i, children[i]);
+      fprintf (stderr, "hivex__visit_node: %s: visiting subkey %d (%zu)\n",
+               name, i, children[i]);
 
     if (hivex__visit_node (h, children[i], vtor, unvisited, opaque, flags) == -1)
       goto error;