[Forensics-changes] [SCM] utility to read and query Windows NT/2000/XP registry branch, debian, updated. debian/0.9.0-2-2-gf1b1ed1

Christophe Monniez christophe.monniez at fccu.be
Mon Jun 22 10:58:45 UTC 2009


The following commit has been merged in the debian branch:
commit 4730a9a9d062f32e9956d71abccda421469633dd
Author: Christophe Monniez <christophe.monniez at fccu.be>
Date:   Mon Jun 22 11:05:03 2009 +0200

    Merging upstream version 0.11.0.

diff --git a/INSTALL b/INSTALL
index 308824c..468ffc5 100644
--- a/INSTALL
+++ b/INSTALL
@@ -10,6 +10,11 @@ comes with any free operating system.  Be sure you have:
  - GNU Make
  - GCC
 
+Note that iconv support is required, as specified in IEEE Std 1003.1 
+(POSIX.1-2001).  Some platforms still do not contain support for this 
+natively, in which case you may need to install libiconv from:
+  http://www.gnu.org/software/libiconv/
+
 
 Survival Commands
 -----------------
@@ -43,3 +48,7 @@ make VAR=value install
 
 
 For more information, see the Makefiles.
+
+Primitive build features have been added to support cross-compiling to
+Windows binaries using MinGW.  For more information on this, see: 
+  doc/mingw-build.txt
diff --git a/Makefile b/Makefile
index 9e58a26..e22d83c 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile 111 2008-05-01 04:06:22Z tim $
+# $Id: Makefile 147 2009-02-22 19:31:52Z tim $
 
 # Installation prefixes.  Change to install elsewhere.
 
@@ -11,14 +11,35 @@ MAN_PREFIX=$(PREFIX)/man
 
 CC=gcc
 OPTS=-std=gnu89 -pedantic -Wall -ggdb
-INC=-I/usr/local/include
+#OPTS=-std=gnu89 -pedantic -Wall
+INC:=-I$(PWD)/include -I/usr/local/include 
 LIB=-L/usr/local/lib -lm
+BIN_EXT=
+EXTRA_OBJ=
 
 UNAME := $(shell uname)
 ifneq ($(UNAME),Linux) 	
   LIB:=$(LIB) -liconv
 endif
 
+
+################################################################################
+# MinGW cross-compiling build settings
+ifdef BUILD_MINGW
+
+## These may need to be changed
+CC=i586-mingw32msvc-cc
+LIBICONV_PATH=/usr/local/src/libiconv-1.9.2-1-lib
+
+## These probably do not need to be changed
+BIN_EXT=.exe
+INC:=$(INC) -I$(LIBICONV_PATH)/include
+EXTRA_OBJ=$(LIBICONV_PATH)/lib/libiconv.dll.a
+
+endif
+################################################################################
+
+
 BUILD=$(CURDIR)/build
 BUILD_BIN=$(BUILD)/bin
 BUILD_DOC=$(BUILD)/doc
diff --git a/doc/Makefile b/doc/Makefile
index f30d9da..39ef109 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile 119 2008-08-09 05:55:45Z tim $
+# $Id: Makefile 155 2009-06-03 23:45:58Z tim $
 
 BUILD_FILES=$(BUILD_DOC)/man/man1/reglookup.1.gz\
 	$(BUILD_DOC)/man/man1/reglookup-timeline.1.gz\
@@ -25,9 +25,11 @@ install:
 
 #XXX: Used during release only
 release:
-	docbook2x-man --to-stdout reglookup.1.docbook > man/man1/reglookup.1
-	docbook2x-man --to-stdout reglookup-timeline.1.docbook > man/man1/reglookup-timeline.1
-	docbook2x-man --to-stdout reglookup-recover.1.docbook > man/man1/reglookup-recover.1
+	#XXX: The sed filter is a temporary hack to work around a bug in docbook2x-man
+	#     See: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=516165
+	docbook2x-man --to-stdout reglookup.1.docbook | sed 's/.SH DESCRIPTION/\n.SH DESCRIPTION/' > man/man1/reglookup.1
+	docbook2x-man --to-stdout reglookup-timeline.1.docbook | sed 's/.SH DESCRIPTION/\n.SH DESCRIPTION/' > man/man1/reglookup-timeline.1
+	docbook2x-man --to-stdout reglookup-recover.1.docbook | sed 's/.SH DESCRIPTION/\n.SH DESCRIPTION/' > man/man1/reglookup-recover.1
 	cd man/man1 && gzip -9 -f reglookup.1
 	cd man/man1 && gzip -9 -f reglookup-timeline.1
 	cd man/man1 && gzip -9 -f reglookup-recover.1
diff --git a/doc/devel/TODO b/doc/devel/TODO
index dba1326..549fac8 100644
--- a/doc/devel/TODO
+++ b/doc/devel/TODO
@@ -1,4 +1,4 @@
-$Id: TODO 122 2008-08-09 20:24:01Z tim $
+$Id: TODO 153 2009-06-02 22:28:07Z tim $
 
 If you are interested in contributing to this project, here's a few
 things you could look into:
@@ -11,6 +11,9 @@ things you could look into:
    paths.  Reglookup should take the user path and unquote each path
    component using the \xQQ syntax prior to searching.
 
+ - It might be nice to have a way to filter results by security 
+   descriptor information.  Maybe by MTIME as well.
+
  - Testing, testing, and more testing.  reglookup needs to be tested on 
    NT/XP/2k3/Vista.  A regression test suite would be nice too.  Some 
    thoughts on this include a script which randomly fuzzes an existing
@@ -25,40 +28,30 @@ things you could look into:
    minimal make rules.  If you got any ideas on this, shoot them my way.
 
  - Unicode support still needs improvement.  While parsing strings seems
-   to be decent, UTF-8 output would be nice.
+   to be decent, UTF-8 output would be nice.  Need support for
+   UTF-16LE key and value names.  To do this, the UTF conversion
+   functions need to be moved to regfi.
 
  - The interface between reglookup.c and regfi.c is much better than it
-   used to be, but the iter2Stack function needs to be moved into the 
-   library, which means the \xQQ quoting syntax will have to go with it.
-   This syntax will need to be more carefully documented if it's going 
-   to be a part of the library.
+   used to be, but the parsing of data objects needs to be moved into the
+   library.  The quoting syntax should stay in reglookup/reglookup-recover
+   but the basic parsing of data types into proper structures should 
+   happen in the library so that they are accessible to other users of the
+   library.
 
- - NK/VK/SK record caching.  Right now, HBINs and perhaps SK records are 
-   cached, but it's pretty haphazard, and NK/VK records are repeatedly
-   re-parsed.  A generic caching library should be introduced which can
-   cache many of these records with a specific memory limit in mind.  
-   I think this will speed things up greatly.
-
- - It might be nice to have a way to filter results by security 
-   descriptor information.  Maybe by MTIME as well.
+ - Develop and solidify regfi API.  Regfi should be better documented and 
+   eventually needs a set of Python wrappers.
 
  - Documentation.  The security descriptor output format needs to be 
    documented.  Also, function contracts should be added to the 
    lower-level functions of regfi.c.
 
- - The stuff in smb_deps.h and smb_deps.c needs to be cleaned up.  The
-   eventual goal is to have it all either integrated into regfi, or to
-   be eliminated, or broken out into small supporting libraries, as
-   necessary.  It is currently just a jumble of old Samba code that I
-   haven't decided where to put yet.
+ - The smb_deps.h and smb_deps.c content is almost eliminated.  Just need
+   to integrate parts that are being kept into regfi or other modules.
 
- - At least one user reported that they use reglookup on a Windows host 
-   through Cygwin, but after version 0.3.0 came out, the dependency on
-   libiconv caused that to break.  libiconv seems to be a portability
-   issue on other platforms as well.  However, it's interface is a POSIX
-   standard, and I think I'd like to keep it around.  Perhaps it would 
-   be nice if reglookup could be cross-compiled using MinGW.  Then a 
-   binary could be distributed for that platform.  This app was never 
-   meant for Windows though, so this isn't a high priority.
+ - Need to figure out a reasonably correct way to convert UTF-16LE charaters
+   to ASCII under Windows/MingW or other platforms that don't have proper 
+   libiconv support yet.  Then a build-time option or autodetection can 
+   dictate which version of conversion function is used.
 
  - Grep through the source for 'XXX', and you'll find more.
diff --git a/doc/man/man1/reglookup-recover.1.gz b/doc/man/man1/reglookup-recover.1.gz
index 7deef08..b885411 100644
Binary files a/doc/man/man1/reglookup-recover.1.gz and b/doc/man/man1/reglookup-recover.1.gz differ
diff --git a/doc/man/man1/reglookup-timeline.1.gz b/doc/man/man1/reglookup-timeline.1.gz
index 81cdc68..c1c4d3f 100644
Binary files a/doc/man/man1/reglookup-timeline.1.gz and b/doc/man/man1/reglookup-timeline.1.gz differ
diff --git a/doc/man/man1/reglookup.1.gz b/doc/man/man1/reglookup.1.gz
index 27fb131..9f14fd4 100644
Binary files a/doc/man/man1/reglookup.1.gz and b/doc/man/man1/reglookup.1.gz differ
diff --git a/doc/mingw-build.txt b/doc/mingw-build.txt
new file mode 100644
index 0000000..9768b87
--- /dev/null
+++ b/doc/mingw-build.txt
@@ -0,0 +1,80 @@
+Cross-compiling RegLookup to Windows with MinGW
+===============================================
+
+MinGW can be used to compile Windows binaries from UNIX environments.
+The following instructions outline the steps required to build
+reglookup.exe and reglookup-recover.exe.  This process is experimental
+and Windows binaries have not been well tested.  You have been warned.
+
+Prerequisites
+-------------
+
+- Before you start, ensure you have MinGW installed.  Under Debian,
+  install the `mingw32' package.
+
+- Download pre-compiled libiconv packages from here:
+   http://gnuwin32.sourceforge.net/packages/libiconv.htm
+
+  You will need to download the "Binaries" and "Developer files"
+  packages, whose files are named libiconv-VERSION-bin.zip and
+  libiconv-VERSION-lib.zip respectively.
+
+- Unpack both zip files into a designated top-level directory.
+  Suggested commands:
+  $ mkdir /usr/local/src/libiconv-VERSION-bin /usr/local/src/libiconv-VERSION-lib
+  $ cd /usr/local/src/libiconv-VERSION-bin
+  $ unzip .../path/to/libiconv-VERSION-bin.zip
+  $ cd /usr/local/src/libiconv-VERSION-lib
+  $ unzip .../path/to/libiconv-VERSION-lib.zip
+  
+
+Building
+--------
+
+Review the top level RegLookup Makefile to ensure the settings match
+your environment.  Find the conditional block which looks like:
+
+################################################################################
+# MinGW cross-compiling build settings
+ifdef BUILD_MINGW
+
+## These may need to be changed
+CC=i586-mingw32msvc-cc
+LIBICONV_PATH=/usr/local/src/libiconv-1.9.2-1-lib
+
+## These probably do not need to be changed
+BIN_EXT=.exe
+INC:=$(INC) -I$(LIBICONV_PATH)/include
+EXTRA_OBJ=$(LIBICONV_PATH)/lib/libiconv.dll.a
+
+endif
+################################################################################
+
+
+If either the CC or LIBICONV_PATH settings are incorrect for your
+system, either update the Makefile, or override these options at build
+time when you run make.  For instance, the above settings in the
+Makefile are correct, you can execute the build by running:
+
+$ make BUILD_MINGW=1
+
+Alternatively, you may override the variables above with:
+
+$ make BUILD_MINGW=1 CC=my-mingw-binary LIBICONV_PATH=.../path/to/libiconv-VERSION-lib
+
+Once the build is complete, you'll find the .exe files under the
+build/bin directory.
+
+
+Installation
+------------
+Naturally, there is no install Makefile target for the MinGW build
+process, since we aren't installing on the local system.  To install
+these binaries on a Windows machine, simply copy over the reglookup.exe
+and reglookup-recover.exe files from the build/bin directory to the
+desired host.  In addition, you will need to install the libiconv2.dll
+file on that host (either in the same directory as the reglookup
+executables, or somewhere in the DLL search path).  This file is
+available in the libiconv-VERSION-bin.zip file you downloaded earlier,
+under the `bin' subdirectory.
+
diff --git a/doc/reglookup-recover.1.docbook b/doc/reglookup-recover.1.docbook
index 41266d9..95f47f5 100644
--- a/doc/reglookup-recover.1.docbook
+++ b/doc/reglookup-recover.1.docbook
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <refentry id='reglookup-recover.1'>
-  <!--  $Id: reglookup-recover.1.docbook 119 2008-08-09 05:55:45Z tim $ -->
+  <!--  $Id: reglookup-recover.1.docbook 138 2009-02-08 19:53:48Z tim $ -->
   <refmeta>
     <refentrytitle>reglookup</refentrytitle>
     <manvolnum>1</manvolnum>
@@ -25,7 +25,6 @@
     <para>
         reglookup-recover attempts to scour a Windows registry hive for 
         deleted data structures and outputs those found in a CSV-like format.
-        print them out to stdout in a CSV-like format.
     </para>
   </refsect1>
 
@@ -42,7 +41,7 @@
         </term>
         <listitem>
 	  <para>
-	    Verbose output. (Currently does little to nothing.)
+	    Verbose output.
 	  </para>
         </listitem>
       </varlistentry>
diff --git a/doc/reglookup.1.docbook b/doc/reglookup.1.docbook
index c7e8d5d..9f208ff 100644
--- a/doc/reglookup.1.docbook
+++ b/doc/reglookup.1.docbook
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <refentry id='reglookup.1'>
-  <!--  $Id: reglookup.1.docbook 119 2008-08-09 05:55:45Z tim $ -->
+  <!--  $Id: reglookup.1.docbook 138 2009-02-08 19:53:48Z tim $ -->
   <refmeta>
     <refentrytitle>reglookup</refentrytitle>
     <manvolnum>1</manvolnum>
@@ -26,8 +26,7 @@
         reglookup is designed to read windows registry elements and
         print them out to stdout in a CSV-like format.  It has filtering
         options to narrow the focus of the output.  This tool is
-        designed to work with on Windows NT/2K/XP/2K3/Vista registries, 
-        though your mileage may vary.
+        designed to work with on Windows NT-based registries.
     </para>
   </refsect1>
 
@@ -107,9 +106,9 @@
         </term>
         <listitem>
 	  <para>
-	    Adds four additional columns to output containing 
-	    information from key security descriptors.  The columns 
-	    are: owner, group, sacl, dacl.
+	    Adds five additional columns to output containing 
+	    information from key security descriptors and rarely used
+            fields.  The columns are: owner, group, sacl, dacl, class.
 	    (This feature's output has not been extensively tested.)
 	  </para>
         </listitem>
@@ -137,7 +136,7 @@
         </term>
         <listitem>
 	  <para>
-	    Verbose output. (Currently does little to nothing.)
+	    Verbose output.
 	  </para>
         </listitem>
       </varlistentry>
@@ -298,9 +297,6 @@
        any bugs.  Sample registry files and/or patches are greatly appreciated.)
     </para>
     <para>
-      Verbose output is not working.
-    </para>
-    <para>
       The SID conversions haven't been carefully checked for accuracy.
     </para>
     <para>
diff --git a/include/lru_cache.h b/include/lru_cache.h
index 5b1ec8c..80f77d3 100644
--- a/include/lru_cache.h
+++ b/include/lru_cache.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 Timothy D. Morgan
+ * Copyright (C) 2008-2009 Timothy D. Morgan
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -14,7 +14,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
  *
- * $Id: lru_cache.h 122 2008-08-09 20:24:01Z tim $
+ * $Id: lru_cache.h 147 2009-02-22 19:31:52Z tim $
  */
 
 #ifndef LRU_CACHE_H
@@ -26,6 +26,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
+#include "talloc.h"
 
 struct lru_cache_element;
 typedef struct lru_cache_element lru_cache_element; 
@@ -49,15 +50,17 @@ typedef struct _lru_cache
   lru_cache_element* oldest;
   lru_cache_element* newest;
   lru_cache_element** table;
-  bool free_data;
+  bool talloc_data;
 } lru_cache;
 
 
-lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret, bool free_data);
+lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret);
+lru_cache* lru_cache_create_ctx(void* talloc_ctx, uint32_t max_keys, 
+				uint32_t secret, bool talloc_data);
 void lru_cache_destroy(lru_cache* ht);
 
-/* Returns a pointer to the old, replaced data stored at index.  
- * Returns NULL if no entry was overwritten.
+/* 
+ * 
  */
 bool lru_cache_update(lru_cache* ht, const void* index, 
 		      uint32_t index_len, void* data);
diff --git a/include/range_list.h b/include/range_list.h
index 7ef9536..942528c 100644
--- a/include/range_list.h
+++ b/include/range_list.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 Timothy D. Morgan
+ * Copyright (C) 2008-2009 Timothy D. Morgan
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -14,17 +14,18 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
  *
- * $Id: range_list.h 122 2008-08-09 20:24:01Z tim $
+ * $Id: range_list.h 148 2009-02-22 23:22:59Z tim $
  */
 
+#ifndef _RANGE_LIST_H
+#define _RANGE_LIST_H
+
 #include <stdlib.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <string.h>
-
-#ifndef _RANGE_LIST_H
-#define _RANGE_LIST_H
-
+#include <math.h>
+#include "talloc.h"
 
 typedef struct _range_list_element
 {
diff --git a/include/regfi.h b/include/regfi.h
index f4165d3..a381560 100644
--- a/include/regfi.h
+++ b/include/regfi.h
@@ -1,16 +1,15 @@
 /*
- * Branched from Samba project, Subversion repository version #6903:
+ * Branched from Samba project Subversion repository, version #6903:
  *   http://viewcvs.samba.org/cgi-bin/viewcvs.cgi/trunk/source/include/regfio.h?rev=6903&view=auto
  *
- * Unix SMB/CIFS implementation.
- * Windows NT registry I/O library
+ * Windows NT (and later) registry parsing library
  *
- * Copyright (C) 2005-2008 Timothy D. Morgan
+ * Copyright (C) 2005-2009 Timothy D. Morgan
  * Copyright (C) 2005 Gerald (Jerry) Carter
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
+ * the Free Software Foundation; version 3 of the License.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -21,7 +20,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: regfi.h 121 2008-08-09 17:22:26Z tim $
+ * $Id: regfi.h 152 2009-06-02 20:00:38Z tim $
  */
 
 /************************************************************
@@ -36,6 +35,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <stdbool.h>
+#include <stdarg.h>
 #include <string.h>
 #include <errno.h>
 #include <time.h>
@@ -45,14 +45,27 @@
 #include <unistd.h>
 #include <assert.h>
 
+#include "talloc.h"
 #include "smb_deps.h"
+#include "winsec.h"
 #include "void_stack.h"
 #include "range_list.h"
 #include "lru_cache.h"
 
 /******************************************************************************/
-/* Macros */
- 
+
+/* regfi library error message types */
+#define REGFI_MSG_INFO  0x0001
+#define REGFI_MSG_WARN  0x0004
+#define REGFI_MSG_ERROR 0x0010
+
+/* Windows is lame */
+#ifdef O_BINARY
+#define REGFI_OPEN_FLAGS O_RDONLY|O_BINARY
+#else
+#define REGFI_OPEN_FLAGS O_RDONLY
+#endif
+
 /* Registry data types */
 #define REG_NONE                       0
 #define REG_SZ		               1
@@ -69,25 +82,37 @@
 #define REG_QWORD                      11 /* 64-bit little endian */
 /* XXX: Has MS defined a REG_QWORD_BE? */
 /* Not a real type in the registry */
-#define REG_KEY                        0x7FFFFFFF
-
-#define REGF_BLOCKSIZE		   0x1000
-#define REGF_ALLOC_BLOCK	   0x1000 /* Minimum allocation unit for HBINs */
-#define REGF_MAX_DEPTH		   512
-
-/* header sizes for various records */
-#define REGF_MAGIC_SIZE		   4
-#define HBIN_MAGIC_SIZE		   4
-#define HBIN_HEADER_REC_SIZE	   0x20
-#define REC_HDR_SIZE		   2
-
-#define REGF_OFFSET_NONE           0xffffffff
+#define REG_KEY                    0x7FFFFFFF
+
+#define REGFI_MAX_DEPTH		   512
+#define REGFI_OFFSET_NONE          0xffffffff
+
+/* XXX: This is totally arbitrary right now. */
+#define REGFI_MAX_SUBKEY_DEPTH     255
+
+/* Header sizes and magic number lengths for various records */
+#define REGFI_HBIN_ALLOC           0x1000 /* Minimum allocation unit for HBINs */
+#define REGFI_REGF_SIZE            0x1000 /* "regf" header block size */
+#define REGFI_REGF_MAGIC_SIZE      4
+#define REGFI_REGF_NAME_SIZE       64
+#define REGFI_REGF_RESERVED1_SIZE  340
+#define REGFI_REGF_RESERVED2_SIZE  3528
+#define REGFI_HBIN_MAGIC_SIZE      4
+#define REGFI_CELL_MAGIC_SIZE      2
+#define REGFI_HBIN_HEADER_SIZE     0x20
 #define REGFI_NK_MIN_LENGTH        0x4C
 #define REGFI_VK_MIN_LENGTH        0x14
 #define REGFI_SK_MIN_LENGTH        0x14
-#define REGFI_HASH_LIST_MIN_LENGTH 0x4
+#define REGFI_SUBKEY_LIST_MIN_LEN  0x4
+
 
 /* Constants used for validation */
+/* XXX: Can we add clock resolution validation as well as range?  It has
+ *      been reported that Windows timestamps are never more than a
+ *      certain granularity (250ms?), which could be used to help
+ *      eliminate false positives.  Would need to verify this and
+ *      perhaps conservatively implement a check.
+ */
  /* Minimum time is Jan 1, 1990 00:00:00 */
 #define REGFI_MTIME_MIN_HIGH       0x01B41E6D
 #define REGFI_MTIME_MIN_LOW        0x26F98000
@@ -99,20 +124,67 @@
 
 
 /* Flags for the vk records */
-#define VK_FLAG_NAME_PRESENT	   0x0001
-#define VK_DATA_IN_OFFSET	   0x80000000
-#define VK_MAX_DATA_LENGTH         1024*1024
+/* XXX: This next flag may be incorrect.  According to Jeffrey Muir,
+*       this may actually indicate that the value name is stored in
+*       UTF-16LE.
+*/
+#define REGFI_VK_FLAG_NAME_PRESENT 0x0001
+#define REGFI_VK_DATA_IN_OFFSET    0x80000000
+#define REGFI_VK_MAX_DATA_LENGTH   1024*1024  /* XXX: This is arbitrary */
+
+
+/* Known key flags */
+/*******************/
+/* These next two show up on normal-seeming keys in Vista and W2K3 registries */
+#define REGFI_NK_FLAG_UNKNOWN1     0x4000
+#define REGFI_NK_FLAG_UNKNOWN2     0x1000
+
+/* This next one shows up on root keys in some Vista "software" registries */
+#define REGFI_NK_FLAG_UNKNOWN3     0x0080
+
+/* Predefined handle.  Rumor has it that the valuelist count for this key is 
+ * where the handle is stored.
+ * http://msdn.microsoft.com/en-us/library/ms724836(VS.85).aspx
+ */
+#define REGFI_NK_FLAG_PREDEF_KEY   0x0040
+
+/* The name will be in ASCII if this next bit is set, otherwise UTF-16LE */
+#define REGFI_NK_FLAG_ASCIINAME    0x0020
+
+/* Symlink key.  
+ * See: http://www.codeproject.com/KB/system/regsymlink.aspx 
+ */
+#define REGFI_NK_FLAG_LINK         0x0010
+
+/* This key cannot be deleted */
+#define REGFI_NK_FLAG_NO_RM        0x0008
+
+/* Root of a hive */
+#define REGFI_NK_FLAG_ROOT         0x0004
 
-/* NK record types */
-#define NK_TYPE_LINKKEY		   0x0010
-#define NK_TYPE_NORMALKEY	   0x0020
-#define NK_TYPE_ROOTKEY		   0x002c
- /* TODO: Unknown type that shows up in Vista registries */
-#define NK_TYPE_UNKNOWN1           0x1020
+/* Mount point of another hive.  NULL/(default) value indicates which hive 
+ * and where in the hive it points to. 
+ */
+#define REGFI_NK_FLAG_HIVE_LINK    0x0002 
 
+/* These keys shouldn't be stored on disk, according to:
+ * http://geekswithblogs.net/sdorman/archive/2007/12/24/volatile-registry-keys.aspx
+ */
+#define REGFI_NK_FLAG_VOLATILE     0x0001
+
+/* Useful for identifying unknown flag types */
+#define REGFI_NK_KNOWN_FLAGS       (REGFI_NK_FLAG_PREDEF_KEY\
+				    | REGFI_NK_FLAG_ASCIINAME\
+				    | REGFI_NK_FLAG_LINK\
+				    | REGFI_NK_FLAG_NO_RM\
+				    | REGFI_NK_FLAG_ROOT\
+				    | REGFI_NK_FLAG_HIVE_LINK\
+				    | REGFI_NK_FLAG_VOLATILE\
+				    | REGFI_NK_FLAG_UNKNOWN1\
+				    | REGFI_NK_FLAG_UNKNOWN2)
 
 /* HBIN block */
-typedef struct regf_hbin 
+typedef struct _regfi_hbin 
 {
   uint32 file_off;       /* my offset in the registry file */
   uint32 ref_count;      /* how many active records are pointing to this
@@ -127,79 +199,94 @@ typedef struct regf_hbin
 			  * NOTE: This value may be unreliable!
 			  */
 
-  uint8 magic[HBIN_MAGIC_SIZE]; /* "hbin" */
-} REGF_HBIN;
+  uint8 magic[REGFI_HBIN_MAGIC_SIZE]; /* "hbin" */
+} REGFI_HBIN;
 
 
-/* Hash List -- list of key offsets and hashed names for consistency */
+/* Subkey List -- list of key offsets and hashed names for consistency */
 typedef struct 
 {
-  uint32 nk_off;
+  /* Virtual offset of NK record or additional subkey list, 
+   * depending on this list's type. 
+   */
+  uint32 offset;
+
   uint32 hash;
-} REGF_HASH_LIST_ELEM;
+} REGFI_SUBKEY_LIST_ELEM;
 
 
-typedef struct 
+typedef struct _regfi_subkey_list
 {
-  uint32 offset;	/* Real offset of this record's cell in the file */
-  uint32 cell_size;	 /* ((start_offset - end_offset) & 0xfffffff8) */
-  REGF_HBIN* hbin;       /* pointer to HBIN record (in memory) containing 
-			  * this nk record 
-			  */
-  uint32 hbin_off;	 /* offset from beginning of this hbin block */
-  REGF_HASH_LIST_ELEM* hashes;
+  /* Real offset of this record's cell in the file */
+  uint32 offset;
+
+  uint32 cell_size;
   
-  uint8 magic[REC_HDR_SIZE];
-  uint16 num_keys;
-} REGF_HASH_LIST;
+  /* Number of immediate children */
+  uint32 num_children;  
 
+  /* Total number of keys referenced by this list and it's children */
+  uint32 num_keys;      
 
-/* Key Value */
+  REGFI_SUBKEY_LIST_ELEM* elements;
+  uint8 magic[REGFI_CELL_MAGIC_SIZE];
+
+  /* Set if the magic indicates this subkey list points to child subkey lists */
+  bool recursive_type;  
+} REGFI_SUBKEY_LIST;
+
+
+typedef uint32 REGFI_VALUE_LIST_ELEM;
+typedef struct _regfi_value_list
+{
+  /* Actual number of values referenced by this list.  
+   * May differ from parent key's num_values if there were parsing errors. 
+   */
+  uint32 num_values;
+
+  REGFI_VALUE_LIST_ELEM* elements;
+} REGFI_VALUE_LIST;
+
+
+/* Value record */
 typedef struct 
 {
   uint32 offset;	/* Real offset of this record's cell in the file */
   uint32 cell_size;	/* ((start_offset - end_offset) & 0xfffffff8) */
 
-  REGF_HBIN* hbin;	/* pointer to HBIN record (in memory) containing 
-			 * this nk record 
-			 */
   uint8* data;
-  uint16 name_length;
   char*  valuename;
+  uint16 name_length;
   uint32 hbin_off;	/* offset from beginning of this hbin block */
   
   uint32 data_size;
   uint32 data_off;      /* offset of data cell (virtual) */
   uint32 type;
-  uint8  magic[REC_HDR_SIZE];
+  uint8  magic[REGFI_CELL_MAGIC_SIZE];
   uint16 flag;
   uint16 unknown1;
   bool data_in_offset;
-} REGF_VK_REC;
+} REGFI_VK_REC;
 
 
 /* Key Security */
-struct _regf_sk_rec;
+struct _regfi_sk_rec;
 
-typedef struct _regf_sk_rec 
+typedef struct _regfi_sk_rec 
 {
   uint32 offset;        /* Real file offset of this record */
   uint32 cell_size;	/* ((start_offset - end_offset) & 0xfffffff8) */
 
-  SEC_DESC* sec_desc;
+  WINSEC_DESC* sec_desc;
   uint32 hbin_off;	/* offset from beginning of this hbin block */
   
-  uint32 sk_off;	/* offset parsed from NK record used as a key
-			 * to lookup reference to this SK record 
-			 */
-  
   uint32 prev_sk_off;
   uint32 next_sk_off;
   uint32 ref_count;
   uint32 desc_size;     /* size of security descriptor */
   uint16 unknown_tag;
-  uint8  magic[REC_HDR_SIZE];
-} REGF_SK_REC;
+  uint8  magic[REGFI_CELL_MAGIC_SIZE];
+} REGFI_SK_REC;
 
 
 /* Key Name */
@@ -211,20 +298,19 @@ typedef struct
 			 */
 
   /* link in the other records here */
-  REGF_VK_REC** values;
-  REGF_HASH_LIST* subkeys;
+  REGFI_VALUE_LIST* values;
+  REGFI_SUBKEY_LIST* subkeys;
   
   /* header information */
-  /* XXX: should we be looking for types other than the root key type? */
   uint16 key_type;
-  uint8  magic[REC_HDR_SIZE];
+  uint8  magic[REGFI_CELL_MAGIC_SIZE];
   NTTIME mtime;
   uint16 name_length;
   uint16 classname_length;
   char* classname;
   char* keyname;
-  uint32 parent_off;	/* back pointer in registry hive */
-  uint32 classname_off;	
+  uint32 parent_off;	            /* pointer to parent key */
+  uint32 classname_off;
   
   /* max lengths */
   uint32 max_bytes_subkeyname;	    /* max subkey name * 2 */
@@ -240,65 +326,109 @@ typedef struct
   
   /* children */
   uint32 num_subkeys;
-  uint32 subkeys_off;	/* hash records that point to NK records */	
+  uint32 subkeys_off;	/* offset of subkey list that points to NK records */
   uint32 num_values;
   uint32 values_off;	/* value lists which point to VK records */
-  uint32 sk_off;	/* offset to SK record */  
-} REGF_NK_REC;
+  uint32 sk_off;	/* offset to SK record */
+} REGFI_NK_REC;
 
 
 
 /* REGF block */
 typedef struct 
 {
-  /* run time information */
-  int fd;	  /* file descriptor */
+  /* Run-time information */
+  /************************/
+  /* file descriptor */
+  int fd;
+
   /* For sanity checking (not part of the registry header) */
   uint32 file_length;
-  void* mem_ctx;  /* memory context for run-time file access information */
 
-  /* Experimental hbin lists */
+  /* Metadata about hbins */
   range_list* hbins;
 
-  /* file format information */  
-  uint8  magic[REGF_MAGIC_SIZE];/* "regf" */
+  /* SK record cached since they're repeatedly reused */
+  lru_cache* sk_cache;
+
+  /* Error/warning/info messages returned by lower layer functions */
+  char* last_message;
+
+  /* Mask for error message types that will be stored. */
+  uint16 msg_mask;
+
+
+  /* Data parsed from file header */
+  /********************************/
+  uint8  magic[REGFI_REGF_MAGIC_SIZE];/* "regf" */
+
+ /* These sequence numbers should match if
+  * the hive was properly synced to disk.
+  */
+  uint32 sequence1;            
+  uint32 sequence2;
+
   NTTIME mtime;
-  uint32 data_offset;		/* offset to record in the first (or any?) 
-				 * hbin block 
-				 */
-  uint32 last_block;		/* offset to last hbin block in file */
-
-  uint32 checksum;		/* Stored checksum. */
-  uint32 computed_checksum;     /* Our own calculation of the checksum.
-				 * (XOR of bytes 0x0000 - 0x01FB) 
-				 */
-  
-  /* unknown data structure values */
-  uint32 unknown1;
-  uint32 unknown2;
-  uint32 unknown3;
-  uint32 unknown4;
-  uint32 unknown5;
-  uint32 unknown6;
-  uint32 unknown7;
-} REGF_FILE;
+  uint32 major_version;  /* Set to 1 in all known hives */
+  uint32 minor_version;  /* Set to 3 or 5 in all known hives */
+  uint32 type;           /* XXX: Unverified.  Set to 0 in all known hives */
+  uint32 format;         /* XXX: Unverified.  Set to 1 in all known hives */
 
+  uint32 root_cell;  /* Offset to root cell in the first (or any?) hbin block */
+  uint32 last_block; /* Offset to last hbin block in file */
 
+  uint32 cluster;    /* XXX: Unverified. Set to 1 in all known hives */
 
-typedef struct 
+  /* Matches hive's base file name. Stored in UTF-16LE */
+  uint8 file_name[REGFI_REGF_NAME_SIZE];
+
+  WINSEC_UUID* rm_id;       /* XXX: Unverified. */
+  WINSEC_UUID* log_id;      /* XXX: Unverified. */
+  WINSEC_UUID* tm_id;       /* XXX: Unverified. */
+  uint32 flags;             /* XXX: Unverified. */
+  uint32 guid_signature;    /* XXX: Unverified. */
+
+  uint32 checksum;          /* Stored checksum from file */
+  uint32 computed_checksum; /* Our own calculation of the checksum. 
+			     * (XOR of bytes 0x0000 - 0x01FB) */
+
+  WINSEC_UUID* thaw_tm_id;  /* XXX: Unverified. */
+  WINSEC_UUID* thaw_rm_id;  /* XXX: Unverified. */
+  WINSEC_UUID* thaw_log_id; /* XXX: Unverified. */
+  uint32 boot_type;         /* XXX: Unverified. */
+  uint32 boot_recover;      /* XXX: Unverified. */
+
+  /* This seems to include random junk.  Possibly unsanitized memory left over
+   * from when header block was written.  For instance, chunks of nk records 
+   * can be found, though often it's all 0s. */
+  uint8 reserved1[REGFI_REGF_RESERVED1_SIZE];
+
+  /* This is likely reserved and unusued currently.  (Should be all 0s.)
+   * Included here for easier access in looking for hidden data 
+   * or doing research. */
+  uint8 reserved2[REGFI_REGF_RESERVED2_SIZE];
+
+} REGFI_FILE;
+
+
+/* XXX: Should move all caching (SK records, HBINs, NKs, etc) to a single
+ *      structure, probably REGFI_FILE.  Once key caching is in place, 
+ *      convert key_positions stack to store just key offsets rather than
+ *      whole keys.
+ */
+typedef struct _regfi_iterator
 {
-  REGF_FILE* f;
+  REGFI_FILE* f;
   void_stack* key_positions;
-  lru_cache* sk_recs;
-  REGF_NK_REC* cur_key;
+  REGFI_NK_REC* cur_key;
   uint32 cur_subkey;
   uint32 cur_value;
 } REGFI_ITERATOR;
 
 
-typedef struct 
+typedef struct _regfi_iter_position
 {
-  REGF_NK_REC* nk;
+  REGFI_NK_REC* nk;
   uint32 cur_subkey;
   /* We could store a cur_value here as well, but didn't see 
    * the use in it right now.
@@ -306,21 +436,32 @@ typedef struct
 } REGFI_ITER_POSITION;
 
 
-/******************************************************************************/
-/* Function Declarations */
-/*  Main API */
-const char*           regfi_type_val2str(unsigned int val);
-int                   regfi_type_str2val(const char* str);
+typedef struct _regfi_buffer
+{
+  uint8* buf;
+  uint32_t len;
+} REGFI_BUFFER;
+
 
-char*                 regfi_get_sacl(SEC_DESC* sec_desc);
-char*                 regfi_get_dacl(SEC_DESC* sec_desc);
-char*                 regfi_get_owner(SEC_DESC* sec_desc);
-char*                 regfi_get_group(SEC_DESC* sec_desc);
+/******************************************************************************/
+/*                         Main iterator API                                  */
+/******************************************************************************/
+REGFI_FILE*           regfi_open(const char* filename);
+int                   regfi_close(REGFI_FILE* r);
 
-REGF_FILE*            regfi_open(const char* filename);
-int                   regfi_close(REGF_FILE* r);
+/* regfi_get_messages: Get errors, warnings, and/or verbose information
+ *                     relating to processing of the given registry file.
+ *
+ * Arguments:
+ *   file     -- the structure for the registry file
+ *
+ * Returns:
+ *   A newly allocated char* which must be free()d by the caller.
+ */
+char*                 regfi_get_messages(REGFI_FILE* file);
+void                  regfi_set_message_mask(REGFI_FILE* file, uint16 mask);
 
-REGFI_ITERATOR*       regfi_iterator_new(REGF_FILE* fh);
+REGFI_ITERATOR*       regfi_iterator_new(REGFI_FILE* fh);
 void                  regfi_iterator_free(REGFI_ITERATOR* i);
 bool                  regfi_iterator_down(REGFI_ITERATOR* i);
 bool                  regfi_iterator_up(REGFI_ITERATOR* i);
@@ -330,23 +471,46 @@ bool                  regfi_iterator_find_subkey(REGFI_ITERATOR* i,
 						 const char* subkey_name);
 bool                  regfi_iterator_walk_path(REGFI_ITERATOR* i, 
 					       const char** path);
-const REGF_NK_REC*    regfi_iterator_cur_key(REGFI_ITERATOR* i);
-const REGF_SK_REC*    regfi_iterator_cur_sk(REGFI_ITERATOR* i);
-const REGF_NK_REC*    regfi_iterator_first_subkey(REGFI_ITERATOR* i);
-const REGF_NK_REC*    regfi_iterator_cur_subkey(REGFI_ITERATOR* i);
-const REGF_NK_REC*    regfi_iterator_next_subkey(REGFI_ITERATOR* i);
+const REGFI_NK_REC*   regfi_iterator_cur_key(REGFI_ITERATOR* i);
+const REGFI_SK_REC*   regfi_iterator_cur_sk(REGFI_ITERATOR* i);
+
+REGFI_NK_REC*         regfi_iterator_first_subkey(REGFI_ITERATOR* i);
+REGFI_NK_REC*         regfi_iterator_cur_subkey(REGFI_ITERATOR* i);
+REGFI_NK_REC*         regfi_iterator_next_subkey(REGFI_ITERATOR* i);
 
 bool                  regfi_iterator_find_value(REGFI_ITERATOR* i, 
 						const char* value_name);
-const REGF_VK_REC*    regfi_iterator_first_value(REGFI_ITERATOR* i);
-const REGF_VK_REC*    regfi_iterator_cur_value(REGFI_ITERATOR* i);
-const REGF_VK_REC*    regfi_iterator_next_value(REGFI_ITERATOR* i);
+REGFI_VK_REC*         regfi_iterator_first_value(REGFI_ITERATOR* i);
+REGFI_VK_REC*         regfi_iterator_cur_value(REGFI_ITERATOR* i);
+REGFI_VK_REC*         regfi_iterator_next_value(REGFI_ITERATOR* i);
+
+
+/********************************************************/
+/* Middle-layer structure loading, linking, and caching */
+/********************************************************/
+REGFI_NK_REC*         regfi_load_key(REGFI_FILE* file, uint32 offset, 
+				     bool strict);
+REGFI_VK_REC*         regfi_load_value(REGFI_FILE* file, uint32 offset, 
+				       bool strict);
+REGFI_SUBKEY_LIST*    regfi_load_subkeylist(REGFI_FILE* file, uint32 offset,
+					    uint32 num_keys, uint32 max_size,
+					    bool strict);
+REGFI_VALUE_LIST*     regfi_load_valuelist(REGFI_FILE* file, uint32 offset, 
+					   uint32 num_values, uint32 max_size,
+					   bool strict);
+
+/* These are cached so return values don't need to be freed. */
+const REGFI_SK_REC*   regfi_load_sk(REGFI_FILE* file, uint32 offset,
+				    bool strict);
+const REGFI_HBIN*     regfi_lookup_hbin(REGFI_FILE* file, uint32 voffset);
+
+
 
 /************************************/
 /*  Low-layer data structure access */
 /************************************/
-REGF_FILE*            regfi_parse_regf(int fd, bool strict);
-REGF_HBIN*            regfi_parse_hbin(REGF_FILE* file, uint32 offset, 
+REGFI_FILE*           regfi_parse_regf(int fd, bool strict);
+REGFI_HBIN*           regfi_parse_hbin(REGFI_FILE* file, uint32 offset, 
 				       bool strict);
 
 
@@ -361,43 +525,67 @@ REGF_HBIN*            regfi_parse_hbin(REGF_FILE* file, uint32 offset,
  * Returns:
  *   A newly allocated NK record structure, or NULL on failure.
  */
-REGF_NK_REC*          regfi_parse_nk(REGF_FILE* file, uint32 offset, 
+REGFI_NK_REC*         regfi_parse_nk(REGFI_FILE* file, uint32 offset,
 				     uint32 max_size, bool strict);
 
+REGFI_SUBKEY_LIST*    regfi_parse_subkeylist(REGFI_FILE* file, uint32 offset,
+					     uint32 max_size, bool strict);
 
-/* Private Functions */
-REGF_NK_REC*          regfi_rootkey(REGF_FILE* file);
-void                  regfi_key_free(REGF_NK_REC* nk);
-uint32                regfi_read(int fd, uint8* buf, uint32* length);
+REGFI_VK_REC*         regfi_parse_vk(REGFI_FILE* file, uint32 offset, 
+				     uint32 max_size, bool strict);
 
+REGFI_BUFFER          regfi_load_data(REGFI_FILE* file, 
+				      uint32 data_type, uint32 offset, 
+				      uint32 length, uint32 max_size, 
+				      bool data_in_offset, bool strict);
 
+REGFI_BUFFER          regfi_load_big_data(REGFI_FILE* file, 
+					  uint32 offset, uint32 data_length,
+					  uint32 cell_length, bool strict);
 
-/****************/
-/* Experimental */
-/****************/
-REGF_NK_REC* regfi_load_key(REGF_FILE* file, uint32 offset, bool strict);
+REGFI_SK_REC*         regfi_parse_sk(REGFI_FILE* file, uint32 offset, 
+				     uint32 max_size, bool strict);
 
-REGF_HASH_LIST* regfi_load_hashlist(REGF_FILE* file, uint32 offset, 
-				    uint32 num_keys, uint32 max_size, 
-				    bool strict);
+range_list*           regfi_parse_unalloc_cells(REGFI_FILE* file);
 
-REGF_VK_REC** regfi_load_valuelist(REGF_FILE* file, uint32 offset, 
-				   uint32 num_values, uint32 max_size, 
-				   bool strict);
+bool                  regfi_parse_cell(int fd, uint32 offset, 
+				       uint8* hdr, uint32 hdr_len,
+				       uint32* cell_length, bool* unalloc);
 
-REGF_VK_REC* regfi_parse_vk(REGF_FILE* file, uint32 offset, 
-			    uint32 max_size, bool strict);
+char*                 regfi_parse_classname(REGFI_FILE* file, uint32 offset,
+					    uint16* name_length, 
+					    uint32 max_size, bool strict);
 
-uint8* regfi_parse_data(REGF_FILE* file, uint32 offset, 
-			uint32 length, bool strict);
+/* Dispose of previously parsed records */
+void                  regfi_free_key(REGFI_NK_REC* nk);
+void                  regfi_free_value(REGFI_VK_REC* vk);
 
-REGF_SK_REC* regfi_parse_sk(REGF_FILE* file, uint32 offset, uint32 max_size, bool strict);
 
-range_list* regfi_parse_unalloc_cells(REGF_FILE* file);
 
-REGF_HBIN* regfi_lookup_hbin(REGF_FILE* file, uint32 offset);
+/************************************/
+/*    Private Functions             */
+/************************************/
+REGFI_NK_REC*         regfi_rootkey(REGFI_FILE* file);
+void                  regfi_subkeylist_free(REGFI_SUBKEY_LIST* list);
+uint32                regfi_read(int fd, uint8* buf, uint32* length);
+
+const char*           regfi_type_val2str(unsigned int val);
+int                   regfi_type_str2val(const char* str);
 
-bool regfi_parse_cell(int fd, uint32 offset, uint8* hdr, uint32 hdr_len,
-		      uint32* cell_length, bool* unalloc);
+char*                 regfi_get_sacl(WINSEC_DESC* sec_desc);
+char*                 regfi_get_dacl(WINSEC_DESC* sec_desc);
+char*                 regfi_get_owner(WINSEC_DESC* sec_desc);
+char*                 regfi_get_group(WINSEC_DESC* sec_desc);
+
+REGFI_SUBKEY_LIST*    regfi_merge_subkeylists(uint16 num_lists, 
+					      REGFI_SUBKEY_LIST** lists,
+					      bool strict);
+REGFI_SUBKEY_LIST*    regfi_load_subkeylist_aux(REGFI_FILE* file, uint32 offset,
+						uint32 max_size, bool strict,
+						uint8 depth_left);
+void                  regfi_add_message(REGFI_FILE* file, uint16 msg_type, 
+					const char* fmt, ...);
+REGFI_NK_REC*         regfi_copy_nk(const REGFI_NK_REC* nk);
+REGFI_VK_REC*         regfi_copy_vk(const REGFI_VK_REC* vk);
 
 #endif	/* _REGFI_H */
diff --git a/include/smb_deps.h b/include/smb_deps.h
index 9602e94..cae3f8b 100644
--- a/include/smb_deps.h
+++ b/include/smb_deps.h
@@ -3,7 +3,7 @@
  * depends upon, from the Samba Subversion tree.  See:
  *   http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/
  *
- * Copyright (C) 2005 Timothy D. Morgan
+ * Copyright (C) 2005,2009 Timothy D. Morgan
  * Copyright (C) 1992-2005 Samba development team 
  *               (see individual files under Subversion for details.)
  *
@@ -20,11 +20,15 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: smb_deps.h 111 2008-05-01 04:06:22Z tim $
+ * $Id: smb_deps.h 134 2009-01-16 18:36:04Z tim $
  */
 
+#ifndef _SMB_DEPS_H
+#define _SMB_DEPS_H
+
 #include <stdlib.h>
 #include <stdbool.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <string.h>
 #include <errno.h>
@@ -42,53 +46,21 @@ void* zcalloc(size_t size, unsigned int count);
 
 /* From includes.h */
 
-#define uint8 unsigned char
-#define int16 short
-#define uint16 unsigned short
-#define int32 int
-#define uint32 unsigned int
-
-#define SMB_STRUCT_STAT struct stat
-#define QSORT_CAST (int (*)(const void *, const void *))
+#define uint8  uint8_t
+#define int16  int8_t
+#define uint16 uint16_t
+#define int32  int32_t
+#define uint32 uint32_t
 
 #define MIN(a,b) ((a)<(b)?(a):(b))
 #define MAX(a,b) ((a)>(b)?(a):(b))
 
 extern int DEBUGLEVEL;
 
-#define DLIST_ADD(list, p) \
-{ \
-        if (!(list)) { \
-		(list) = (p); \
-		(p)->next = (p)->prev = NULL; \
-	} else { \
-		(list)->prev = (p); \
-		(p)->next = (list); \
-		(p)->prev = NULL; \
-		(list) = (p); \
-	}\
-}
-
 /* End of stuff from includes.h */
 
 /* From smb.h */
 
-#define MAXSUBAUTHS 15
-
-typedef struct sid_info
-{
-  uint8  sid_rev_num;             /**< SID revision number */
-  uint8  num_auths;               /**< Number of sub-authorities */
-  uint8  id_auth[6];              /**< Identifier Authority */
-  /*
-   *  Pointer to sub-authorities.
-   *
-   * @note The values in these uint32's are in *native* byteorder, not
-   * neccessarily little-endian...... JRA.
-   */
-  uint32 sub_auths[MAXSUBAUTHS];
-} DOM_SID;
-
 typedef struct nttime_info
 {
   uint32 low;
@@ -97,51 +69,6 @@ typedef struct nttime_info
 
 /* End of stuff from smb.h */
 
-/* From smb_macros.h */
-
-#define TALLOC_ZERO_P(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type)
-#define SMB_MALLOC_P(type) (type *)malloc_(sizeof(type))
-#define TALLOC_ARRAY(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type)
-#define TALLOC_ZERO_ARRAY(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type)
-#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
-
-/* End of stuff from smb_macros.h */
-
-/* From ntdomain.h */
-
-struct uuid {
-       uint32 time_low;
-       uint16 time_mid;
-       uint16 time_hi_and_version;
-       uint8  clock_seq[2];
-       uint8  node[6];
-};
-
-typedef struct _prs_struct {
-	bool io; /* parsing in or out of data stream */
-	/* 
-	 * If the (incoming) data is big-endian. On output we are
-	  * always little-endian.
-	   */ 
-	   bool bigendian_data;
-	   uint8 align; /* data alignment */
-	   bool is_dynamic; /* Do we own this memory or not ? */
-	   uint32 data_offset; /* Current working offset into data. */
-	   uint32 buffer_size; /* Current allocated size of the buffer. */
-	   uint32 grow_size; /* size requested via prs_grow() calls */
-	   char *data_p; /* The buffer itself. */
-	   void *mem_ctx; /* When unmarshalling, use this.... */
-} prs_struct;
-
-#define MARSHALL 0
-#define UNMARSHALL 1
-
-#define RPC_LITTLE_ENDIAN  0
-#define RPC_PARSE_ALIGN    4
-
-/* End of stuff from ntdomain.h */
-
-
 /* From lib/time.c */
 
 #define CHAR_BIT 8
@@ -155,161 +82,4 @@ time_t nt_time_to_unix(const NTTIME* nt);
 
 /* End of stuff from lib/time.c */
 
-/* From rpc_dce.h */
-
-#define MAX_PDU_FRAG_LEN 0x10b8 /* this is what w2k sets */
-
-/* End of stuff from rpc_dce.h */
-
-/* From parse_prs.h */
-
-bool prs_grow(prs_struct *ps, uint32 extra_space);
-bool prs_align(prs_struct *ps);
-bool prs_init(prs_struct *ps, uint32 size, void *ctx, bool io);
-char *prs_mem_get(prs_struct *ps, uint32 extra_size);
-bool prs_uint32(const char *name, prs_struct *ps, int depth, uint32 *data32);
-bool prs_uint32s(const char *name, prs_struct *ps, 
-		 int depth, uint32 *data32s, int len);
-bool prs_uint16(const char *name, prs_struct *ps, int depth, uint16 *data16);
-bool prs_uint16_pre(const char *name, prs_struct *ps, int depth, 
-		    uint16 *data16, uint32 *offset);
-bool prs_uint16_post(const char *name, prs_struct *ps, int depth, 
-		     uint16 *data16, uint32 ptr_uint16, uint32 start_offset);
-bool prs_uint8(const char *name, prs_struct *ps, int depth, uint8 *data8);
-bool prs_uint8s(const char *name, prs_struct *ps, int depth, 
-		uint8* data8s, int len);
-bool prs_set_offset(prs_struct *ps, uint32 offset);
-
-/* End of stuff from parse_prs.h */
-
-
-/* From rpc_secdesc.h */
-
-typedef struct security_info_info
-{
-	uint32 mask;
-
-} SEC_ACCESS;
-
-typedef struct security_ace_info
-{
-	uint8 type;  /* xxxx_xxxx_ACE_TYPE - e.g allowed / denied etc */
-	uint8 flags; /* xxxx_INHERIT_xxxx - e.g OBJECT_INHERIT_ACE */
-	uint16 size;
-
-	SEC_ACCESS info;
-
-	/* this stuff may be present when type is XXXX_TYPE_XXXX_OBJECT */
-	uint32  obj_flags; /* xxxx_ACE_OBJECT_xxxx e.g present/inherited present etc */
-	struct uuid obj_guid;  /* object GUID */
-	struct uuid inh_guid;  /* inherited object GUID */		
-        /* eof object stuff */
-
-	DOM_SID trustee;
-
-} SEC_ACE;
-
-typedef struct security_acl_info
-{
-	uint16 revision; /* 0x0003 */
-	uint16 size; /* size in bytes of the entire ACL structure */
-	uint32 num_aces; /* number of Access Control Entries */
-
-	SEC_ACE *ace;
-
-} SEC_ACL;
-
-typedef struct security_descriptor_info
-{
-	uint16 revision; /* 0x0001 */
-	uint16 type;     /* SEC_DESC_xxxx flags */
-
-	uint32 off_owner_sid; /* offset to owner sid */
-	uint32 off_grp_sid  ; /* offset to group sid */
-	uint32 off_sacl     ; /* offset to system list of permissions */
-	uint32 off_dacl     ; /* offset to list of permissions */
-
-	SEC_ACL *dacl; /* user ACL */
-	SEC_ACL *sacl; /* system ACL */
-	DOM_SID *owner_sid; 
-	DOM_SID *grp_sid;
-
-} SEC_DESC;
-
-/* End of stuff from rpc_secdesc.h */
-
-
-/* From pstring.h */
-
-#define FSTRING_LEN 256
-typedef char fstring[FSTRING_LEN];
-
-/* End of stuff from pstring.h */
-
-
-/* From rpc_secdes.h */
-
-#define SEC_DESC_DACL_PRESENT		0x0004
-#define SEC_DESC_SACL_PRESENT		0x0010
-#define  SEC_DESC_HEADER_SIZE (2 * sizeof(uint16) + 4 * sizeof(uint32))
-   /* thanks for Jim McDonough <jmcd at us.ibm.com> */
-#define SEC_ACE_OBJECT_PRESENT        0x00000001 
-#define SEC_ACE_OBJECT_INHERITED_PRESENT 0x00000002
-
-#define SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT	0x5
-#define SEC_ACE_TYPE_ACCESS_DENIED_OBJECT     	0x6
-#define SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT      	0x7
-#define SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT	0x8
-
-/* End of stuff from rpc_secdes.h */
-
-/* From rpc_parse/parse_misc.c */
-
-bool smb_io_uuid(const char *desc, struct uuid *uuid, 
-		 prs_struct *ps, int depth);
-bool smb_io_time(const char *desc, NTTIME *nttime, prs_struct *ps, int depth);
-bool smb_io_dom_sid(const char *desc, DOM_SID *sid, prs_struct *ps, int depth);
-
-/* End of stuff from rpc_parse/parse_misc.c */
-
-/* From lib/util_sid.c */
-
-size_t sid_size(const DOM_SID *sid);
-int sid_compare_auth(const DOM_SID *sid1, const DOM_SID *sid2);
-int sid_compare(const DOM_SID *sid1, const DOM_SID *sid2);
-bool sid_equal(const DOM_SID *sid1, const DOM_SID *sid2);
-
-/* End of stuff from lib/util_sid.c */
-
-/* From lib/secace.c */
-
-bool sec_ace_object(uint8 type);
-
-/* End of stuff from lib/secace.c */
-
-/* From rpc_parse/parse_sec.c */
-
-bool sec_io_access(const char *desc, SEC_ACCESS *t, prs_struct *ps, int depth);
-bool sec_io_ace(const char *desc, SEC_ACE *psa, prs_struct *ps, int depth);
-bool sec_io_acl(const char *desc, SEC_ACL **ppsa, prs_struct *ps, int depth);
-bool sec_io_desc(const char *desc, SEC_DESC **ppsd, prs_struct *ps, int depth);
-
-/* End of stuff from rpc_parse/parse_sec.c */
-
-/* From lib/secace.c */
-
-bool sec_ace_equal(SEC_ACE *s1, SEC_ACE *s2);
-
-/* End of stuff from lib/secace.c */
-
-/* From lib/secacl.c */
-
-bool sec_acl_equal(SEC_ACL *s1, SEC_ACL *s2);
-
-/* End of stuff from lib/secacl.c */
-
-/* From lib/secdesc.c */
-
-bool sec_desc_equal(SEC_DESC *s1, SEC_DESC *s2);
-
-/* End of stuff from lib/secdesc.c */
+#endif /* _SMB_DEPS_H */
diff --git a/include/talloc.h b/include/talloc.h
new file mode 100644
index 0000000..f34065c
--- /dev/null
+++ b/include/talloc.h
@@ -0,0 +1,162 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba temporary memory allocation functions
+
+   Copyright (C) Andrew Tridgell 2004-2005
+   Copyright (C) Stefan Metzmacher 2006
+   Copyright (C) Timothy D. Morgan 2009 
+   
+     ** NOTE! The following LGPL license applies to the talloc library.
+     ** This does NOT imply that all of reglookup is released under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 3 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+/* $Id: talloc.h 147 2009-02-22 19:31:52Z tim $ */
+
+#ifndef _TALLOC_H_
+#define _TALLOC_H_
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdbool.h>
+#include "smb_deps.h" /* MAX macro */
+
+/*
+  this uses a little trick to allow __LINE__ to be stringified
+*/
+#ifndef __location__
+#define __TALLOC_STRING_LINE1__(s)    #s
+#define __TALLOC_STRING_LINE2__(s)   __TALLOC_STRING_LINE1__(s)
+#define __TALLOC_STRING_LINE3__  __TALLOC_STRING_LINE2__(__LINE__)
+#define __location__ __FILE__ ":" __TALLOC_STRING_LINE3__
+#endif
+
+#ifndef TALLOC_DEPRECATED
+#define TALLOC_DEPRECATED 0
+#endif
+
+#ifndef PRINTF_ATTRIBUTE
+#if (__GNUC__ >= 3)
+/** Use gcc attribute to check printf fns.  a1 is the 1-based index of
+ * the parameter containing the format, and a2 the index of the first
+ * argument. Note that some gcc 2.x versions don't handle this
+ * properly **/
+#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2)))
+#else
+#define PRINTF_ATTRIBUTE(a1, a2)
+#endif
+#endif
+
+#define talloc_set_destructor(ptr, function) \
+	_talloc_set_destructor((ptr), (int (*)(void *))(function))
+#define _TALLOC_TYPEOF(ptr) void *
+#define talloc_steal(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr))
+
+#define talloc_reference(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_reference((ctx),(ptr))
+#define talloc_move(ctx, ptr) (_TALLOC_TYPEOF(*(ptr)))_talloc_move((ctx),(void *)(ptr))
+
+/* useful macros for creating type checked pointers */
+#define talloc(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type)
+#define talloc_size(ctx, size) talloc_named_const(ctx, size, __location__)
+#define talloc_ptrtype(ctx, ptr) (_TALLOC_TYPEOF(ptr))talloc_size(ctx, sizeof(*(ptr)))
+
+#define talloc_new(ctx) talloc_named_const(ctx, 0, "talloc_new: " __location__)
+
+#define talloc_zero(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type)
+#define talloc_zero_size(ctx, size) _talloc_zero(ctx, size, __location__)
+
+#define talloc_zero_array(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type)
+#define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type)
+#define talloc_array_size(ctx, size, count) _talloc_array(ctx, size, count, __location__)
+#define talloc_array_ptrtype(ctx, ptr, count) (_TALLOC_TYPEOF(ptr))talloc_array_size(ctx, sizeof(*(ptr)), count)
+
+#define talloc_realloc(ctx, p, type, count) (type *)_talloc_realloc_array(ctx, p, sizeof(type), count, #type)
+#define talloc_realloc_size(ctx, ptr, size) _talloc_realloc(ctx, ptr, size, __location__)
+
+#define talloc_memdup(t, p, size) _talloc_memdup(t, p, size, __location__)
+
+#define talloc_set_type(ptr, type) talloc_set_name_const(ptr, #type)
+#define talloc_get_type(ptr, type) (type *)talloc_check_name(ptr, #type)
+
+#define talloc_find_parent_bytype(ptr, type) (type *)talloc_find_parent_byname(ptr, #type)
+
+/* The following definitions come from talloc.c  */
+void *_talloc(const void *context, size_t size);
+void *talloc_pool(const void *context, size_t size);
+void _talloc_set_destructor(const void *ptr, int (*destructor)(void *));
+int talloc_increase_ref_count(const void *ptr);
+size_t talloc_reference_count(const void *ptr);
+void *_talloc_reference(const void *context, const void *ptr);
+int talloc_unlink(const void *context, void *ptr);
+const char *talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+void talloc_set_name_const(const void *ptr, const char *name);
+void *talloc_named(const void *context, size_t size, 
+		   const char *fmt, ...) PRINTF_ATTRIBUTE(3,4);
+void *talloc_named_const(const void *context, size_t size, const char *name);
+const char *talloc_get_name(const void *ptr);
+void *talloc_check_name(const void *ptr, const char *name);
+void *talloc_parent(const void *ptr);
+const char *talloc_parent_name(const void *ptr);
+void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2);
+int talloc_free(void *ptr);
+void talloc_free_children(void *ptr);
+void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name);
+void *_talloc_steal(const void *new_ctx, const void *ptr);
+void *_talloc_move(const void *new_ctx, const void *pptr);
+size_t talloc_total_size(const void *ptr);
+size_t talloc_total_blocks(const void *ptr);
+void talloc_report_depth_cb(const void *ptr, int depth, int max_depth,
+			    void (*callback)(const void *ptr,
+			  		     int depth, int max_depth,
+					     int is_ref,
+					     void *private_data),
+			    void *private_data);
+void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f);
+void talloc_report_full(const void *ptr, FILE *f);
+void talloc_report(const void *ptr, FILE *f);
+void talloc_enable_null_tracking(void);
+void talloc_disable_null_tracking(void);
+void talloc_enable_leak_report(void);
+void talloc_enable_leak_report_full(void);
+void *_talloc_zero(const void *ctx, size_t size, const char *name);
+void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name);
+void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name);
+void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name);
+void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name);
+void *talloc_realloc_fn(const void *context, void *ptr, size_t size);
+void *talloc_autofree_context(void);
+size_t talloc_get_size(const void *ctx);
+void *talloc_find_parent_byname(const void *ctx, const char *name);
+void talloc_show_parents(const void *context, FILE *file);
+int talloc_is_parent(const void *context, const void *ptr);
+
+char *talloc_strdup(const void *t, const char *p);
+char *talloc_strdup_append(char *s, const char *a);
+char *talloc_strdup_append_buffer(char *s, const char *a);
+
+char *talloc_strndup(const void *t, const char *p, size_t n);
+char *talloc_strndup_append(char *s, const char *a, size_t n);
+char *talloc_strndup_append_buffer(char *s, const char *a, size_t n);
+
+char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+
+char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+char *talloc_asprintf_append(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+
+#endif
diff --git a/include/void_stack.h b/include/void_stack.h
index 7d5f415..42fefb6 100644
--- a/include/void_stack.h
+++ b/include/void_stack.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005,2007 Timothy D. Morgan
+ * Copyright (C) 2005,2007,2009 Timothy D. Morgan
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -14,15 +14,16 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
  *
- * $Id: void_stack.h 111 2008-05-01 04:06:22Z tim $
+ * $Id: void_stack.h 150 2009-03-02 02:17:46Z tim $
  */
 
+#ifndef _VOID_STACK_H
+#define _VOID_STACK_H
+
 #include <stdlib.h>
 #include <stdbool.h>
 #include <string.h>
-
-#ifndef _VOID_STACK_H
-#define _VOID_STACK_H
+#include "talloc.h"
 
 typedef struct _void_stack
 {
diff --git a/include/winsec.h b/include/winsec.h
new file mode 100644
index 0000000..1c2db4a
--- /dev/null
+++ b/include/winsec.h
@@ -0,0 +1,171 @@
+/*
+ * This file contains refactored Samba code used to interpret Windows
+ * Security Descriptors. See:
+ *   http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/
+ *
+ * Revisions have been made based on information provided by Microsoft
+ * at: 
+ *    http://msdn.microsoft.com/en-us/library/cc230366(PROT.10).aspx
+ *
+ * Copyright (C) 2005,2009 Timothy D. Morgan
+ * Copyright (C) 1992-2005 Samba development team 
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: winsec.h 148 2009-02-22 23:22:59Z tim $
+ */
+
+#ifndef _WINSEC_H
+#define _WINSEC_H
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "smb_deps.h"
+#include "talloc.h"
+
+
+/* This is the maximum number of subauths in a SID, as defined here:
+ *   http://msdn.microsoft.com/en-us/library/cc230371(PROT.10).aspx
+ */
+#define WINSEC_MAX_SUBAUTHS 15
+
+#define WINSEC_DESC_HEADER_SIZE     (5 * sizeof(uint32_t))
+#define WINSEC_ACL_HEADER_SIZE      (2 * sizeof(uint32_t))
+#define WINSEC_ACE_MIN_SIZE         16
+
+/* XXX: Fill in definitions of other flags */
+/* This self relative flag means offsets contained in the descriptor are relative
+ * to the descriptor's offset.  This had better be true in the registry.
+ */
+#define WINSEC_DESC_SELF_RELATIVE   0x8000
+#define WINSEC_DESC_SACL_PRESENT    0x0010
+#define WINSEC_DESC_DACL_PRESENT    0x0004
+
+#define WINSEC_ACE_OBJECT_PRESENT              0x00000001 
+#define WINSEC_ACE_OBJECT_INHERITED_PRESENT    0x00000002
+#define WINSEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT  0x5
+#define WINSEC_ACE_TYPE_ACCESS_DENIED_OBJECT   0x6
+#define WINSEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT    0x7
+#define WINSEC_ACE_TYPE_SYSTEM_ALARM_OBJECT    0x8
+
+
+typedef struct _winsec_uuid
+{
+       uint32 time_low;
+       uint16 time_mid;
+       uint16 time_hi_and_version;
+       uint8  clock_seq[2];
+       uint8  node[6];
+} WINSEC_UUID;
+
+
+typedef struct _winsec_sid
+{
+  uint8_t  sid_rev_num;             /* SID revision number */
+  uint8_t  num_auths;               /* Number of sub-authorities */
+  uint8_t  id_auth[6];              /* Identifier Authority */
+  /*
+   *  Pointer to sub-authorities.
+   *
+   * @note The values in these uint32_t's are in *native* byteorder, not
+   * neccessarily little-endian...... JRA.
+   */
+  /* XXX: Make this dynamically allocated? */
+  uint32_t sub_auths[WINSEC_MAX_SUBAUTHS];
+} WINSEC_DOM_SID;
+
+
+typedef struct _winsec_ace
+{
+	uint8_t type;  /* xxxx_xxxx_ACE_TYPE - e.g allowed / denied etc */
+	uint8_t flags; /* xxxx_INHERIT_xxxx - e.g OBJECT_INHERIT_ACE */
+	uint16_t size;
+	uint32_t access_mask;
+
+	/* this stuff may be present when type is XXXX_TYPE_XXXX_OBJECT */
+	uint32_t  obj_flags;   /* xxxx_ACE_OBJECT_xxxx e.g present/inherited present etc */
+	WINSEC_UUID* obj_guid;  /* object GUID */
+	WINSEC_UUID* inh_guid;  /* inherited object GUID */		
+        /* eof object stuff */
+
+	WINSEC_DOM_SID* trustee;
+
+} WINSEC_ACE;
+
+typedef struct _winsec_acl
+{
+	uint16_t revision; /* 0x0003 */
+	uint16_t size;     /* size in bytes of the entire ACL structure */
+	uint32_t num_aces; /* number of Access Control Entries */
+
+	WINSEC_ACE** aces;
+
+} WINSEC_ACL;
+
+typedef struct _winsec_desc
+{
+	uint8_t revision; /* 0x01 */
+	uint8_t sbz1;     /* "If the Control field has the RM flag set,
+			   *  then this field contains the resource
+			   *  manager (RM) control value. ... Otherwise,
+			   *  this field is reserved and MUST be set to
+			   *  zero." -- Microsoft.  See reference above.
+			   */
+	uint16_t control; /* WINSEC_DESC_* flags */
+
+	uint32_t off_owner_sid; /* offset to owner sid */
+	uint32_t off_grp_sid  ; /* offset to group sid */
+	uint32_t off_sacl     ; /* offset to system list of permissions */
+	uint32_t off_dacl     ; /* offset to list of permissions */
+
+	WINSEC_DOM_SID* owner_sid; 
+	WINSEC_DOM_SID* grp_sid;
+	WINSEC_ACL* sacl;       /* system ACL */
+	WINSEC_ACL* dacl;       /* user ACL */
+
+} WINSEC_DESC;
+
+WINSEC_DESC* winsec_parse_descriptor(const uint8_t* buf, uint32_t buf_len);
+void winsec_free_descriptor(WINSEC_DESC* desc);
+
+WINSEC_DESC* winsec_parse_desc(void* talloc_ctx,
+			       const uint8_t* buf, uint32_t buf_len);
+WINSEC_ACL* winsec_parse_acl(void* talloc_ctx, 
+			     const uint8_t* buf, uint32_t buf_len);
+WINSEC_ACE* winsec_parse_ace(void* talloc_ctx, 
+			     const uint8_t* buf, uint32_t buf_len);
+WINSEC_DOM_SID* winsec_parse_dom_sid(void* talloc_ctx, 
+				     const uint8_t* buf, uint32_t buf_len);
+WINSEC_UUID* winsec_parse_uuid(void* talloc_ctx, 
+			       const uint8_t* buf, uint32_t buf_len);
+
+size_t winsec_sid_size(const WINSEC_DOM_SID* sid);
+int winsec_sid_compare_auth(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2);
+int winsec_sid_compare(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2);
+bool winsec_sid_equal(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2);
+bool winsec_desc_equal(WINSEC_DESC* s1, WINSEC_DESC* s2);
+bool winsec_acl_equal(WINSEC_ACL* s1, WINSEC_ACL* s2);
+bool winsec_ace_equal(WINSEC_ACE* s1, WINSEC_ACE* s2);
+bool winsec_ace_object(uint8_t type);
+
+#endif /* _WINSEC_H */
diff --git a/lib/Makefile b/lib/Makefile
index c406e16..c761c61 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -2,7 +2,7 @@
 
 ################################################################################
 
-FILES=regfi.o smb_deps.o void_stack.o range_list.o lru_cache.o
+FILES=regfi.o smb_deps.o winsec.o void_stack.o range_list.o lru_cache.o talloc.o
 
 all: $(FILES)
 
@@ -12,6 +12,9 @@ regfi.o: regfi.c
 smb_deps.o: smb_deps.c
 	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ smb_deps.c
 
+winsec.o: winsec.c
+	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ winsec.c
+
 void_stack.o: void_stack.c
 	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ void_stack.c
 
@@ -21,5 +24,8 @@ range_list.o: range_list.c
 lru_cache.o: lru_cache.c
 	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ lru_cache.c
 
+talloc.o: talloc.c
+	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ talloc.c
+
 clean:
 	rm -f $(FILES)
diff --git a/lib/lru_cache.c b/lib/lru_cache.c
index 987717d..74f5f08 100644
--- a/lib/lru_cache.c
+++ b/lib/lru_cache.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 Timothy D. Morgan
+ * Copyright (C) 2008-2009 Timothy D. Morgan
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -14,10 +14,10 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
  *
- * $Id: lru_cache.c 122 2008-08-09 20:24:01Z tim $
+ * $Id: lru_cache.c 147 2009-02-22 19:31:52Z tim $
  */
 
-#include "../include/lru_cache.h"
+#include "lru_cache.h"
 
 
 #define LRU_CACHE_DEBUG 0
@@ -96,16 +96,24 @@ static void lru_cache_print(lru_cache* ht)
 }
 #endif
 
-lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret, bool free_data)
+
+lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret)
+{
+  return lru_cache_create_ctx(NULL, max_keys, secret, false);
+}
+
+
+lru_cache* lru_cache_create_ctx(void* talloc_ctx, uint32_t max_keys, 
+				uint32_t secret, bool talloc_data)
 {
   lru_cache* ret_val;
 
-  ret_val = (lru_cache*)malloc(sizeof(lru_cache));
+  ret_val = talloc(talloc_ctx, lru_cache);
   if(ret_val == NULL)
     return NULL;
 
   if(max_keys == 0)
-    ret_val->num_buckets = 2048;
+    ret_val->num_buckets = 1024;
   else
   {
     ret_val->num_buckets = max_keys/lru_cache_floor_log2(max_keys);
@@ -113,12 +121,11 @@ lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret, bool free_data)
       ret_val->num_buckets = 1;
   }
 
-  ret_val->table 
-    = (lru_cache_element**)malloc(sizeof(lru_cache_element*) 
-				  * ret_val->num_buckets);
+  ret_val->table = talloc_array(ret_val, 
+				lru_cache_element*, ret_val->num_buckets);
   if(ret_val->table == NULL)
   {
-    free(ret_val);
+    talloc_free(ret_val);
     return NULL;
   }
   
@@ -126,7 +133,7 @@ lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret, bool free_data)
   ret_val->newest = NULL;
   ret_val->max_keys = max_keys;
   ret_val->secret = secret;
-  ret_val->free_data = free_data;
+  ret_val->talloc_data = talloc_data;
   ret_val->num_keys = 0;
   memset(ret_val->table, 0, ret_val->num_buckets*sizeof(lru_cache_element*));
 
@@ -136,22 +143,8 @@ lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret, bool free_data)
 
 void lru_cache_destroy(lru_cache* ht)
 {
-  lru_cache_element* cur;
-  lru_cache_element* last = NULL;
-  
-  for(cur=ht->oldest; cur != NULL; last=cur,cur=cur->newer)
-  {
-    if(last != NULL)
-    {
-      if(ht->free_data)
-	free(last->data);
-      free(last->index);
-      free(last);
-    }
-  }
-  free(ht->table);
   ht->secret = 0;
-  free(ht);
+  talloc_free(ht);
 }
 
 
@@ -178,8 +171,8 @@ bool lru_cache_update(lru_cache* ht, const void* index,
      * We also need to reposition the element to the newest position,
      * so remove it from the list for now.
      */
-    if(ht->free_data)
-      free(e->data);
+    if(ht->talloc_data)
+      talloc_free(e->data);
 
     if(e->newer == NULL)
       ht->newest = e->older;
@@ -225,28 +218,29 @@ bool lru_cache_update(lru_cache* ht, const void* index,
 	last->next = e->next;
       e->next = NULL;
 
-      if(ht->free_data)
-	free(e->data);
+      if(ht->talloc_data)
+	talloc_free(e->data);
 
-      tmp_index = realloc(e->index, index_len);
+      tmp_index = talloc_realloc_size(e, e->index, index_len);
       if(tmp_index == NULL)
       {
-	free(e->index);
-	free(e);
+	talloc_free(e);
 	return false;
       }
+      else
+	e->index = tmp_index;
     }
     else
     { /* Brand new element because we have room to spare. */
 
-      e = (lru_cache_element*)malloc(sizeof(lru_cache_element));
+      e = talloc(ht->table, lru_cache_element);
       if(e == NULL)
 	return false;
       
-      e->index = malloc(index_len);
+      e->index = talloc_size(e, index_len);
       if(e->index == NULL)
       {
-	free(e);
+	talloc_free(e);
 	return false;
       }
       
@@ -261,6 +255,8 @@ bool lru_cache_update(lru_cache* ht, const void* index,
     ht->table[hash] = e;
   }
   e->data = data;
+  if(ht->talloc_data)
+    talloc_steal(e, e->data);
 
   /* Finally, let's insert the element to the newest position in the LRU list.*/
   if(ht->newest != NULL)
@@ -333,9 +329,6 @@ bool lru_cache_remove(lru_cache* ht, const void* index,
   if(cur == NULL)
     return false;
 
-  if(ht->free_data)
-    free(cur->data);
-
   /* Detach from list */
   if(cur->newer == NULL)
     ht->newest = cur->older;
@@ -353,8 +346,7 @@ bool lru_cache_remove(lru_cache* ht, const void* index,
   else
     last->next = cur->next;
 
-  free(cur->index);
-  free(cur);
+  talloc_free(cur);
   
   /* Removing entry, decrement counters. */
   ht->num_keys--;
diff --git a/lib/range_list.c b/lib/range_list.c
index e874cff..7e80a72 100644
--- a/lib/range_list.c
+++ b/lib/range_list.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 Timothy D. Morgan
+ * Copyright (C) 2008-2009 Timothy D. Morgan
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -14,11 +14,10 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
  *
- * $Id: range_list.c 122 2008-08-09 20:24:01Z tim $
+ * $Id: range_list.c 150 2009-03-02 02:17:46Z tim $
  */
 
-#include <math.h>
-#include "../include/range_list.h"
+#include "range_list.h"
 
 
 /*******************/
@@ -26,7 +25,7 @@
 /*******************/
 #define RANGE_LIST_ALLOC_SIZE 256
 
-#if 0
+#if 0 /* For debugging */
 #include <stdio.h>
 static void range_list_print(const range_list* rl)
 {
@@ -52,9 +51,8 @@ static bool range_list_insert(range_list* rl, range_list_element* elem, uint32_t
 
   if(rl->size == rl->elem_alloced)
   {
-    tmp = (range_list_element**)realloc(rl->elements, 
-					(rl->elem_alloced+RANGE_LIST_ALLOC_SIZE)
-					* sizeof(range_list_element*));
+    tmp = talloc_realloc(rl, rl->elements, range_list_element*, 
+			 (rl->elem_alloced+RANGE_LIST_ALLOC_SIZE));
     if(tmp == NULL)
       return false;
     rl->elements = tmp;
@@ -124,16 +122,14 @@ range_list* range_list_new()
 {
   range_list* rl;
 
-  rl = (range_list*)malloc(sizeof(range_list));
+  rl = talloc(NULL, range_list);
   if(rl == NULL)
     return NULL;
 
-  rl->elements = (range_list_element**)malloc(sizeof(range_list_element*)
-					      * RANGE_LIST_ALLOC_SIZE);
-
+  rl->elements = talloc_array(rl, range_list_element*, RANGE_LIST_ALLOC_SIZE);
   if(rl->elements == NULL)
   {
-    free(rl);
+    talloc_free(rl);
     return NULL;
   }
 
@@ -146,16 +142,8 @@ range_list* range_list_new()
 
 void range_list_free(range_list* rl)
 {
-  uint32_t i;
-
-  if(rl == NULL)
-    return;
-
-  for(i=0; i < rl->size; i++)
-    free(rl->elements[i]);
-
-  free(rl->elements);
-  free(rl);
+  if(rl != NULL)
+    talloc_free(rl);
 }
 
 
@@ -200,7 +188,7 @@ bool range_list_add(range_list* rl, uint32_t offset, uint32_t length, void* data
      && (offset+length > rl->elements[insert_index+1]->offset))
     return false;
 
-  elem = (range_list_element*)malloc(sizeof(range_list_element));
+  elem = talloc(rl->elements, range_list_element);
   if(elem == NULL)
     return false;
   elem->offset = offset;
@@ -209,7 +197,7 @@ bool range_list_add(range_list* rl, uint32_t offset, uint32_t length, void* data
   
   if(!range_list_insert(rl, elem, insert_index))
   {
-    free(elem);
+    talloc_free(elem);
     return false;
   }
 
@@ -225,7 +213,7 @@ bool range_list_remove(range_list* rl, uint32_t index)
   if(index >= rl->size)
     return false;
 
-  free(rl->elements[index]);
+  talloc_free(rl->elements[index]);
 
   /* Do the shuffle to the left. */
   for(i=index; i < (rl->size-1); i++)
@@ -236,9 +224,8 @@ bool range_list_remove(range_list* rl, uint32_t index)
   /* Try to keep memory usage down */
   if(rl->size + 2 * RANGE_LIST_ALLOC_SIZE  < rl->elem_alloced)
   {
-    tmp = (range_list_element**)realloc(rl->elements, 
-					(rl->elem_alloced-2*RANGE_LIST_ALLOC_SIZE)
-					* sizeof(range_list_element*));
+    tmp = talloc_realloc(rl, rl->elements, range_list_element*, 
+			 (rl->elem_alloced-2*RANGE_LIST_ALLOC_SIZE));
     if(tmp != NULL)
     {
       rl->elements = tmp;
@@ -264,17 +251,20 @@ int32_t range_list_find(const range_list* rl, uint32_t offset)
   uint32_t prev_idx;
   range_list_element* elem;
 
+  if(rl->size == 0)
+    return -1;
+
   if((offset < rl->elements[0]->offset)
      || (offset > rl->elements[rl->size-1]->offset 
 	 + rl->elements[rl->size-1]->length))
-    return -1;
+    return -2;
 
   prev_idx = range_list_find_previous(rl, offset);
   elem = rl->elements[prev_idx];
   if(offset < elem->offset+elem->length)
     return prev_idx;
 
-  return -2;
+  return -3;
 }
 
 
@@ -301,7 +291,7 @@ bool range_list_split_element(range_list* rl, uint32_t index, uint32_t offset)
      || (offset >= cur_elem->offset+cur_elem->length))
     return false;
 
-  new_elem = (range_list_element*)malloc(sizeof(range_list_element));
+  new_elem = talloc(rl->elements, range_list_element);
   if(new_elem == NULL)
     return false;
   
@@ -311,7 +301,7 @@ bool range_list_split_element(range_list* rl, uint32_t index, uint32_t offset)
   
   if(!range_list_insert(rl, new_elem, index+1))
   {
-    free(new_elem);
+    talloc_free(new_elem);
     return false;
   }
 
diff --git a/lib/regfi.c b/lib/regfi.c
index 2736c84..7a6da3c 100644
--- a/lib/regfi.c
+++ b/lib/regfi.c
@@ -2,10 +2,9 @@
  * Branched from Samba project Subversion repository, version #7470:
  *   http://viewcvs.samba.org/cgi-bin/viewcvs.cgi/trunk/source/registry/regfio.c?rev=7470&view=auto
  *
- * Unix SMB/CIFS implementation.
- * Windows NT registry I/O library
+ * Windows NT (and later) registry parsing library
  *
- * Copyright (C) 2005-2008 Timothy D. Morgan
+ * Copyright (C) 2005-2009 Timothy D. Morgan
  * Copyright (C) 2005 Gerald (Jerry) Carter
  *
  * This program is free software; you can redistribute it and/or modify
@@ -21,10 +20,10 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
  *
- * $Id: regfi.c 116 2008-08-03 19:34:27Z tim $
+ * $Id: regfi.c 155 2009-06-03 23:45:58Z tim $
  */
 
-#include "../include/regfi.h"
+#include "regfi.h"
 
 
 /* Registry types mapping */
@@ -34,6 +33,74 @@ static const char* regfi_type_names[] =
    "MULTI_SZ", "RSRC_LIST", "RSRC_DESC", "RSRC_REQ_LIST", "QWORD"};
 
 
+
+/******************************************************************************
+ ******************************************************************************/
+void regfi_add_message(REGFI_FILE* file, uint16 msg_type, const char* fmt, ...)
+{
+  /* XXX: This function is not particularly efficient,
+   *      but then it is mostly used during errors. 
+   */
+  uint32 buf_size, buf_used;
+  char* new_msg;
+  va_list args;
+
+  if((file->msg_mask & msg_type) != 0)
+  {
+    if(file->last_message == NULL)
+      buf_used = 0;
+    else
+      buf_used = strlen(file->last_message);
+    
+    buf_size = buf_used+strlen(fmt)+160;
+    new_msg = realloc(file->last_message, buf_size);
+    if(new_msg == NULL)
+      /* XXX: should we report this? */
+      return;
+
+    switch (msg_type)
+    {
+    case REGFI_MSG_INFO:
+      strcpy(new_msg+buf_used, "INFO: ");
+      buf_used += 6;
+      break;
+    case REGFI_MSG_WARN:
+      strcpy(new_msg+buf_used, "WARN: ");
+      buf_used += 6;
+      break;
+    case REGFI_MSG_ERROR:
+      strcpy(new_msg+buf_used, "ERROR: ");
+      buf_used += 7;
+      break;
+    }
+
+    va_start(args, fmt);
+    vsnprintf(new_msg+buf_used, buf_size-buf_used, fmt, args);
+    va_end(args);
+    strncat(new_msg, "\n", buf_size-1);
+    
+    file->last_message = new_msg;
+  }
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+char* regfi_get_messages(REGFI_FILE* file)
+{
+  char* ret_val = file->last_message;
+  file->last_message = NULL;
+
+  return ret_val;
+}
+
+
+void regfi_set_message_mask(REGFI_FILE* file, uint16 mask)
+{
+  file->msg_mask = mask;
+}
+
+
 /* Returns NULL on error */
 const char* regfi_type_val2str(unsigned int val)
 {
@@ -66,7 +133,7 @@ int regfi_type_str2val(const char* str)
 }
 
 
-/* Security descriptor parsing functions  */
+/* Security descriptor formatting functions  */
 
 const char* regfi_ace_type2str(uint8 type)
 {
@@ -133,15 +200,6 @@ char* regfi_ace_flags2str(uint8 flags)
   if(fo != ret_val)
     fo[-1] = '\0';
 
-  /* XXX: what was this old VI flag for??
-     XXX: Is this check right?  0xF == 1|2|4|8, which makes it redundant...
-  if (flags == 0xF) {
-    if (some) strcat(flg_output, " ");
-    some = 1;
-    strcat(flg_output, "VI");
-  }
-  */
-
   return ret_val;
 }
 
@@ -225,9 +283,9 @@ char* regfi_ace_perms2str(uint32 perms)
 }
 
 
-char* regfi_sid2str(DOM_SID* sid)
+char* regfi_sid2str(WINSEC_DOM_SID* sid)
 {
-  uint32 i, size = MAXSUBAUTHS*11 + 24;
+  uint32 i, size = WINSEC_MAX_SUBAUTHS*11 + 24;
   uint32 left = size;
   uint8 comps = sid->num_auths;
   char* ret_val = malloc(size);
@@ -235,8 +293,8 @@ char* regfi_sid2str(DOM_SID* sid)
   if(ret_val == NULL)
     return NULL;
 
-  if(comps > MAXSUBAUTHS)
-    comps = MAXSUBAUTHS;
+  if(comps > WINSEC_MAX_SUBAUTHS)
+    comps = WINSEC_MAX_SUBAUTHS;
 
   left -= sprintf(ret_val, "S-%u-%u", sid->sid_rev_num, sid->id_auth[5]);
 
@@ -247,7 +305,7 @@ char* regfi_sid2str(DOM_SID* sid)
 }
 
 
-char* regfi_get_acl(SEC_ACL* acl)
+char* regfi_get_acl(WINSEC_ACL* acl)
 {
   uint32 i, extra, size = 0;
   const char* type_str;
@@ -262,32 +320,33 @@ char* regfi_get_acl(SEC_ACL* acl)
 
   for (i = 0; i < acl->num_aces && !failed; i++)
   {
-    sid_str = regfi_sid2str(&acl->ace[i].trustee);
-    type_str = regfi_ace_type2str(acl->ace[i].type);
-    perms_str = regfi_ace_perms2str(acl->ace[i].info.mask);
-    flags_str = regfi_ace_flags2str(acl->ace[i].flags);
+    sid_str = regfi_sid2str(acl->aces[i]->trustee);
+    type_str = regfi_ace_type2str(acl->aces[i]->type);
+    perms_str = regfi_ace_perms2str(acl->aces[i]->access_mask);
+    flags_str = regfi_ace_flags2str(acl->aces[i]->flags);
     
     if(flags_str != NULL && perms_str != NULL 
        && type_str != NULL && sid_str != NULL)
     {
       /* XXX: this is slow */
       extra = strlen(sid_str) + strlen(type_str) 
-	+ strlen(perms_str) + strlen(flags_str)+5;
+	+ strlen(perms_str) + strlen(flags_str) + 5;
       tmp_val = realloc(ret_val, size+extra);
 
       if(tmp_val == NULL)
       {
 	free(ret_val);
+	ret_val = NULL;
 	failed = true;
       }
       else
       {
 	ret_val = tmp_val;
-	size += snprintf(ret_val+size, extra, "%s%s%c%s%c%s%c%s",
-			 ace_delim,sid_str,
-			 field_delim,type_str,
-			 field_delim,perms_str,
-			 field_delim,flags_str);
+	size += sprintf(ret_val+size, "%s%s%c%s%c%s%c%s",
+			ace_delim,sid_str,
+			field_delim,type_str,
+			field_delim,perms_str,
+			field_delim,flags_str);
 	ace_delim = "|";
       }
     }
@@ -306,7 +365,7 @@ char* regfi_get_acl(SEC_ACL* acl)
 }
 
 
-char* regfi_get_sacl(SEC_DESC *sec_desc)
+char* regfi_get_sacl(WINSEC_DESC *sec_desc)
 {
   if (sec_desc->sacl)
     return regfi_get_acl(sec_desc->sacl);
@@ -315,7 +374,7 @@ char* regfi_get_sacl(SEC_DESC *sec_desc)
 }
 
 
-char* regfi_get_dacl(SEC_DESC *sec_desc)
+char* regfi_get_dacl(WINSEC_DESC *sec_desc)
 {
   if (sec_desc->dacl)
     return regfi_get_acl(sec_desc->dacl);
@@ -324,13 +383,13 @@ char* regfi_get_dacl(SEC_DESC *sec_desc)
 }
 
 
-char* regfi_get_owner(SEC_DESC *sec_desc)
+char* regfi_get_owner(WINSEC_DESC *sec_desc)
 {
   return regfi_sid2str(sec_desc->owner_sid);
 }
 
 
-char* regfi_get_group(SEC_DESC *sec_desc)
+char* regfi_get_group(WINSEC_DESC *sec_desc)
 {
   return regfi_sid2str(sec_desc->grp_sid);
 }
@@ -415,13 +474,13 @@ bool regfi_parse_cell(int fd, uint32 offset, uint8* hdr, uint32 hdr_len,
  * Given an offset and an hbin, is the offset within that hbin?
  * The offset is a virtual file offset.
  *******************************************************************/
-static bool regfi_offset_in_hbin(REGF_HBIN* hbin, uint32 offset)
+static bool regfi_offset_in_hbin(const REGFI_HBIN* hbin, uint32 voffset)
 {
   if(!hbin)
     return false;
 
-  if((offset > hbin->first_hbin_off) 
-     && (offset < (hbin->first_hbin_off + hbin->block_size)))
+  if((voffset > hbin->first_hbin_off) 
+     && (voffset < (hbin->first_hbin_off + hbin->block_size)))
     return true;
 		
   return false;
@@ -430,141 +489,296 @@ static bool regfi_offset_in_hbin(REGF_HBIN* hbin, uint32 offset)
 
 
 /*******************************************************************
- * Given a virtual offset, and receive the correpsonding HBIN 
+ * Provide a virtual offset and receive the correpsonding HBIN 
  * block for it.  NULL if one doesn't exist.
  *******************************************************************/
-REGF_HBIN* regfi_lookup_hbin(REGF_FILE* file, uint32 offset)
+const REGFI_HBIN* regfi_lookup_hbin(REGFI_FILE* file, uint32 voffset)
 {
-  return (REGF_HBIN*)range_list_find_data(file->hbins, offset+REGF_BLOCKSIZE);
+  return (const REGFI_HBIN*)range_list_find_data(file->hbins, 
+						 voffset+REGFI_REGF_SIZE);
 }
 
 
 
-/*******************************************************************
- *******************************************************************/
-REGF_HASH_LIST* regfi_load_hashlist(REGF_FILE* file, uint32 offset, 
-				    uint32 num_keys, uint32 max_size, 
-				    bool strict)
+/******************************************************************************
+ ******************************************************************************/
+REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32 offset, 
+					 uint32 num_keys, uint32 max_size, 
+					 bool strict)
 {
-  REGF_HASH_LIST* ret_val;
-  uint32 i, cell_length, length;
-  uint8* hashes;
-  uint8 buf[REGFI_HASH_LIST_MIN_LENGTH];
-  bool unalloc;
+  REGFI_SUBKEY_LIST* ret_val;
 
-  if(!regfi_parse_cell(file->fd, offset, buf, REGFI_HASH_LIST_MIN_LENGTH, 
-		       &cell_length, &unalloc))
+  ret_val = regfi_load_subkeylist_aux(file, offset, max_size, strict, 
+				      REGFI_MAX_SUBKEY_DEPTH);
+  if(ret_val == NULL)
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Failed to load subkey list at"
+		      " offset 0x%.8X.", offset);
+    return NULL;
+  }
+
+  if(num_keys != ret_val->num_keys)
+  {
+    /*  Not sure which should be authoritative, the number from the 
+     *  NK record, or the number in the subkey list.  Just emit a warning for
+     *  now if they don't match.
+     */
+    regfi_add_message(file, REGFI_MSG_WARN, "Number of subkeys listed in parent"
+		      " (%d) did not match number found in subkey list/tree (%d)"
+		      " while parsing subkey list/tree at offset 0x%.8X.", 
+		      num_keys, ret_val->num_keys, offset);
+  }
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+REGFI_SUBKEY_LIST* regfi_load_subkeylist_aux(REGFI_FILE* file, uint32 offset, 
+					     uint32 max_size, bool strict,
+					     uint8 depth_left)
+{
+  REGFI_SUBKEY_LIST* ret_val;
+  REGFI_SUBKEY_LIST** sublists;
+  const REGFI_HBIN* sublist_hbin;
+  uint32 i, num_sublists, off, max_length;
+
+  if(depth_left == 0)
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Maximum depth reached"
+		      " while parsing subkey list/tree at offset 0x%.8X.", 
+		      offset);
     return NULL;
+  }
 
-  ret_val = (REGF_HASH_LIST*)zalloc(sizeof(REGF_HASH_LIST));
+  ret_val = regfi_parse_subkeylist(file, offset, max_size, strict);
   if(ret_val == NULL)
     return NULL;
 
-  ret_val->offset = offset;
+  if(ret_val->recursive_type)
+  {
+    num_sublists = ret_val->num_children;
+    sublists = (REGFI_SUBKEY_LIST**)malloc(num_sublists 
+					   * sizeof(REGFI_SUBKEY_LIST*));
+    for(i=0; i < num_sublists; i++)
+    {
+      off = ret_val->elements[i].offset + REGFI_REGF_SIZE;
+      sublist_hbin = regfi_lookup_hbin(file, ret_val->elements[i].offset);
+      if(sublist_hbin == NULL)
+	sublists[i] = NULL;
+      else
+      {
+	max_length = sublist_hbin->block_size + sublist_hbin->file_off - off;
+	sublists[i] = regfi_load_subkeylist_aux(file, off, max_length, strict,
+						depth_left-1);
+      }
+    }
+    talloc_free(ret_val);
+
+    return regfi_merge_subkeylists(num_sublists, sublists, strict);
+  }
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32 offset, 
+					  uint32 max_size, bool strict)
+{
+  REGFI_SUBKEY_LIST* ret_val;
+  uint32 i, cell_length, length, elem_size, read_len;
+  uint8* elements = NULL;
+  uint8 buf[REGFI_SUBKEY_LIST_MIN_LEN];
+  bool unalloc;
+  bool recursive_type;
+
+  if(!regfi_parse_cell(file->fd, offset, buf, REGFI_SUBKEY_LIST_MIN_LEN, 
+		       &cell_length, &unalloc))
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while "
+		      "parsing subkey-list at offset 0x%.8X.", offset);
+    return NULL;
+  }
+
   if(cell_length > max_size)
   {
+    regfi_add_message(file, REGFI_MSG_WARN, "Cell size longer than max_size"
+		      " while parsing subkey-list at offset 0x%.8X.", offset);
     if(strict)
       return NULL;
     cell_length = max_size & 0xFFFFFFF8;
   }
-  ret_val->cell_size = cell_length;
 
-  if((buf[0] != 'l' || buf[1] != 'f') && (buf[0] != 'l' || buf[1] != 'h')
-     && (buf[0] != 'r' || buf[1] != 'i'))
+  recursive_type = false;
+  if(buf[0] == 'r' && buf[1] == 'i')
   {
-    /*printf("DEBUG: lf->header=%c%c\n", buf[0], buf[1]);*/
-    free(ret_val);
-    return NULL;
+    recursive_type = true;
+    elem_size = sizeof(uint32);
   }
-
-  if(buf[0] == 'r' && buf[1] == 'i')
+  else if(buf[0] == 'l' && buf[1] == 'i')
+    elem_size = sizeof(uint32);
+  else if((buf[0] == 'l') && (buf[1] == 'f' || buf[1] == 'h'))
+    elem_size = sizeof(REGFI_SUBKEY_LIST_ELEM);
+  else
   {
-    fprintf(stderr, "WARNING: ignoring encountered \"ri\" record.\n");
-    free(ret_val);
+    regfi_add_message(file, REGFI_MSG_ERROR, "Unknown magic number"
+		      " (0x%.2X, 0x%.2X) encountered while parsing"
+		      " subkey-list at offset 0x%.8X.", buf[0], buf[1], offset);
     return NULL;
   }
 
+  ret_val = talloc(NULL, REGFI_SUBKEY_LIST);
+  if(ret_val == NULL)
+    return NULL;
+
+  ret_val->offset = offset;
+  ret_val->cell_size = cell_length;
   ret_val->magic[0] = buf[0];
   ret_val->magic[1] = buf[1];
+  ret_val->recursive_type = recursive_type;
+  ret_val->num_children = SVAL(buf, 0x2);
 
-  ret_val->num_keys = SVAL(buf, 0x2);
-  if(num_keys != ret_val->num_keys)
+  if(!recursive_type)
+    ret_val->num_keys = ret_val->num_children;
+
+  length = elem_size*ret_val->num_children;
+  if(cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32) < length)
   {
+    regfi_add_message(file, REGFI_MSG_WARN, "Number of elements too large for"
+		      " cell while parsing subkey-list at offset 0x%.8X.", 
+		      offset);
     if(strict)
-    {
-      free(ret_val);
-      return NULL;
-    }
-    /* XXX: Not sure which should be authoritative, the number from the 
-     *      NK record, or the number in the hash list.  Go with the larger
-     *      of the two to ensure all keys are found.  Note the length checks
-     *      on the cell later ensure that there won't be any critical errors.
-     */
-    if(num_keys < ret_val->num_keys)
-      num_keys = ret_val->num_keys;
-    else
-      ret_val->num_keys = num_keys;
+      goto fail;
+    length = cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32);
   }
 
-  if(cell_length - REGFI_HASH_LIST_MIN_LENGTH - sizeof(uint32) 
-     < ret_val->num_keys*sizeof(REGF_HASH_LIST_ELEM))
-    return NULL;
+  ret_val->elements = talloc_array(ret_val, REGFI_SUBKEY_LIST_ELEM, 
+				   ret_val->num_children);
+  if(ret_val->elements == NULL)
+    goto fail;
 
-  length = sizeof(REGF_HASH_LIST_ELEM)*ret_val->num_keys;
-  ret_val->hashes = (REGF_HASH_LIST_ELEM*)zalloc(length);
-  if(ret_val->hashes == NULL)
+  elements = (uint8*)malloc(length);
+  if(elements == NULL)
+    goto fail;
+
+  read_len = length;
+  if(regfi_read(file->fd, elements, &read_len) != 0 || read_len != length)
+    goto fail;
+
+  if(elem_size == sizeof(uint32))
   {
-    free(ret_val);
-    return NULL;
+    for (i=0; i < ret_val->num_children; i++)
+    {
+      ret_val->elements[i].offset = IVAL(elements, i*elem_size);
+      ret_val->elements[i].hash = 0;
+    }
   }
-
-  hashes = (uint8*)zalloc(length);
-  if(hashes == NULL)
+  else
   {
-    free(ret_val->hashes);
-    free(ret_val);
-    return NULL;
+    for (i=0; i < ret_val->num_children; i++)
+    {
+      ret_val->elements[i].offset = IVAL(elements, i*elem_size);
+      ret_val->elements[i].hash = IVAL(elements, i*elem_size+4);
+    }
   }
+  free(elements);
 
-  if(regfi_read(file->fd, hashes, &length) != 0
-     || length != sizeof(REGF_HASH_LIST_ELEM)*ret_val->num_keys)
-  {
-    free(ret_val->hashes);
-    free(ret_val);
+  return ret_val;
+
+ fail:
+  if(elements != NULL)
+    free(elements);
+  talloc_free(ret_val);
+  return NULL;
+}
+
+
+/*******************************************************************
+ *******************************************************************/
+REGFI_SUBKEY_LIST* regfi_merge_subkeylists(uint16 num_lists, 
+					   REGFI_SUBKEY_LIST** lists,
+					   bool strict)
+{
+  uint32 i,j,k;
+  REGFI_SUBKEY_LIST* ret_val;
+
+  if(lists == NULL)
     return NULL;
+  ret_val = talloc(NULL, REGFI_SUBKEY_LIST);
+
+  if(ret_val == NULL)
+    return NULL;
+  
+  /* Obtain total number of elements */
+  ret_val->num_keys = 0;
+  for(i=0; i < num_lists; i++)
+  {
+    if(lists[i] != NULL)
+      ret_val->num_keys += lists[i]->num_children;
   }
+  ret_val->num_children = ret_val->num_keys;
 
-  for (i=0; i < ret_val->num_keys; i++)
+  if(ret_val->num_keys > 0)
   {
-    ret_val->hashes[i].nk_off = IVAL(hashes, i*sizeof(REGF_HASH_LIST_ELEM));
-    ret_val->hashes[i].hash = IVAL(hashes, i*sizeof(REGF_HASH_LIST_ELEM)+4);
+    ret_val->elements = talloc_array(ret_val, REGFI_SUBKEY_LIST_ELEM,
+				     ret_val->num_keys);
+    k=0;
+
+    if(ret_val->elements != NULL)
+    {
+      for(i=0; i < num_lists; i++)
+      {
+	if(lists[i] != NULL)
+	{
+	  for(j=0; j < lists[i]->num_keys; j++)
+	  {
+	    ret_val->elements[k].hash = lists[i]->elements[j].hash;
+	    ret_val->elements[k++].offset = lists[i]->elements[j].offset;
+	  }
+	}
+      }
+    }
   }
-  free(hashes);
+  
+  for(i=0; i < num_lists; i++)
+    regfi_subkeylist_free(lists[i]);
+  free(lists);
 
   return ret_val;
 }
 
 
-
-/*******************************************************************
- *******************************************************************/
-REGF_SK_REC* regfi_parse_sk(REGF_FILE* file, uint32 offset, uint32 max_size, bool strict)
+/******************************************************************************
+ *
+ ******************************************************************************/
+REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32 offset, uint32 max_size, 
+			     bool strict)
 {
-  REGF_SK_REC* ret_val;
+  REGFI_SK_REC* ret_val;
+  uint8* sec_desc_buf = NULL;
   uint32 cell_length, length;
-  prs_struct ps;
   uint8 sk_header[REGFI_SK_MIN_LENGTH];
   bool unalloc = false;
 
-
   if(!regfi_parse_cell(file->fd, offset, sk_header, REGFI_SK_MIN_LENGTH,
 		       &cell_length, &unalloc))
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse SK record cell"
+		      " at offset 0x%.8X.", offset);
     return NULL;
+  }
    
   if(sk_header[0] != 's' || sk_header[1] != 'k')
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Magic number mismatch in parsing"
+		      " SK record at offset 0x%.8X.", offset);
     return NULL;
-  
-  ret_val = (REGF_SK_REC*)zalloc(sizeof(REGF_SK_REC));
+  }
+
+  ret_val = talloc(NULL, REGFI_SK_REC);
   if(ret_val == NULL)
     return NULL;
 
@@ -579,98 +793,143 @@ REGF_SK_REC* regfi_parse_sk(REGF_FILE* file, uint32 offset, uint32 max_size, boo
   if((ret_val->cell_size < REGFI_SK_MIN_LENGTH) 
      || (strict && ret_val->cell_size != (ret_val->cell_size & 0xFFFFFFF8)))
   {
-    free(ret_val);
-    return NULL;
+    regfi_add_message(file, REGFI_MSG_WARN, "Invalid cell size found while"
+		      " parsing SK record at offset 0x%.8X.", offset);
+    goto fail;
   }
 
   ret_val->magic[0] = sk_header[0];
   ret_val->magic[1] = sk_header[1];
 
-  /* XXX: Can additional validation be added here? */
   ret_val->unknown_tag = SVAL(sk_header, 0x2);
   ret_val->prev_sk_off = IVAL(sk_header, 0x4);
   ret_val->next_sk_off = IVAL(sk_header, 0x8);
   ret_val->ref_count = IVAL(sk_header, 0xC);
   ret_val->desc_size = IVAL(sk_header, 0x10);
 
-  if(ret_val->desc_size + REGFI_SK_MIN_LENGTH > ret_val->cell_size)
+  if(ret_val->prev_sk_off != (ret_val->prev_sk_off & 0xFFFFFFF8) 
+     || ret_val->next_sk_off != (ret_val->next_sk_off & 0xFFFFFFF8))
   {
-    free(ret_val);
-    return NULL;
+    regfi_add_message(file, REGFI_MSG_WARN, "SK record's next/previous offsets"
+		      " are not a multiple of 8 while parsing SK record at"
+		      " offset 0x%.8X.", offset);
+    goto fail;
   }
 
-  /* XXX: need to get rid of this, but currently the security descriptor
-   * code depends on the ps structure.
-   */
-  if(!prs_init(&ps, ret_val->desc_size, NULL, UNMARSHALL))
+  if(ret_val->desc_size + REGFI_SK_MIN_LENGTH > ret_val->cell_size)
   {
-    free(ret_val);
-    return NULL;
+    regfi_add_message(file, REGFI_MSG_WARN, "Security descriptor too large for"
+		      " cell while parsing SK record at offset 0x%.8X.", 
+		      offset);
+    goto fail;
   }
 
+  sec_desc_buf = (uint8*)malloc(ret_val->desc_size);
+  if(sec_desc_buf == NULL)
+    goto fail;
+
   length = ret_val->desc_size;
-  if(regfi_read(file->fd, (uint8*)ps.data_p, &length) != 0 
+  if(regfi_read(file->fd, sec_desc_buf, &length) != 0 
      || length != ret_val->desc_size)
   {
-    free(ret_val);
-    return NULL;
+    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read security"
+		      " descriptor while parsing SK record at offset 0x%.8X.",
+		      offset);
+    goto fail;
   }
 
-  if (!sec_io_desc("sec_desc", &ret_val->sec_desc, &ps, 0))
+  if(!(ret_val->sec_desc = winsec_parse_desc(ret_val, sec_desc_buf, 
+						   ret_val->desc_size)))
   {
-    free(ret_val);
-    return NULL;
+    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to parse security"
+		      " descriptor while parsing SK record at offset 0x%.8X.",
+		      offset);
+    goto fail;
   }
 
-  free(ps.data_p);
-
+  free(sec_desc_buf);
   return ret_val;
+
+ fail:
+  if(sec_desc_buf != NULL)
+    free(sec_desc_buf);
+  talloc_free(ret_val);
+  return NULL;
 }
 
 
-uint32* regfi_parse_valuelist(REGF_FILE* file, uint32 offset, 
-			      uint32 num_values, bool strict)
+REGFI_VALUE_LIST* regfi_parse_valuelist(REGFI_FILE* file, uint32 offset, 
+					uint32 num_values, bool strict)
 {
-  uint32* ret_val;
+  REGFI_VALUE_LIST* ret_val;
   uint32 i, cell_length, length, read_len;
   bool unalloc;
 
   if(!regfi_parse_cell(file->fd, offset, NULL, 0, &cell_length, &unalloc))
+  {
+    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read cell header"
+		      " while parsing value list at offset 0x%.8X.", offset);
     return NULL;
+  }
 
   if(cell_length != (cell_length & 0xFFFFFFF8))
   {
+    regfi_add_message(file, REGFI_MSG_WARN, "Cell length not a multiple of 8"
+		      " while parsing value list at offset 0x%.8X.", offset);
     if(strict)
       return NULL;
     cell_length = cell_length & 0xFFFFFFF8;
   }
+
   if((num_values * sizeof(uint32)) > cell_length-sizeof(uint32))
-    return NULL;
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Too many values found"
+		      " while parsing value list at offset 0x%.8X.", offset);
+    if(strict)
+      return NULL;
+    num_values = cell_length/sizeof(uint32) - sizeof(uint32);
+  }
 
   read_len = num_values*sizeof(uint32);
-  ret_val = (uint32*)malloc(read_len);
+  ret_val = talloc(NULL, REGFI_VALUE_LIST);
   if(ret_val == NULL)
     return NULL;
 
+  ret_val->elements = (REGFI_VALUE_LIST_ELEM*)talloc_size(ret_val, read_len);
+  if(ret_val->elements == NULL)
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+  ret_val->num_values = num_values;
+
   length = read_len;
-  if((regfi_read(file->fd, (uint8*)ret_val, &length) != 0) || length != read_len)
+  if((regfi_read(file->fd, (uint8*)ret_val->elements, &length) != 0) 
+     || length != read_len)
   {
-    free(ret_val);
+    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read value pointers"
+		      " while parsing value list at offset 0x%.8X.", offset);
+    talloc_free(ret_val);
     return NULL;
   }
   
   for(i=0; i < num_values; i++)
   {
     /* Fix endianness */
-    ret_val[i] = IVAL(&ret_val[i], 0);
+    ret_val->elements[i] = IVAL(&ret_val->elements[i], 0);
 
     /* Validate the first num_values values to ensure they make sense */
     if(strict)
     {
-      if((ret_val[i] + REGF_BLOCKSIZE > file->file_length)
-	 || ((ret_val[i] & 0xFFFFFFF8) != ret_val[i]))
+      /* XXX: Need to revisit this file length check when we start dealing 
+       *      with partial files. */
+      if((ret_val->elements[i] + REGFI_REGF_SIZE > file->file_length)
+	 || ((ret_val->elements[i] & 0xFFFFFFF8) != ret_val->elements[i]))
       {
-	free(ret_val);
+	regfi_add_message(file, REGFI_MSG_WARN, "Invalid value pointer"
+			  " (0x%.8X) found while parsing value list at offset"
+			  " 0x%.8X.", ret_val->elements[i], offset);
+	talloc_free(ret_val);
 	return NULL;
       }
     }
@@ -682,91 +941,125 @@ uint32* regfi_parse_valuelist(REGF_FILE* file, uint32 offset,
 
 
 /******************************************************************************
- * If !strict, the list may contain NULLs, VK records may point to NULL.
  ******************************************************************************/
-REGF_VK_REC** regfi_load_valuelist(REGF_FILE* file, uint32 offset, 
-				   uint32 num_values, uint32 max_size, 
-				   bool strict)
+REGFI_VK_REC* regfi_load_value(REGFI_FILE* file, uint32 offset, bool strict)
 {
-  REGF_VK_REC** ret_val;
-  REGF_HBIN* hbin;
-  uint32 i, vk_offset, vk_max_length, usable_num_values;
-  uint32* voffsets;
+  REGFI_VK_REC* ret_val = NULL;
+  const REGFI_HBIN* hbin;
+  uint32 data_offset, data_maxsize;
+  REGFI_BUFFER data;
 
-  if((num_values+1) * sizeof(uint32) > max_size)
-  {
-    if(strict)
-      return NULL;
-    usable_num_values = max_size/sizeof(uint32) - sizeof(uint32);
-  }
-  else
-    usable_num_values = num_values;
-
-  voffsets = regfi_parse_valuelist(file, offset, usable_num_values, strict);
-  if(voffsets == NULL)
+  hbin = regfi_lookup_hbin(file, offset - REGFI_REGF_SIZE);
+  if(!hbin)
     return NULL;
+  
+  ret_val = regfi_parse_vk(file, offset, 
+			   hbin->block_size + hbin->file_off - offset, strict);
 
-  ret_val = (REGF_VK_REC**)zalloc(sizeof(REGF_VK_REC*) * num_values);
   if(ret_val == NULL)
-  {
-    free(voffsets);
     return NULL;
-  }
-  
-  for(i=0; i < usable_num_values; i++)
+
+  if(ret_val->data_size == 0)
+    ret_val->data = NULL;
+  else
   {
-    hbin = regfi_lookup_hbin(file, voffsets[i]);
-    if(!hbin)
+    if(ret_val->data_in_offset)
     {
-      free(voffsets);
-      free(ret_val);
-      return NULL;
+      data = regfi_load_data(file, ret_val->type, ret_val->data_off,
+			      ret_val->data_size, 4,
+			      ret_val->data_in_offset, strict);
+      ret_val->data = data.buf;
+      ret_val->data_size = data.len;
     }
-    
-    vk_offset =  voffsets[i] + REGF_BLOCKSIZE;
-    vk_max_length = hbin->block_size - vk_offset + sizeof(uint32);
-    ret_val[i] = regfi_parse_vk(file, vk_offset, vk_max_length, strict);
-    if(ret_val[i] == NULL)
-    { /* If we're being strict, throw out the whole list.
-       * Otherwise, let it be NULL.
-       */
-      if(strict)
+    else
+    {
+      hbin = regfi_lookup_hbin(file, ret_val->data_off);
+      if(hbin)
       {
-	free(voffsets);
-	free(ret_val);
-	return NULL;
+	data_offset = ret_val->data_off+REGFI_REGF_SIZE;
+	data_maxsize = hbin->block_size + hbin->file_off - data_offset;
+	data = regfi_load_data(file, ret_val->type, data_offset, 
+				ret_val->data_size, data_maxsize, 
+				ret_val->data_in_offset, strict);
+	ret_val->data = data.buf;
+	ret_val->data_size = data.len;
+
+      }
+      else
+      {
+	regfi_add_message(file, REGFI_MSG_WARN, "Could not find HBIN for data"
+			  " while parsing VK record at offset 0x%.8X.", 
+			  ret_val->offset);
+	ret_val->data = NULL;
       }
     }
+
+    if(ret_val->data == NULL)
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Could not parse data record"
+			" while parsing VK record at offset 0x%.8X.",
+			ret_val->offset, ret_val->valuename);
+    }
+    else
+      talloc_steal(ret_val, ret_val->data);
   }
 
-  free(voffsets);
   return ret_val;
 }
 
 
+/******************************************************************************
+ * If !strict, the list may contain NULLs, VK records may point to NULL.
+ ******************************************************************************/
+REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32 offset, 
+				       uint32 num_values, uint32 max_size,
+				       bool strict)
+{
+  uint32 usable_num_values;
 
-/*******************************************************************
- * XXX: Need to add full key caching using a 
- *      custom cache structure.
- *******************************************************************/
-REGF_NK_REC* regfi_load_key(REGF_FILE* file, uint32 offset, bool strict)
+  if((num_values+1) * sizeof(uint32) > max_size)
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Number of values indicated by"
+		      " parent key (%d) would cause cell to straddle HBIN"
+		      " boundary while loading value list at offset"
+		      " 0x%.8X.", num_values, offset);
+    if(strict)
+      return NULL;
+    usable_num_values = max_size/sizeof(uint32) - sizeof(uint32);
+  }
+  else
+    usable_num_values = num_values;
+
+  return regfi_parse_valuelist(file, offset, usable_num_values, strict);
+}
+
+
+
+/******************************************************************************
+ *
+ ******************************************************************************/
+REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32 offset, bool strict)
 {
-  REGF_HBIN* hbin;
-  REGF_HBIN* sub_hbin;
-  REGF_NK_REC* nk;
+  const REGFI_HBIN* hbin;
+  const REGFI_HBIN* sub_hbin;
+  REGFI_NK_REC* nk;
   uint32 max_length, off;
 
-  hbin = regfi_lookup_hbin(file, offset-REGF_BLOCKSIZE);
+  hbin = regfi_lookup_hbin(file, offset-REGFI_REGF_SIZE);
   if (hbin == NULL) 
     return NULL;
 
   /* get the initial nk record */
   max_length = hbin->block_size + hbin->file_off - offset;
-  if ((nk = regfi_parse_nk(file, offset, max_length, true)) == NULL)
+  if((nk = regfi_parse_nk(file, offset, max_length, true)) == NULL)
+  {
+    regfi_add_message(file, REGFI_MSG_ERROR, "Could not load NK record at"
+		      " offset 0x%.8X.", offset);
     return NULL;
+  }
 
-  /* fill in values */
-  if(nk->num_values && (nk->values_off!=REGF_OFFSET_NONE)) 
+  /* get value list */
+  if(nk->num_values && (nk->values_off!=REGFI_OFFSET_NONE)) 
   {
     sub_hbin = hbin;
     if(!regfi_offset_in_hbin(hbin, nk->values_off)) 
@@ -776,38 +1069,45 @@ REGF_NK_REC* regfi_load_key(REGF_FILE* file, uint32 offset, bool strict)
     {
       if(strict)
       {
-	free(nk);
+	regfi_free_key(nk);
 	return NULL;
       }
       else
 	nk->values = NULL;
+
     }
     else
     {
-      off = nk->values_off + REGF_BLOCKSIZE;
+      off = nk->values_off + REGFI_REGF_SIZE;
       max_length = sub_hbin->block_size + sub_hbin->file_off - off;
       nk->values = regfi_load_valuelist(file, off, nk->num_values, max_length, 
 					true);
-      if(strict && nk->values == NULL)
+      if(nk->values == NULL)
       {
-	free(nk);
-	return NULL;
+	regfi_add_message(file, REGFI_MSG_WARN, "Could not load value list"
+			  " for NK record at offset 0x%.8X.", offset);
+	if(strict)
+	{
+	  regfi_free_key(nk);
+	  return NULL;
+	}
       }
+      talloc_steal(nk, nk->values);
     }
   }
 
-  /* now get subkeys */
-  if(nk->num_subkeys && (nk->subkeys_off != REGF_OFFSET_NONE)) 
+  /* now get subkey list */
+  if(nk->num_subkeys && (nk->subkeys_off != REGFI_OFFSET_NONE)) 
   {
     sub_hbin = hbin;
     if(!regfi_offset_in_hbin(hbin, nk->subkeys_off))
       sub_hbin = regfi_lookup_hbin(file, nk->subkeys_off);
 
-    if (sub_hbin == NULL) 
+    if(sub_hbin == NULL) 
     {
       if(strict)
       {
-	regfi_key_free(nk);
+	regfi_free_key(nk);
 	return NULL;
       }
       else
@@ -815,15 +1115,18 @@ REGF_NK_REC* regfi_load_key(REGF_FILE* file, uint32 offset, bool strict)
     }
     else
     {
-      off = nk->subkeys_off + REGF_BLOCKSIZE;
+      off = nk->subkeys_off + REGFI_REGF_SIZE;
       max_length = sub_hbin->block_size + sub_hbin->file_off - off;
-      nk->subkeys = regfi_load_hashlist(file, off, nk->num_subkeys, 
-					max_length, true);
+      nk->subkeys = regfi_load_subkeylist(file, off, nk->num_subkeys,
+					  max_length, true);
+
       if(nk->subkeys == NULL)
       {
-	/* XXX: Temporary hack to get around 'ri' records */
+	regfi_add_message(file, REGFI_MSG_WARN, "Could not load subkey list"
+			  " while parsing NK record at offset 0x%.8X.", offset);
 	nk->num_subkeys = 0;
       }
+      talloc_steal(nk, nk->subkeys);
     }
   }
 
@@ -832,15 +1135,56 @@ REGF_NK_REC* regfi_load_key(REGF_FILE* file, uint32 offset, bool strict)
 
 
 /******************************************************************************
+ ******************************************************************************/
+const REGFI_SK_REC* regfi_load_sk(REGFI_FILE* file, uint32 offset, bool strict)
+{
+  REGFI_SK_REC* ret_val = NULL;
+  const REGFI_HBIN* hbin;
+  uint32 max_length;
+  void* failure_ptr = NULL;
+  
+  /* First look if we have already parsed it */
+  ret_val = (REGFI_SK_REC*)lru_cache_find(file->sk_cache, &offset, 4);
+
+  /* Bail out if we have previously cached a parse failure at this offset. */
+  if(ret_val == (void*)REGFI_OFFSET_NONE)
+    return NULL;
+
+  if(ret_val == NULL)
+  {
+    hbin = regfi_lookup_hbin(file, offset - REGFI_REGF_SIZE);
+    if(hbin == NULL)
+      return NULL;
+
+    max_length = hbin->block_size + hbin->file_off - offset;
+    ret_val = regfi_parse_sk(file, offset, max_length, strict);
+    if(ret_val == NULL)
+    { /* Cache the parse failure and bail out. */
+      failure_ptr = talloc(NULL, uint32_t);
+      if(failure_ptr == NULL)
+	return NULL;
+      *(uint32_t*)failure_ptr = REGFI_OFFSET_NONE;
+      lru_cache_update(file->sk_cache, &offset, 4, failure_ptr);
+      return NULL;
+    }
+
+    lru_cache_update(file->sk_cache, &offset, 4, ret_val);
+  }
+
+  return ret_val;
+}
+
+
 
+/******************************************************************************
  ******************************************************************************/
-static bool regfi_find_root_nk(REGF_FILE* file, uint32 offset, uint32 hbin_size,
+static bool regfi_find_root_nk(REGFI_FILE* file, uint32 offset,uint32 hbin_size,
 			       uint32* root_offset)
 {
   uint8 tmp[4];
   int32 record_size;
   uint32 length, hbin_offset = 0;
-  REGF_NK_REC* nk = NULL;
+  REGFI_NK_REC* nk = NULL;
   bool found = false;
 
   for(record_size=0; !found && (hbin_offset < hbin_size); )
@@ -859,12 +1203,12 @@ static bool regfi_find_root_nk(REGF_FILE* file, uint32 offset, uint32 hbin_size,
       nk = regfi_parse_nk(file, offset+hbin_offset, hbin_size-hbin_offset, true);
       if(nk != NULL)
       {
-	if(nk->key_type == NK_TYPE_ROOTKEY)
+	if(nk->key_type & REGFI_NK_FLAG_ROOT)
 	{
 	  found = true;
 	  *root_offset = nk->offset;
 	}
-	free(nk);
+	regfi_free_key(nk);
       }
     }
 
@@ -879,59 +1223,84 @@ static bool regfi_find_root_nk(REGF_FILE* file, uint32 offset, uint32 hbin_size,
  * Open the registry file and then read in the REGF block to get the
  * first hbin offset.
  *******************************************************************/
-REGF_FILE* regfi_open(const char* filename)
+REGFI_FILE* regfi_open(const char* filename)
 {
-  REGF_FILE* rb;
-  REGF_HBIN* hbin = NULL;
-  uint32 hbin_off;
+  struct stat sbuf;
+  REGFI_FILE* rb;
+  REGFI_HBIN* hbin = NULL;
+  uint32 hbin_off, file_length, cache_secret;
   int fd;
   bool rla;
 
   /* open an existing file */
-  if ((fd = open(filename, O_RDONLY)) == -1) 
+  if ((fd = open(filename, REGFI_OPEN_FLAGS)) == -1)
   {
-    /* DEBUG(0,("regfi_open: failure to open %s (%s)\n", filename, strerror(errno)));*/
+    /* fprintf(stderr, "regfi_open: failure to open %s (%s)\n", filename, strerror(errno));*/
     return NULL;
   }
   
+  /* Determine file length.  Must be at least big enough 
+   * for the header and one hbin. 
+   */
+  if (fstat(fd, &sbuf) == -1)
+    return NULL;
+  file_length = sbuf.st_size;
+  if(file_length < REGFI_REGF_SIZE+REGFI_HBIN_ALLOC)
+    return NULL;
+
   /* read in an existing file */
   if ((rb = regfi_parse_regf(fd, true)) == NULL) 
   {
-    /* DEBUG(0,("regfi_open: Failed to read initial REGF block\n"));*/
+    /* fprintf(stderr, "regfi_open: Failed to read initial REGF block\n"); */
     close(fd);
     return NULL;
   }
-  
+  rb->file_length = file_length;  
+
   rb->hbins = range_list_new();
   if(rb->hbins == NULL)
   {
-    range_list_free(rb->hbins);
+    /* fprintf(stderr, "regfi_open: Failed to create HBIN list.\n"); */
     close(fd);
-    free(rb);
+    talloc_free(rb);
     return NULL;
   }
-  
+  talloc_steal(rb, rb->hbins);
+
   rla = true;
-  hbin_off = REGF_BLOCKSIZE;
+  hbin_off = REGFI_REGF_SIZE;
   hbin = regfi_parse_hbin(rb, hbin_off, true);
   while(hbin && rla)
   {
-    hbin_off = hbin->file_off + hbin->block_size;
     rla = range_list_add(rb->hbins, hbin->file_off, hbin->block_size, hbin);
+    if(rla)
+      talloc_steal(rb->hbins, hbin);
+    hbin_off = hbin->file_off + hbin->block_size;
     hbin = regfi_parse_hbin(rb, hbin_off, true);
   }
 
+  /* This secret isn't very secret, but we don't need a good one.  This 
+   * secret is just designed to prevent someone from trying to blow our
+   * caching and make things slow.
+   */
+  cache_secret = 0x15DEAD05^time(NULL)^(getpid()<<16);
+
+  /* Cache an unlimited number of SK records.  Typically there are very few. */
+  rb->sk_cache = lru_cache_create_ctx(rb, 0, cache_secret, true);
+
+  /* Default message mask */
+  rb->msg_mask = REGFI_MSG_ERROR|REGFI_MSG_WARN;
+
   /* success */
   return rb;
 }
 
 
-/*******************************************************************
- *******************************************************************/
-int regfi_close( REGF_FILE *file )
+/******************************************************************************
+ ******************************************************************************/
+int regfi_close(REGFI_FILE *file)
 {
   int fd;
-  uint32 i;
 
   /* nothing to do if there is no open file */
   if ((file == NULL) || (file->fd == -1))
@@ -939,12 +1308,13 @@ int regfi_close( REGF_FILE *file )
 
   fd = file->fd;
   file->fd = -1;
-  for(i=0; i < range_list_size(file->hbins); i++)
-    free(range_list_get(file->hbins, i)->data);
+
   range_list_free(file->hbins);
 
-  free(file);
+  if(file->sk_cache != NULL)
+    lru_cache_destroy(file->sk_cache);
 
+  talloc_free(file);
   return close(fd);
 }
 
@@ -952,27 +1322,27 @@ int regfi_close( REGF_FILE *file )
 /******************************************************************************
  * There should be only *one* root key in the registry file based 
  * on my experience.  --jerry
- *****************************************************************************/
-REGF_NK_REC* regfi_rootkey(REGF_FILE *file)
+ ******************************************************************************/
+REGFI_NK_REC* regfi_rootkey(REGFI_FILE *file)
 {
-  REGF_NK_REC* nk = NULL;
-  REGF_HBIN*   hbin;
-  uint32       root_offset, i, num_hbins;
+  REGFI_NK_REC* nk = NULL;
+  REGFI_HBIN* hbin;
+  uint32 root_offset, i, num_hbins;
   
   if(!file)
     return NULL;
 
   /* Scan through the file one HBIN block at a time looking 
-     for an NK record with a type == 0x002c.
-     Normally this is the first nk record in the first hbin 
-     block (but I'm not assuming that for now) */
-
+   * for an NK record with a root key type.
+   * This is typically the first NK record in the first HBIN
+   * block (but we're not assuming that generally).
+   */
   num_hbins = range_list_size(file->hbins);
   for(i=0; i < num_hbins; i++)
   {
-    hbin = (REGF_HBIN*)range_list_get(file->hbins, i)->data;
-    if(regfi_find_root_nk(file, hbin->file_off+HBIN_HEADER_REC_SIZE, 
-			  hbin->block_size-HBIN_HEADER_REC_SIZE, &root_offset))
+    hbin = (REGFI_HBIN*)range_list_get(file->hbins, i)->data;
+    if(regfi_find_root_nk(file, hbin->file_off+REGFI_HBIN_HEADER_SIZE, 
+			  hbin->block_size-REGFI_HBIN_HEADER_SIZE, &root_offset))
     {
       nk = regfi_load_key(file, root_offset, true);
       break;
@@ -985,65 +1355,55 @@ REGF_NK_REC* regfi_rootkey(REGF_FILE *file)
 
 /******************************************************************************
  *****************************************************************************/
-void regfi_key_free(REGF_NK_REC* nk)
+void regfi_free_key(REGFI_NK_REC* nk)
 {
-  uint32 i;
-  
-  if((nk->values != NULL) && (nk->values_off!=REGF_OFFSET_NONE))
-  {
-    for(i=0; i < nk->num_values; i++)
-    {
-      if(nk->values[i]->valuename != NULL)
-	free(nk->values[i]->valuename);
-      if(nk->values[i]->data != NULL)
-	free(nk->values[i]->data);
-      free(nk->values[i]);
-    }
-    free(nk->values);
-  }
+  regfi_subkeylist_free(nk->subkeys);
+  talloc_free(nk);
+}
 
-  if(nk->keyname != NULL)
-    free(nk->keyname);
-  if(nk->classname != NULL)
-    free(nk->classname);
 
-  /* XXX: not freeing hbin because these are cached.  This needs to be reviewed. */
-  /* XXX: not freeing sec_desc because these are cached.  This needs to be reviewed. */
-  free(nk);
+/******************************************************************************
+ *****************************************************************************/
+void regfi_free_value(REGFI_VK_REC* vk)
+{
+  talloc_free(vk);
 }
 
 
 /******************************************************************************
  *****************************************************************************/
-REGFI_ITERATOR* regfi_iterator_new(REGF_FILE* fh)
+void regfi_subkeylist_free(REGFI_SUBKEY_LIST* list)
 {
-  REGF_NK_REC* root;
-  REGFI_ITERATOR* ret_val = (REGFI_ITERATOR*)malloc(sizeof(REGFI_ITERATOR));
+  if(list != NULL)
+  {
+    talloc_free(list);
+  }
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* fh)
+{
+  REGFI_NK_REC* root;
+  REGFI_ITERATOR* ret_val = talloc(NULL, REGFI_ITERATOR);
   if(ret_val == NULL)
     return NULL;
 
   root = regfi_rootkey(fh);
   if(root == NULL)
   {
-    free(ret_val);
+    talloc_free(ret_val);
     return NULL;
   }
 
-  ret_val->key_positions = void_stack_new(REGF_MAX_DEPTH);
+  ret_val->key_positions = void_stack_new(REGFI_MAX_DEPTH);
   if(ret_val->key_positions == NULL)
   {
-    free(ret_val);
-    free(root);
+    talloc_free(ret_val);
     return NULL;
   }
-
-  /* This secret isn't very secret, but we don't need a good one.  This 
-   * secret is just designed to prevent someone from trying to blow our
-   * caching and make things slow.
-   */
-  ret_val->sk_recs = lru_cache_create(127, 0x15DEAD05^time(NULL)
-				           ^(getpid()<<16)^(getppid()<<8),
-				      true);
+  talloc_steal(ret_val, ret_val->key_positions);
 
   ret_val->f = fh;
   ret_val->cur_key = root;
@@ -1058,20 +1418,7 @@ REGFI_ITERATOR* regfi_iterator_new(REGF_FILE* fh)
  *****************************************************************************/
 void regfi_iterator_free(REGFI_ITERATOR* i)
 {
-  REGFI_ITER_POSITION* cur;
-
-  if(i->cur_key != NULL)
-    regfi_key_free(i->cur_key);
-
-  while((cur = (REGFI_ITER_POSITION*)void_stack_pop(i->key_positions)) != NULL)
-  {
-    regfi_key_free(cur->nk);
-    free(cur);
-  }
-  
-  lru_cache_destroy(i->sk_recs);
-
-  free(i);
+  talloc_free(i);
 }
 
 
@@ -1081,17 +1428,17 @@ void regfi_iterator_free(REGFI_ITERATOR* i)
 /* XXX: some way of indicating reason for failure should be added. */
 bool regfi_iterator_down(REGFI_ITERATOR* i)
 {
-  REGF_NK_REC* subkey;
+  REGFI_NK_REC* subkey;
   REGFI_ITER_POSITION* pos;
 
-  pos = (REGFI_ITER_POSITION*)malloc(sizeof(REGFI_ITER_POSITION));
+  pos = talloc(i->key_positions, REGFI_ITER_POSITION);
   if(pos == NULL)
     return false;
 
-  subkey = (REGF_NK_REC*)regfi_iterator_cur_subkey(i);
+  subkey = (REGFI_NK_REC*)regfi_iterator_cur_subkey(i);
   if(subkey == NULL)
   {
-    free(pos);
+    talloc_free(pos);
     return false;
   }
 
@@ -1099,10 +1446,11 @@ bool regfi_iterator_down(REGFI_ITERATOR* i)
   pos->cur_subkey = i->cur_subkey;
   if(!void_stack_push(i->key_positions, pos))
   {
-    free(pos);
-    regfi_key_free(subkey);
+    talloc_free(pos);
+    regfi_free_key(subkey);
     return false;
   }
+  talloc_steal(i, subkey);
 
   i->cur_key = subkey;
   i->cur_subkey = 0;
@@ -1122,11 +1470,11 @@ bool regfi_iterator_up(REGFI_ITERATOR* i)
   if(pos == NULL)
     return false;
 
-  regfi_key_free(i->cur_key);
+  regfi_free_key(i->cur_key);
   i->cur_key = pos->nk;
   i->cur_subkey = pos->cur_subkey;
   i->cur_value = 0;
-  free(pos);
+  talloc_free(pos);
 
   return true;
 }
@@ -1147,15 +1495,15 @@ bool regfi_iterator_to_root(REGFI_ITERATOR* i)
  *****************************************************************************/
 bool regfi_iterator_find_subkey(REGFI_ITERATOR* i, const char* subkey_name)
 {
-  REGF_NK_REC* subkey;
+  REGFI_NK_REC* subkey;
   bool found = false;
   uint32 old_subkey = i->cur_subkey;
-  
+
   if(subkey_name == NULL)
     return false;
 
   /* XXX: this alloc/free of each sub key might be a bit excessive */
-  subkey = (REGF_NK_REC*)regfi_iterator_first_subkey(i);
+  subkey = (REGFI_NK_REC*)regfi_iterator_first_subkey(i);
   while((subkey != NULL) && (found == false))
   {
     if(subkey->keyname != NULL 
@@ -1163,8 +1511,8 @@ bool regfi_iterator_find_subkey(REGFI_ITERATOR* i, const char* subkey_name)
       found = true;
     else
     {
-      regfi_key_free(subkey);
-      subkey = (REGF_NK_REC*)regfi_iterator_next_subkey(i);
+      regfi_free_key(subkey);
+      subkey = (REGFI_NK_REC*)regfi_iterator_next_subkey(i);
     }
   }
 
@@ -1174,7 +1522,7 @@ bool regfi_iterator_find_subkey(REGFI_ITERATOR* i, const char* subkey_name)
     return false;
   }
 
-  regfi_key_free(subkey);
+  regfi_free_key(subkey);
   return true;
 }
 
@@ -1206,7 +1554,7 @@ bool regfi_iterator_walk_path(REGFI_ITERATOR* i, const char** path)
 
 /******************************************************************************
  *****************************************************************************/
-const REGF_NK_REC* regfi_iterator_cur_key(REGFI_ITERATOR* i)
+const REGFI_NK_REC* regfi_iterator_cur_key(REGFI_ITERATOR* i)
 {
   return i->cur_key;
 }
@@ -1214,43 +1562,18 @@ const REGF_NK_REC* regfi_iterator_cur_key(REGFI_ITERATOR* i)
 
 /******************************************************************************
  *****************************************************************************/
-const REGF_SK_REC* regfi_iterator_cur_sk(REGFI_ITERATOR* i)
+const REGFI_SK_REC* regfi_iterator_cur_sk(REGFI_ITERATOR* i)
 {
-  REGF_SK_REC* ret_val;
-  REGF_HBIN* hbin;
-  uint32 max_length, off;
-
-  if(i->cur_key == NULL)
+  if(i->cur_key == NULL || i->cur_key->sk_off == REGFI_OFFSET_NONE)
     return NULL;
-  
-  /* First look if we have already parsed it */
-  if((i->cur_key->sk_off!=REGF_OFFSET_NONE)
-     && !(ret_val =(REGF_SK_REC*)lru_cache_find(i->sk_recs, 
-						&i->cur_key->sk_off, 4)))
-  {
-    hbin = regfi_lookup_hbin(i->f, i->cur_key->sk_off);
-
-    if(hbin == NULL)
-      return NULL;
-
-    off = i->cur_key->sk_off + REGF_BLOCKSIZE;
-    max_length = hbin->block_size + hbin->file_off - off;
-    ret_val = regfi_parse_sk(i->f, off, max_length, true);
-    if(ret_val == NULL)
-      return NULL;
 
-    ret_val->sk_off = i->cur_key->sk_off;
-    lru_cache_update(i->sk_recs, &i->cur_key->sk_off, 4, ret_val);
-  }
-
-  return ret_val;
+  return regfi_load_sk(i->f, i->cur_key->sk_off + REGFI_REGF_SIZE, true);
 }
 
 
-
 /******************************************************************************
  *****************************************************************************/
-const REGF_NK_REC* regfi_iterator_first_subkey(REGFI_ITERATOR* i)
+REGFI_NK_REC* regfi_iterator_first_subkey(REGFI_ITERATOR* i)
 {
   i->cur_subkey = 0;
   return regfi_iterator_cur_subkey(i);
@@ -1259,27 +1582,27 @@ const REGF_NK_REC* regfi_iterator_first_subkey(REGFI_ITERATOR* i)
 
 /******************************************************************************
  *****************************************************************************/
-const REGF_NK_REC* regfi_iterator_cur_subkey(REGFI_ITERATOR* i)
+REGFI_NK_REC* regfi_iterator_cur_subkey(REGFI_ITERATOR* i)
 {
   uint32 nk_offset;
 
   /* see if there is anything left to report */
-  if (!(i->cur_key) || (i->cur_key->subkeys_off==REGF_OFFSET_NONE)
+  if (!(i->cur_key) || (i->cur_key->subkeys_off==REGFI_OFFSET_NONE)
       || (i->cur_subkey >= i->cur_key->num_subkeys))
     return NULL;
 
-  nk_offset = i->cur_key->subkeys->hashes[i->cur_subkey].nk_off;
-  
-  return regfi_load_key(i->f, nk_offset+REGF_BLOCKSIZE, true);
+  nk_offset = i->cur_key->subkeys->elements[i->cur_subkey].offset;
+
+  return regfi_load_key(i->f, nk_offset+REGFI_REGF_SIZE, true);
 }
 
 
 /******************************************************************************
  *****************************************************************************/
 /* XXX: some way of indicating reason for failure should be added. */
-const REGF_NK_REC* regfi_iterator_next_subkey(REGFI_ITERATOR* i)
+REGFI_NK_REC* regfi_iterator_next_subkey(REGFI_ITERATOR* i)
 {
-  const REGF_NK_REC* subkey;
+  REGFI_NK_REC* subkey;
 
   i->cur_subkey++;
   subkey = regfi_iterator_cur_subkey(i);
@@ -1295,7 +1618,7 @@ const REGF_NK_REC* regfi_iterator_next_subkey(REGFI_ITERATOR* i)
  *****************************************************************************/
 bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* value_name)
 {
-  const REGF_VK_REC* cur;
+  REGFI_VK_REC* cur;
   bool found = false;
 
   /* XXX: cur->valuename can be NULL in the registry.  
@@ -1311,7 +1634,10 @@ bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* value_name)
        && (strcasecmp(cur->valuename, value_name) == 0))
       found = true;
     else
+    {
+      regfi_free_value(cur);
       cur = regfi_iterator_next_value(i);
+    }
   }
 
   return found;
@@ -1320,7 +1646,7 @@ bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* value_name)
 
 /******************************************************************************
  *****************************************************************************/
-const REGF_VK_REC* regfi_iterator_first_value(REGFI_ITERATOR* i)
+REGFI_VK_REC* regfi_iterator_first_value(REGFI_ITERATOR* i)
 {
   i->cur_value = 0;
   return regfi_iterator_cur_value(i);
@@ -1329,11 +1655,19 @@ const REGF_VK_REC* regfi_iterator_first_value(REGFI_ITERATOR* i)
 
 /******************************************************************************
  *****************************************************************************/
-const REGF_VK_REC* regfi_iterator_cur_value(REGFI_ITERATOR* i)
+REGFI_VK_REC* regfi_iterator_cur_value(REGFI_ITERATOR* i)
 {
-  REGF_VK_REC* ret_val = NULL;
-  if(i->cur_value < i->cur_key->num_values)
-    ret_val = i->cur_key->values[i->cur_value];
+  REGFI_VK_REC* ret_val = NULL;
+  uint32 voffset;
+
+  if(i->cur_key->values != NULL && i->cur_key->values->elements != NULL)
+  {
+    if(i->cur_value < i->cur_key->values->num_values)
+    {
+      voffset = i->cur_key->values->elements[i->cur_value];
+      ret_val = regfi_load_value(i->f, voffset+REGFI_REGF_SIZE, true);
+    }
+  }
 
   return ret_val;
 }
@@ -1341,9 +1675,9 @@ const REGF_VK_REC* regfi_iterator_cur_value(REGFI_ITERATOR* i)
 
 /******************************************************************************
  *****************************************************************************/
-const REGF_VK_REC* regfi_iterator_next_value(REGFI_ITERATOR* i)
+REGFI_VK_REC* regfi_iterator_next_value(REGFI_ITERATOR* i)
 {
-  const REGF_VK_REC* ret_val;
+  REGFI_VK_REC* ret_val;
 
   i->cur_value++;
   ret_val = regfi_iterator_cur_value(i);
@@ -1354,7 +1688,6 @@ const REGF_VK_REC* regfi_iterator_next_value(REGFI_ITERATOR* i)
 }
 
 
-
 /*******************************************************************
  * Computes the checksum of the registry file header.
  * buffer must be at least the size of an regf header (4096 bytes).
@@ -1380,109 +1713,125 @@ static uint32 regfi_compute_header_checksum(uint8* buffer)
 /*******************************************************************
  * XXX: Add way to return more detailed error information.
  *******************************************************************/
-REGF_FILE* regfi_parse_regf(int fd, bool strict)
+REGFI_FILE* regfi_parse_regf(int fd, bool strict)
 {
-  uint8 file_header[REGF_BLOCKSIZE];
+  uint8 file_header[REGFI_REGF_SIZE];
   uint32 length;
-  uint32 file_length;
-  struct stat sbuf;
-  REGF_FILE* ret_val;
+  REGFI_FILE* ret_val;
 
-  /* Determine file length.  Must be at least big enough 
-   * for the header and one hbin. 
-   */
-  if (fstat(fd, &sbuf) == -1)
-    return NULL;
-  file_length = sbuf.st_size;
-  if(file_length < REGF_BLOCKSIZE+REGF_ALLOC_BLOCK)
-    return NULL;
-
-  ret_val = (REGF_FILE*)zalloc(sizeof(REGF_FILE));
+  ret_val = talloc(NULL, REGFI_FILE);
   if(ret_val == NULL)
     return NULL;
 
   ret_val->fd = fd;
-  ret_val->file_length = file_length;
-
-  length = REGF_BLOCKSIZE;
-  if((regfi_read(fd, file_header, &length)) != 0 
-     || length != REGF_BLOCKSIZE)
-  {
-    free(ret_val);
-    return NULL;
-  }
-
+  ret_val->sk_cache = NULL;
+  ret_val->last_message = NULL;
+  ret_val->hbins = NULL;
+  
+  length = REGFI_REGF_SIZE;
+  if((regfi_read(fd, file_header, &length)) != 0 || length != REGFI_REGF_SIZE)
+    goto fail;
+  
   ret_val->checksum = IVAL(file_header, 0x1FC);
   ret_val->computed_checksum = regfi_compute_header_checksum(file_header);
   if (strict && (ret_val->checksum != ret_val->computed_checksum))
-  {
-    free(ret_val);
-    return NULL;
-  }
+    goto fail;
 
-  memcpy(ret_val->magic, file_header, 4);
-  if(strict && (memcmp(ret_val->magic, "regf", 4) != 0))
+  memcpy(ret_val->magic, file_header, REGFI_REGF_MAGIC_SIZE);
+  if(memcmp(ret_val->magic, "regf", REGFI_REGF_MAGIC_SIZE) != 0)
   {
-    free(ret_val);
-    return NULL;
-  }
-  
-  ret_val->unknown1 = IVAL(file_header, 0x4);
-  ret_val->unknown2 = IVAL(file_header, 0x8);
-
+    if(strict)
+      goto fail;
+    regfi_add_message(ret_val, REGFI_MSG_WARN, "Magic number mismatch "
+		      "(%.2X %.2X %.2X %.2X) while parsing hive header",
+		      ret_val->magic[0], ret_val->magic[1], 
+		      ret_val->magic[2], ret_val->magic[3]);
+  }
+  ret_val->sequence1 = IVAL(file_header, 0x4);
+  ret_val->sequence2 = IVAL(file_header, 0x8);
   ret_val->mtime.low = IVAL(file_header, 0xC);
   ret_val->mtime.high = IVAL(file_header, 0x10);
-
-  ret_val->unknown3 = IVAL(file_header, 0x14);
-  ret_val->unknown4 = IVAL(file_header, 0x18);
-  ret_val->unknown5 = IVAL(file_header, 0x1C);
-  ret_val->unknown6 = IVAL(file_header, 0x20);
-  
-  ret_val->data_offset = IVAL(file_header, 0x24);
+  ret_val->major_version = IVAL(file_header, 0x14);
+  ret_val->minor_version = IVAL(file_header, 0x18);
+  ret_val->type = IVAL(file_header, 0x1C);
+  ret_val->format = IVAL(file_header, 0x20);
+  ret_val->root_cell = IVAL(file_header, 0x24);
   ret_val->last_block = IVAL(file_header, 0x28);
 
-  ret_val->unknown7 = IVAL(file_header, 0x2C);
+  ret_val->cluster = IVAL(file_header, 0x2C);
+
+  memcpy(ret_val->file_name, file_header+0x30,  REGFI_REGF_NAME_SIZE);
+
+  /* XXX: Should we add a warning if these uuid parsers fail?  Can they? */
+  ret_val->rm_id = winsec_parse_uuid(ret_val, file_header+0x70, 16);
+  ret_val->log_id = winsec_parse_uuid(ret_val, file_header+0x80, 16);
+  ret_val->flags = IVAL(file_header, 0x90);
+  ret_val->tm_id = winsec_parse_uuid(ret_val, file_header+0x94, 16);
+  ret_val->guid_signature = IVAL(file_header, 0xa4);
+
+  memcpy(ret_val->reserved1, file_header+0xa8, REGFI_REGF_RESERVED1_SIZE);
+  memcpy(ret_val->reserved2, file_header+0x200, REGFI_REGF_RESERVED2_SIZE);
+
+  ret_val->thaw_tm_id = winsec_parse_uuid(ret_val, file_header+0xFC8, 16);
+  ret_val->thaw_rm_id = winsec_parse_uuid(ret_val, file_header+0xFD8, 16);
+  ret_val->thaw_log_id = winsec_parse_uuid(ret_val, file_header+0xFE8, 16);
+  ret_val->boot_type = IVAL(file_header, 0xFF8);
+  ret_val->boot_recover = IVAL(file_header, 0xFFC);
 
   return ret_val;
+
+ fail:
+  talloc_free(ret_val);
+  return NULL;
 }
 
 
 
-/*******************************************************************
+/******************************************************************************
  * Given real file offset, read and parse the hbin at that location
  * along with it's associated cells.
- *******************************************************************/
-/* XXX: Need a way to return types of errors.
- */
-REGF_HBIN* regfi_parse_hbin(REGF_FILE* file, uint32 offset, bool strict)
+ ******************************************************************************/
+REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32 offset, bool strict)
 {
-  REGF_HBIN *hbin;
-  uint8 hbin_header[HBIN_HEADER_REC_SIZE];
+  REGFI_HBIN *hbin;
+  uint8 hbin_header[REGFI_HBIN_HEADER_SIZE];
   uint32 length;
   
   if(offset >= file->file_length)
     return NULL;
 
   if(lseek(file->fd, offset, SEEK_SET) == -1)
+  {
+    regfi_add_message(file, REGFI_MSG_ERROR, "Seek failed"
+		      " while parsing hbin at offset 0x%.8X.", offset);
     return NULL;
+  }
 
-  length = HBIN_HEADER_REC_SIZE;
+  length = REGFI_HBIN_HEADER_SIZE;
   if((regfi_read(file->fd, hbin_header, &length) != 0) 
-     || length != HBIN_HEADER_REC_SIZE)
+     || length != REGFI_HBIN_HEADER_SIZE)
     return NULL;
 
-
   if(lseek(file->fd, offset, SEEK_SET) == -1)
+  {
+    regfi_add_message(file, REGFI_MSG_ERROR, "Seek failed"
+		      " while parsing hbin at offset 0x%.8X.", offset);
     return NULL;
+  }
 
-  if(!(hbin = (REGF_HBIN*)zalloc(sizeof(REGF_HBIN)))) 
+  hbin = talloc(NULL, REGFI_HBIN);
+  if(hbin == NULL)
     return NULL;
   hbin->file_off = offset;
 
   memcpy(hbin->magic, hbin_header, 4);
   if(strict && (memcmp(hbin->magic, "hbin", 4) != 0))
   {
-    free(hbin);
+    regfi_add_message(file, REGFI_MSG_INFO, "Magic number mismatch "
+		      "(%.2X %.2X %.2X %.2X) while parsing hbin at offset"
+		      " 0x%.8X.", hbin->magic[0], hbin->magic[1], 
+		      hbin->magic[2], hbin->magic[3], offset);
+    talloc_free(hbin);
     return NULL;
   }
 
@@ -1501,7 +1850,10 @@ REGF_HBIN* regfi_parse_hbin(REGF_FILE* file, uint32 offset, bool strict)
   if((offset + hbin->block_size > file->file_length)
      || (hbin->block_size & 0xFFFFF000) != hbin->block_size)
   {
-    free(hbin);
+    regfi_add_message(file, REGFI_MSG_ERROR, "The hbin offset is not aligned"
+		      " or runs off the end of the file"
+		      " while parsing hbin at offset 0x%.8X.", offset);
+    talloc_free(hbin);
     return NULL;
   }
 
@@ -1509,33 +1861,44 @@ REGF_HBIN* regfi_parse_hbin(REGF_FILE* file, uint32 offset, bool strict)
 }
 
 
-
-REGF_NK_REC* regfi_parse_nk(REGF_FILE* file, uint32 offset, 
+/*******************************************************************
+ *******************************************************************/
+REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32 offset, 
 			    uint32 max_size, bool strict)
 {
   uint8 nk_header[REGFI_NK_MIN_LENGTH];
-  REGF_NK_REC* ret_val;
-  uint32 length;
-  uint32 cell_length;
+  const REGFI_HBIN *hbin;
+  REGFI_NK_REC* ret_val;
+  uint32 length,cell_length;
+  uint32 class_offset, class_maxsize;
   bool unalloc = false;
 
   if(!regfi_parse_cell(file->fd, offset, nk_header, REGFI_NK_MIN_LENGTH,
 		       &cell_length, &unalloc))
-     return NULL;
- 
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell header"
+		      " while parsing NK record at offset 0x%.8X.", offset);
+    return NULL;
+  }
+
   /* A bit of validation before bothering to allocate memory */
   if((nk_header[0x0] != 'n') || (nk_header[0x1] != 'k'))
   {
-    /* XXX: Deal with subkey-lists that reference other subkey-lists
-     *      (e.g. 'ri' records). 
-     */
+    regfi_add_message(file, REGFI_MSG_WARN, "Magic number mismatch in parsing"
+		      " NK record at offset 0x%.8X.", offset);
     return NULL;
   }
 
-  ret_val = (REGF_NK_REC*)zalloc(sizeof(REGF_NK_REC));
+  ret_val = talloc(NULL, REGFI_NK_REC);
   if(ret_val == NULL)
+  {
+    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to allocate memory while"
+		      " parsing NK record at offset 0x%.8X.", offset);
     return NULL;
+  }
 
+  ret_val->values = NULL;
+  ret_val->subkeys = NULL;
   ret_val->offset = offset;
   ret_val->cell_size = cell_length;
 
@@ -1544,20 +1907,21 @@ REGF_NK_REC* regfi_parse_nk(REGF_FILE* file, uint32 offset,
   if((ret_val->cell_size < REGFI_NK_MIN_LENGTH) 
      || (strict && ret_val->cell_size != (ret_val->cell_size & 0xFFFFFFF8)))
   {
-    free(ret_val);
+    regfi_add_message(file, REGFI_MSG_WARN, "A length check failed while"
+		      " parsing NK record at offset 0x%.8X.", offset);
+    talloc_free(ret_val);
     return NULL;
   }
 
   ret_val->magic[0] = nk_header[0x0];
   ret_val->magic[1] = nk_header[0x1];
   ret_val->key_type = SVAL(nk_header, 0x2);
-  if((ret_val->key_type != NK_TYPE_NORMALKEY)
-     && (ret_val->key_type != NK_TYPE_ROOTKEY) 
-     && (ret_val->key_type != NK_TYPE_LINKKEY)
-     && (ret_val->key_type != NK_TYPE_UNKNOWN1))
+  
+  if((ret_val->key_type & ~REGFI_NK_KNOWN_FLAGS) != 0)
   {
-    free(ret_val);
-    return NULL;
+    regfi_add_message(file, REGFI_MSG_WARN, "Unknown key flags (0x%.4X) while"
+		      " parsing NK record at offset 0x%.8X.", 
+		      (ret_val->key_type & ~REGFI_NK_KNOWN_FLAGS), offset);
   }
 
   ret_val->mtime.low = IVAL(nk_header, 0x4);
@@ -1582,7 +1946,6 @@ REGF_NK_REC* regfi_parse_nk(REGF_FILE* file, uint32 offset,
   ret_val->num_values = IVAL(nk_header, 0x24);
   ret_val->values_off = IVAL(nk_header, 0x28);
   ret_val->sk_off = IVAL(nk_header, 0x2C);
-  /* XXX: currently we do nothing with class names.  Need to investigate. */
   ret_val->classname_off = IVAL(nk_header, 0x30);
 
   ret_val->max_bytes_subkeyname = IVAL(nk_header, 0x34);
@@ -1598,7 +1961,9 @@ REGF_NK_REC* regfi_parse_nk(REGF_FILE* file, uint32 offset,
   {
     if(strict)
     {
-      free(ret_val);
+      regfi_add_message(file, REGFI_MSG_ERROR, "Contents too large for cell"
+			" while parsing NK record at offset 0x%.8X.", offset);
+      talloc_free(ret_val);
       return NULL;
     }
     else
@@ -1616,10 +1981,10 @@ REGF_NK_REC* regfi_parse_nk(REGF_FILE* file, uint32 offset,
       ret_val->cell_size = length;
   }
 
-  ret_val->keyname = (char*)zalloc(sizeof(char)*(ret_val->name_length+1));
+  ret_val->keyname = talloc_array(ret_val, char, ret_val->name_length+1);
   if(ret_val->keyname == NULL)
   {
-    free(ret_val);
+    talloc_free(ret_val);
     return NULL;
   }
 
@@ -1628,44 +1993,146 @@ REGF_NK_REC* regfi_parse_nk(REGF_FILE* file, uint32 offset,
   if((regfi_read(file->fd, (uint8*)ret_val->keyname, &length) != 0)
      || length != ret_val->name_length)
   {
-    free(ret_val->keyname);
-    free(ret_val);
+    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read key name"
+		      " while parsing NK record at offset 0x%.8X.", offset);
+    talloc_free(ret_val);
     return NULL;
   }
   ret_val->keyname[ret_val->name_length] = '\0';
 
+  /* XXX: This linking should be moved up to regfi_load_key */
+  if(ret_val->classname_off != REGFI_OFFSET_NONE)
+  {
+    hbin = regfi_lookup_hbin(file, ret_val->classname_off);
+    if(hbin)
+    {
+      class_offset = ret_val->classname_off+REGFI_REGF_SIZE;
+      class_maxsize = hbin->block_size + hbin->file_off - class_offset;
+      ret_val->classname
+	= regfi_parse_classname(file, class_offset, &ret_val->classname_length, 
+				class_maxsize, strict);
+    }
+    else
+    {
+      ret_val->classname = NULL;
+      regfi_add_message(file, REGFI_MSG_WARN, "Could not find hbin for class"
+			" name while parsing NK record at offset 0x%.8X.", 
+			offset);
+    }
+
+    if(ret_val->classname == NULL)
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Could not parse class"
+			" name while parsing NK record at offset 0x%.8X.", 
+			offset);
+    }
+    else
+      talloc_steal(ret_val, ret_val->classname);
+  }
+
   return ret_val;
 }
 
 
+char* regfi_parse_classname(REGFI_FILE* file, uint32 offset, 
+			    uint16* name_length, uint32 max_size, bool strict)
+{
+  char* ret_val = NULL;
+  uint32 length;
+  uint32 cell_length;
+  bool unalloc = false;
+
+  if(*name_length > 0 && offset != REGFI_OFFSET_NONE 
+     && offset == (offset & 0xFFFFFFF8))
+  {
+    if(!regfi_parse_cell(file->fd, offset, NULL, 0, &cell_length, &unalloc))
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell header"
+			" while parsing class name at offset 0x%.8X.", offset);
+	return NULL;
+    }
 
-/*******************************************************************
- *******************************************************************/
-REGF_VK_REC* regfi_parse_vk(REGF_FILE* file, uint32 offset, 
-			    uint32 max_size, bool strict)
+    if((cell_length & 0xFFFFFFF8) != cell_length)
+    {
+      regfi_add_message(file, REGFI_MSG_ERROR, "Cell length not a multiple of 8"
+			" while parsing class name at offset 0x%.8X.", offset);
+      return NULL;
+    }
+
+    if(cell_length > max_size)
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Cell stretches past hbin "
+			"boundary while parsing class name at offset 0x%.8X.",
+			offset);
+      if(strict)
+	return NULL;
+      cell_length = max_size;
+    }
+
+    if((cell_length - 4) < *name_length)
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Class name is larger than"
+			" cell_length while parsing class name at offset"
+			" 0x%.8X.", offset);
+      if(strict)
+	return NULL;
+      *name_length = cell_length - 4;
+    }
+    
+    ret_val = talloc_array(NULL, char, *name_length);
+    if(ret_val != NULL)
+    {
+      length = *name_length;
+      if((regfi_read(file->fd, (uint8*)ret_val, &length) != 0)
+	 || length != *name_length)
+      {
+	regfi_add_message(file, REGFI_MSG_ERROR, "Could not read class name"
+			  " while parsing class name at offset 0x%.8X.", offset);
+	talloc_free(ret_val);
+	return NULL;
+      }
+    }
+  }
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+*******************************************************************************/
+REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32 offset, 
+			     uint32 max_size, bool strict)
 {
-  REGF_VK_REC* ret_val;
+  REGFI_VK_REC* ret_val;
   uint8 vk_header[REGFI_VK_MIN_LENGTH];
   uint32 raw_data_size, length, cell_length;
   bool unalloc = false;
 
   if(!regfi_parse_cell(file->fd, offset, vk_header, REGFI_VK_MIN_LENGTH,
 		       &cell_length, &unalloc))
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell header"
+		      " while parsing VK record at offset 0x%.8X.", offset);
     return NULL;
+  }
 
-  ret_val = (REGF_VK_REC*)zalloc(sizeof(REGF_VK_REC));
+  ret_val = talloc(NULL, REGFI_VK_REC);
   if(ret_val == NULL)
     return NULL;
 
   ret_val->offset = offset;
   ret_val->cell_size = cell_length;
-
+  ret_val->data = NULL;
+  ret_val->valuename = NULL;
+  
   if(ret_val->cell_size > max_size)
     ret_val->cell_size = max_size & 0xFFFFFFF8;
   if((ret_val->cell_size < REGFI_VK_MIN_LENGTH) 
      || ret_val->cell_size != (ret_val->cell_size & 0xFFFFFFF8))
   {
-    free(ret_val);
+    regfi_add_message(file, REGFI_MSG_WARN, "Invalid cell size encountered"
+		      " while parsing VK record at offset 0x%.8X.", offset);
+    talloc_free(ret_val);
     return NULL;
   }
 
@@ -1673,26 +2140,35 @@ REGF_VK_REC* regfi_parse_vk(REGF_FILE* file, uint32 offset,
   ret_val->magic[1] = vk_header[0x1];
   if((ret_val->magic[0] != 'v') || (ret_val->magic[1] != 'k'))
   {
-    free(ret_val);
+    /* XXX: This does not account for deleted keys under Win2K which
+     *      often have this (and the name length) overwritten with
+     *      0xFFFF. 
+     */
+    regfi_add_message(file, REGFI_MSG_WARN, "Magic number mismatch"
+		      " while parsing VK record at offset 0x%.8X.", offset);
+    talloc_free(ret_val);
     return NULL;
   }
 
   ret_val->name_length = SVAL(vk_header, 0x2);
   raw_data_size = IVAL(vk_header, 0x4);
-  ret_val->data_size = raw_data_size & ~VK_DATA_IN_OFFSET;
-  ret_val->data_in_offset = (bool)(raw_data_size & VK_DATA_IN_OFFSET);
+  ret_val->data_size = raw_data_size & ~REGFI_VK_DATA_IN_OFFSET;
+  ret_val->data_in_offset = (bool)(raw_data_size & REGFI_VK_DATA_IN_OFFSET);
   ret_val->data_off = IVAL(vk_header, 0x8);
   ret_val->type = IVAL(vk_header, 0xC);
   ret_val->flag = SVAL(vk_header, 0x10);
   ret_val->unknown1 = SVAL(vk_header, 0x12);
 
-  if(ret_val->flag & VK_FLAG_NAME_PRESENT)
+  if(ret_val->flag & REGFI_VK_FLAG_NAME_PRESENT)
   {
     if(ret_val->name_length + REGFI_VK_MIN_LENGTH + 4 > ret_val->cell_size)
     {
+      regfi_add_message(file, REGFI_MSG_WARN, "Name too long for remaining cell"
+			" space while parsing VK record at offset 0x%.8X.",
+			offset);
       if(strict)
       {
-	free(ret_val);
+	talloc_free(ret_val);
 	return NULL;
       }
       else
@@ -1704,10 +2180,10 @@ REGF_VK_REC* regfi_parse_vk(REGF_FILE* file, uint32 offset,
     if(cell_length < ret_val->name_length + REGFI_VK_MIN_LENGTH + 4)
       cell_length+=8;
 
-    ret_val->valuename = (char*)zalloc(sizeof(char)*(ret_val->name_length+1));
+    ret_val->valuename = talloc_array(ret_val, char, ret_val->name_length+1);
     if(ret_val->valuename == NULL)
     {
-      free(ret_val);
+      talloc_free(ret_val);
       return NULL;
     }
 
@@ -1715,11 +2191,13 @@ REGF_VK_REC* regfi_parse_vk(REGF_FILE* file, uint32 offset,
     if((regfi_read(file->fd, (uint8*)ret_val->valuename, &length) != 0)
        || length != ret_val->name_length)
     {
-      free(ret_val->valuename);
-      free(ret_val);
+      regfi_add_message(file, REGFI_MSG_ERROR, "Could not read value name"
+			" while parsing VK record at offset 0x%.8X.", offset);
+      talloc_free(ret_val);
       return NULL;
     }
     ret_val->valuename[ret_val->name_length] = '\0';
+
   }
   else
     cell_length = REGFI_VK_MIN_LENGTH + 4;
@@ -1731,90 +2209,239 @@ REGF_VK_REC* regfi_parse_vk(REGF_FILE* file, uint32 offset,
       ret_val->cell_size = cell_length;
   }
 
-  if(ret_val->data_size == 0)
-    ret_val->data = NULL;
-  else
-  {
-    ret_val->data = regfi_parse_data(file, ret_val->data_off+REGF_BLOCKSIZE,
-				     raw_data_size, strict);
-    if(strict && (ret_val->data == NULL))
-    {
-      free(ret_val->valuename);
-      free(ret_val);
-      return NULL;
-    }
-  }
-
   return ret_val;
 }
 
 
-uint8* regfi_parse_data(REGF_FILE* file, uint32 offset, uint32 length, bool strict)
+/******************************************************************************
+*******************************************************************************/
+REGFI_BUFFER regfi_load_data(REGFI_FILE* file, 
+			     uint32 data_type, uint32 offset,
+			     uint32 length, uint32 max_size, 
+			     bool data_in_offset, bool strict)
 {
-  uint8* ret_val;
+  REGFI_BUFFER ret_val;
   uint32 read_length, cell_length;
   uint8 i;
   bool unalloc;
-
-  /* The data is stored in the offset if the size <= 4 */
-  if (length & VK_DATA_IN_OFFSET)
+  
+  /* The data is typically stored in the offset if the size <= 4 */
+  if(data_in_offset)
   {
-    length = length & ~VK_DATA_IN_OFFSET;
     if(length > 4)
-      return NULL;
+    {
+      regfi_add_message(file, REGFI_MSG_ERROR, "Data in offset but length > 4"
+			" while parsing data record at offset 0x%.8X.", 
+			offset);
+      goto fail;
+    }
 
-    if((ret_val = (uint8*)zalloc(sizeof(uint8)*length)) == NULL)
-      return NULL;
+    if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL)
+      goto fail;
+    ret_val.len = length;
 
-    offset = offset - REGF_BLOCKSIZE;
     for(i = 0; i < length; i++)
-      ret_val[i] = (uint8)((offset >> i*8) & 0xFF);
+      ret_val.buf[i] = (uint8)((offset >> i*8) & 0xFF);
   }
   else
   {
     if(!regfi_parse_cell(file->fd, offset, NULL, 0,
 			 &cell_length, &unalloc))
-      return NULL;
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
+			" parsing data record at offset 0x%.8X.", offset);
+      goto fail;
+    }
 
     if((cell_length & 0xFFFFFFF8) != cell_length)
-      return NULL;
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Cell length not multiple of 8"
+			" while parsing data record at offset 0x%.8X.",
+			offset);
+      goto fail;
+    }
 
-    if(cell_length - 4 < length)
+    if(cell_length > max_size)
     {
-      /* XXX: This strict condition has been triggered in multiple registries.
-       *      Not sure the cause, but the data length values are very large,
-       *      such as 53392.
-       */
+      regfi_add_message(file, REGFI_MSG_WARN, "Cell extends past HBIN boundary"
+			" while parsing data record at offset 0x%.8X.",
+			offset);
       if(strict)
-	return NULL;
+	goto fail;
       else
-	length = cell_length - 4;
+	cell_length = max_size;
     }
 
-    /* XXX: There is currently no check to ensure the data 
-     *      cell doesn't cross HBIN boundary.
-     */
+    if(cell_length - 4 < length)
+    {
+      /* XXX: All big data records thus far have been 16 bytes long.  
+       *      Should we check for this precise size instead of just 
+       *      relying upon the above check?
+       */
+      if (file->major_version >= 1 && file->minor_version >= 5)
+      {
+	/* Attempt to parse a big data record */
+	return regfi_load_big_data(file, offset, length, cell_length, strict);
+      }
+      else
+      {
+	regfi_add_message(file, REGFI_MSG_WARN, "Data length (0x%.8X) larger than"
+			  " remaining cell length (0x%.8X)"
+			  " while parsing data record at offset 0x%.8X.", 
+			  length, cell_length - 4, offset);
+	if(strict)
+	  goto fail;
+	else
+	  length = cell_length - 4;
+      }
+    }
 
-    if((ret_val = (uint8*)zalloc(sizeof(uint8)*length)) == NULL)
-      return NULL;
+    if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL)
+      goto fail;
+    ret_val.len = length;
 
     read_length = length;
-    if((regfi_read(file->fd, ret_val, &read_length) != 0) 
+    if((regfi_read(file->fd, ret_val.buf, &read_length) != 0)
        || read_length != length)
     {
-      free(ret_val);
-      return NULL;
+      regfi_add_message(file, REGFI_MSG_ERROR, "Could not read data block while"
+			" parsing data record at offset 0x%.8X.", offset);
+      talloc_free(ret_val.buf);
+      goto fail;
+    }
+  }
+
+  return ret_val;
+
+ fail:
+  ret_val.buf = NULL;
+  ret_val.len = 0;
+  return ret_val;
+}
+
+
+/******************************************************************************
+*******************************************************************************/
+REGFI_BUFFER regfi_load_big_data(REGFI_FILE* file, 
+				 uint32 offset, uint32 data_length, 
+				 uint32 cell_length, bool strict)
+{
+  REGFI_BUFFER ret_val;
+  uint16 num_chunks, i;
+  uint32 indirect_offset, indirect_length, chunk_length, chunk_offset;
+  uint32 read_length, data_left;
+  bool unalloc;
+  uint32* indirect_ptrs;
+  uint8* big_data_cell = (uint8*)malloc(cell_length*sizeof(uint8));
+  if(big_data_cell == NULL)
+    goto fail;
+
+  if(!regfi_parse_cell(file->fd, offset, big_data_cell, cell_length-4,
+		       &cell_length, &unalloc))
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
+		      " parsing big data record at offset 0x%.8X.", offset);
+    free(big_data_cell);
+    goto fail;
+  }
+  
+  if((big_data_cell[0] != 'd') || (big_data_cell[1] != 'b'))
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Unknown magic number"
+		      " (0x%.2X, 0x%.2X) encountered while parsing"
+		      " big data record at offset 0x%.8X.", 
+		      big_data_cell[0], big_data_cell[1], offset);
+    free(big_data_cell);
+    goto fail;
+  }
+  num_chunks = SVAL(big_data_cell, 0x2);
+  /* XXX: Should check sanity of pointers here and in the indirect
+   *      block.  At least ensure they are a multiple of 8. 
+   */
+  indirect_offset = IVAL(big_data_cell, 0x4) + REGFI_REGF_SIZE;
+  free(big_data_cell);
+
+  indirect_ptrs = (uint32*)malloc(num_chunks*sizeof(uint32));
+  if(indirect_ptrs == NULL)
+    goto fail;
+
+  if(!regfi_parse_cell(file->fd, indirect_offset, (uint8*)indirect_ptrs,
+		       num_chunks*sizeof(uint32),
+		       &indirect_length, &unalloc))
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
+		      " parsing big data indirect record at offset 0x%.8X.", 
+		      offset);
+    free(indirect_ptrs);
+    goto fail;
+  }
+  
+  if((ret_val.buf = talloc_array(NULL, uint8_t, data_length)) == NULL)
+  {
+    free(indirect_ptrs);
+    goto fail;
+  }
+  data_left = data_length;
+
+  for(i=0; (i<num_chunks) && (data_left>0); i++)
+  {
+    /* Fix endianness of pointer and convert to physical offset */
+    chunk_offset = IVAL(indirect_ptrs, i*4) + REGFI_REGF_SIZE;
+    if(!regfi_parse_cell(file->fd, chunk_offset, NULL, 0, 
+			 &chunk_length, &unalloc))
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
+			" parsing big data chunk at offset 0x%.8X.", 
+			chunk_offset);
+      if(strict)
+	goto chunk_fail;
+      else
+	break;
     }
+
+    /* XXX: This should be "chunk_length-4" to account for the 4 byte cell 
+     *      length.  However, it has been observed that some (all?) chunks
+     *      have an additional 4 bytes of 0 at the end of their cells that 
+     *      isn't part of the data, so we're trimming that off too. 
+     */
+    if(chunk_length-8 >= data_left)
+      read_length = data_left;
+    else
+      read_length = chunk_length-8;
+
+    if(regfi_read(file->fd, ret_val.buf+(data_length-data_left), 
+		  &read_length) != 0)
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Could not read data chunk while"
+			" constructing big data at offset 0x%.8X.", 
+			chunk_offset);
+      if(strict)
+	goto chunk_fail;
+      else
+	break;
+    }
+
+    data_left -= read_length;
   }
+  free(indirect_ptrs);
+  ret_val.len = data_length-data_left;
+
+  return ret_val;
 
+ chunk_fail:
+  free(indirect_ptrs);
+  talloc_free(ret_val.buf);
+ fail:
+  ret_val.buf = NULL;
+  ret_val.len = 0;
   return ret_val;
 }
 
 
-range_list* regfi_parse_unalloc_cells(REGF_FILE* file)
+range_list* regfi_parse_unalloc_cells(REGFI_FILE* file)
 {
   range_list* ret_val;
-  REGF_HBIN* hbin;
+  REGFI_HBIN* hbin;
   const range_list_element* hbins_elem;
   uint32 i, num_hbins, curr_off, cell_len;
   bool is_unalloc;
@@ -1829,9 +2456,9 @@ range_list* regfi_parse_unalloc_cells(REGF_FILE* file)
     hbins_elem = range_list_get(file->hbins, i);
     if(hbins_elem == NULL)
       break;
-    hbin = (REGF_HBIN*)hbins_elem->data;
+    hbin = (REGFI_HBIN*)hbins_elem->data;
 
-    curr_off = HBIN_HEADER_REC_SIZE;
+    curr_off = REGFI_HBIN_HEADER_SIZE;
     while(curr_off < hbin->block_size)
     {
       if(!regfi_parse_cell(file->fd, hbin->file_off+curr_off, NULL, 0,
@@ -1839,9 +2466,13 @@ range_list* regfi_parse_unalloc_cells(REGF_FILE* file)
 	break;
       
       if((cell_len == 0) || ((cell_len & 0xFFFFFFF8) != cell_len))
-	/* XXX: should report an error here. */
+      {
+	regfi_add_message(file, REGFI_MSG_ERROR, "Bad cell length encountered"
+			  " while parsing unallocated cells at offset 0x%.8X.",
+			  hbin->file_off+curr_off);
 	break;
-      
+      }
+
       /* for some reason the record_size of the last record in
 	 an hbin block can extend past the end of the block
 	 even though the record fits within the remaining 
diff --git a/lib/smb_deps.c b/lib/smb_deps.c
index da7345d..eb09aa4 100644
--- a/lib/smb_deps.c
+++ b/lib/smb_deps.c
@@ -3,7 +3,7 @@
  * depends upon, from the Samba Subversion tree.  See:
  *   http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/
  *
- * Copyright (C) 2005-2006 Timothy D. Morgan
+ * Copyright (C) 2005-2006,2009 Timothy D. Morgan
  * Copyright (C) 1992-2005 Samba development team 
  *               (see individual files under Subversion for details.)
  *
@@ -20,10 +20,10 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: smb_deps.c 111 2008-05-01 04:06:22Z tim $
+ * $Id: smb_deps.c 147 2009-02-22 19:31:52Z tim $
  */
 
-#include "../include/smb_deps.h"
+#include "smb_deps.h"
 
 
 /* These act as replacements for numerous Samba memory allocation
@@ -32,8 +32,9 @@
 void* zalloc(size_t size)
 {
   void* ret_val = NULL;
-  if((ret_val = (void*)malloc(size)) != NULL)
+  if((size > 0) && (ret_val = (void*)malloc(size)) != NULL)
     memset(ret_val, 0, size);
+
   return ret_val;
 }
 
@@ -141,1133 +142,3 @@ time_t nt_time_to_unix(const NTTIME* nt)
 }
 
 /* End of stuff from lib/time.c */
-
-/* From parse_prs.c */
-
-/*******************************************************************
- Attempt, if needed, to grow a data buffer.
- Also depends on the data stream mode (io).
- ********************************************************************/
-bool prs_grow(prs_struct *ps, uint32 extra_space)
-{
-  uint32 new_size;
-  char *new_data;
-  
-  ps->grow_size = MAX(ps->grow_size, ps->data_offset + extra_space);
-  
-  if(ps->data_offset + extra_space <= ps->buffer_size)
-    return true;
-  
-  /*
-   * We cannot grow the buffer if we're not reading
-   * into the prs_struct, or if we don't own the memory.
-   */
-  
-  if(ps->io || !ps->is_dynamic)
-    return false;
-  
-  /*
-   * Decide how much extra space we really need.
-   */
-  extra_space -= (ps->buffer_size - ps->data_offset);
-  if(ps->buffer_size == 0) 
-  {
-    /*
-     * Ensure we have at least a PDU's length, or extra_space, 
-     * whichever is greater.
-     */  
-    new_size = MAX(MAX_PDU_FRAG_LEN,extra_space);
-    
-    if((new_data = zalloc(new_size)) == NULL)
-      return false;
-  } 
-  else 
-  {
-    /*
-     * If the current buffer size is bigger than the space needed, just 
-     * double it, else add extra_space.
-     */
-    new_size = MAX(ps->buffer_size*2, ps->buffer_size + extra_space);		
-    
-    if ((new_data = (char*)realloc(ps->data_p, new_size)) == NULL)
-      return false;
-    
-    memset(&new_data[ps->buffer_size], '\0', 
-	   (size_t)(new_size - ps->buffer_size));
-  }
-  ps->buffer_size = new_size;
-  ps->data_p = new_data;
-  
-  return true;
-}
-
-
-/*******************************************************************
- Align a the data_len to a multiple of align bytes - filling with
- zeros.
- ********************************************************************/
-bool prs_align(prs_struct *ps)
-{
-  uint32 mod = ps->data_offset & (ps->align-1);
-  
-  if (ps->align != 0 && mod != 0) 
-  {
-    uint32 extra_space = (ps->align - mod);
-    if(!prs_grow(ps, extra_space))
-      return false;
-    memset(&ps->data_p[ps->data_offset], '\0', (size_t)extra_space);
-    ps->data_offset += extra_space;
-  }
-  
-  return true;
-}
-
-
-/**
- * Initialise an expandable parse structure.
- *
- * @param size Initial buffer size.  If >0, a new buffer will be
- * created with malloc().
- *
- * @return false if allocation fails, otherwise true.
- **/
-
-bool prs_init(prs_struct *ps, uint32 size, void *ctx, bool io)
-{
-  if(ps == NULL)
-    return false;
-  memset(ps, 0, sizeof(prs_struct));
-
-  ps->io = io;
-  ps->bigendian_data = RPC_LITTLE_ENDIAN;
-  ps->align = RPC_PARSE_ALIGN;
-  ps->is_dynamic = false;
-  ps->data_offset = 0;
-  ps->buffer_size = 0;
-  ps->data_p = NULL;
-  ps->mem_ctx = ctx;
-  
-  if (size != 0) 
-  {
-    ps->buffer_size = size;
-    if((ps->data_p = (char *)zalloc((size_t)size)) == NULL)
-      return false;
-
-    ps->is_dynamic = true; /* We own this memory. */
-  }
-  
-  return true;
-}
-
-
-char *prs_mem_get(prs_struct *ps, uint32 extra_size)
-{
-  if(ps->io) 
-  {
-    /*
-     * If reading, ensure that we can read the requested size item.
-     */
-    if (ps->data_offset + extra_size > ps->buffer_size)
-      return NULL;
-  } 
-  else 
-  {
-    /*
-     * Writing - grow the buffer if needed.
-     */
-    if(!prs_grow(ps, extra_size))
-      return NULL;
-  }
-
-  return &ps->data_p[ps->data_offset];
-}
-
-
-/*******************************************************************
- Stream a uint32.
- ********************************************************************/
-bool prs_uint32(const char *name, prs_struct *ps, int depth, uint32 *data32)
-{
-  char *q = prs_mem_get(ps, sizeof(uint32));
-  if (q == NULL)
-    return false;
-  
-  if (ps->io) 
-  {
-    if (ps->bigendian_data)
-      *data32 = RIVAL(q,0);
-    else
-      *data32 = IVAL(q,0);
-  } 
-  else 
-  {
-    if (ps->bigendian_data)
-      RSIVAL(q,0,*data32);
-    else
-      SIVAL(q,0,*data32);
-  }
-  ps->data_offset += sizeof(uint32);
-  
-  return true;
-}
-
-
-/******************************************************************
- Stream an array of uint32s. Length is number of uint32s.
- ********************************************************************/
-bool prs_uint32s(const char *name, prs_struct *ps, 
-		 int depth, uint32 *data32s, int len)
-{
-  int i;
-  char *q = prs_mem_get(ps, len * sizeof(uint32));
-  if (q == NULL)
-    return false;
-  
-  if (ps->io) 
-  {
-    if (ps->bigendian_data) 
-    {
-      for (i = 0; i < len; i++)
-	data32s[i] = RIVAL(q, 4*i);
-    } 
-    else 
-    {
-      for (i = 0; i < len; i++)
-	data32s[i] = IVAL(q, 4*i);
-    }
-  } 
-  else 
-  {
-    if (ps->bigendian_data) 
-    {
-      for (i = 0; i < len; i++)
-	RSIVAL(q, 4*i, data32s[i]);
-    } 
-    else 
-    {
-      for (i = 0; i < len; i++)
-	SIVAL(q, 4*i, data32s[i]);
-    }
-  }
-  ps->data_offset += (len * sizeof(uint32));
-  
-  return true;
-}
-
-
-/*******************************************************************
- Stream a uint16.
- ********************************************************************/
-bool prs_uint16(const char *name, prs_struct *ps, int depth, uint16 *data16)
-{
-  char *q = prs_mem_get(ps, sizeof(uint16));
-  if (q == NULL)
-    return false;
-  
-  if (ps->io) 
-  {
-    if (ps->bigendian_data)
-      *data16 = RSVAL(q,0);
-    else
-      *data16 = SVAL(q,0);
-  } 
-  else 
-  {
-    if (ps->bigendian_data)
-      RSSVAL(q,0,*data16);
-    else
-      SSVAL(q,0,*data16);
-  }
-  ps->data_offset += sizeof(uint16);
-  
-  return true;
-}
-
-
-/*******************************************************************
- prs_uint16 wrapper. Call this and it sets up a pointer to where the
- uint16 should be stored, or gets the size if reading.
- ********************************************************************/
-bool prs_uint16_pre(const char *name, prs_struct *ps, int depth, 
-		    uint16 *data16, uint32 *offset)
-{
-  *offset = ps->data_offset;
-  if (ps->io) 
-  {
-    /* reading. */
-    return prs_uint16(name, ps, depth, data16);
-  } 
-  else 
-  {
-    char *q = prs_mem_get(ps, sizeof(uint16));
-    if(q ==NULL)
-      return false;
-    ps->data_offset += sizeof(uint16);
-  }
-  return true;
-}
-
-
-/*******************************************************************
- prs_uint16 wrapper.  call this and it retrospectively stores the size.
- does nothing on reading, as that is already handled by ...._pre()
- ********************************************************************/
-bool prs_uint16_post(const char *name, prs_struct *ps, int depth, 
-		     uint16 *data16, uint32 ptr_uint16, uint32 start_offset)
-{
-  if (!ps->io) 
-  {
-    /* 
-     * Writing - temporarily move the offset pointer.
-     */
-    uint16 data_size = ps->data_offset - start_offset;
-    uint32 old_offset = ps->data_offset;
-    
-    ps->data_offset = ptr_uint16;
-    if(!prs_uint16(name, ps, depth, &data_size)) 
-    {
-      ps->data_offset = old_offset;
-      return false;
-    }
-    ps->data_offset = old_offset;
-  } 
-  else 
-    ps->data_offset = start_offset + (uint32)(*data16);
-
-  return true;
-}
-
-
-/*******************************************************************
- Stream a uint8.
- ********************************************************************/
-bool prs_uint8(const char *name, prs_struct *ps, int depth, uint8 *data8)
-{
-  char *q = prs_mem_get(ps, 1);
-  if (q == NULL)
-    return false;
-  
-  if (ps->io)
-    *data8 = CVAL(q,0);
-  else
-    SCVAL(q,0,*data8);
-  
-  ps->data_offset += 1;
-  
-  return true;
-}
-
-
-/******************************************************************
- Stream an array of uint8s. Length is number of uint8s.
- ********************************************************************/
-bool prs_uint8s(const char *name, prs_struct *ps, int depth, 
-		uint8* data8s, int len)
-{
-  int i;
-  char *q = prs_mem_get(ps, len);
-  if (q == NULL)
-    return false;
-  
-  if (ps->io) 
-  {
-    for (i = 0; i < len; i++)
-      data8s[i] = CVAL(q,i);
-  } 
-  else 
-  {
-    for (i = 0; i < len; i++)
-      SCVAL(q, i, data8s[i]);
-  }
-  
-  ps->data_offset += len;
-  
-  return true;
-}
-
-
-/*******************************************************************
- Set the current offset (external interface).
- ********************************************************************/
-bool prs_set_offset(prs_struct *ps, uint32 offset)
-{
-  if(offset <= ps->data_offset) 
-  {
-    ps->data_offset = offset;
-    return true;
-  }
-  
-  if(!prs_grow(ps, offset - ps->data_offset))
-    return false;
-  
-  ps->data_offset = offset;
-  return true;
-}
-
-/* End of stuff from parse_prs.c */
-
-/* From rpc_parse/parse_misc.c */
-
-/*******************************************************************
- Reads or writes a struct uuid
-********************************************************************/
-bool smb_io_uuid(const char *desc, struct uuid *uuid, 
-		 prs_struct *ps, int depth)
-{
-  if (uuid == NULL)
-    return false;
-  depth++;
-  
-  if(!prs_uint32 ("data   ", ps, depth, &uuid->time_low))
-    return false;
-  if(!prs_uint16 ("data   ", ps, depth, &uuid->time_mid))
-    return false;
-  if(!prs_uint16 ("data   ", ps, depth, &uuid->time_hi_and_version))
-    return false;
-  
-  if(!prs_uint8s ("data   ", ps, depth, 
-		  uuid->clock_seq, sizeof(uuid->clock_seq)))
-    return false;
-
-  if(!prs_uint8s ("data   ", ps, depth, uuid->node, sizeof(uuid->node)))
-    return false;
-  
-  return true;
-}
-
-
-/*******************************************************************
- Reads or writes an NTTIME structure.
-********************************************************************/
-bool smb_io_time(const char *desc, NTTIME *nttime, prs_struct *ps, int depth)
-{
-  if (nttime == NULL)
-    return false;
-  depth++;
-
-  if(!prs_align(ps))
-    return false;
-	
-  if(!prs_uint32("low ", ps, depth, &nttime->low)) /* low part */
-    return false;
-  if(!prs_uint32("high", ps, depth, &nttime->high)) /* high part */
-    return false;
-
-  return true;
-}
-
-
-/*******************************************************************
- Reads or writes a DOM_SID structure.
-********************************************************************/
-bool smb_io_dom_sid(const char *desc, DOM_SID *sid, prs_struct *ps, int depth)
-{
-  int i;
-
-  if (sid == NULL)
-    return false;
-  depth++;
-
-  if(!prs_uint8 ("sid_rev_num", ps, depth, &sid->sid_rev_num))
-    return false;
-
-  if(!prs_uint8 ("num_auths  ", ps, depth, &sid->num_auths))
-    return false;
-
-  for (i = 0; i < 6; i++)
-  {
-    fstring tmp;
-    snprintf(tmp, sizeof(tmp) - 1, "id_auth[%d] ", i);
-    if(!prs_uint8 (tmp, ps, depth, &sid->id_auth[i]))
-      return false;
-  }
-
-  /* oops! XXXX should really issue a warning here... */
-  if (sid->num_auths > MAXSUBAUTHS)
-    sid->num_auths = MAXSUBAUTHS;
-
-  if(!prs_uint32s("sub_auths ", ps, depth, 
-		  sid->sub_auths, sid->num_auths))
-  { return false; }
-
-  return true;
-}
-
-/* End of stuff from rpc_parse/parse_misc.c */
-
-/* From lib/util_sid.c */
-
-/*****************************************************************
- Calculates size of a sid.
-*****************************************************************/  
-size_t sid_size(const DOM_SID *sid)
-{
-  if (sid == NULL)
-    return 0;
-
-  return sid->num_auths * sizeof(uint32) + 8;
-}
-
-
-/*****************************************************************
- Compare the auth portion of two sids.
-*****************************************************************/  
-int sid_compare_auth(const DOM_SID *sid1, const DOM_SID *sid2)
-{
-  int i;
-
-  if (sid1 == sid2)
-    return 0;
-  if (!sid1)
-    return -1;
-  if (!sid2)
-    return 1;
-
-  if (sid1->sid_rev_num != sid2->sid_rev_num)
-    return sid1->sid_rev_num - sid2->sid_rev_num;
-
-  for (i = 0; i < 6; i++)
-    if (sid1->id_auth[i] != sid2->id_auth[i])
-      return sid1->id_auth[i] - sid2->id_auth[i];
-
-  return 0;
-}
-
-
-/*****************************************************************
- Compare two sids.
-*****************************************************************/  
-int sid_compare(const DOM_SID *sid1, const DOM_SID *sid2)
-{
-  int i;
-
-  if (sid1 == sid2)
-    return 0;
-  if (!sid1)
-    return -1;
-  if (!sid2)
-    return 1;
-
-  /* Compare most likely different rids, first: i.e start at end */
-  if (sid1->num_auths != sid2->num_auths)
-    return sid1->num_auths - sid2->num_auths;
-
-  for (i = sid1->num_auths-1; i >= 0; --i)
-    if (sid1->sub_auths[i] != sid2->sub_auths[i])
-      return sid1->sub_auths[i] - sid2->sub_auths[i];
-
-  return sid_compare_auth(sid1, sid2);
-}
-
-
-/*****************************************************************
- Compare two sids.
-*****************************************************************/  
-bool sid_equal(const DOM_SID *sid1, const DOM_SID *sid2)
-{
-  return sid_compare(sid1, sid2) == 0;
-}
-
-/* End of stuff from lib/util_sid.c */
-
-/* From lib/secace.c */
-
-/*******************************************************************
- Check if ACE has OBJECT type.
-********************************************************************/
-
-bool sec_ace_object(uint8 type)
-{
-  if (type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
-      type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT ||
-      type == SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT ||
-      type == SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT) {
-    return true;
-  }
-  return false;
-}
-
-/* End of stuff from lib/secace.c */
-
-/* From rpc_parse/parse_sec.c */
-
-/*******************************************************************
- Reads or writes a SEC_ACCESS structure.
-********************************************************************/
-bool sec_io_access(const char *desc, SEC_ACCESS *t, prs_struct *ps, int depth)
-{
-  if (t == NULL)
-    return false;
-
-  depth++;
-	
-  if(!prs_uint32("mask", ps, depth, &t->mask))
-    return false;
-
-  return true;
-}
-
-
-/*******************************************************************
- Reads or writes a SEC_ACE structure.
-********************************************************************/
-bool sec_io_ace(const char *desc, SEC_ACE *psa, prs_struct *ps, int depth)
-{
-  uint32 old_offset;
-  uint32 offset_ace_size;
-
-  if (psa == NULL)
-    return false;
-
-  depth++;
-	
-  old_offset = ps->data_offset;
-
-  if(!prs_uint8("type ", ps, depth, &psa->type))
-    return false;
-
-  if(!prs_uint8("flags", ps, depth, &psa->flags))
-    return false;
-
-  if(!prs_uint16_pre("size ", ps, depth, &psa->size, &offset_ace_size))
-    return false;
-
-  if(!sec_io_access("info ", &psa->info, ps, depth))
-    return false;
-
-  /* check whether object access is present */
-  if (!sec_ace_object(psa->type)) 
-  {
-    if (!smb_io_dom_sid("trustee  ", &psa->trustee , ps, depth))
-      return false;
-  } 
-  else 
-  {
-    if (!prs_uint32("obj_flags", ps, depth, &psa->obj_flags))
-      return false;
-
-    if (psa->obj_flags & SEC_ACE_OBJECT_PRESENT)
-      if (!smb_io_uuid("obj_guid", &psa->obj_guid, ps,depth))
-	return false;
-
-    if (psa->obj_flags & SEC_ACE_OBJECT_INHERITED_PRESENT)
-      if (!smb_io_uuid("inh_guid", &psa->inh_guid, ps,depth))
-	return false;
-
-    if(!smb_io_dom_sid("trustee  ", &psa->trustee , ps, depth))
-      return false;
-  }
-
-  /* Theorectically an ACE can have a size greater than the
-   * sum of its components. When marshalling, pad with extra null bytes 
-   * up to the
-   * correct size. 
-   */
-  if (!ps->io && (psa->size > ps->data_offset - old_offset)) 
-  {
-    uint32 extra_len = psa->size - (ps->data_offset - old_offset);
-    uint32 i;
-    uint8 c = 0;
-
-    for (i = 0; i < extra_len; i++) 
-    {
-      if (!prs_uint8("ace extra space", ps, depth, &c))
-	return false;
-    }
-  }
-
-  if(!prs_uint16_post("size ", ps, depth, &psa->size, 
-		      offset_ace_size, old_offset))
-  { return false; }
-
-  return true;
-}
-
-
-/*******************************************************************
- Reads or writes a SEC_ACL structure.  
-
- First of the xx_io_xx functions that allocates its data structures
- for you as it reads them.
-********************************************************************/
-bool sec_io_acl(const char *desc, SEC_ACL **ppsa, prs_struct *ps, int depth)
-{
-  unsigned int i;
-  uint32 old_offset;
-  uint32 offset_acl_size;
-  SEC_ACL* psa;
-
-  /*
-   * Note that the size is always a multiple of 4 bytes due to the
-   * nature of the data structure.  Therefore the prs_align() calls
-   * have been removed as they through us off when doing two-layer
-   * marshalling such as in the printing code (RPC_BUFFER).  --jerry
-   */
-
-  if (ppsa == NULL || ps == NULL)
-    return false;
-
-  psa = *ppsa;
-
-  if(ps->io && psa == NULL) 
-  {
-    /*
-     * This is a read and we must allocate the stuct to read into.
-     */
-    if((psa = (SEC_ACL*)zalloc(sizeof(SEC_ACL))) == NULL)
-      return false;
-    *ppsa = psa;
-  }
-
-  depth++;	
-  old_offset = ps->data_offset;
-
-  if(!prs_uint16("revision", ps, depth, &psa->revision)
-     || !prs_uint16_pre("size     ", ps, depth, &psa->size, &offset_acl_size)
-     || !prs_uint32("num_aces ", ps, depth, &psa->num_aces))
-  {
-    free(psa);
-    *ppsa = NULL;
-    return false;
-  }
-
-  if (ps->io) 
-  {
-    /*
-     * Even if the num_aces is zero, allocate memory as there's a difference
-     * between a non-present DACL (allow all access) and a DACL with no ACE's
-     * (allow no access).
-     */
-    if((psa->ace = (SEC_ACE*)zcalloc(sizeof(SEC_ACE), psa->num_aces+1)) == NULL)
-    {
-      free(psa);
-      *ppsa = NULL;
-      return false;
-    }
-  }
-
-  for (i = 0; i < psa->num_aces; i++) 
-  {
-    fstring tmp;
-    snprintf(tmp, sizeof(tmp)-1, "ace_list[%02d]: ", i);
-    if(!sec_io_ace(tmp, &psa->ace[i], ps, depth))
-    {
-      free(psa);
-      *ppsa = NULL;
-      return false;
-    }
-  }
-
-  /* Theoretically an ACL can have a size greater than the
-   *  sum of its components. When marshalling, pad with extra null 
-   *  bytes up to the
-   *  correct size. 
-   */
-  if (!ps->io && (psa->size > ps->data_offset - old_offset)) 
-  {
-    uint32 extra_len = psa->size - (ps->data_offset - old_offset);
-    uint8 c = 0;
-
-    for (i = 0; i < extra_len; i++) 
-    {
-      if (!prs_uint8("acl extra space", ps, depth, &c))
-      {
-	free(psa);
-	*ppsa = NULL;
-	return false;
-      }
-    }
-  }
-
-  if(!prs_uint16_post("size     ", ps, depth, &psa->size, 
-		      offset_acl_size, old_offset))
-  { 
-    free(psa);
-    *ppsa = NULL;
-    return false; 
-  }
-
-  return true;
-}
-
-
-/*******************************************************************
- Reads or writes a SEC_DESC structure.
- If reading and the *ppsd = NULL, allocates the structure.
-********************************************************************/
-bool sec_io_desc(const char *desc, SEC_DESC **ppsd, prs_struct *ps, int depth)
-{
-  uint32 old_offset;
-  uint32 max_offset = 0; /* after we're done, move offset to end */
-  uint32 tmp_offset = 0;
-
-  SEC_DESC *psd;
-
-  if (ppsd == NULL || ps == NULL)
-    return false;
-
-  psd = *ppsd;
-  if (psd == NULL) 
-  {
-    if(ps->io) 
-    {
-      if((psd = (SEC_DESC*)zalloc(sizeof(SEC_DESC))) == NULL)
-	return false;
-      *ppsd = psd;
-    } 
-    else 
-    {
-      /* Marshalling - just ignore. */
-      return true;
-    }
-  }
-
-  depth++;
-
-  /* start of security descriptor stored for back-calc offset purposes */
-  old_offset = ps->data_offset;
-
-  if(!prs_uint16("revision ", ps, depth, &psd->revision)
-     || !prs_uint16("type     ", ps, depth, &psd->type))
-  {
-    free(psd);
-    *ppsd = NULL;
-    return false;
-  }
-
-  if (!ps->io)
-  {
-    uint32 offset = SEC_DESC_HEADER_SIZE;
-
-    /*
-     * Work out the offsets here, as we write it out.
-     */
-
-    if (psd->sacl != NULL) 
-    {
-      psd->off_sacl = offset;
-      offset += psd->sacl->size;
-    } 
-    else
-      psd->off_sacl = 0;
-
-    if (psd->dacl != NULL) 
-    {
-      psd->off_dacl = offset;
-      offset += psd->dacl->size;
-    } 
-    else 
-      psd->off_dacl = 0;
-
-    if (psd->owner_sid != NULL) 
-    {
-      psd->off_owner_sid = offset;
-      offset += sid_size(psd->owner_sid);
-    } 
-    else
-      psd->off_owner_sid = 0;
-
-    if (psd->grp_sid != NULL) 
-    {
-      psd->off_grp_sid = offset;
-      offset += sid_size(psd->grp_sid);
-    } 
-    else
-      psd->off_grp_sid = 0;
-  }
-
-  if(!prs_uint32("off_owner_sid", ps, depth, &psd->off_owner_sid)
-     || !prs_uint32("off_grp_sid  ", ps, depth, &psd->off_grp_sid)
-     || !prs_uint32("off_sacl     ", ps, depth, &psd->off_sacl)
-     || !prs_uint32("off_dacl     ", ps, depth, &psd->off_dacl))
-  {
-    free(psd);
-    *ppsd = NULL;    
-    return false;
-  }
-  max_offset = MAX(max_offset, ps->data_offset);
-
-  if (psd->off_owner_sid != 0) 
-  {
-    tmp_offset = ps->data_offset;
-    if(!prs_set_offset(ps, old_offset + psd->off_owner_sid))
-    {
-      free(psd);
-      *ppsd = NULL;
-      return false;
-    }
-
-    if (ps->io) 
-    {
-      /* reading */
-      if((psd->owner_sid = (DOM_SID*)zalloc(sizeof(DOM_SID))) == NULL)
-      {
-	free(psd);
-	*ppsd = NULL;
-	return false;
-      }
-    }
-
-    if(!smb_io_dom_sid("owner_sid ", psd->owner_sid , ps, depth))
-    {
-      if(ps->io)
-	free(psd->owner_sid);
-      free(psd);
-      *ppsd = NULL;
-      return false;
-    }
-
-    max_offset = MAX(max_offset, ps->data_offset);
-
-    if (!prs_set_offset(ps,tmp_offset))
-    {
-      if(ps->io)
-	free(psd->owner_sid);
-      free(psd);
-      *ppsd = NULL;
-      return false;
-    }
-  }
-
-  if (psd->off_grp_sid != 0) 
-  {
-    tmp_offset = ps->data_offset;
-    if(!prs_set_offset(ps, old_offset + psd->off_grp_sid))
-    {
-      if(ps->io)
-	free(psd->owner_sid);
-      free(psd);
-      *ppsd = NULL;
-      return false;
-    }
-
-    if (ps->io) 
-    {
-      /* reading */
-      if((psd->grp_sid = (DOM_SID*)zalloc(sizeof(DOM_SID))) == NULL)
-      {
-	free(psd->owner_sid);
-	free(psd);
-	*ppsd = NULL;
-	return false;
-      }
-    }
-
-    if(!smb_io_dom_sid("grp_sid", psd->grp_sid, ps, depth))
-    {
-      if(ps->io)
-      {
-	free(psd->grp_sid);
-	free(psd->owner_sid);
-      }
-      free(psd);
-      *ppsd = NULL;
-      return false;
-    }
-			
-    max_offset = MAX(max_offset, ps->data_offset);
-
-    if (!prs_set_offset(ps,tmp_offset))
-    {
-      if(ps->io)
-      {
-	free(psd->grp_sid);
-	free(psd->owner_sid);
-      }
-      free(psd);
-      *ppsd = NULL;
-      return false;
-    }
-  }
-
-  if ((psd->type & SEC_DESC_SACL_PRESENT) && psd->off_sacl) 
-  {
-    tmp_offset = ps->data_offset;
-    if(!prs_set_offset(ps, old_offset + psd->off_sacl)
-       || !sec_io_acl("sacl", &psd->sacl, ps, depth))
-    {
-      if(ps->io)
-      {
-	free(psd->grp_sid);
-	free(psd->owner_sid);
-      }
-      free(psd);
-      *ppsd = NULL;
-      return false;
-    }
-    max_offset = MAX(max_offset, ps->data_offset);
-    if (!prs_set_offset(ps,tmp_offset))
-    {
-      if(ps->io)
-      {
-	free(psd->grp_sid);
-	free(psd->owner_sid);
-      }
-      free(psd);
-      *ppsd = NULL;
-      return false;
-    }
-  }
-
-  if ((psd->type & SEC_DESC_DACL_PRESENT) && psd->off_dacl != 0) 
-  {
-    tmp_offset = ps->data_offset;
-    if(!prs_set_offset(ps, old_offset + psd->off_dacl)
-       || !sec_io_acl("dacl", &psd->dacl, ps, depth))
-    {
-      if(ps->io)
-      {
-	free(psd->grp_sid);
-	free(psd->owner_sid);
-      }
-      free(psd);
-      *ppsd = NULL;
-      return false;
-    }
-    max_offset = MAX(max_offset, ps->data_offset);
-    if (!prs_set_offset(ps,tmp_offset))
-    {
-      if(ps->io)
-      {
-	free(psd->grp_sid);
-	free(psd->owner_sid);
-      }
-      free(psd);
-      *ppsd = NULL;
-      return false;
-    }
-  }
-
-  if(!prs_set_offset(ps, max_offset))
-  {
-      if(ps->io)
-      {
-	free(psd->grp_sid);
-	free(psd->owner_sid);
-      }
-      free(psd);
-      *ppsd = NULL;
-      return false;
-  }
-
-  return true;
-}
-
-/* End of stuff from rpc_parse/parse_sec.c */
-
-/* From lib/secace.c */
-
-/*******************************************************************
- Compares two SEC_ACE structures
-********************************************************************/
-bool sec_ace_equal(SEC_ACE *s1, SEC_ACE *s2)
-{
-  /* Trivial cases */
-  if (!s1 && !s2) 
-    return true;
-  if (!s1 || !s2) 
-    return false;
-
-  /* Check top level stuff */
-  if (s1->type != s2->type || s1->flags != s2->flags ||
-      s1->info.mask != s2->info.mask) 
-  { return false; }
-
-  /* Check SID */
-  if (!sid_equal(&s1->trustee, &s2->trustee))
-    return false;
-
-  return true;
-}
-
-/* End of stuff from lib/secace.c */
-
-/* From lib/secacl.c */
-
-/*******************************************************************
- Compares two SEC_ACL structures
-********************************************************************/
-
-bool sec_acl_equal(SEC_ACL *s1, SEC_ACL *s2)
-{
-  unsigned int i, j;
-
-  /* Trivial cases */
-  if (!s1 && !s2) 
-    return true;
-  if (!s1 || !s2) 
-    return false;
-
-  /* Check top level stuff */
-  if (s1->revision != s2->revision)
-    return false;
-
-  if (s1->num_aces != s2->num_aces)
-    return false;
-
-  /* The ACEs could be in any order so check each ACE in s1 against 
-     each ACE in s2. */
-
-  for (i = 0; i < s1->num_aces; i++) 
-  {
-    bool found = false;
-
-    for (j = 0; j < s2->num_aces; j++) 
-    {
-      if (sec_ace_equal(&s1->ace[i], &s2->ace[j])) 
-      {
-	found = true;
-	break;
-      }
-    }
-
-    if (!found)
-      return false;
-  }
-
-  return true;
-}
-
-/* End of stuff from lib/secacl.c */
-
-/* From lib/secdesc.c */
-
-/*******************************************************************
- Compares two SEC_DESC structures
-********************************************************************/
-bool sec_desc_equal(SEC_DESC *s1, SEC_DESC *s2)
-{
-  /* Trivial cases */
-  if (!s1 && !s2)
-    return true;
-  if (!s1 || !s2)
-    return false;
-
-  /* Check top level stuff */
-  if (s1->revision != s2->revision)
-    return false;
-
-  if (s1->type!= s2->type)
-    return false;
-
-  /* Check owner and group */
-  if (!sid_equal(s1->owner_sid, s2->owner_sid))
-    return false;
-
-  if (!sid_equal(s1->grp_sid, s2->grp_sid)) 
-    return false;
-
-  /* Check ACLs present in one but not the other */
-  if ((s1->dacl && !s2->dacl) || (!s1->dacl && s2->dacl) ||
-      (s1->sacl && !s2->sacl) || (!s1->sacl && s2->sacl)) 
-  { return false; }
-
-  /* Sigh - we have to do it the hard way by iterating over all
-     the ACEs in the ACLs */
-  if(!sec_acl_equal(s1->dacl, s2->dacl) || !sec_acl_equal(s1->sacl, s2->sacl)) 
-    return false;
-
-  return true;
-}
-
-/* End of stuff from lib/secdesc.c */
diff --git a/lib/talloc.c b/lib/talloc.c
new file mode 100644
index 0000000..faa4378
--- /dev/null
+++ b/lib/talloc.c
@@ -0,0 +1,1696 @@
+/* 
+   Samba Unix SMB/CIFS implementation.
+
+   Samba trivial allocation library - new interface
+     inspired by http://swapped.cc/halloc/
+
+   NOTE: Please read talloc_guide.txt for full documentation
+
+   Copyright (C) Andrew Tridgell 2004
+   Copyright (C) Stefan Metzmacher 2006
+   Copyright (C) Timothy D. Morgan 2009 
+ 
+     ** NOTE! The following LGPL license applies to the talloc library.
+     ** This does NOT imply that all of reglookup is released under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 3 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+/* $Id: talloc.c 147 2009-02-22 19:31:52Z tim $ */
+
+#include "talloc.h"
+
+/* use this to force every realloc to change the pointer, to stress test
+   code that might not cope */
+#define ALWAYS_REALLOC 0
+
+
+#define MAX_TALLOC_SIZE 0x10000000
+#define TALLOC_MAGIC 0xe814ec70
+#define TALLOC_FLAG_FREE 0x01
+#define TALLOC_FLAG_LOOP 0x02
+#define TALLOC_FLAG_POOL 0x04		/* This is a talloc pool */
+#define TALLOC_FLAG_POOLMEM 0x08	/* This is allocated in a pool */
+#define TALLOC_MAGIC_REFERENCE ((const char *)1)
+
+/* by default we abort when given a bad pointer (such as when talloc_free() is called 
+   on a pointer that came from malloc() */
+#ifndef TALLOC_ABORT
+#define TALLOC_ABORT(reason) abort()
+#endif
+
+#ifndef discard_const_p
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+# define discard_const_p(type, ptr) ((type *)((intptr_t)(ptr)))
+#else
+# define discard_const_p(type, ptr) ((type *)(ptr))
+#endif
+#endif
+
+/* these macros gain us a few percent of speed on gcc */
+#if (__GNUC__ >= 3)
+/* the strange !! is to ensure that __builtin_expect() takes either 0 or 1
+   as its first argument */
+#define likely(x)   __builtin_expect(!!(x), 1)
+#define unlikely(x) __builtin_expect(!!(x), 0)
+#else
+#define likely(x) x
+#define unlikely(x) x
+#endif
+
+/* this null_context is only used if talloc_enable_leak_report() or
+   talloc_enable_leak_report_full() is called, otherwise it remains
+   NULL
+*/
+static void *null_context;
+static void *autofree_context;
+
+struct talloc_reference_handle {
+	struct talloc_reference_handle *next, *prev;
+	void *ptr;
+};
+
+typedef int (*talloc_destructor_t)(void *);
+
+struct talloc_chunk {
+	struct talloc_chunk *next, *prev;
+	struct talloc_chunk *parent, *child;
+	struct talloc_reference_handle *refs;
+	talloc_destructor_t destructor;
+	const char *name;
+	size_t size;
+	unsigned flags;
+
+	/*
+	 * "pool" has dual use:
+	 *
+	 * For the talloc pool itself (i.e. TALLOC_FLAG_POOL is set), "pool"
+	 * marks the end of the currently allocated area.
+	 *
+	 * For members of the pool (i.e. TALLOC_FLAG_POOLMEM is set), "pool"
+	 * is a pointer to the struct talloc_chunk of the pool that it was
+	 * allocated from. This way children can quickly find the pool to chew
+	 * from.
+	 */
+	void *pool;
+};
+
+/* 16 byte alignment seems to keep everyone happy */
+#define TC_HDR_SIZE ((sizeof(struct talloc_chunk)+15)&~15)
+#define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc))
+
+static void talloc_abort_double_free(void)
+{
+	TALLOC_ABORT("Bad talloc magic value - double free"); 
+}
+
+static void talloc_abort_unknown_value(void)
+{
+	TALLOC_ABORT("Bad talloc magic value - unknown value"); 
+}
+
+/* panic if we get a bad magic value */
+static inline struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr)
+{
+	const char *pp = (const char *)ptr;
+	struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, pp - TC_HDR_SIZE);
+	if (unlikely((tc->flags & (TALLOC_FLAG_FREE | ~0xF)) != TALLOC_MAGIC)) { 
+		if (tc->flags & TALLOC_FLAG_FREE) {
+			talloc_abort_double_free();
+		} else {
+			talloc_abort_unknown_value();
+		}
+	}
+	return tc;
+}
+
+/* hook into the front of the list */
+#define _TLIST_ADD(list, p) \
+do { \
+        if (!(list)) { \
+		(list) = (p); \
+		(p)->next = (p)->prev = NULL; \
+	} else { \
+		(list)->prev = (p); \
+		(p)->next = (list); \
+		(p)->prev = NULL; \
+		(list) = (p); \
+	}\
+} while (0)
+
+/* remove an element from a list - element doesn't have to be in list. */
+#define _TLIST_REMOVE(list, p) \
+do { \
+	if ((p) == (list)) { \
+		(list) = (p)->next; \
+		if (list) (list)->prev = NULL; \
+	} else { \
+		if ((p)->prev) (p)->prev->next = (p)->next; \
+		if ((p)->next) (p)->next->prev = (p)->prev; \
+	} \
+	if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \
+} while (0)
+
+
+/*
+  return the parent chunk of a pointer
+*/
+static inline struct talloc_chunk *talloc_parent_chunk(const void *ptr)
+{
+	struct talloc_chunk *tc;
+
+	if (unlikely(ptr == NULL)) {
+		return NULL;
+	}
+
+	tc = talloc_chunk_from_ptr(ptr);
+	while (tc->prev) tc=tc->prev;
+
+	return tc->parent;
+}
+
+void *talloc_parent(const void *ptr)
+{
+	struct talloc_chunk *tc = talloc_parent_chunk(ptr);
+	return tc? TC_PTR_FROM_CHUNK(tc) : NULL;
+}
+
+/*
+  find parents name
+*/
+const char *talloc_parent_name(const void *ptr)
+{
+	struct talloc_chunk *tc = talloc_parent_chunk(ptr);
+	return tc? tc->name : NULL;
+}
+
+/*
+  A pool carries an in-pool object count count in the first 16 bytes.
+  bytes. This is done to support talloc_steal() to a parent outside of the
+  pool. The count includes the pool itself, so a talloc_free() on a pool will
+  only destroy the pool if the count has dropped to zero. A talloc_free() of a
+  pool member will reduce the count, and eventually also call free(3) on the
+  pool memory.
+
+  The object count is not put into "struct talloc_chunk" because it is only
+  relevant for talloc pools and the alignment to 16 bytes would increase the
+  memory footprint of each talloc chunk by those 16 bytes.
+*/
+
+#define TALLOC_POOL_HDR_SIZE 16
+
+static unsigned int *talloc_pool_objectcount(struct talloc_chunk *tc)
+{
+	return (unsigned int *)((char *)tc + sizeof(struct talloc_chunk));
+}
+
+/*
+  Allocate from a pool
+*/
+
+static struct talloc_chunk *talloc_alloc_pool(struct talloc_chunk *parent,
+					      size_t size)
+{
+	struct talloc_chunk *pool_ctx = NULL;
+	size_t space_left;
+	struct talloc_chunk *result;
+	size_t chunk_size;
+
+	if (parent == NULL) {
+		return NULL;
+	}
+
+	if (parent->flags & TALLOC_FLAG_POOL) {
+		pool_ctx = parent;
+	}
+	else if (parent->flags & TALLOC_FLAG_POOLMEM) {
+		pool_ctx = (struct talloc_chunk *)parent->pool;
+	}
+
+	if (pool_ctx == NULL) {
+		return NULL;
+	}
+
+	space_left = ((char *)pool_ctx + TC_HDR_SIZE + pool_ctx->size)
+		- ((char *)pool_ctx->pool);
+
+	/*
+	 * Align size to 16 bytes
+	 */
+	chunk_size = ((size + 15) & ~15);
+
+	if (space_left < chunk_size) {
+		return NULL;
+	}
+
+	result = (struct talloc_chunk *)pool_ctx->pool;
+
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED)
+	VALGRIND_MAKE_MEM_UNDEFINED(result, size);
+#endif
+
+	pool_ctx->pool = (void *)((char *)result + chunk_size);
+
+	result->flags = TALLOC_MAGIC | TALLOC_FLAG_POOLMEM;
+	result->pool = pool_ctx;
+
+	*talloc_pool_objectcount(pool_ctx) += 1;
+
+	return result;
+}
+
+/* 
+   Allocate a bit of memory as a child of an existing pointer
+*/
+static inline void *__talloc(const void *context, size_t size)
+{
+	struct talloc_chunk *tc = NULL;
+
+	if (unlikely(context == NULL)) {
+		context = null_context;
+	}
+
+	if (unlikely(size >= MAX_TALLOC_SIZE)) {
+		return NULL;
+	}
+
+	if (context != NULL) {
+		tc = talloc_alloc_pool(talloc_chunk_from_ptr(context),
+				       TC_HDR_SIZE+size);
+	}
+
+	if (tc == NULL) {
+		tc = (struct talloc_chunk *)malloc(TC_HDR_SIZE+size);
+		if (unlikely(tc == NULL)) return NULL;
+		tc->flags = TALLOC_MAGIC;
+		tc->pool  = NULL;
+	}
+
+	tc->size = size;
+	tc->destructor = NULL;
+	tc->child = NULL;
+	tc->name = NULL;
+	tc->refs = NULL;
+
+	if (likely(context)) {
+		struct talloc_chunk *parent = talloc_chunk_from_ptr(context);
+
+		if (parent->child) {
+			parent->child->parent = NULL;
+			tc->next = parent->child;
+			tc->next->prev = tc;
+		} else {
+			tc->next = NULL;
+		}
+		tc->parent = parent;
+		tc->prev = NULL;
+		parent->child = tc;
+	} else {
+		tc->next = tc->prev = tc->parent = NULL;
+	}
+
+	return TC_PTR_FROM_CHUNK(tc);
+}
+
+/*
+ * Create a talloc pool
+ */
+
+void *talloc_pool(const void *context, size_t size)
+{
+	void *result = __talloc(context, size + TALLOC_POOL_HDR_SIZE);
+	struct talloc_chunk *tc;
+
+	if (unlikely(result == NULL)) {
+		return NULL;
+	}
+
+	tc = talloc_chunk_from_ptr(result);
+
+	tc->flags |= TALLOC_FLAG_POOL;
+	tc->pool = (char *)result + TALLOC_POOL_HDR_SIZE;
+
+	*talloc_pool_objectcount(tc) = 1;
+
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS)
+	VALGRIND_MAKE_MEM_NOACCESS(tc->pool, size);
+#endif
+
+	return result;
+}
+
+/*
+  setup a destructor to be called on free of a pointer
+  the destructor should return 0 on success, or -1 on failure.
+  if the destructor fails then the free is failed, and the memory can
+  be continued to be used
+*/
+void _talloc_set_destructor(const void *ptr, int (*destructor)(void *))
+{
+	struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+	tc->destructor = destructor;
+}
+
+/*
+  increase the reference count on a piece of memory. 
+*/
+int talloc_increase_ref_count(const void *ptr)
+{
+	if (unlikely(!talloc_reference(null_context, ptr))) {
+		return -1;
+	}
+	return 0;
+}
+
+/*
+  helper for talloc_reference()
+
+  this is referenced by a function pointer and should not be inline
+*/
+static int talloc_reference_destructor(struct talloc_reference_handle *handle)
+{
+	struct talloc_chunk *ptr_tc = talloc_chunk_from_ptr(handle->ptr);
+	_TLIST_REMOVE(ptr_tc->refs, handle);
+	return 0;
+}
+
+/*
+   more efficient way to add a name to a pointer - the name must point to a 
+   true string constant
+*/
+static inline void _talloc_set_name_const(const void *ptr, const char *name)
+{
+	struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+	tc->name = name;
+}
+
+/*
+  internal talloc_named_const()
+*/
+static inline void *_talloc_named_const(const void *context, size_t size, const char *name)
+{
+	void *ptr;
+
+	ptr = __talloc(context, size);
+	if (unlikely(ptr == NULL)) {
+		return NULL;
+	}
+
+	_talloc_set_name_const(ptr, name);
+
+	return ptr;
+}
+
+/*
+  make a secondary reference to a pointer, hanging off the given context.
+  the pointer remains valid until both the original caller and this given
+  context are freed.
+  
+  the major use for this is when two different structures need to reference the 
+  same underlying data, and you want to be able to free the two instances separately,
+  and in either order
+*/
+void *_talloc_reference(const void *context, const void *ptr)
+{
+	struct talloc_chunk *tc;
+	struct talloc_reference_handle *handle;
+	if (unlikely(ptr == NULL)) return NULL;
+
+	tc = talloc_chunk_from_ptr(ptr);
+	handle = (struct talloc_reference_handle *)_talloc_named_const(context,
+						   sizeof(struct talloc_reference_handle),
+						   TALLOC_MAGIC_REFERENCE);
+	if (unlikely(handle == NULL)) return NULL;
+
+	/* note that we hang the destructor off the handle, not the
+	   main context as that allows the caller to still setup their
+	   own destructor on the context if they want to */
+	talloc_set_destructor(handle, talloc_reference_destructor);
+	handle->ptr = discard_const_p(void, ptr);
+	_TLIST_ADD(tc->refs, handle);
+	return handle->ptr;
+}
+
+
+/* 
+   internal talloc_free call
+*/
+static inline int _talloc_free(void *ptr)
+{
+	struct talloc_chunk *tc;
+
+	if (unlikely(ptr == NULL)) {
+		return -1;
+	}
+
+	tc = talloc_chunk_from_ptr(ptr);
+
+	if (unlikely(tc->refs)) {
+		int is_child;
+		/* check this is a reference from a child or grantchild
+		 * back to it's parent or grantparent
+		 *
+		 * in that case we need to remove the reference and
+		 * call another instance of talloc_free() on the current
+		 * pointer.
+		 */
+		is_child = talloc_is_parent(tc->refs, ptr);
+		_talloc_free(tc->refs);
+		if (is_child) {
+			return _talloc_free(ptr);
+		}
+		return -1;
+	}
+
+	if (unlikely(tc->flags & TALLOC_FLAG_LOOP)) {
+		/* we have a free loop - stop looping */
+		return 0;
+	}
+
+	if (unlikely(tc->destructor)) {
+		talloc_destructor_t d = tc->destructor;
+		if (d == (talloc_destructor_t)-1) {
+			return -1;
+		}
+		tc->destructor = (talloc_destructor_t)-1;
+		if (d(ptr) == -1) {
+			tc->destructor = d;
+			return -1;
+		}
+		tc->destructor = NULL;
+	}
+
+	if (tc->parent) {
+		_TLIST_REMOVE(tc->parent->child, tc);
+		if (tc->parent->child) {
+			tc->parent->child->parent = tc->parent;
+		}
+	} else {
+		if (tc->prev) tc->prev->next = tc->next;
+		if (tc->next) tc->next->prev = tc->prev;
+	}
+
+	tc->flags |= TALLOC_FLAG_LOOP;
+
+	while (tc->child) {
+		/* we need to work out who will own an abandoned child
+		   if it cannot be freed. In priority order, the first
+		   choice is owner of any remaining reference to this
+		   pointer, the second choice is our parent, and the
+		   final choice is the null context. */
+		void *child = TC_PTR_FROM_CHUNK(tc->child);
+		const void *new_parent = null_context;
+		if (unlikely(tc->child->refs)) {
+			struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
+			if (p) new_parent = TC_PTR_FROM_CHUNK(p);
+		}
+		if (unlikely(_talloc_free(child) == -1)) {
+			if (new_parent == null_context) {
+				struct talloc_chunk *p = talloc_parent_chunk(ptr);
+				if (p) new_parent = TC_PTR_FROM_CHUNK(p);
+			}
+			talloc_steal(new_parent, child);
+		}
+	}
+
+	tc->flags |= TALLOC_FLAG_FREE;
+
+	if (tc->flags & (TALLOC_FLAG_POOL|TALLOC_FLAG_POOLMEM)) {
+		struct talloc_chunk *pool;
+		unsigned int *pool_object_count;
+
+		pool = (tc->flags & TALLOC_FLAG_POOL)
+			? tc : (struct talloc_chunk *)tc->pool;
+
+		pool_object_count = talloc_pool_objectcount(pool);
+
+		if (*pool_object_count == 0) {
+			TALLOC_ABORT("Pool object count zero!");
+		}
+
+		*pool_object_count -= 1;
+
+		if (*pool_object_count == 0) {
+			free(pool);
+		}
+	}
+	else {
+		free(tc);
+	}
+	return 0;
+}
+
+/* 
+   move a lump of memory from one talloc context to another return the
+   ptr on success, or NULL if it could not be transferred.
+   passing NULL as ptr will always return NULL with no side effects.
+*/
+void *_talloc_steal(const void *new_ctx, const void *ptr)
+{
+	struct talloc_chunk *tc, *new_tc;
+
+	if (unlikely(!ptr)) {
+		return NULL;
+	}
+
+	if (unlikely(new_ctx == NULL)) {
+		new_ctx = null_context;
+	}
+
+	tc = talloc_chunk_from_ptr(ptr);
+
+	if (unlikely(new_ctx == NULL)) {
+		if (tc->parent) {
+			_TLIST_REMOVE(tc->parent->child, tc);
+			if (tc->parent->child) {
+				tc->parent->child->parent = tc->parent;
+			}
+		} else {
+			if (tc->prev) tc->prev->next = tc->next;
+			if (tc->next) tc->next->prev = tc->prev;
+		}
+		
+		tc->parent = tc->next = tc->prev = NULL;
+		return discard_const_p(void, ptr);
+	}
+
+	new_tc = talloc_chunk_from_ptr(new_ctx);
+
+	if (unlikely(tc == new_tc || tc->parent == new_tc)) {
+		return discard_const_p(void, ptr);
+	}
+
+	if (tc->parent) {
+		_TLIST_REMOVE(tc->parent->child, tc);
+		if (tc->parent->child) {
+			tc->parent->child->parent = tc->parent;
+		}
+	} else {
+		if (tc->prev) tc->prev->next = tc->next;
+		if (tc->next) tc->next->prev = tc->prev;
+	}
+
+	tc->parent = new_tc;
+	if (new_tc->child) new_tc->child->parent = NULL;
+	_TLIST_ADD(new_tc->child, tc);
+
+	return discard_const_p(void, ptr);
+}
+
+
+
+/*
+  remove a secondary reference to a pointer. This undo's what
+  talloc_reference() has done. The context and pointer arguments
+  must match those given to a talloc_reference()
+*/
+static inline int talloc_unreference(const void *context, const void *ptr)
+{
+	struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+	struct talloc_reference_handle *h;
+
+	if (unlikely(context == NULL)) {
+		context = null_context;
+	}
+
+	for (h=tc->refs;h;h=h->next) {
+		struct talloc_chunk *p = talloc_parent_chunk(h);
+		if (p == NULL) {
+			if (context == NULL) break;
+		} else if (TC_PTR_FROM_CHUNK(p) == context) {
+			break;
+		}
+	}
+	if (h == NULL) {
+		return -1;
+	}
+
+	return _talloc_free(h);
+}
+
+/*
+  remove a specific parent context from a pointer. This is a more
+  controlled varient of talloc_free()
+*/
+int talloc_unlink(const void *context, void *ptr)
+{
+	struct talloc_chunk *tc_p, *new_p;
+	void *new_parent;
+
+	if (ptr == NULL) {
+		return -1;
+	}
+
+	if (context == NULL) {
+		context = null_context;
+	}
+
+	if (talloc_unreference(context, ptr) == 0) {
+		return 0;
+	}
+
+	if (context == NULL) {
+		if (talloc_parent_chunk(ptr) != NULL) {
+			return -1;
+		}
+	} else {
+		if (talloc_chunk_from_ptr(context) != talloc_parent_chunk(ptr)) {
+			return -1;
+		}
+	}
+	
+	tc_p = talloc_chunk_from_ptr(ptr);
+
+	if (tc_p->refs == NULL) {
+		return _talloc_free(ptr);
+	}
+
+	new_p = talloc_parent_chunk(tc_p->refs);
+	if (new_p) {
+		new_parent = TC_PTR_FROM_CHUNK(new_p);
+	} else {
+		new_parent = NULL;
+	}
+
+	if (talloc_unreference(new_parent, ptr) != 0) {
+		return -1;
+	}
+
+	talloc_steal(new_parent, ptr);
+
+	return 0;
+}
+
+/*
+  add a name to an existing pointer - va_list version
+*/
+static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+
+static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap)
+{
+	struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+	tc->name = talloc_vasprintf(ptr, fmt, ap);
+	if (likely(tc->name)) {
+		_talloc_set_name_const(tc->name, ".name");
+	}
+	return tc->name;
+}
+
+/*
+  add a name to an existing pointer
+*/
+const char *talloc_set_name(const void *ptr, const char *fmt, ...)
+{
+	const char *name;
+	va_list ap;
+	va_start(ap, fmt);
+	name = talloc_set_name_v(ptr, fmt, ap);
+	va_end(ap);
+	return name;
+}
+
+
+/*
+  create a named talloc pointer. Any talloc pointer can be named, and
+  talloc_named() operates just like talloc() except that it allows you
+  to name the pointer.
+*/
+void *talloc_named(const void *context, size_t size, const char *fmt, ...)
+{
+	va_list ap;
+	void *ptr;
+	const char *name;
+
+	ptr = __talloc(context, size);
+	if (unlikely(ptr == NULL)) return NULL;
+
+	va_start(ap, fmt);
+	name = talloc_set_name_v(ptr, fmt, ap);
+	va_end(ap);
+
+	if (unlikely(name == NULL)) {
+		_talloc_free(ptr);
+		return NULL;
+	}
+
+	return ptr;
+}
+
+/*
+  return the name of a talloc ptr, or "UNNAMED"
+*/
+const char *talloc_get_name(const void *ptr)
+{
+	struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+	if (unlikely(tc->name == TALLOC_MAGIC_REFERENCE)) {
+		return ".reference";
+	}
+	if (likely(tc->name)) {
+		return tc->name;
+	}
+	return "UNNAMED";
+}
+
+
+/*
+  check if a pointer has the given name. If it does, return the pointer,
+  otherwise return NULL
+*/
+void *talloc_check_name(const void *ptr, const char *name)
+{
+	const char *pname;
+	if (unlikely(ptr == NULL)) return NULL;
+	pname = talloc_get_name(ptr);
+	if (likely(pname == name || strcmp(pname, name) == 0)) {
+		return discard_const_p(void, ptr);
+	}
+	return NULL;
+}
+
+
+/*
+  this is for compatibility with older versions of talloc
+*/
+void *talloc_init(const char *fmt, ...)
+{
+	va_list ap;
+	void *ptr;
+	const char *name;
+
+	/*
+	 * samba3 expects talloc_report_depth_cb(NULL, ...)
+	 * reports all talloc'ed memory, so we need to enable
+	 * null_tracking
+	 */
+	talloc_enable_null_tracking();
+
+	ptr = __talloc(NULL, 0);
+	if (unlikely(ptr == NULL)) return NULL;
+
+	va_start(ap, fmt);
+	name = talloc_set_name_v(ptr, fmt, ap);
+	va_end(ap);
+
+	if (unlikely(name == NULL)) {
+		_talloc_free(ptr);
+		return NULL;
+	}
+
+	return ptr;
+}
+
+/*
+  this is a replacement for the Samba3 talloc_destroy_pool functionality. It
+  should probably not be used in new code. It's in here to keep the talloc
+  code consistent across Samba 3 and 4.
+*/
+void talloc_free_children(void *ptr)
+{
+	struct talloc_chunk *tc;
+
+	if (unlikely(ptr == NULL)) {
+		return;
+	}
+
+	tc = talloc_chunk_from_ptr(ptr);
+
+	while (tc->child) {
+		/* we need to work out who will own an abandoned child
+		   if it cannot be freed. In priority order, the first
+		   choice is owner of any remaining reference to this
+		   pointer, the second choice is our parent, and the
+		   final choice is the null context. */
+		void *child = TC_PTR_FROM_CHUNK(tc->child);
+		const void *new_parent = null_context;
+		if (unlikely(tc->child->refs)) {
+			struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
+			if (p) new_parent = TC_PTR_FROM_CHUNK(p);
+		}
+		if (unlikely(_talloc_free(child) == -1)) {
+			if (new_parent == null_context) {
+				struct talloc_chunk *p = talloc_parent_chunk(ptr);
+				if (p) new_parent = TC_PTR_FROM_CHUNK(p);
+			}
+			talloc_steal(new_parent, child);
+		}
+	}
+
+	if ((tc->flags & TALLOC_FLAG_POOL)
+	    && (*talloc_pool_objectcount(tc) == 1)) {
+		tc->pool = ((char *)tc + TC_HDR_SIZE + TALLOC_POOL_HDR_SIZE);
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS)
+		VALGRIND_MAKE_MEM_NOACCESS(
+			tc->pool, tc->size - TALLOC_POOL_HDR_SIZE);
+#endif
+	}
+}
+
+/* 
+   Allocate a bit of memory as a child of an existing pointer
+*/
+void *_talloc(const void *context, size_t size)
+{
+	return __talloc(context, size);
+}
+
+/*
+  externally callable talloc_set_name_const()
+*/
+void talloc_set_name_const(const void *ptr, const char *name)
+{
+	_talloc_set_name_const(ptr, name);
+}
+
+/*
+  create a named talloc pointer. Any talloc pointer can be named, and
+  talloc_named() operates just like talloc() except that it allows you
+  to name the pointer.
+*/
+void *talloc_named_const(const void *context, size_t size, const char *name)
+{
+	return _talloc_named_const(context, size, name);
+}
+
+/* 
+   free a talloc pointer. This also frees all child pointers of this 
+   pointer recursively
+
+   return 0 if the memory is actually freed, otherwise -1. The memory
+   will not be freed if the ref_count is > 1 or the destructor (if
+   any) returns non-zero
+*/
+int talloc_free(void *ptr)
+{
+	return _talloc_free(ptr);
+}
+
+
+
+/*
+  A talloc version of realloc. The context argument is only used if
+  ptr is NULL
+*/
+void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name)
+{
+	struct talloc_chunk *tc;
+	void *new_ptr;
+	bool malloced = false;
+
+	/* size zero is equivalent to free() */
+	if (unlikely(size == 0)) {
+		_talloc_free(ptr);
+		return NULL;
+	}
+
+	if (unlikely(size >= MAX_TALLOC_SIZE)) {
+		return NULL;
+	}
+
+	/* realloc(NULL) is equivalent to malloc() */
+	if (ptr == NULL) {
+		return _talloc_named_const(context, size, name);
+	}
+
+	tc = talloc_chunk_from_ptr(ptr);
+
+	/* don't allow realloc on referenced pointers */
+	if (unlikely(tc->refs)) {
+		return NULL;
+	}
+
+	/* don't shrink if we have less than 1k to gain */
+	if ((size < tc->size) && ((tc->size - size) < 1024)) {
+		tc->size = size;
+		return ptr;
+	}
+
+	/* by resetting magic we catch users of the old memory */
+	tc->flags |= TALLOC_FLAG_FREE;
+
+#if ALWAYS_REALLOC
+	new_ptr = malloc(size + TC_HDR_SIZE);
+	if (new_ptr) {
+		memcpy(new_ptr, tc, tc->size + TC_HDR_SIZE);
+		free(tc);
+	}
+#else
+	if (tc->flags & TALLOC_FLAG_POOLMEM) {
+
+		new_ptr = talloc_alloc_pool(tc, size + TC_HDR_SIZE);
+		*talloc_pool_objectcount((struct talloc_chunk *)
+					 (tc->pool)) -= 1;
+
+		if (new_ptr == NULL) {
+			new_ptr = malloc(TC_HDR_SIZE+size);
+			malloced = true;
+		}
+
+		if (new_ptr) {
+			memcpy(new_ptr, tc, MIN(tc->size,size) + TC_HDR_SIZE);
+		}
+	}
+	else {
+		new_ptr = realloc(tc, size + TC_HDR_SIZE);
+	}
+#endif
+	if (unlikely(!new_ptr)) {	
+		tc->flags &= ~TALLOC_FLAG_FREE; 
+		return NULL; 
+	}
+
+	tc = (struct talloc_chunk *)new_ptr;
+	tc->flags &= ~TALLOC_FLAG_FREE;
+	if (malloced) {
+		tc->flags &= ~TALLOC_FLAG_POOLMEM;
+	}
+	if (tc->parent) {
+		tc->parent->child = tc;
+	}
+	if (tc->child) {
+		tc->child->parent = tc;
+	}
+
+	if (tc->prev) {
+		tc->prev->next = tc;
+	}
+	if (tc->next) {
+		tc->next->prev = tc;
+	}
+
+	tc->size = size;
+	_talloc_set_name_const(TC_PTR_FROM_CHUNK(tc), name);
+
+	return TC_PTR_FROM_CHUNK(tc);
+}
+
+/*
+  a wrapper around talloc_steal() for situations where you are moving a pointer
+  between two structures, and want the old pointer to be set to NULL
+*/
+void *_talloc_move(const void *new_ctx, const void *_pptr)
+{
+	const void **pptr = discard_const_p(const void *,_pptr);
+	void *ret = _talloc_steal(new_ctx, *pptr);
+	(*pptr) = NULL;
+	return ret;
+}
+
+/*
+  return the total size of a talloc pool (subtree)
+*/
+size_t talloc_total_size(const void *ptr)
+{
+	size_t total = 0;
+	struct talloc_chunk *c, *tc;
+
+	if (ptr == NULL) {
+		ptr = null_context;
+	}
+	if (ptr == NULL) {
+		return 0;
+	}
+
+	tc = talloc_chunk_from_ptr(ptr);
+
+	if (tc->flags & TALLOC_FLAG_LOOP) {
+		return 0;
+	}
+
+	tc->flags |= TALLOC_FLAG_LOOP;
+
+	total = tc->size;
+	for (c=tc->child;c;c=c->next) {
+		total += talloc_total_size(TC_PTR_FROM_CHUNK(c));
+	}
+
+	tc->flags &= ~TALLOC_FLAG_LOOP;
+
+	return total;
+}
+
+/*
+  return the total number of blocks in a talloc pool (subtree)
+*/
+size_t talloc_total_blocks(const void *ptr)
+{
+	size_t total = 0;
+	struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr);
+
+	if (tc->flags & TALLOC_FLAG_LOOP) {
+		return 0;
+	}
+
+	tc->flags |= TALLOC_FLAG_LOOP;
+
+	total++;
+	for (c=tc->child;c;c=c->next) {
+		total += talloc_total_blocks(TC_PTR_FROM_CHUNK(c));
+	}
+
+	tc->flags &= ~TALLOC_FLAG_LOOP;
+
+	return total;
+}
+
+/*
+  return the number of external references to a pointer
+*/
+size_t talloc_reference_count(const void *ptr)
+{
+	struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+	struct talloc_reference_handle *h;
+	size_t ret = 0;
+
+	for (h=tc->refs;h;h=h->next) {
+		ret++;
+	}
+	return ret;
+}
+
+/*
+  report on memory usage by all children of a pointer, giving a full tree view
+*/
+void talloc_report_depth_cb(const void *ptr, int depth, int max_depth,
+			    void (*callback)(const void *ptr,
+			  		     int depth, int max_depth,
+					     int is_ref,
+					     void *private_data),
+			    void *private_data)
+{
+	struct talloc_chunk *c, *tc;
+
+	if (ptr == NULL) {
+		ptr = null_context;
+	}
+	if (ptr == NULL) return;
+
+	tc = talloc_chunk_from_ptr(ptr);
+
+	if (tc->flags & TALLOC_FLAG_LOOP) {
+		return;
+	}
+
+	callback(ptr, depth, max_depth, 0, private_data);
+
+	if (max_depth >= 0 && depth >= max_depth) {
+		return;
+	}
+
+	tc->flags |= TALLOC_FLAG_LOOP;
+	for (c=tc->child;c;c=c->next) {
+		if (c->name == TALLOC_MAGIC_REFERENCE) {
+			struct talloc_reference_handle *h = (struct talloc_reference_handle *)TC_PTR_FROM_CHUNK(c);
+			callback(h->ptr, depth + 1, max_depth, 1, private_data);
+		} else {
+			talloc_report_depth_cb(TC_PTR_FROM_CHUNK(c), depth + 1, max_depth, callback, private_data);
+		}
+	}
+	tc->flags &= ~TALLOC_FLAG_LOOP;
+}
+
+static void talloc_report_depth_FILE_helper(const void *ptr, int depth, int max_depth, int is_ref, void *_f)
+{
+	const char *name = talloc_get_name(ptr);
+	FILE *f = (FILE *)_f;
+
+	if (is_ref) {
+		fprintf(f, "%*sreference to: %s\n", depth*4, "", name);
+		return;
+	}
+
+	if (depth == 0) {
+		fprintf(f,"%stalloc report on '%s' (total %6lu bytes in %3lu blocks)\n", 
+			(max_depth < 0 ? "full " :""), name,
+			(unsigned long)talloc_total_size(ptr),
+			(unsigned long)talloc_total_blocks(ptr));
+		return;
+	}
+
+	fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d) %p\n", 
+		depth*4, "",
+		name,
+		(unsigned long)talloc_total_size(ptr),
+		(unsigned long)talloc_total_blocks(ptr),
+		(int)talloc_reference_count(ptr), ptr);
+
+#if 0
+	fprintf(f, "content: ");
+	if (talloc_total_size(ptr)) {
+		int tot = talloc_total_size(ptr);
+		int i;
+
+		for (i = 0; i < tot; i++) {
+			if ((((char *)ptr)[i] > 31) && (((char *)ptr)[i] < 126)) {
+				fprintf(f, "%c", ((char *)ptr)[i]);
+			} else {
+				fprintf(f, "~%02x", ((char *)ptr)[i]);
+			}
+		}
+	}
+	fprintf(f, "\n");
+#endif
+}
+
+/*
+  report on memory usage by all children of a pointer, giving a full tree view
+*/
+void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f)
+{
+	talloc_report_depth_cb(ptr, depth, max_depth, talloc_report_depth_FILE_helper, f);
+	fflush(f);
+}
+
+/*
+  report on memory usage by all children of a pointer, giving a full tree view
+*/
+void talloc_report_full(const void *ptr, FILE *f)
+{
+	talloc_report_depth_file(ptr, 0, -1, f);
+}
+
+/*
+  report on memory usage by all children of a pointer
+*/
+void talloc_report(const void *ptr, FILE *f)
+{
+	talloc_report_depth_file(ptr, 0, 1, f);
+}
+
+/*
+  report on any memory hanging off the null context
+*/
+static void talloc_report_null(void)
+{
+	if (talloc_total_size(null_context) != 0) {
+		talloc_report(null_context, stderr);
+	}
+}
+
+/*
+  report on any memory hanging off the null context
+*/
+static void talloc_report_null_full(void)
+{
+	if (talloc_total_size(null_context) != 0) {
+		talloc_report_full(null_context, stderr);
+	}
+}
+
+/*
+  enable tracking of the NULL context
+*/
+void talloc_enable_null_tracking(void)
+{
+	if (null_context == NULL) {
+		null_context = _talloc_named_const(NULL, 0, "null_context");
+	}
+}
+
+/*
+  disable tracking of the NULL context
+*/
+void talloc_disable_null_tracking(void)
+{
+	_talloc_free(null_context);
+	null_context = NULL;
+}
+
+/*
+  enable leak reporting on exit
+*/
+void talloc_enable_leak_report(void)
+{
+	talloc_enable_null_tracking();
+	atexit(talloc_report_null);
+}
+
+/*
+  enable full leak reporting on exit
+*/
+void talloc_enable_leak_report_full(void)
+{
+	talloc_enable_null_tracking();
+	atexit(talloc_report_null_full);
+}
+
+/* 
+   talloc and zero memory. 
+*/
+void *_talloc_zero(const void *ctx, size_t size, const char *name)
+{
+	void *p = _talloc_named_const(ctx, size, name);
+
+	if (p) {
+		memset(p, '\0', size);
+	}
+
+	return p;
+}
+
+/*
+  memdup with a talloc. 
+*/
+void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name)
+{
+	void *newp = _talloc_named_const(t, size, name);
+
+	if (likely(newp)) {
+		memcpy(newp, p, size);
+	}
+
+	return newp;
+}
+
+static inline char *__talloc_strlendup(const void *t, const char *p, size_t len)
+{
+	char *ret;
+
+	ret = (char *)__talloc(t, len + 1);
+	if (unlikely(!ret)) return NULL;
+
+	memcpy(ret, p, len);
+	ret[len] = 0;
+
+	_talloc_set_name_const(ret, ret);
+	return ret;
+}
+
+/*
+  strdup with a talloc
+*/
+char *talloc_strdup(const void *t, const char *p)
+{
+	if (unlikely(!p)) return NULL;
+	return __talloc_strlendup(t, p, strlen(p));
+}
+
+/*
+  strndup with a talloc
+*/
+char *talloc_strndup(const void *t, const char *p, size_t n)
+{
+	if (unlikely(!p)) return NULL;
+	return __talloc_strlendup(t, p, n);
+}
+
+static inline char *__talloc_strlendup_append(char *s, size_t slen,
+					      const char *a, size_t alen)
+{
+	char *ret;
+
+	ret = talloc_realloc(NULL, s, char, slen + alen + 1);
+	if (unlikely(!ret)) return NULL;
+
+	/* append the string and the trailing \0 */
+	memcpy(&ret[slen], a, alen);
+	ret[slen+alen] = 0;
+
+	_talloc_set_name_const(ret, ret);
+	return ret;
+}
+
+/*
+ * Appends at the end of the string.
+ */
+char *talloc_strdup_append(char *s, const char *a)
+{
+	if (unlikely(!s)) {
+		return talloc_strdup(NULL, a);
+	}
+
+	if (unlikely(!a)) {
+		return s;
+	}
+
+	return __talloc_strlendup_append(s, strlen(s), a, strlen(a));
+}
+
+/*
+ * Appends at the end of the talloc'ed buffer,
+ * not the end of the string.
+ */
+char *talloc_strdup_append_buffer(char *s, const char *a)
+{
+	size_t slen;
+
+	if (unlikely(!s)) {
+		return talloc_strdup(NULL, a);
+	}
+
+	if (unlikely(!a)) {
+		return s;
+	}
+
+	slen = talloc_get_size(s);
+	if (likely(slen > 0)) {
+		slen--;
+	}
+
+	return __talloc_strlendup_append(s, slen, a, strlen(a));
+}
+
+/*
+ * Appends at the end of the string.
+ */
+char *talloc_strndup_append(char *s, const char *a, size_t n)
+{
+	if (unlikely(!s)) {
+		return talloc_strdup(NULL, a);
+	}
+
+	if (unlikely(!a)) {
+		return s;
+	}
+
+	return __talloc_strlendup_append(s, strlen(s), a, n);
+}
+
+/*
+ * Appends at the end of the talloc'ed buffer,
+ * not the end of the string.
+ */
+char *talloc_strndup_append_buffer(char *s, const char *a, size_t n)
+{
+	size_t slen;
+
+	if (unlikely(!s)) {
+		return talloc_strdup(NULL, a);
+	}
+
+	if (unlikely(!a)) {
+		return s;
+	}
+
+	slen = talloc_get_size(s);
+	if (likely(slen > 0)) {
+		slen--;
+	}
+
+	return __talloc_strlendup_append(s, slen, a, n);
+}
+
+
+char *talloc_vasprintf(const void *t, const char *fmt, va_list ap)
+{
+	int len;
+	char *ret;
+	va_list ap2;
+	char c;
+
+	/* this call looks strange, but it makes it work on older solaris boxes */
+	va_copy(ap2, ap);
+	len = vsnprintf(&c, 1, fmt, ap2);
+	va_end(ap2);
+	if (unlikely(len < 0)) {
+		return NULL;
+	}
+
+	ret = (char *)__talloc(t, len+1);
+	if (unlikely(!ret)) return NULL;
+
+	va_copy(ap2, ap);
+	vsnprintf(ret, len+1, fmt, ap2);
+	va_end(ap2);
+
+	_talloc_set_name_const(ret, ret);
+	return ret;
+}
+
+
+/*
+  Perform string formatting, and return a pointer to newly allocated
+  memory holding the result, inside a memory pool.
+ */
+char *talloc_asprintf(const void *t, const char *fmt, ...)
+{
+	va_list ap;
+	char *ret;
+
+	va_start(ap, fmt);
+	ret = talloc_vasprintf(t, fmt, ap);
+	va_end(ap);
+	return ret;
+}
+
+static inline char *__talloc_vaslenprintf_append(char *s, size_t slen,
+						 const char *fmt, va_list ap)
+						 PRINTF_ATTRIBUTE(3,0);
+
+static inline char *__talloc_vaslenprintf_append(char *s, size_t slen,
+						 const char *fmt, va_list ap)
+{
+	ssize_t alen;
+	va_list ap2;
+	char c;
+
+	va_copy(ap2, ap);
+	alen = vsnprintf(&c, 1, fmt, ap2);
+	va_end(ap2);
+
+	if (alen <= 0) {
+		/* Either the vsnprintf failed or the format resulted in
+		 * no characters being formatted. In the former case, we
+		 * ought to return NULL, in the latter we ought to return
+		 * the original string. Most current callers of this
+		 * function expect it to never return NULL.
+		 */
+		return s;
+	}
+
+	s = talloc_realloc(NULL, s, char, slen + alen + 1);
+	if (!s) return NULL;
+
+	va_copy(ap2, ap);
+	vsnprintf(s + slen, alen + 1, fmt, ap2);
+	va_end(ap2);
+
+	_talloc_set_name_const(s, s);
+	return s;
+}
+
+/**
+ * Realloc @p s to append the formatted result of @p fmt and @p ap,
+ * and return @p s, which may have moved.  Good for gradually
+ * accumulating output into a string buffer. Appends at the end
+ * of the string.
+ **/
+char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap)
+{
+	if (unlikely(!s)) {
+		return talloc_vasprintf(NULL, fmt, ap);
+	}
+
+	return __talloc_vaslenprintf_append(s, strlen(s), fmt, ap);
+}
+
+/**
+ * Realloc @p s to append the formatted result of @p fmt and @p ap,
+ * and return @p s, which may have moved. Always appends at the
+ * end of the talloc'ed buffer, not the end of the string.
+ **/
+char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap)
+{
+	size_t slen;
+
+	if (unlikely(!s)) {
+		return talloc_vasprintf(NULL, fmt, ap);
+	}
+
+	slen = talloc_get_size(s);
+	if (likely(slen > 0)) {
+		slen--;
+	}
+
+	return __talloc_vaslenprintf_append(s, slen, fmt, ap);
+}
+
+/*
+  Realloc @p s to append the formatted result of @p fmt and return @p
+  s, which may have moved.  Good for gradually accumulating output
+  into a string buffer.
+ */
+char *talloc_asprintf_append(char *s, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	s = talloc_vasprintf_append(s, fmt, ap);
+	va_end(ap);
+	return s;
+}
+
+/*
+  Realloc @p s to append the formatted result of @p fmt and return @p
+  s, which may have moved.  Good for gradually accumulating output
+  into a buffer.
+ */
+char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	s = talloc_vasprintf_append_buffer(s, fmt, ap);
+	va_end(ap);
+	return s;
+}
+
+/*
+  alloc an array, checking for integer overflow in the array size
+*/
+void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name)
+{
+	if (count >= MAX_TALLOC_SIZE/el_size) {
+		return NULL;
+	}
+	return _talloc_named_const(ctx, el_size * count, name);
+}
+
+/*
+  alloc an zero array, checking for integer overflow in the array size
+*/
+void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name)
+{
+	if (count >= MAX_TALLOC_SIZE/el_size) {
+		return NULL;
+	}
+	return _talloc_zero(ctx, el_size * count, name);
+}
+
+/*
+  realloc an array, checking for integer overflow in the array size
+*/
+void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name)
+{
+	if (count >= MAX_TALLOC_SIZE/el_size) {
+		return NULL;
+	}
+	return _talloc_realloc(ctx, ptr, el_size * count, name);
+}
+
+/*
+  a function version of talloc_realloc(), so it can be passed as a function pointer
+  to libraries that want a realloc function (a realloc function encapsulates
+  all the basic capabilities of an allocation library, which is why this is useful)
+*/
+void *talloc_realloc_fn(const void *context, void *ptr, size_t size)
+{
+	return _talloc_realloc(context, ptr, size, NULL);
+}
+
+
+static int talloc_autofree_destructor(void *ptr)
+{
+	autofree_context = NULL;
+	return 0;
+}
+
+static void talloc_autofree(void)
+{
+	_talloc_free(autofree_context);
+}
+
+/*
+  return a context which will be auto-freed on exit
+  this is useful for reducing the noise in leak reports
+*/
+void *talloc_autofree_context(void)
+{
+	if (autofree_context == NULL) {
+		autofree_context = _talloc_named_const(NULL, 0, "autofree_context");
+		talloc_set_destructor(autofree_context, talloc_autofree_destructor);
+		atexit(talloc_autofree);
+	}
+	return autofree_context;
+}
+
+size_t talloc_get_size(const void *context)
+{
+	struct talloc_chunk *tc;
+
+	if (context == NULL)
+		return 0;
+
+	tc = talloc_chunk_from_ptr(context);
+
+	return tc->size;
+}
+
+/*
+  find a parent of this context that has the given name, if any
+*/
+void *talloc_find_parent_byname(const void *context, const char *name)
+{
+	struct talloc_chunk *tc;
+
+	if (context == NULL) {
+		return NULL;
+	}
+
+	tc = talloc_chunk_from_ptr(context);
+	while (tc) {
+		if (tc->name && strcmp(tc->name, name) == 0) {
+			return TC_PTR_FROM_CHUNK(tc);
+		}
+		while (tc && tc->prev) tc = tc->prev;
+		if (tc) {
+			tc = tc->parent;
+		}
+	}
+	return NULL;
+}
+
+/*
+  show the parentage of a context
+*/
+void talloc_show_parents(const void *context, FILE *file)
+{
+	struct talloc_chunk *tc;
+
+	if (context == NULL) {
+		fprintf(file, "talloc no parents for NULL\n");
+		return;
+	}
+
+	tc = talloc_chunk_from_ptr(context);
+	fprintf(file, "talloc parents of '%s'\n", talloc_get_name(context));
+	while (tc) {
+		fprintf(file, "\t'%s'\n", talloc_get_name(TC_PTR_FROM_CHUNK(tc)));
+		while (tc && tc->prev) tc = tc->prev;
+		if (tc) {
+			tc = tc->parent;
+		}
+	}
+	fflush(file);
+}
+
+/*
+  return 1 if ptr is a parent of context
+*/
+int talloc_is_parent(const void *context, const void *ptr)
+{
+	struct talloc_chunk *tc;
+
+	if (context == NULL) {
+		return 0;
+	}
+
+	tc = talloc_chunk_from_ptr(context);
+	while (tc) {
+		if (TC_PTR_FROM_CHUNK(tc) == ptr) return 1;
+		while (tc && tc->prev) tc = tc->prev;
+		if (tc) {
+			tc = tc->parent;
+		}
+	}
+	return 0;
+}
diff --git a/lib/void_stack.c b/lib/void_stack.c
index 402df0b..34c792c 100644
--- a/lib/void_stack.c
+++ b/lib/void_stack.c
@@ -3,7 +3,7 @@
  * of memory of any type.  It still needs work to eliminate memory
  * leaks. 
  *
- * Copyright (C) 2005,2007 Timothy D. Morgan
+ * Copyright (C) 2005,2007,2009 Timothy D. Morgan
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -18,22 +18,22 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
  *
- * $Id: void_stack.c 111 2008-05-01 04:06:22Z tim $
+ * $Id: void_stack.c 150 2009-03-02 02:17:46Z tim $
  */
 
-#include "../include/void_stack.h"
+#include "void_stack.h"
 
 void_stack* void_stack_new(unsigned short max_size)
 {
-  void_stack* ret_val = (void_stack*)malloc(sizeof(void_stack));
+  void_stack* ret_val = talloc(NULL, void_stack);
 
   if (ret_val != NULL)
   {
     memset(ret_val, 0, sizeof(*ret_val));
-    ret_val->elements = (void**)malloc(max_size*sizeof(void*));
+    ret_val->elements = talloc_array(ret_val, void*, max_size);
     if (ret_val->elements == NULL)
     {
-      free(ret_val);
+      talloc_free(ret_val);
       ret_val = NULL;
     }
     else
@@ -89,8 +89,7 @@ void_stack* void_stack_copy_reverse(const void_stack* v)
 
 void void_stack_free(void_stack* stack)
 {
-  free(stack->elements);
-  free(stack);
+  talloc_free(stack);
 }
 
 
@@ -99,8 +98,7 @@ void void_stack_free_deep(void_stack* stack)
   unsigned short i;
   for(i=0; i < stack->top; i++)
     free(stack->elements[i]);
-  free(stack->elements);
-  free(stack);
+  talloc_free(stack);
 }
 
 
@@ -153,7 +151,7 @@ void_stack_iterator* void_stack_iterator_new(const void_stack* stack)
   
   if(stack != NULL)
   {
-    ret_val = (void_stack_iterator*)malloc(sizeof(void_stack_iterator));
+    ret_val = talloc(stack, void_stack_iterator);
     if (ret_val != NULL)
     {
       ret_val->stack = stack;
@@ -167,7 +165,7 @@ void_stack_iterator* void_stack_iterator_new(const void_stack* stack)
 
 void void_stack_iterator_free(void_stack_iterator* iter)
 {
-  free(iter);
+  talloc_free(iter);
 }
 
 
diff --git a/lib/winsec.c b/lib/winsec.c
new file mode 100644
index 0000000..dfd57a7
--- /dev/null
+++ b/lib/winsec.c
@@ -0,0 +1,532 @@
+/*
+ * This file contains refactored Samba code used to interpret Windows
+ * Security Descriptors. See:
+ *   http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/
+ *
+ * Copyright (C) 2005-2006,2009 Timothy D. Morgan
+ * Copyright (C) 1992-2005 Samba development team 
+ *               (see individual files under Subversion for details.)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: winsec.c 148 2009-02-22 23:22:59Z tim $
+ */
+
+#include "winsec.h"
+
+
+/******************************************************************************
+ * Non-talloc() interface for parsing a descriptor.
+ ******************************************************************************/
+WINSEC_DESC* winsec_parse_descriptor(const uint8_t* buf, uint32_t buf_len)
+{
+  return winsec_parse_desc(NULL, buf, buf_len);
+}
+
+
+/******************************************************************************
+ * Free a descriptor.  Not needed if using talloc and a parent context is freed.
+ ******************************************************************************/
+void winsec_free_descriptor(WINSEC_DESC* desc)
+{
+  talloc_free(desc);
+}
+
+
+/******************************************************************************
+ * Parses a WINSEC_DESC structure and substructures.
+ ******************************************************************************/
+WINSEC_DESC* winsec_parse_desc(void* talloc_ctx, 
+			       const uint8_t* buf, uint32_t buf_len)
+{
+  WINSEC_DESC* ret_val;
+
+  if (buf == NULL || buf_len <  WINSEC_DESC_HEADER_SIZE)
+    return NULL;
+
+  if((ret_val = talloc(talloc_ctx, WINSEC_DESC)) == NULL)
+    return NULL;
+
+  ret_val->revision = buf[0];
+  ret_val->sbz1 = buf[1];
+  ret_val->control = SVAL(buf, 0x2);
+
+  if(!(ret_val->control & WINSEC_DESC_SELF_RELATIVE))
+    fprintf(stderr, "DEBUG: NOT self-relative!\n");
+
+  ret_val->off_owner_sid = IVAL(buf, 0x4);
+  ret_val->off_grp_sid = IVAL(buf, 0x8);
+  ret_val->off_sacl = IVAL(buf, 0xC);
+  ret_val->off_dacl = IVAL(buf, 0x10);
+
+  /* A basic sanity check to ensure our offsets are within our buffer.
+   * Additional length checking is done in secondary parsing functions.
+   */
+  if((ret_val->off_owner_sid >= buf_len)
+     || (ret_val->off_grp_sid >= buf_len)
+     || (ret_val->off_sacl >= buf_len)
+     || (ret_val->off_dacl >= buf_len))
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+
+  if(ret_val->off_owner_sid == 0)
+    ret_val->owner_sid = NULL;
+  else
+  {
+    ret_val->owner_sid = winsec_parse_dom_sid(ret_val, 
+					      buf + ret_val->off_owner_sid,
+					      buf_len - ret_val->off_owner_sid);
+    if(ret_val->owner_sid == NULL)
+    {
+      talloc_free(ret_val);
+      return NULL;
+    }
+  }
+
+  if(ret_val->off_grp_sid == 0) 
+    ret_val->grp_sid = NULL;
+  else
+  {
+    ret_val->grp_sid = winsec_parse_dom_sid(ret_val, buf + ret_val->off_grp_sid,
+					    buf_len - ret_val->off_grp_sid);
+    if(ret_val->grp_sid == NULL)
+    {
+      talloc_free(ret_val);
+      return NULL;
+    }
+  }
+
+  if((ret_val->control & WINSEC_DESC_SACL_PRESENT) && ret_val->off_sacl)
+  {
+    ret_val->sacl = winsec_parse_acl(ret_val, buf + ret_val->off_sacl,
+				     buf_len - ret_val->off_sacl);
+    if(ret_val->sacl == NULL)
+    {
+      talloc_free(ret_val);
+      return NULL;
+    }
+  }
+  else
+    ret_val->sacl = NULL;
+
+  if((ret_val->control & WINSEC_DESC_DACL_PRESENT) && ret_val->off_dacl != 0) 
+  {
+    ret_val->dacl = winsec_parse_acl(ret_val, buf + ret_val->off_dacl,
+				     buf_len - ret_val->off_dacl);
+    if(ret_val->dacl == NULL)
+    {
+      talloc_free(ret_val);
+      return NULL;
+    }
+  }
+  else
+    ret_val->dacl = NULL;
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ * Parses a WINSEC_ACL structure and all substructures.
+ ******************************************************************************/
+WINSEC_ACL* winsec_parse_acl(void* talloc_ctx,
+			     const uint8_t* buf, uint32_t buf_len)
+{
+  uint32_t i, offset;
+  WINSEC_ACL* ret_val;
+
+  /*
+   * Note that the size is always a multiple of 4 bytes due to the
+   * nature of the data structure.
+   */
+  if (buf == NULL || buf_len < 8)
+    return NULL;
+
+  if((ret_val = talloc(talloc_ctx, WINSEC_ACL)) == NULL)
+    return NULL;
+  
+  ret_val->revision = SVAL(buf, 0x0);
+  ret_val->size     = SVAL(buf, 0x2);
+  ret_val->num_aces = IVAL(buf, 0x4);
+
+  /* The num_aces can be at most around 4k because anything greater
+   * wouldn't fit in the 16 bit size even if every ace was as small as
+   * possible. 
+   */
+  if((ret_val->size > buf_len) || (ret_val->num_aces > 4095))
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+
+  /* Even if the num_aces is zero, allocate memory as there's a difference
+   * between a non-present DACL (allow all access) and a DACL with no ACE's
+   * (allow no access).
+   */
+  if((ret_val->aces = talloc_array(ret_val, WINSEC_ACE*, 
+				   ret_val->num_aces+1)) == NULL)
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+
+  offset = 8;
+  for(i=0; i < ret_val->num_aces; i++)
+  {
+    ret_val->aces[i] = winsec_parse_ace(ret_val->aces, 
+					buf+offset, buf_len-offset);
+    if(ret_val->aces[i] == NULL)
+    {
+      talloc_free(ret_val);
+      return NULL;
+    }
+
+    offset += ret_val->aces[i]->size;
+    if(offset > buf_len)
+    {
+      talloc_free(ret_val);
+      return NULL;
+    }
+  }
+  ret_val->aces[ret_val->num_aces] = NULL;
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ * Parses a WINSEC_ACE structure and all substructures.
+ ******************************************************************************/
+WINSEC_ACE* winsec_parse_ace(void* talloc_ctx,
+			     const uint8_t* buf, uint32_t buf_len)
+{
+  uint32_t offset;
+  WINSEC_ACE* ret_val;
+
+  if(buf == NULL || buf_len < WINSEC_ACE_MIN_SIZE)
+    return NULL;
+
+  if((ret_val = talloc(talloc_ctx, WINSEC_ACE)) == NULL)
+    return NULL;
+
+  ret_val->type = buf[0];
+  ret_val->flags = buf[1];
+  ret_val->size = SVAL(buf, 0x2);
+  ret_val->access_mask = IVAL(buf, 0x4);
+
+  offset = 0x8;
+
+  /* check whether object access is present */
+  if (winsec_ace_object(ret_val->type))
+  {
+    ret_val->obj_flags = IVAL(buf, offset);
+    offset += 4;
+
+    if(ret_val->obj_flags & WINSEC_ACE_OBJECT_PRESENT)
+    {
+      ret_val->obj_guid = winsec_parse_uuid(ret_val, 
+					    buf+offset, buf_len-offset);
+      if(ret_val->obj_guid == NULL)
+      {
+	talloc_free(ret_val);
+	return NULL;
+      }
+      offset += sizeof(WINSEC_UUID);
+    }
+    else
+      ret_val->obj_guid = NULL;
+
+    if(ret_val->obj_flags & WINSEC_ACE_OBJECT_INHERITED_PRESENT)
+    {
+      ret_val->inh_guid = winsec_parse_uuid(ret_val, 
+					    buf+offset, buf_len-offset);
+      if(ret_val->inh_guid == NULL)
+      {
+	talloc_free(ret_val);
+	return NULL;
+      }
+      offset += sizeof(WINSEC_UUID);
+    }
+    else
+      ret_val->inh_guid = NULL;
+  }
+
+  ret_val->trustee = winsec_parse_dom_sid(ret_val, buf+offset, buf_len-offset);
+  if(ret_val->trustee == NULL)
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+  
+  return ret_val;
+}
+
+
+/******************************************************************************
+ * Parses a WINSEC_DOM_SID structure.
+ ******************************************************************************/
+WINSEC_DOM_SID* winsec_parse_dom_sid(void* talloc_ctx,
+				     const uint8_t* buf, uint32_t buf_len)
+{
+  uint32_t i;
+  WINSEC_DOM_SID* ret_val;
+
+  if(buf == NULL || buf_len < 8)
+    return NULL;
+
+  /*  if((ret_val = (WINSEC_DOM_SID*)zalloc(sizeof(WINSEC_DOM_SID))) == NULL)*/
+  if((ret_val = talloc(talloc_ctx, WINSEC_DOM_SID)) == NULL)
+    return NULL;
+
+  ret_val->sid_rev_num = buf[0];
+  ret_val->num_auths = buf[1];
+  memcpy(ret_val->id_auth, buf+2, 6);
+
+  /* XXX: should really issue a warning here... */
+  if (ret_val->num_auths > WINSEC_MAX_SUBAUTHS)
+    ret_val->num_auths = WINSEC_MAX_SUBAUTHS;
+
+  if(buf_len < ret_val->num_auths*sizeof(uint32_t)+8)
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+  
+  for(i=0; i < ret_val->num_auths; i++)
+    ret_val->sub_auths[i] = IVAL(buf, 8+i*sizeof(uint32_t));
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ * Parses a WINSEC_UUID struct.
+ ******************************************************************************/
+WINSEC_UUID* winsec_parse_uuid(void* talloc_ctx,
+			       const uint8_t* buf, uint32_t buf_len)
+{
+  WINSEC_UUID* ret_val;
+
+  if(buf == NULL || buf_len < sizeof(WINSEC_UUID))
+    return false;
+
+  if((ret_val = talloc(talloc_ctx, WINSEC_UUID)) == NULL)
+    return NULL;
+  
+  ret_val->time_low = IVAL(buf, 0x0);
+  ret_val->time_mid = SVAL(buf, 0x4);
+  ret_val->time_hi_and_version = SVAL(buf, 0x6);
+  
+  memcpy(ret_val->clock_seq, buf+0x8, 2);
+  memcpy(ret_val->node, buf+0xB, 6);
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ * Calculates the size of a SID.
+ ******************************************************************************/
+size_t winsec_sid_size(const WINSEC_DOM_SID* sid)
+{
+  if (sid == NULL)
+    return 0;
+
+  return sid->num_auths * sizeof(uint32_t) + 8;
+}
+
+
+/******************************************************************************
+ * Compare the auth portion of two SIDs.
+ ******************************************************************************/
+int winsec_sid_compare_auth(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2)
+{
+  int i;
+
+  if (sid1 == sid2)
+    return 0;
+  if (!sid1)
+    return -1;
+  if (!sid2)
+    return 1;
+
+  if (sid1->sid_rev_num != sid2->sid_rev_num)
+    return sid1->sid_rev_num - sid2->sid_rev_num;
+
+  for (i = 0; i < 6; i++)
+    if (sid1->id_auth[i] != sid2->id_auth[i])
+      return sid1->id_auth[i] - sid2->id_auth[i];
+
+  return 0;
+}
+
+
+/******************************************************************************
+ * Compare two SIDs.
+ ******************************************************************************/
+int winsec_sid_compare(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2)
+{
+  int i;
+
+  if (sid1 == sid2)
+    return 0;
+  if (!sid1)
+    return -1;
+  if (!sid2)
+    return 1;
+
+  /* Compare most likely different rids, first: i.e start at end */
+  if (sid1->num_auths != sid2->num_auths)
+    return sid1->num_auths - sid2->num_auths;
+
+  for (i = sid1->num_auths-1; i >= 0; --i)
+    if (sid1->sub_auths[i] != sid2->sub_auths[i])
+      return sid1->sub_auths[i] - sid2->sub_auths[i];
+
+  return winsec_sid_compare_auth(sid1, sid2);
+}
+
+
+/******************************************************************************
+ * Compare two SIDs.
+ ******************************************************************************/
+bool winsec_sid_equal(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2)
+{
+  return winsec_sid_compare(sid1, sid2) == 0;
+}
+
+
+/******************************************************************************
+ * Compares two WINSEC_DESC structures.
+ ******************************************************************************/
+bool winsec_desc_equal(WINSEC_DESC* s1, WINSEC_DESC* s2)
+{
+  /* Trivial cases */
+  if (!s1 && !s2)
+    return true;
+  if (!s1 || !s2)
+    return false;
+
+  /* Check top level stuff */
+  if (s1->revision != s2->revision)
+    return false;
+
+  if (s1->control != s2->control)
+    return false;
+
+  /* Check owner and group */
+  if (!winsec_sid_equal(s1->owner_sid, s2->owner_sid))
+    return false;
+
+  if (!winsec_sid_equal(s1->grp_sid, s2->grp_sid)) 
+    return false;
+
+  /* Check ACLs present in one but not the other */
+  if ((s1->dacl && !s2->dacl) || (!s1->dacl && s2->dacl) ||
+      (s1->sacl && !s2->sacl) || (!s1->sacl && s2->sacl)) 
+  { return false; }
+
+  /* Sigh - we have to do it the hard way by iterating over all
+     the ACEs in the ACLs */
+  if(!winsec_acl_equal(s1->dacl, s2->dacl) || !winsec_acl_equal(s1->sacl, s2->sacl)) 
+    return false;
+
+  return true;
+}
+
+
+
+/******************************************************************************
+ * Compares two WINSEC_ACL structures.
+ ******************************************************************************/
+bool winsec_acl_equal(WINSEC_ACL* s1, WINSEC_ACL* s2)
+{
+  unsigned int i, j;
+
+  /* Trivial cases */
+  if (!s1 && !s2) 
+    return true;
+  if (!s1 || !s2) 
+    return false;
+
+  /* Check top level stuff */
+  if (s1->revision != s2->revision)
+    return false;
+
+  if (s1->num_aces != s2->num_aces)
+    return false;
+
+  /* The ACEs could be in any order so check each ACE in s1 against 
+     each ACE in s2. */
+
+  for (i = 0; i < s1->num_aces; i++)
+  {
+    bool found = false;
+
+    for (j = 0; j < s2->num_aces; j++) 
+    {
+      if (winsec_ace_equal(s1->aces[i], s2->aces[j])) 
+      {
+	found = true;
+	break;
+      }
+    }
+
+    if (!found)
+      return false;
+  }
+
+  return true;
+}
+
+
+/******************************************************************************
+ * Compares two WINSEC_ACE structures.
+ ******************************************************************************/
+bool winsec_ace_equal(WINSEC_ACE* s1, WINSEC_ACE* s2)
+{
+  /* Trivial cases */
+  if (!s1 && !s2) 
+    return true;
+  if (!s1 || !s2) 
+    return false;
+
+  /* Check top level stuff */
+  if (s1->type != s2->type || s1->flags != s2->flags ||
+      s1->access_mask != s2->access_mask)
+  { return false; }
+
+  /* Check SID */
+  if (!winsec_sid_equal(s1->trustee, s2->trustee))
+    return false;
+
+  return true;
+}
+
+
+/******************************************************************************
+ * Check if ACE has OBJECT type.
+ ******************************************************************************/
+bool winsec_ace_object(uint8_t type)
+{
+  if (type == WINSEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
+      type == WINSEC_ACE_TYPE_ACCESS_DENIED_OBJECT ||
+      type == WINSEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT ||
+      type == WINSEC_ACE_TYPE_SYSTEM_ALARM_OBJECT) 
+  { return true; }
+
+  return false;
+}
diff --git a/src/Makefile b/src/Makefile
index 8fde7f1..880485d 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,19 +1,19 @@
-# $Id: Makefile 110 2008-04-29 22:59:55Z tim $
+# $Id: Makefile 143 2009-02-13 03:24:27Z tim $
 
 ################################################################################
 
-REGLOOKUP=$(BUILD_BIN)/reglookup
-REGLOOKUP_RECOVER=$(BUILD_BIN)/reglookup-recover
+REGLOOKUP=$(BUILD_BIN)/reglookup$(BIN_EXT)
+REGLOOKUP_RECOVER=$(BUILD_BIN)/reglookup-recover$(BIN_EXT)
 OBJ=$(wildcard ../lib/*.o)
 FILES=$(REGLOOKUP) $(REGLOOKUP_RECOVER)
 
 all: $(FILES)
 
 $(REGLOOKUP): reglookup.o $(OBJ)
-	$(CC) $(CFLAGS) $(OPTS) $(LIB) -o $@ reglookup.o $(OBJ)
+	$(CC) $(CFLAGS) $(OPTS) $(LIB) -o $@ reglookup.o $(OBJ) $(EXTRA_OBJ)
 
 $(REGLOOKUP_RECOVER): reglookup-recover.o $(OBJ)
-	$(CC) $(CFLAGS) $(OPTS) $(LIB) -o $@ reglookup-recover.o $(OBJ)
+	$(CC) $(CFLAGS) $(OPTS) $(LIB) -o $@ reglookup-recover.o $(OBJ) $(EXTRA_OBJ)
 
 reglookup.o: reglookup.c
 	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ reglookup.c
diff --git a/src/common.c b/src/common.c
index 4012c4b..734ddb1 100644
--- a/src/common.c
+++ b/src/common.c
@@ -18,7 +18,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
  *
- * $Id: common.c 121 2008-08-09 17:22:26Z tim $
+ * $Id: common.c 154 2009-06-03 15:21:47Z tim $
  */
 
 #include <iconv.h>
@@ -28,7 +28,13 @@ const char* key_special_chars = ",\"\\/";
 const char* subfield_special_chars = ",\"\\|";
 const char* common_special_chars = ",\"\\";
 
-#define REGLOOKUP_VERSION "0.9.0"
+#define REGLOOKUP_VERSION "0.11.0"
+
+#define REGLOOKUP_EXIT_OK       0
+#define REGLOOKUP_EXIT_OSERR   71
+#define REGLOOKUP_EXIT_USAGE   64
+#define REGLOOKUP_EXIT_DATAERR 65
+#define REGLOOKUP_EXIT_NOINPUT 66
 
 
 void bailOut(int code, char* message)
@@ -37,6 +43,23 @@ void bailOut(int code, char* message)
   exit(code);
 }
 
+void printMsgs(REGFI_FILE* f)
+{
+  char* msgs = regfi_get_messages(f);
+  if(msgs != NULL)
+  {
+    fprintf(stderr, "%s", msgs);
+    free(msgs);
+  }
+}
+
+void clearMsgs(REGFI_FILE* f)
+{
+  char* msgs = regfi_get_messages(f);
+  if(msgs != NULL)
+    free(msgs);
+}
+
 
 /* Returns a newly malloc()ed string which contains original buffer,
  * except for non-printable or special characters are quoted in hex
@@ -51,9 +74,11 @@ static char* quote_buffer(const unsigned char* str,
   unsigned int num_written = 0;
 
   unsigned int buf_len = sizeof(char)*(len+1);
-  char* ret_val = malloc(buf_len);
+  char* ret_val = NULL; 
   char* tmp_buf;
 
+  if(buf_len > 0) 
+    ret_val = malloc(buf_len);
   if(ret_val == NULL)
     return NULL;
 
@@ -122,11 +147,11 @@ static char* quote_string(const char* str, const char* special)
  * Convert from UTF-16LE to ASCII.  Accepts a Unicode buffer, uni, and
  * it's length, uni_max.  Writes ASCII to the buffer ascii, whose size
  * is ascii_max.  Writes at most (ascii_max-1) bytes to ascii, and null
- * terminates the string.  Returns the length of the string stored in
+ * terminates the string.  Returns the length of the data written to
  * ascii.  On error, returns a negative errno code.
  */
 static int uni_to_ascii(unsigned char* uni, char* ascii, 
-			unsigned int uni_max, unsigned int ascii_max)
+			uint32 uni_max, uint32 ascii_max)
 {
   char* inbuf = (char*)uni;
   char* outbuf = ascii;
@@ -135,7 +160,7 @@ static int uni_to_ascii(unsigned char* uni, char* ascii,
   int ret;
 
   /* Set up conversion descriptor. */
-  conv_desc = iconv_open("US-ASCII", "UTF-16LE");
+  conv_desc = iconv_open("US-ASCII//TRANSLIT", "UTF-16LE");
 
   ret = iconv(conv_desc, &inbuf, &in_len, &outbuf, &out_len);
   if(ret == -1)
@@ -146,7 +171,50 @@ static int uni_to_ascii(unsigned char* uni, char* ascii,
   *outbuf = '\0';
 
   iconv_close(conv_desc);  
-  return strlen(ascii);
+  return ascii_max-out_len-1;
+}
+
+
+static char* quote_unicode(unsigned char* uni, uint32 length, 
+			   const char* special, char** error_msg)
+{
+  char* ret_val;
+  char* ascii = NULL;
+  char* tmp_err;
+  int ret_err;
+  *error_msg = NULL;
+
+  if(length+1 > 0)
+    ascii = malloc(length+1);
+  if(ascii == NULL)
+  {
+    *error_msg = (char*)malloc(27);
+    if(*error_msg == NULL)
+      return NULL;
+    strcpy(*error_msg, "Memory allocation failure.");
+    return NULL;
+  }
+  
+  ret_err = uni_to_ascii(uni, ascii, length, length+1);
+  if(ret_err < 0)
+  {
+    free(ascii);
+    tmp_err = strerror(-ret_err);
+    *error_msg = (char*)malloc(61+strlen(tmp_err));
+    if(*error_msg == NULL)
+      return NULL;
+
+    sprintf(*error_msg, 
+	    "Unicode conversion failed with '%s'. Quoting as binary.", tmp_err);
+    ret_val = quote_buffer(uni, length, special);
+  }
+  else
+  {
+    ret_val = quote_string(ascii, special);
+    free(ascii);
+  }
+  
+  return ret_val;
 }
 
 
@@ -160,22 +228,24 @@ static int uni_to_ascii(unsigned char* uni, char* ascii,
  * is the responsibility of the caller to free both a non-NULL return
  * value, and a non-NULL (*error_msg).
  */
+/* XXX: Part of this function's logic should be pushed into the regfi API.
+ *      The structures should be parsed and stored with VK records and only 
+ *      escaped/encoded later in reglookup and reglookup-recover.
+ */
 static char* data_to_ascii(unsigned char* datap, uint32 len, uint32 type, 
 			   char** error_msg)
 {
   char* asciip;
   char* ascii;
-  unsigned char* cur_str;
-  char* cur_ascii;
+  char* ascii_tmp;
   char* cur_quoted;
-  char* tmp_err;
-  const char* str_type;
+  char* tmp_err = NULL;
+  const char* delim;
   uint32 i;
   uint32 cur_str_len;
-  uint32 ascii_max, cur_str_max;
-  uint32 str_rem, cur_str_rem, alen;
+  uint32 ascii_max;
+  uint32 str_rem, alen;
   int ret_err;
-  unsigned short num_nulls;
 
   if(datap == NULL)
   {
@@ -193,39 +263,23 @@ static char* data_to_ascii(unsigned char* datap, uint32 len, uint32 type,
   case REG_EXPAND_SZ:
     /* REG_LINK is a symbolic link, stored as a unicode string. */
   case REG_LINK:
-    ascii_max = sizeof(char)*(len+1);
-    ascii = malloc(ascii_max);
-    if(ascii == NULL)
-      return NULL;
-    
     /* Sometimes values have binary stored in them.  If the unicode
      * conversion fails, just quote it raw.
      */
-    ret_err = uni_to_ascii(datap, ascii, len, ascii_max);
-    if(ret_err < 0)
+    cur_quoted = quote_unicode(datap, len, common_special_chars, &tmp_err);
+    if(cur_quoted == NULL)
     {
-      tmp_err = strerror(-ret_err);
-      str_type = regfi_type_val2str(type);
-      *error_msg = (char*)malloc(65+strlen(str_type)+strlen(tmp_err)+1);
-      if(*error_msg == NULL)
+      if(tmp_err == NULL && (*error_msg = (char*)malloc(49)) != NULL)
+	strcpy(*error_msg, "Buffer could not be quoted due to unknown error.");
+      else if((*error_msg = (char*)malloc(42+strlen(tmp_err))) != NULL)
       {
-	free(ascii);
-	return NULL;
+	sprintf(*error_msg, "Buffer could not be quoted due to error: %s", 
+		tmp_err);
+	free(tmp_err);
       }
-      sprintf(*error_msg, "Unicode conversion failed on %s field; "
-	       "printing as binary.  Error: %s", str_type, tmp_err);
-      
-      cur_quoted = quote_buffer(datap, len, common_special_chars);
-    }
-    else
-      cur_quoted = quote_string(ascii, common_special_chars);
-    free(ascii);
-    if(cur_quoted == NULL)
-    {
-      *error_msg = (char*)malloc(27+1);
-      if(*error_msg != NULL)
-	strcpy(*error_msg, "Buffer could not be quoted.");
     }
+    else if (tmp_err != NULL)
+      *error_msg = tmp_err;
     return cur_quoted;
     break;
 
@@ -263,104 +317,73 @@ static char* data_to_ascii(unsigned char* datap, uint32 len, uint32 type,
     return ascii;
     break;
     
-
-  /* XXX: this MULTI_SZ parser is pretty inefficient.  Should be
-   *      redone with fewer malloc calls and better string concatenation.
-   *      Also, gives lame output when "\0\0" is the string.
-   */
   case REG_MULTI_SZ:
     ascii_max = sizeof(char)*(len*4+1);
-    cur_str_max = sizeof(char)*(len+1);
-    cur_str = malloc(cur_str_max);
-    cur_ascii = malloc(cur_str_max);
-    ascii = malloc(ascii_max);
-    if(ascii == NULL || cur_str == NULL || cur_ascii == NULL)
+    ascii_tmp = malloc(ascii_max);
+    if(ascii_tmp == NULL)
       return NULL;
 
-    /* Reads until it reaches 4 consecutive NULLs, 
-     * which is two nulls in unicode, or until it reaches len, or until we
-     * run out of buffer.  The latter should never happen, but we shouldn't
-     * trust our file to have the right lengths/delimiters.
+    /* Attempt to convert entire string from UTF-16LE to ASCII, 
+     * then parse and quote fields individually.
+     * If this fails, simply quote entire buffer as binary. 
      */
-    asciip = ascii;
-    num_nulls = 0;
-    str_rem = ascii_max;
-    cur_str_rem = cur_str_max;
-    cur_str_len = 0;
-
-    for(i=0; (i < len) && str_rem > 0; i++)
+    ret_err = uni_to_ascii(datap, ascii_tmp, len, ascii_max);
+    if(ret_err < 0)
     {
-      *(cur_str+cur_str_len) = *(datap+i);
-      if(*(cur_str+cur_str_len) == 0)
-	num_nulls++;
-      else
-	num_nulls = 0;
-      cur_str_len++;
-
-      if(num_nulls == 2)
+      tmp_err = strerror(-ret_err);
+      *error_msg = (char*)malloc(61+strlen(tmp_err));
+      if(*error_msg == NULL)
       {
-	ret_err = uni_to_ascii(cur_str, cur_ascii, cur_str_len-1, cur_str_max);
-	if(ret_err < 0)
-	{
-	  /* XXX: should every sub-field error be enumerated? */
-	  if(*error_msg == NULL)
-	  {
-	    tmp_err = strerror(-ret_err);
-	    *error_msg = (char*)malloc(90+strlen(tmp_err)+1);
-	    if(*error_msg == NULL)
-	    {
-	      free(cur_str);
-	      free(cur_ascii);
-	      free(ascii);
-	      return NULL;
-	    }
-	    sprintf(*error_msg, "Unicode conversion failed on at least one "
-		    "MULTI_SZ sub-field; printing as binary.  Error: %s",
-		    tmp_err);
-	  }
-	  cur_quoted = quote_buffer(cur_str, cur_str_len-1, 
-				    subfield_special_chars);
-	}
-	else
-	  cur_quoted = quote_string(cur_ascii, subfield_special_chars);
-
-	alen = snprintf(asciip, str_rem, "%s", cur_quoted);
-	asciip += alen;
-	str_rem -= alen;
-	free(cur_quoted);
+	free(ascii_tmp);
+	return NULL;
+      }
 
-	if(*(datap+i+1) == 0 && *(datap+i+2) == 0)
-	  break;
-	else
+      sprintf(*error_msg, "MULTI_SZ unicode conversion"
+	      " failed with '%s'. Quoting as binary.", tmp_err);
+      ascii = quote_buffer(datap, len, subfield_special_chars);
+    }
+    else
+    {
+      ascii = malloc(ascii_max);
+      if(ascii == NULL)
+      {
+	free(ascii_tmp);
+	return NULL;
+      }
+      asciip = ascii;
+      asciip[0] = '\0';
+      str_rem = ascii_max;
+      delim = "";
+      for(i=0; i<ret_err; i+=cur_str_len+1)
+      {
+	cur_str_len = strlen(ascii_tmp+i);
+	if(ascii_tmp[i] != '\0')
 	{
-	  if(str_rem > 0)
+	  cur_quoted = quote_string(ascii_tmp+i, subfield_special_chars);
+	  if(cur_quoted != NULL)
 	  {
-	    asciip[0] = '|';
-	    asciip[1] = '\0';
-	    asciip++;
-	    str_rem--;
+	    alen = snprintf(asciip, str_rem, "%s%s", delim, cur_quoted);
+	    asciip += alen;
+	    str_rem -= alen;
+	    free(cur_quoted);
 	  }
-	  memset(cur_str, 0, cur_str_max);
-	  cur_str_len = 0;
-	  num_nulls = 0;
-	  /* To eliminate leading nulls in subsequent strings. */
-	  i++;
 	}
+	delim = "|";
       }
     }
-    *asciip = 0;
-    free(cur_str);
-    free(cur_ascii);
+
+    free(ascii_tmp);
     return ascii;
     break;
 
   /* XXX: Dont know what to do with these yet, just print as binary... */
   default:
-    /* XXX: It would be really nice if this message somehow included the
-     *      name of the current value we're having trouble with, since
-     *      stderr/stdout don't always sync nicely.
-     */
-    fprintf(stderr, "WARNING: Unrecognized registry data type (0x%.8X); quoting as binary.\n", type);
+    *error_msg = (char*)malloc(65);
+    if(*error_msg == NULL)
+      return NULL;
+    sprintf(*error_msg,
+	    "Unrecognized registry data type (0x%.8X); quoting as binary.",
+	    type);
     
   case REG_NONE:
   case REG_RESOURCE_LIST:
diff --git a/src/reglookup-recover.c b/src/reglookup-recover.c
index e761a41..b549a78 100644
--- a/src/reglookup-recover.c
+++ b/src/reglookup-recover.c
@@ -1,7 +1,7 @@
 /*
  * This program attempts to recover deleted data structures in a registry hive.
  *
- * Copyright (C) 2008 Timothy D. Morgan
+ * Copyright (C) 2008-2009 Timothy D. Morgan
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -14,18 +14,18 @@
  * 
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: reglookup-recover.c 121 2008-08-09 17:22:26Z tim $
+ * $Id: reglookup-recover.c 152 2009-06-02 20:00:38Z tim $
  */
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <sysexits.h>
 
-#include "../include/regfi.h"
-#include "../include/range_list.h"
-#include "../include/lru_cache.h"
+#include "talloc.h"
+#include "regfi.h"
+#include "range_list.h"
+#include "lru_cache.h"
 
 
 /* Globals, influenced by command line parameters */
@@ -66,7 +66,7 @@ char* getQuotedData(int fd, uint32 offset, uint32 length)
 }
 
 
-void printKey(REGF_FILE* f, REGF_NK_REC* nk, const char* prefix)
+void printKey(REGFI_FILE* f, REGFI_NK_REC* nk, const char* prefix)
 {
   char mtime[20];
   time_t tmp_time[1];
@@ -83,10 +83,11 @@ void printKey(REGF_FILE* f, REGF_NK_REC* nk, const char* prefix)
   {
     quoted_name = malloc(1*sizeof(char));
     if(quoted_name == NULL)
-      bailOut(EX_OSERR, "ERROR: Could not allocate sufficient memory.\n");
+      bailOut(REGLOOKUP_EXIT_OSERR, 
+	      "ERROR: Could not allocate sufficient memory.\n");
     quoted_name[0] = '\0';
 
-    fprintf(stderr, "WARNING: NULL key name in NK record at offset %.8X.\n",
+    fprintf(stderr, "WARN: NULL key name in NK record at offset %.8X.\n",
 	    nk->offset);
   }
 
@@ -98,10 +99,11 @@ void printKey(REGF_FILE* f, REGF_NK_REC* nk, const char* prefix)
   
   if(print_parsedraw)
     free(quoted_raw);
+  free(quoted_name);
 }
 
 
-void printValue(REGF_FILE* f, const REGF_VK_REC* vk, const char* prefix)
+void printValue(REGFI_FILE* f, const REGFI_VK_REC* vk, const char* prefix)
 {
   char* quoted_value = NULL;
   char* quoted_name = NULL;
@@ -121,11 +123,11 @@ void printValue(REGF_FILE* f, const REGF_VK_REC* vk, const char* prefix)
    *      name of the current value we're having trouble with, since
    *      stderr/stdout don't always sync nicely.
    */
-  if(size > VK_MAX_DATA_LENGTH)
+  if(size > REGFI_VK_MAX_DATA_LENGTH)
   {
-    fprintf(stderr, "WARNING: value data size %d larger than "
-	    "%d, truncating...\n", size, VK_MAX_DATA_LENGTH);
-    size = VK_MAX_DATA_LENGTH;
+    fprintf(stderr, "WARN: value data size %d larger than "
+	    "%d, truncating...\n", size, REGFI_VK_MAX_DATA_LENGTH);
+    size = REGFI_VK_MAX_DATA_LENGTH;
   }
   
   quoted_name = quote_string(vk->valuename, key_special_chars);
@@ -138,7 +140,7 @@ void printValue(REGF_FILE* f, const REGF_VK_REC* vk, const char* prefix)
      */
     quoted_name = malloc(1*sizeof(char));
     if(quoted_name == NULL)
-      bailOut(EX_OSERR, "ERROR: Could not allocate sufficient memory.\n");
+      bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not allocate sufficient memory.\n");
     quoted_name[0] = '\0';
   }
 
@@ -147,19 +149,19 @@ void printValue(REGF_FILE* f, const REGF_VK_REC* vk, const char* prefix)
   {
     quoted_value = malloc(1*sizeof(char));
     if(quoted_value == NULL)
-      bailOut(EX_OSERR, "ERROR: Could not allocate sufficient memory.\n");
+      bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not allocate sufficient memory.\n");
     quoted_value[0] = '\0';
 
     if(conv_error == NULL)
-      fprintf(stderr, "WARNING: Could not quote value for '%s/%s'.  "
+      fprintf(stderr, "WARN: Could not quote value for '%s/%s'.  "
 	      "Memory allocation failure likely.\n", prefix, quoted_name);
     else if(print_verbose)
-      fprintf(stderr, "WARNING: Could not quote value for '%s/%s'.  "
+      fprintf(stderr, "WARN: Could not quote value for '%s/%s'.  "
 	      "Returned error: %s\n", prefix, quoted_name, conv_error);
   }
   /* XXX: should these always be printed? */
   else if(conv_error != NULL && print_verbose)
-    fprintf(stderr, "VERBOSE: While quoting value for '%s/%s', "
+    fprintf(stderr, "INFO: While quoting value for '%s/%s', "
 	    "warning returned: %s\n", prefix, quoted_name, conv_error);
 
 
@@ -187,7 +189,7 @@ void printValue(REGF_FILE* f, const REGF_VK_REC* vk, const char* prefix)
 }
 
 
-void printSK(REGF_FILE* f, REGF_SK_REC* sk)
+void printSK(REGFI_FILE* f, REGFI_SK_REC* sk)
 {
   char* quoted_raw = NULL;
   char* empty_str = "";
@@ -225,7 +227,7 @@ void printSK(REGF_FILE* f, REGF_SK_REC* sk)
 }
 
 
-int printCell(REGF_FILE* f, uint32 offset)
+int printCell(REGFI_FILE* f, uint32 offset)
 {
   char* quoted_buf;
   uint32 cell_length;
@@ -250,65 +252,75 @@ int printCell(REGF_FILE* f, uint32 offset)
  * Paths returned must be free()d.
  */
 /* XXX: This is not terribly efficient, as it may reparse many keys 
- *      repeatedly.  Should try to add caching.  Also, piecing the path 
- *      together is slow and redundant.
+ *      repeatedly.  Should try to add caching.
  */
-char* getParentPath(REGF_FILE* f, REGF_NK_REC* nk)
+char* getParentPath(REGFI_FILE* f, REGFI_NK_REC* nk)
 {
-  void_stack* path_stack = void_stack_new(REGF_MAX_DEPTH);
-  REGF_HBIN* hbin;
-  REGF_NK_REC* cur_ancestor;
+  void_stack* path_stack = void_stack_new(REGFI_MAX_DEPTH);
+  const REGFI_HBIN* hbin;
+  REGFI_NK_REC* cur_ancestor;
   char* ret_val;
-  char* path_element;
-  char* tmp_str;
-  uint32 virt_offset, i, stack_size, ret_val_size, ret_val_left, element_size;
+  uint32 virt_offset, i, stack_size, ret_val_size, ret_val_used;
   uint32 max_length;
-
+  REGFI_BUFFER* path_element;
+  
   /* The path_stack size limit should guarantee that we don't recurse forever. */
   virt_offset = nk->parent_off;
-  while(virt_offset != REGF_OFFSET_NONE)
+  ret_val_size = 1; /* NUL */
+  while(virt_offset != REGFI_OFFSET_NONE)
   {  
     hbin = regfi_lookup_hbin(f, virt_offset);
     if(hbin == NULL)
-      virt_offset = REGF_OFFSET_NONE;
+      virt_offset = REGFI_OFFSET_NONE;
     else
     {
       max_length = hbin->block_size + hbin->file_off 
-	- (virt_offset+REGF_BLOCKSIZE);
-      cur_ancestor = regfi_parse_nk(f, virt_offset+REGF_BLOCKSIZE, 
+	- (virt_offset+REGFI_REGF_SIZE);
+      cur_ancestor = regfi_parse_nk(f, virt_offset+REGFI_REGF_SIZE, 
 				    max_length, true);
+      printMsgs(f);
+
       if(cur_ancestor == NULL)
-	virt_offset = REGF_OFFSET_NONE;
+	virt_offset = REGFI_OFFSET_NONE;
       else
       {
-	if(cur_ancestor->key_type == NK_TYPE_ROOTKEY)
-	  virt_offset = REGF_OFFSET_NONE;
+	if(cur_ancestor->key_type & REGFI_NK_FLAG_ROOT)
+	  virt_offset = REGFI_OFFSET_NONE;
 	else
 	  virt_offset = cur_ancestor->parent_off;
 	
-	path_element = quote_string(cur_ancestor->keyname, key_special_chars);
-	if(path_element == NULL || !void_stack_push(path_stack, path_element))
+	path_element = talloc(path_stack, REGFI_BUFFER);
+	if(path_element != NULL)
+	  path_element->buf = (uint8*)quote_string(cur_ancestor->keyname, 
+						   key_special_chars);
+	  
+	if(path_element == NULL || path_element->buf == NULL 
+	   || !void_stack_push(path_stack, path_element))
 	{
-	  free(cur_ancestor->keyname);
-	  free(cur_ancestor);
-	  void_stack_free_deep(path_stack);
+	  /* XXX: Need to add a warning here */
+	  regfi_free_key(cur_ancestor);
+	  void_stack_free(path_stack);
 	  return NULL;
 	}
 
-	regfi_key_free(cur_ancestor);
+	/* Path element and preceeding delimiter
+	 * Note that this integer can't overflow since key name lengths are
+	 * 16 bits and the max depth is 512.
+	 */
+	path_element->len = strlen((char*)path_element->buf);
+	ret_val_size += path_element->len + 1;
+
+	regfi_free_key(cur_ancestor);
       }
     }
   }
   
   stack_size = void_stack_size(path_stack);
-  ret_val_size = 16*stack_size;
-  if(ret_val_size == 0)
-    ret_val_size = 1;
-  ret_val_left = ret_val_size;
+  ret_val_used = 0;
   ret_val = malloc(ret_val_size);
   if(ret_val == NULL)
   {
-    void_stack_free_deep(path_stack);
+    void_stack_free(path_stack);
     return NULL;
   }
   ret_val[0] = '\0';
@@ -316,23 +328,11 @@ char* getParentPath(REGF_FILE* f, REGF_NK_REC* nk)
   for(i=0; i<stack_size; i++)
   {
     path_element = void_stack_pop(path_stack);
-    element_size = strlen(path_element);
-    if(ret_val_left < element_size+2)
-    {
-      ret_val_size += element_size+16;
-      ret_val_left += element_size+16;
-      tmp_str = (char*)realloc(ret_val, ret_val_size);
-      if(tmp_str == NULL)
-      {
-	free(ret_val);
-	void_stack_free_deep(path_stack);
-	return NULL;
-      }
-      ret_val = tmp_str;
-    }
-
-    ret_val_left -= snprintf(ret_val+ret_val_size-ret_val_left,ret_val_left, "/%s", path_element);
-    free(path_element);
+    snprintf(ret_val+ret_val_used, ret_val_size-ret_val_used, 
+	     "/%s", path_element->buf);
+    ret_val_used += path_element->len + 1;
+    free(path_element->buf);
+    talloc_free(path_element);
   }
   void_stack_free(path_stack);
 
@@ -412,31 +412,154 @@ bool removeRange(range_list* rl, uint32 offset, uint32 length)
 }
 
 
+int extractVKs(REGFI_FILE* f,
+	       range_list* unalloc_cells,
+	       range_list* unalloc_values)
+{
+  const range_list_element* cur_elem;
+  REGFI_VK_REC* vk;
+  uint32 i, j;
+
+  for(i=0; i < range_list_size(unalloc_cells); i++)
+  {
+    printMsgs(f);
+    cur_elem = range_list_get(unalloc_cells, i);
+    for(j=0; j <= cur_elem->length; j+=8)
+    {
+      vk = regfi_parse_vk(f, cur_elem->offset+j, 
+			   cur_elem->length-j, false);
+      printMsgs(f);
+
+      if(vk != NULL)
+      {
+	if(!range_list_add(unalloc_values, vk->offset,
+			   vk->cell_size, vk))
+	{
+	  fprintf(stderr, "ERROR: Couldn't add value to unalloc_values.\n");
+	  return 20;
+	}
+	j+=vk->cell_size-8;
+      }
+    }
+  }
+
+  /* Remove value ranges from the unalloc_cells before we continue. */
+  for(i=0; i<range_list_size(unalloc_values); i++)
+  {
+    cur_elem = range_list_get(unalloc_values, i);
+    if(!removeRange(unalloc_cells, cur_elem->offset, cur_elem->length))
+      return 30;
+  }
+
+  return 0;
+}
+
+
+int extractDataCells(REGFI_FILE* f,
+		     range_list* unalloc_cells,
+		     range_list* unalloc_values)
+{
+  const range_list_element* cur_elem;
+  REGFI_VK_REC* vk;
+  const REGFI_HBIN* hbin;
+  REGFI_BUFFER data;
+  uint32 i, off, data_offset, data_maxsize;
+
+  for(i=0; i<range_list_size(unalloc_values); i++)
+  {
+    cur_elem = range_list_get(unalloc_values, i);
+    vk = (REGFI_VK_REC*)cur_elem->data;
+    if(vk == NULL)
+      return 40;
+
+    if(vk->data_size == 0)
+      vk->data = NULL;
+    else
+    {
+      off = vk->data_off+REGFI_REGF_SIZE;
+
+      if(vk->data_in_offset)
+      {
+	data = regfi_load_data(f, vk->type, vk->data_off,
+			       vk->data_size, 4,
+			       vk->data_in_offset, false);
+	vk->data = data.buf;
+	vk->data_size = data.len;
+      }
+      else if(range_list_has_range(unalloc_cells, off, vk->data_size))
+      {
+	hbin = regfi_lookup_hbin(f, vk->data_off);
+	if(hbin)
+	{
+	  data_offset = vk->data_off+REGFI_REGF_SIZE;
+	  data_maxsize = hbin->block_size + hbin->file_off - data_offset;
+	  data = regfi_load_data(f, vk->type, data_offset, 
+				 vk->data_size, data_maxsize, 
+				 vk->data_in_offset, false);
+	  vk->data = data.buf;
+	  vk->data_size = data.len;
+
+	  if(vk->data != NULL)
+	  {
+	    /* XXX: The following may not make sense now in light of big data
+	     *      records.
+	     */
+	    /* XXX: This strict checking prevents partial recovery of data 
+	     *      cells.  Also, see code for regfi_load_data and note that
+	     *      lengths indicated in VK records are sometimes just plain 
+	     *      wrong.  Need a feedback mechanism to be more fuzzy with 
+	     *      data cell lengths and the ranges removed. 
+	     *
+	     *      The introduction of REGFI_BUFFER in regfi_load_data has 
+	     *      fixed some of this.  Should review again with respect to 
+	     *      the other issues mentioned above though.
+	     */
+	    /* A data record was recovered. Remove from unalloc_cells. */
+	    if(!removeRange(unalloc_cells, off, vk->data_size))
+	      return 50;
+	  }
+	}
+	else
+	  vk->data = NULL;
+      }
+    }
+  }
+
+  return 0;
+}
+
+
 /* NOTE: unalloc_keys should be an empty range_list. */
-int extractKeys(REGF_FILE* f, 
+int extractKeys(REGFI_FILE* f, 
 		range_list* unalloc_cells, 
 		range_list* unalloc_keys)
 {
   const range_list_element* cur_elem;
-  REGF_NK_REC* key;
+  REGFI_NK_REC* key;
   uint32 i, j;
+  int error_code = 0;
 
   for(i=0; i < range_list_size(unalloc_cells); i++)
   {
+    printMsgs(f);
     cur_elem = range_list_get(unalloc_cells, i);
     for(j=0; cur_elem->length > REGFI_NK_MIN_LENGTH 
 	  && j <= cur_elem->length-REGFI_NK_MIN_LENGTH; j+=8)
     {
       key = regfi_parse_nk(f, cur_elem->offset+j,
 			   cur_elem->length-j, false);
+      printMsgs(f);
+
       if(key != NULL)
       {
 	if(!range_list_add(unalloc_keys, key->offset, 
 			   key->cell_size, key))
 	{
 	  fprintf(stderr, "ERROR: Couldn't add key to unalloc_keys.\n");
-	  return 20;
+	  error_code = 20;
+	  goto fail;
 	}
+	talloc_steal(unalloc_keys, key);
 	j+=key->cell_size-8;
       }
     }
@@ -446,19 +569,27 @@ int extractKeys(REGF_FILE* f,
   {
     cur_elem = range_list_get(unalloc_keys, i);
     if(!removeRange(unalloc_cells, cur_elem->offset, cur_elem->length))
-      return 30;
+    {
+      error_code = 30;
+      goto fail;
+    }
   }
 
   return 0;
-}
 
+ fail:
+  regfi_free_key(key);
+  return error_code;
+}
 
-int extractValueLists(REGF_FILE* f,
+int extractValueLists(REGFI_FILE* f,
 		      range_list* unalloc_cells,
-		      range_list* unalloc_keys)
+		      range_list* unalloc_keys,
+		      range_list* unalloc_linked_values)
 {
-  REGF_NK_REC* nk;
-  REGF_HBIN* hbin;
+  REGFI_NK_REC* nk;
+  REGFI_VK_REC* vk;
+  const REGFI_HBIN* hbin;
   const range_list_element* cur_elem;
   uint32 i, j, num_keys, off, values_length, max_length;
 
@@ -470,43 +601,32 @@ int extractValueLists(REGF_FILE* f,
       return 10;
     nk = cur_elem->data;
 
-    if(nk->num_values && (nk->values_off!=REGF_OFFSET_NONE))
+    if(nk->num_values && (nk->values_off!=REGFI_OFFSET_NONE))
     {
       hbin = regfi_lookup_hbin(f, nk->values_off);
       
       if(hbin != NULL)
       {
-	off = nk->values_off + REGF_BLOCKSIZE;
+	off = nk->values_off + REGFI_REGF_SIZE;
 	max_length = hbin->block_size + hbin->file_off - off;
-	/* XXX: This is a hack.  We parse all value-lists, VK records,
-	 *      and data records without regard for current allocation status.  
-	 *      On the off chance that such a record correctly parsed but is 
-	 *      actually a reallocated structure used by something else, we 
-	 *      simply prune it after the fact.  Would be faster to check this
-	 *      up front somehow.
-	 */
-	nk->values = regfi_load_valuelist(f, off, nk->num_values, max_length,
-					  false);
-	values_length = (nk->num_values+1)*sizeof(uint32);
-	if(values_length != (values_length & 0xFFFFFFF8))
-	  values_length = (values_length & 0xFFFFFFF8) + 8;
-
-	if(nk->values != NULL)
+	nk->values = regfi_load_valuelist(f, off, nk->num_values, 
+					  max_length, false);
+	if(nk->values != NULL && nk->values->elements != NULL)
 	{
+	  /* Number of elements in the value list may be shorter than advertised 
+	   * by NK record due to cell truncation.  We'll consider this valid and 
+	   * only throw out the whole value list if it bleeds into an already 
+	   * parsed structure.
+	   */
+	  values_length = (nk->values->num_values+1)*sizeof(uint32);
+	  if(values_length != (values_length & 0xFFFFFFF8))
+	    values_length = (values_length & 0xFFFFFFF8) + 8;
+
 	  if(!range_list_has_range(unalloc_cells, off, values_length))
 	  { /* We've parsed a values-list which isn't in the unallocated list,
-	     * so prune it. 
+	     * so prune it.
 	     */
-	    for(j=0; j<nk->num_values; j++)
-	    {
-	      if(nk->values[j] != NULL)
-	      {
-		if(nk->values[j]->data != NULL)
-		  free(nk->values[j]->data);
-		free(nk->values[j]);
-	      }
-	    }
-	    free(nk->values);
+	    talloc_free(nk->values);
 	    nk->values = NULL;
 	  }
 	  else
@@ -516,51 +636,32 @@ int extractValueLists(REGF_FILE* f,
 	    if(!removeRange(unalloc_cells, off, values_length))
 	      return 20;
 
-	    for(j=0; j < nk->num_values; j++)
+	    for(j=0; j < nk->values->num_values; j++)
 	    {
-	      if(nk->values[j] != NULL)
+	      /* Don't bother to restrict cell length here, since we'll
+	       * check our unalloc_cells range_list later. 
+	       */
+	      vk = regfi_parse_vk(f, nk->values->elements[j]+REGFI_REGF_SIZE,
+				  0x7FFFFFFF, false);
+	      printMsgs(f);
+	      
+	      if(vk != NULL)
 	      {
-		if(!range_list_has_range(unalloc_cells, nk->values[j]->offset, 
-					 nk->values[j]->cell_size))
-		{ /* We've parsed a value which isn't in the unallocated list,
-		   * so prune it.
-		   */
-		  if(nk->values[j]->data != NULL)
-		    free(nk->values[j]->data);
-		  free(nk->values[j]);
-		  nk->values[j] = NULL;
-		}
-		else
+		if(range_list_has_range(unalloc_cells, 
+					vk->offset, vk->cell_size))
 		{
-		  /* A VK record was recovered.  Remove from unalloc_cells
-		   * and inspect data.
-		   */
-		  if(!removeRange(unalloc_cells, nk->values[j]->offset,
-				  nk->values[j]->cell_size))
-		    return 21;
-
-		  /* Don't bother pruning or removing from unalloc_cells if 
-		   * there is no data, or it is stored in the offset.
-		   */
-		  if(nk->values[j]->data != NULL && !nk->values[j]->data_in_offset)
+		  if(!range_list_add(unalloc_linked_values, vk->offset,
+				     vk->cell_size, vk))
 		  {
-		    off = nk->values[j]->data_off+REGF_BLOCKSIZE;
-		    if(!range_list_has_range(unalloc_cells, off, 
-					     nk->values[j]->data_size))
-		    { /* We've parsed a data cell which isn't in the unallocated 
-		       * list, so prune it.
-		       */
-		      free(nk->values[j]->data);
-		      nk->values[j]->data = NULL;
-		    }
-		    else
-		    { /*A data record was recovered. Remove from unalloc_cells.*/
-		      if(!removeRange(unalloc_cells, off, 
-				      nk->values[j]->data_size))
-			return 22;
-		    }
+		    talloc_free(vk);
+		    return 30;
 		  }
+
+		  if(!removeRange(unalloc_cells, vk->offset, vk->cell_size))
+		    return 40;
 		}
+		else
+		  talloc_free(vk);
 	      }
 	    }
 	  }
@@ -573,89 +674,26 @@ int extractValueLists(REGF_FILE* f,
 }
 
 
-/* NOTE: unalloc_values should be an empty range_list. */
-int extractValues(REGF_FILE* f,
-		  range_list* unalloc_cells,
-		  range_list* unalloc_values)
-{
-  const range_list_element* cur_elem;
-  REGF_VK_REC* vk;
-  uint32 i, j, off;
-
-  for(i=0; i < range_list_size(unalloc_cells); i++)
-  {
-    cur_elem = range_list_get(unalloc_cells, i);
-    for(j=0; j <= cur_elem->length; j+=8)
-    {
-      vk = regfi_parse_vk(f, cur_elem->offset+j, 
-			   cur_elem->length-j, false);
-      if(vk != NULL)
-      {
-	if(!range_list_add(unalloc_values, vk->offset,
-			   vk->cell_size, vk))
-	{
-	  fprintf(stderr, "ERROR: Couldn't add value to unalloc_values.\n");
-	  return 20;
-	}
-	j+=vk->cell_size-8;
-      }
-    }
-  }
-  
-  /* Remove value ranges from the unalloc_cells before we continue. */
-  for(i=0; i<range_list_size(unalloc_values); i++)
-  {
-    cur_elem = range_list_get(unalloc_values, i);
-    if(!removeRange(unalloc_cells, cur_elem->offset, cur_elem->length))
-      return 30;
-  }
-
-  /* Now see if the data associated with each value is intact */
-  for(i=0; i<range_list_size(unalloc_values); i++)
-  {
-    cur_elem = range_list_get(unalloc_values, i);
-    vk = (REGF_VK_REC*)cur_elem->data;
-    if(vk == NULL)
-      return 40;
-
-    if(vk->data != NULL && !vk->data_in_offset)
-    {
-      off = vk->data_off+REGF_BLOCKSIZE;
-      if(!range_list_has_range(unalloc_cells, off, vk->data_size))
-      { /* We've parsed a data cell which isn't in the unallocated 
-	 * list, so prune it.
-	 */
-	free(vk->data);
-	vk->data = NULL;
-      }
-      else
-      { /*A data record was recovered. Remove from unalloc_cells.*/
-	if(!removeRange(unalloc_cells, off, vk->data_size))
-	  return 50;
-      }
-    }
-  }
-
-  return 0;
-}
-
 
 /* NOTE: unalloc_sks should be an empty range_list. */
-int extractSKs(REGF_FILE* f, 
+int extractSKs(REGFI_FILE* f, 
 	       range_list* unalloc_cells,
 	       range_list* unalloc_sks)
 {
   const range_list_element* cur_elem;
-  REGF_SK_REC* sk;
+  REGFI_SK_REC* sk;
   uint32 i, j;
 
   for(i=0; i < range_list_size(unalloc_cells); i++)
   {
+    printMsgs(f);
     cur_elem = range_list_get(unalloc_cells, i);
     for(j=0; j <= cur_elem->length; j+=8)
     {
       sk = regfi_parse_sk(f, cur_elem->offset+j, 
 			  cur_elem->length-j, false);
+      printMsgs(f);
+
       if(sk != NULL)
       {
 	if(!range_list_add(unalloc_sks, sk->offset,
@@ -664,6 +702,7 @@ int extractSKs(REGF_FILE* f,
 	  fprintf(stderr, "ERROR: Couldn't add sk to unalloc_sks.\n");
 	  return 20;
 	}
+	talloc_steal(unalloc_sks, sk);
 	j+=sk->cell_size-8;
       }
     }
@@ -682,25 +721,25 @@ int extractSKs(REGF_FILE* f,
 
 int main(int argc, char** argv)
 { 
-  REGF_FILE* f;
+  REGFI_FILE* f;
   const range_list_element* cur_elem;
   range_list* unalloc_cells;
   range_list* unalloc_keys;
+  range_list* unalloc_linked_values;
   range_list* unalloc_values;
   range_list* unalloc_sks;
   char** parent_paths;
   char* tmp_name;
   char* tmp_path;
-  REGF_NK_REC* tmp_key;
-  REGF_VK_REC* tmp_value;
+  REGFI_NK_REC* tmp_key;
+  REGFI_VK_REC* tmp_value;
   uint32 argi, arge, i, j, ret, num_unalloc_keys;
-  /* uint32 test_offset;*/
   
   /* Process command line arguments */
   if(argc < 2)
   {
     usage();
-    bailOut(EX_USAGE, "ERROR: Requires at least one argument.\n");
+    bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: Requires at least one argument.\n");
   }
   
   arge = argc-1;
@@ -724,20 +763,24 @@ int main(int argc, char** argv)
     {
       usage();
       fprintf(stderr, "ERROR: Unrecognized option: %s\n", argv[argi]);
-      bailOut(EX_USAGE, "");
+      bailOut(REGLOOKUP_EXIT_USAGE, "");
     }
   }
   /*test_offset = strtol(argv[argi++], NULL, 16);*/
 
   if((registry_file = strdup(argv[argi])) == NULL)
-    bailOut(EX_OSERR, "ERROR: Memory allocation problem.\n");
+    bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Memory allocation problem.\n");
 
   f = regfi_open(registry_file);
   if(f == NULL)
   {
     fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file);
-    bailOut(EX_NOINPUT, "");
+    bailOut(REGLOOKUP_EXIT_NOINPUT, "");
   }
+  if(print_verbose)
+    regfi_set_message_mask(f, REGFI_MSG_ERROR|REGFI_MSG_WARN|REGFI_MSG_INFO);
+  else
+    regfi_set_message_mask(f, REGFI_MSG_ERROR);
 
   if(print_header)
     printf("OFFSET,REC_LENGTH,REC_TYPE,PATH,NAME,"
@@ -755,6 +798,10 @@ int main(int argc, char** argv)
   if(unalloc_keys == NULL)
     return 10;
 
+  unalloc_linked_values = range_list_new();
+  if(unalloc_linked_values == NULL)
+    return 10;
+
   unalloc_values = range_list_new();
   if(unalloc_values == NULL)
     return 10;
@@ -770,21 +817,35 @@ int main(int argc, char** argv)
     return ret;
   }
 
-  ret = extractValueLists(f, unalloc_cells, unalloc_keys);
+  ret = extractValueLists(f, unalloc_cells, unalloc_keys,unalloc_linked_values);
   if(ret != 0)
   {
     fprintf(stderr, "ERROR: extractValueLists() failed with %d.\n", ret);
     return ret;
   }
 
-  /* Carve any orphan values and associated data */
-  ret = extractValues(f, unalloc_cells, unalloc_values);
+  /* Carve any orphan values */
+  ret = extractVKs(f, unalloc_cells, unalloc_values);
   if(ret != 0)
   {
-    fprintf(stderr, "ERROR: extractValues() failed with %d.\n", ret);
+    fprintf(stderr, "ERROR: extractVKs() failed with %d.\n", ret);
     return ret;
   }
 
+  /* Carve any data associated with VK records */
+  ret = extractDataCells(f, unalloc_cells, unalloc_linked_values);
+  if(ret != 0)
+  {
+    fprintf(stderr, "ERROR: extractDataCells() failed with %d.\n", ret);
+    return ret;
+  }
+  ret = extractDataCells(f, unalloc_cells, unalloc_values);
+  if(ret != 0)
+  {
+    fprintf(stderr, "ERROR: extractDataCells() failed with %d.\n", ret);
+    return ret;
+  }
+  
   /* Carve any SK records */
   ret = extractSKs(f, unalloc_cells, unalloc_sks);
   if(ret != 0)
@@ -804,7 +865,7 @@ int main(int argc, char** argv)
   for(i=0; i < num_unalloc_keys; i++)
   {
     cur_elem = range_list_get(unalloc_keys, i);
-    tmp_key = (REGF_NK_REC*)cur_elem->data;
+    tmp_key = (REGFI_NK_REC*)cur_elem->data;
 
     if(tmp_key == NULL)
       return 20;
@@ -815,11 +876,10 @@ int main(int argc, char** argv)
   }
   
   /* Now start the output */
-
   for(i=0; i < num_unalloc_keys; i++)
   {
     cur_elem = range_list_get(unalloc_keys, i);
-    tmp_key = (REGF_NK_REC*)cur_elem->data;
+    tmp_key = (REGFI_NK_REC*)cur_elem->data;
 
     printKey(f, tmp_key, parent_paths[i]);
     if(tmp_key->num_values > 0 && tmp_key->values != NULL)
@@ -827,11 +887,18 @@ int main(int argc, char** argv)
       tmp_name = quote_string(tmp_key->keyname, key_special_chars);
       tmp_path = (char*)malloc(strlen(parent_paths[i])+strlen(tmp_name)+2);
       if(tmp_path == NULL)
+      {
+	free(tmp_name);
 	return 10;
+      }
+
       sprintf(tmp_path, "%s/%s", parent_paths[i], tmp_name);
-      for(j=0; j < tmp_key->num_values; j++)
+      for(j=0; j < tmp_key->values->num_values; j++)
       {
-	tmp_value = tmp_key->values[j];
+	tmp_value = 
+	  (REGFI_VK_REC*)range_list_find_data(unalloc_linked_values, 
+					      tmp_key->values->elements[j]
+					      + REGFI_REGF_SIZE);
 	if(tmp_value != NULL)
 	  printValue(f, tmp_value, tmp_path);
       }
@@ -846,7 +913,7 @@ int main(int argc, char** argv)
   for(i=0; i < range_list_size(unalloc_values); i++)
   {
     cur_elem = range_list_get(unalloc_values, i);
-    tmp_value = (REGF_VK_REC*)cur_elem->data; 
+    tmp_value = (REGFI_VK_REC*)cur_elem->data; 
 
     printValue(f, tmp_value, "");
   }
@@ -860,5 +927,11 @@ int main(int argc, char** argv)
     }
   }
 
+  range_list_free(unalloc_cells);
+  range_list_free(unalloc_keys);
+  range_list_free(unalloc_linked_values);
+  range_list_free(unalloc_values);
+  range_list_free(unalloc_sks);
+
   return 0;
 }
diff --git a/src/reglookup.c b/src/reglookup.c
index 4eb43c9..e9d2a2c 100644
--- a/src/reglookup.c
+++ b/src/reglookup.c
@@ -1,8 +1,7 @@
 /*
- * A utility to read a Windows NT/2K/XP/2K3 registry file, using 
- * Gerald Carter''s regfio interface.
+ * A utility to read a Windows NT and later registry files.
  *
- * Copyright (C) 2005-2008 Timothy D. Morgan
+ * Copyright (C) 2005-2009 Timothy D. Morgan
  * Copyright (C) 2002 Richard Sharpe, rsharpe at richardsharpe.com
  *
  * This program is free software; you can redistribute it and/or modify
@@ -18,18 +17,17 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
  *
- * $Id: reglookup.c 121 2008-08-09 17:22:26Z tim $
+ * $Id: reglookup.c 150 2009-03-02 02:17:46Z tim $
  */
 
 
 #include <stdlib.h>
-#include <sysexits.h>
 #include <stdio.h>
 #include <string.h>
 #include <strings.h>
 #include <time.h>
-#include "../include/regfi.h"
-#include "../include/void_stack.h"
+#include "regfi.h"
+#include "void_stack.h"
 
 /* Globals, influenced by command line parameters */
 bool print_verbose = false;
@@ -42,16 +40,16 @@ int type_filter;
 char* registry_file = NULL;
 
 /* Other globals */
-REGF_FILE* f;
+REGFI_FILE* f;
 
 
 /* XXX: A hack to share some functions with reglookup-recover.c.
- *      Should move these into a properly library at some point.
+ *      Should move these into a proper library at some point.
  */
 #include "common.c"
 
 
-void printValue(const REGF_VK_REC* vk, char* prefix)
+void printValue(const REGFI_VK_REC* vk, char* prefix)
 {
   char* quoted_value = NULL;
   char* quoted_name = NULL;
@@ -65,11 +63,11 @@ void printValue(const REGF_VK_REC* vk, char* prefix)
    * malicious. For more info, see:
    *   http://msdn2.microsoft.com/en-us/library/ms724872.aspx
    */
-  if(size > VK_MAX_DATA_LENGTH)
+  if(size > REGFI_VK_MAX_DATA_LENGTH)
   {
-    fprintf(stderr, "WARNING: value data size %d larger than "
-	    "%d, truncating...\n", size, VK_MAX_DATA_LENGTH);
-    size = VK_MAX_DATA_LENGTH;
+    fprintf(stderr, "WARN: value data size %d larger than "
+	    "%d, truncating...\n", size, REGFI_VK_MAX_DATA_LENGTH);
+    size = REGFI_VK_MAX_DATA_LENGTH;
   }
   
   quoted_name = quote_string(vk->valuename, key_special_chars);
@@ -82,24 +80,32 @@ void printValue(const REGF_VK_REC* vk, char* prefix)
      */
     quoted_name = malloc(1*sizeof(char));
     if(quoted_name == NULL)
-      bailOut(EX_OSERR, "ERROR: Could not allocate sufficient memory.\n");
+      bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not allocate sufficient memory.\n");
     quoted_name[0] = '\0';
   }
 
-  quoted_value = data_to_ascii(vk->data, size, vk->type, &conv_error);
-  if(quoted_value == NULL)
+  if(vk->data == NULL)
   {
-    if(conv_error == NULL)
-      fprintf(stderr, "WARNING: Could not quote value for '%s/%s'.  "
-	      "Memory allocation failure likely.\n", prefix, quoted_name);
-    else if(print_verbose)
-      fprintf(stderr, "WARNING: Could not quote value for '%s/%s'.  "
-	      "Returned error: %s\n", prefix, quoted_name, conv_error);
+    if(print_verbose)
+      fprintf(stderr, "INFO: While quoting value for '%s/%s', "
+	      "data pointer was NULL.\n", prefix, quoted_name);
+  }
+  else
+  {
+    quoted_value = data_to_ascii(vk->data, size, vk->type, &conv_error);
+    if(quoted_value == NULL)
+    {
+      if(conv_error == NULL)
+	fprintf(stderr, "WARN: Could not quote value for '%s/%s'.  "
+		"Memory allocation failure likely.\n", prefix, quoted_name);
+      else
+	fprintf(stderr, "WARN: Could not quote value for '%s/%s'.  "
+		"Returned error: %s\n", prefix, quoted_name, conv_error);
+    }
+    else if(conv_error != NULL && print_verbose)
+      fprintf(stderr, "INFO: While quoting value for '%s/%s', "
+	      "warning returned: %s\n", prefix, quoted_name, conv_error);
   }
-  /* XXX: should these always be printed? */
-  else if(conv_error != NULL && print_verbose)
-    fprintf(stderr, "VERBOSE: While quoting value for '%s/%s', "
-	    "warning returned: %s\n", prefix, quoted_name, conv_error);
 
   str_type = regfi_type_val2str(vk->type);
   if(print_security)
@@ -130,11 +136,6 @@ void printValue(const REGF_VK_REC* vk, char* prefix)
 }
 
 
-
-/* XXX: Each chunk must be unquoted after it is split out. 
- *      Quoting syntax may need to be standardized and pushed into the API 
- *      to deal with this issue and others.
- */
 char** splitPath(const char* s)
 {
   char** ret_val;
@@ -143,7 +144,7 @@ char** splitPath(const char* s)
   char* copy;
   uint32 ret_cur = 0;
 
-  ret_val = (char**)malloc((REGF_MAX_DEPTH+1+1)*sizeof(char**));
+  ret_val = (char**)malloc((REGFI_MAX_DEPTH+1+1)*sizeof(char**));
   if (ret_val == NULL)
     return NULL;
   ret_val[0] = NULL;
@@ -158,15 +159,15 @@ char** splitPath(const char* s)
     {
       copy = (char*)malloc((next-cur+1)*sizeof(char));
       if(copy == NULL)
-	bailOut(EX_OSERR, "ERROR: Memory allocation problem.\n");
+	bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Memory allocation problem.\n");
 	  
       memcpy(copy, cur, next-cur);
       copy[next-cur] = '\0';
       ret_val[ret_cur++] = copy;
-      if(ret_cur < (REGF_MAX_DEPTH+1+1))
+      if(ret_cur < (REGFI_MAX_DEPTH+1+1))
 	ret_val[ret_cur] = NULL;
       else
-	bailOut(EX_DATAERR, "ERROR: Registry maximum depth exceeded.\n");
+	bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: Registry maximum depth exceeded.\n");
     }
     cur = next+1;
   }
@@ -176,10 +177,10 @@ char** splitPath(const char* s)
   {
     copy = strdup(cur);
     ret_val[ret_cur++] = copy;
-    if(ret_cur < (REGF_MAX_DEPTH+1+1))
+    if(ret_cur < (REGFI_MAX_DEPTH+1+1))
       ret_val[ret_cur] = NULL;
     else
-      bailOut(EX_DATAERR, "ERROR: Registry maximum depth exceeded.\n");
+      bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: Registry maximum depth exceeded.\n");
   }
 
   return ret_val;
@@ -201,9 +202,6 @@ void freePath(char** path)
 
 
 /* Returns a quoted path from an iterator's stack */
-/* XXX: Some way should be found to integrate this into regfi's API 
- *      The problem is that the escaping is sorta reglookup-specific.
- */
 char* iter2Path(REGFI_ITERATOR* i)
 {
   const REGFI_ITER_POSITION* cur;
@@ -256,6 +254,7 @@ char* iter2Path(REGFI_ITERATOR* i)
       buf_len += name_len+1+grow_amt-buf_left;
       if((new_buf = realloc(buf, buf_len)) == NULL)
       {
+	free(name);
 	free(buf);
 	free(iter);
 	return NULL;
@@ -273,38 +272,42 @@ char* iter2Path(REGFI_ITERATOR* i)
 }
 
 
-void printValueList(REGFI_ITERATOR* i, char* prefix)
+void printValueList(REGFI_ITERATOR* iter, char* prefix)
 {
-  const REGF_VK_REC* value;
+  REGFI_VK_REC* value;
 
-  value = regfi_iterator_first_value(i);
+  value = regfi_iterator_first_value(iter);
   while(value != NULL)
   {
     if(!type_filter_enabled || (value->type == type_filter))
       printValue(value, prefix);
-    value = regfi_iterator_next_value(i);
+    regfi_free_value(value);
+    value = regfi_iterator_next_value(iter);
+    printMsgs(iter->f);
   }
 }
 
 
-void printKey(REGFI_ITERATOR* i, char* full_path)
+void printKey(REGFI_ITERATOR* iter, char* full_path)
 {
   static char empty_str[1] = "";
   char* owner = NULL;
   char* group = NULL;
   char* sacl = NULL;
   char* dacl = NULL;
+  char* quoted_classname;
+  char* error_msg = NULL;
   char mtime[20];
   time_t tmp_time[1];
   struct tm* tmp_time_s = NULL;
-  const REGF_SK_REC* sk;
-  const REGF_NK_REC* k = regfi_iterator_cur_key(i);
+  const REGFI_SK_REC* sk;
+  const REGFI_NK_REC* k = regfi_iterator_cur_key(iter);
 
   *tmp_time = nt_time_to_unix(&k->mtime);
   tmp_time_s = gmtime(tmp_time);
   strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s);
 
-  if(print_security && (sk=regfi_iterator_cur_sk(i)))
+  if(print_security && (sk=regfi_iterator_cur_sk(iter)))
   {
     owner = regfi_get_owner(sk->sec_desc);
     group = regfi_get_group(sk->sec_desc);
@@ -319,8 +322,36 @@ void printKey(REGFI_ITERATOR* i, char* full_path)
     if(dacl == NULL)
       dacl = empty_str;
 
-    printf("%s,KEY,,%s,%s,%s,%s,%s\n", full_path, mtime, 
-	   owner, group, sacl, dacl);
+    if(k->classname != NULL)
+    {
+      quoted_classname = quote_unicode((uint8*)k->classname, k->classname_length,
+				       key_special_chars, &error_msg);
+      if(quoted_classname == NULL)
+      {
+	if(error_msg == NULL)
+	  fprintf(stderr, "ERROR: Could not quote classname"
+		  " for key '%s' due to unknown error.\n", full_path);
+	else
+	{
+	  fprintf(stderr, "ERROR: Could not quote classname"
+		  " for key '%s' due to error: %s\n", full_path, error_msg);
+	  free(error_msg);
+	}
+      }
+      else if (error_msg != NULL)
+      {
+	if(print_verbose)
+	  fprintf(stderr, "INFO: While converting classname"
+		  " for key '%s': %s.\n", full_path, error_msg);
+	free(error_msg);
+      }
+    }
+    else
+      quoted_classname = empty_str;
+
+    printMsgs(iter->f);
+    printf("%s,KEY,,%s,%s,%s,%s,%s,%s\n", full_path, mtime, 
+	   owner, group, sacl, dacl, quoted_classname);
 
     if(owner != empty_str)
       free(owner);
@@ -330,6 +361,8 @@ void printKey(REGFI_ITERATOR* i, char* full_path)
       free(sacl);
     if(dacl != empty_str)
       free(dacl);
+    if(quoted_classname != empty_str)
+      free(quoted_classname);
   }
   else
     printf("%s,KEY,,%s\n", full_path, mtime);
@@ -338,18 +371,19 @@ void printKey(REGFI_ITERATOR* i, char* full_path)
 
 void printKeyTree(REGFI_ITERATOR* iter)
 {
-  const REGF_NK_REC* root = NULL;
-  const REGF_NK_REC* cur = NULL;
-  const REGF_NK_REC* sub = NULL;
+  const REGFI_NK_REC* root = NULL;
+  const REGFI_NK_REC* cur = NULL;
+  REGFI_NK_REC* sub = NULL;
   char* path = NULL;
   int key_type = regfi_type_str2val("KEY");
   bool print_this = true;
 
   root = cur = regfi_iterator_cur_key(iter);
   sub = regfi_iterator_first_subkey(iter);
-  
+  printMsgs(iter->f);
+
   if(root == NULL)
-    bailOut(EX_DATAERR, "ERROR: root cannot be NULL.\n");
+    bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: root cannot be NULL.\n");
   
   do
   {
@@ -357,8 +391,8 @@ void printKeyTree(REGFI_ITERATOR* iter)
     {
       path = iter2Path(iter);
       if(path == NULL)
-	bailOut(EX_OSERR, "ERROR: Could not construct iterator's path.\n");
-      
+	bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not construct iterator's path.\n");
+
       if(!type_filter_enabled || (key_type == type_filter))
 	printKey(iter, path);
       if(!type_filter_enabled || (key_type != type_filter))
@@ -373,11 +407,17 @@ void printKeyTree(REGFI_ITERATOR* iter)
       {
 	/* We're done with this sub-tree, going up and hitting other branches. */
 	if(!regfi_iterator_up(iter))
-	  bailOut(EX_DATAERR, "ERROR: could not traverse iterator upward.\n");
-	
+	{
+	  printMsgs(iter->f);
+	  bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: could not traverse iterator upward.\n");
+	}
+
 	cur = regfi_iterator_cur_key(iter);
 	if(cur == NULL)
-	  bailOut(EX_DATAERR, "ERROR: unexpected NULL for key.\n");
+	{
+	  printMsgs(iter->f);
+	  bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: unexpected NULL for key.\n");
+	}
 	
 	sub = regfi_iterator_next_subkey(iter);
       }
@@ -388,20 +428,27 @@ void printKeyTree(REGFI_ITERATOR* iter)
        * Let's move down and print this first sub-tree out. 
        */
       if(!regfi_iterator_down(iter))
-	bailOut(EX_DATAERR, "ERROR: could not traverse iterator downward.\n");
+      {
+	printMsgs(iter->f);
+	bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: could not traverse iterator downward.\n");
+      }
 
-      cur = sub;
+      cur = regfi_iterator_cur_key(iter);
+      regfi_free_key(sub);
       sub = regfi_iterator_first_subkey(iter);
       print_this = true;
     }
+    printMsgs(iter->f);
   } while(!((cur == root) && (sub == NULL)));
 
   if(print_verbose)
-    fprintf(stderr, "VERBOSE: Finished printing key tree.\n");
+    fprintf(stderr, "INFO: Finished printing key tree.\n");
 }
 
 
-/* XXX: what if there is BOTH a value AND a key with that name?? */
+/* XXX: What if there is BOTH a value AND a key with that name?? 
+ *      What if there are multiple keys/values with the same name?? 
+ */
 /*
  * Returns 0 if path was not found.
  * Returns 1 if path was found as value.
@@ -410,7 +457,7 @@ void printKeyTree(REGFI_ITERATOR* iter)
  */
 int retrievePath(REGFI_ITERATOR* iter, char** path)
 {
-  const REGF_VK_REC* value;
+  REGFI_VK_REC* value;
   char* tmp_path_joined;
   const char** tmp_path;
   uint32 i;
@@ -419,33 +466,33 @@ int retrievePath(REGFI_ITERATOR* iter, char** path)
     return -1;
 
   /* One extra for any value at the end, and one more for NULL */
-  tmp_path = (const char**)malloc(sizeof(const char**)*(REGF_MAX_DEPTH+1+1));
+  tmp_path = (const char**)malloc(sizeof(const char**)*(REGFI_MAX_DEPTH+1+1));
   if(tmp_path == NULL)
     return -2;
 
   /* Strip any potential value name at end of path */
   for(i=0; 
-      (path[i] != NULL) && (path[i+1] != NULL) 
-	&& (i < REGF_MAX_DEPTH+1+1);
+      (path[i] != NULL) && (path[i+1] != NULL) && (i < REGFI_MAX_DEPTH+1);
       i++)
-    tmp_path[i] = path[i];
-
+  { tmp_path[i] = path[i]; }
   tmp_path[i] = NULL;
 
   if(print_verbose)
-    fprintf(stderr, "VERBOSE: Attempting to retrieve specified path: %s\n",
+    fprintf(stderr, "INFO: Attempting to retrieve specified path: %s\n",
 	    path_filter);
 
   /* Special check for '/' path filter */
   if(path[0] == NULL)
   {
     if(print_verbose)
-      fprintf(stderr, "VERBOSE: Found final path element as root key.\n");
+      fprintf(stderr, "INFO: Found final path element as root key.\n");
+    free(tmp_path);
     return 2;
   }
 
   if(!regfi_iterator_walk_path(iter, tmp_path))
   {
+    printMsgs(iter->f);
     free(tmp_path);
     return 0;
   }
@@ -453,34 +500,41 @@ int retrievePath(REGFI_ITERATOR* iter, char** path)
   if(regfi_iterator_find_value(iter, path[i]))
   {
     if(print_verbose)
-      fprintf(stderr, "VERBOSE: Found final path element as value.\n");
+      fprintf(stderr, "INFO: Found final path element as value.\n");
 
     value = regfi_iterator_cur_value(iter);
+    printMsgs(iter->f);
     tmp_path_joined = iter2Path(iter);
 
     if((value == NULL) || (tmp_path_joined == NULL))
-      bailOut(EX_OSERR, "ERROR: Unexpected error before printValue.\n");
+      bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Unexpected error before printValue.\n");
 
     if(!type_filter_enabled || (value->type == type_filter))
       printValue(value, tmp_path_joined);
 
+    regfi_free_value(value);
     free(tmp_path);
     free(tmp_path_joined);
     return 1;
   }
   else if(regfi_iterator_find_subkey(iter, path[i]))
   {
+    printMsgs(iter->f);
     if(print_verbose)
-      fprintf(stderr, "VERBOSE: Found final path element as key.\n");
+      fprintf(stderr, "INFO: Found final path element as key.\n");
 
     if(!regfi_iterator_down(iter))
-      bailOut(EX_DATAERR, "ERROR: Unexpected error on traversing path filter key.\n");
+    {
+      printMsgs(iter->f);
+      bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: Unexpected error on traversing path filter key.\n");
+    }
 
     return 2;
   }
+  printMsgs(iter->f);
 
   if(print_verbose)
-    fprintf(stderr, "VERBOSE: Could not find last element of path.\n");
+    fprintf(stderr, "INFO: Could not find last element of path.\n");
 
   return 0;
 }
@@ -515,7 +569,7 @@ int main(int argc, char** argv)
   if(argc < 2)
   {
     usage();
-    bailOut(EX_USAGE, "ERROR: Requires at least one argument.\n");
+    bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: Requires at least one argument.\n");
   }
   
   arge = argc-1;
@@ -526,10 +580,10 @@ int main(int argc, char** argv)
       if(++argi >= arge)
       {
 	usage();
-	bailOut(EX_USAGE, "ERROR: '-p' option requires parameter.\n");
+	bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: '-p' option requires parameter.\n");
       }
       if((path_filter = strdup(argv[argi])) == NULL)
-	bailOut(EX_OSERR, "ERROR: Memory allocation problem.\n");
+	bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Memory allocation problem.\n");
 
       path_filter_enabled = true;
     }
@@ -538,12 +592,12 @@ int main(int argc, char** argv)
       if(++argi >= arge)
       {
 	usage();
-	bailOut(EX_USAGE, "ERROR: '-t' option requires parameter.\n");
+	bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: '-t' option requires parameter.\n");
       }
       if((type_filter = regfi_type_str2val(argv[argi])) < 0)
       {
 	fprintf(stderr, "ERROR: Invalid type specified: %s.\n", argv[argi]);
-	bailOut(EX_USAGE, "");
+	bailOut(REGLOOKUP_EXIT_USAGE, "");
       }
       type_filter_enabled = true;
     }
@@ -561,27 +615,30 @@ int main(int argc, char** argv)
     {
       usage();
       fprintf(stderr, "ERROR: Unrecognized option: %s\n", argv[argi]);
-      bailOut(EX_USAGE, "");
+      bailOut(REGLOOKUP_EXIT_USAGE, "");
     }
   }
   if((registry_file = strdup(argv[argi])) == NULL)
-    bailOut(EX_OSERR, "ERROR: Memory allocation problem.\n");
+    bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Memory allocation problem.\n");
 
   f = regfi_open(registry_file);
   if(f == NULL)
   {
     fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file);
-    bailOut(EX_NOINPUT, "");
+    bailOut(REGLOOKUP_EXIT_NOINPUT, "");
   }
 
+  if(print_verbose)
+    regfi_set_message_mask(f, REGFI_MSG_INFO|REGFI_MSG_WARN|REGFI_MSG_ERROR);
+
   iter = regfi_iterator_new(f);
   if(iter == NULL)
-    bailOut(EX_OSERR, "ERROR: Couldn't create registry iterator.\n");
+    bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Couldn't create registry iterator.\n");
 
   if(print_header)
   {
     if(print_security)
-      printf("PATH,TYPE,VALUE,MTIME,OWNER,GROUP,SACL,DACL\n");
+      printf("PATH,TYPE,VALUE,MTIME,OWNER,GROUP,SACL,DACL,CLASS\n");
     else
       printf("PATH,TYPE,VALUE,MTIME\n");
   }
@@ -592,17 +649,19 @@ int main(int argc, char** argv)
   if(path != NULL)
   {
     retr_path_ret = retrievePath(iter, path);
+    printMsgs(iter->f);
     freePath(path);
 
     if(retr_path_ret == 0)
-      fprintf(stderr, "WARNING: specified path not found.\n");
+      fprintf(stderr, "WARN: Specified path '%s' not found.\n", path_filter);
     else if (retr_path_ret == 2)
       printKeyTree(iter);
     else if(retr_path_ret < 0)
     {
       fprintf(stderr, "ERROR: retrievePath() returned %d.\n", 
 	      retr_path_ret);
-      bailOut(EX_DATAERR,"ERROR: Unknown error occurred in retrieving path.\n");
+      bailOut(REGLOOKUP_EXIT_DATAERR,
+	      "ERROR: Unknown error occurred in retrieving path.\n");
     }
   }
   else

-- 
utility to read and query Windows NT/2000/XP registry



More information about the forensics-changes mailing list