hivex: Add HIVEX_OPEN_WRITE flag to allow hive to be opened for writing.
[libguestfs.git] / hivex / hivex.c
index e1df96a..44f2998 100644 (file)
@@ -1,5 +1,5 @@
 /* hivex - Windows Registry "hive" extraction library.
- * Copyright (C) 2009 Red Hat Inc.
+ * Copyright (C) 2009-2010 Red Hat Inc.
  * Derived from code by Petter Nordahl-Hagen under a compatible license:
  *   Copyright (c) 1997-2007 Petter Nordahl-Hagen.
  * Derived from code by Markus Stephany under a compatible license:
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <assert.h>
-#ifdef HAVE_ENDIAN_H
-#include <endian.h>
-#endif
-#ifdef HAVE_BYTESWAP_H
-#include <byteswap.h>
+
+#include "full-read.h"
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
 #endif
 
 #define STREQ(a,b) (strcmp((a),(b)) == 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)
-#endif
-#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)
-#endif
-#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"
+#include "byte_conversions.h"
 
 static char *windows_utf16_to_utf8 (/* const */ char *input, size_t len);
 
@@ -93,8 +60,9 @@ struct hive_h {
   int fd;
   size_t size;
   int msglvl;
+  int writable;
 
-  /* Memory-mapped (readonly) registry file. */
+  /* Registry file, memory mapped if read-only, or malloc'd if writing. */
   union {
     char *addr;
     struct ntreg_header *hdr;
@@ -196,18 +164,25 @@ struct ntreg_nk_record {
   int32_t seg_len;              /* length (always -ve because used) */
   char id[2];                   /* "nk" */
   uint16_t flags;
-  char timestamp[12];
+  char timestamp[8];
+  uint32_t unknown1;
   uint32_t parent;              /* offset of owner/parent */
   uint32_t nr_subkeys;          /* number of subkeys */
-  uint32_t unknown1;
+  uint32_t nr_subkeys_volatile;
   uint32_t subkey_lf;           /* lf record containing list of subkeys */
-  uint32_t unknown2;
+  uint32_t subkey_lf_volatile;
   uint32_t nr_values;           /* number of values */
   uint32_t vallist;             /* value-list record */
   uint32_t sk;                  /* offset of sk-record */
   uint32_t classname;           /* offset of classname record */
-  char unknown3[16];
-  uint32_t unknown4;
+  uint16_t max_subkey_name_len; /* maximum length of a subkey name in bytes
+                                   if the subkey was reencoded as UTF-16LE */
+  uint16_t unknown2;
+  uint32_t unknown3;
+  uint32_t max_vk_name_len;     /* maximum length of any vk name in bytes
+                                   if the name was reencoded as UTF-16LE */
+  uint32_t max_vk_data_len;     /* maximum length of any vk data in bytes */
+  uint32_t unknown6;
   uint16_t name_len;            /* length of name */
   uint16_t classname_len;       /* length of classname */
   char name[1];                 /* name follows here */
@@ -219,7 +194,7 @@ struct ntreg_lf_record {
   uint16_t nr_keys;             /* number of keys in this record */
   struct {
     uint32_t offset;            /* offset of nk-record for this subkey */
-    char name[4];               /* first 4 characters of subkey name */
+    char hash[4];               /* hash of subkey name */
   } keys[1];
 } __attribute__((__packed__));
 
@@ -247,16 +222,17 @@ struct ntreg_vk_record {
    */
   uint32_t data_len;
   uint32_t data_offset;         /* pointer to the data (or data if inline) */
-  hive_type data_type;          /* type of the data */
+  uint32_t data_type;           /* type of the data */
   uint16_t flags;               /* bit 0 set => key name ASCII,
                                    bit 0 clr => key name UTF-16.
-                                   Only seen ASCII here in the wild. */
+                                   Only seen ASCII here in the wild.
+                                   NB: this is CLEAR for default key. */
   uint16_t unknown2;
   char name[1];                 /* key name follows here */
 } __attribute__((__packed__));
 
 static uint32_t
-header_checksum (hive_h *h)
+header_checksum (const hive_h *h)
 {
   uint32_t *daddr = (uint32_t *) h->addr;
   size_t i;
@@ -291,11 +267,12 @@ hivex_open (const char *filename, int flags)
   if (h->msglvl >= 2)
     fprintf (stderr, "hivex_open: created handle %p\n", h);
 
+  h->writable = !!(flags & HIVEX_OPEN_WRITE);
   h->filename = strdup (filename);
   if (h->filename == NULL)
     goto error;
 
-  h->fd = open (filename, O_RDONLY);
+  h->fd = open (filename, O_RDONLY | O_CLOEXEC);
   if (h->fd == -1)
     goto error;
 
@@ -305,12 +282,21 @@ hivex_open (const char *filename, int flags)
 
   h->size = statbuf.st_size;
 
-  h->addr = mmap (NULL, h->size, PROT_READ, MAP_SHARED, h->fd, 0);
-  if (h->addr == MAP_FAILED)
-    goto error;
+  if (!h->writable) {
+    h->addr = mmap (NULL, h->size, PROT_READ, MAP_SHARED, h->fd, 0);
+    if (h->addr == MAP_FAILED)
+      goto error;
 
-  if (h->msglvl >= 2)
-    fprintf (stderr, "hivex_open: mapped file at %p\n", h->addr);
+    if (h->msglvl >= 2)
+      fprintf (stderr, "hivex_open: mapped file at %p\n", h->addr);
+  } else {
+    h->addr = malloc (h->size);
+    if (h->addr == NULL)
+      goto error;
+
+    if (full_read (h->fd, h->addr, h->size) < h->size)
+      goto error;
+  }
 
   /* Check header. */
   if (h->hdr->magic[0] != 'r' ||
@@ -441,7 +427,7 @@ hivex_open (const char *filename, int flags)
       int used;
       seg_len = block_len (h, blkoff, &used);
       if (seg_len <= 4 || (seg_len & 3) != 0) {
-        fprintf (stderr, "hivex: %s: block size %d at %zu, bad registry\n",
+        fprintf (stderr, "hivex: %s: block size %" PRIu32 " at 0x%zx, bad registry\n",
                  filename, le32toh (block->seg_len), blkoff);
         errno = ENOTSUP;
         goto error;
@@ -502,8 +488,12 @@ hivex_open (const char *filename, int flags)
   int err = errno;
   if (h) {
     free (h->bitmap);
-    if (h->addr && h->size && h->addr != MAP_FAILED)
-      munmap (h->addr, h->size);
+    if (h->addr && h->size && h->addr != MAP_FAILED) {
+      if (!h->writable)
+        munmap (h->addr, h->size);
+      else
+        free (h->addr);
+    }
     if (h->fd >= 0)
       close (h->fd);
     free (h->filename);
@@ -519,7 +509,10 @@ hivex_close (hive_h *h)
   int r;
 
   free (h->bitmap);
-  munmap (h->addr, h->size);
+  if (!h->writable)
+    munmap (h->addr, h->size);
+  else
+    free (h->addr);
   r = close (h->fd);
   free (h->filename);
   free (h);
@@ -762,7 +755,7 @@ get_children (hive_h *h, hive_node_h node,
 
     size_t i;
     for (i = 0; i < nr_subkeys_in_lf; ++i) {
-      hive_node_h subkey = lf->keys[i].offset;
+      hive_node_h subkey = le32toh (lf->keys[i].offset);
       subkey += 0x1000;
       if (!IS_VALID_BLOCK (h, subkey)) {
         if (h->msglvl >= 2)
@@ -840,7 +833,7 @@ get_children (hive_h *h, hive_node_h node,
 
       size_t j;
       for (j = 0; j < le16toh (lf->nr_keys); ++j) {
-        hive_node_h subkey = lf->keys[j].offset;
+        hive_node_h subkey = le32toh (lf->keys[j].offset);
         subkey += 0x1000;
         if (!IS_VALID_BLOCK (h, subkey)) {
           if (h->msglvl >= 2)
@@ -1173,7 +1166,7 @@ hivex_value_value (hive_h *h, hive_value_h value,
     return ret;
   }
 
-  size_t data_offset = vk->data_offset;
+  size_t data_offset = le32toh (vk->data_offset);
   data_offset += 0x1000;
   if (!IS_VALID_BLOCK (h, data_offset)) {
     if (h->msglvl >= 2)
@@ -1186,7 +1179,7 @@ hivex_value_value (hive_h *h, hive_value_h value,
 
   /* Check that the declared size isn't larger than the block its in. */
   size_t blen = block_len (h, data_offset, NULL);
-  if (len > blen) {
+  if (len > blen - 4 /* subtract 4 for block header */) {
     if (h->msglvl >= 2)
       fprintf (stderr, "hivex_value_value: returning EFAULT because data is longer than its block (data 0x%zx, data len %zu, block len %zu)\n",
                data_offset, len, blen);