/* hivex - Windows Registry "hive" extraction library.
- * Copyright (C) 2009-2010 Red Hat Inc.
+ * Copyright (C) 2009-2011 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 <unistd.h>
#include <errno.h>
#include <iconv.h>
-#include <sys/mman.h>
#include <sys/stat.h>
#include <assert.h>
+#ifdef HAVE_MMAP
+#include <sys/mman.h>
+#else
+/* On systems without mmap (and munmap), use a replacement function. */
+#include "mmap.h"
+#endif
+
#include "c-ctype.h"
#include "full-read.h"
#include "full-write.h"
-#ifndef O_CLOEXEC
-#define O_CLOEXEC 0
-#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)
-
#include "hivex.h"
+#include "hivex-internal.h"
#include "byte_conversions.h"
/* These limits are in place to stop really stupid stuff and/or exploits. */
#define HIVEX_MAX_ALLOCATION 1000000
static char *windows_utf16_to_utf8 (/* const */ char *input, size_t len);
-static size_t utf16_string_len_in_bytes (const char *str);
static size_t utf16_string_len_in_bytes_max (const char *str, size_t len);
-struct hive_h {
- char *filename;
- int fd;
- size_t size;
- int msglvl;
- int writable;
-
- /* Registry file, memory mapped if read-only, or malloc'd if writing. */
- union {
- char *addr;
- struct ntreg_header *hdr;
- };
-
- /* Use a bitmap to store which file offsets are valid (point to a
- * used block). We only need to store 1 bit per 32 bits of the file
- * (because blocks are 4-byte aligned). We found that the average
- * block size in a registry file is ~50 bytes. So roughly 1 in 12
- * bits in the bitmap will be set, making it likely a more efficient
- * structure than a hash table.
- */
- char *bitmap;
-#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) \
- (((off) & 3) == 0 && \
- (off) >= 0x1000 && \
- (off) < (h)->size && \
- BITMAP_TST((h)->bitmap,(off)))
-
- /* Fields from the header, extracted from little-endianness hell. */
- size_t rootoffs; /* Root key offset (always an nk-block). */
- size_t endpages; /* Offset of end of pages. */
-
- /* For writing. */
- size_t endblocks; /* Offset to next block allocation (0
- if not allocated anything yet). */
-};
-
/* NB. All fields are little endian. */
struct ntreg_header {
char magic[4]; /* "regf" */
uint32_t sequence1;
uint32_t sequence2;
- char last_modified[8];
+ int64_t last_modified;
uint32_t major_ver; /* 1 */
uint32_t minor_ver; /* 3 */
uint32_t unknown5; /* 0 */
int32_t seg_len; /* length (always -ve because used) */
char id[2]; /* "nk" */
uint16_t flags;
- char timestamp[8];
+ int64_t timestamp;
uint32_t unknown1;
uint32_t parent; /* offset of owner/parent */
uint32_t nr_subkeys; /* number of subkeys */
if (h->filename == NULL)
goto error;
+#ifdef O_CLOEXEC
h->fd = open (filename, O_RDONLY | O_CLOEXEC);
+#else
+ h->fd = open (filename, O_RDONLY);
+#endif
if (h->fd == -1)
goto error;
+#ifndef O_CLOEXEC
+ fcntl (h->fd, F_SETFD, FD_CLOEXEC);
+#endif
struct stat statbuf;
if (fstat (h->fd, &statbuf) == -1)
if (full_read (h->fd, h->addr, h->size) < h->size)
goto error;
+
+ /* We don't need the file descriptor along this path, since we
+ * have read all the data.
+ */
+ if (close (h->fd) == -1)
+ goto error;
+ h->fd = -1;
}
/* Check header. */
goto error;
}
+ /* Last modified time. */
+ h->last_modified = le64toh ((int64_t) h->hdr->last_modified);
+
if (h->msglvl >= 2) {
char *name = windows_utf16_to_utf8 (h->hdr->name, 64);
" file version %" PRIu32 ".%" PRIu32 "\n"
" sequence nos %" PRIu32 " %" PRIu32 "\n"
" (sequences nos should match if hive was synched at shutdown)\n"
+ " last modified %" PRIu64 "\n"
+ " (Windows filetime, x 100 ns since 1601-01-01)\n"
" original file name %s\n"
" (only 32 chars are stored, name is probably truncated)\n"
" root offset 0x%x + 0x1000\n"
" checksum 0x%x (calculated 0x%x)\n",
major_ver, le32toh (h->hdr->minor_ver),
le32toh (h->hdr->sequence1), le32toh (h->hdr->sequence2),
+ h->last_modified,
name ? name : "(conversion failed)",
le32toh (h->hdr->offset),
le32toh (h->hdr->blocks), h->size,
page->magic[1] != 'b' ||
page->magic[2] != 'i' ||
page->magic[3] != 'n') {
- fprintf (stderr, "hivex: %s: trailing garbage at end of file (at 0x%zx, after %zu pages)\n",
+ fprintf (stderr, "hivex: %s: trailing garbage at end of file "
+ "(at 0x%zx, after %zu pages)\n",
filename, off, pages);
errno = ENOTSUP;
goto error;
int used;
seg_len = block_len (h, blkoff, &used);
if (seg_len <= 4 || (seg_len & 3) != 0) {
- fprintf (stderr, "hivex: %s: block size %" PRIu32 " at 0x%zx, 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;
munmap (h->addr, h->size);
else
free (h->addr);
- r = close (h->fd);
+ if (h->fd >= 0)
+ r = close (h->fd);
+ else
+ r = 0;
free (h->filename);
free (h);
{
hive_node_h ret = h->rootoffs;
if (!IS_VALID_BLOCK (h, ret)) {
- errno = ENOKEY;
+ errno = HIVEX_NO_KEY;
+ return 0;
+ }
+ return ret;
+}
+
+size_t
+hivex_node_struct_length (hive_h *h, hive_node_h node)
+{
+ if (!IS_VALID_BLOCK (h, node) || !BLOCK_ID_EQ (h, node, "nk")) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ struct ntreg_nk_record *nk = (struct ntreg_nk_record *) (h->addr + node);
+ size_t name_len = le16toh (nk->name_len);
+ /* -1 to avoid double-counting the first name character */
+ size_t ret = name_len + sizeof (struct ntreg_nk_record) - 1;
+ int used;
+ size_t seg_len = block_len (h, node, &used);
+ if (ret > seg_len) {
+ if (h->msglvl >= 2)
+ fprintf (stderr, "hivex_node_struct_length: returning EFAULT because"
+ " node name is too long (%zu, %zu)\n", name_len, seg_len);
+ errno = EFAULT;
return 0;
}
return ret;
size_t seg_len = block_len (h, node, NULL);
if (sizeof (struct ntreg_nk_record) + len - 1 > seg_len) {
if (h->msglvl >= 2)
- fprintf (stderr, "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;
return ret;
}
+static int64_t
+timestamp_check (hive_h *h, hive_node_h node, int64_t timestamp)
+{
+ if (timestamp < 0) {
+ if (h->msglvl >= 2)
+ fprintf (stderr, "hivex: timestamp_check: "
+ "negative time reported at %zu: %" PRIi64 "\n",
+ node, timestamp);
+ errno = EINVAL;
+ return -1;
+ }
+
+ return timestamp;
+}
+
+int64_t
+hivex_last_modified (hive_h *h)
+{
+ return timestamp_check (h, 0, h->last_modified);
+}
+
+int64_t
+hivex_node_timestamp (hive_h *h, hive_node_h node)
+{
+ int64_t ret;
+
+ if (!IS_VALID_BLOCK (h, node) || !BLOCK_ID_EQ (h, node, "nk")) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ struct ntreg_nk_record *nk = (struct ntreg_nk_record *) (h->addr + node);
+
+ ret = le64toh (nk->timestamp);
+ return timestamp_check (h, node, ret);
+}
+
#if 0
/* I think the documentation for the sk and classname fields in the nk
* record is wrong, or else the offset field is in the wrong place.
/* Arbitrarily limit the number of subkeys we will ever deal with. */
if (nr_subkeys_in_nk > HIVEX_MAX_SUBKEYS) {
if (h->msglvl >= 2)
- fprintf (stderr, "hivex: get_children: returning ERANGE because nr_subkeys_in_nk > HIVEX_MAX_SUBKEYS (%zu > %d)\n",
+ fprintf (stderr, "hivex: get_children: returning ERANGE because "
+ "nr_subkeys_in_nk > HIVEX_MAX_SUBKEYS (%zu > %d)\n",
nr_subkeys_in_nk, HIVEX_MAX_SUBKEYS);
errno = ERANGE;
goto error;
subkey_lf += 0x1000;
if (!IS_VALID_BLOCK (h, subkey_lf)) {
if (h->msglvl >= 2)
- fprintf (stderr, "hivex_node_children: returning EFAULT because subkey_lf is not a valid block (0x%zx)\n",
+ fprintf (stderr, "hivex_node_children: returning EFAULT"
+ " because subkey_lf is not a valid block (0x%zx)\n",
subkey_lf);
errno = EFAULT;
goto error;
size_t nr_subkeys_in_lf = le16toh (lf->nr_keys);
if (h->msglvl >= 2)
- fprintf (stderr, "hivex_node_children: nr_subkeys_in_nk = %zu, nr_subkeys_in_lf = %zu\n",
+ 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) {
size_t len = block_len (h, subkey_lf, NULL);
if (8 + nr_subkeys_in_lf * 8 > len) {
if (h->msglvl >= 2)
- fprintf (stderr, "hivex_node_children: returning EFAULT because too many subkeys (%zu, %zu)\n",
+ fprintf (stderr, "hivex_node_children: returning EFAULT"
+ " because too many subkeys (%zu, %zu)\n",
nr_subkeys_in_lf, len);
errno = EFAULT;
goto error;
if (!(flags & GET_CHILDREN_NO_CHECK_NK)) {
if (!IS_VALID_BLOCK (h, subkey)) {
if (h->msglvl >= 2)
- fprintf (stderr, "hivex_node_children: returning EFAULT because subkey is not a valid block (0x%zx)\n",
+ fprintf (stderr, "hivex_node_children: returning EFAULT"
+ " because subkey is not a valid block (0x%zx)\n",
subkey);
errno = EFAULT;
goto error;
offset += 0x1000;
if (!IS_VALID_BLOCK (h, offset)) {
if (h->msglvl >= 2)
- fprintf (stderr, "hivex_node_children: returning EFAULT because ri-offset is not a valid block (0x%zx)\n",
+ fprintf (stderr, "hivex_node_children: returning EFAULT"
+ " because ri-offset is not a valid block (0x%zx)\n",
offset);
errno = EFAULT;
goto error;
}
if (!BLOCK_ID_EQ (h, offset, "lf") && !BLOCK_ID_EQ (h, offset, "lh")) {
if (h->msglvl >= 2)
- fprintf (stderr, "get_children: returning ENOTSUP because ri-record offset does not point to lf/lh (0x%zx)\n",
+ fprintf (stderr, "get_children: returning ENOTSUP"
+ " because ri-record offset does not point to lf/lh (0x%zx)\n",
offset);
errno = ENOTSUP;
goto error;
}
if (h->msglvl >= 2)
- fprintf (stderr, "hivex_node_children: nr_subkeys_in_nk = %zu, counted = %zu\n",
+ fprintf (stderr, "hivex_node_children: nr_subkeys_in_nk = %zu,"
+ " counted = %zu\n",
nr_subkeys_in_nk, count);
if (nr_subkeys_in_nk != count) {
offset += 0x1000;
if (!IS_VALID_BLOCK (h, offset)) {
if (h->msglvl >= 2)
- fprintf (stderr, "hivex_node_children: returning EFAULT because ri-offset is not a valid block (0x%zx)\n",
+ fprintf (stderr, "hivex_node_children: returning EFAULT"
+ " because ri-offset is not a valid block (0x%zx)\n",
offset);
errno = EFAULT;
goto error;
}
if (!BLOCK_ID_EQ (h, offset, "lf") && !BLOCK_ID_EQ (h, offset, "lh")) {
if (h->msglvl >= 2)
- fprintf (stderr, "get_children: returning ENOTSUP because ri-record offset does not point to lf/lh (0x%zx)\n",
+ fprintf (stderr, "get_children: returning ENOTSUP"
+ " because ri-record offset does not point to lf/lh (0x%zx)\n",
offset);
errno = ENOTSUP;
goto error;
if (!(flags & GET_CHILDREN_NO_CHECK_NK)) {
if (!IS_VALID_BLOCK (h, subkey)) {
if (h->msglvl >= 2)
- fprintf (stderr, "hivex_node_children: returning EFAULT because indirect subkey is not a valid block (0x%zx)\n",
+ fprintf (stderr, "hivex_node_children: returning EFAULT"
+ " because indirect subkey is not a valid block (0x%zx)\n",
subkey);
errno = EFAULT;
goto error;
}
/* else not supported, set errno and fall through */
if (h->msglvl >= 2)
- fprintf (stderr, "get_children: returning ENOTSUP because subkey block is not lf/lh/ri (0x%zx, %d, %d)\n",
+ fprintf (stderr, "get_children: returning ENOTSUP"
+ " because subkey block is not lf/lh/ri (0x%zx, %d, %d)\n",
subkey_lf, block->id[0], block->id[1]);
errno = ENOTSUP;
error:
ret += 0x1000;
if (!IS_VALID_BLOCK (h, ret)) {
if (h->msglvl >= 2)
- fprintf (stderr, "hivex_node_parent: returning EFAULT because parent is not a valid block (0x%zx)\n",
+ fprintf (stderr, "hivex_node_parent: returning EFAULT"
+ " because parent is not a valid block (0x%zx)\n",
ret);
errno = EFAULT;
return 0;
/* Arbitrarily limit the number of values we will ever deal with. */
if (nr_values > HIVEX_MAX_VALUES) {
if (h->msglvl >= 2)
- fprintf (stderr, "hivex: get_values: returning ERANGE because nr_values > HIVEX_MAX_VALUES (%zu > %d)\n",
+ fprintf (stderr, "hivex: get_values: returning ERANGE"
+ " because nr_values > HIVEX_MAX_VALUES (%zu > %d)\n",
nr_values, HIVEX_MAX_VALUES);
errno = ERANGE;
goto error;
vlist_offset += 0x1000;
if (!IS_VALID_BLOCK (h, vlist_offset)) {
if (h->msglvl >= 2)
- fprintf (stderr, "hivex_node_values: returning EFAULT because value list is not a valid block (0x%zx)\n",
+ fprintf (stderr, "hivex_node_values: returning EFAULT"
+ " because value list is not a valid block (0x%zx)\n",
vlist_offset);
errno = EFAULT;
goto error;
size_t len = block_len (h, vlist_offset, NULL);
if (4 + nr_values * 4 > len) {
if (h->msglvl >= 2)
- fprintf (stderr, "hivex_node_values: returning EFAULT because value list is too long (%zu, %zu)\n",
+ fprintf (stderr, "hivex_node_values: returning EFAULT"
+ " because value list is too long (%zu, %zu)\n",
nr_values, len);
errno = EFAULT;
goto error;
size_t i;
for (i = 0; i < nr_values; ++i) {
- hive_node_h value = vlist->offset[i];
+ hive_node_h value = le32toh (vlist->offset[i]);
value += 0x1000;
if (!IS_VALID_BLOCK (h, value)) {
if (h->msglvl >= 2)
- fprintf (stderr, "hivex_node_values: returning EFAULT because value is not a valid block (0x%zx)\n",
+ fprintf (stderr, "hivex_node_values: returning EFAULT"
+ " because value is not a valid block (0x%zx)\n",
value);
errno = EFAULT;
goto error;
return ret;
}
-char *
-hivex_value_key (hive_h *h, hive_value_h value)
+size_t
+hivex_value_struct_length (hive_h *h, hive_value_h value)
+{
+ size_t key_len;
+
+ errno = 0;
+ key_len = hivex_value_key_len (h, value);
+ if (key_len == 0 && errno != 0)
+ return 0;
+
+ /* -1 to avoid double-counting the first name character */
+ return key_len + sizeof (struct ntreg_vk_record) - 1;
+}
+
+size_t
+hivex_value_key_len (hive_h *h, hive_value_h value)
{
if (!IS_VALID_BLOCK (h, value) || !BLOCK_ID_EQ (h, value, "vk")) {
errno = EINVAL;
struct ntreg_vk_record *vk = (struct ntreg_vk_record *) (h->addr + value);
- /* AFAIK the key is always plain ASCII, so no conversion to UTF-8 is
- * necessary. However we do need to nul-terminate the string.
- */
-
/* vk->name_len is unsigned, 16 bit, so this is safe ... However
* we have to make sure the length doesn't exceed the block length.
*/
- size_t len = le16toh (vk->name_len);
+ size_t ret = le16toh (vk->name_len);
size_t seg_len = block_len (h, value, NULL);
- if (sizeof (struct ntreg_vk_record) + len - 1 > seg_len) {
+ if (sizeof (struct ntreg_vk_record) + ret - 1 > seg_len) {
if (h->msglvl >= 2)
- fprintf (stderr, "hivex_value_key: returning EFAULT because key length is too long (%zu, %zu)\n",
- len, seg_len);
+ fprintf (stderr, "hivex_value_key_len: returning EFAULT"
+ " because key length is too long (%zu, %zu)\n",
+ ret, seg_len);
errno = EFAULT;
- return NULL;
+ return 0;
+ }
+ return ret;
+}
+
+char *
+hivex_value_key (hive_h *h, hive_value_h value)
+{
+ if (!IS_VALID_BLOCK (h, value) || !BLOCK_ID_EQ (h, value, "vk")) {
+ errno = EINVAL;
+ return 0;
}
+ struct ntreg_vk_record *vk = (struct ntreg_vk_record *) (h->addr + value);
+
+ /* AFAIK the key is always plain ASCII, so no conversion to UTF-8 is
+ * necessary. However we do need to nul-terminate the string.
+ */
+ errno = 0;
+ size_t len = hivex_value_key_len (h, value);
+ if (len == 0 && errno != 0)
+ return NULL;
+
char *ret = malloc (len + 1);
if (ret == NULL)
return NULL;
return 0;
}
+hive_value_h
+hivex_value_data_cell_offset (hive_h *h, hive_value_h value, size_t *len)
+{
+ if (!IS_VALID_BLOCK (h, value) || !BLOCK_ID_EQ (h, value, "vk")) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ if (h->msglvl >= 2)
+ fprintf (stderr, "hivex_value_data_cell_offset: value=0x%zx\n", value);
+ struct ntreg_vk_record *vk = (struct ntreg_vk_record *) (h->addr + value);
+
+ size_t data_len;
+ int is_inline;
+
+ data_len = le32toh (vk->data_len);
+ is_inline = !!(data_len & 0x80000000);
+ data_len &= 0x7fffffff;
+
+ if (h->msglvl >= 2)
+ fprintf (stderr, "hivex_value_data_cell_offset: is_inline=%d\n", is_inline);
+
+ if (h->msglvl >= 2)
+ fprintf (stderr, "hivex_value_data_cell_offset: data_len=%zx\n", data_len);
+
+ if (is_inline && data_len > 4) {
+ errno = ENOTSUP;
+ return 0;
+ }
+
+ if (is_inline) {
+ /* There is no other location for the value data. */
+ if (len)
+ *len = 0;
+ return 0;
+ } else {
+ if (len)
+ *len = data_len + 4; /* Include 4 header length bytes */
+ }
+
+ if (h->msglvl >= 2)
+ fprintf (stderr, "hivex_value_data_cell_offset: Proceeding with indirect data.\n");
+
+ size_t data_offset = le32toh (vk->data_offset);
+ data_offset += 0x1000; /* Add 0x1000 because everything's off by 4KiB */
+ if (!IS_VALID_BLOCK (h, data_offset)) {
+ if (h->msglvl >= 2)
+ fprintf (stderr, "hivex_value_data_cell_offset: returning EFAULT because data "
+ "offset is not a valid block (0x%zx)\n",
+ data_offset);
+ errno = EFAULT;
+ return 0;
+ }
+
+ if (h->msglvl >= 2)
+ fprintf (stderr, "hivex_value_data_cell_offset: data_offset=%zx\n", data_offset);
+
+ return data_offset;
+}
+
char *
hivex_value_value (hive_h *h, hive_value_h value,
hive_type *t_rtn, size_t *len_rtn)
/* Arbitrarily limit the length that we will read. */
if (len > HIVEX_MAX_VALUE_LEN) {
if (h->msglvl >= 2)
- fprintf (stderr, "hivex_value_value: returning ERANGE because data length > HIVEX_MAX_VALUE_LEN (%zu > %d)\n",
+ fprintf (stderr, "hivex_value_value: returning ERANGE because data "
+ "length > HIVEX_MAX_VALUE_LEN (%zu > %d)\n",
len, HIVEX_MAX_SUBKEYS);
errno = ERANGE;
return NULL;
data_offset += 0x1000;
if (!IS_VALID_BLOCK (h, data_offset)) {
if (h->msglvl >= 2)
- fprintf (stderr, "hivex_value_value: returning EFAULT because data offset is not a valid block (0x%zx)\n",
+ fprintf (stderr, "hivex_value_value: returning EFAULT because data "
+ "offset is not a valid block (0x%zx)\n",
data_offset);
errno = EFAULT;
free (ret);
size_t blen = block_len (h, data_offset, NULL);
if (len > blen - 4 /* subtract 4 for block header */) {
if (h->msglvl >= 2)
- fprintf (stderr, "hivex_value_value: warning: declared data length is longer than the block it is in (data 0x%zx, data len %zu, block len %zu)\n",
+ fprintf (stderr, "hivex_value_value: warning: declared data length "
+ "is longer than the block it is in "
+ "(data 0x%zx, data len %zu, block len %zu)\n",
data_offset, len, blen);
len = blen - 4;
}
/* Get the length of a UTF-16 format string. Handle the string as
- * pairs of bytes, looking for the first \0\0 pair.
+ * pairs of bytes, looking for the first \0\0 pair. Only read up to
+ * 'len' maximum bytes.
*/
static size_t
-utf16_string_len_in_bytes (const char *str)
-{
- size_t ret = 0;
-
- while (str[0] || str[1]) {
- str += 2;
- ret += 2;
- }
-
- return ret;
-}
-
-/* As for utf16_string_len_in_bytes but only read up to a maximum length. */
-static size_t
utf16_string_len_in_bytes_max (const char *str, size_t len)
{
size_t ret = 0;
- while (len > 0 && (str[0] || str[1])) {
+ while (len >= 2 && (str[0] || str[1])) {
str += 2;
ret += 2;
len -= 2;
char *p = data;
size_t plen;
- while (p < data + len && (plen = utf16_string_len_in_bytes (p)) > 0) {
+ while (p < data + len &&
+ (plen = utf16_string_len_in_bytes_max (p, data + len - p)) > 0) {
nr_strings++;
char **ret2 = realloc (ret, (1 + nr_strings) * sizeof (char *));
if (ret2 == NULL) {
return hivex_visit_node (h, hivex_root (h), visitor, len, opaque, flags);
}
-static int hivex__visit_node (hive_h *h, hive_node_h node, const struct hivex_visitor *vtor, char *unvisited, void *opaque, int flags);
+static int hivex__visit_node (hive_h *h, hive_node_h node,
+ const struct hivex_visitor *vtor,
+ char *unvisited, void *opaque, int flags);
int
hivex_visit_node (hive_h *h, hive_node_h node,
if (!BITMAP_TST (unvisited, node)) {
if (h->msglvl >= 2)
- fprintf (stderr, "hivex__visit_node: contains cycle: visited node 0x%zx already\n",
+ fprintf (stderr, "hivex__visit_node: contains cycle:"
+ " visited node 0x%zx already\n",
node);
errno = ELOOP;
}
if (vtor->value_string_invalid_utf16) {
str = hivex_value_value (h, values[i], &t, &len);
- if (vtor->value_string_invalid_utf16 (h, opaque, node, values[i], t, len, key, str) == -1)
+ if (vtor->value_string_invalid_utf16 (h, opaque, node, values[i],
+ t, len, key, str) == -1)
goto error;
free (str); str = NULL;
}
break;
}
if (vtor->value_string &&
- vtor->value_string (h, opaque, node, values[i], t, len, key, str) == -1)
+ vtor->value_string (h, opaque, node, values[i],
+ t, len, key, str) == -1)
goto error;
free (str); str = NULL;
break;
case hive_t_dword_be: {
int32_t i32 = hivex_value_dword (h, values[i]);
if (vtor->value_dword &&
- vtor->value_dword (h, opaque, node, values[i], t, len, key, i32) == -1)
+ vtor->value_dword (h, opaque, node, values[i],
+ t, len, key, i32) == -1)
goto error;
break;
}
case hive_t_qword: {
int64_t i64 = hivex_value_qword (h, values[i]);
if (vtor->value_qword &&
- vtor->value_qword (h, opaque, node, values[i], t, len, key, i64) == -1)
+ vtor->value_qword (h, opaque, node, values[i],
+ t, len, key, i64) == -1)
goto error;
break;
}
goto error;
}
if (vtor->value_binary &&
- vtor->value_binary (h, opaque, node, values[i], t, len, key, str) == -1)
+ vtor->value_binary (h, opaque, node, values[i],
+ t, len, key, str) == -1)
goto error;
free (str); str = NULL;
break;
}
if (vtor->value_string_invalid_utf16) {
str = hivex_value_value (h, values[i], &t, &len);
- if (vtor->value_string_invalid_utf16 (h, opaque, node, values[i], t, len, key, str) == -1)
+ if (vtor->value_string_invalid_utf16 (h, opaque, node, values[i],
+ t, len, key, str) == -1)
goto error;
free (str); str = NULL;
}
break;
}
if (vtor->value_multiple_strings &&
- vtor->value_multiple_strings (h, opaque, node, values[i], t, len, key, strs) == -1)
+ vtor->value_multiple_strings (h, opaque, node, values[i],
+ t, len, key, strs) == -1)
goto error;
free_strings (strs); strs = NULL;
break;
goto error;
}
if (vtor->value_other &&
- vtor->value_other (h, opaque, node, values[i], t, len, key, str) == -1)
+ vtor->value_other (h, opaque, node, values[i],
+ t, len, key, str) == -1)
goto error;
free (str); str = NULL;
break;
ssize_t extend = h->endpages + nr_4k_pages * 4096 - h->size;
if (h->msglvl >= 2) {
- fprintf (stderr, "allocate_page: current endpages = 0x%zx, current size = 0x%zx\n",
+ fprintf (stderr, "allocate_page: current endpages = 0x%zx,"
+ " current size = 0x%zx\n",
h->endpages, h->size);
- fprintf (stderr, "allocate_page: extending file by %zd bytes (<= 0 if no extension)\n",
+ fprintf (stderr, "allocate_page: extending file by %zd bytes"
+ " (<= 0 if no extension)\n",
extend);
}
* for them, albeit unusual.
*/
if (h->msglvl >= 2)
- fprintf (stderr, "allocate_block: refusing too small allocation (%zu), returning ERANGE\n",
- seg_len);
+ fprintf (stderr, "allocate_block: refusing too small allocation (%zu),"
+ " returning ERANGE\n", seg_len);
errno = ERANGE;
return 0;
}
/* Refuse really large allocations. */
if (seg_len > HIVEX_MAX_ALLOCATION) {
if (h->msglvl >= 2)
- fprintf (stderr, "allocate_block: refusing large allocation (%zu), returning ERANGE\n",
- seg_len);
+ fprintf (stderr, "allocate_block: refusing large allocation (%zu),"
+ " returning ERANGE\n", seg_len);
errno = ERANGE;
return 0;
}
ssize_t rem = h->endpages - h->endblocks;
if (rem > 0) {
if (h->msglvl >= 2)
- fprintf (stderr, "allocate_block: marking remainder of page free starting at 0x%zx, size %zd\n",
- h->endblocks, rem);
+ fprintf (stderr, "allocate_block: marking remainder of page free"
+ " starting at 0x%zx, size %zd\n", h->endblocks, rem);
assert (rem >= 4);
return 0;
if (h->msglvl >= 2)
- fprintf (stderr, "hivex_node_add_child: allocated new nk-record for child at 0x%zx\n", node);
+ fprintf (stderr, "hivex_node_add_child: allocated new nk-record"
+ " for child at 0x%zx\n", node);
struct ntreg_nk_record *nk = (struct ntreg_nk_record *) (h->addr + node);
nk->flags = htole16 (0x0020); /* key is ASCII. */
if (!IS_VALID_BLOCK (h, parent_sk_offset) ||
!BLOCK_ID_EQ (h, parent_sk_offset, "sk")) {
if (h->msglvl >= 2)
- fprintf (stderr, "hivex_node_add_child: returning EFAULT because parent sk is not a valid block (%zu)\n",
+ fprintf (stderr, "hivex_node_add_child: returning EFAULT"
+ " because parent sk is not a valid block (%zu)\n",
parent_sk_offset);
errno = EFAULT;
return 0;
nk->sk = htole32 (parent_sk_offset - 0x1000);
/* Inherit parent timestamp. */
- memcpy (nk->timestamp, parent_nk->timestamp, sizeof (parent_nk->timestamp));
+ nk->timestamp = parent_nk->timestamp;
/* What I found out the hard way (not documented anywhere): the
* subkeys in lh-records must be kept sorted. If you just add a
parent_nk = (struct ntreg_nk_record *) (h->addr + parent);
if (h->msglvl >= 2)
- fprintf (stderr, "hivex_node_add_child: no keys, allocated new lh-record at 0x%zx\n", lh_offs);
+ fprintf (stderr, "hivex_node_add_child: no keys, allocated new"
+ " lh-record at 0x%zx\n", lh_offs);
parent_nk->subkey_lf = htole32 (lh_offs - 0x1000);
}
/* Insert it. */
insert_it:
if (h->msglvl >= 2)
- fprintf (stderr, "hivex_node_add_child: insert key in existing lh-record at 0x%zx, posn %zu\n", old_offs, j);
+ fprintf (stderr, "hivex_node_add_child: insert key in existing"
+ " lh-record at 0x%zx, posn %zu\n", old_offs, j);
new_offs = insert_lf_record (h, old_offs, j, name, node);
if (new_offs == 0) {
/* Not found .. This is an internal error. */
if (h->msglvl >= 2)
- fprintf (stderr, "hivex_node_add_child: returning ENOTSUP because could not find ri->lf link\n");
+ fprintf (stderr, "hivex_node_add_child: returning ENOTSUP"
+ " because could not find ri->lf link\n");
errno = ENOTSUP;
free (blocks);
return 0;
}
}
if (h->msglvl >= 2)
- fprintf (stderr, "hivex_node_delete_child: could not find parent to child link\n");
+ fprintf (stderr, "hivex_node_delete_child: could not find parent"
+ " to child link\n");
errno = ENOTSUP;
return -1;
nk->nr_subkeys = htole32 (nr_subkeys_in_nk - 1);
if (h->msglvl >= 2)
- fprintf (stderr, "hivex_node_delete_child: updating nr_subkeys in parent 0x%zx to %zu\n",
- parent, nr_subkeys_in_nk);
+ fprintf (stderr, "hivex_node_delete_child: updating nr_subkeys"
+ " in parent 0x%zx to %zu\n", parent, nr_subkeys_in_nk);
return 0;
}
int
hivex_node_set_value (hive_h *h, hive_node_h node,
- const hive_set_value *val, int flags)
+ const hive_set_value *val, int flags)
{
hive_value_h *prev_values = hivex_node_values (h, node);
if (prev_values == NULL)
leave_partial:
for (int i = 0; i < alloc_ct; i += 2) {
- if (values[i / 2].value != NULL)
- free (values[i / 2].value);
+ free (values[i / 2].value);
if (i + 1 < alloc_ct && values[i / 2].key != NULL)
free (values[i / 2].key);
}