From e8b3381aa5e6facdf6ad6b12e582a7bf544f9e5c Mon Sep 17 00:00:00 2001
From: Brian Carrier <carrier@sleuthkit.org>
Date: Thu, 7 Apr 2011 17:59:07 +0000
Subject: [PATCH] Merging in C++ classes and multithreaded support

---
 Makefile.am                               |    5 +-
 NEWS.txt                                  |    6 +
 ax_pthread.m4                             |  283 +++
 configure.ac                              |   34 +-
 cppunit.m4                                |   92 +
 samples/Makefile.am                       |    6 +-
 samples/callback-cpp-style.cpp            |  328 +++
 samples/callback-style.cpp                |    7 +-
 samples/posix-cpp-style.cpp               |  345 +++
 samples/posix-style.cpp                   |    2 +-
 tests/Makefile.am                         |   58 +-
 tests/fs_attrlist_apis.cpp                |   14 +-
 tests/fs_fname_apis.cpp                   |    2 +-
 tests/fs_thread_test.cpp                  |  236 +++
 tests/read_apis.cpp                       |    2 +-
 tests/tsk_thread.cpp                      |   39 +
 tools/autotools/tsk_comparedir.cpp        |    2 +-
 tools/autotools/tsk_gettimes.cpp          |    2 +-
 tools/autotools/tsk_loaddb.cpp            |    2 +-
 tools/autotools/tsk_recover.cpp           |    2 +-
 tools/fstools/blkcalc.cpp                 |    6 +-
 tools/fstools/blkcat.cpp                  |    4 +-
 tools/fstools/blkls.cpp                   |    6 +-
 tools/fstools/blkstat.cpp                 |    4 +-
 tools/fstools/ffind.cpp                   |    4 +-
 tools/fstools/fls.cpp                     |    6 +-
 tools/fstools/fscheck.cpp                 |    2 +-
 tools/fstools/fsstat.cpp                  |    4 +-
 tools/fstools/icat.cpp                    |    6 +-
 tools/fstools/ifind.cpp                   |    4 +-
 tools/fstools/ils.cpp                     |    4 +-
 tools/fstools/istat.cpp                   |    4 +-
 tools/fstools/jcat.cpp                    |    6 +-
 tools/fstools/jls.cpp                     |    6 +-
 tools/hashtools/hfind.cpp                 |    2 +-
 tools/imgtools/img_cat.cpp                |    2 +-
 tools/imgtools/img_stat.cpp               |    2 +-
 tools/sorter/sorter.base                  |    2 +-
 tools/timeline/mactime.base               |    2 +-
 tools/vstools/mmcat.cpp                   |   11 +-
 tools/vstools/mmls.cpp                    |    4 +-
 tools/vstools/mmstat.cpp                  |    4 +-
 tsk3/auto/auto.cpp                        |  112 +-
 tsk3/auto/auto_db.cpp                     |  133 +-
 tsk3/auto/tsk_auto.h                      |    2 +-
 tsk3/auto/tsk_auto_i.h                    |    2 +-
 tsk3/base/Makefile.am                     |    3 +-
 tsk3/base/mymalloc.c                      |   14 +-
 tsk3/base/tsk_base.h                      |  192 +-
 tsk3/base/tsk_base_i.h                    |   43 +-
 tsk3/base/tsk_endian.c                    |    2 +-
 tsk3/base/tsk_error.c                     |  319 ++-
 tsk3/base/tsk_list.c                      |    2 +-
 tsk3/base/tsk_lock.c                      |  106 +
 tsk3/base/tsk_os.h                        |    3 +-
 tsk3/base/tsk_parse.c                     |   34 +-
 tsk3/base/tsk_printf.c                    |    2 +-
 tsk3/base/tsk_stack.c                     |    2 +-
 tsk3/base/tsk_version.c                   |    2 +-
 tsk3/docs/Doxyfile                        |    7 +-
 tsk3/docs/base.dox                        |    2 +-
 tsk3/docs/basics.dox                      |    3 +
 tsk3/docs/footer.html                     |    2 +-
 tsk3/docs/fs.dox                          |   15 +-
 tsk3/docs/hashdb.dox                      |    2 +
 tsk3/docs/img.dox                         |    4 +-
 tsk3/docs/main.dox                        |   13 +-
 tsk3/docs/vs.dox                          |    6 +-
 tsk3/fs/Makefile.am                       |    4 +-
 tsk3/fs/dcalc_lib.c                       |    2 +-
 tsk3/fs/dcat_lib.c                        |   24 +-
 tsk3/fs/dls_lib.c                         |   20 +-
 tsk3/fs/dstat_lib.c                       |    2 +-
 tsk3/fs/ext2fs.c                          |  552 ++---
 tsk3/fs/ext2fs_dent.c                     |   56 +-
 tsk3/fs/ext2fs_journal.c                  |   54 +-
 tsk3/fs/fatfs.c                           |  184 +-
 tsk3/fs/fatfs_dent.c                      |  113 +-
 tsk3/fs/fatfs_meta.c                      |  290 +--
 tsk3/fs/ffind_lib.c                       |    2 +-
 tsk3/fs/ffs.c                             |  377 ++--
 tsk3/fs/ffs_dent.c                        |   48 +-
 tsk3/fs/fls_lib.c                         |    6 +-
 tsk3/fs/fs_attr.c                         |   96 +-
 tsk3/fs/fs_attrlist.c                     |   87 +-
 tsk3/fs/fs_block.c                        |   28 +-
 tsk3/fs/fs_dir.c                          |  278 ++-
 tsk3/fs/fs_file.c                         |   62 +-
 tsk3/fs/fs_inode.c                        |    2 +-
 tsk3/fs/fs_io.c                           |   22 +-
 tsk3/fs/fs_load.c                         |    2 +-
 tsk3/fs/fs_name.c                         |    4 +-
 tsk3/fs/fs_open.c                         |  101 +-
 tsk3/fs/fs_parse.c                        |    2 +-
 tsk3/fs/fs_types.c                        |    2 +-
 tsk3/fs/hfs.c                             |  422 ++--
 tsk3/fs/hfs_dent.c                        |   43 +-
 tsk3/fs/icat_lib.c                        |   10 +-
 tsk3/fs/ifind_lib.c                       |    8 +-
 tsk3/fs/ils_lib.c                         |    2 +-
 tsk3/fs/iso9660.c                         |  427 ++--
 tsk3/fs/iso9660_dent.c                    |   37 +-
 tsk3/fs/nofs_misc.c                       |   46 +-
 tsk3/fs/ntfs.c                            |  897 ++++----
 tsk3/fs/ntfs_dent.c                       |  223 +-
 tsk3/fs/rawfs.c                           |    4 +-
 tsk3/fs/swapfs.c                          |    4 +-
 tsk3/fs/tsk_ext2fs.h                      |   18 +-
 tsk3/fs/tsk_fatfs.h                       |   26 +-
 tsk3/fs/tsk_ffs.h                         |   24 +-
 tsk3/fs/tsk_fs.h                          | 2358 +++++++++++++++++++--
 tsk3/fs/tsk_fs_i.h                        |  491 ++---
 tsk3/fs/tsk_hfs.h                         |   15 +-
 tsk3/fs/tsk_iso9660.h                     |    6 +-
 tsk3/fs/tsk_ntfs.h                        |   24 +-
 tsk3/fs/unix_misc.c                       |   18 +-
 tsk3/fs/walk_cpp.cpp                      |   80 +
 tsk3/hashdb/hk_index.c                    |   38 +-
 tsk3/hashdb/idxonly_index.c               |   10 +-
 tsk3/hashdb/md5sum_index.c                |   56 +-
 tsk3/hashdb/nsrl_index.c                  |  102 +-
 tsk3/hashdb/tm_lookup.c                   |  279 ++-
 tsk3/hashdb/tsk_hashdb.h                  |  242 ++-
 tsk3/hashdb/tsk_hashdb_i.h                |    2 +-
 tsk3/img/aff.c                            |   40 +-
 tsk3/img/aff.h                            |    4 +-
 tsk3/img/ewf.c                            |   37 +-
 tsk3/img/img_io.c                         |   22 +-
 tsk3/img/img_open.c                       |   88 +-
 tsk3/img/img_types.c                      |    2 +-
 tsk3/img/raw.c                            |   90 +-
 tsk3/img/raw.h                            |    4 +-
 tsk3/img/split.c                          |   70 +-
 tsk3/img/split.h                          |   10 +-
 tsk3/img/tsk_img.h                        |  254 ++-
 tsk3/img/tsk_img_i.h                      |    5 +-
 tsk3/tsk_config.h.in                      |   35 +-
 tsk3/vs/bsd.c                             |   26 +-
 tsk3/vs/dos.c                             |   86 +-
 tsk3/vs/gpt.c                             |   34 +-
 tsk3/vs/mac.c                             |   18 +-
 tsk3/vs/mm_io.c                           |   10 +-
 tsk3/vs/mm_open.c                         |   24 +-
 tsk3/vs/mm_part.c                         |   18 +-
 tsk3/vs/mm_types.c                        |    2 +-
 tsk3/vs/sun.c                             |   30 +-
 tsk3/vs/tsk_bsd.h                         |    2 +-
 tsk3/vs/tsk_dos.h                         |    2 +-
 tsk3/vs/tsk_gpt.h                         |    2 +-
 tsk3/vs/tsk_mac.h                         |    2 +-
 tsk3/vs/tsk_sun.h                         |    2 +-
 tsk3/vs/tsk_vs.h                          |  444 +++-
 tsk3/vs/tsk_vs_i.h                        |    2 +-
 unit_tests/Makefile                       |  498 +++++
 unit_tests/Makefile.am                    |    1 +
 unit_tests/Makefile.in                    |  498 +++++
 unit_tests/base/.deps/errors_test.Po      |    1 +
 unit_tests/base/.deps/test_base.Po        |    1 +
 unit_tests/base/Makefile                  |  462 ++++
 unit_tests/base/Makefile.am               |   15 +
 unit_tests/base/Makefile.in               |  462 ++++
 unit_tests/base/errors_test.cpp           |  196 ++
 unit_tests/base/test_base.cpp             |   39 +
 win32/libauxtools/libauxtools.vcproj      |    8 +
 win32/libfstools/libfstools.vcproj        |    4 +
 win32/tsk-win.sln                         |   36 +-
 win32/tsk_comparedir/tsk_compare.vcproj   |    2 +
 win32/tsk_gettimes/tsk_gettimes.vcproj    |   76 +-
 win32/tsk_recover/tsk_recover.vcproj      |   17 +-
 xcode/sleuthkit.xcodeproj/project.pbxproj |    2 +
 170 files changed, 10940 insertions(+), 3664 deletions(-)
 create mode 100644 ax_pthread.m4
 create mode 100644 cppunit.m4
 create mode 100644 samples/callback-cpp-style.cpp
 create mode 100644 samples/posix-cpp-style.cpp
 create mode 100644 tests/fs_thread_test.cpp
 create mode 100644 tests/tsk_thread.cpp
 create mode 100644 tsk3/base/tsk_lock.c
 create mode 100644 tsk3/fs/walk_cpp.cpp
 create mode 100644 unit_tests/Makefile
 create mode 100644 unit_tests/Makefile.am
 create mode 100644 unit_tests/Makefile.in
 create mode 100644 unit_tests/base/.deps/errors_test.Po
 create mode 100644 unit_tests/base/.deps/test_base.Po
 create mode 100644 unit_tests/base/Makefile
 create mode 100644 unit_tests/base/Makefile.am
 create mode 100644 unit_tests/base/Makefile.in
 create mode 100644 unit_tests/base/errors_test.cpp
 create mode 100644 unit_tests/base/test_base.cpp

diff --git a/Makefile.am b/Makefile.am
index b43864313..a1506bf3f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -30,7 +30,10 @@ EXTRA_DIST = README_win32.txt README.txt INSTALL.txt ChangeLog.txt NEWS.txt \
 CLEANFILES = tsk3/tsk_incs.h
 
 # directories to compile
-SUBDIRS = tsk3 tools tests samples man
+if CPPUNIT
+  UNIT_TESTS=unit_tests
+endif
+SUBDIRS = tsk3 tools tests samples man $(UNIT_TESTS)
 
 nobase_include_HEADERS = tsk3/libtsk.h tsk3/tsk_incs.h \
     tsk3/base/tsk_base.h tsk3/base/tsk_os.h \
diff --git a/NEWS.txt b/NEWS.txt
index 4325b7567..671517807 100644
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -1,6 +1,12 @@
 Numbers refer to SourceForge.net tracker IDs:
     http://sourceforge.net/tracker/?group_id=55685
 
+
+---------------- VERSION 3.3.0 --------------
+New Features:
+- Added multithreaded support
+- Added C++ wrapper classes
+
 ---------------- VERSION 3.2.2 --------------
 Bug Fixes
 - 3213886: ISO9660 directory hole not advancing
diff --git a/ax_pthread.m4 b/ax_pthread.m4
new file mode 100644
index 000000000..63896322d
--- /dev/null
+++ b/ax_pthread.m4
@@ -0,0 +1,283 @@
+# ===========================================================================
+#        http://www.gnu.org/software/autoconf-archive/ax_pthread.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
+#
+# DESCRIPTION
+#
+#   This macro figures out how to build C programs using POSIX threads. It
+#   sets the PTHREAD_LIBS output variable to the threads library and linker
+#   flags, and the PTHREAD_CFLAGS output variable to any special C compiler
+#   flags that are needed. (The user can also force certain compiler
+#   flags/libs to be tested by setting these environment variables.)
+#
+#   Also sets PTHREAD_CC to any special C compiler that is needed for
+#   multi-threaded programs (defaults to the value of CC otherwise). (This
+#   is necessary on AIX to use the special cc_r compiler alias.)
+#
+#   NOTE: You are assumed to not only compile your program with these flags,
+#   but also link it with them as well. e.g. you should link with
+#   $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
+#
+#   If you are only building threads programs, you may wish to use these
+#   variables in your default LIBS, CFLAGS, and CC:
+#
+#     LIBS="$PTHREAD_LIBS $LIBS"
+#     CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+#     CC="$PTHREAD_CC"
+#
+#   In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
+#   has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
+#   (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
+#
+#   ACTION-IF-FOUND is a list of shell commands to run if a threads library
+#   is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
+#   is not found. If ACTION-IF-FOUND is not specified, the default action
+#   will define HAVE_PTHREAD.
+#
+#   Please let the authors know if this macro fails on any platform, or if
+#   you have any other suggestions or comments. This macro was based on work
+#   by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
+#   from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
+#   Alejandro Forero Cuervo to the autoconf macro repository. We are also
+#   grateful for the helpful feedback of numerous users.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
+#
+#   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, either version 3 of the License, or (at your
+#   option) any later version.
+#
+#   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, see <http://www.gnu.org/licenses/>.
+#
+#   As a special exception, the respective Autoconf Macro's copyright owner
+#   gives unlimited permission to copy, distribute and modify the configure
+#   scripts that are the output of Autoconf when processing the Macro. You
+#   need not follow the terms of the GNU General Public License when using
+#   or distributing such scripts, even though portions of the text of the
+#   Macro appear in them. The GNU General Public License (GPL) does govern
+#   all other use of the material that constitutes the Autoconf Macro.
+#
+#   This special exception to the GPL applies to versions of the Autoconf
+#   Macro released by the Autoconf Archive. When you make and distribute a
+#   modified version of the Autoconf Macro, you may extend this special
+#   exception to the GPL to apply to your modified version as well.
+
+#serial 11
+
+AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
+AC_DEFUN([AX_PTHREAD], [
+AC_REQUIRE([AC_CANONICAL_HOST])
+AC_LANG_SAVE
+AC_LANG_C
+ax_pthread_ok=no
+
+# We used to check for pthread.h first, but this fails if pthread.h
+# requires special compiler flags (e.g. on True64 or Sequent).
+# It gets checked for in the link test anyway.
+
+# First of all, check if the user has set any of the PTHREAD_LIBS,
+# etcetera environment variables, and if threads linking works using
+# them:
+if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
+        save_CFLAGS="$CFLAGS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+        save_LIBS="$LIBS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
+        AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes)
+        AC_MSG_RESULT($ax_pthread_ok)
+        if test x"$ax_pthread_ok" = xno; then
+                PTHREAD_LIBS=""
+                PTHREAD_CFLAGS=""
+        fi
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+fi
+
+# We must check for the threads library under a number of different
+# names; the ordering is very important because some systems
+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+# libraries is broken (non-POSIX).
+
+# Create a list of thread flags to try.  Items starting with a "-" are
+# C compiler flags, and other items are library names, except for "none"
+# which indicates that we try without any flags at all, and "pthread-config"
+# which is a program returning the flags for the Pth emulation library.
+
+ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread lpthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+
+# The ordering *is* (sometimes) important.  Some notes on the
+# individual items follow:
+
+# pthreads: AIX (must check this before -lpthread)
+# none: in case threads are in libc; should be tried before -Kthread and
+#       other compiler flags to prevent continual compiler warnings
+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
+# -pthreads: Solaris/gcc
+# -mthreads: Mingw32/gcc, Lynx/gcc
+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+#      doesn't hurt to check since this sometimes defines pthreads too;
+#      also defines -D_REENTRANT)
+#      ... -mt is also the pthreads flag for HP/aCC
+# pthread: Linux, etcetera
+# --thread-safe: KAI C++
+# pthread-config: use pthread-config program (for GNU Pth library)
+
+case "${host_cpu}-${host_os}" in
+        *solaris*)
+
+        # On Solaris (at least, for some versions), libc contains stubbed
+        # (non-functional) versions of the pthreads routines, so link-based
+        # tests will erroneously succeed.  (We need to link with -pthreads/-mt/
+        # -lpthread.)  (The stubs are missing pthread_cleanup_push, or rather
+        # a function called by this macro, so we could check for that, but
+        # who knows whether they'll stub that too in a future libc.)  So,
+        # we'll just look for -pthreads and -lpthread first:
+
+        ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
+        ;;
+
+	*-darwin*)
+	ax_pthread_flags="-pthread $ax_pthread_flags"
+	;;
+esac
+
+if test x"$ax_pthread_ok" = xno; then
+for flag in $ax_pthread_flags; do
+
+        case $flag in
+                none)
+                AC_MSG_CHECKING([whether pthreads work without any flags])
+                ;;
+
+                -*)
+                AC_MSG_CHECKING([whether pthreads work with $flag])
+                PTHREAD_CFLAGS="$flag"
+                ;;
+
+		pthread-config)
+		AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no)
+		if test x"$ax_pthread_config" = xno; then continue; fi
+		PTHREAD_CFLAGS="`pthread-config --cflags`"
+		PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+		;;
+
+                *)
+                AC_MSG_CHECKING([for the pthreads library -l$flag])
+                PTHREAD_LIBS="-l$flag"
+                ;;
+        esac
+
+        save_LIBS="$LIBS"
+        save_CFLAGS="$CFLAGS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+        # Check for various functions.  We must include pthread.h,
+        # since some functions may be macros.  (On the Sequent, we
+        # need a special flag -Kthread to make this header compile.)
+        # We check for pthread_join because it is in -lpthread on IRIX
+        # while pthread_create is in libc.  We check for pthread_attr_init
+        # due to DEC craziness with -lpthreads.  We check for
+        # pthread_cleanup_push because it is one of the few pthread
+        # functions on Solaris that doesn't have a non-functional libc stub.
+        # We try pthread_create on general principles.
+        AC_TRY_LINK([#include <pthread.h>
+		     static void routine(void* a) {a=0;}
+		     static void* start_routine(void* a) {return a;}],
+                    [pthread_t th; pthread_attr_t attr;
+                     pthread_create(&th,0,start_routine,0);
+                     pthread_join(th, 0);
+                     pthread_attr_init(&attr);
+                     pthread_cleanup_push(routine, 0);
+                     pthread_cleanup_pop(0); ],
+                    [ax_pthread_ok=yes])
+
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+
+        AC_MSG_RESULT($ax_pthread_ok)
+        if test "x$ax_pthread_ok" = xyes; then
+                break;
+        fi
+
+        PTHREAD_LIBS=""
+        PTHREAD_CFLAGS=""
+done
+fi
+
+# Various other checks:
+if test "x$ax_pthread_ok" = xyes; then
+        save_LIBS="$LIBS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        save_CFLAGS="$CFLAGS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+        # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
+	AC_MSG_CHECKING([for joinable pthread attribute])
+	attr_name=unknown
+	for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+	    AC_TRY_LINK([#include <pthread.h>], [int attr=$attr; return attr;],
+                        [attr_name=$attr; break])
+	done
+        AC_MSG_RESULT($attr_name)
+        if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
+            AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
+                               [Define to necessary symbol if this constant
+                                uses a non-standard name on your system.])
+        fi
+
+        AC_MSG_CHECKING([if more special flags are required for pthreads])
+        flag=no
+        case "${host_cpu}-${host_os}" in
+            *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
+            *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
+        esac
+        AC_MSG_RESULT(${flag})
+        if test "x$flag" != xno; then
+            PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
+        fi
+
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+
+        # More AIX lossage: must compile with xlc_r or cc_r
+	if test x"$GCC" != xyes; then
+          AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
+        else
+          PTHREAD_CC=$CC
+	fi
+else
+        PTHREAD_CC="$CC"
+fi
+
+AC_SUBST(PTHREAD_LIBS)
+AC_SUBST(PTHREAD_CFLAGS)
+AC_SUBST(PTHREAD_CC)
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test x"$ax_pthread_ok" = xyes; then
+        ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
+        :
+else
+        ax_pthread_ok=no
+        $2
+fi
+AC_LANG_RESTORE
+])dnl AX_PTHREAD
diff --git a/configure.ac b/configure.ac
index 768910cab..243c1da43 100644
--- a/configure.ac
+++ b/configure.ac
@@ -5,10 +5,15 @@
 AC_PREREQ(2.59)
 
 AC_INIT(sleuthkit, 0.0.0)
+m4_include([ax_pthread.m4])
+# include the version from 1.12.1. This will work for 
+m4_include([cppunit.m4])
 AC_CONFIG_SRCDIR([tsk3/base/tsk_base.h])
 AM_CONFIG_HEADER([tsk3/tsk_config.h])
 AC_CONFIG_AUX_DIR(config)
 AM_INIT_AUTOMAKE([foreign])
+AM_PATH_CPPUNIT(1.12.1)
+AM_CONDITIONAL([CPPUNIT],[test "x$no_cppunit" = x])
 AM_PROG_LIBTOOL
 AM_MAINTAINER_MODE
 AC_CONFIG_MACRO_DIR([m4])
@@ -63,7 +68,12 @@ AC_FUNC_SELECT_ARGTYPES
 AC_FUNC_UTIME_NULL
 AC_FUNC_VPRINTF
 #AC_CHECK_FUNCS([dup2 gethostname isascii iswprint memset munmap regcomp select setlocale strcasecmp strchr strdup strerror strndup strrchr strtol strtoul strtoull utime wcwidth])
-
+AX_PTHREAD( [
+            AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.])
+            CLIBS="$PTHREAD_LIBS $LIBS"
+            CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+            LDFLAGS="$LDFLAGS $PTHREAD_CFLAGS"
+            CC="$PTHREAD_CC"],[])
 # Not all compilers include /usr/local in the include and link path
 if test -d /usr/local/include; then
     CFLAGS="$CFLAGS -I/usr/local/include"
@@ -72,7 +82,7 @@ fi
 
 # Checks for libraries.
 
-# Check if we should link afflib.  
+# Check if we should link afflib.
 AC_ARG_WITH([afflib],
     [AS_HELP_STRING([--without-afflib],[Do not use AFFLIB even if it is installed])]
     [AS_HELP_STRING([--with-afflib=dir],[Specify that AFFLIB is installed in directory 'dir'])],
@@ -93,13 +103,15 @@ AS_IF([test "x$with_afflib" != "xno"],
         ]
     )]
     # Check for the header file first to make sure they have the dev install
-    [AC_CHECK_HEADERS([afflib/afflib.h], 
+    [AC_CHECK_HEADERS([afflib/afflib.h],
       [AC_CHECK_LIB([afflib], [af_open])]
     )]
 )
+#needed for sqllite
+AC_CHECK_LIB(dl, dlopen) 
 
 
-# Check if we should link libewf.  
+# Check if we should link libewf.
 AC_ARG_WITH([libewf],
     [AS_HELP_STRING([--without-libewf],[Do not use libewf even if it is installed])]
     [AS_HELP_STRING([--with-libewf=dir],[Specify that libewf is installed in directory 'dir'])],
@@ -120,13 +132,13 @@ AS_IF([test "x$with_libewf" != "xno"],
         ]
     )]
     # Check for the header file first to make sure they have the dev install
-    [AC_CHECK_HEADERS([libewf.h], 
+    [AC_CHECK_HEADERS([libewf.h],
       [AC_CHECK_LIB([ewf], [libewf_open])]
     )]
 )
 
 # sqlite requires pthread libraries - this was copied from its configure.ac
-AC_SEARCH_LIBS(pthread_create, pthread)
+# AC_SEARCH_LIBS(pthread_create, pthread)
 AC_SEARCH_LIBS(dlopen, dl)
 
 AC_CONFIG_COMMANDS([tsk3/tsk_incs.h],
@@ -148,6 +160,11 @@ AC_CONFIG_COMMANDS([tsk3/tsk_incs.h],
           if test x$ac_cv_header_sys_param_h = xyes; then
             echo "#include <sys/param.h>" >> tsk3/tsk_incs.h
           fi
+
+          if test x$ax_pthread_ok = xyes; then
+            echo "#define TSK_MULTITHREAD_LIB" >> tsk3/tsk_incs.h
+          fi
+
           echo "" >> tsk3/tsk_incs.h
           echo "#endif" >> tsk3/tsk_incs.h
         ],
@@ -155,6 +172,7 @@ AC_CONFIG_COMMANDS([tsk3/tsk_incs.h],
           ac_cv_header_unistd_h=$ac_cv_header_unistd_h
           ac_cv_header_inttypes_h=$ac_cv_header_inttypes_h
           ac_cv_header_sys_param_h=$ac_cv_header_sys_param_h
+          ax_pthread_ok=$ax_pthread_ok
         ])
 
 AC_CONFIG_FILES([Makefile
@@ -176,7 +194,9 @@ AC_CONFIG_FILES([Makefile
                  tools/timeline/Makefile
                  tests/Makefile
                  samples/Makefile
-                 man/Makefile])
+                 man/Makefile
+                 unit_tests/Makefile
+                 unit_tests/base/Makefile])
 AC_OUTPUT
 
 
diff --git a/cppunit.m4 b/cppunit.m4
new file mode 100644
index 000000000..41f067b6d
--- /dev/null
+++ b/cppunit.m4
@@ -0,0 +1,92 @@
+dnl
+dnl AM_PATH_CPPUNIT(MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
+dnl
+AC_DEFUN([AM_PATH_CPPUNIT],
+[
+
+AC_ARG_WITH(cppunit-prefix,[  --with-cppunit-prefix=PFX   Prefix where CppUnit is installed (optional)],
+            cppunit_config_prefix="$withval", cppunit_config_prefix="")
+AC_ARG_WITH(cppunit-exec-prefix,[  --with-cppunit-exec-prefix=PFX  Exec prefix where CppUnit is installed (optional)],
+            cppunit_config_exec_prefix="$withval", cppunit_config_exec_prefix="")
+
+  if test x$cppunit_config_exec_prefix != x ; then
+     cppunit_config_args="$cppunit_config_args --exec-prefix=$cppunit_config_exec_prefix"
+     if test x${CPPUNIT_CONFIG+set} != xset ; then
+        CPPUNIT_CONFIG=$cppunit_config_exec_prefix/bin/cppunit-config
+     fi
+  fi
+  if test x$cppunit_config_prefix != x ; then
+     cppunit_config_args="$cppunit_config_args --prefix=$cppunit_config_prefix"
+     if test x${CPPUNIT_CONFIG+set} != xset ; then
+        CPPUNIT_CONFIG=$cppunit_config_prefix/bin/cppunit-config
+     fi
+  fi
+
+  AC_PATH_PROG(CPPUNIT_CONFIG, cppunit-config, no)
+  cppunit_version_min=$1
+
+  AC_MSG_CHECKING(for Cppunit - version >= $cppunit_version_min)
+  no_cppunit=""
+  if test "$CPPUNIT_CONFIG" = "no" ; then
+    AC_MSG_RESULT(no)
+    no_cppunit=yes
+  else
+    CPPUNIT_CFLAGS=`$CPPUNIT_CONFIG --cflags`
+    CPPUNIT_LIBS=`$CPPUNIT_CONFIG --libs`
+    cppunit_version=`$CPPUNIT_CONFIG --version`
+
+    cppunit_major_version=`echo $cppunit_version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+    cppunit_minor_version=`echo $cppunit_version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+    cppunit_micro_version=`echo $cppunit_version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+
+    cppunit_major_min=`echo $cppunit_version_min | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+    if test "x${cppunit_major_min}" = "x" ; then
+       cppunit_major_min=0
+    fi
+    
+    cppunit_minor_min=`echo $cppunit_version_min | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+    if test "x${cppunit_minor_min}" = "x" ; then
+       cppunit_minor_min=0
+    fi
+    
+    cppunit_micro_min=`echo $cppunit_version_min | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+    if test "x${cppunit_micro_min}" = "x" ; then
+       cppunit_micro_min=0
+    fi
+
+    cppunit_version_proper=`expr \
+        $cppunit_major_version \> $cppunit_major_min \| \
+        $cppunit_major_version \= $cppunit_major_min \& \
+        $cppunit_minor_version \> $cppunit_minor_min \| \
+        $cppunit_major_version \= $cppunit_major_min \& \
+        $cppunit_minor_version \= $cppunit_minor_min \& \
+        $cppunit_micro_version \>= $cppunit_micro_min `
+
+    if test "$cppunit_version_proper" = "1" ; then
+      AC_MSG_RESULT([$cppunit_major_version.$cppunit_minor_version.$cppunit_micro_version])
+    else
+      AC_MSG_RESULT(no)
+      no_cppunit=yes
+    fi
+  fi
+
+  if test "x$no_cppunit" = x ; then
+     ifelse([$2], , :, [$2])     
+  else
+     CPPUNIT_CFLAGS=""
+     CPPUNIT_LIBS=""
+     ifelse([$3], , :, [$3])
+  fi
+
+  AC_SUBST(CPPUNIT_CFLAGS)
+  AC_SUBST(CPPUNIT_LIBS)
+])
+
+
+
diff --git a/samples/Makefile.am b/samples/Makefile.am
index 76094447d..aee7a5ce4 100644
--- a/samples/Makefile.am
+++ b/samples/Makefile.am
@@ -1,11 +1,13 @@
 AM_CPPFLAGS = -I.. -I$(srcdir)/.. -Wall 
 LDADD = ../tsk3/libtsk3.la
-LDFLAGS = -static
+AM_LDFLAGS = -static
 EXTRA_DIST = .indent.pro 
 
-noinst_PROGRAMS = posix_style callback_style
+noinst_PROGRAMS = posix_style callback_style posix_cpp_style callback_cpp_style
 posix_style_SOURCES = posix-style.cpp
 callback_style_SOURCES = callback-style.cpp
+posix_cpp_style_SOURCES = posix-cpp-style.cpp
+callback_cpp_style_SOURCES = callback-cpp-style.cpp
 
 indent:
 	indent *.cpp 
diff --git a/samples/callback-cpp-style.cpp b/samples/callback-cpp-style.cpp
new file mode 100644
index 000000000..c2bb21027
--- /dev/null
+++ b/samples/callback-cpp-style.cpp
@@ -0,0 +1,328 @@
+/* 
+*
+* This is a sample file that shows how to use some of the basic C++
+* POSIX-style library functions in The Sleuth Kit (www.sleuthkit.org).
+* There are also callback-style functions that can be used to read
+* the data and partitions.
+*
+* Copyright (c) 2008>, Brian Carrier <carrier <at> sleuthkit <dot> org>
+* All rights reserved.
+* 
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 
+* - Redistributions of source code must retain the above copyright notice,
+*   this list of conditions and the following disclaimer.
+* - Redistributions in binary form must reproduce the above copyright
+*   notice, this list of conditions and the following disclaimer in the
+*   documentation and/or other materials provided with the distribution.
+* - Neither the Sleuth Kit name nor the names of its contributors may be
+*   used to endorse or promote products derived from this software without
+*   specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <tsk3/libtsk.h>
+
+static TskHdbInfo *hdb_info;
+
+#define DO_HASHING  1
+#define DO_HASHLOOKUP 0
+
+
+/** 
+ * dent_walk callback function 
+ */
+static TSK_WALK_RET_ENUM
+fileAct(TskFsFile * fs_file, TSK_OFF_T a_off, TSK_DADDR_T addr,
+    char *buf, size_t size, TSK_FS_BLOCK_FLAG_ENUM flags, void *ptr)
+{
+    TSK_MD5_CTX *md = (TSK_MD5_CTX *) ptr;
+    if (md == NULL)
+        return TSK_WALK_CONT;
+
+    TSK_MD5_Update(md, (unsigned char *) buf, (unsigned int) size);
+
+    return TSK_WALK_CONT;
+}
+
+
+/**
+ * Process the contents of a file.
+ *
+ * @return 1 on error and 0 on success 
+ */
+static uint8_t
+procFile(TskFsFile * fs_file, const char *path)
+{
+    TSK_MD5_CTX md;
+
+    if ((fs_file->getMeta() == NULL) || (fs_file->getName() == NULL))
+        return 1;
+
+    if (fs_file->getMeta()->getType() != TSK_FS_META_TYPE_REG)
+        return 0;
+
+    //printf("Processing %s%s\n", path, fs_file->name->name);
+
+    int myflags = TSK_FS_FILE_WALK_FLAG_NOID;
+
+    TSK_MD5_Init(&md);
+
+    /* Note that we could also cycle through all of the attributes in the
+     * file by using one of the tsk_fs_attr_get() functions and walking it
+     * with tsk_fs_attr_walk(). See the File Systems section of the Library
+     * User's Guide for more details: 
+     * http://www.sleuthkit.org/sleuthkit/docs/api-docs/ */
+    if (fs_file->walk
+        ((TSK_FS_FILE_WALK_FLAG_ENUM) myflags, fileAct,
+            (void *) &md)) {
+        // ignore errors from deleted files that were being recovered
+        if (tsk_error_get_errno() != TSK_ERR_FS_RECOVER) {
+            printf("Processing: %s/%s (%" PRIuINUM ")\n", path,
+                fs_file->getName()->getName(), fs_file->getMeta()->getAddr());
+            tsk_error_print(stderr);
+        }
+        tsk_error_reset();
+    }
+    // otherwise, compute the hash of the file.
+    else {
+        unsigned char hash[16];
+
+        TSK_MD5_Final(hash, &md);
+#if 0
+        {
+            int i;
+            printf("Hash of %s/%s: ", path, fs_file->name->name);
+
+            for (i = 0; i < 16; i++) {
+                printf("%x%x", (hash[i] >> 4) & 0xf, hash[i] & 0xf);
+            }
+            printf("\n");
+        }
+#endif
+#if DO_HASHLOOKUP
+        {
+            int retval;
+            retval = tsk_hdb_lookup_raw(hdb_info, (uint8_t *) hash, 16,
+                TSK_HDB_FLAG_QUICK, NULL, NULL);
+            if (retval == 1) {
+                //printf("Ignoring file %s\n", fs_dent->name);
+            }
+            else if (retval == 0) {
+//            printf("Not Ignoring: %s/%s\n", path, name);
+            }
+        }
+#endif
+    }
+
+    return 0;
+}
+
+/**
+ * file name walk callback.  Walk the contents of each file 
+ * that is found.
+ */
+static TSK_WALK_RET_ENUM
+dirAct(TskFsFile * fs_file, const char *path, void *ptr)
+{
+	fprintf(stdout,
+               "file systems file name: %s\n", fs_file->getName()->getName());
+
+    /* Ignore NTFS System files */
+    if ((TSK_FS_TYPE_ISNTFS(fs_file->getFsInfo()->getFsType()))
+        && (fs_file->getName()->getName()[0] == '$'))
+        return TSK_WALK_CONT;
+
+    /* If the name has corresponding metadata, then walk it */
+    if (fs_file->getMeta()) {
+        procFile(fs_file, path);
+    }
+
+    return TSK_WALK_CONT;
+}
+
+
+
+
+/** 
+ * Analyze the volume starting at byte offset 'start' 
+ * and walk each file that can be found.
+ *
+ * @param img Disk image to be analyzed.
+ * @param start Byte offset of volume starting location.
+ *
+ * @return 1 on error and 0 on success
+*/
+static uint8_t
+procFs(TskImgInfo * img_info, TSK_OFF_T start)
+{
+    TskFsInfo *fs_info = new TskFsInfo();
+
+    /* Try it as a file system */
+    if (fs_info->open(img_info, start, TSK_FS_TYPE_DETECT))
+    {
+        delete fs_info;
+        tsk_error_print(stderr);
+
+        /* We could do some carving on the volume data at this point */
+
+        return 1;
+    }
+
+    /* Walk the files, starting at the root directory */
+    if (fs_info->dirWalk(fs_info->getRootINum(),
+            (TSK_FS_DIR_WALK_FLAG_ENUM) (TSK_FS_DIR_WALK_FLAG_RECURSE),
+            dirAct, NULL)) {
+        delete fs_info;
+        tsk_error_print(stderr);
+        fs_info->close();
+        return 1;
+    }
+
+    /* We could do some analysis of unallocated blocks at this point...  */
+
+    fs_info->close();
+    delete fs_info;
+    return 0;
+}
+
+/**
+ * Volume system walk callback function that will analyze 
+ * each volume to find a file system.
+ */
+static TSK_WALK_RET_ENUM
+vsAct(TskVsInfo * vs_info, const TskVsPartInfo * vs_part, void *ptr)
+{
+    if (procFs(const_cast<TskImgInfo *>(vs_info->getImgInfo()), const_cast<TskVsPartInfo *>(vs_part)->getStart() * vs_info->getBlockSize())) {
+        // if we return ERROR here, then the walk will stop.  But, the 
+        // error could just be because we looked into an unallocated volume.
+        // do any special error handling / reporting here.
+        tsk_error_reset();
+        return TSK_WALK_CONT;
+    }
+
+    return TSK_WALK_CONT;
+}
+
+
+/**
+ * Process the data as a volume system to find the partitions
+ * and volumes.  
+ * File system analysis will be performed on each partition.
+ *
+ * @param img Image file information structure for data to analyze
+ * @param start Byte offset to start analyzing from. 
+ *
+ * @return 1 on error and 0 on success
+ */
+static uint8_t
+procVs(TskImgInfo * img_info, TSK_OFF_T start)
+{
+    TskVsInfo *vs_info = new TskVsInfo();;
+
+    // USE mm_walk to get the volumes 
+    if (vs_info->open(img_info, start, TSK_VS_TYPE_DETECT)) {
+        if (tsk_verbose)
+            fprintf(stderr,
+                "Error determining volume system -- trying file systems\n");
+
+        /* There was no volume system, but there could be a file system */
+        tsk_error_reset();
+        if (procFs(img_info, start)) {
+            delete vs_info;
+            return 1;
+        }
+    }
+    else {
+        fprintf(stderr, "Volume system open, examining each\n");
+
+        /* Walk the allocated volumes (skip metadata and unallocated volumes) */
+        if (vs_info->vsPartWalk(0, vs_info->getPartCount() - 1,
+                (TSK_VS_PART_FLAG_ENUM) (TSK_VS_PART_FLAG_ALLOC), vsAct,
+                NULL)) {
+            delete vs_info;
+            return 1;
+        }
+    }
+    delete vs_info;
+    return 0;
+}
+
+
+int
+main(int argc, char **argv1)
+{
+    TskImgInfo *img_info = new TskImgInfo();
+    TSK_TCHAR **argv;
+
+#ifdef TSK_WIN32
+    // On Windows, get the wide arguments (mingw doesn't support wmain)
+    argv = CommandLineToArgvW(GetCommandLineW(), &argc);
+    if (argv == NULL) {
+        delete img_info;
+        fprintf(stderr, "Error getting wide arguments\n");
+        exit(1);
+    }
+#else
+    argv = (TSK_TCHAR **) argv1;
+#endif
+
+    if (argc != 2) {
+        delete img_info;
+        fprintf(stderr, "Missing image name\n");
+        exit(1);
+    }
+
+#if DO_HASHLOOKUP
+    /* Setup hash infrastructure */
+    if ((hdb_info =
+            tsk_hdb_open(_TSK_T("/XXX/NSRLFile.txt"),
+                TSK_HDB_OPEN_NONE)) == NULL) {
+        delete img_info;
+        tsk_error_print(stderr);
+        exit(1);
+    }
+
+    if (tsk_hdb_hasindex(hdb_info, TSK_HDB_HTYPE_MD5_ID) == 0) {
+        delete img_info;
+        fprintf(stderr,
+            "Hash database does not have an index (create one using hfind -i nsrl-md5 HASHFILE\n");
+        exit(1);
+    }
+#else
+    hdb_info = NULL;
+#endif
+
+    if (img_info->open(argv[1], TSK_IMG_TYPE_DETECT, 0) == 1) {
+        delete img_info;
+        fprintf(stderr, "Error opening file\n");
+        tsk_error_print(stderr);
+        exit(1);
+    }
+
+    if (procVs(img_info, 0)) {
+        delete img_info;
+        tsk_error_print(stderr);
+        exit(1);
+    }
+
+    delete img_info;
+    return 0;
+}
diff --git a/samples/callback-style.cpp b/samples/callback-style.cpp
index 963d55049..e31f51bbf 100644
--- a/samples/callback-style.cpp
+++ b/samples/callback-style.cpp
@@ -5,7 +5,7 @@
 * There are also callback-style functions that can be used to read
 * the data and partitions.
 *
-* Copyright (c) 2008>, Brian Carrier <carrier <at> sleuthkit <dot> org>
+* Copyright (c) 2008-2011  Brian Carrier <carrier <at> sleuthkit <dot> org>
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
@@ -94,7 +94,7 @@ proc_file(TSK_FS_FILE * fs_file, const char *path)
         (fs_file, (TSK_FS_FILE_WALK_FLAG_ENUM) myflags, file_act,
             (void *) &md)) {
         // ignore errors from deleted files that were being recovered
-        if (tsk_errno != TSK_ERR_FS_RECOVER) {
+        if (tsk_error_get_errno() != TSK_ERR_FS_RECOVER) {
             printf("Processing: %s/%s (%" PRIuINUM ")\n", path,
                 fs_file->name->name, fs_file->meta->addr);
             tsk_error_print(stderr);
@@ -142,6 +142,9 @@ proc_file(TSK_FS_FILE * fs_file, const char *path)
 static TSK_WALK_RET_ENUM
 dir_act(TSK_FS_FILE * fs_file, const char *path, void *ptr)
 {
+	fprintf(stdout,
+		"file systems file name: %s\n", fs_file->name->name);
+
     /* Ignore NTFS System files */
     if ((TSK_FS_TYPE_ISNTFS(fs_file->fs_info->ftype))
         && (fs_file->name->name[0] == '$'))
diff --git a/samples/posix-cpp-style.cpp b/samples/posix-cpp-style.cpp
new file mode 100644
index 000000000..7c83003ae
--- /dev/null
+++ b/samples/posix-cpp-style.cpp
@@ -0,0 +1,345 @@
+/*
+ * 
+ * This is a sample file that shows how to use some of the basic C++
+ * POSIX-style library functions in The Sleuth Kit (www.sleuthkit.org).
+ * There are also callback-style functions that can be used to read
+ * the data and partitions.
+ *
+ * Copyright (c) 2008>, Brian Carrier <carrier <at> sleuthkit <dot> org>
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - Neither the Sleuth Kit name nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <tsk3/libtsk.h>
+
+/**
+ * Open a directory and cycle through its contents.  Read each file and recurse
+ * into each directory.
+ *
+ * @param fs_info File system to process
+ * @param stack Stack to prevent infinite recursion loops
+ * @param dir_inum Metadata address of directory to open
+ * @param path Path of directory being opened
+ * @returns 1 on error
+ */
+static uint8_t
+procDir(TskFsInfo * fs_info, TSK_STACK * stack,
+    TSK_INUM_T dir_inum, const char *path)
+{
+    TskFsDir *fs_dir = new TskFsDir();
+    size_t i;
+    char *path2 = NULL;
+    char *buf = NULL;
+
+    // open the directory
+    if ((fs_dir->open(fs_info, dir_inum)) == 1) {
+        fprintf(stderr, "Error opening directory: %" PRIuINUM "\n",
+            dir_inum);
+        tsk_error_print(stderr);
+        return 1;
+    }
+
+    /* These should be dynamic lengths, but this is just a sample program.
+     * Allocate heap space instead of stack to prevent overflow for deep
+     * directories. */
+    if ((path2 = (char *) malloc(4096)) == NULL) {
+        return 1;
+    }
+
+    if ((buf = (char *) malloc(2048)) == NULL) {
+        free(path2);
+        return 1;
+    }
+
+    // cycle through each entry
+    for (i = 0; i < fs_dir->getSize(); i++) {
+        TskFsFile *fs_file;
+        TSK_OFF_T off = 0;
+        size_t len = 0;
+
+        // get the entry
+        if ((fs_file = fs_dir->getFile(i)) == NULL) {
+            fprintf(stderr,
+                "Error getting directory entry %" PRIuSIZE
+                " in directory %" PRIuINUM "\n", i, dir_inum);
+            tsk_error_print(stderr);
+
+            free(path2);
+            free(buf);
+            return 1;
+        }
+
+        /* Ignore NTFS System files */
+        if ((TSK_FS_TYPE_ISNTFS(fs_file->getFsInfo()->getFsType())) &&
+            (const_cast<TskFsName *>(fs_file->getName())->getName()[0] == '$')) {
+            fs_file->close();
+            continue;
+        }
+
+        //printf("Processing %s/%s\n", path, fs_file->name->name);
+
+        // make sure it's got metadata and not only a name
+        if (fs_file->getMeta()) {
+            ssize_t cnt;
+
+            /* Note that we could also cycle through all of the attributes in the
+             * file by using one of the tsk_fs_attr_get() functions and reading it
+             * with tsk_fs_attr_read().  See the File Systems section of the Library
+             * User's Guide for more details: 
+             * http://www.sleuthkit.org/sleuthkit/docs/api-docs/ */
+            
+            // read file contents
+            if (fs_file->getMeta()->getType() == TSK_FS_META_TYPE_REG) {
+                int myflags = 0;
+                TSK_OFF_T fSize = fs_file->getMeta()->getSize();
+                for (off = 0; off < fSize; off += len) {
+                    if (fSize - off < 2048)
+                        len = (size_t) (fSize - off);
+                    else
+                        len = 2048;
+
+                    cnt = fs_file->read(off, buf, len,
+                        (TSK_FS_FILE_READ_FLAG_ENUM) myflags);
+                    if (cnt == -1) {
+                        // could check tsk_errno here for a recovery error (TSK_ERR_FS_RECOVER)
+                        fprintf(stderr, "Error reading %s file: %s\n",
+                            ((fs_file->getName()->getFlags()
+                                     & TSK_FS_NAME_FLAG_UNALLOC)
+                                || (fs_file->getMeta()->getFlags()
+                                     & TSK_FS_META_FLAG_UNALLOC)) ?
+                            "unallocated" : "allocated",
+                            fs_file->getName()->getName());
+                        tsk_error_print(stderr);
+                        break;
+                    }
+                    else if (cnt != (ssize_t) len) {
+                        fprintf(stderr,
+                            "Warning: %" PRIuSIZE " of %" PRIuSIZE
+                            " bytes read from %s file %s\n", cnt, len,
+                            ((fs_file->getName()->getFlags()
+                                    & TSK_FS_NAME_FLAG_UNALLOC)
+                                || (fs_file->getMeta()->getFlags()
+                                    & TSK_FS_META_FLAG_UNALLOC)) ?
+                            "unallocated" : "allocated",
+                            fs_file->getName()->getName());
+                    }
+
+                    // do something with the data...
+                }
+            }
+
+            // recurse into another directory (unless it is a '.' or '..')
+            else if (fs_file->getMeta()->getType() == TSK_FS_META_TYPE_DIR) {
+                if (TSK_FS_ISDOT(fs_file->getName()->getName()) == 0) {
+
+                    // only go in if it is not on our stack
+                    if (tsk_stack_find(stack, fs_file->getMeta()->getAddr()) == 0) {
+                        // add the address to the top of the stack
+                        tsk_stack_push(stack, fs_file->getMeta()->getAddr() );
+
+                        snprintf(path2, 4096, "%s/%s", path,
+                            fs_file->getName()->getName());
+                        if (procDir(fs_info, stack, fs_file->getMeta()->getAddr(),
+                                path2)) {
+                            fs_file->close();
+                            fs_dir->close();
+                            free(path2);
+                            free(buf);
+                            return 1;
+                        }
+
+                        // pop the address
+                        tsk_stack_pop(stack);
+                    }
+                }
+            }
+        }
+        fs_file->close();
+    }
+    fs_dir->close();
+
+    free(path2);
+    free(buf);
+    return 0;
+}
+
+/** 
+* Analyze the volume starting at byte offset 'start' and look
+* for a file system.  When found, the files will be analyzed. 
+*
+* @param img Disk image to be analyzed.
+* @param start Byte offset of volume starting location.
+*
+* @return 1 on error and 0 on success
+*/
+static uint8_t
+procFs(TskImgInfo * img_info, TSK_OFF_T start)
+{
+    TskFsInfo *fs_info = new TskFsInfo();
+    TSK_STACK *stack;
+
+    /* Try it as a file system */
+    if ((fs_info->open(img_info, start, TSK_FS_TYPE_DETECT)) == 1)
+    {
+        fprintf(stderr,
+            "Error opening file system in partition at offset %" PRIuOFF
+            "\n", start);
+        tsk_error_print(stderr);
+
+        /* We could do some carving on the volume data at this point */
+
+        return 1;
+    }
+
+    // create a stack to prevent infinite loops
+    stack = tsk_stack_create();
+
+    // Process the directories
+    if (procDir(fs_info, stack, fs_info->getRootINum(), "")) {
+        fprintf(stderr,
+            "Error processing file system in partition at offset %" PRIuOFF
+            "\n", start);
+        delete fs_info;
+        return 1;
+    }
+
+    tsk_stack_free(stack);
+
+    /* We could do some analysis of unallocated blocks at this point...  */
+
+
+    delete fs_info;
+    return 0;
+}
+
+
+/**
+* Process the data as a volume system to find the partitions
+ * and volumes.  
+ * File system analysis will be performed on each partition.
+ *
+ * @param img Image file information structure for data to analyze
+ * @param start Byte offset to start analyzing from. 
+ *
+ * @return 1 on error and 0 on success
+ */
+static uint8_t
+procVs(TskImgInfo * img_info, TSK_OFF_T start)
+{
+    TskVsInfo *vs_info = new TskVsInfo();
+
+    // Open the volume system
+    if ((vs_info->open(img_info, start, TSK_VS_TYPE_DETECT)) == 1) {
+        if (tsk_verbose)
+            fprintf(stderr,
+                "Error determining volume system -- trying file systems\n");
+
+        /* There was no volume system, but there could be a file system */
+        tsk_error_reset();
+        if (procFs(img_info, start)) {
+            return 1;
+        }
+    }
+    else {
+        fprintf(stderr, "Volume system open, examining each\n");
+
+        // cycle through the partitions
+        for (TSK_PNUM_T i = 0; i < vs_info->getPartCount(); i++) {
+            const TskVsPartInfo *vs_part;
+
+            if ((vs_part = vs_info->getPart(i)) == NULL) {
+                fprintf(stderr, "Error getting volume %" PRIuPNUM "\n", i);
+                continue;
+            }
+
+            // ignore the metadata partitions
+            if (const_cast<TskVsPartInfo *>(vs_part)->getFlags() & TSK_VS_PART_FLAG_META)
+                continue;
+
+            // could do something with unallocated volumes
+            else if (const_cast<TskVsPartInfo *>(vs_part)->getFlags() & TSK_VS_PART_FLAG_UNALLOC) {
+
+            }
+            else {
+                if (procFs(img_info,
+                    const_cast<TskVsPartInfo *>(vs_part)->getStart() * vs_info->getBlockSize())) {
+                    // We could do more fancy error checking here to see the cause 
+                    // of the error or consider the allocation status of the volume...
+                    tsk_error_reset();
+                }
+            }
+        }
+        vs_info->close();
+    }
+    return 0;
+}
+
+int
+main(int argc, char **argv1)
+{
+    TskImgInfo *img_info = new TskImgInfo();
+    TSK_TCHAR **argv;
+
+#ifdef TSK_WIN32
+    // On Windows, get the wide arguments (mingw doesn't support wmain)
+    argv = CommandLineToArgvW(GetCommandLineW(), &argc);
+    if (argv == NULL) {
+        fprintf(stderr, "Error getting wide arguments\n");
+        exit(1);
+    }
+#else
+    argv = (TSK_TCHAR **) argv1;
+#endif
+
+    if (argc != 2) {
+        fprintf(stderr, "Missing image name\n");
+        exit(1);
+    }
+
+    // open the disk image
+    img_info->open((const TSK_TCHAR *) argv[1],
+        TSK_IMG_TYPE_DETECT, 0);
+    if (img_info == NULL) {
+        fprintf(stderr, "Error opening file\n");
+        tsk_error_print(stderr);
+        exit(1);
+    }
+
+    // process the volume starting at sector 0
+    if (procVs(img_info, 0)) {
+        tsk_error_print(stderr);
+        delete img_info;
+        exit(1);
+    }
+
+    // close the image
+    delete img_info;
+    return 0;
+}
diff --git a/samples/posix-style.cpp b/samples/posix-style.cpp
index c14b22903..c4246a7fe 100644
--- a/samples/posix-style.cpp
+++ b/samples/posix-style.cpp
@@ -5,7 +5,7 @@
  * There are also callback-style functions that can be used to read
  * the data and partitions.
  *
- * Copyright (c) 2008>, Brian Carrier <carrier <at> sleuthkit <dot> org>
+ * Copyright (c) 2008-2011, Brian Carrier <carrier <at> sleuthkit <dot> org>
  * All rights reserved.
  * 
  * Redistribution and use in source and binary forms, with or without
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 6581bf9e3..7433c334f 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -3,13 +3,69 @@ LDADD = ../tsk3/libtsk3.la
 LDFLAGS = -static
 EXTRA_DIST = .indent.pro 
 
-noinst_PROGRAMS = read_apis fs_fname_apis fs_attrlist_apis
+noinst_PROGRAMS = read_apis fs_fname_apis fs_attrlist_apis fs_thread_test
 read_apis_SOURCES = read_apis.cpp
 fs_fname_apis_SOURCES = fs_fname_apis.cpp
 fs_attrlist_apis_SOURCES = fs_attrlist_apis.cpp
+fs_thread_test_SOURCES = fs_thread_test.cpp tsk_thread.cpp tsk_thread.h
 
 indent:
 	indent *.cpp 
 
 clean-local:
 	-rm -f *.cpp~ 
+	rm -f base.log thread-*.log
+
+IMAGE_DIR=$(HOME)/from_brian
+NTHREADS=1
+NITERS=1
+
+# The 'check' target can be run by the normal build process, but we
+# don't (yet) check in a set of standard test images.  So, our target
+# must be run manually for now.  For example, to run all fs images
+# through 10 threads with 2 iterations per thread:
+#
+#  make check-manual NTHREADS=10 NITERS=2 IMAGE_DIR=/path/to/test/images/
+#
+check-manual:
+	$(MAKE) check_ext2fs check_diffs
+	$(MAKE) check_ffs check_diffs
+	$(MAKE) check_hfs check_diffs
+	$(MAKE) check_ntfs check_diffs
+	$(MAKE) check_fatfs check_diffs
+
+check_ext2fs: fs_thread_test
+	rm -f base.log thread-*.log
+	./fs_thread_test -f ext2 $(IMAGE_DIR)/ext2fs.dd 1 1
+	mv thread-0.log base.log
+	./fs_thread_test -f ext2 $(IMAGE_DIR)/ext2fs.dd $(NTHREADS) $(NITERS)
+
+check_ffs: fs_thread_test
+	rm -f base.log thread-*.log
+	./fs_thread_test -f ufs $(IMAGE_DIR)/misc-ufs1.dd 1 1
+	mv thread-0.log base.log
+	./fs_thread_test -f ufs $(IMAGE_DIR)/misc-ufs1.dd $(NTHREADS) $(NITERS)
+
+check_hfs: fs_thread_test
+	rm -f base.log thread-*.log
+	./fs_thread_test -f hfs -o 64 $(IMAGE_DIR)/test_hfs.dmg 1 1
+	mv thread-0.log base.log
+	./fs_thread_test -f hfs -o 64 $(IMAGE_DIR)/test_hfs.dmg $(NTHREADS) $(NITERS)
+
+check_ntfs: fs_thread_test
+	rm -f base.log thread-*.log
+	./fs_thread_test -f ntfs $(IMAGE_DIR)/ntfs-img-kw-1.dd 1 1
+	mv thread-0.log base.log
+	./fs_thread_test -f ntfs $(IMAGE_DIR)/ntfs-img-kw-1.dd $(NTHREADS) $(NITERS)
+
+check_fatfs: fs_thread_test
+	rm -f base.log thread-*.log
+	./fs_thread_test -f fat $(IMAGE_DIR)/fat32.dd 1 1
+	mv thread-0.log base.log
+	./fs_thread_test -f fat $(IMAGE_DIR)/fat32.dd $(NTHREADS) $(NITERS)
+
+check_diffs:
+	@for i in thread-*.log; do \
+	  echo diff base.log $$i; \
+	  diff base.log $$i || exit 1; \
+	done;
diff --git a/tests/fs_attrlist_apis.cpp b/tests/fs_attrlist_apis.cpp
index c0b9fa990..00410bfec 100644
--- a/tests/fs_attrlist_apis.cpp
+++ b/tests/fs_attrlist_apis.cpp
@@ -1,8 +1,8 @@
 /*
-* The Sleuth Kit 
+* The Sleuth Kit
 *
 * Brian Carrier [carrier <at> sleuthkit [dot] org]
-* Copyright (c) 2008 Brian Carrier.  All Rights reserved
+* Copyright (c) 2008-2011 Brian Carrier.  All Rights reserved
 *
 *
 * This software is distributed under the Common Public License 1.0
@@ -54,7 +54,7 @@ test_get_type(TSK_FS_INFO * a_fs, TSK_INUM_T a_addr,
 
 /* Verify that all attributes can be accessed from both get_idx and get_type...
  * @param a_addr The metadata address of the file to analyze
- * @param a_len Expected number of attributes in file. 
+ * @param a_len Expected number of attributes in file.
  * @returns 1 if a test failed
  */
 static int
@@ -152,10 +152,10 @@ test_get_apis(TSK_FS_INFO * a_fs, TSK_INUM_T a_addr, int a_len)
             tsk_error_print(stderr);
             return 1;
         }
-        else if (tsk_errno != TSK_ERR_FS_ATTR_NOTFOUND) {
+        else if (tsk_error_get_errno() != TSK_ERR_FS_ATTR_NOTFOUND) {
             fprintf(stderr,
                 "Unexpected error code %x from getting %d-0xfd (random ID) from %"
-                PRIuINUM "\n", (int) tsk_errno, fs_attr->type, a_addr);
+                PRIuINUM "\n", (int)tsk_error_get_errno(), fs_attr->type, a_addr);
             tsk_error_print(stderr);
             return 1;
         }
@@ -172,10 +172,10 @@ test_get_apis(TSK_FS_INFO * a_fs, TSK_INUM_T a_addr, int a_len)
             tsk_error_print(stderr);
             return 1;
         }
-        else if (tsk_errno != TSK_ERR_FS_ATTR_NOTFOUND) {
+        else if (tsk_error_get_errno() != TSK_ERR_FS_ATTR_NOTFOUND) {
             fprintf(stderr,
                 "Unexpected error code %x from getting %d-X (random type, no id) from %"
-                PRIuINUM "\n", (int) tsk_errno, fs_attr->type, a_addr);
+                PRIuINUM "\n", (int)tsk_error_get_errno(), fs_attr->type, a_addr);
             tsk_error_print(stderr);
             return 1;
         }
diff --git a/tests/fs_fname_apis.cpp b/tests/fs_fname_apis.cpp
index 154208f8f..8f58abdce 100644
--- a/tests/fs_fname_apis.cpp
+++ b/tests/fs_fname_apis.cpp
@@ -3,7 +3,7 @@
 *
 *
 * Brian Carrier [carrier <at> sleuthkit [dot] org]
-* Copyright (c) 2008 Brian Carrier.  All Rights reserved
+* Copyright (c) 2008-2011 Brian Carrier.  All Rights reserved
 *
 *
 * This software is distributed under the Common Public License 1.0
diff --git a/tests/fs_thread_test.cpp b/tests/fs_thread_test.cpp
new file mode 100644
index 000000000..224eb6ff2
--- /dev/null
+++ b/tests/fs_thread_test.cpp
@@ -0,0 +1,236 @@
+// This file implements a thread test for the fs layer.  The program
+// opens a file system and then launches N threads.  Each thread walks
+// through the same shared file system (TSK_FS_INFO) and produces an
+// output file named "thread-N.log".  The actual format of the output
+// doesn't really matter.  All we need is for the output to be
+// different if we hit race conditions.
+//
+// To turn this into a thread test, the caller (e.g. Makefile or
+// script) should arrange to run the program as follows:
+// 
+//   run with one thread; produce thread-0.log; rename to base.log
+//   run with N threads; produce thread-0.log, thread-1.log, etc.
+//   diff base.log thread-0.log
+//   diff base.log thread-1.log
+//   ...
+//
+// The test passes if all of the thread-N.log files are identical to
+// the base.log file.  Of course, this does not guarantee thread
+// safety, but by running enough threads and enough repetitions of
+// the test without error, you can be more confident.
+
+#include <tsk3/libtsk.h>
+
+#include "tsk_thread.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+static TSK_WALK_RET_ENUM
+proc_dir(TSK_FS_FILE* fs_file, const char* path, void* stuff)
+{
+    FILE* log = (FILE*)stuff;
+    
+    fprintf(log, "%s%s: flags: %d, addr: %d", path, fs_file->name->name,
+            fs_file->meta->flags, (int)fs_file->meta->addr);
+    
+    // hmm, not sure if the ntfs sid stuff is working at all, but at
+    // least call it to detect possible hangs
+    if (fs_file->fs_info->fread_owner_sid) {
+        char* sid_str = 0;
+        if (tsk_fs_file_get_owner_sid(fs_file, &sid_str)) {
+            if (tsk_verbose) {
+                tsk_error_print(stderr);
+            }
+        } else {
+            fprintf(log, ", sid_str: %s\n", sid_str);
+            free(sid_str);
+        }
+    }
+    fputc('\n', log);
+
+    if (fs_file->meta->type == TSK_FS_META_TYPE_REG) {
+        char buf[2048];
+        size_t len = 0;
+        for (TSK_OFF_T off = 0; off < fs_file->meta->size; off += len) {
+            if (fs_file->meta->size - off < (TSK_OFF_T)sizeof(buf)) {
+                len = (size_t) (fs_file->meta->size - off);
+            } else {
+                len = sizeof(buf);
+            }
+
+            int myflags = 0;    
+            ssize_t cnt = tsk_fs_file_read(fs_file, off, buf, len, (TSK_FS_FILE_READ_FLAG_ENUM)myflags);
+            if (cnt == -1) {
+                if (tsk_verbose) {
+                    fprintf(stderr, "Error reading %s file: %s\n",
+                            ((fs_file->name->
+                              flags & TSK_FS_NAME_FLAG_UNALLOC)
+                             || (fs_file->meta->
+                                 flags & TSK_FS_META_FLAG_UNALLOC)) ?
+                            "unallocated" : "allocated",
+                            fs_file->name->name);
+                    tsk_error_print(stderr);
+                }
+                break;
+            } else if (cnt != (ssize_t) len) {
+                if (tsk_verbose) {
+                    fprintf(stderr,
+                            "Warning: %" PRIuSIZE " of %" PRIuSIZE
+                            " bytes read from %s file %s\n", cnt, len,
+                            ((fs_file->name->
+                              flags & TSK_FS_NAME_FLAG_UNALLOC)
+                             || (fs_file->meta->
+                                 flags & TSK_FS_META_FLAG_UNALLOC)) ?
+                            "unallocated" : "allocated",
+                            fs_file->name->name);
+                }
+            }
+
+            // data is in buf[0..len); could be binary, not null terminated
+            // might consider printing it out if all ascii or looks like text
+            // or print hexdump, just for thread comparison
+        }
+    }
+
+    return TSK_WALK_CONT;
+}
+
+static void
+proc_fs(TSK_FS_INFO* fs, FILE* log)
+{
+    // Walk starting at $OrphanFiles to provoke recursive call to tsk_fs_dir_load_inum_named.
+    if (tsk_fs_dir_walk(fs, TSK_FS_ORPHANDIR_INUM(fs), TSK_FS_DIR_WALK_FLAG_RECURSE, proc_dir, log)) {
+        fprintf(stderr, "dir walk from $OrphanFiles failed\n");
+        tsk_error_print(stderr);
+    }
+
+    // Walk starting at the root.  Note that we walk the root tree
+    // -after- the $OrphanFile because if we use the other order,
+    // things are already cached.
+    if (tsk_fs_dir_walk(fs, fs->root_inum, TSK_FS_DIR_WALK_FLAG_RECURSE, proc_dir, log)) {
+        fprintf(stderr, "dir walk from root failed\n");
+        tsk_error_print(stderr);
+    }
+}
+
+class MyThread : public TskThread {
+public:
+    // The threads share the same TSK_FS_INFO
+    MyThread(int id, TSK_FS_INFO* fs, size_t niters) :
+        m_id(id), m_fs(fs), m_niters(niters) {}
+
+    void operator()() {
+        // We rewrite the log on every iteration to prevent truly huge
+        // logs files.
+        char logname[256];
+        sprintf(logname, "thread-%d.log", m_id);
+        for (size_t i = 0; i < m_niters; ++i) {
+            FILE* log = fopen(logname, "w");
+            if (log == 0) {
+                perror(logname);
+                exit(1);
+            }
+            proc_fs(m_fs, log);
+            fclose(log);
+        }
+    }
+private:
+    int m_id;
+    TSK_FS_INFO* m_fs;
+    size_t m_niters;
+
+    // disable copy and assignment
+    MyThread(const MyThread&);
+    MyThread& operator=(const MyThread&);
+};
+
+static const char* progname;
+
+static void
+usage()
+{
+    fprintf(stderr, "Usage: %s [-f fstype ] [-o imgoffset ] [-v] image nthreads niters\n", progname);
+    exit(1);
+}
+
+int
+main(int argc, char* argv[])
+{
+    progname = argv[0];
+
+    TSK_FS_TYPE_ENUM fstype = TSK_FS_TYPE_DETECT;
+    TSK_OFF_T imgaddr = 0;
+    int ch;
+    while ((ch = GETOPT(argc, argv, "f:o:v")) != -1) {
+        switch (ch) {
+        case 'f':
+            fstype = tsk_fs_type_toid(OPTARG);
+            if (fstype == TSK_FS_TYPE_UNSUPP) {
+                fprintf(stderr, "Unsupported file system type: %s\n", OPTARG);
+                usage();
+            }
+            break;
+        case 'o':
+            if ((imgaddr = tsk_parse_offset(OPTARG)) == -1) {
+                tsk_error_print(stderr);
+                exit(1);
+            }
+            break;
+        case 'v':
+            tsk_verbose = 1;
+            break;
+        default:
+            usage();
+            break;
+        }
+    }
+    if (argc - OPTIND != 3) {
+        usage();
+    }
+
+    const char* image = argv[OPTIND];
+    size_t nthreads = atoi(argv[OPTIND + 1]);
+    if (nthreads == 0) {
+        fprintf(stderr, "invalid nthreads\n");
+        exit(1);
+    }
+    size_t niters = atoi(argv[OPTIND + 2]);
+    if (niters == 0) {
+        fprintf(stderr, "invalid nthreads\n");
+        exit(1);
+    }
+    
+    TSK_IMG_INFO* img = tsk_img_open_sing(image, TSK_IMG_TYPE_DETECT, 0);
+    if (img == 0) {
+        tsk_error_print(stderr);
+        exit(1);
+    }
+
+    if ((imgaddr * img->sector_size) >= img->size) {
+        fprintf(stderr, "Sector offset supplied is larger than disk image (maximum: %"
+                PRIu64 ")\n", img->size / img->sector_size);
+        exit(1);
+    }
+
+    TSK_FS_INFO* fs = tsk_fs_open_img(img, imgaddr * img->sector_size, fstype);
+    if (fs == 0) {
+        tsk_img_close(img);
+        tsk_error_print(stderr);
+        exit(1);
+    }
+
+    TskThread** threads = new TskThread*[nthreads];
+    for (size_t i = 0; i < nthreads; ++i) {
+        threads[i] = new MyThread(i, fs, niters);
+    }
+    TskThread::run(threads, nthreads);
+    for (size_t i = 0; i < nthreads; ++i) {
+        delete threads[i];
+    }
+    delete[] threads;
+    
+    tsk_fs_close(fs);
+    tsk_img_close(img);
+}
diff --git a/tests/read_apis.cpp b/tests/read_apis.cpp
index 4e06807cf..e4539a737 100644
--- a/tests/read_apis.cpp
+++ b/tests/read_apis.cpp
@@ -4,7 +4,7 @@
 *
 *
 * Brian Carrier [carrier <at> sleuthkit [dot] org]
-* Copyright (c) 2008-2010 Brian Carrier.  All Rights reserved
+* Copyright (c) 2008-2011 Brian Carrier.  All Rights reserved
 *
 * This software is distributed under the Common Public License 1.0
 */
diff --git a/tests/tsk_thread.cpp b/tests/tsk_thread.cpp
new file mode 100644
index 000000000..a05842a5d
--- /dev/null
+++ b/tests/tsk_thread.cpp
@@ -0,0 +1,39 @@
+#include "tsk_thread.h"
+
+#include <pthread.h>
+#include <assert.h>
+
+TskThread::~TskThread()
+{
+    // empty
+}
+
+static void *x_thread_top(void *ta)
+{
+    TskThread *thread = (TskThread *)ta;
+    (*thread)();
+    return 0;
+}
+
+void TskThread::run(TskThread** threads, size_t nthreads)
+{
+    pthread_t* the_threads = new pthread_t[nthreads];
+    pthread_attr_t pat;
+    pthread_attr_init(&pat);
+    for(size_t p = 0; p < nthreads; p++) {
+        pthread_t tid;
+        int pt = pthread_create(&tid, 0, x_thread_top, threads[p]);
+        assert(0 == pt);
+        the_threads[p] = tid;
+    }
+    /* It's quite annoying, you can't wait for more than one in pthreads. */
+    for(size_t p = 0; p < nthreads; p++) {
+        void *thread_return;
+        int pt;
+        pt = pthread_join(the_threads[p], &thread_return);
+        assert(0 == pt);
+        assert(0 == thread_return);
+    }
+    pthread_attr_destroy(&pat);
+    delete[] the_threads;
+}
diff --git a/tools/autotools/tsk_comparedir.cpp b/tools/autotools/tsk_comparedir.cpp
index bff531392..48387d045 100644
--- a/tools/autotools/tsk_comparedir.cpp
+++ b/tools/autotools/tsk_comparedir.cpp
@@ -3,7 +3,7 @@
  ** The Sleuth Kit 
  **
  ** Brian Carrier [carrier <at> sleuthkit [dot] org]
- ** Copyright (c) 2010 Brian Carrier.  All Rights reserved
+ ** Copyright (c) 2010-2011 Brian Carrier.  All Rights reserved
  **
  ** This software is distributed under the Common Public License 1.0
  **
diff --git a/tools/autotools/tsk_gettimes.cpp b/tools/autotools/tsk_gettimes.cpp
index 816d093ec..a2d5a5d40 100644
--- a/tools/autotools/tsk_gettimes.cpp
+++ b/tools/autotools/tsk_gettimes.cpp
@@ -3,7 +3,7 @@
  ** The Sleuth Kit 
  **
  ** Brian Carrier [carrier <at> sleuthkit [dot] org]
- ** Copyright (c) 2010 Brian Carrier.  All Rights reserved
+ ** Copyright (c) 2010-2011 Brian Carrier.  All Rights reserved
  **
  ** This software is distributed under the Common Public License 1.0
  **
diff --git a/tools/autotools/tsk_loaddb.cpp b/tools/autotools/tsk_loaddb.cpp
index 4a80fc292..291934473 100644
--- a/tools/autotools/tsk_loaddb.cpp
+++ b/tools/autotools/tsk_loaddb.cpp
@@ -3,7 +3,7 @@
  ** The Sleuth Kit 
  **
  ** Brian Carrier [carrier <at> sleuthkit [dot] org]
- ** Copyright (c) 2010 Brian Carrier.  All Rights reserved
+ ** Copyright (c) 2010-2011 Brian Carrier.  All Rights reserved
  **
  ** This software is distributed under the Common Public License 1.0
  **
diff --git a/tools/autotools/tsk_recover.cpp b/tools/autotools/tsk_recover.cpp
index 499f5151c..dd58b5043 100644
--- a/tools/autotools/tsk_recover.cpp
+++ b/tools/autotools/tsk_recover.cpp
@@ -3,7 +3,7 @@
  ** The Sleuth Kit 
  **
  ** Brian Carrier [carrier <at> sleuthkit [dot] org]
- ** Copyright (c) 2010 Brian Carrier.  All Rights reserved
+ ** Copyright (c) 2010-2011 Brian Carrier.  All Rights reserved
  **
  ** This software is distributed under the Common Public License 1.0
  **
diff --git a/tools/fstools/blkcalc.cpp b/tools/fstools/blkcalc.cpp
index d530ffcbf..adbbf1ca3 100644
--- a/tools/fstools/blkcalc.cpp
+++ b/tools/fstools/blkcalc.cpp
@@ -1,6 +1,6 @@
 /*
 ** blkcalc
-** The Sleuth Kit 
+** The Sleuth Kit
 **
 ** Calculates the corresponding block number between 'blkls' and 'dd' images
 ** when given an 'blkls' block number, it determines the block number it
@@ -8,7 +8,7 @@
 ** value it would have in a 'blkls' image (if the block is unallocated)
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2003-2005 Brian Carrier. All Rights reserved
 **
 ** TASK
@@ -216,7 +216,7 @@ main(int argc, char **argv1)
 
     if ((fs = tsk_fs_open_img(img, imgaddr * img->sector_size, fstype)) == NULL) {
         tsk_error_print(stderr);
-        if (tsk_errno == TSK_ERR_FS_UNSUPTYPE)
+        if (tsk_error_get_errno() == TSK_ERR_FS_UNSUPTYPE)
             tsk_fs_type_print(stderr);
         img->close(img);
         exit(1);
diff --git a/tools/fstools/blkcat.cpp b/tools/fstools/blkcat.cpp
index 95a33cc75..ada48cd09 100644
--- a/tools/fstools/blkcat.cpp
+++ b/tools/fstools/blkcat.cpp
@@ -6,7 +6,7 @@
 ** of the block to stdout.
 ** 
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
 **
 ** TASK
@@ -280,7 +280,7 @@ main(int argc, char **argv1)
     /* open the file */
     if ((fs = tsk_fs_open_img(img, imgaddr * img->sector_size, fstype)) == NULL) {
         tsk_error_print(stderr);
-        if (tsk_errno == TSK_ERR_FS_UNSUPTYPE)
+        if (tsk_error_get_errno() == TSK_ERR_FS_UNSUPTYPE)
             tsk_fs_type_print(stderr);
         img->close(img);
         exit(1);
diff --git a/tools/fstools/blkls.cpp b/tools/fstools/blkls.cpp
index feed765a0..54105c934 100644
--- a/tools/fstools/blkls.cpp
+++ b/tools/fstools/blkls.cpp
@@ -2,7 +2,7 @@
 ** The Sleuth Kit
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved 
 **
 ** TASK
@@ -204,7 +204,7 @@ main(int argc, char **argv1)
 
         if ((fs = tsk_fs_open_img(img, imgaddr * img->sector_size, fstype)) == NULL) {
             tsk_error_print(stderr);
-            if (tsk_errno == TSK_ERR_FS_UNSUPTYPE)
+            if (tsk_error_get_errno() == TSK_ERR_FS_UNSUPTYPE)
                 tsk_fs_type_print(stderr);
             img->close(img);
             exit(1);
@@ -298,7 +298,7 @@ main(int argc, char **argv1)
 
         if ((fs = tsk_fs_open_img(img, imgaddr * img->sector_size, fstype)) == NULL) {
             tsk_error_print(stderr);
-            if (tsk_errno == TSK_ERR_FS_UNSUPTYPE)
+            if (tsk_error_get_errno() == TSK_ERR_FS_UNSUPTYPE)
                 tsk_fs_type_print(stderr);
             img->close(img);
             exit(1);
diff --git a/tools/fstools/blkstat.cpp b/tools/fstools/blkstat.cpp
index bb4a6f716..69dbc8f74 100644
--- a/tools/fstools/blkstat.cpp
+++ b/tools/fstools/blkstat.cpp
@@ -5,7 +5,7 @@
 ** Get the details about a data unit
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved 
 **
 ** TASK
@@ -159,7 +159,7 @@ main(int argc, char **argv1)
     }
     if ((fs = tsk_fs_open_img(img, imgaddr * img->sector_size, fstype)) == NULL) {
         tsk_error_print(stderr);
-        if (tsk_errno == TSK_ERR_FS_UNSUPTYPE)
+        if (tsk_error_get_errno() == TSK_ERR_FS_UNSUPTYPE)
             tsk_fs_type_print(stderr);
         img->close(img);
         exit(1);
diff --git a/tools/fstools/ffind.cpp b/tools/fstools/ffind.cpp
index 7df730db2..32fefb87d 100644
--- a/tools/fstools/ffind.cpp
+++ b/tools/fstools/ffind.cpp
@@ -5,7 +5,7 @@
 ** Find the file that uses the specified inode (including deleted files)
 ** 
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved 
 **
 ** TASK
@@ -186,7 +186,7 @@ main(int argc, char **argv1)
     }
     if ((fs = tsk_fs_open_img(img, imgaddr * img->sector_size, fstype)) == NULL) {
         tsk_error_print(stderr);
-        if (tsk_errno == TSK_ERR_FS_UNSUPTYPE)
+        if (tsk_error_get_errno() == TSK_ERR_FS_UNSUPTYPE)
             tsk_fs_type_print(stderr);
         img->close(img);
         exit(1);
diff --git a/tools/fstools/fls.cpp b/tools/fstools/fls.cpp
index 96c7bcb55..de9940fda 100644
--- a/tools/fstools/fls.cpp
+++ b/tools/fstools/fls.cpp
@@ -6,7 +6,7 @@
 ** directories that exist (both active and deleted)
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2003-2005 Brian Carier.  All rights reserved
 **
 ** TASK
@@ -262,7 +262,7 @@ main(int argc, char **argv1)
         }
         if ((fs = tsk_fs_open_img(img, imgaddr * img->sector_size, fstype)) == NULL) {
             tsk_error_print(stderr);
-            if (tsk_errno == TSK_ERR_FS_UNSUPTYPE)
+            if (tsk_error_get_errno() == TSK_ERR_FS_UNSUPTYPE)
                 tsk_fs_type_print(stderr);
 
             img->close(img);
@@ -293,7 +293,7 @@ main(int argc, char **argv1)
 
         if ((fs = tsk_fs_open_img(img, imgaddr * img->sector_size, fstype)) == NULL) {
             tsk_error_print(stderr);
-            if (tsk_errno == TSK_ERR_FS_UNSUPTYPE)
+            if (tsk_error_get_errno() == TSK_ERR_FS_UNSUPTYPE)
                 tsk_fs_type_print(stderr);
             img->close(img);
             exit(1);
diff --git a/tools/fstools/fscheck.cpp b/tools/fstools/fscheck.cpp
index b528fc997..0cee3c786 100644
--- a/tools/fstools/fscheck.cpp
+++ b/tools/fstools/fscheck.cpp
@@ -133,7 +133,7 @@ main(int argc, char **argv)
     }
 
     if (fs = fs_open(img, fstype)) {
-        if (tsk_errno == TSK_ERR_FS_UNSUPTYPE)
+        if (tsk_error_get_errno() == TSK_ERR_FS_UNSUPTYPE)
             tsk_print_types(stderr);
 
         tsk_error_print(stderr);
diff --git a/tools/fstools/fsstat.cpp b/tools/fstools/fsstat.cpp
index 93b85d3e0..4c35da7dc 100644
--- a/tools/fstools/fsstat.cpp
+++ b/tools/fstools/fsstat.cpp
@@ -3,7 +3,7 @@
 ** The Sleuth Kit 
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved 
 **
 ** TASK
@@ -155,7 +155,7 @@ main(int argc, char **argv1)
 
     if ((fs = tsk_fs_open_img(img, imgaddr * img->sector_size, fstype)) == NULL) {
         tsk_error_print(stderr);
-        if (tsk_errno == TSK_ERR_FS_UNSUPTYPE)
+        if (tsk_error_get_errno() == TSK_ERR_FS_UNSUPTYPE)
             tsk_fs_type_print(stderr);
         img->close(img);
         exit(1);
diff --git a/tools/fstools/icat.cpp b/tools/fstools/icat.cpp
index 50ad25414..3c74c7dc0 100644
--- a/tools/fstools/icat.cpp
+++ b/tools/fstools/icat.cpp
@@ -3,7 +3,7 @@
 ** The Sleuth Kit 
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved 
 **
 ** TASK
@@ -186,7 +186,7 @@ main(int argc, char **argv1)
     }
     if ((fs = tsk_fs_open_img(img, imgaddr * img->sector_size, fstype)) == NULL) {
         tsk_error_print(stderr);
-        if (tsk_errno == TSK_ERR_FS_UNSUPTYPE)
+        if (tsk_error_get_errno() == TSK_ERR_FS_UNSUPTYPE)
             tsk_fs_type_print(stderr);
         img->close(img);
         exit(1);
@@ -214,7 +214,7 @@ main(int argc, char **argv1)
         (TSK_FS_FILE_WALK_FLAG_ENUM) fw_flags);
     if (retval) {
         if ((suppress_recover_error == 1)
-            && (tsk_errno == TSK_ERR_FS_RECOVER)) {
+            && (tsk_error_get_errno() == TSK_ERR_FS_RECOVER)) {
             tsk_error_reset();
         }
         else {
diff --git a/tools/fstools/ifind.cpp b/tools/fstools/ifind.cpp
index a5efaf590..6101d7ada 100644
--- a/tools/fstools/ifind.cpp
+++ b/tools/fstools/ifind.cpp
@@ -5,7 +5,7 @@
 ** Given an image  and block number, identify which inode it is used by
 ** 
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
 **
 ** TASK
@@ -247,7 +247,7 @@ main(int argc, char **argv1)
 
     if ((fs = tsk_fs_open_img(img, imgaddr * img->sector_size, fstype)) == NULL) {
         tsk_error_print(stderr);
-        if (tsk_errno == TSK_ERR_FS_UNSUPTYPE)
+        if (tsk_error_get_errno() == TSK_ERR_FS_UNSUPTYPE)
             tsk_fs_type_print(stderr);
         img->close(img);
         if (path)
diff --git a/tools/fstools/ils.cpp b/tools/fstools/ils.cpp
index 6a6786e7a..9ebc0ce17 100644
--- a/tools/fstools/ils.cpp
+++ b/tools/fstools/ils.cpp
@@ -2,7 +2,7 @@
 ** The Sleuth Kit 
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved 
 **
 ** TASK
@@ -322,7 +322,7 @@ main(int argc, char **argv1)
 
     if ((fs = tsk_fs_open_img(img, imgaddr * img->sector_size, fstype)) == NULL) {
         tsk_error_print(stderr);
-        if (tsk_errno == TSK_ERR_FS_UNSUPTYPE)
+        if (tsk_error_get_errno() == TSK_ERR_FS_UNSUPTYPE)
             tsk_fs_type_print(stderr);
         img->close(img);
         exit(1);
diff --git a/tools/fstools/istat.cpp b/tools/fstools/istat.cpp
index 121796102..c013dede7 100644
--- a/tools/fstools/istat.cpp
+++ b/tools/fstools/istat.cpp
@@ -5,7 +5,7 @@
 ** Display all inode info about a given inode.
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
 **
 ** TASK
@@ -201,7 +201,7 @@ main(int argc, char **argv1)
 
     if ((fs = tsk_fs_open_img(img, imgaddr * img->sector_size, fstype)) == NULL) {
         tsk_error_print(stderr);
-        if (tsk_errno == TSK_ERR_FS_UNSUPTYPE)
+        if (tsk_error_get_errno() == TSK_ERR_FS_UNSUPTYPE)
             tsk_fs_type_print(stderr);
         img->close(img);
         exit(1);
diff --git a/tools/fstools/jcat.cpp b/tools/fstools/jcat.cpp
index 29fd21f53..f21ee3dc0 100644
--- a/tools/fstools/jcat.cpp
+++ b/tools/fstools/jcat.cpp
@@ -3,7 +3,7 @@
 ** The Sleuth Kit 
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2004-2005 Brian Carrier.  All rights reserved
 **
 **
@@ -160,7 +160,7 @@ main(int argc, char **argv1)
 
         if ((fs = tsk_fs_open_img(img, imgaddr * img->sector_size, fstype)) == NULL) {
             tsk_error_print(stderr);
-            if (tsk_errno == TSK_ERR_FS_UNSUPTYPE)
+            if (tsk_error_get_errno() == TSK_ERR_FS_UNSUPTYPE)
                 tsk_fs_type_print(stderr);
             img->close(img);
             exit(1);
@@ -177,7 +177,7 @@ main(int argc, char **argv1)
 
         if ((fs = tsk_fs_open_img(img, imgaddr * img->sector_size, fstype)) == NULL) {
             tsk_error_print(stderr);
-            if (tsk_errno == TSK_ERR_FS_UNSUPTYPE)
+            if (tsk_error_get_errno() == TSK_ERR_FS_UNSUPTYPE)
                 tsk_fs_type_print(stderr);
             img->close(img);
             exit(1);
diff --git a/tools/fstools/jls.cpp b/tools/fstools/jls.cpp
index 9075fb7b1..1a993d84a 100644
--- a/tools/fstools/jls.cpp
+++ b/tools/fstools/jls.cpp
@@ -3,7 +3,7 @@
 ** The Sleuth Kit 
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2004-2005 Brian Carrier.  All rights reserved
 **
 ** This software is distributed under the Common Public License 1.0
@@ -150,7 +150,7 @@ main(int argc, char **argv1)
 
         if ((fs = tsk_fs_open_img(img, imgaddr * img->sector_size, fstype)) == NULL) {
             tsk_error_print(stderr);
-            if (tsk_errno == TSK_ERR_FS_UNSUPTYPE)
+            if (tsk_error_get_errno() == TSK_ERR_FS_UNSUPTYPE)
                 tsk_fs_type_print(stderr);
             img->close(img);
             exit(1);
@@ -168,7 +168,7 @@ main(int argc, char **argv1)
 
         if ((fs = tsk_fs_open_img(img, imgaddr * img->sector_size, fstype)) == NULL) {
             tsk_error_print(stderr);
-            if (tsk_errno == TSK_ERR_FS_UNSUPTYPE)
+            if (tsk_error_get_errno() == TSK_ERR_FS_UNSUPTYPE)
                 tsk_fs_type_print(stderr);
             img->close(img);
             exit(1);
diff --git a/tools/hashtools/hfind.cpp b/tools/hashtools/hfind.cpp
index 3419a8dce..2e5f1727a 100644
--- a/tools/hashtools/hfind.cpp
+++ b/tools/hashtools/hfind.cpp
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2003-2008 Brian Carrier.  All rights reserved
+ * Copyright (c) 2003-2011 Brian Carrier.  All rights reserved
  *
  * This software is distributed under the Common Public License 1.0
  */
diff --git a/tools/imgtools/img_cat.cpp b/tools/imgtools/img_cat.cpp
index d37b999df..c533b1135 100644
--- a/tools/imgtools/img_cat.cpp
+++ b/tools/imgtools/img_cat.cpp
@@ -3,7 +3,7 @@
  * The Sleuth Kit 
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All rights reserved 
+ * Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All rights reserved 
  *
  * This software is distributed under the Common Public License 1.0
  */
diff --git a/tools/imgtools/img_stat.cpp b/tools/imgtools/img_stat.cpp
index 11c79725d..d9d32b884 100644
--- a/tools/imgtools/img_stat.cpp
+++ b/tools/imgtools/img_stat.cpp
@@ -3,7 +3,7 @@
  * The Sleuth Kit 
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2005-2008 Brian Carrier.  All rights reserved 
+ * Copyright (c) 2005-2011 Brian Carrier.  All rights reserved 
  *
  *
  * This software is distributed under the Common Public License 1.0
diff --git a/tools/sorter/sorter.base b/tools/sorter/sorter.base
index 32c071c55..4b98fc13f 100644
--- a/tools/sorter/sorter.base
+++ b/tools/sorter/sorter.base
@@ -2,7 +2,7 @@
 # The Sleuth Kit
 #
 # Brian Carrier [carrier <at> sleuthkit [dot] org]
-# Copyright (c) 2003-2009 Brian Carrier.  All rights reserved
+# Copyright (c) 2003-2011 Brian Carrier.  All rights reserved
 #
 # TASK
 # Copyright (c) 2002-2003 Brian Carrier, @stake Inc.  All rights reserved
diff --git a/tools/timeline/mactime.base b/tools/timeline/mactime.base
index 1cc3b6cc7..591299652 100644
--- a/tools/timeline/mactime.base
+++ b/tools/timeline/mactime.base
@@ -9,7 +9,7 @@
 #
 # The Sleuth Kit
 # Brian Carrier [carrier <at> sleuthkit [dot] org]
-# Copyright (c) 2003-2009 Brian Carrier.  All rights reserved
+# Copyright (c) 2003-2011 Brian Carrier.  All rights reserved
 #
 # TASK
 # Copyright (c) 2002 Brian Carrier, @stake Inc.  All rights reserved
diff --git a/tools/vstools/mmcat.cpp b/tools/vstools/mmcat.cpp
index a0cda1b12..8d3bf208d 100644
--- a/tools/vstools/mmcat.cpp
+++ b/tools/vstools/mmcat.cpp
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2008 Brian Carrier.  All rights reserved
+ * Copyright (c) 2008-2011 Brian Carrier.  All rights reserved
  *
  * Output the contents of a partition
  *
@@ -155,7 +155,7 @@ main(int argc, char **argv1)
     /* process the partition tables */
     if ((vs = tsk_vs_open(img, imgaddr * img->sector_size, vstype)) == NULL) {
         tsk_error_print(stderr);
-        if (tsk_errno == TSK_ERR_VS_UNSUPTYPE)
+        if (tsk_error_get_errno() == TSK_ERR_VS_UNSUPTYPE)
             tsk_vs_type_print(stderr);
 
         exit(1);
@@ -181,12 +181,13 @@ main(int argc, char **argv1)
     }
 
 #ifdef TSK_WIN32
+    char strerror_buffer[1024];
     if (-1 == _setmode(_fileno(stdout), _O_BINARY)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WRITE;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WRITE);
+        tsk_error_set_errstr(
             "mmcat: error setting stdout to binary: %s",
-            strerror(errno));
+            strerror_s(strerror_buffer, 1024, errno));
         return 1;
     }
 #endif
diff --git a/tools/vstools/mmls.cpp b/tools/vstools/mmls.cpp
index 6186b1e78..b95d30537 100644
--- a/tools/vstools/mmls.cpp
+++ b/tools/vstools/mmls.cpp
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2006-2010 Brian Carrier, Basis Technology.  All rights reserved
+ * Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All rights reserved
  * Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
  *
  * mmls - list media management structure contents
@@ -281,7 +281,7 @@ main(int argc, char **argv1)
     vs = tsk_vs_open(img, imgaddr * img->sector_size, vstype);
     if (vs == NULL) {
         tsk_error_print(stderr);
-        if (tsk_errno == TSK_ERR_VS_UNSUPTYPE)
+        if (tsk_error_get_errno() == TSK_ERR_VS_UNSUPTYPE)
             tsk_vs_type_print(stderr);
         tsk_img_close(img);
         exit(1);
diff --git a/tools/vstools/mmstat.cpp b/tools/vstools/mmstat.cpp
index 6ad94ec24..ec7130f1a 100644
--- a/tools/vstools/mmstat.cpp
+++ b/tools/vstools/mmstat.cpp
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2005 Brian Carrier.  All rights reserved
+ * Copyright (c) 2005-2011 Brian Carrier.  All rights reserved
  *
  * mmstat - Get stats on the volume system / media management
  *
@@ -148,7 +148,7 @@ main(int argc, char **argv1)
     /* process the partition tables */
     if ((vs = tsk_vs_open(img, imgaddr * img->sector_size, vstype)) == NULL) {
         tsk_error_print(stderr);
-        if (tsk_errno == TSK_ERR_VS_UNSUPTYPE)
+        if (tsk_error_get_errno() == TSK_ERR_VS_UNSUPTYPE)
             tsk_vs_type_print(stderr);
 
         exit(1);
diff --git a/tsk3/auto/auto.cpp b/tsk3/auto/auto.cpp
index 93a3fee01..608d3a0c7 100644
--- a/tsk3/auto/auto.cpp
+++ b/tsk3/auto/auto.cpp
@@ -1,8 +1,8 @@
 /*
- ** The Sleuth Kit 
+ ** The Sleuth Kit
  **
  ** Brian Carrier [carrier <at> sleuthkit [dot] org]
- ** Copyright (c) 2010 Brian Carrier.  All Rights reserved
+ ** Copyright (c) 2010-2011 Brian Carrier.  All Rights reserved
  **
  ** This software is distributed under the Common Public License 1.0
  **
@@ -10,7 +10,7 @@
 
 /**
  * \file auto.cpp
- * Contains C++ code that creates the base file extraction automation class. 
+ * Contains C++ code that creates the base file extraction automation class.
  */
 
 #include "tsk_auto_i.h"
@@ -36,7 +36,7 @@ TskAuto::~TskAuto()
 
 /**
  * Opens the disk image to be analyzed.  This must be called before any
- * of the findFilesInXXX() methods. 
+ * of the findFilesInXXX() methods.
  *
  * @param a_numImg The number of images to open (will be > 1 for split images).
  * @param a_images The path to the image files (the number of files must
@@ -60,7 +60,7 @@ uint8_t
 }
 
 
-/** 
+/**
  * Closes the handles to the open disk image. Should be called after
  * you have completed analysis of the image.
  */
@@ -76,7 +76,7 @@ void
 
 /**
  * Set the attributes for the volumes that should be processed.
- * The default settings are for Allocated Non-Meta volumes only. 
+ * The default settings are for Allocated Non-Meta volumes only.
  * This must be called before the findFilesInXX() method.
  * @param vs_flags Flags to use for filtering
  */
@@ -101,17 +101,17 @@ void
 
 
 /**
- * Starts in sector 0 of the opened disk images and looks for a 
+ * Starts in sector 0 of the opened disk images and looks for a
  * volume or file system. Will call processFile() on each file
- * that is found. 
+ * that is found.
  * @return 1 on error, 0 on success
  */
 uint8_t TskAuto::findFilesInImg()
 {
     if (!m_img_info) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_AUTO_NOTOPEN;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "findFilesInImg\n");
+        tsk_error_set_errno(TSK_ERR_AUTO_NOTOPEN);
+        tsk_error_set_errstr("findFilesInImg\n");
         return 1;
     }
     if (findFilesInVs(0)) {
@@ -124,7 +124,7 @@ uint8_t TskAuto::findFilesInImg()
 
 
 /** \internal
- * Volume system walk callback function that will analyze 
+ * Volume system walk callback function that will analyze
  * each volume to find a file system.
  */
 TSK_WALK_RET_ENUM
@@ -148,7 +148,7 @@ TSK_WALK_RET_ENUM
         return TSK_WALK_STOP;
     }
     else if (retval2 != TSK_OK) {
-        // if we return ERROR here, then the walk will stop.  But, the 
+        // if we return ERROR here, then the walk will stop.  But, the
         // error could just be because we looked into an unallocated volume.
         // do any special error handling / reporting here.
         tsk_error_reset();
@@ -159,10 +159,10 @@ TSK_WALK_RET_ENUM
 
 
 /**
- * Starts in a specified sector of the opened disk images and looks for a 
+ * Starts in a specified sector of the opened disk images and looks for a
  * volume system or file system. Will call processFile() on each file
- * that is found. 
- * @param a_start Byte offset to start analyzing from. 
+ * that is found.
+ * @param a_start Byte offset to start analyzing from.
  * @param a_vtype Volume system type to analyze
  * @return 1 on error, 0 on success
  */
@@ -170,14 +170,14 @@ uint8_t TskAuto::findFilesInVs(TSK_OFF_T a_start, TSK_VS_TYPE_ENUM a_vtype)
 {
     if (!m_img_info) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_AUTO_NOTOPEN;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "findFilesInVs\n");
+        tsk_error_set_errno(TSK_ERR_AUTO_NOTOPEN);
+        tsk_error_set_errstr("findFilesInVs\n");
         return 1;
     }
 
     TSK_VS_INFO *
         vs_info;
-    // USE mm_walk to get the volumes 
+    // USE mm_walk to get the volumes
     if ((vs_info =
             tsk_vs_open(m_img_info, a_start,
                 a_vtype)) == NULL) {
@@ -210,10 +210,10 @@ uint8_t TskAuto::findFilesInVs(TSK_OFF_T a_start, TSK_VS_TYPE_ENUM a_vtype)
 }
 
 /**
- * Starts in a specified sector of the opened disk images and looks for a 
+ * Starts in a specified sector of the opened disk images and looks for a
  * volume system or file system. Will call processFile() on each file
- * that is found. 
- * @param a_start Byte offset to start analyzing from. 
+ * that is found.
+ * @param a_start Byte offset to start analyzing from.
  * @return 1 on error, 0 on success
  */
 uint8_t TskAuto::findFilesInVs(TSK_OFF_T a_start)
@@ -222,8 +222,8 @@ uint8_t TskAuto::findFilesInVs(TSK_OFF_T a_start)
 }
 
 
-/** 
- * Starts in a specified sector of the opened disk images and looks for a 
+/**
+ * Starts in a specified sector of the opened disk images and looks for a
  * file system. Will call processFile() on each file
  * that is found.  Same as findFilesInFs, but gives more detailed return values.
  * @param a_start Byte offset to start analyzing from. 
@@ -234,8 +234,8 @@ TSK_RETVAL_ENUM TskAuto::findFilesInFsRet(TSK_OFF_T a_start, TSK_FS_TYPE_ENUM a_
 {
     if (!m_img_info) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_AUTO_NOTOPEN;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "findFilesInFsRet\n");        
+        tsk_error_set_errno(TSK_ERR_AUTO_NOTOPEN);
+        tsk_error_set_errstr( "findFilesInFsRet\n");
         return TSK_ERR;
     }
 
@@ -250,17 +250,17 @@ TSK_RETVAL_ENUM TskAuto::findFilesInFsRet(TSK_OFF_T a_start, TSK_FS_TYPE_ENUM a_
 
         return TSK_ERR;
     }
-    
+
     TSK_RETVAL_ENUM
         retval = findFilesInFsInt(fs_info, fs_info->root_inum);
     tsk_fs_close(fs_info);
     return retval;
 }
 
-/** 
- * Starts in a specified sector of the opened disk images and looks for a 
+/**
+ * Starts in a specified sector of the opened disk images and looks for a
  * file system. Will call processFile() on each file
- * that is found. 
+ * that is found.
  *
  * @param a_start Byte offset of file system starting location.
  *
@@ -275,10 +275,10 @@ uint8_t TskAuto::findFilesInFs(TSK_OFF_T a_start)
 }
 
 
-/** 
- * Starts in a specified sector of the opened disk images and looks for a 
+/**
+ * Starts in a specified sector of the opened disk images and looks for a
  * file system. Will call processFile() on each file
- * that is found. 
+ * that is found.
  *
  * @param a_start Byte offset of file system starting location.
  * @param a_ftype Type of file system that is located at the offset.
@@ -293,11 +293,11 @@ uint8_t TskAuto::findFilesInFs(TSK_OFF_T a_start, TSK_FS_TYPE_ENUM a_ftype)
         return 0;
 }
 
-/** 
- * Starts in a specified sector of the opened disk images and looks for a 
- * file system. Will start processing the file system at a specified 
+/**
+ * Starts in a specified sector of the opened disk images and looks for a
+ * file system. Will start processing the file system at a specified
  * file system. Will call processFile() on each file
- * that is found in that directory. 
+ * that is found in that directory.
  *
  * @param a_start Byte offset of file system starting location.
  * @param a_ftype Type of file system that will be analyzed.
@@ -309,8 +309,8 @@ uint8_t TskAuto::findFilesInFs(TSK_OFF_T a_start, TSK_FS_TYPE_ENUM a_ftype, TSK_
 {
     if (!m_img_info) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_AUTO_NOTOPEN;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "findFilesInFs\n");        
+        tsk_error_set_errno(TSK_ERR_AUTO_NOTOPEN);
+        tsk_error_set_errstr( "findFilesInFs\n");
         return 1;
     }
 
@@ -336,11 +336,11 @@ uint8_t TskAuto::findFilesInFs(TSK_OFF_T a_start, TSK_FS_TYPE_ENUM a_ftype, TSK_
 }
 
 
-/** 
- * Starts in a specified sector of the opened disk images and looks for a 
- * file system. Will start processing the file system at a specified 
+/**
+ * Starts in a specified sector of the opened disk images and looks for a
+ * file system. Will start processing the file system at a specified
  * file system. Will call processFile() on each file
- * that is found in that directory. 
+ * that is found in that directory.
  *
  * @param a_start Byte offset of file system starting location.
  * @param a_inum inum to start walking files system at.
@@ -354,7 +354,7 @@ uint8_t TskAuto::findFilesInFs(TSK_OFF_T a_start, TSK_INUM_T a_inum)
 
 
 /** \internal
- * file name walk callback.  Walk the contents of each file 
+ * file name walk callback.  Walk the contents of each file
  * that is found.
  */
 TSK_WALK_RET_ENUM
@@ -377,7 +377,7 @@ TSK_WALK_RET_ENUM
 
 
 /* Internal method that the other findFilesInFs can call after they
- * have opened FS_INFO. 
+ * have opened FS_INFO.
  */
 TSK_RETVAL_ENUM
     TskAuto::findFilesInFsInt(TSK_FS_INFO * a_fs_info, TSK_INUM_T a_inum)
@@ -402,13 +402,13 @@ TSK_RETVAL_ENUM
 }
 
 
-/** 
- * Method that can be used from within processFile() to look at each 
+/**
+ * Method that can be used from within processFile() to look at each
  * attribute that a file may have.  This will call the processAttribute()
  * method (which you must implement) on each of the attributes in the file.
  * @param fs_file file  details
  * @param path full path of parent directory
- * @returns 1 if the file system processing should stop and not process more files. 
+ * @returns 1 if the file system processing should stop and not process more files.
  */
 TSK_RETVAL_ENUM
 TskAuto::processAttributes(TSK_FS_FILE * fs_file, const char *path)
@@ -426,9 +426,9 @@ TskAuto::processAttributes(TSK_FS_FILE * fs_file, const char *path)
 
 
 /**
- * Utility method to help determine if a file is an NTFS file system file (such as $MFT). 
+ * Utility method to help determine if a file is an NTFS file system file (such as $MFT).
  *
- * @returns 1 if the file is an NTFS System file, 0 if not. 
+ * @returns 1 if the file is an NTFS System file, 0 if not.
  */
 uint8_t
     TskAuto::isNtfsSystemFiles(TSK_FS_FILE * a_fs_file, const char *a_path)
@@ -443,9 +443,9 @@ uint8_t
 }
 
 /**
- * Utility method to help determine if a file is a FAT file system file (such as $MBR). 
+ * Utility method to help determine if a file is a FAT file system file (such as $MBR).
  *
- * @returns 1 if the file is an FAT System file, 0 if not. 
+ * @returns 1 if the file is an FAT System file, 0 if not.
  */
 uint8_t TskAuto::isFATSystemFiles(TSK_FS_FILE * a_fs_file)
 {
@@ -465,7 +465,7 @@ uint8_t TskAuto::isFATSystemFiles(TSK_FS_FILE * a_fs_file)
 /**
  * Utility method to help determine if a file is a . or .. directory.
  *
- * @returns 1 if the file is a dot directory, 0 if not. 
+ * @returns 1 if the file is a dot directory, 0 if not.
  */
 uint8_t TskAuto::isDotDir(TSK_FS_FILE * a_fs_file, const char *a_path)
 {
@@ -487,7 +487,7 @@ uint8_t TskAuto::isDotDir(TSK_FS_FILE * a_fs_file, const char *a_path)
 /**
  * Utility method to help determine if a file is a directory.
  *
- * @returns 1 if the file is a directory, 0 if not. 
+ * @returns 1 if the file is a directory, 0 if not.
  */
 uint8_t TskAuto::isDir(TSK_FS_FILE * a_fs_file)
 {
@@ -501,7 +501,7 @@ uint8_t TskAuto::isDir(TSK_FS_FILE * a_fs_file)
 /**
  * Utility method to help determine if a file is a file (and not a directory).
  *
- * @returns 1 if the file is a file, 0 if not. 
+ * @returns 1 if the file is a file, 0 if not.
  */
 uint8_t TskAuto::isFile(TSK_FS_FILE * a_fs_file)
 {
@@ -515,7 +515,7 @@ uint8_t TskAuto::isFile(TSK_FS_FILE * a_fs_file)
 /**
  * Utility method to help determine if an attribute is the default type for the file/dir.
  *
- * @returns 1 if the attribute is a default type, 0 if not. 
+ * @returns 1 if the attribute is a default type, 0 if not.
  */
 uint8_t
     TskAuto::isDefaultType(TSK_FS_FILE * a_fs_file,
@@ -532,7 +532,7 @@ uint8_t
 /**
  * Utility method to help determine if an attribute is non-resident (meaning it uses blocks to store data)
  *
- * @returns 1 if the attribute is non-resident, 0 if not. 
+ * @returns 1 if the attribute is non-resident, 0 if not.
  */
 uint8_t TskAuto::isNonResident(const TSK_FS_ATTR * a_fs_attr)
 {
diff --git a/tsk3/auto/auto_db.cpp b/tsk3/auto/auto_db.cpp
index f77b8a162..7929f5966 100644
--- a/tsk3/auto/auto_db.cpp
+++ b/tsk3/auto/auto_db.cpp
@@ -1,8 +1,8 @@
 /*
- ** The Sleuth Kit 
+ ** The Sleuth Kit
  **
  ** Brian Carrier [carrier <at> sleuthkit [dot] org]
- ** Copyright (c) 2010 Brian Carrier.  All Rights reserved
+ ** Copyright (c) 2010-2011 Brian Carrier.  All Rights reserved
  **
  ** This software is distributed under the Common Public License 1.0
  **
@@ -10,7 +10,7 @@
 
 /**
  * \file auto_db.cpp
- * Contains code to populate SQLite database with volume and file system information. 
+ * Contains code to populate SQLite database with volume and file system information.
  */
 
 #include "tsk_auto_i.h"
@@ -154,16 +154,16 @@ uint8_t
     struct STAT_STR stat_buf;
     if (TSTAT(dbFile, &stat_buf) == 0) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_AUTO_DB;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, 
+        tsk_error_set_errno(TSK_ERR_AUTO_DB);
+        tsk_error_set_errstr( 
                 "Database %s already exists.  Must be deleted first.", dbFile);
         return 1;
     }
     
     if (sqlite3_open(dbFile, &m_db)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_AUTO_DB;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, 
+        tsk_error_set_errno(TSK_ERR_AUTO_DB);
+        tsk_error_set_errstr( 
             "Can't open database: %s\n", sqlite3_errmsg(m_db));
         sqlite3_close(m_db);
         return 1;
@@ -172,14 +172,13 @@ uint8_t
 #endif
 
 
-
     char *errmsg;
     // disable synchronous for loading the DB since we have no crash recovery anyway...
     if (sqlite3_exec(m_db,
                      "PRAGMA synchronous =  OFF;", NULL, NULL, &errmsg) != SQLITE_OK) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_AUTO_DB;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_AUTO_DB);
+        tsk_error_set_errstr(
                  "Error setting PRAGMA synchronous: %s\n", errmsg);
         sqlite3_free(errmsg);
         return 1;
@@ -188,19 +187,19 @@ uint8_t
     if (sqlite3_exec(m_db,
                      "PRAGMA count_changes = false;", NULL, NULL, &errmsg) != SQLITE_OK) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_AUTO_DB;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_AUTO_DB);
+        tsk_error_set_errstr(
                  "Error setting PRAGMA count changes: %s\n", errmsg);
         sqlite3_free(errmsg);
         return 1;
     }
-    
+
     if (sqlite3_exec(m_db,
             "CREATE TABLE tsk_db_info (schema_ver INTEGER, tsk_ver INTEGER);",
             NULL, NULL, &errmsg) != SQLITE_OK) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_AUTO_DB;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_AUTO_DB);
+        tsk_error_set_errstr(
             "Error creating tsk_db_info table: %s\n", errmsg);
         sqlite3_free(errmsg);
         return 1;
@@ -211,8 +210,8 @@ uint8_t
         TSK_SCHEMA_VER, TSK_VERSION_NUM);
     if (sqlite3_exec(m_db, foo, NULL, NULL, &errmsg) != SQLITE_OK) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_AUTO_DB;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_AUTO_DB);
+        tsk_error_set_errstr(
             "Error adding data to tsk_db_info table: %s\n", errmsg);
         sqlite3_free(errmsg);
         return 1;
@@ -222,8 +221,8 @@ uint8_t
             "CREATE TABLE tsk_image_info (type INTEGER, ssize INTEGER);",
             NULL, NULL, &errmsg) != SQLITE_OK) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_AUTO_DB;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_AUTO_DB);
+        tsk_error_set_errstr(
             "Error creating tsk_image_info table: %s\n", errmsg);
         sqlite3_free(errmsg);
         return 1;
@@ -234,8 +233,8 @@ uint8_t
         (int) a_type, m_img_info->sector_size);
     if (sqlite3_exec(m_db, foo, NULL, NULL, &errmsg) != SQLITE_OK) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_AUTO_DB;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_AUTO_DB);
+        tsk_error_set_errstr(
             "Error adding data to tsk_image_info table: %s\n", errmsg);
         sqlite3_free(errmsg);
         return 1;
@@ -245,8 +244,8 @@ uint8_t
     if (sqlite3_exec(m_db, "CREATE TABLE tsk_image_names (name TEXT);",
             NULL, NULL, &errmsg) != SQLITE_OK) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_AUTO_DB;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_AUTO_DB);
+        tsk_error_set_errstr(
             "Error creating tsk_image_names table: %s\n", errmsg);
         sqlite3_free(errmsg);
         return 1;
@@ -269,8 +268,8 @@ uint8_t
             (UTF8 *) ((uintptr_t) ptr8 + 1024), TSKlenientConversion);
         if (retval != TSKconversionOK) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_AUTO_UNICODE;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_AUTO_UNICODE);
+            tsk_error_set_errstr(
                 "Error converting image to UTF-8\n");
             return 1;
         }
@@ -291,8 +290,8 @@ uint8_t
             &img_ptr[a]);
         if (sqlite3_exec(m_db, foo, NULL, NULL, &errmsg) != SQLITE_OK) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_AUTO_DB;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_AUTO_DB);
+            tsk_error_set_errstr(
                 "Error adding data to tsk_image_names table: %s\n",
                 errmsg);
             sqlite3_free(errmsg);
@@ -304,8 +303,8 @@ uint8_t
             "CREATE TABLE tsk_vs_info (vs_type INTEGER, img_offset INTEGER NOT NULL, block_size INTEGER NOT NULL);",
             NULL, NULL, &errmsg) != SQLITE_OK) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_AUTO_DB;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_AUTO_DB);
+        tsk_error_set_errstr(
             "Error creating tsk_vs_info table: %s\n", errmsg);
         sqlite3_free(errmsg);
         return 1;
@@ -315,9 +314,9 @@ uint8_t
             "CREATE TABLE tsk_vs_parts (vol_id INTEGER PRIMARY KEY, start INTEGER NOT NULL, length INTEGER NOT NULL, desc TEXT, flags INTEGER);",
             NULL, NULL, &errmsg) != SQLITE_OK) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_AUTO_DB;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
-            "Error creating tsk_vs_parts table: %s\n", errmsg);
+        tsk_error_set_errno(TSK_ERR_AUTO_DB);
+        tsk_error_set_errstr(
+            "Error creating tsk_vol_info table: %s\n", errmsg);
         sqlite3_free(errmsg);
         return 1;
     }
@@ -326,8 +325,8 @@ uint8_t
             "CREATE TABLE tsk_fs_info (fs_id INTEGER PRIMARY KEY, img_offset INTEGER, vol_id INTEGER NOT NULL, fs_type INTEGER, block_size INTEGER, block_count INTEGER, root_inum INTEGER, first_inum INTEGER, last_inum INTEGER);",
             NULL, NULL, &errmsg) != SQLITE_OK) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_AUTO_DB;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_AUTO_DB);
+        tsk_error_set_errstr(
             "Error creating tsk_fs_info table: %s\n", errmsg);
         sqlite3_free(errmsg);
         return 1;
@@ -337,8 +336,8 @@ uint8_t
             "CREATE TABLE tsk_fs_files (fs_id INTEGER NOT NULL, file_id INTEGER NOT NULL, attr_type INTEGER, attr_id INTEGER, name TEXT NOT NULL, par_file_id INTEGER, dir_type INTEGER, meta_type INTEGER, dir_flags INTEGER, meta_flags INTEGER, size INTEGER, ctime INTEGER, crtime INTEGER, atime INTEGER, mtime INTEGER, mode INTEGER, uid INTEGER, gid INTEGER);",
             NULL, NULL, &errmsg) != SQLITE_OK) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_AUTO_DB;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_AUTO_DB);
+        tsk_error_set_errstr(
             "Error creating tsk_fs_files table: %s\n", errmsg);
         sqlite3_free(errmsg);
         return 1;
@@ -349,8 +348,8 @@ uint8_t
                 "CREATE TABLE tsk_fs_blocks (fs_id INTEGER NOT NULL, blk_start INTEGER NOT NULL, blk_len INTEGER NOT NULL, file_id INTEGER NOT NULL, attr_type INTEGER, attr_id INTEGER);",
                 NULL, NULL, &errmsg) != SQLITE_OK) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_AUTO_DB;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_AUTO_DB);
+            tsk_error_set_errstr(
                 "Error creating tsk_fs_blocks table: %s\n", errmsg);
             sqlite3_free(errmsg);
             return 1;
@@ -379,8 +378,8 @@ TskAutoDb::createParentDirIndex()
             "CREATE INDEX parentDir ON tsk_fs_files(par_file_id, fs_id);",
             NULL, NULL, &errmsg) != SQLITE_OK) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_AUTO_DB;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_AUTO_DB);
+        tsk_error_set_errstr(
             "Error creating tsk_fs_files index on par_file_id: %s\n",
             errmsg);
         sqlite3_free(errmsg);
@@ -398,8 +397,8 @@ uint8_t TskAutoDb::addFilesInImgToDB()
 {
     if (m_db == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_AUTO_DB;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_AUTO_DB);
+        tsk_error_set_errstr(
             "addFilesInImgToDB: m_db not open\n");
         return 1;
     }
@@ -429,8 +428,8 @@ TSK_FILTER_ENUM TskAutoDb::filterVs(const TSK_VS_INFO * vs_info) {
 
     if (sqlite3_exec(m_db, statement, NULL, NULL, &errmsg) != SQLITE_OK) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_AUTO_DB;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_AUTO_DB);
+        tsk_error_set_errstr(
             "Error adding data to tsk_vs_info table: %s\n", errmsg);
         sqlite3_free(errmsg);
         return TSK_FILTER_STOP;
@@ -454,9 +453,9 @@ TskAutoDb::filterVol(const TSK_VS_PART_INFO * vs_part)
 
     if (sqlite3_exec(m_db, foo, NULL, NULL, &errmsg) != SQLITE_OK) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_AUTO_DB;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
-            "Error adding data to tsk_vs_parts table: %s\n", errmsg);
+        tsk_error_set_errno(TSK_ERR_AUTO_DB);
+        tsk_error_set_errstr(
+            "Error adding data to tsk_vol_info table: %s\n", errmsg);
         sqlite3_free(errmsg);
         return TSK_FILTER_STOP;
     }
@@ -489,8 +488,8 @@ TskAutoDb::filterFs(TSK_FS_INFO * fs_info)
 
         if (sqlite3_exec(m_db, foo, NULL, NULL, &errmsg) != SQLITE_OK) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_AUTO_DB;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_AUTO_DB);
+            tsk_error_set_errstr(
                 "Error adding data to tsk_vs_info table: %s\n", errmsg);
             sqlite3_free(errmsg);
             return TSK_FILTER_STOP;
@@ -507,8 +506,8 @@ TskAutoDb::filterFs(TSK_FS_INFO * fs_info)
         m_curVsId = 0;
         if (sqlite3_exec(m_db, foo, NULL, NULL, &errmsg) != SQLITE_OK) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_AUTO_DB;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_AUTO_DB);
+            tsk_error_set_errstr(
                 "Error adding data to tsk_vs_parts table: %s\n", errmsg);
             sqlite3_free(errmsg);
             return TSK_FILTER_STOP;
@@ -525,8 +524,8 @@ TskAutoDb::filterFs(TSK_FS_INFO * fs_info)
 
     if (sqlite3_exec(m_db, foo, NULL, NULL, &errmsg) != SQLITE_OK) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_AUTO_DB;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_AUTO_DB);
+        tsk_error_set_errstr(
             "Error adding data to tsk_fs_info table: %s\n", errmsg);
         sqlite3_free(errmsg);
         return TSK_FILTER_STOP;
@@ -536,7 +535,7 @@ TskAutoDb::filterFs(TSK_FS_INFO * fs_info)
     if ((file_root = tsk_fs_file_open(fs_info, NULL, "/")) != NULL) {
         processAttributes(file_root, "");
     }
-    
+
     // make sure that flags are set to get all files -- we need this to
     // find parent directory
     setFileFilterFlags((TSK_FS_DIR_WALK_FLAG_ENUM)
@@ -545,7 +544,7 @@ TskAutoDb::filterFs(TSK_FS_INFO * fs_info)
     return TSK_FILTER_CONT;
 }
 
-/* Insert the file data into the file table.  
+/* Insert the file data into the file table.
  * Returns 1 on error.
  */
 TSK_RETVAL_ENUM
@@ -651,8 +650,8 @@ TSK_RETVAL_ENUM
 
     if (sqlite3_exec(m_db, foo, NULL, NULL, &errmsg) != SQLITE_OK) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_AUTO_DB;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_AUTO_DB);
+        tsk_error_set_errstr(
             "Error adding data to tsk_fs_files table: %s\n", errmsg);
         sqlite3_free(errmsg);
         free(name);
@@ -673,8 +672,8 @@ TSK_RETVAL_ENUM
     char *errmsg;
     if (sqlite3_exec(m_db, "BEGIN", NULL, NULL, &errmsg) != SQLITE_OK) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_AUTO_DB;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_AUTO_DB);
+        tsk_error_set_errstr(
             "Error using BEGIN for insert transaction: %s\n", errmsg);
         sqlite3_free(errmsg);
         return TSK_ERR;
@@ -689,8 +688,8 @@ TSK_RETVAL_ENUM
 
     if (sqlite3_exec(m_db, "COMMIT", NULL, NULL, &errmsg) != SQLITE_OK) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_AUTO_DB;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_AUTO_DB);
+        tsk_error_set_errstr(
             "Error using COMMIT for insert transaction: %s\n", errmsg);
         sqlite3_free(errmsg);
         return TSK_ERR;
@@ -715,22 +714,22 @@ TSK_RETVAL_ENUM
         TSK_FS_ATTR_RUN *run;
         for (run = fs_attr->nrd.run; run != NULL; run = run->next) {
             char foo[1024];
-            char *errmsg;            
-            
+            char *errmsg;
+
             // ignore sparse blocks
             if (run->flags & TSK_FS_ATTR_RUN_FLAG_SPARSE)
                 continue;
-            
+
             snprintf(foo, 1024,
                      "INSERT INTO tsk_fs_blocks (fs_id, blk_start, blk_len, file_id, attr_type, attr_id) VALUES (%d,%"
                      PRIuDADDR ",%"PRIuDADDR ",%" PRIuINUM ",%d,%d)", m_curFsId, run->addr, run->len,
                      fs_file->meta->addr, fs_attr->type, fs_attr->id);
-            
+
             if (sqlite3_exec(m_db, foo, NULL, NULL,
                              &errmsg) != SQLITE_OK) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_AUTO_DB;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_AUTO_DB);
+                tsk_error_set_errstr(
                          "Error adding data to tsk_fs_info table: %s\n", errmsg);
                 sqlite3_free(errmsg);
                 return TSK_ERR;
diff --git a/tsk3/auto/tsk_auto.h b/tsk3/auto/tsk_auto.h
index a894d1cee..7c5546411 100644
--- a/tsk3/auto/tsk_auto.h
+++ b/tsk3/auto/tsk_auto.h
@@ -2,7 +2,7 @@
  ** The Sleuth Kit 
  **
  ** Brian Carrier [carrier <at> sleuthkit [dot] org]
- ** Copyright (c) 2010 Brian Carrier.  All Rights reserved
+ ** Copyright (c) 2010-2011 Brian Carrier.  All Rights reserved
  **
  ** This software is distributed under the Common Public License 1.0
  **
diff --git a/tsk3/auto/tsk_auto_i.h b/tsk3/auto/tsk_auto_i.h
index 6367cf4fd..b87a958af 100644
--- a/tsk3/auto/tsk_auto_i.h
+++ b/tsk3/auto/tsk_auto_i.h
@@ -2,7 +2,7 @@
  ** The Sleuth Kit 
  **
  ** Brian Carrier [carrier <at> sleuthkit [dot] org]
- ** Copyright (c) 2010 Brian Carrier.  All Rights reserved
+ ** Copyright (c) 2010-2011 Brian Carrier.  All Rights reserved
  **
  ** This software is distributed under the Common Public License 1.0
  **
diff --git a/tsk3/base/Makefile.am b/tsk3/base/Makefile.am
index 9696c0c2e..1076f19b1 100644
--- a/tsk3/base/Makefile.am
+++ b/tsk3/base/Makefile.am
@@ -3,7 +3,8 @@ AM_CFLAGS = -I../.. -Wall
 noinst_LTLIBRARIES = libtskbase.la
 libtskbase_la_SOURCES = md5c.c mymalloc.c sha1c.c \
     tsk_endian.c tsk_error.c tsk_list.c tsk_parse.c tsk_printf.c \
-    tsk_unicode.c tsk_version.c tsk_stack.c XGetopt.c tsk_base_i.h
+    tsk_unicode.c tsk_version.c tsk_stack.c XGetopt.c tsk_base_i.h \
+    tsk_lock.c
 
 EXTRA_DIST = .indent.pro
 
diff --git a/tsk3/base/mymalloc.c b/tsk3/base/mymalloc.c
index 766644441..94107b0a5 100644
--- a/tsk3/base/mymalloc.c
+++ b/tsk3/base/mymalloc.c
@@ -1,9 +1,9 @@
 /*
- * The Sleuth Kit 
+ * The Sleuth Kit
  *
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All rights reserved.
+ * Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All rights reserved.
  */
 
 /** \file mymalloc.c
@@ -69,9 +69,8 @@ tsk_malloc(size_t len)
 
     if ((ptr = malloc(len)) == 0) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_AUX_MALLOC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "tsk_malloc: %s",
-            strerror(errno));
+        tsk_error_set_errno(TSK_ERR_AUX_MALLOC);
+        tsk_error_set_errstr("tsk_malloc: %s", strerror(errno));
     }
     else {
         memset(ptr, 0, len);
@@ -85,9 +84,8 @@ tsk_realloc(void *ptr, size_t len)
 {
     if ((ptr = realloc(ptr, len)) == 0) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_AUX_MALLOC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "tsk_realloc: %s",
-            strerror(errno));
+        tsk_error_set_errno(TSK_ERR_AUX_MALLOC);
+        tsk_error_set_errstr("tsk_realloc: %s", strerror(errno));
     }
     return (ptr);
 }
diff --git a/tsk3/base/tsk_base.h b/tsk3/base/tsk_base.h
index ead992d83..823f18dba 100644
--- a/tsk3/base/tsk_base.h
+++ b/tsk3/base/tsk_base.h
@@ -2,21 +2,22 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2007-2010 Brian Carrier.  All Rights reserved
+ * Copyright (c) 2007-2011 Brian Carrier.  All Rights reserved
  *
  * This software is distributed under the Common Public License 1.0
  */
 
 /** \file tsk_base.h
  * Contains the type and function definitions that are needed
- * by external programs to use the TSK library.  
- * Note that this file is not meant to be directly included.   
+ * by external programs to use the TSK library.
+ * Note that this file is not meant to be directly included.
  * It is included by both libtsk.h and tsk_base_i.h.
  */
 
 
 /**
- * \defgroup baselib Base TSK Library Functions
+ * \defgroup baselib C Base TSK Library Functions
+ * \defgroup baselib_cpp C++ Base TSK Library Classes
  */
 
 #ifndef _TSK_BASE_H
@@ -25,22 +26,22 @@
 // standard C header files
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdarg.h>
 
-
-/** Version of code in number form.  
+/** Version of code in number form.
  * Upper byte is A, next is B, and next byte is C in version A.B.C.
- * Lowest byte is 0xff, except in beta releases, in which case it 
+ * Lowest byte is 0xff, except in beta releases, in which case it
  * increments from 1.  Nightly snapshots will have upper byte as
- * 0xff and next bytes with year, month, and date, respectively. 
+ * 0xff and next bytes with year, month, and date, respectively.
  * Note that you will not be able to differentiate between snapshots
  * from the trunk or branches with this method...
- * For example, 3.1.2 would be stored as 0x030102FF.  
+ * For example, 3.1.2 would be stored as 0x030102FF.
  * 3.1.2b1 would be 0x03010201.  Snapshot from Jan 2, 2003 would be
- * 0xFF030102. 
- * See TSK_VERSION_STR for string form. */ 
-#define TSK_VERSION_NUM 0x00000000  
+ * 0xFF030102.
+ * See TSK_VERSION_STR for string form. */
+#define TSK_VERSION_NUM 0x00000000
 
-/** Version of code in string form. See TSK_VERSION_NUM for 
+/** Version of code in string form. See TSK_VERSION_NUM for
  * integer form. */
 #define TSK_VERSION_STR "0.0.0"
 
@@ -59,7 +60,56 @@
 extern "C" {
 #endif
 
+#define TSK_ERROR_STRING_MAX_LENGTH 1024
+
+    typedef struct {
+    	uint32_t t_errno;
+    	char errstr[TSK_ERROR_STRING_MAX_LENGTH+1];
+    	char errstr2[TSK_ERROR_STRING_MAX_LENGTH+1];
+    	char errstr_print[TSK_ERROR_STRING_MAX_LENGTH+1];
+    } TSK_ERROR_INFO;
+
+    /* The core function here is to retrieve the per-thread error structure. Other functions to follow
+     * are for convenience of performing common operations. */
+    extern TSK_ERROR_INFO* tsk_error_get_info();
+
+    extern uint32_t tsk_error_get_errno();
+    extern void tsk_error_set_errno(uint32_t t_errno);
+
+#ifdef __GNUC__
+#define TSK_ERROR_FORMAT_ATTRIBUTE(n,m) __attribute__((format (printf, n, m)))
+#else
+#define TSK_ERROR_FORMAT_ATTRIBUTE(n,m)
+#endif
+
+    extern char *tsk_error_get_errstr();
+    extern void tsk_error_set_errstr(char const * format, ...) TSK_ERROR_FORMAT_ATTRIBUTE(1, 2);
+    extern void tsk_error_vset_errstr(char const * format, va_list args);
+    extern char *tsk_error_get_errstr2();
+    extern void tsk_error_set_errstr2(char const * format, ...) TSK_ERROR_FORMAT_ATTRIBUTE(1, 2);
+    extern void tsk_error_vset_errstr2(char const * format, va_list args);
+    extern void tsk_error_errstr2_concat(char const * format, ...) TSK_ERROR_FORMAT_ATTRIBUTE(1, 2);
 
+    /** Return a human-readable form of tsk_error_get_errno **/
+    extern const char *tsk_error_get();
+
+    extern void tsk_error_print(FILE *);
+    extern void tsk_error_reset();
+
+
+#ifdef TSK_MULTITHREAD_LIB
+#ifdef TSK_WIN32
+    void *tsk_error_win32_get_per_thread_(unsigned struct_size);
+    typedef struct { CRITICAL_SECTION critical_section; } tsk_lock_t;
+#else
+#include <pthread.h>
+    typedef struct { pthread_mutex_t mutex; } tsk_lock_t;
+#endif
+
+    // single threaded lib
+#else
+    typedef struct { void *dummy; } tsk_lock_t;
+#endif
 
 /**
  * Return values for some TSK functions that need to differentiate between errors and corrupt data.
@@ -68,20 +118,20 @@ extern "C" {
         TSK_OK,                 ///< Ok -- success
         TSK_ERR,                ///< System error -- should abort
         TSK_COR,                 ///< Data is corrupt, can still process another set of data
-        TSK_STOP                ///< Stop further processing, not an error though. 
+        TSK_STOP                ///< Stop further processing, not an error though.
     } TSK_RETVAL_ENUM;
 
 
     typedef struct TSK_LIST TSK_LIST;
-    /** 
-    * Linked list structure that holds a 'key' and optional 'length'. 
+    /**
+    * Linked list structure that holds a 'key' and optional 'length'.
     * Note that the data is stored in reverse sort order so that inserts
     * are faster.  Also note that the length is a negative number. A key of
-    * '6' and a len of '2' means that the run contains 6 and 5. 
+    * '6' and a len of '2' means that the run contains 6 and 5.
     */
     struct TSK_LIST {
         TSK_LIST *next;         ///< Pointer to next entry in list
-        uint64_t key;           ///< Largest value in this run 
+        uint64_t key;           ///< Largest value in this run
         uint64_t len;           ///< Length of run (negative number, stored as positive)
     };
     extern uint8_t tsk_list_find(TSK_LIST * list, uint64_t key);
@@ -90,7 +140,7 @@ extern "C" {
 
 
     // note that the stack code is in this file and not internal for convenience to users
-    /** 
+    /**
      * Basic stack structure to push and pop (used for finding loops in recursion).
      */
     typedef struct {
@@ -200,7 +250,7 @@ extern "C" {
 #define PRIxOFF		PRIx64
 #define PRIdOFF		PRId64
 
-    typedef uint32_t TSK_PNUM_T;        ///< Data type used to internally store partition addresses 
+    typedef uint32_t TSK_PNUM_T;        ///< Data type used to internally store partition addresses
 #define PRIuPNUM	PRIu32
 #define PRIxPNUM	PRIx32
 #define PRIdPNUM	PRId32
@@ -214,7 +264,7 @@ extern "C" {
 /*********** RETURN VALUES ************/
 
 /**
- * Values that callback functions can return to calling walk function. 
+ * Values that callback functions can return to calling walk function.
  */
     typedef enum {
         TSK_WALK_CONT = 0x0,    ///< Walk function should continue to next object
@@ -224,15 +274,13 @@ extern "C" {
 
 
 /************ ERROR HANDLING *************/
+    //TODO: make this per-thread?
     extern int tsk_verbose;     ///< Set to 1 to have verbose debug messages printed to stderr
 
     /** \name Error Handling */
 //@{
 
-    extern uint32_t tsk_errno;
-    extern const char *tsk_error_get();
-    extern void tsk_error_print(FILE *);
-    extern void tsk_error_reset();
+
 
 #define TSK_ERR_AUX	0x01000000
 #define TSK_ERR_IMG	0x02000000
@@ -277,7 +325,7 @@ extern "C" {
 #define TSK_ERR_FS_WALK_RNG	(TSK_ERR_FS | 3)
 #define TSK_ERR_FS_READ		(TSK_ERR_FS | 4)
 #define TSK_ERR_FS_READ_OFF	(TSK_ERR_FS | 5)
-#define TSK_ERR_FS_ARG		(TSK_ERR_FS | 6)    
+#define TSK_ERR_FS_ARG		(TSK_ERR_FS | 6)
 #define TSK_ERR_FS_BLK_NUM	(TSK_ERR_FS | 7)
 #define TSK_ERR_FS_INODE_NUM	(TSK_ERR_FS | 8)
 #define TSK_ERR_FS_INODE_COR	(TSK_ERR_FS | 9)
@@ -316,7 +364,7 @@ extern "C" {
 
 /** \name Endian Ordering Functions */
 //@{
-    /** 
+    /**
      * Flag that identifies the endian ordering of the data being read.
      */
     typedef enum {
@@ -414,5 +462,95 @@ documentation and/or software.
 
 #ifdef __cplusplus
 }
+#endif
+
+#ifdef __cplusplus
+#if 0
+class TskStack{
+private:
+    TSK_STACK *m_stack;
+    
+public:
+    /**
+    * Create a TSK_STACK structure. See tsk_stack_create() for details.
+    * @returns Pointer to structure or NULL on error
+    */
+   TskStack(){
+       m_stack = tsk_stack_create();
+    };
+   /**
+   * Free an allocated TSK_STACK structure. See tsk_stack_free() for details.
+   */
+    ~TskStack(){
+        tsk_stack_free(m_stack);
+    };
+    /**
+    * Pop a value from the top of the stack. See tsk_stack_pop() for details.
+    */
+    void pop(){
+        tsk_stack_pop(m_stack);
+    };
+    /**
+    * Push a value to the top of TSK_STACK. See tsk_stack_push() for details.
+    * @param a_val Value to push on
+    * @returns 1 on error 
+    */
+    uint8_t push(uint64_t a_val){
+        return tsk_stack_push(m_stack, a_val);
+    };
+    /**
+    * Search a TSK_STACK for a given value. See tsk_stack_find() for details.
+    * @param a_val Value to search for 
+    * @returns 1 if found and 0 if not
+    */
+    uint8_t find(uint64_t a_val){
+        return tsk_stack_find(m_stack, a_val);
+    };
+     /**
+    * Return Number of entries in the stack
+    * @returns number of entries in the stack
+    */
+   size_t length(){
+       if (m_stack != NULL)
+        return m_stack->len;
+       else
+           return 0;
+    };            
+};
+#endif
+
+/**
+ * \ingroup baselib_cpp
+ * Allows access to most recent error message and code in the thread.
+ */
+class TskError{ 
+public:
+    /**
+    * Return the string with the current error message.  The string does not end with a 
+    * newline. See tsk_error_get() for details.
+    *
+    * @returns String with error message or NULL if there is no error
+    */
+   static const char *get(){
+        return tsk_error_get();
+    };
+    
+   /**
+   * Print the current error message to a file. See tsk_error_print() for details.
+   *
+   * @param a_hFile File to print message to
+   */
+    static void print(FILE *a_hFile){
+        tsk_error_print(a_hFile);
+    };
+
+    /**
+    * Clear the error number and error message. See tsk_error_reset() for details.
+    */
+    static void reset(){
+        tsk_error_reset();
+    };
+};
+
 #endif
 #endif
diff --git a/tsk3/base/tsk_base_i.h b/tsk3/base/tsk_base_i.h
index 24772b639..85c197601 100644
--- a/tsk3/base/tsk_base_i.h
+++ b/tsk3/base/tsk_base_i.h
@@ -1,16 +1,16 @@
 /*
  * The Sleuth Kit
- * 
+ *
  */
 
 /** \file tsk_base_i.h
- * Contains the general internal TSK type and function definitions.  
- * This is needed by the library as it is built. 
+ * Contains the general internal TSK type and function definitions.
+ * This is needed by the library as it is built.
  */
 #ifndef _TSK_BASE_I_H
 #define _TSK_BASE_I_H
 
-// include the autoconf header file 
+// include the autoconf header file
 #if HAVE_CONFIG_H
 #include "tsk3/tsk_config.h"
 #endif
@@ -33,6 +33,12 @@
 extern "C" {
 #endif
 
+
+extern void tsk_init_lock(tsk_lock_t *);
+extern void tsk_deinit_lock(tsk_lock_t *);
+extern void tsk_take_lock(tsk_lock_t *);
+extern void tsk_release_lock(tsk_lock_t *);
+
 #ifndef rounddown
 #define rounddown(x, y)	\
     ((((x) % (y)) == 0) ? (x) : \
@@ -52,15 +58,6 @@ extern "C" {
 
 
 
-/* Error handling */
-#define TSK_ERRSTR_L	512
-#define TSK_ERRSTR_PR_L	((TSK_ERRSTR_L << 2) + 64)
-
-    extern char tsk_errstr[TSK_ERRSTR_L];
-    extern char tsk_errstr2[TSK_ERRSTR_L];
-    extern char tsk_errstr_print[TSK_ERRSTR_PR_L];
-
-
 
 /* Endian Ordering */
 /* macros to read in multi-byte fields
@@ -188,16 +185,16 @@ extern "C" {
 
 /** \name Unicode */
 //@{
-    // basic check to see if a Unicode file has been included 
+    // basic check to see if a Unicode file has been included
     // in an app that is using this as a library
 #ifndef TSK_UNI_REPLACEMENT_CHAR
 
 /**************** UNICODE *******************/
 /*
  * Copyright 2001-2004 Unicode, Inc.
- * 
+ *
  * Disclaimer
- * 
+ *
  * This source code is provided as is by Unicode, Inc. No claims are
  * made as to fitness for any particular purpose. No warranties of any
  * kind are expressed or implied. The recipient agrees to determine
@@ -205,9 +202,9 @@ extern "C" {
  * purchased on magnetic or optical media from Unicode, Inc., the
  * sole remedy for any claim will be exchange of defective media
  * within 90 days of receipt.
- * 
+ *
  * Limitations on Rights to Redistribute This Code
- * 
+ *
  * Unicode, Inc. hereby grants the right to freely use the information
  * supplied in this file in the creation of products supporting the
  * Unicode Standard, and to make copies of this file in any form
@@ -228,7 +225,7 @@ extern "C" {
 
     Each routine converts the text between *sourceStart and sourceEnd,
     putting the result into the buffer between *targetStart and
-    targetEnd. Note: the end pointers are *after* the last item: e.g. 
+    targetEnd. Note: the end pointers are *after* the last item: e.g.
     *(sourceEnd - 1) is the last item.
 
     The return result indicates whether the conversion was successful,
@@ -266,7 +263,7 @@ extern "C" {
 	sequence is malformed.  When "TSKsourceIllegal" is returned, the source
 	value will point to the illegal value that caused the problem. E.g.,
 	in UTF-8 when a sequence is malformed, it points to the start of the
-	malformed sequence.  
+	malformed sequence.
 
     Author: Mark E. Davis, 1994.
     Rev History: Rick McGowan, fixes & updates May 2001.
@@ -290,9 +287,9 @@ extern "C" {
 
     typedef enum {
         TSKconversionOK,        ///< conversion successful
-        TSKsourceExhausted,     ///< partial character in source, but hit end 
-        TSKtargetExhausted,     ///< insuff. room in target for conversion 
-        TSKsourceIllegal        ///< source sequence is illegal/malformed 
+        TSKsourceExhausted,     ///< partial character in source, but hit end
+        TSKtargetExhausted,     ///< insuff. room in target for conversion
+        TSKsourceIllegal        ///< source sequence is illegal/malformed
     } TSKConversionResult;
 
     typedef enum {
diff --git a/tsk3/base/tsk_endian.c b/tsk3/base/tsk_endian.c
index 0ae52023b..fb75250e6 100644
--- a/tsk3/base/tsk_endian.c
+++ b/tsk3/base/tsk_endian.c
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2003-2008 Brian Carrier.  All rights reserved 
+ * Copyright (c) 2003-2011 Brian Carrier.  All rights reserved 
  *
  * Copyright (c) 2002 Brian Carrier, @stake Inc.  All rights reserved
  *
diff --git a/tsk3/base/tsk_error.c b/tsk3/base/tsk_error.c
index 3122f3601..430ca76f1 100644
--- a/tsk3/base/tsk_error.c
+++ b/tsk3/base/tsk_error.c
@@ -1,11 +1,12 @@
 /*
- * The Sleuth Kit 
+ * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2006-2008 Brian Carrier.  All Rights reserved
+ * Copyright (c) 2006-2011 Brian Carrier.  All Rights reserved
  *
  * This software is distributed under the Common Public License 1.0
  */
+
 #include "tsk_base_i.h"
 
 /**
@@ -18,38 +19,6 @@
 char *progname = "unknown";
 int tsk_verbose = 0;
 
-/** 
- * \ingroup baselib
- * Set when an error occurs and contains the error code.
- */
-uint32_t tsk_errno = 0;
-
-
-/* \internal
- * Contains an error-specific string and is valid only 
- * when tsk_errno is set. This should be set when errno is set,
- * if it is not needed, then set tsk_errstr[0] to '\0'. */
-char tsk_errstr[TSK_ERRSTR_L];
-
-/* \internal 
-* Contains a caller-specific string and is valid only when tsk_errno is set 
-*
-* This is typically set to start with a NULL char when errno is set and then set with
-* a string by the code that called the 
-* function that had the error.  For
-* example, the X_read() function may set why
-* the read failed in tsk_errstr and the
-* function that called X_read() can provide
-* more context about why X_read() was 
-* called in the first place
-*/
-char tsk_errstr2[TSK_ERRSTR_L];
-
-
-/* \internal
- * Buffer used to store the printed message formed by tsk_errstr and tsk_errstr2 */
-char tsk_errstr_print[TSK_ERRSTR_PR_L];
-
 
 /* Error messages */
 static const char *tsk_err_aux_str[TSK_ERR_IMG_MAX] = {
@@ -130,10 +99,59 @@ static const char *tsk_err_auto_str[TSK_ERR_AUTO_MAX] = {
     "Image not opened yet"
 };
 
+#ifdef HAVE_PTHREAD
+static pthread_key_t pt_tls_key;
+static pthread_once_t pt_tls_key_once = PTHREAD_ONCE_INIT;
+
+static void 
+free_error_info(void *per_thread_error_info)
+{
+    if (per_thread_error_info != 0) {
+        free(per_thread_error_info);
+        pthread_setspecific(pt_tls_key, 0);
+    }
+}
+
+static void
+make_pt_tls_key()
+{
+    (void) pthread_key_create(&pt_tls_key, free_error_info);
+}
+
+TSK_ERROR_INFO* tsk_error_get_info()
+{
+    TSK_ERROR_INFO *ptr = 0;
+    (void) pthread_once(&pt_tls_key_once, make_pt_tls_key);
+    if ((ptr = (TSK_ERROR_INFO*)pthread_getspecific(pt_tls_key)) == 0) {
+        ptr = (TSK_ERROR_INFO*)malloc(sizeof(TSK_ERROR_INFO));
+        ptr->t_errno = 0;
+        ptr->errstr[0] = 0;
+        ptr->errstr2[0] = 0;
+        (void) pthread_setspecific(pt_tls_key, ptr);
+    }
+    return ptr;
+}
+
+#else
+#ifdef TSK_WIN32
+
+TSK_ERROR_INFO* tsk_error_get_info()
+{
+    return (TSK_ERROR_INFO*)tsk_error_win32_get_per_thread_(sizeof(TSK_ERROR_INFO));
+}
+
+#else
+
+/* No pthreads */
+static TSK_ERROR_INFO error_info = { 0, {0}, {0} };
+TSK_ERROR_INFO* tsk_error_get_info() { return &error_info; }
+
+#endif
+#endif
 
 /**
  * \ingroup baselib
- * Return the string with the current error message.  The string does not end with a 
+ * Return the string with the current error message.  The string does not end with a
  * newline.
  *
  * @returns String with error message or NULL if there is no error
@@ -142,83 +160,198 @@ const char *
 tsk_error_get()
 {
     size_t pidx = 0;
+    TSK_ERROR_INFO* error_info = tsk_error_get_info();
+    int t_errno = error_info->t_errno;
+    char *errstr_print = error_info->errstr_print;
 
-    if (tsk_errno == 0)
+    if (t_errno == 0) {
         return NULL;
+    }
 
-    memset(tsk_errstr_print, 0, TSK_ERRSTR_PR_L);
-    if (tsk_errno & TSK_ERR_AUX) {
-        if ((TSK_ERR_MASK & tsk_errno) < TSK_ERR_AUX_MAX)
-            snprintf(&tsk_errstr_print[pidx], TSK_ERRSTR_PR_L - pidx,
-                "%s", tsk_err_aux_str[tsk_errno & TSK_ERR_MASK]);
+    memset(errstr_print, 0, TSK_ERROR_STRING_MAX_LENGTH);
+    if (t_errno & TSK_ERR_AUX) {
+        if ((TSK_ERR_MASK && t_errno) < TSK_ERR_AUX_MAX)
+            snprintf(&errstr_print[pidx], TSK_ERROR_STRING_MAX_LENGTH - pidx,
+                "%s", tsk_err_aux_str[t_errno & TSK_ERR_MASK]);
         else
-            snprintf(&tsk_errstr_print[pidx], TSK_ERRSTR_PR_L - pidx,
-                "auxtools error: %" PRIu32, TSK_ERR_MASK & tsk_errno);
+            snprintf(&errstr_print[pidx], TSK_ERROR_STRING_MAX_LENGTH - pidx,
+                "auxtools error: %" PRIu32, TSK_ERR_MASK & t_errno);
     }
-    else if (tsk_errno & TSK_ERR_IMG) {
-        if ((TSK_ERR_MASK & tsk_errno) < TSK_ERR_IMG_MAX)
-            snprintf(&tsk_errstr_print[pidx], TSK_ERRSTR_PR_L - pidx,
-                "%s", tsk_err_img_str[tsk_errno & TSK_ERR_MASK]);
+    else if (t_errno & TSK_ERR_IMG) {
+        if ((TSK_ERR_MASK & t_errno) < TSK_ERR_IMG_MAX)
+            snprintf(&errstr_print[pidx], TSK_ERROR_STRING_MAX_LENGTH - pidx,
+                "%s", tsk_err_img_str[t_errno & TSK_ERR_MASK]);
         else
-            snprintf(&tsk_errstr_print[pidx], TSK_ERRSTR_PR_L - pidx,
-                "imgtools error: %" PRIu32, TSK_ERR_MASK & tsk_errno);
+            snprintf(&errstr_print[pidx], TSK_ERROR_STRING_MAX_LENGTH - pidx,
+                "imgtools error: %" PRIu32, TSK_ERR_MASK & t_errno);
     }
-    else if (tsk_errno & TSK_ERR_VS) {
-        if ((TSK_ERR_MASK & tsk_errno) < TSK_ERR_VS_MAX)
-            snprintf(&tsk_errstr_print[pidx], TSK_ERRSTR_PR_L - pidx,
-                "%s", tsk_err_mm_str[tsk_errno & TSK_ERR_MASK]);
+    else if (t_errno & TSK_ERR_VS) {
+        if ((TSK_ERR_MASK & t_errno) < TSK_ERR_VS_MAX)
+            snprintf(&errstr_print[pidx], TSK_ERROR_STRING_MAX_LENGTH - pidx,
+                "%s", tsk_err_mm_str[t_errno & TSK_ERR_MASK]);
         else
-            snprintf(&tsk_errstr_print[pidx], TSK_ERRSTR_PR_L - pidx,
-                "mmtools error: %" PRIu32, TSK_ERR_MASK & tsk_errno);
+            snprintf(&errstr_print[pidx], TSK_ERROR_STRING_MAX_LENGTH - pidx,
+                "mmtools error: %" PRIu32, TSK_ERR_MASK & t_errno);
     }
-    else if (tsk_errno & TSK_ERR_FS) {
-        if ((TSK_ERR_MASK & tsk_errno) < TSK_ERR_FS_MAX)
-            snprintf(&tsk_errstr_print[pidx], TSK_ERRSTR_PR_L - pidx,
-                "%s", tsk_err_fs_str[tsk_errno & TSK_ERR_MASK]);
+    else if (t_errno & TSK_ERR_FS) {
+        if ((TSK_ERR_MASK & t_errno) < TSK_ERR_FS_MAX)
+            snprintf(&errstr_print[pidx], TSK_ERROR_STRING_MAX_LENGTH - pidx,
+                "%s", tsk_err_fs_str[t_errno & TSK_ERR_MASK]);
         else
-            snprintf(&tsk_errstr_print[pidx], TSK_ERRSTR_PR_L - pidx,
-                "fstools error: %" PRIu32, TSK_ERR_MASK & tsk_errno);
+            snprintf(&errstr_print[pidx], TSK_ERROR_STRING_MAX_LENGTH - pidx,
+                "fstools error: %" PRIu32, TSK_ERR_MASK & t_errno);
     }
-    else if (tsk_errno & TSK_ERR_HDB) {
-        if ((TSK_ERR_MASK & tsk_errno) < TSK_ERR_HDB_MAX)
-            snprintf(&tsk_errstr_print[pidx], TSK_ERRSTR_PR_L - pidx,
-                "%s", tsk_err_hdb_str[tsk_errno & TSK_ERR_MASK]);
+    else if (t_errno & TSK_ERR_HDB) {
+        if ((TSK_ERR_MASK & t_errno) < TSK_ERR_HDB_MAX)
+            snprintf(&errstr_print[pidx], TSK_ERROR_STRING_MAX_LENGTH - pidx,
+                "%s", tsk_err_hdb_str[t_errno & TSK_ERR_MASK]);
         else
-            snprintf(&tsk_errstr_print[pidx], TSK_ERRSTR_PR_L - pidx,
-                "hashtools error: %" PRIu32, TSK_ERR_MASK & tsk_errno);
+            snprintf(&errstr_print[pidx], TSK_ERROR_STRING_MAX_LENGTH - pidx,
+                "hashtools error: %" PRIu32, TSK_ERR_MASK & t_errno);
     }
-    else if (tsk_errno & TSK_ERR_AUTO) {
-        if ((TSK_ERR_MASK & tsk_errno) < TSK_ERR_AUTO_MAX)
-            snprintf(&tsk_errstr_print[pidx], TSK_ERRSTR_PR_L - pidx,
-                "%s", tsk_err_auto_str[tsk_errno & TSK_ERR_MASK]);
+    else if (t_errno & TSK_ERR_AUTO) {
+        if ((TSK_ERR_MASK & t_errno) < TSK_ERR_AUTO_MAX)
+            snprintf(&errstr_print[pidx], TSK_ERROR_STRING_MAX_LENGTH - pidx,
+                "%s", tsk_err_auto_str[t_errno & TSK_ERR_MASK]);
         else
-            snprintf(&tsk_errstr_print[pidx], TSK_ERRSTR_PR_L - pidx,
-                "auto error: %" PRIu32, TSK_ERR_MASK & tsk_errno);
+            snprintf(&errstr_print[pidx], TSK_ERROR_STRING_MAX_LENGTH - pidx,
+                "auto error: %" PRIu32, TSK_ERR_MASK & t_errno);
     }
     else {
-        snprintf(&tsk_errstr_print[pidx], TSK_ERRSTR_PR_L - pidx,
-            "Unknown Error: %" PRIu32, tsk_errno);
+        snprintf(&errstr_print[pidx], TSK_ERROR_STRING_MAX_LENGTH - pidx,
+            "Unknown Error: %" PRIu32, t_errno);
     }
-    pidx = strlen(tsk_errstr_print);
+    pidx = strlen(errstr_print);
 
     /* Print the unique string, if it exists */
-    if (tsk_errstr[0] != '\0') {
-        snprintf(&tsk_errstr_print[pidx], TSK_ERRSTR_PR_L - pidx,
-            " (%s)", tsk_errstr);
-        pidx = strlen(tsk_errstr_print);
+    if (error_info->errstr[0] != '\0') {
+        snprintf(&errstr_print[pidx], TSK_ERROR_STRING_MAX_LENGTH - pidx,
+            " (%s)", error_info->errstr);
+        pidx = strlen(errstr_print);
     }
 
-    if (tsk_errstr2[0] != '\0') {
-        snprintf(&tsk_errstr_print[pidx], TSK_ERRSTR_PR_L - pidx,
-            " (%s)", tsk_errstr2);
-        pidx = strlen(tsk_errstr_print);
+    if (error_info->errstr2[0] != '\0') {
+        snprintf(&errstr_print[pidx], TSK_ERROR_STRING_MAX_LENGTH - pidx,
+            " (%s)", error_info->errstr2);
+        pidx = strlen(errstr_print);
+    }
+    return (char *) error_info->errstr_print;
+}
+
+/**
+ * \ingroup baselib
+ * Return the current error number.
+ * @returns the current error number.
+ */
+uint32_t tsk_error_get_errno()
+{
+    return tsk_error_get_info()->t_errno;
+}
+
+/**
+ * \ingroup baselib
+ * Set the current TSK error number.
+ * @param t_errno the error number.
+ */
+void tsk_error_set_errno(uint32_t t_errno)
+{
+    tsk_error_get_info()->t_errno = t_errno;
+}
+
+/**
+ * \ingroup baselib
+ * Retrieve the current, basic error string.  
+ * Additional information is in errstr2.  
+ * Use tsk_error_get() to get a fully formatted string. 
+ * @returns the string. This is only valid until the next call to a tsk function.
+ */
+char *tsk_error_get_errstr()
+{
+    return tsk_error_get_info()->errstr;
+}
+
+/**
+ * \ingroup baselib
+ * Set the error string #1. This should contain the basic message. 
+ * @param format the printf-style format string
+ */
+void tsk_error_set_errstr(char const * format, ...)
+{
+    va_list args;
+    va_start(args, format);
+    vsnprintf(tsk_error_get_info()->errstr, TSK_ERROR_STRING_MAX_LENGTH, format, args);
+    va_end(args);
+}
+
+/**
+ * \ingroup baselib
+ * Set the error string
+ * @param format the printf-style format string
+ * @param args the printf-style args
+ */
+void tsk_error_vset_errstr(char const * format, va_list args)
+{
+    vsnprintf(tsk_error_get_info()->errstr, TSK_ERROR_STRING_MAX_LENGTH, format, args);
+}
+
+/**
+ * \ingroup baselib
+ * Retrieve the current error string #2.
+ * This has additional information than string #1.
+ * @returns the string. This is only valid until the next call to a tsk function.
+ */
+char *tsk_error_get_errstr2()
+{
+    return tsk_error_get_info()->errstr2;
+}
+
+/**
+ * \ingroup baselib
+ * Set the error string #2. This is called by methods who encounter the error,
+ * but did not set errno. 
+ * @param format the printf-style format string
+ */
+void tsk_error_set_errstr2(char const * format, ...)
+{
+    va_list args;
+    va_start(args, format);
+    vsnprintf(tsk_error_get_info()->errstr2, TSK_ERROR_STRING_MAX_LENGTH, format, args);
+    va_end(args);
+}
+
+/**
+ * \ingroup baselib
+ * Set the error string
+ * @param format the printf-style format string
+ * @param args the printf-style format args
+ */
+void tsk_error_vset_errstr2(char const * format, va_list args)
+{
+    vsnprintf(tsk_error_get_info()->errstr2, TSK_ERROR_STRING_MAX_LENGTH, format, args);
+}
+
+/**
+ * \ingroup baselib
+ * Concatenate a message onto the end of the errstr2.
+ * @param format
+ */
+void tsk_error_errstr2_concat(char const * format, ...)
+{
+    va_list args;
+    char * errstr2 = tsk_error_get_info()->errstr2;
+    int current_length = (int)(strlen(errstr2) + 1); // +1 for a space
+    if (current_length > 0) {
+        int remaining = TSK_ERROR_STRING_MAX_LENGTH - current_length;
+        errstr2[current_length-1] = ' ';
+        va_start(args, format);
+        vsnprintf(&errstr2[current_length], remaining, format, args);
+        va_end(args);
     }
-    return (char *) &tsk_errstr_print[0];
 }
 
 /**
  * \ingroup baselib
- * Print the current error message to a file.
+ * Print the current fully formed error message to a file.
  *
  * @param hFile File to print message to
  */
@@ -226,7 +359,7 @@ void
 tsk_error_print(FILE * hFile)
 {
     const char *str;
-    if (tsk_errno == 0)
+    if (tsk_error_get_errno() == 0)
         return;
 
     str = tsk_error_get();
@@ -236,18 +369,20 @@ tsk_error_print(FILE * hFile)
     else {
         tsk_fprintf(hFile,
             "Error creating Sleuth Kit error string (Errno: %d)\n",
-            tsk_errno);
+            tsk_error_get_errno());
     }
 }
 
 /**
  * \ingroup baselib
- * Clear the error number and error message.  
+ * Clear the error number and error message.
  */
 void
 tsk_error_reset()
 {
-    tsk_errno = 0;
-    tsk_errstr[0] = '\0';
-    tsk_errstr2[0] = '\0';
+    TSK_ERROR_INFO* info = tsk_error_get_info();
+    info->t_errno = 0;
+    info->errstr[0] = 0;
+    info->errstr2[0] = 0;
+    info->errstr_print[0] = 0;
 }
diff --git a/tsk3/base/tsk_list.c b/tsk3/base/tsk_list.c
index 7e985ad04..5ed5dfc49 100644
--- a/tsk3/base/tsk_list.c
+++ b/tsk3/base/tsk_list.c
@@ -2,7 +2,7 @@
  * The Sleuth Kit 
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2007-2008 Brian Carrier.  All Rights reserved
+ * Copyright (c) 2007-2011 Brian Carrier.  All Rights reserved
  *
  * This software is distributed under the Common Public License 1.0
  */
diff --git a/tsk3/base/tsk_lock.c b/tsk3/base/tsk_lock.c
new file mode 100644
index 000000000..d6e057994
--- /dev/null
+++ b/tsk3/base/tsk_lock.c
@@ -0,0 +1,106 @@
+/*
+ * The Sleuth Kit
+ *
+ * Brian Carrier [carrier <at> sleuthkit [dot] org]
+ * Copyright (c) 2011 Brian Carrier.  All Rights reserved
+ *
+ * This software is distributed under the Common Public License 1.0
+ */
+
+#include "tsk_base_i.h"
+
+#ifdef TSK_MULTITHREAD_LIB
+
+#ifdef TSK_WIN32
+
+void tsk_init_lock(tsk_lock_t * lock)
+{
+    InitializeCriticalSection(&lock->critical_section);
+}
+
+void tsk_deinit_lock(tsk_lock_t * lock)
+{
+    DeleteCriticalSection(&lock->critical_section);
+}
+
+void tsk_take_lock(tsk_lock_t * lock)
+{
+    EnterCriticalSection(&lock->critical_section);
+}
+
+void tsk_release_lock(tsk_lock_t *lock)
+{
+    LeaveCriticalSection(&lock->critical_section);
+}
+
+#else
+
+#include <assert.h>
+
+void tsk_init_lock(tsk_lock_t * lock)
+{
+    pthread_mutexattr_t attr;
+    pthread_mutexattr_init(&attr);
+
+    // Locks on Linux are not recursive (unlike on Windows), so things
+    // will hang if the current thread tries to take the lock again.
+    // While debugging, it's sometimes useful to call
+    //
+    //  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
+    //
+    // which will provoke an immediate error rather than a hang.
+    //
+    // PTHREAD_MUTEX_ERRORCHECK is not defined by default on Linux,
+    // but it is portable if you have the right_XOPEN_SOURCE settings.
+    // However, that macro affects the availability of other features
+    // like BSD-style u_int types used in afflib.
+    // PTHREAD_MUTEX_ERRORCHECK -is- available by default on Mac.
+    //
+    // PTHREAD_MUTEX_ERRORCHECK_NP is the Linux/gcc non-portable
+    // equivalent which does not require _XOPEN_SOURCE.
+    //
+    // Other interesting attributes are PTHREAD_MUTEX_RECURSIVE (and
+    // PTHREAD_MUTEX_RECURSIVE_NP).  We avoided those out of portability
+    // concerns with the _XOPEN_SOURCE settings.
+
+    int e = pthread_mutex_init(&lock->mutex, &attr);
+    pthread_mutexattr_destroy(&attr);
+    if (e != 0) {
+        fprintf(stderr, "tsk_init_lock: thread_mutex_init failed %d\n", e);
+        assert(0);
+    }
+}
+
+void tsk_deinit_lock(tsk_lock_t * lock)
+{
+    pthread_mutex_destroy(&lock->mutex);
+}
+
+void tsk_take_lock(tsk_lock_t * lock)
+{
+    int e = pthread_mutex_lock(&lock->mutex);
+    if (e != 0) {
+        fprintf(stderr, "tsk_take_lock: thread_mutex_lock failed %d\n", e);
+        assert(0);
+    }
+}
+
+void tsk_release_lock(tsk_lock_t *lock)
+{
+    int e = pthread_mutex_unlock(&lock->mutex);
+    if (e != 0) {
+        fprintf(stderr, "tsk_release_lock: thread_mutex_unlock failed %d\n", e);
+        assert(0);
+    }
+}
+
+#endif
+
+#else
+
+void tsk_init_lock(tsk_lock_t * lock) {}
+void tsk_deinit_lock(tsk_lock_t * lock) {}
+void tsk_take_lock(tsk_lock_t * lock) {}
+void tsk_release_lock(tsk_lock_t *lock) {}
+
+#endif
diff --git a/tsk3/base/tsk_os.h b/tsk3/base/tsk_os.h
index c41abc19a..ba97328ed 100644
--- a/tsk3/base/tsk_os.h
+++ b/tsk3/base/tsk_os.h
@@ -2,7 +2,7 @@
 ** The Sleuth Kit 
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2004-2005 Brian Carrier.  All rights reserved
+** Copyright (c) 2004-2011 Brian Carrier.  All rights reserved
 */
 
 /** \file tsk_os.h
@@ -60,6 +60,7 @@
 // Visual Studio / Windows
 #ifdef _MSC_VER
 #define TSK_WIN32
+#define TSK_MULTITHREAD_LIB
 
 #ifndef UNICODE
 #define UNICODE
diff --git a/tsk3/base/tsk_parse.c b/tsk3/base/tsk_parse.c
index c77352dfd..e939bfad9 100644
--- a/tsk3/base/tsk_parse.c
+++ b/tsk3/base/tsk_parse.c
@@ -1,8 +1,8 @@
 /*
- * The Sleuth Kit 
+ * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2005-2008 Brian Carrier.  All Rights reserved
+ * Copyright (c) 2005-2011 Brian Carrier.  All Rights reserved
  *
  * This software is distributed under the Common Public License 1.0
  */
@@ -11,15 +11,15 @@
 
 /**
  * \file tsk_parse.c
- * Contains code to parse specific types of data from 
+ * Contains code to parse specific types of data from
  * the command line
  */
 
 /**
  * \ingroup baselib
- * Parse a TSK_TCHAR block address string. 
+ * Parse a TSK_TCHAR block address string.
  * Note that the cnt\@size format is no longer supported.
- * Set the device sector size in img_open to set the block size. 
+ * Set the device sector size in img_open to set the block size.
  *
  * @param [in] a_offset_str The string version of the offset
  * @return -1 on error or block offset on success
@@ -30,15 +30,14 @@ tsk_parse_offset(const TSK_TCHAR * a_offset_str)
     TSK_TCHAR offset_lcl[64], *offset_lcl_p;
     TSK_DADDR_T num_blk;
     TSK_TCHAR *cp;
-    
+
     if (a_offset_str == NULL) {
         return 0;
     }
     if (TSTRLEN(a_offset_str) > 63) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_IMG_OFFSET;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
-            "tsk_parse: offset string is too long: %" PRIttocTSK,
+        tsk_error_set_errno(TSK_ERR_IMG_OFFSET);
+        tsk_error_set_errstr("tsk_parse: offset string is too long: %" PRIttocTSK,
             a_offset_str);
         return -1;
     }
@@ -50,9 +49,8 @@ tsk_parse_offset(const TSK_TCHAR * a_offset_str)
     /* Check for the old x@y setup */
     if (TSTRCHR(offset_lcl_p, '@') != NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_IMG_OFFSET;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
-                 "tsk_parse: offset string format no longer supported.  Use -b to specify sector size: %" PRIttocTSK,
+        tsk_error_set_errno(TSK_ERR_IMG_OFFSET);
+        tsk_error_set_errstr("tsk_parse: offset string format no longer supported.  Use -b to specify sector size: %" PRIttocTSK,
                  a_offset_str);
         return -1;
     }
@@ -68,9 +66,8 @@ tsk_parse_offset(const TSK_TCHAR * a_offset_str)
         num_blk = TSTRTOULL(offset_lcl_p, &cp, 0);
         if (*cp || *cp == *offset_lcl_p) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_IMG_OFFSET;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
-                "tsk_parse: invalid image offset: %" PRIttocTSK,
+            tsk_error_set_errno(TSK_ERR_IMG_OFFSET);
+            tsk_error_set_errstr("tsk_parse: invalid image offset: %" PRIttocTSK,
                 offset_lcl_p);
             return -1;
         }
@@ -84,7 +81,7 @@ tsk_parse_offset(const TSK_TCHAR * a_offset_str)
 
 /**
  * \ingroup baselib
- * Parse a TSK_TCHAR string of a partition byte offset and the 
+ * Parse a TSK_TCHAR string of a partition byte offset and the
  * integer version of it.
  *
  * @param [in] a_pnum_str The string version of the address
@@ -103,9 +100,8 @@ tsk_parse_pnum(const TSK_TCHAR * a_pnum_str, TSK_PNUM_T * a_pnum)
     *a_pnum = TSTRTOUL(a_pnum_str, &cp, 0);
     if (*cp || *cp == *a_pnum_str) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_IMG_OFFSET;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
-            "tsk_parse: invalid partition address: %" PRIttocTSK,
+        tsk_error_set_errno(TSK_ERR_IMG_OFFSET);
+        tsk_error_set_errstr("tsk_parse: invalid partition address: %" PRIttocTSK,
             a_pnum_str);
         return 1;
     }
diff --git a/tsk3/base/tsk_printf.c b/tsk3/base/tsk_printf.c
index 1a0c48c1b..5f8dfe473 100644
--- a/tsk3/base/tsk_printf.c
+++ b/tsk3/base/tsk_printf.c
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2007 Brian Carrier.  All Rights reserved
+ * Copyright (c) 2007-2011 Brian Carrier.  All Rights reserved
  *
  * This software is distributed under the Common Public License 1.0
  */
diff --git a/tsk3/base/tsk_stack.c b/tsk3/base/tsk_stack.c
index d9bee5372..ceac7e463 100644
--- a/tsk3/base/tsk_stack.c
+++ b/tsk3/base/tsk_stack.c
@@ -2,7 +2,7 @@
  * The Sleuth Kit 
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2007 Brian Carrier.  All Rights reserved
+ * Copyright (c) 2007-2011 Brian Carrier.  All Rights reserved
  *
  * This software is distributed under the Common Public License 1.0
  */
diff --git a/tsk3/base/tsk_version.c b/tsk3/base/tsk_version.c
index 4a8871278..e89748d89 100644
--- a/tsk3/base/tsk_version.c
+++ b/tsk3/base/tsk_version.c
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2007 Brian Carrier.  All rights reserved 
+ * Copyright (c) 2007-2011 Brian Carrier.  All rights reserved 
  *
  * This software is distributed under the Common Public License 1.0
  */
diff --git a/tsk3/docs/Doxyfile b/tsk3/docs/Doxyfile
index e0655bc39..29e492411 100644
--- a/tsk3/docs/Doxyfile
+++ b/tsk3/docs/Doxyfile
@@ -31,14 +31,14 @@ PROJECT_NAME           = "The Sleuth Kit"
 # This could be handy for archiving the generated documentation or 
 # if some version control system is used.
 
-PROJECT_NUMBER         = 3.2
+PROJECT_NUMBER         = 3.3
 
 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
 # base path where the generated documentation will be put. 
 # If a relative path is entered, it will be relative to the location 
 # where doxygen was started. If left blank the current directory will be used.
 
-OUTPUT_DIRECTORY       = ../../../www/docs
+OUTPUT_DIRECTORY       = ../www/docs
 
 # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 
 # 4096 sub-directories (in 2 levels) under the output directory of each output 
@@ -572,12 +572,13 @@ INPUT                  = tsk3/docs/main.dox \
                          tsk3/docs/fs.dox \
                          tsk3/docs/hashdb.dox \
                          tsk3/docs/auto.dox \
+                         tsk3/docs/cpp.dox \
                          tsk3/base \
                          tsk3/fs \
                          tsk3/hashdb \
                          tsk3/img \
                          tsk3/vs \
-                         tsk3/auto
+                         tsk3/auto 
 
 # This tag can be used to specify the character encoding of the source files 
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is 
diff --git a/tsk3/docs/base.dox b/tsk3/docs/base.dox
index fb890935e..3e65755e4 100644
--- a/tsk3/docs/base.dox
+++ b/tsk3/docs/base.dox
@@ -24,7 +24,7 @@ Note that in some environments, the TSK_TCHAR design is difficult to work with.
 
 \section basic_error Error Handling
 
-The public API functions all return a value to indicate when an error occurs. More details about the error can be learned by reading the global tsk_errno value. This will be one of several predefined error codes.  A description of the error can be obtained using the tsk_error_get() function or the error can be printed to a FILE handle using tsk_error_print().  
+The public API functions all return a value to indicate when an error occurs. More details about the error can be learned by reading the global tsk_errno value. This will be one of several predefined error codes.  A description of the error can be obtained using the tsk_error_get() function or the error can be printed to a FILE handle using tsk_error_print().  The C++ wrapper is the TskError class.
 
 
 \section basic_misc Miscellaneous Utilities
diff --git a/tsk3/docs/basics.dox b/tsk3/docs/basics.dox
index e316879af..23b699656 100644
--- a/tsk3/docs/basics.dox
+++ b/tsk3/docs/basics.dox
@@ -77,6 +77,9 @@ To link with the libraries, you must configure your environment to include the l
 
 Note that your Windows application must have UNICODE support enabled. 
 
+\section basic_cpp C++ Wrapers
+Nearly all of the TSK code is written in C and the original API is a collection of C functions and structs.  There are also C++ classes that are wrappers around the C code.  The C++ class allocates the C structs and provides getter and setter methods to access the public data.  The remainder of this doc primarily refers to the C functions, but will provide a link to the corresponding C++ class when one exists. 
+
 Next to \ref basepage
 
 Back to \ref users_guide "Table of Contents"
diff --git a/tsk3/docs/footer.html b/tsk3/docs/footer.html
index 37c68f754..febf8d6ed 100644
--- a/tsk3/docs/footer.html
+++ b/tsk3/docs/footer.html
@@ -1,5 +1,5 @@
 <hr/>
-<p><i>Copyright &#169; 2007-2010 Brian Carrier.  (carrier -at- sleuthkit -dot- org)<br/> 
+<p><i>Copyright &#169; 2007-2011 Brian Carrier.  (carrier -at- sleuthkit -dot- org)<br/> 
 This work is licensed under a
 <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/3.0/us/">Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License</a>.
 </i></p>
diff --git a/tsk3/docs/fs.dox b/tsk3/docs/fs.dox
index 48f8bbd77..d6b64e72d 100644
--- a/tsk3/docs/fs.dox
+++ b/tsk3/docs/fs.dox
@@ -51,6 +51,8 @@ The tsk_fs_open_img() function allows you to open a file system using only a TSK
 
 Both of these functions return a TSK_FS_INFO structure that is used as a handle for more detailed file system analysis.  Close the file system with the tsk_fs_close() function.   The TSK_FS_INFO structure contains data regarding the number of data units, the number of metadata structures, etc. 
 
+The C++ wrappers use the TskFsInfo class and it has open methods to open the file system. 
+
     \subsection fs_types File System Types
 
     The functions that open the file system can detect the file system type, but sometimes you may need to specify it (if for example TSK detects multiple file system structures).   Internally, TSK uses a numerical ID (TSK_FS_TYPE_ENUM) for each file system type, which is stored in TSK_FS_INFO::ftype.  
@@ -70,6 +72,7 @@ To map from the numerical ID to a short name (such as "ntfs"), the tsk_fs_type_t
  	\subsection fs_read Reading General File System Data
 As you will see, there are many ways to access file system data from the different categories. One of the most generic methods is using the tsk_fs_read() function.  It simply takes a byte offset relative to the start of the file system and a buffer to read the data into.  It does not care about block addresses or files.  In the following sections, there are smarter versions of this function that will take block addresses as an argument, instead of a byte offset. 
 
+The TskFsInfo::read() method allows data to be read using the C++ classes. 
 
 \section fs_block_open Opening and Reading File System Blocks
 TSK allows you to read the contents of any block in the file system. The size of each data unit is defined in the TSK_FS_INFO::block_size field and the number of data units (as defined by the file system) is defined in the TSK_FS_INFO::block_count field.  The address of the first block is defined in TSK_FS_INFO::first_block and the last block address (as defined by the file system structures) is defined in TSK_FS_INFO::last_block.  In some cases, the image may not be complete and the last block that can be read is less than TSK_FS_INFO::last_block.  In that case, TSK_FS_INFO::last_block_act contains the actual last block in the image.  When last_block_act is less than last_block, it means that the image is not complete. 
@@ -80,6 +83,8 @@ You can also walk the data units by calling tsk_fs_block_walk().  This function
 
 You can also read the contents of a data unit using the tsk_fs_read_block() function, which reads a block of data (given its data unit address) into a buffer.  tsk_fs_read_block() does not provide the data unit's allocation status and is therefore more efficient than tsk_fs_block_get() if you want only the content. 
 
+Similar methods exist in the TskFsInfo C++ class.  The C++ wrapper to TSK_FS_BLOCK is the TskFsBlock class. 
+
 One FAT file system specific thing should be mentioned here. FAT stores its file content in clusters, but the first cluster does not start at the beginning of the file system.  There is typically many megabytes of data (FAT tables) before it.  If TSK were to use clusters as the standard data unit, it would have no way of addressing the sectors prior to the first cluster and a confusing addressing scheme would need to exist so that all data could be accessed.  For simplicity, TSK uses sector addresses everywhere with FAT file systems. All data unit addresses used by the TSK API are in 512-byte sectors (and TSK_FS_INFO::block_size is 512 and not the original cluster size).  
 
 
@@ -98,6 +103,8 @@ The tsk_fs_file_open() function takes a file name as an argument and first ident
  
 The TSK_FS_FILE structure can be used to read file content and its fields can be used for processing. The  structure must be closed with tsk_fs_file_close().
 
+The C++ wrappers use the TskFsFile class.  It has open methods that allow a file to be opened and read from. 
+
 	\subsection Reading File Content
 There are two basic approaches to reading file content after you have a TSK_FS_FILE structure.  You can read data into a buffer from a specific offset in a file using tsk_fs_file_read().  You can also use  the tsk_fs_file_walk() function, which takes a callback function as an argument and will call the callback with the contents of each data unit in the file. 
 
@@ -114,7 +121,7 @@ The TSK APIs that have been previously presented will use the default attribute.
 	\subsection fs_file_attr_read Accessing Attributes
 The previous section outlined that some API functions allow you to access a specific attribute.  Sometimes though, you may want access to the attribute, which is stored in a TSK_FS_ATTR structure. 
 
-To access the default attribute use tsk_fs_file_attr_get().  If you know the type that you want to access, you can use the tsk_fs_file_attr_get_type() function.
+To access the default attribute use tsk_fs_file_attr_get().  If you know the type that you want to access, you can use the tsk_fs_file_attr_get_type() function.  The C++ class has similar methods, TskFsFile::getAttr() for example. 
 
 If you want to figure out what types exist or want to cycle through all of the attributes, you can use the tsk_fs_file_attr_getsize() function to get the number of attributes and the tsk_fs_file_attr_get_idx() function to get an attribute based on a 0 to n-1 based index. For example:
 
@@ -131,7 +138,7 @@ If you want to figure out what types exist or want to cycle through all of the a
 \endcode
 
 Once you have a TSK_FS_ATTR structure, you can read from it using the tsk_fs_attr_read() and tsk_fs_attr_walk() functions.  These operate just like the
-tsk_fs_file_read() and tsk_fs_file_walk() functions and in fact the file-based functions simply load the relevant attribute and call the corresponding attribute-based function. 
+tsk_fs_file_read() and tsk_fs_file_walk() functions and in fact the file-based functions simply load the relevant attribute and call the corresponding attribute-based function.   Similar methods exist in the TskFsAttr class. 
 
 	\subsection fs_file_runs Data Runs
 This section provides some details on how the file content is stored in TSK.  If you use the APIs previously described, you will not need to read this section.  It is more of an FYI. 
@@ -156,7 +163,7 @@ These approaches all return a TSK_FS_FILE structure and these will all have the
 
 Another way to browse the files is using the tsk_fs_meta_walk() function, which will process a range of metadata structures and call a callback function on each one.  The callback gets the corresponding TSK_FS_FILE structure with the file's metadata in TSK_FS_FILE::meta and TSK_FS_FILE::name set to NULL. 
 
-
+This functionality also exists in the TskFsDir C++ class.  
 
 	\subsection fs_dir_spec Virtual Files
 When browsing the file system, using the directory structure is most convenient and therefore special files and directories were added to make finding all relevant data easier.  Orphan files, which were discussed in \ref fs_del, can be accessed from the <tt>/$OrphanFiles</tt> directory. This is a virtual directory, but TSK allows you to treat it as a normal directory (its flags in TSK_FS_META::flags will show that it is virtual though). 
@@ -166,7 +173,7 @@ TSK also provides special files so that you can access the boot sector and FATs
 \section fs_map Mapping Data
 In some cases, you may want to identify which file has allocated a given block or which name points to a meta data structure. This can typically be done by using the tsk_fs_meta_walk() or tsk_fs_dir_walk() functions, respectively.  But, there are some convenience functions to make this easier. 
 
-The tsk_fs_path2inum() function takes a UTF-8 path as an argument and will identify the meta data address that it points to.   
+The tsk_fs_path2inum() function takes a UTF-8 path as an argument and will identify the meta data address that it points to.    This exists in the C++ class as TskFsInfo::path2Inum().
 
 Next to \ref hashdbpage
  
diff --git a/tsk3/docs/hashdb.dox b/tsk3/docs/hashdb.dox
index 7af164978..b28400f5f 100644
--- a/tsk3/docs/hashdb.dox
+++ b/tsk3/docs/hashdb.dox
@@ -13,6 +13,8 @@ Before a database can be indexed or searched, it must first be opened.  Use the
 
 The tsk_hdb_open() function will return a TSK_HDB_INFO structure that will be used as a handle to index and search the database. An open hash database can be closed with tsk_hdb_close(). 
 
+This functionality also exists in the TskHdbInfo C++ class. 
+
 \section hash_index Indexing a Hash Database
 A single database can have more than one index if the database contains multiple hash types.  For example, both MD5 and SHA-1 indexes can be made for the NSRL database.  Because multiple indexes can exist, you must specify the type when making or testing for an index. 
 
diff --git a/tsk3/docs/img.dox b/tsk3/docs/img.dox
index dda986abc..be87c4b9a 100644
--- a/tsk3/docs/img.dox
+++ b/tsk3/docs/img.dox
@@ -9,6 +9,8 @@ This section describes the general disk image analysis concepts and correspondin
     The tsk_img_open() function returns a TSK_IMG_INFO structure.  This structure has fields that contain the type and size (uncompressed, if applicable) of the disk image.  When you are done analyzing the disk image, it can be closed with tsk_img_close(). 
 
     Note that the tsk_img_open() and tsk_img_open_sing() functions use the TSK_TCHAR type to store the disk image paths. This type is system dependent and is a wchar_t on Windows and char on other sytems.  See \ref basic_enc_t for more details. If you are in an environment where you will have UTF-8 text even in Windows, then you can use the tsk_img_open_utf8() and tsk_img_open_utf8_sing() functions. 
+
+    To use the C++ wrappers, create a TskImgInfo object and call one of the TskImgInfo::open() methods. 
     
     \section img_types File Format Types
 
@@ -18,7 +20,7 @@ This section describes the general disk image analysis concepts and correspondin
     
     \section img_read Reading Data
 
-    To read data from the disk image, the tsk_img_read() function is used.  This function can read an arbitrary amount of data from an arbitrary byte offset. 
+    To read data from the disk image, the tsk_img_read() function is used.  This function can read an arbitrary amount of data from an arbitrary byte offset.  The C++ class has a public read method, TskImgInfo::read().
 
 Next to \ref vspage
 
diff --git a/tsk3/docs/main.dox b/tsk3/docs/main.dox
index 47872545c..026fc8d4e 100644
--- a/tsk3/docs/main.dox
+++ b/tsk3/docs/main.dox
@@ -17,14 +17,15 @@ The User's Guide describes the various components of TSK and how to use them.  I
   - \subpage fspage 
   - \subpage hashdbpage
   - \subpage autopage
+  - \subpage cpppage
 
 <h3>API Reference</h3>
-The API Reference lists the public API functions and the arguments and return values.  The Users's Guide should be read first so that the interaction and use of the functions are understood.  These pages can also be found in the <a href="modules.html">Modules</a> section.  
-  - \ref baselib
-  - \ref imglib
-  - \ref vslib
-  - \ref fslib
-  - \ref hashdblib
+The API Reference lists the public C and C++ API functions with their arguments and return values.  The Users's Guide should be read first so that the interaction and use of the functions are understood.  These pages can also be found in the <a href="modules.html">Modules</a> section.  
+  - \ref baselib and \ref baselib_cpp
+  - \ref imglib and \ref imglib_cpp
+  - \ref vslib and \ref vslib_cpp
+  - \ref fslib and \ref fslib_cpp
+  - \ref hashdblib and \ref hashdblib_cpp
   - \ref autolib
 
 */
diff --git a/tsk3/docs/vs.dox b/tsk3/docs/vs.dox
index 1252ebdfd..32b4d8569 100644
--- a/tsk3/docs/vs.dox
+++ b/tsk3/docs/vs.dox
@@ -8,6 +8,8 @@ This section describes the general volume system analysis concepts and correspon
     
     The first step is to open the volume system using the tsk_vs_open() function. This function takes an offset as a argument.  TSK uses this offset to start looking for volume systems.  In general, an offset of 0 should be used.  This function returns a TSK_VS_INFO structure that has values for the volume system type and number of volumes (also called partitions).  The volume system is closed with the tsk_vs_close() function.
 
+    The C++ wrapper uses the TskImgInfo class, which has TskImgInfo::open() and TskImgInfo::close() methods that are similar to those previously described. 
+
     \section vs_types Volume System Types
 
     tsk_vs_open() can detect the type of the volume system, but sometimes you may need to specify it (if for example TSK detects multiple volume system structures) or you may want to print the name of the volume system type.   Internally, TSK uses a numerical ID for each volume system type.  To map from the numerical ID to a short name (such as "dos"), the tsk_vs_type_toname() function can be used.  To map the ID to a longer description (such as "DOS Partition Table"), use the tsk_vs_type_todesc() function.  You can also map from the short name to the ID using the tsk_vs_type_toid() function.
@@ -19,7 +21,7 @@ This section describes the general volume system analysis concepts and correspon
 
     The TSK_VS_INFO structure that tsk_vs_open() returns is for all volumes in the volume system. Each volume will be assigned an address, starting with 0. There are two general ways for accessing the individual volumes.  The tsk_vs_part_get() returns information about a specific volume while the tsk_vs_part_walk() function goes through a specified range of volumes and calls a callback function with data about each volume.
 
-    Regardless of the method used, a TSK_VS_PART_INFO structure is used to describe each volume.  This structure has fields for the volume starting offset (relative to the start of the disk image), length, and type.
+    Regardless of the method used, a TSK_VS_PART_INFO structure is used to describe each volume.  This structure has fields for the volume starting offset (relative to the start of the disk image), length, and type.  The C++ wrapper has a TskVsPartInfo class that wraps the TSK_VS_PART_INFO structure. 
 
     TSK defines several volume types.  \b Allocated volumes are those that are defined by the volume system.  An allocated volume has an entry in a partition table in the volume system.  \b Unallocated volumes contain the sectors that are not allocated to a volume in the volume system.  For example, if one volume has sectors 0 to 9 and another has sectors 50 to 99 then TSK would create an unallocated volume for sectors 10 to 49.  Note that these volumes are virtually created by TSK and they are not defined in the volume system.   Allocated and unallocated volumes will cover the entire disk image. 
     
@@ -31,6 +33,8 @@ This section describes the general volume system analysis concepts and correspon
 
     There are three ways to read data from a volume system.  The tsk_vs_read_block() function reads sectors using a sector address relative to the start of the volume system.  However, to read sectors using an address that is relative to the start of a specific volume, use the tsk_vs_part_read_block() function.  To read data in a volume in non-sector sized chunks, use the tsk_vs_part_read() function. 
 
+    The C++ TskVsInfo and TskVsPartInfo classes have read methods that allow the data to be read from the start of the volume system or inside of a volume. 
+
 Next to \ref fspage
 
 Back to \ref users_guide "Table of Contents"
diff --git a/tsk3/fs/Makefile.am b/tsk3/fs/Makefile.am
index b36bce9ce..d17bd4f25 100644
--- a/tsk3/fs/Makefile.am
+++ b/tsk3/fs/Makefile.am
@@ -1,4 +1,5 @@
 AM_CFLAGS = -I../.. -I$(srcdir)/../.. -Wall 
+AM_CPPFLAGS = -I../.. -I$(srcdir)/../.. -Wall 
 EXTRA_DIST = .indent.pro
 
 noinst_LTLIBRARIES = libtskfs.la
@@ -12,7 +13,8 @@ libtskfs_la_SOURCES  = tsk_fs_i.h fs_inode.c fs_io.c fs_block.c fs_open.c \
     iso9660.c iso9660_dent.c \
     hfs.c hfs_dent.c hfs_journal.c hfs_unicompare.c \
     dcalc_lib.c dcat_lib.c dls_lib.c dstat_lib.c ffind_lib.c \
-    fls_lib.c icat_lib.c ifind_lib.c ils_lib.c 
+    fls_lib.c icat_lib.c ifind_lib.c ils_lib.c \
+    walk_cpp.cpp
 
 indent:
 	indent *.c *.h
diff --git a/tsk3/fs/dcalc_lib.c b/tsk3/fs/dcalc_lib.c
index c420d2ca7..5b33d9d6f 100644
--- a/tsk3/fs/dcalc_lib.c
+++ b/tsk3/fs/dcalc_lib.c
@@ -8,7 +8,7 @@
 ** value it would have in a 'blkls' image (if the block is unallocated)
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2003-2005 Brian Carrier. All Rights reserved
 **
 ** TASK
diff --git a/tsk3/fs/dcat_lib.c b/tsk3/fs/dcat_lib.c
index 3ff27b5f6..e2c70e348 100644
--- a/tsk3/fs/dcat_lib.c
+++ b/tsk3/fs/dcat_lib.c
@@ -1,12 +1,12 @@
 /*
 ** blkcat
-** The  Sleuth Kit 
+** The  Sleuth Kit
 **
 ** Given an image , block number, and size, display the contents
 ** of the block to stdout.
-** 
+**
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
 **
 ** TASK
@@ -68,9 +68,8 @@ tsk_fs_blkcat(TSK_FS_INFO * fs, TSK_FS_BLKCAT_FLAG_ENUM lclflags,
 
     if (addr + read_num_units - 1 > fs->last_block) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
-            "tsk_fs_blkcat: requested size is larger than last block in image (%"
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr("tsk_fs_blkcat: requested size is larger than last block in image (%"
             PRIuDADDR ")", fs->last_block);
         return 1;
     }
@@ -78,8 +77,8 @@ tsk_fs_blkcat(TSK_FS_INFO * fs, TSK_FS_BLKCAT_FLAG_ENUM lclflags,
 #ifdef TSK_WIN32
     if (-1 == _setmode(_fileno(stdout), _O_BINARY)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WRITE;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WRITE);
+        tsk_error_set_errstr(
             "blkcat_lib: error setting stdout to binary: %s",
             strerror(errno));
         return 1;
@@ -109,10 +108,9 @@ tsk_fs_blkcat(TSK_FS_INFO * fs, TSK_FS_BLKCAT_FLAG_ENUM lclflags,
         if (cnt != fs->block_size) {
             if (cnt >= 0) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_READ;
+                tsk_error_set_errno(TSK_ERR_FS_READ);
             }
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
-                "blkcat: Error reading block at %" PRIuDADDR, addr);
+            tsk_error_set_errstr("blkcat: Error reading block at %" PRIuDADDR, addr);
             return 1;
         }
 
@@ -200,8 +198,8 @@ tsk_fs_blkcat(TSK_FS_INFO * fs, TSK_FS_BLKCAT_FLAG_ENUM lclflags,
         else {
             if (fwrite(buf, fs->block_size, 1, stdout) != 1) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_WRITE;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_FS_WRITE);
+                tsk_error_set_errstr(
                     "blkcat_lib: error writing to stdout: %s",
                     strerror(errno));
                 free(buf);
diff --git a/tsk3/fs/dls_lib.c b/tsk3/fs/dls_lib.c
index 323589c58..dd84cb9f7 100644
--- a/tsk3/fs/dls_lib.c
+++ b/tsk3/fs/dls_lib.c
@@ -2,7 +2,7 @@
 ** The Sleuth Kit
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved 
 **
 ** TASK
@@ -89,8 +89,8 @@ print_block(const TSK_FS_BLOCK * fs_block, void *ptr)
     if (fwrite(fs_block->buf, fs_block->fs_info->block_size, 1,
             stdout) != 1) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WRITE;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WRITE);
+        tsk_error_set_errstr(
             "blkls_lib: error writing to stdout: %s", strerror(errno));
         return TSK_WALK_ERROR;
     }
@@ -129,8 +129,8 @@ slack_file_act(TSK_FS_FILE * fs_file, TSK_OFF_T a_off, TSK_DADDR_T addr,
     else if (data->flen == 0) {
         if (fwrite(buf, size, 1, stdout) != 1) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_WRITE;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_WRITE);
+            tsk_error_set_errstr(
                 "blkls_lib: error writing to stdout: %s", strerror(errno));
             return TSK_WALK_ERROR;
         }
@@ -141,8 +141,8 @@ slack_file_act(TSK_FS_FILE * fs_file, TSK_OFF_T a_off, TSK_DADDR_T addr,
         memset(buf, 0, (size_t) data->flen);
         if (fwrite(buf, size, 1, stdout) != 1) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_WRITE;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_WRITE);
+            tsk_error_set_errstr(
                 "blkls_lib: error writing to stdout: %s", strerror(errno));
             return TSK_WALK_ERROR;
         }
@@ -219,7 +219,7 @@ tsk_fs_blkls(TSK_FS_INFO * fs, TSK_FS_BLKLS_FLAG_ENUM a_blklsflags,
     if (a_blklsflags & TSK_FS_BLKLS_SLACK) {
         /* get the info on each allocated inode */
         if (fs->inode_walk(fs, fs->first_inum, fs->last_inum,
-                TSK_FS_META_FLAG_ALLOC, slack_inode_act, &data))
+                TSK_FS_META_FLAG_ALLOC,slack_inode_act, &data))
             return 1;
     }
     else if (a_blklsflags & TSK_FS_BLKLS_LIST) {
@@ -234,8 +234,8 @@ tsk_fs_blkls(TSK_FS_INFO * fs, TSK_FS_BLKLS_FLAG_ENUM a_blklsflags,
 #ifdef TSK_WIN32
         if (-1 == _setmode(_fileno(stdout), _O_BINARY)) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_WRITE;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_WRITE);
+            tsk_error_set_errstr(
                 "blkls_lib: error setting stdout to binary: %s",
                 strerror(errno));
             return 1;
diff --git a/tsk3/fs/dstat_lib.c b/tsk3/fs/dstat_lib.c
index f482be2ba..427320766 100644
--- a/tsk3/fs/dstat_lib.c
+++ b/tsk3/fs/dstat_lib.c
@@ -5,7 +5,7 @@
 ** Get the details about a data unit
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved 
 **
 ** TASK
diff --git a/tsk3/fs/ext2fs.c b/tsk3/fs/ext2fs.c
index 3061d823d..7d56b2580 100644
--- a/tsk3/fs/ext2fs.c
+++ b/tsk3/fs/ext2fs.c
@@ -1,14 +1,14 @@
 /*
-** The Sleuth Kit 
+** The Sleuth Kit
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
-** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved 
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
 **
 ** TASK
 ** Copyright (c) 2002-2003 Brian Carrier, @stake Inc.  All rights reserved
-** 
-** Copyright (c) 1997,1998,1999, International Business Machines          
+**
+** Copyright (c) 1997,1998,1999, International Business Machines
 ** Corporation and others. All Rights Reserved.
 */
 
@@ -17,7 +17,7 @@
  * Contains the internal TSK ext2/ext3 file system functions.
  */
 
-/* TCT 
+/* TCT
  * LICENSE
  *	This software is distributed under the IBM Public License.
  * AUTHOR(S)
@@ -31,7 +31,9 @@
 #include "tsk_ext2fs.h"
 
 
-/* ext2fs_group_load - load block group descriptor into cache 
+/* ext2fs_group_load - load block group descriptor into cache
+ *
+ * Note: This routine assumes &ext2fs->lock is locked by the caller.
  *
  * return 1 on error and 0 on success
  *
@@ -49,8 +51,8 @@ ext2fs_group_load(EXT2FS_INFO * ext2fs, EXT2_GRPNUM_T grp_num)
      */
     if (grp_num < 0 || grp_num >= ext2fs->groups_count) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "ext2fs_group_load: invalid cylinder group number: %"
             PRI_EXT2GRP "", grp_num);
         return 1;
@@ -79,9 +81,9 @@ ext2fs_group_load(EXT2FS_INFO * ext2fs, EXT2_GRPNUM_T grp_num)
     if (cnt != sizeof(ext2fs_gd)) {
         if (cnt >= 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_READ;
+            tsk_error_set_errno(TSK_ERR_FS_READ);
         }
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
+        tsk_error_set_errstr2(
             "ext2fs_group_load: Group descriptor %" PRI_EXT2GRP " at %"
             PRIuOFF, grp_num, offs);
         return 1;
@@ -95,8 +97,8 @@ ext2fs_group_load(EXT2FS_INFO * ext2fs, EXT2_GRPNUM_T grp_num)
                 gd->bg_inode_bitmap) > fs->last_block) ||
         (tsk_getu32(fs->endian, gd->bg_inode_table) > fs->last_block)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_CORRUPT;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_CORRUPT);
+        tsk_error_set_errstr(
             "extXfs_group_load: Group %" PRI_EXT2GRP
             " descriptor block locations too large at byte offset %"
             PRIuDADDR, grp_num, offs);
@@ -133,8 +135,13 @@ ext2fs_print_map(uint8_t * map, int len)
     putc('\n', stderr);
 }
 
+#define INODE_TABLE_SIZE(ext2fs) \
+    ((tsk_getu32(ext2fs->fs_info.endian, ext2fs->fs->s_inodes_per_group) * ext2fs->inode_size - 1) \
+           / ext2fs->fs_info.block_size + 1)
 
-/* ext2fs_bmap_load - look up block bitmap & load into cache 
+/* ext2fs_bmap_load - look up block bitmap & load into cache
+ *
+ * Note: This routine assumes &ext2fs->lock is locked by the caller.
  *
  * return 1 on error and 0 on success
  * */
@@ -147,10 +154,8 @@ ext2fs_bmap_load(EXT2FS_INFO * ext2fs, EXT2_GRPNUM_T grp_num)
     /*
      * Look up the group descriptor info.  The load will do the sanity check.
      */
-    if ((ext2fs->grp_buf == NULL) || (ext2fs->grp_num != grp_num)) {
-        if (ext2fs_group_load(ext2fs, grp_num)) {
-            return 1;
-        }
+    if (ext2fs_group_load(ext2fs, grp_num)) {
+        return 1;
     }
 
     if (ext2fs->bmap_buf == NULL) {
@@ -159,8 +164,31 @@ ext2fs_bmap_load(EXT2FS_INFO * ext2fs, EXT2_GRPNUM_T grp_num)
             return 1;
         }
     }
-    else if (ext2fs->bmap_grp_num == grp_num)
+    else if (ext2fs->bmap_grp_num == grp_num) {
         return 0;
+    }
+
+    if (tsk_verbose) {
+        TSK_DADDR_T dbase = 0;      /* first block number in group */
+        TSK_DADDR_T dmin = 0;       /* first block after inodes */
+
+        dbase = ext2_cgbase_lcl(fs, ext2fs->fs, grp_num);
+        dmin = tsk_getu32(fs->endian,
+            ext2fs->grp_buf->bg_inode_table) + INODE_TABLE_SIZE(ext2fs);
+
+        tsk_fprintf(stderr,
+                "ext2_block_walk: loading group %" PRI_EXT2GRP
+                " dbase %" PRIuDADDR " bmap +%" PRIuDADDR
+                " imap +%" PRIuDADDR " inos +%" PRIuDADDR "..%"
+                PRIuDADDR "\n", grp_num, dbase,
+                (TSK_DADDR_T) tsk_getu32(fs->endian,
+                    ext2fs->grp_buf->bg_block_bitmap)
+                - dbase, (TSK_DADDR_T) tsk_getu32(fs->endian,
+                    ext2fs->grp_buf->bg_inode_bitmap) - dbase,
+                (TSK_DADDR_T) tsk_getu32(fs->endian,
+                    ext2fs->grp_buf->bg_inode_table) - dbase,
+                dmin - 1 - dbase);
+    }
 
     /*
      * Look up the block allocation bitmap.
@@ -168,8 +196,8 @@ ext2fs_bmap_load(EXT2FS_INFO * ext2fs, EXT2_GRPNUM_T grp_num)
     if (tsk_getu32(fs->endian,
             ext2fs->grp_buf->bg_block_bitmap) > fs->last_block) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_BLK_NUM;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_BLK_NUM);
+        tsk_error_set_errstr(
             "ext2fs_bmap_load: Block too large for image: %" PRIu32
             "", tsk_getu32(fs->endian, ext2fs->grp_buf->bg_block_bitmap));
         return 1;
@@ -182,9 +210,9 @@ ext2fs_bmap_load(EXT2FS_INFO * ext2fs, EXT2_GRPNUM_T grp_num)
     if (cnt != ext2fs->fs_info.block_size) {
         if (cnt >= 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_READ;
+            tsk_error_set_errno(TSK_ERR_FS_READ);
         }
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
+        tsk_error_set_errstr2(
             "ext2fs_bmap_load: Bitmap group %" PRI_EXT2GRP " at %"
             PRIu32, grp_num, tsk_getu32(fs->endian,
                 ext2fs->grp_buf->bg_block_bitmap));
@@ -200,7 +228,9 @@ ext2fs_bmap_load(EXT2FS_INFO * ext2fs, EXT2_GRPNUM_T grp_num)
 }
 
 
-/* ext2fs_imap_load - look up inode bitmap & load into cache 
+/* ext2fs_imap_load - look up inode bitmap & load into cache
+ *
+ * Note: This routine assumes &ext2fs->lock is locked by the caller.
  *
  * return 0 on success and 1 on error
  * */
@@ -213,10 +243,8 @@ ext2fs_imap_load(EXT2FS_INFO * ext2fs, EXT2_GRPNUM_T grp_num)
     /*
      * Look up the group descriptor info.
      */
-    if ((ext2fs->grp_buf == NULL) || (ext2fs->grp_num != grp_num)) {
-        if (ext2fs_group_load(ext2fs, grp_num)) {
-            return 1;
-        }
+    if (ext2fs_group_load(ext2fs, grp_num)) {
+        return 1;
     }
 
     /* Allocate the cache buffer and exit if map is already loaded */
@@ -236,8 +264,8 @@ ext2fs_imap_load(EXT2FS_INFO * ext2fs, EXT2_GRPNUM_T grp_num)
     if (tsk_getu32(fs->endian,
             ext2fs->grp_buf->bg_inode_bitmap) > fs->last_block) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_BLK_NUM;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_BLK_NUM);
+        tsk_error_set_errstr(
             "ext2fs_imap_load: Block too large for image: %" PRIu32
             "", tsk_getu32(fs->endian, ext2fs->grp_buf->bg_inode_bitmap));
     }
@@ -250,9 +278,9 @@ ext2fs_imap_load(EXT2FS_INFO * ext2fs, EXT2_GRPNUM_T grp_num)
     if (cnt != ext2fs->fs_info.block_size) {
         if (cnt >= 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_READ;
+            tsk_error_set_errno(TSK_ERR_FS_READ);
         }
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
+        tsk_error_set_errstr2(
             "ext2fs_imap_load: Inode bitmap %" PRI_EXT2GRP " at %"
             PRIu32, grp_num, tsk_getu32(fs->endian,
                 ext2fs->grp_buf->bg_inode_bitmap));
@@ -266,15 +294,17 @@ ext2fs_imap_load(EXT2FS_INFO * ext2fs, EXT2_GRPNUM_T grp_num)
     return 0;
 }
 
-/* ext2fs_dinode_load - look up disk inode & load into cache 
+/* ext2fs_dinode_load - look up disk inode & load into ext2fs_inode structure 
+ * @param ext2fs A ext2fs file system information structure
+ * @param dino_inum Metadata address
+ * @param dino_buf The buffer to store the block in (must be size of ext2fs->inode_size or larger)
  *
  * return 1 on error and 0 on success
  * */
 
 static uint8_t
-ext2fs_dinode_load(EXT2FS_INFO * ext2fs, TSK_INUM_T inum)
+ext2fs_dinode_load(EXT2FS_INFO * ext2fs, TSK_INUM_T dino_inum, ext2fs_inode * dino_buf)
 {
-    ext2fs_inode *dino;
     EXT2_GRPNUM_T grp_num;
     TSK_OFF_T addr;
     ssize_t cnt;
@@ -285,102 +315,100 @@ ext2fs_dinode_load(EXT2FS_INFO * ext2fs, TSK_INUM_T inum)
      * Sanity check.
      * Use last_num-1 to account for virtual Orphan directory in last_inum.
      */
-    if ((inum < fs->first_inum) || (inum > fs->last_inum - 1)) {
+    if ((dino_inum < fs->first_inum) || (dino_inum > fs->last_inum - 1)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_INODE_NUM;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
-            "ext2fs_dinode_load: address: %" PRIuINUM, inum);
+        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
+        tsk_error_set_errstr(
+            "ext2fs_dinode_load: address: %" PRIuINUM, dino_inum);
         return 1;
     }
 
-    /* Allocate the buffer or return if already loaded */
-    if (ext2fs->dino_buf == NULL) {
-        if ((ext2fs->dino_buf =
-                (ext2fs_inode *) tsk_malloc(ext2fs->inode_size)) == NULL) {
-            return 1;
-        }
-    }
-    else if (ext2fs->dino_inum == inum) {
-        return 0;
+    if (dino_buf == NULL){
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
+            "ext2fs_dinode_load: dino_buf is NULL");
+        return 1;
     }
 
-    dino = ext2fs->dino_buf;
-
     /*
-     * Look up the group descriptor for this inode. 
+     * Look up the group descriptor for this inode.
      */
-    grp_num = (EXT2_GRPNUM_T) ((inum - fs->first_inum) /
+    grp_num = (EXT2_GRPNUM_T) ((dino_inum - fs->first_inum) /
         tsk_getu32(fs->endian, ext2fs->fs->s_inodes_per_group));
 
-    if ((ext2fs->grp_buf == NULL) || (ext2fs->grp_num != grp_num)) {
-        if (ext2fs_group_load(ext2fs, grp_num)) {
-            return 1;
-        }
+    /* lock access to grp_buf */
+    tsk_take_lock(&ext2fs->lock);
+
+    if (ext2fs_group_load(ext2fs, grp_num)) {
+        tsk_release_lock(&ext2fs->lock);
+        return 1;
     }
 
     /*
      * Look up the inode table block for this inode.
      */
     rel_inum =
-        (inum - 1) - tsk_getu32(fs->endian,
+        (dino_inum - 1) - tsk_getu32(fs->endian,
         ext2fs->fs->s_inodes_per_group) * grp_num;
     addr =
         (TSK_OFF_T) tsk_getu32(fs->endian,
         ext2fs->grp_buf->bg_inode_table) * (TSK_OFF_T) fs->block_size +
         rel_inum * (TSK_OFF_T) ext2fs->inode_size;
 
-    cnt = tsk_fs_read(fs, addr, (char *) dino, ext2fs->inode_size);
+    tsk_release_lock(&ext2fs->lock);
+
+    cnt = tsk_fs_read(fs, addr, (char *) dino_buf, ext2fs->inode_size);
     if (cnt != ext2fs->inode_size) {
         if (cnt >= 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_READ;
+            tsk_error_set_errno(TSK_ERR_FS_READ);
         }
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
+        tsk_error_set_errstr2(
             "ext2fs_dinode_load: Inode %" PRIuINUM
-            " from %" PRIuOFF, inum, addr);
+            " from %" PRIuOFF, dino_inum, addr);
         return 1;
     }
 
-    ext2fs->dino_inum = inum;
-    if (tsk_verbose)
+    if (tsk_verbose) {
         tsk_fprintf(stderr,
             "%" PRIuINUM " m/l/s=%o/%d/%" PRIuOFF
             " u/g=%d/%d macd=%" PRIu32 "/%" PRIu32 "/%" PRIu32 "/%" PRIu32
-            "\n", inum, tsk_getu16(fs->endian, dino->i_mode),
-            tsk_getu16(fs->endian, dino->i_nlink), (tsk_getu32(fs->endian,
-                    dino->i_size) + (tsk_getu16(fs->endian,
-                        dino->i_mode) & EXT2_IN_REG) ? (uint64_t)
-                tsk_getu32(fs->endian, dino->i_size_high) << 32 : 0),
-            tsk_getu16(fs->endian, dino->i_uid) + (tsk_getu16(fs->endian,
-                    dino->i_uid_high) << 16), tsk_getu16(fs->endian,
-                dino->i_gid) + (tsk_getu16(fs->endian,
-                    dino->i_gid_high) << 16), tsk_getu32(fs->endian,
-                dino->i_mtime), tsk_getu32(fs->endian, dino->i_atime),
-            tsk_getu32(fs->endian, dino->i_ctime), tsk_getu32(fs->endian,
-                dino->i_dtime));
+            "\n", dino_inum, tsk_getu16(fs->endian, dino_buf->i_mode),
+            tsk_getu16(fs->endian, dino_buf->i_nlink), (tsk_getu32(fs->endian,
+                    dino_buf->i_size) + (tsk_getu16(fs->endian,
+                        dino_buf->i_mode) & EXT2_IN_REG) ? (uint64_t)
+                tsk_getu32(fs->endian, dino_buf->i_size_high) << 32 : 0),
+            tsk_getu16(fs->endian, dino_buf->i_uid) + (tsk_getu16(fs->endian,
+                    dino_buf->i_uid_high) << 16), tsk_getu16(fs->endian,
+                dino_buf->i_gid) + (tsk_getu16(fs->endian,
+                    dino_buf->i_gid_high) << 16), tsk_getu32(fs->endian,
+                dino_buf->i_mtime), tsk_getu32(fs->endian, dino_buf->i_atime),
+            tsk_getu32(fs->endian, dino_buf->i_ctime), tsk_getu32(fs->endian,
+                dino_buf->i_dtime));
+    }
 
     return 0;
 }
 
-/* ext2fs_dinode_copy - copy cached disk inode into generic inode 
+/* ext2fs_dinode_copy - copy cached disk inode into generic inode
  *
  * returns 1 on error and 0 on success
  * */
 static uint8_t
-ext2fs_dinode_copy(EXT2FS_INFO * ext2fs, TSK_FS_META * fs_meta)
+ext2fs_dinode_copy(EXT2FS_INFO * ext2fs, TSK_FS_META * fs_meta, TSK_INUM_T inum, const ext2fs_inode * dino_buf)
 {
     int i;
-    ext2fs_inode *in = ext2fs->dino_buf;
     TSK_FS_INFO *fs = (TSK_FS_INFO *) & ext2fs->fs_info;
     ext2fs_sb *sb = ext2fs->fs;
     EXT2_GRPNUM_T grp_num;
     TSK_INUM_T ibase = 0;
     TSK_DADDR_T *addr_ptr;
 
-    if (ext2fs->dino_buf == NULL) {
+    if (dino_buf == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "ext2fs_dinode_copy: dino_buf is NULL");
         return 1;
     }
@@ -391,7 +419,7 @@ ext2fs_dinode_copy(EXT2FS_INFO * ext2fs, TSK_FS_META * fs_meta)
     }
 
     // set the type
-    switch (tsk_getu16(fs->endian, in->i_mode) & EXT2_IN_FMT) {
+    switch (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_FMT) {
     case EXT2_IN_REG:
         fs_meta->type = TSK_FS_META_TYPE_REG;
         break;
@@ -420,43 +448,43 @@ ext2fs_dinode_copy(EXT2FS_INFO * ext2fs, TSK_FS_META * fs_meta)
 
     // set the mode
     fs_meta->mode = 0;
-    if (tsk_getu16(fs->endian, in->i_mode) & EXT2_IN_ISUID)
+    if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_ISUID)
         fs_meta->mode |= TSK_FS_META_MODE_ISUID;
-    if (tsk_getu16(fs->endian, in->i_mode) & EXT2_IN_ISGID)
+    if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_ISGID)
         fs_meta->mode |= TSK_FS_META_MODE_ISGID;
-    if (tsk_getu16(fs->endian, in->i_mode) & EXT2_IN_ISVTX)
+    if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_ISVTX)
         fs_meta->mode |= TSK_FS_META_MODE_ISVTX;
 
-    if (tsk_getu16(fs->endian, in->i_mode) & EXT2_IN_IRUSR)
+    if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_IRUSR)
         fs_meta->mode |= TSK_FS_META_MODE_IRUSR;
-    if (tsk_getu16(fs->endian, in->i_mode) & EXT2_IN_IWUSR)
+    if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_IWUSR)
         fs_meta->mode |= TSK_FS_META_MODE_IWUSR;
-    if (tsk_getu16(fs->endian, in->i_mode) & EXT2_IN_IXUSR)
+    if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_IXUSR)
         fs_meta->mode |= TSK_FS_META_MODE_IXUSR;
 
-    if (tsk_getu16(fs->endian, in->i_mode) & EXT2_IN_IRGRP)
+    if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_IRGRP)
         fs_meta->mode |= TSK_FS_META_MODE_IRGRP;
-    if (tsk_getu16(fs->endian, in->i_mode) & EXT2_IN_IWGRP)
+    if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_IWGRP)
         fs_meta->mode |= TSK_FS_META_MODE_IWGRP;
-    if (tsk_getu16(fs->endian, in->i_mode) & EXT2_IN_IXGRP)
+    if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_IXGRP)
         fs_meta->mode |= TSK_FS_META_MODE_IXGRP;
 
-    if (tsk_getu16(fs->endian, in->i_mode) & EXT2_IN_IROTH)
+    if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_IROTH)
         fs_meta->mode |= TSK_FS_META_MODE_IROTH;
-    if (tsk_getu16(fs->endian, in->i_mode) & EXT2_IN_IWOTH)
+    if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_IWOTH)
         fs_meta->mode |= TSK_FS_META_MODE_IWOTH;
-    if (tsk_getu16(fs->endian, in->i_mode) & EXT2_IN_IXOTH)
+    if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_IXOTH)
         fs_meta->mode |= TSK_FS_META_MODE_IXOTH;
 
-    fs_meta->nlink = tsk_getu16(fs->endian, in->i_nlink);
+    fs_meta->nlink = tsk_getu16(fs->endian, dino_buf->i_nlink);
 
-    fs_meta->size = tsk_getu32(fs->endian, in->i_size);
+    fs_meta->size = tsk_getu32(fs->endian, dino_buf->i_size);
 
-    fs_meta->addr = ext2fs->dino_inum;
+    fs_meta->addr = inum;
 
     /* the general size value in the inode is only 32-bits,
-     * but the i_dir_acl value is used for regular files to 
-     * hold the upper 32-bits 
+     * but the i_dir_acl value is used for regular files to
+     * hold the upper 32-bits
      *
      * The RO_COMPAT_LARGE_FILE flag in the super block will identify
      * if there are any large files in the file system
@@ -465,20 +493,20 @@ ext2fs_dinode_copy(EXT2FS_INFO * ext2fs, TSK_FS_META * fs_meta)
         (tsk_getu32(fs->endian, sb->s_feature_ro_compat) &
             EXT2FS_FEATURE_RO_COMPAT_LARGE_FILE)) {
         fs_meta->size +=
-            ((uint64_t) tsk_getu32(fs->endian, in->i_size_high) << 32);
+            ((uint64_t) tsk_getu32(fs->endian, dino_buf->i_size_high) << 32);
     }
 
     fs_meta->uid =
-        tsk_getu16(fs->endian, in->i_uid) + (tsk_getu16(fs->endian,
-            in->i_uid_high) << 16);
+        tsk_getu16(fs->endian, dino_buf->i_uid) + (tsk_getu16(fs->endian,
+            dino_buf->i_uid_high) << 16);
     fs_meta->gid =
-        tsk_getu16(fs->endian, in->i_gid) + (tsk_getu16(fs->endian,
-            in->i_gid_high) << 16);
-    fs_meta->mtime = tsk_getu32(fs->endian, in->i_mtime);
-    fs_meta->atime = tsk_getu32(fs->endian, in->i_atime);
-    fs_meta->ctime = tsk_getu32(fs->endian, in->i_ctime);
+        tsk_getu16(fs->endian, dino_buf->i_gid) + (tsk_getu16(fs->endian,
+            dino_buf->i_gid_high) << 16);
+    fs_meta->mtime = tsk_getu32(fs->endian, dino_buf->i_mtime);
+    fs_meta->atime = tsk_getu32(fs->endian, dino_buf->i_atime);
+    fs_meta->ctime = tsk_getu32(fs->endian, dino_buf->i_ctime);
     fs_meta->crtime = 0;
-    fs_meta->time2.ext2.dtime = tsk_getu32(fs->endian, in->i_dtime);
+    fs_meta->time2.ext2.dtime = tsk_getu32(fs->endian, dino_buf->i_dtime);
     fs_meta->mtime_nano = fs_meta->atime_nano = fs_meta->ctime_nano =
         fs_meta->crtime_nano = 0;
     fs_meta->time2.ext2.dtime_nano = 0;
@@ -499,9 +527,9 @@ ext2fs_dinode_copy(EXT2FS_INFO * ext2fs, TSK_FS_META * fs_meta)
 
     addr_ptr = (TSK_DADDR_T *) fs_meta->content_ptr;
     for (i = 0; i < EXT2FS_NDADDR + EXT2FS_NIADDR; i++)
-        addr_ptr[i] = tsk_gets32(fs->endian, in->i_block[i]);
+        addr_ptr[i] = tsk_gets32(fs->endian, dino_buf->i_block[i]);
 
-    /* set the link string 
+    /* set the link string
      * the size check prevents us from trying to allocate a huge amount of
      * memory for a bad inode value
      */
@@ -520,7 +548,7 @@ ext2fs_dinode_copy(EXT2FS_INFO * ext2fs, TSK_FS_META * fs_meta)
 
             for (i = 0; i < (EXT2FS_NDADDR + EXT2FS_NIADDR) &&
                 count < fs_meta->size; i++) {
-                char *a_ptr = (char *) &in->i_block[i];
+                char *a_ptr = (char *) &dino_buf->i_block[i];
                 for (j = 0; j < 4 && count < fs_meta->size; j++) {
                     fs_meta->link[count++] = a_ptr[j];
                 }
@@ -534,14 +562,15 @@ ext2fs_dinode_copy(EXT2FS_INFO * ext2fs, TSK_FS_META * fs_meta)
         /* it is in blocks */
         else {
             TSK_FS_INFO *fs = (TSK_FS_INFO *) & ext2fs->fs_info;
-            char *data_buf;
+            char *data_buf = NULL;
             char *a_ptr = fs_meta->link;
             TSK_DADDR_T *addr_ptr = fs_meta->content_ptr;;
 
-            if ((data_buf = tsk_malloc(fs->block_size)) == NULL)
+            if ((data_buf = tsk_malloc(fs->block_size)) == NULL){
                 return 1;
+            }
 
-            /* we only need to do the direct blocks due to the limit 
+            /* we only need to do the direct blocks due to the limit
              * on path length */
             for (i = 0; i < EXT2FS_NDADDR && count < fs_meta->size; i++) {
                 ssize_t cnt;
@@ -557,9 +586,9 @@ ext2fs_dinode_copy(EXT2FS_INFO * ext2fs, TSK_FS_META * fs_meta)
                 if (cnt != fs->block_size) {
                     if (cnt >= 0) {
                         tsk_error_reset();
-                        tsk_errno = TSK_ERR_FS_READ;
+                        tsk_error_set_errno(TSK_ERR_FS_READ);
                     }
-                    snprintf(tsk_errstr2, TSK_ERRSTR_L,
+                    tsk_error_set_errstr2(
                         "ext2fs_dinode_copy: symlink destination from %"
                         PRIuDADDR, addr_ptr[i]);
                     free(data_buf);
@@ -586,13 +615,15 @@ ext2fs_dinode_copy(EXT2FS_INFO * ext2fs, TSK_FS_META * fs_meta)
     }
 
     /* Fill in the flags value */
-    grp_num = (EXT2_GRPNUM_T) ((ext2fs->dino_inum - fs->first_inum) /
+    grp_num = (EXT2_GRPNUM_T) ((inum - fs->first_inum) /
         tsk_getu32(fs->endian, ext2fs->fs->s_inodes_per_group));
 
-    if (ext2fs->imap_grp_num != grp_num) {
-        if (ext2fs_imap_load(ext2fs, grp_num)) {
-            return 1;
-        }
+
+    tsk_take_lock(&ext2fs->lock);
+
+    if (ext2fs_imap_load(ext2fs, grp_num)) {
+        tsk_release_lock(&ext2fs->lock);
+        return 1;
     }
 
     ibase =
@@ -602,9 +633,12 @@ ext2fs_dinode_copy(EXT2FS_INFO * ext2fs, TSK_FS_META * fs_meta)
     /*
      * Apply the allocated/unallocated restriction.
      */
-    fs_meta->flags = (isset(ext2fs->imap_buf, ext2fs->dino_inum - ibase) ?
+    fs_meta->flags = (isset(ext2fs->imap_buf, inum - ibase) ?
         TSK_FS_META_FLAG_ALLOC : TSK_FS_META_FLAG_UNALLOC);
 
+    tsk_release_lock(&ext2fs->lock);
+
+
     /*
      * Apply the used/unused restriction.
      */
@@ -616,10 +650,10 @@ ext2fs_dinode_copy(EXT2FS_INFO * ext2fs, TSK_FS_META * fs_meta)
 
 
 
-/* ext2fs_inode_lookup - lookup inode, external interface 
+/* ext2fs_inode_lookup - lookup inode, external interface
  *
  * Returns 1 on error and 0 on success
- * 
+ *
  */
 
 static uint8_t
@@ -627,10 +661,11 @@ ext2fs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file,
     TSK_INUM_T inum)
 {
     EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) fs;
+    ext2fs_inode *dino_buf = NULL;
 
     if (a_fs_file == NULL) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "ext2fs_inode_lookup: fs_file is NULL");
         return 1;
     }
@@ -652,20 +687,30 @@ ext2fs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file,
             return 0;
     }
 
-    if (ext2fs_dinode_load(ext2fs, inum)) {
+    if ( (dino_buf = (ext2fs_inode *) tsk_malloc(ext2fs->inode_size)) == NULL) {
         return 1;
     }
 
-    if (ext2fs_dinode_copy(ext2fs, a_fs_file->meta)) {
+    if (ext2fs_dinode_load(ext2fs, inum, dino_buf)) {
+        tsk_fs_meta_close(a_fs_file->meta);
+        free(dino_buf);
         return 1;
     }
 
+    if (ext2fs_dinode_copy(ext2fs, a_fs_file->meta, inum, dino_buf)) {
+        tsk_fs_meta_close(a_fs_file->meta);
+        free(dino_buf);
+        return 1;
+    }
+
+    if (dino_buf != NULL)
+        free((char *)dino_buf);
     return 0;
 }
 
 
 
-/* ext2fs_inode_walk - inode iterator 
+/* ext2fs_inode_walk - inode iterator
  *
  * flags used: TSK_FS_META_FLAG_USED, TSK_FS_META_FLAG_UNUSED,
  *  TSK_FS_META_FLAG_ALLOC, TSK_FS_META_FLAG_UNALLOC, TSK_FS_META_FLAG_ORPHAN
@@ -686,6 +731,7 @@ ext2fs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
     TSK_INUM_T ibase = 0;
     TSK_FS_FILE *fs_file;
     int myflags;
+    ext2fs_inode * dino_buf = NULL;
 
     // clean up any error messages that are lying around
     tsk_error_reset();
@@ -695,8 +741,8 @@ ext2fs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
      */
     if (start_inum < fs->first_inum || start_inum > fs->last_inum) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "%s: start inode: %" PRIuINUM "", myname, start_inum);
         return 1;
     }
@@ -704,8 +750,8 @@ ext2fs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
     if (end_inum < fs->first_inum || end_inum > fs->last_inum
         || end_inum < start_inum) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "%s: end inode: %" PRIuINUM "", myname, end_inum);
         return 1;
     }
@@ -738,13 +784,10 @@ ext2fs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
      * in the list of unalloc inodes that are pointed to, then fill
      * in the list
      */
-    if ((flags & TSK_FS_META_FLAG_ORPHAN)
-        && (fs->list_inum_named == NULL)) {
-
+    if ((flags & TSK_FS_META_FLAG_ORPHAN)) {
         if (tsk_fs_dir_load_inum_named(fs) != TSK_OK) {
-            strncat(tsk_errstr2,
-                " - ext2fs_inode_walk: identifying inodes allocated by file names",
-                TSK_ERRSTR_L);
+            tsk_error_errstr2_concat(
+                "- ext2fs_inode_walk: identifying inodes allocated by file names");
             return 1;
         }
 
@@ -756,7 +799,7 @@ ext2fs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
             tsk_fs_meta_alloc(EXT2FS_FILE_CONTENT_LEN)) == NULL)
         return 1;
 
-    // we need to handle fs->last_inum specially because it is for the 
+    // we need to handle fs->last_inum specially because it is for the
     // virtual ORPHANS directory.  Handle it outside of the loop.
     if (end_inum == TSK_FS_ORPHANDIR_INUM(fs))
         end_inum_tmp = end_inum - 1;
@@ -766,6 +809,10 @@ ext2fs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
     /*
      * Iterate.
      */
+    if ( (dino_buf = (ext2fs_inode *) tsk_malloc(ext2fs->inode_size)) == NULL) {
+        return 1;
+    }
+
     for (inum = start_inum; inum <= end_inum_tmp; inum++) {
         int retval;
 
@@ -776,33 +823,33 @@ ext2fs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
         grp_num =
             (EXT2_GRPNUM_T) ((inum - 1) / tsk_getu32(fs->endian,
                 ext2fs->fs->s_inodes_per_group));
-        if ((ext2fs->imap_buf == NULL)
-            || (ext2fs->imap_grp_num != grp_num)) {
-            if (ext2fs_imap_load(ext2fs, grp_num)) {
-                return 1;
-            }
-            ibase =
-                grp_num * tsk_getu32(fs->endian,
-                ext2fs->fs->s_inodes_per_group) + 1;
-        }
 
-        /* In case we didn't need to load it the bitmap */
-        else if (inum == start_inum) {
-            ibase =
-                grp_num * tsk_getu32(fs->endian,
-                ext2fs->fs->s_inodes_per_group) + 1;
+        /* lock access to imap_buf */
+        tsk_take_lock(&ext2fs->lock);
+
+        if (ext2fs_imap_load(ext2fs, grp_num)) {
+            tsk_release_lock(&ext2fs->lock);
+            free(dino_buf);
+            return 1;
         }
+        ibase =
+            grp_num * tsk_getu32(fs->endian,
+            ext2fs->fs->s_inodes_per_group) + 1;
 
         /*
          * Apply the allocated/unallocated restriction.
          */
         myflags = (isset(ext2fs->imap_buf, inum - ibase) ?
             TSK_FS_META_FLAG_ALLOC : TSK_FS_META_FLAG_UNALLOC);
+
+        tsk_release_lock(&ext2fs->lock);
+
         if ((flags & myflags) != myflags)
             continue;
 
-        if (ext2fs_dinode_load(ext2fs, inum)) {
+        if (ext2fs_dinode_load(ext2fs, inum, dino_buf)) {
             tsk_fs_file_close(fs_file);
+            free(dino_buf);
             return 1;
         }
 
@@ -810,7 +857,7 @@ ext2fs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
         /*
          * Apply the used/unused restriction.
          */
-        myflags |= (tsk_getu32(fs->endian, ext2fs->dino_buf->i_ctime) ?
+        myflags |= (tsk_getu32(fs->endian, dino_buf->i_ctime) ?
             TSK_FS_META_FLAG_USED : TSK_FS_META_FLAG_UNUSED);
 
         if ((flags & myflags) != myflags)
@@ -820,8 +867,8 @@ ext2fs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
          * inode is in the seen list
          */
         if ((myflags & TSK_FS_META_FLAG_UNALLOC) &&
-            (flags & TSK_FS_META_FLAG_ORPHAN) &&
-            (tsk_list_find(fs->list_inum_named, inum))) {
+            (flags & TSK_FS_META_FLAG_ORPHAN) &&             
+            (tsk_fs_dir_find_inum_named(fs, inum))) {
             continue;
         }
 
@@ -830,18 +877,21 @@ ext2fs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
          * Fill in a file system-independent inode structure and pass control
          * to the application.
          */
-        if (ext2fs_dinode_copy(ext2fs, fs_file->meta)) {
+        if (ext2fs_dinode_copy(ext2fs, fs_file->meta, inum, dino_buf)) {
             tsk_fs_meta_close(fs_file->meta);
+            free(dino_buf);
             return 1;
         }
 
         retval = a_action(fs_file, a_ptr);
         if (retval == TSK_WALK_STOP) {
             tsk_fs_file_close(fs_file);
+            free(dino_buf);
             return 0;
         }
         else if (retval == TSK_WALK_ERROR) {
             tsk_fs_file_close(fs_file);
+            free(dino_buf);
             return 1;
         }
     }
@@ -854,16 +904,19 @@ ext2fs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
 
         if (tsk_fs_dir_make_orphan_dir_meta(fs, fs_file->meta)) {
             tsk_fs_file_close(fs_file);
+            free(dino_buf);
             return 1;
         }
         /* call action */
         retval = a_action(fs_file, a_ptr);
         if (retval == TSK_WALK_STOP) {
             tsk_fs_file_close(fs_file);
+            free(dino_buf);
             return 0;
         }
         else if (retval == TSK_WALK_ERROR) {
             tsk_fs_file_close(fs_file);
+            free(dino_buf);
             return 1;
         }
     }
@@ -872,6 +925,9 @@ ext2fs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
      * Cleanup.
      */
     tsk_fs_file_close(fs_file);
+    if (dino_buf != NULL)
+        free ((char *) dino_buf);
+
     return 0;
 }
 
@@ -895,27 +951,13 @@ ext2fs_block_getflags(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr)
 
     grp_num = ext2_dtog_lcl(a_fs, ext2fs->fs, a_addr);
 
-    /* Lookup bitmap if not loaded */
-    if ((ext2fs->bmap_buf == NULL)
-        || (ext2fs->bmap_grp_num != grp_num)) {
+    /* lock access to bmap_buf */
+    tsk_take_lock(&ext2fs->lock);
 
-        if (ext2fs_bmap_load(ext2fs, grp_num)) {
-            return 0;
-        }
-
-        if (tsk_verbose)
-            tsk_fprintf(stderr,
-                "ext2_block_walk: loading group %" PRI_EXT2GRP
-                " dbase %" PRIuDADDR " bmap +%" PRIuDADDR
-                " imap +%" PRIuDADDR " inos +%" PRIuDADDR "..%"
-                PRIuDADDR "\n", grp_num, dbase,
-                (TSK_DADDR_T) tsk_getu32(a_fs->endian,
-                    ext2fs->grp_buf->bg_block_bitmap)
-                - dbase, (TSK_DADDR_T) tsk_getu32(a_fs->endian,
-                    ext2fs->grp_buf->bg_inode_bitmap) - dbase,
-                (TSK_DADDR_T) tsk_getu32(a_fs->endian,
-                    ext2fs->grp_buf->bg_inode_table) - dbase,
-                dmin - 1 - dbase);
+    /* Lookup bitmap if not loaded */
+    if (ext2fs_bmap_load(ext2fs, grp_num)) {
+        tsk_release_lock(&ext2fs->lock);
+        return 0;
     }
 
     /*
@@ -926,10 +968,6 @@ ext2fs_block_getflags(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr)
      * Addendum: this offset is controlled by the super block's
      * s_first_data_block field.
      */
-#define INODE_TABLE_SIZE(ext2fs) \
-    ((tsk_getu32(a_fs->endian, ext2fs->fs->s_inodes_per_group) * ext2fs->inode_size - 1) \
-           / a_fs->block_size + 1)
-
     dbase = ext2_cgbase_lcl(a_fs, ext2fs->fs, grp_num);
     dmin =
         tsk_getu32(a_fs->endian,
@@ -969,11 +1007,12 @@ ext2fs_block_getflags(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr)
     else
         flags |= TSK_FS_BLOCK_FLAG_CONT;
 
+    tsk_release_lock(&ext2fs->lock);
     return flags;
 }
 
 
-/* ext2fs_block_walk - block iterator 
+/* ext2fs_block_walk - block iterator
  *
  * flags: TSK_FS_BLOCK_FLAG_ALLOC, TSK_FS_BLOCK_FLAG_UNALLOC, TSK_FS_BLOCK_FLAG_CONT,
  *  TSK_FS_BLOCK_FLAG_META
@@ -998,16 +1037,16 @@ ext2fs_block_walk(TSK_FS_INFO * a_fs, TSK_DADDR_T a_start_blk,
      */
     if (a_start_blk < a_fs->first_block || a_start_blk > a_fs->last_block) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "%s: start block: %" PRIuDADDR, myname, a_start_blk);
         return 1;
     }
     if (a_end_blk < a_fs->first_block || a_end_blk > a_fs->last_block
         || a_end_blk < a_start_blk) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "%s: end block: %" PRIuDADDR, myname, a_end_blk);
         return 1;
     }
@@ -1056,7 +1095,7 @@ ext2fs_block_walk(TSK_FS_INFO * a_fs, TSK_DADDR_T a_start_blk,
             continue;
 
         if (tsk_fs_block_get(a_fs, fs_block, addr) == NULL) {
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
+            tsk_error_set_errstr2(
                 "ext2fs_block_walk: block %" PRIuDADDR, addr);
             tsk_fs_block_free(fs_block);
             return 1;
@@ -1079,25 +1118,23 @@ ext2fs_block_walk(TSK_FS_INFO * a_fs, TSK_DADDR_T a_start_blk,
     return 0;
 }
 
-
-
 static uint8_t
 ext2fs_fscheck(TSK_FS_INFO * fs, FILE * hFile)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr(
         "fscheck not implemented yet for Ext3");
     return 1;
 }
 
 
 /**
- * Print details about the file system to a file handle. 
+ * Print details about the file system to a file handle.
  *
  * @param fs File system to print details on
  * @param hFile File handle to print text to
- * 
+ *
  * @returns 1 on error and 0 on success
  */
 static uint8_t
@@ -1338,7 +1375,11 @@ ext2fs_fsstat(TSK_FS_INFO * fs, FILE * hFile)
         TSK_DADDR_T cg_base;
         TSK_INUM_T inum;
 
+        /* lock access to grp_buf */
+        tsk_take_lock(&ext2fs->lock);
+
         if (ext2fs_group_load(ext2fs, i)) {
+            tsk_release_lock(&ext2fs->lock);
             return 1;
         }
         tsk_fprintf(hFile, "\nGroup: %d:\n", i);
@@ -1369,7 +1410,7 @@ ext2fs_fsstat(TSK_FS_INFO * fs, FILE * hFile)
         tsk_fprintf(hFile, "  Layout:\n");
 
         /* only print the super block data if we are not in a sparse
-         * group 
+         * group
          */
         if (((tsk_getu32(fs->endian, ext2fs->fs->s_feature_ro_compat) &
                     EXT2FS_FEATURE_RO_COMPAT_SPARSE_SUPER) &&
@@ -1505,6 +1546,8 @@ ext2fs_fsstat(TSK_FS_INFO * fs, FILE * hFile)
 
         tsk_fprintf(hFile, "  Total Directories: %" PRIu16 "\n",
             tsk_getu16(fs->endian, ext2fs->grp_buf->bg_used_dirs_count));
+
+        tsk_release_lock(&ext2fs->lock);
     }
 
     return 0;
@@ -1578,14 +1621,14 @@ print_addr_act(TSK_FS_FILE * fs_file, TSK_OFF_T a_off, TSK_DADDR_T addr,
 
 
 /**
- * Print details on a specific file to a file handle. 
+ * Print details on a specific file to a file handle.
  *
  * @param fs File system file is located in
  * @param hFile File handle to print text to
  * @param inum Address of file in file system
  * @param numblock The number of blocks in file to force print (can go beyond file size)
  * @param sec_skew Clock skew in seconds to also print times in
- * 
+ *
  * @returns 1 on error and 0 on success
  */
 static uint8_t
@@ -1598,11 +1641,21 @@ ext2fs_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
     char ls[12];
     EXT2FS_PRINT_ADDR print;
     const TSK_FS_ATTR *fs_attr_indir;
+    ext2fs_inode *dino_buf = NULL;
 
     // clean up any error messages that are lying around
     tsk_error_reset();
+    if ( (dino_buf = (ext2fs_inode *) tsk_malloc(ext2fs->inode_size)) == NULL) {
+        return 1;
+    }
+
+    if (ext2fs_dinode_load(ext2fs, inum, dino_buf)) {
+        free(dino_buf);
+        return 1;
+    }
 
     if ((fs_file = tsk_fs_file_open_meta(fs, NULL, inum)) == NULL) {
+        free(dino_buf);
         return 1;
     }
     fs_meta = fs_file->meta;
@@ -1614,9 +1667,8 @@ ext2fs_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
     tsk_fprintf(hFile, "Group: %" PRIuGID "\n", ext2fs->grp_num);
 
     // Note that if this is a "virtual file", then ext2fs->dino_buf may not be set.
-    if (ext2fs->dino_buf)
         tsk_fprintf(hFile, "Generation Id: %" PRIu32 "\n",
-            tsk_getu32(fs->endian, ext2fs->dino_buf->i_generation));
+            tsk_getu32(fs->endian, dino_buf->i_generation));
 
     if (fs_meta->link)
         tsk_fprintf(hFile, "symbolic link to: %s\n", fs_meta->link);
@@ -1627,64 +1679,61 @@ ext2fs_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
     tsk_fs_meta_make_ls(fs_meta, ls, sizeof(ls));
     tsk_fprintf(hFile, "mode: %s\n", ls);
 
-    if (ext2fs->dino_buf) {
         /* Print the device ids */
         if ((fs_meta->type == TSK_FS_META_TYPE_BLK)
             || (fs_meta->type == TSK_FS_META_TYPE_CHR)) {
             tsk_fprintf(hFile,
                 "Device Major: %" PRIu8 "   Minor: %" PRIu8 "\n",
-                ext2fs->dino_buf->i_block[0][1],
-                ext2fs->dino_buf->i_block[0][0]);
+                dino_buf->i_block[0][1],
+                dino_buf->i_block[0][0]);
         }
 
-        if (tsk_getu32(fs->endian, ext2fs->dino_buf->i_flags)) {
+        if (tsk_getu32(fs->endian, dino_buf->i_flags)) {
             tsk_fprintf(hFile, "Flags: ");
             if (tsk_getu32(fs->endian,
-                    ext2fs->dino_buf->i_flags) & EXT2_IN_SECDEL)
+                    dino_buf->i_flags) & EXT2_IN_SECDEL)
                 tsk_fprintf(hFile, "Secure Delete, ");
 
             if (tsk_getu32(fs->endian,
-                    ext2fs->dino_buf->i_flags) & EXT2_IN_UNRM)
+                    dino_buf->i_flags) & EXT2_IN_UNRM)
                 tsk_fprintf(hFile, "Undelete, ");
 
             if (tsk_getu32(fs->endian,
-                    ext2fs->dino_buf->i_flags) & EXT2_IN_COMP)
+                    dino_buf->i_flags) & EXT2_IN_COMP)
                 tsk_fprintf(hFile, "Compressed, ");
 
             if (tsk_getu32(fs->endian,
-                    ext2fs->dino_buf->i_flags) & EXT2_IN_SYNC)
+                    dino_buf->i_flags) & EXT2_IN_SYNC)
                 tsk_fprintf(hFile, "Sync Updates, ");
 
             if (tsk_getu32(fs->endian,
-                    ext2fs->dino_buf->i_flags) & EXT2_IN_IMM)
+                    dino_buf->i_flags) & EXT2_IN_IMM)
                 tsk_fprintf(hFile, "Immutable, ");
 
             if (tsk_getu32(fs->endian,
-                    ext2fs->dino_buf->i_flags) & EXT2_IN_APPEND)
+                    dino_buf->i_flags) & EXT2_IN_APPEND)
                 tsk_fprintf(hFile, "Append Only, ");
 
             if (tsk_getu32(fs->endian,
-                    ext2fs->dino_buf->i_flags) & EXT2_IN_NODUMP)
+                    dino_buf->i_flags) & EXT2_IN_NODUMP)
                 tsk_fprintf(hFile, "Do Not Dump, ");
 
             if (tsk_getu32(fs->endian,
-                    ext2fs->dino_buf->i_flags) & EXT2_IN_NOA)
+                    dino_buf->i_flags) & EXT2_IN_NOA)
                 tsk_fprintf(hFile, "No A-Time, ");
 
             tsk_fprintf(hFile, "\n");
         }
-    }
 
     tsk_fprintf(hFile, "size: %" PRIuOFF "\n", fs_meta->size);
     tsk_fprintf(hFile, "num of links: %d\n", fs_meta->nlink);
 
-    if (ext2fs->dino_buf) {
         /* Ext attribute are stored in a block with a header and a list
          * of entries that are aligned to 4-byte boundaries.  The attr
          * value is stored at the end of the block.  There are 4 null bytes
-         * in between the headers and values 
+         * in between the headers and values
          */
-        if (tsk_getu32(fs->endian, ext2fs->dino_buf->i_file_acl) != 0) {
+        if (tsk_getu32(fs->endian, dino_buf->i_file_acl) != 0) {
             char *buf;
             ext2fs_ea_header *ea_head;
             ext2fs_ea_entry *ea_entry;
@@ -1692,16 +1741,17 @@ ext2fs_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
 
             if ((buf = tsk_malloc(fs->block_size)) == NULL) {
                 tsk_fs_file_close(fs_file);
+                free(dino_buf);
                 return 1;
             }
 
             tsk_fprintf(hFile,
                 "\nExtended Attributes  (Block: %" PRIu32 ")\n",
-                tsk_getu32(fs->endian, ext2fs->dino_buf->i_file_acl));
+                tsk_getu32(fs->endian, dino_buf->i_file_acl));
 
             /* Is the value too big? */
             if (tsk_getu32(fs->endian,
-                    ext2fs->dino_buf->i_file_acl) > fs->last_block) {
+                    dino_buf->i_file_acl) > fs->last_block) {
                 tsk_fprintf(hFile,
                     "Extended Attributes block is larger than file system\n");
                 goto egress_ea;
@@ -1709,19 +1759,20 @@ ext2fs_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
 
             cnt = tsk_fs_read(fs,
                 (TSK_DADDR_T) tsk_getu32(fs->endian,
-                    ext2fs->dino_buf->i_file_acl) * fs->block_size,
+                    dino_buf->i_file_acl) * fs->block_size,
                 buf, fs->block_size);
 
             if (cnt != fs->block_size) {
                 if (cnt >= 0) {
                     tsk_error_reset();
-                    tsk_errno = TSK_ERR_FS_READ;
+                    tsk_error_set_errno(TSK_ERR_FS_READ);
                 }
-                snprintf(tsk_errstr2, TSK_ERRSTR_L,
+                tsk_error_set_errstr2(
                     "ext2fs_istat: ACL block %" PRIu32,
-                    tsk_getu32(fs->endian, ext2fs->dino_buf->i_file_acl));
+                    tsk_getu32(fs->endian, dino_buf->i_file_acl));
                 tsk_fs_file_close(fs_file);
                 free(buf);
+                free(dino_buf);
                 return 1;
             }
 
@@ -1904,7 +1955,6 @@ ext2fs_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
 
             free(buf);
         }
-    }
 
     if (sec_skew != 0) {
         tsk_fprintf(hFile, "\nAdjusted Inode Times:\n");
@@ -1978,6 +2028,9 @@ ext2fs_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
     }
 
     tsk_fs_file_close(fs_file);
+    if (dino_buf != NULL)
+        free((char *) dino_buf);
+
     return 0;
 }
 
@@ -1991,8 +2044,6 @@ ext2fs_close(TSK_FS_INFO * fs)
 
     fs->tag = 0;
     free((char *) ext2fs->fs);
-    if (ext2fs->dino_buf != NULL)
-        free((char *) ext2fs->dino_buf);
 
     if (ext2fs->grp_buf != NULL)
         free((char *) ext2fs->grp_buf);
@@ -2003,17 +2054,14 @@ ext2fs_close(TSK_FS_INFO * fs)
     if (ext2fs->imap_buf != NULL)
         free((char *) ext2fs->imap_buf);
 
-    if (fs->list_inum_named) {
-        tsk_list_free(fs->list_inum_named);
-        fs->list_inum_named = NULL;
-    }
+    tsk_deinit_lock(&ext2fs->lock);
 
-    free(ext2fs);
+    tsk_fs_free(fs);
 }
 
 /**
  * \internal
- * Open part of a disk image as a Ext2/3 file system. 
+ * Open part of a disk image as a Ext2/3 file system.
  *
  * @param img_info Disk image to analyze
  * @param offset Byte offset where file system starts
@@ -2035,13 +2083,13 @@ ext2fs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
 
     if (TSK_FS_TYPE_ISEXT(ftype) == 0) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "Invalid FS Type in ext2fs_open");
         return NULL;
     }
 
-    if ((ext2fs = (EXT2FS_INFO *) tsk_malloc(sizeof(*ext2fs))) == NULL)
+    if ((ext2fs = (EXT2FS_INFO *) tsk_fs_malloc(sizeof(*ext2fs))) == NULL)
         return NULL;
 
     fs = &(ext2fs->fs_info);
@@ -2066,16 +2114,16 @@ ext2fs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
     if (cnt != len) {
         if (cnt >= 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_READ;
+            tsk_error_set_errno(TSK_ERR_FS_READ);
         }
-        snprintf(tsk_errstr2, TSK_ERRSTR_L, "ext2fs_open: superblock");
+        tsk_error_set_errstr2( "ext2fs_open: superblock");
         fs->tag = 0;
         free(ext2fs->fs);
         free(ext2fs);
         return NULL;
     }
 
-    /* 
+    /*
      * Verify we are looking at an EXTxFS image
      */
     if (tsk_fs_guessu16(fs, ext2fs->fs->s_magic, EXT2FS_FS_MAGIC)) {
@@ -2083,8 +2131,8 @@ ext2fs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
         free(ext2fs->fs);
         free(ext2fs);
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr(
             "not an EXTxFS file system (magic)");
         return NULL;
     }
@@ -2117,8 +2165,8 @@ ext2fs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
         ext2fs->deentry_type = EXT2_DE_V1;
 
 
-    /* 
-     * Calculate the meta data info 
+    /*
+     * Calculate the meta data info
      */
     fs->inum_count = tsk_getu32(fs->endian, ext2fs->fs->s_inodes_count) + 1;    // we are adding 1 in this calc to account for Orphans directory
     fs->last_inum = fs->inum_count;
@@ -2130,8 +2178,8 @@ ext2fs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
         free(ext2fs->fs);
         free(ext2fs);
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr(
             "Not an EXTxFS file system (inum count)");
         return NULL;
     }
@@ -2148,8 +2196,8 @@ ext2fs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
     }
 
 
-    /* 
-     * Calculate the block info 
+    /*
+     * Calculate the block info
      */
     fs->dev_bsize = img_info->sector_size;
     fs->block_count = tsk_getu32(fs->endian, ext2fs->fs->s_blocks_count);
@@ -2164,8 +2212,8 @@ ext2fs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
         free(ext2fs->fs);
         free(ext2fs);
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+        tsk_error_set_errstr(
             "This file system has fragments that are a different size than blocks, which is not currently supported\nContact brian with details of the system that created this image");
         return NULL;
     }
@@ -2181,7 +2229,7 @@ ext2fs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
             (img_info->size - offset) / fs->block_size - 1;
 
 
-    /* The group descriptors are located in the block following the 
+    /* The group descriptors are located in the block following the
      * super block */
     ext2fs->groups_offset =
         roundup((EXT2FS_SBOFF + sizeof(ext2fs_sb)), fs->block_size);
@@ -2231,16 +2279,10 @@ ext2fs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
     ext2fs->bmap_buf = NULL;
     ext2fs->bmap_grp_num = 0xffffffff;
 
-    /* dinode */
-    ext2fs->dino_buf = NULL;
-    ext2fs->dino_inum = 0xffffffff;
-
     /* group descriptor */
     ext2fs->grp_buf = NULL;
     ext2fs->grp_num = 0xffffffff;
 
-    fs->list_inum_named = NULL;
-
 
     /*
      * Print some stats.
@@ -2254,5 +2296,7 @@ ext2fs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
                 ext2fs->fs->s_blocks_count), tsk_getu32(fs->endian,
                 ext2fs->fs->s_blocks_per_group));
 
+    tsk_init_lock(&ext2fs->lock);
+
     return (fs);
 }
diff --git a/tsk3/fs/ext2fs_dent.c b/tsk3/fs/ext2fs_dent.c
index 895e28894..800ceb1ae 100644
--- a/tsk3/fs/ext2fs_dent.c
+++ b/tsk3/fs/ext2fs_dent.c
@@ -1,12 +1,12 @@
 /*
 ** ext2fs_dent
-** The Sleuth Kit 
+** The Sleuth Kit
 **
 ** File name layer support for an Ext2 / Ext3 FS
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
-** Copyright (c) 2003-2006 Brian Carrier.  All rights reserved 
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2003-2006 Brian Carrier.  All rights reserved
 **
 ** TASK
 ** Copyright (c) 2002 Brian Carrier, @stake Inc.  All rights reserved
@@ -44,8 +44,8 @@ ext2fs_dent_copy(EXT2FS_INFO * ext2fs,
         /* ext2 does not null terminate */
         if (tsk_getu16(fs->endian, dir->name_len) >= fs_name->name_size) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_ARG;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_ARG);
+            tsk_error_set_errstr(
                 "ext2fs_dent_copy: Name Space too Small %d %" PRIuSIZE "",
                 tsk_getu16(fs->endian, dir->name_len), fs_name->name_size);
             return 1;
@@ -66,8 +66,8 @@ ext2fs_dent_copy(EXT2FS_INFO * ext2fs,
         /* ext2 does not null terminate */
         if (dir->name_len >= fs_name->name_size) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_ARG;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_ARG);
+            tsk_error_set_errstr(
                 "ext2_dent_copy: Name Space too Small %d %" PRIuSIZE "",
                 dir->name_len, fs_name->name_size);
             return 1;
@@ -114,7 +114,7 @@ ext2fs_dent_copy(EXT2FS_INFO * ext2fs,
 
 
 /*
- * @param a_is_del Set to 1 if block is from a deleted directory. 
+ * @param a_is_del Set to 1 if block is from a deleted directory.
  */
 static TSK_RETVAL_ENUM
 ext2fs_dent_parse_block(EXT2FS_INFO * ext2fs, TSK_FS_DIR * a_fs_dir,
@@ -134,7 +134,7 @@ ext2fs_dent_parse_block(EXT2FS_INFO * ext2fs, TSK_FS_DIR * a_fs_dir,
         return TSK_ERR;
 
     /* update each time by the actual length instead of the
-     ** recorded length so we can view the deleted entries 
+     ** recorded length so we can view the deleted entries
      */
     for (idx = 0; idx <= len - EXT2FS_DIRSIZ_lcl(1); idx += minreclen) {
 
@@ -156,9 +156,9 @@ ext2fs_dent_parse_block(EXT2FS_INFO * ext2fs, TSK_FS_DIR * a_fs_dir,
 
         minreclen = EXT2FS_DIRSIZ_lcl(namelen);
 
-        /* 
+        /*
          ** Check if we may have a valid directory entry.  If we don't,
-         ** then increment to the next word and try again.  
+         ** then increment to the next word and try again.
          */
         if ((inode > fs->last_inum) ||  // inode is unsigned
             (namelen > EXT2FS_MAXNAMLEN) || (namelen == 0) ||   // namelen is unsigned
@@ -198,9 +198,9 @@ ext2fs_dent_parse_block(EXT2FS_INFO * ext2fs, TSK_FS_DIR * a_fs_dir,
             return TSK_ERR;
         }
 
-        /* If the actual length is shorter then the 
-         ** recorded length, then the next entry(ies) have been 
-         ** deleted.  Set dellen to the length of data that 
+        /* If the actual length is shorter then the
+         ** recorded length, then the next entry(ies) have been
+         ** deleted.  Set dellen to the length of data that
          ** has been deleted
          **
          ** Because we aren't guaranteed with Ext2FS that the next
@@ -224,15 +224,15 @@ ext2fs_dent_parse_block(EXT2FS_INFO * ext2fs, TSK_FS_DIR * a_fs_dir,
 /** \internal
 * Process a directory and load up FS_DIR with the entries. If a pointer to
 * an already allocated FS_DIR struture is given, it will be cleared.  If no existing
-* FS_DIR structure is passed (i.e. NULL), then a new one will be created. If the return 
-* value is error or corruption, then the FS_DIR structure could  
-* have entries (depending on when the error occured). 
+* FS_DIR structure is passed (i.e. NULL), then a new one will be created. If the return
+* value is error or corruption, then the FS_DIR structure could
+* have entries (depending on when the error occured).
 *
 * @param a_fs File system to analyze
 * @param a_fs_dir Pointer to FS_DIR pointer. Can contain an already allocated
-* structure or a new structure. 
+* structure or a new structure.
 * @param a_addr Address of directory to process.
-* @returns error, corruption, ok etc. 
+* @returns error, corruption, ok etc.
 */
 
 TSK_RETVAL_ENUM
@@ -254,15 +254,15 @@ ext2fs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
 
     if (a_addr < a_fs->first_inum || a_addr > a_fs->last_inum) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "ext2fs_dir_open_meta: inode value: %" PRIuINUM "\n", a_addr);
         return TSK_ERR;
     }
     else if (a_fs_dir == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "ext2fs_dir_open_meta: NULL fs_attr argument given");
         return TSK_ERR;
     }
@@ -290,8 +290,7 @@ ext2fs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
     if ((fs_dir->fs_file =
             tsk_fs_file_open_meta(a_fs, NULL, a_addr)) == NULL) {
         tsk_error_reset();
-        strncat(tsk_errstr2, " - ext2fs_dir_open_meta",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- ext2fs_dir_open_meta");
         return TSK_COR;
     }
 
@@ -308,8 +307,7 @@ ext2fs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
             TSK_FS_FILE_WALK_FLAG_SLACK,
             tsk_fs_load_file_action, (void *) &load_file)) {
         tsk_error_reset();
-        strncat(tsk_errstr2, " - ext2fs_dir_open_meta",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- ext2fs_dir_open_meta");
         free(dirbuf);
         return TSK_COR;
     }
@@ -317,8 +315,8 @@ ext2fs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
     /* Not all of the directory was copied, so we exit */
     if (load_file.left > 0) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_FWALK;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_FWALK);
+        tsk_error_set_errstr(
             "ext2fs_dir_open_meta: Error reading directory contents: %"
             PRIuINUM "\n", a_addr);
         free(dirbuf);
diff --git a/tsk3/fs/ext2fs_journal.c b/tsk3/fs/ext2fs_journal.c
index e99d6cf77..7488bd3d0 100644
--- a/tsk3/fs/ext2fs_journal.c
+++ b/tsk3/fs/ext2fs_journal.c
@@ -6,7 +6,7 @@
 ** Journaling code for TSK_FS_INFO_TYPE_EXT_3 image
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2004-2005 Brian Carrier.  All rights reserved 
 **
 **
@@ -46,8 +46,8 @@ load_sb_action(TSK_FS_FILE * fs_file, TSK_OFF_T a_off, TSK_DADDR_T addr,
 
     if (size < 1024) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+        tsk_error_set_errstr(
             "FS block size is less than 1024, not supported in journal yet");
         return TSK_WALK_ERROR;
     }
@@ -56,8 +56,8 @@ load_sb_action(TSK_FS_FILE * fs_file, TSK_OFF_T a_off, TSK_DADDR_T addr,
 
     if (big_tsk_getu32(sb->magic) != EXT2_JMAGIC) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr(
             "Journal inode %" PRIuINUM
             " does not have a valid magic value: %" PRIx32,
             jinfo->j_inum, big_tsk_getu32(sb->magic));
@@ -88,8 +88,8 @@ ext2fs_jopen(TSK_FS_INFO * fs, TSK_INUM_T inum)
 
     if (!fs) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "ext2fs_jopen: fs is null");
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr("ext2fs_jopen: fs is null");
         return 1;
     }
 
@@ -109,8 +109,8 @@ ext2fs_jopen(TSK_FS_INFO * fs, TSK_INUM_T inum)
 
     if (tsk_fs_file_walk(jinfo->fs_file, 0, load_sb_action, NULL)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_FWALK;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "Error loading ext3 journal");
+        tsk_error_set_errno(TSK_ERR_FS_FWALK);
+        tsk_error_set_errstr("Error loading ext3 journal");
         tsk_fs_file_close(jinfo->fs_file);
         free(jinfo);
         return 1;
@@ -148,8 +148,8 @@ ext2fs_jentry_walk(TSK_FS_INFO * fs, int flags,
     if ((jinfo == NULL) || (jinfo->fs_file == NULL)
         || (jinfo->fs_file->meta == NULL)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "ext2fs_jentry_walk: journal is not open");
         return 1;
     }
@@ -157,8 +157,8 @@ ext2fs_jentry_walk(TSK_FS_INFO * fs, int flags,
     if (jinfo->fs_file->meta->size !=
         (jinfo->last_block + 1) * jinfo->bsize) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "ext2fs_jentry_walk: journal file size is different from \nsize reported in journal super block");
         return 1;
     }
@@ -178,8 +178,8 @@ ext2fs_jentry_walk(TSK_FS_INFO * fs, int flags,
 
     if (buf1.left > 0) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_FWALK;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_FWALK);
+        tsk_error_set_errstr(
             "ext2fs_jentry_walk: Buffer not fully copied");
         free(journ);
         return 1;
@@ -444,24 +444,24 @@ ext2fs_jblk_walk(TSK_FS_INFO * fs, TSK_DADDR_T start, TSK_DADDR_T end,
     if ((jinfo == NULL) || (jinfo->fs_file == NULL)
         || (jinfo->fs_file->meta == NULL)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "ext2fs_jblk_walk: journal is not open");
         return 1;
     }
 
     if (jinfo->last_block < end) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "ext2fs_jblk_walk: end is too large ");
         return 1;
     }
 
     if (start != end) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "ext2fs_blk_walk: only start == end is currently supported");
         return 1;
     }
@@ -469,8 +469,8 @@ ext2fs_jblk_walk(TSK_FS_INFO * fs, TSK_DADDR_T start, TSK_DADDR_T end,
     if (jinfo->fs_file->meta->size !=
         (jinfo->last_block + 1) * jinfo->bsize) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+        tsk_error_set_errstr(
             "ext2fs_jblk_walk: journal file size is different from size reported in journal super block");
         return 1;
     }
@@ -493,8 +493,8 @@ ext2fs_jblk_walk(TSK_FS_INFO * fs, TSK_DADDR_T start, TSK_DADDR_T end,
 
     if (buf1.left > 0) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_FWALK;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_FWALK);
+        tsk_error_set_errstr(
             "ext2fs_jblk_walk: Buffer not fully copied");
         free(journ);
         return 1;
@@ -573,8 +573,8 @@ ext2fs_jblk_walk(TSK_FS_INFO * fs, TSK_DADDR_T start, TSK_DADDR_T end,
 
     if (fwrite(&journ[end * jinfo->bsize], jinfo->bsize, 1, stdout) != 1) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WRITE;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WRITE);
+        tsk_error_set_errstr(
             "ext2fs_jblk_walk: error writing buffer block");
         free(journ);
         return 1;
diff --git a/tsk3/fs/fatfs.c b/tsk3/fs/fatfs.c
index 004336e4e..6fd636663 100644
--- a/tsk3/fs/fatfs.c
+++ b/tsk3/fs/fatfs.c
@@ -5,7 +5,7 @@
 ** Content and meta data layer support for the FAT file system 
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved 
 **
 ** TASK
@@ -52,6 +52,8 @@
 /* TTL is 0 if the entry has not been used.  TTL of 1 means it was the
  * most recently used, and TTL of FAT_CACHE_N means it was the least 
  * recently used.  This function has a LRU replacement algo
+ *
+ * Note: This routine assumes &fatfs->cache_lock is locked by the caller.
  */
 // return -1 on error, or cache index on success (0 to FAT_CACHE_N)
 
@@ -105,9 +107,9 @@ getFATCacheIdx(FATFS_INFO * fatfs, TSK_DADDR_T sect)
     if (cnt != FAT_CACHE_B) {
         if (cnt >= 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_READ;
+            tsk_error_set_errno(TSK_ERR_FS_READ);
         }
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
+        tsk_error_set_errstr2(
             "getFATCacheIdx: FAT: %" PRIuDADDR, sect);
         return -1;
     }
@@ -126,6 +128,7 @@ getFATCacheIdx(FATFS_INFO * fatfs, TSK_DADDR_T sect)
 
     fatfs->fatc_ttl[cidx] = 1;
     fatfs->fatc_addr[cidx] = sect;
+
     return cidx;
 }
 
@@ -165,8 +168,8 @@ fatfs_getFAT(FATFS_INFO * fatfs, TSK_DADDR_T clust, TSK_DADDR_T * value)
         }
 
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "fatfs_getFAT: invalid cluster address: %" PRIuDADDR, clust);
         return 1;
     }
@@ -175,8 +178,8 @@ fatfs_getFAT(FATFS_INFO * fatfs, TSK_DADDR_T clust, TSK_DADDR_T * value)
     case TSK_FS_TYPE_FAT12:
         if (clust & 0xf000) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_ARG;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_ARG);
+            tsk_error_set_errstr(
                 "fatfs_getFAT: TSK_FS_TYPE_FAT12 Cluster %" PRIuDADDR
                 " too large", clust);
             return 1;
@@ -186,10 +189,14 @@ fatfs_getFAT(FATFS_INFO * fatfs, TSK_DADDR_T clust, TSK_DADDR_T * value)
         sect = fatfs->firstfatsect +
             ((clust + (clust >> 1)) >> fatfs->ssize_sh);
 
+        tsk_take_lock(&fatfs->cache_lock);
+
         /* Load the FAT if we don't have it */
         // see if it is in the cache
-        if (-1 == (cidx = getFATCacheIdx(fatfs, sect)))
+        if (-1 == (cidx = getFATCacheIdx(fatfs, sect))) {
+            tsk_release_lock(&fatfs->cache_lock);
             return 1;
+        }
 
         /* get the offset into the cache */
         offs = ((sect - fatfs->fatc_addr[cidx]) << fatfs->ssize_sh) +
@@ -206,11 +213,12 @@ fatfs_getFAT(FATFS_INFO * fatfs, TSK_DADDR_T clust, TSK_DADDR_T * value)
                 tsk_fs_read(fs, sect * fs->block_size,
                 fatfs->fatc_buf[cidx], FAT_CACHE_B);
             if (cnt != FAT_CACHE_B) {
+                tsk_release_lock(&fatfs->cache_lock);
                 if (cnt >= 0) {
                     tsk_error_reset();
-                    tsk_errno = TSK_ERR_FS_READ;
+                    tsk_error_set_errno(TSK_ERR_FS_READ);
                 }
-                snprintf(tsk_errstr2, TSK_ERRSTR_L,
+                tsk_error_set_errstr2(
                     "fatfs_getFAT: TSK_FS_TYPE_FAT12 FAT overlap: %"
                     PRIuDADDR, sect);
                 return 1;
@@ -225,6 +233,8 @@ fatfs_getFAT(FATFS_INFO * fatfs, TSK_DADDR_T clust, TSK_DADDR_T * value)
 
         tmp16 = tsk_getu16(fs->endian, a_ptr);
 
+        tsk_release_lock(&fatfs->cache_lock);
+
         /* slide it over if it is one of the odd clusters */
         if (clust & 1)
             tmp16 >>= 4;
@@ -246,8 +256,14 @@ fatfs_getFAT(FATFS_INFO * fatfs, TSK_DADDR_T clust, TSK_DADDR_T * value)
     case TSK_FS_TYPE_FAT16:
         /* Get sector in FAT for cluster and load it if needed */
         sect = fatfs->firstfatsect + ((clust << 1) >> fatfs->ssize_sh);
-        if (-1 == (cidx = getFATCacheIdx(fatfs, sect)))
+
+        tsk_take_lock(&fatfs->cache_lock);
+
+        if (-1 == (cidx = getFATCacheIdx(fatfs, sect))) {
+            tsk_release_lock(&fatfs->cache_lock);
             return 1;
+        }
+
 
         /* get pointer to entry in the cache buffer */
         a_ptr = (uint8_t *) fatfs->fatc_buf[cidx] +
@@ -256,6 +272,8 @@ fatfs_getFAT(FATFS_INFO * fatfs, TSK_DADDR_T clust, TSK_DADDR_T * value)
 
         *value = tsk_getu16(fs->endian, a_ptr) & FATFS_16_MASK;
 
+        tsk_release_lock(&fatfs->cache_lock);
+
         /* sanity check */
         if ((*value > (fatfs->lastclust)) &&
             (*value < (0x0ffffff7 & FATFS_16_MASK))) {
@@ -270,8 +288,14 @@ fatfs_getFAT(FATFS_INFO * fatfs, TSK_DADDR_T clust, TSK_DADDR_T * value)
     case TSK_FS_TYPE_FAT32:
         /* Get sector in FAT for cluster and load if needed */
         sect = fatfs->firstfatsect + ((clust << 2) >> fatfs->ssize_sh);
-        if (-1 == (cidx = getFATCacheIdx(fatfs, sect)))
+
+        tsk_take_lock(&fatfs->cache_lock);
+
+        if (-1 == (cidx = getFATCacheIdx(fatfs, sect))) {
+            tsk_release_lock(&fatfs->cache_lock);
             return 1;
+        }
+
 
         /* get pointer to entry in current buffer */
         a_ptr = (uint8_t *) fatfs->fatc_buf[cidx] +
@@ -280,6 +304,8 @@ fatfs_getFAT(FATFS_INFO * fatfs, TSK_DADDR_T clust, TSK_DADDR_T * value)
 
         *value = tsk_getu32(fs->endian, a_ptr) & FATFS_32_MASK;
 
+        tsk_release_lock(&fatfs->cache_lock);
+
         /* sanity check */
         if ((*value > fatfs->lastclust) &&
             (*value < (0x0ffffff7 & FATFS_32_MASK))) {
@@ -294,8 +320,8 @@ fatfs_getFAT(FATFS_INFO * fatfs, TSK_DADDR_T clust, TSK_DADDR_T * value)
 
     default:
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "fatfs_getFAT: Unknown FAT type: %d", fatfs->fs_info.ftype);
         return 1;
     }
@@ -413,15 +439,15 @@ fatfs_block_walk(TSK_FS_INFO * fs, TSK_DADDR_T a_start_blk,
      */
     if (a_start_blk < fs->first_block || a_start_blk > fs->last_block) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "%s: Start block: %" PRIuDADDR "", myname, a_start_blk);
         return 1;
     }
     if (a_end_blk < fs->first_block || a_end_blk > fs->last_block) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "%s: End block: %" PRIuDADDR "", myname, a_end_blk);
         return 1;
     }
@@ -475,9 +501,9 @@ fatfs_block_walk(TSK_FS_INFO * fs, TSK_DADDR_T a_start_blk,
             if (cnt != fs->block_size * 8) {
                 if (cnt >= 0) {
                     tsk_error_reset();
-                    tsk_errno = TSK_ERR_FS_READ;
+                    tsk_error_set_errno(TSK_ERR_FS_READ);
                 }
-                snprintf(tsk_errstr2, TSK_ERRSTR_L,
+                tsk_error_set_errstr2(
                     "fatfs_block_walk: pre-data area block: %"
                     PRIuDADDR, addr);
                 free(data_buf);
@@ -606,9 +632,9 @@ fatfs_block_walk(TSK_FS_INFO * fs, TSK_DADDR_T a_start_blk,
         if (cnt != fs->block_size * read_size) {
             if (cnt >= 0) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_READ;
+                tsk_error_set_errno(TSK_ERR_FS_READ);
             }
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
+            tsk_error_set_errstr2(
                 "fatfs_block_walk: block: %" PRIuDADDR, addr);
             free(data_buf);
             tsk_fs_block_free(fs_block);
@@ -656,8 +682,8 @@ static uint8_t
 fatfs_fscheck(TSK_FS_INFO * fs, FILE * hFile)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr(
         "fscheck not implemented for FAT yet");
     return 1;
 
@@ -719,9 +745,9 @@ fatfs_fsstat(TSK_FS_INFO * fs, FILE * hFile)
     if (cnt != fs->block_size) {
         if (cnt >= 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_READ;
+            tsk_error_set_errno(TSK_ERR_FS_READ);
         }
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
+        tsk_error_set_errstr2(
             "fatfs_fsstat: root directory: %" PRIuDADDR, fatfs->rootsect);
         free(data_buf);
         return 1;
@@ -841,9 +867,9 @@ fatfs_fsstat(TSK_FS_INFO * fs, FILE * hFile)
             if (cnt != sizeof(fatfs_fsinfo)) {
                 if (cnt >= 0) {
                     tsk_error_reset();
-                    tsk_errno = TSK_ERR_FS_READ;
+                    tsk_error_set_errno(TSK_ERR_FS_READ);
                 }
-                snprintf(tsk_errstr2, TSK_ERRSTR_L,
+                tsk_error_set_errstr2(
                     "fatfs_fsstat: TSK_FS_TYPE_FAT32 FSINFO block: %"
                     PRIuDADDR, (TSK_DADDR_T) tsk_getu16(fs->endian,
                         sb->a.f32.fsinfo));
@@ -1121,12 +1147,13 @@ fatfs_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
     TSK_FS_META *fs_meta;
     TSK_FS_FILE *fs_file;
     TSK_FS_META_NAME_LIST *fs_name_list;
-    FATFS_INFO *fatfs = (FATFS_INFO *) fs;
     FATFS_PRINT_ADDR print;
+    fatfs_dentry dep;
 
     // clean up any error messages that are lying around
     tsk_error_reset();
 
+
     if ((fs_file = tsk_fs_file_open_meta(fs, NULL, inum)) == NULL) {
         return 1;
     }
@@ -1140,7 +1167,7 @@ fatfs_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
     tsk_fprintf(hFile, "File Attributes: ");
 
     /* This should only be null if we have the root directory or special file */
-    if (fatfs->dep == NULL) {
+    if (fatfs_dinode_load(fs, &dep, inum)){
         if (inum == FATFS_ROOTINO)
             tsk_fprintf(hFile, "Directory\n");
         else if (fs_file->meta->type == TSK_FS_META_TYPE_VIRT)
@@ -1148,24 +1175,24 @@ fatfs_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
         else
             tsk_fprintf(hFile, "File\n");
     }
-    else if ((fatfs->dep->attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) {
+    else if ((dep.attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) {
         tsk_fprintf(hFile, "Long File Name\n");
     }
     else {
-        if (fatfs->dep->attrib & FATFS_ATTR_DIRECTORY)
+        if (dep.attrib & FATFS_ATTR_DIRECTORY)
             tsk_fprintf(hFile, "Directory");
-        else if (fatfs->dep->attrib & FATFS_ATTR_VOLUME)
+        else if (dep.attrib & FATFS_ATTR_VOLUME)
             tsk_fprintf(hFile, "Volume Label");
         else
             tsk_fprintf(hFile, "File");
 
-        if (fatfs->dep->attrib & FATFS_ATTR_READONLY)
+        if (dep.attrib & FATFS_ATTR_READONLY)
             tsk_fprintf(hFile, ", Read Only");
-        if (fatfs->dep->attrib & FATFS_ATTR_HIDDEN)
+        if (dep.attrib & FATFS_ATTR_HIDDEN)
             tsk_fprintf(hFile, ", Hidden");
-        if (fatfs->dep->attrib & FATFS_ATTR_SYSTEM)
+        if (dep.attrib & FATFS_ATTR_SYSTEM)
             tsk_fprintf(hFile, ", System");
-        if (fatfs->dep->attrib & FATFS_ATTR_ARCHIVE)
+        if (dep.attrib & FATFS_ATTR_ARCHIVE)
             tsk_fprintf(hFile, ", Archive");
 
         tsk_fprintf(hFile, "\n");
@@ -1232,8 +1259,8 @@ uint8_t
 fatfs_jopen(TSK_FS_INFO * fs, TSK_INUM_T inum)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-    snprintf(tsk_errstr, TSK_ERRSTR_L, "FAT does not have a journal\n");
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr("FAT does not have a journal\n");
     return 1;
 }
 
@@ -1243,8 +1270,8 @@ fatfs_jentry_walk(TSK_FS_INFO * fs, int a_flags,
     TSK_FS_JENTRY_WALK_CB a_action, void *a_ptr)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-    snprintf(tsk_errstr, TSK_ERRSTR_L, "FAT does not have a journal\n");
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr("FAT does not have a journal\n");
     return 1;
 }
 
@@ -1255,8 +1282,8 @@ fatfs_jblk_walk(TSK_FS_INFO * fs, TSK_DADDR_T start, TSK_DADDR_T end,
     int a_flags, TSK_FS_JBLK_WALK_CB a_action, void *a_ptr)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-    snprintf(tsk_errstr, TSK_ERRSTR_L, "FAT does not have a journal\n");
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr("FAT does not have a journal\n");
     return 1;
 }
 
@@ -1273,17 +1300,15 @@ fatfs_close(TSK_FS_INFO * fs)
     FATFS_INFO *fatfs = (FATFS_INFO *) fs;
     fs->tag = 0;
 
-    free(fatfs->dinodes);
-
     if (fatfs->dir_buf)
         free(fatfs->dir_buf);
     if (fatfs->par_buf)
         free(fatfs->par_buf);
 
-    tsk_list_free(fs->list_inum_named);
-    fs->list_inum_named = NULL;
     free(fatfs->sb);
-    free(fs);
+    tsk_deinit_lock(&fatfs->cache_lock);
+    tsk_deinit_lock(&fatfs->dir_lock);
+    tsk_fs_free(fs);
 }
 
 
@@ -1315,12 +1340,12 @@ fatfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
 
     if (TSK_FS_TYPE_ISFAT(ftype) == 0) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "%s: Invalid FS Type", myname);
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr("%s: Invalid FS Type", myname);
         return NULL;
     }
 
-    if ((fatfs = (FATFS_INFO *) tsk_malloc(sizeof(*fatfs))) == NULL)
+    if ((fatfs = (FATFS_INFO *) tsk_fs_malloc(sizeof(*fatfs))) == NULL)
         return NULL;
 
     fs = &(fatfs->fs_info);
@@ -1356,9 +1381,9 @@ fatfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
         if (cnt != len) {
             if (cnt >= 0) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_READ;
+                tsk_error_set_errno(TSK_ERR_FS_READ);
             }
-            snprintf(tsk_errstr2, TSK_ERRSTR_L, "%s: boot sector", myname);
+            tsk_error_set_errstr2( "%s: boot sector", myname);
             fs->tag = 0;
             free(fatfs->sb);
             free(fatfs);
@@ -1378,8 +1403,8 @@ fatfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
                 free(fatsb);
                 free(fatfs);
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_MAGIC;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+                tsk_error_set_errstr(
                     "Not a FATFS file system (magic)");
                 return NULL;
             }
@@ -1408,8 +1433,8 @@ fatfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
     }
     else {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr(
             "Error: sector size (%d) is not a multiple of device size (%d)\nDo you have a disk image instead of a partition image?",
             fatfs->ssize, fs->dev_bsize);
         fs->tag = 0;
@@ -1431,8 +1456,8 @@ fatfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
         free(fatsb);
         free(fatfs);
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr(
             "Not a FATFS file system (cluster size)");
         return NULL;
     }
@@ -1444,8 +1469,8 @@ fatfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
         free(fatsb);
         free(fatfs);
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr(
             "Not a FATFS file system (number of FATs)");
         return NULL;
     }
@@ -1470,8 +1495,8 @@ fatfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
         free(fatsb);
         free(fatfs);
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr(
             "Not a FATFS file system (invalid sectors per FAT)");
         return NULL;
     }
@@ -1479,8 +1504,8 @@ fatfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
     fatfs->firstfatsect = tsk_getu16(fs->endian, fatsb->reserved);
     if ((fatfs->firstfatsect == 0) || (fatfs->firstfatsect > sectors)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "Not a FATFS file system (invalid first FAT sector %"
             PRIuDADDR ")", fatfs->firstfatsect);
 
@@ -1548,8 +1573,8 @@ fatfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
             free(fatsb);
             free(fatfs);
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_MAGIC;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+            tsk_error_set_errstr(
                 "Too many sectors for TSK_FS_TYPE_FAT12: try auto-detect mode");
             return NULL;
         }
@@ -1560,8 +1585,8 @@ fatfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
         free(fatsb);
         free(fatfs);
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr(
             "Invalid TSK_FS_TYPE_FAT32 image (numroot != 0)");
         return NULL;
     }
@@ -1571,8 +1596,8 @@ fatfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
         free(fatsb);
         free(fatfs);
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr(
             "Invalid FAT image (numroot == 0, and not TSK_FS_TYPE_FAT32)");
         return NULL;
     }
@@ -1593,8 +1618,8 @@ fatfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
         free(fatsb);
         free(fatfs);
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "Unknown FAT type in fatfs_open: %d\n", ftype);
         return NULL;
     }
@@ -1614,16 +1639,6 @@ fatfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
         fatfs->fatc_ttl[i] = 0;
     }
 
-    /* allocate a cluster-sized buffer for inodes */
-    if ((fatfs->dinodes = (char *)
-            tsk_malloc(fatfs->csize << fatfs->ssize_sh)) == NULL) {
-        fs->tag = 0;
-        free(fatsb);
-        free(fatfs);
-        return NULL;
-    }
-
-
     /*
      * block calculations : although there are no blocks in fat, we will
      * use these fields for sector calculations
@@ -1694,7 +1709,8 @@ fatfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
     fs->journ_inum = 0;
 
     // initialize the caches
-    fs->list_inum_named = NULL;
+    tsk_init_lock(&fatfs->cache_lock);
+    tsk_init_lock(&fatfs->dir_lock);
 
     return (fs);
 }
diff --git a/tsk3/fs/fatfs_dent.c b/tsk3/fs/fatfs_dent.c
index 3e4841042..097593a0b 100644
--- a/tsk3/fs/fatfs_dent.c
+++ b/tsk3/fs/fatfs_dent.c
@@ -1,12 +1,12 @@
 /*
 ** fatfs_dent
-** The Sleuth Kit 
+** The Sleuth Kit
 **
 ** file name layer support for the FAT file system
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
-** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved 
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
 **
 ** TASK
 ** Copyright (c) 2002 Brian Carrier, @stake Inc.  All rights reserved
@@ -32,13 +32,13 @@
  * the basic goal of this code is to parse directory entry structures for
  * file names.  The main function is fatfs_parse_buf, which parses
  * a buffer and stores the entries in FS_DIR.  That structure is then
- * used by dir_get() or dir_walk() to provide the data back to the user. 
+ * used by dir_get() or dir_walk() to provide the data back to the user.
  *
  * One of the odd aspects of this code is that the 'inode' values are
  * the 'slot-address'.  Refer to the document on how FAT was implemented
  * for more details. This means that we need to search for the actual
  * 'inode' address for the '.' and '..' entries though!  The search
- * for '..' is quite painful if this code is called from a random 
+ * for '..' is quite painful if this code is called from a random
  * location.  It does save what the parent is though, so the search
  * only has to be done once per session.
  */
@@ -84,8 +84,10 @@ fatfs_dir_buf_add(FATFS_INFO * fatfs, TSK_INUM_T par_inum,
 {
     size_t q;
 
+    tsk_take_lock(&fatfs->dir_lock);
     for (q = 0; q < fatfs->dir_buf_next; q++) {
         if (fatfs->dir_buf[q] == dir_inum) {
+            tsk_release_lock(&fatfs->dir_lock);
             return 0;
         }
     }
@@ -97,11 +99,13 @@ fatfs_dir_buf_add(FATFS_INFO * fatfs, TSK_INUM_T par_inum,
         if ((fatfs->dir_buf =
                 (TSK_INUM_T *) tsk_realloc(fatfs->dir_buf,
                     fatfs->dir_buf_size * sizeof(TSK_INUM_T))) == NULL) {
+            tsk_release_lock(&fatfs->dir_lock);
             return 1;
         }
         if ((fatfs->par_buf =
                 (TSK_INUM_T *) tsk_realloc(fatfs->par_buf,
                     fatfs->dir_buf_size * sizeof(TSK_INUM_T))) == NULL) {
+            tsk_release_lock(&fatfs->dir_lock);
             return 1;
         }
     }
@@ -110,19 +114,20 @@ fatfs_dir_buf_add(FATFS_INFO * fatfs, TSK_INUM_T par_inum,
     fatfs->dir_buf[fatfs->dir_buf_next] = dir_inum;
     fatfs->par_buf[fatfs->dir_buf_next] = par_inum;
     fatfs->dir_buf_next++;
+    tsk_release_lock(&fatfs->dir_lock);
     return 0;
 }
 
-/* 
- * Process the contents of a directory and add them to FS_DIR. 
- * 
+/*
+ * Process the contents of a directory and add them to FS_DIR.
+ *
  * @param fatfs File system information structure
- * @param a_fs_dir Structure to store the files in. 
+ * @param a_fs_dir Structure to store the files in.
  * @param list_seen List of directory inodes that have been seen thus far in
- * directory walking (can be a pointer to a NULL pointer on first call). 
- * @param buf Buffer that contains the directory contents. 
+ * directory walking (can be a pointer to a NULL pointer on first call).
+ * @param buf Buffer that contains the directory contents.
  * @param len Length of buffer in bytes (must be a multiple of sector size)
- * @param addrs Array where each element is the original address of the 
+ * @param addrs Array where each element is the original address of the
  * corresponding block in buf (size of array is number of blocks in directory).
  *
  * @return -1 on error, 0 on success, and 1 to stop
@@ -142,8 +147,8 @@ fatfs_dent_parse_buf(FATFS_INFO * fatfs, TSK_FS_DIR * a_fs_dir, char *buf,
 
     if (buf == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "fatfs_dent_parse_buf: buffer is NULL");
         return TSK_ERR;
     }
@@ -164,8 +169,8 @@ fatfs_dent_parse_buf(FATFS_INFO * fatfs, TSK_FS_DIR * a_fs_dir, char *buf,
 
         if (ibase > fs->last_inum) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_ARG;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_ARG);
+            tsk_error_set_errstr(
                 "fatfs_parse: inode address is too large");
             tsk_fs_name_free(fs_name);
             return TSK_COR;
@@ -205,14 +210,14 @@ fatfs_dent_parse_buf(FATFS_INFO * fatfs, TSK_FS_DIR * a_fs_dir, char *buf,
 
             inode = ibase + idx;
 
-            /* Take care of the name 
+            /* Take care of the name
              * Copy a long name to a buffer and take action if it
              * is a small name */
             if ((dir->attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) {
                 fatfs_dentry_lfn *dirl = (fatfs_dentry_lfn *) dir;
 
-                /* Store the name in dinfo until we get the 8.3 name 
-                 * Use the checksum to identify a new sequence 
+                /* Store the name in dinfo until we get the 8.3 name
+                 * Use the checksum to identify a new sequence
                  * */
                 if (((dirl->seq & FATFS_LFN_SEQ_FIRST)
                         && (dirl->seq != FATFS_SLOT_DELETED))
@@ -307,8 +312,8 @@ fatfs_dent_parse_buf(FATFS_INFO * fatfs, TSK_FS_DIR * a_fs_dir, char *buf,
 
                     if (retVal != TSKconversionOK) {
                         tsk_error_reset();
-                        tsk_errno = TSK_ERR_FS_UNICODE;
-                        snprintf(tsk_errstr, TSK_ERRSTR_L,
+                        tsk_error_set_errno(TSK_ERR_FS_UNICODE);
+                        tsk_error_set_errstr(
                             "fatfs_parse: Error converting FAT LFN to UTF8: %d",
                             retVal);
                         continue;
@@ -324,7 +329,7 @@ fatfs_dent_parse_buf(FATFS_INFO * fatfs, TSK_FS_DIR * a_fs_dir, char *buf,
                     lfninfo.start = FATFS_MAXNAMLEN_UTF8 - 1;
                     name_ptr = fs_name->shrt_name;      // put 8.3 into shrt_name
                 }
-                /* We don't have a LFN, so put the short name in 
+                /* We don't have a LFN, so put the short name in
                  * fs_name->name */
                 else {
                     fs_name->shrt_name[0] = '\0';
@@ -332,7 +337,7 @@ fatfs_dent_parse_buf(FATFS_INFO * fatfs, TSK_FS_DIR * a_fs_dir, char *buf,
                 }
 
 
-                /* copy in the short name into the place specified above. 
+                /* copy in the short name into the place specified above.
                  * Skip spaces and put in the . */
                 a = 0;
                 for (b = 0; b < 8; b++) {
@@ -382,18 +387,20 @@ fatfs_dent_parse_buf(FATFS_INFO * fatfs, TSK_FS_DIR * a_fs_dir, char *buf,
             /* Handle the . and .. entries specially
              * The current inode 'address' they have is for the current
              * slot in the cluster, but it needs to refer to the original
-             * slot 
+             * slot
              */
             if (TSK_FS_ISDOT(fs_name->name)) {
                 if (fs_name->name[1] == '\0') {
                     inode = fs_name->meta_addr =
                         a_fs_dir->fs_file->meta->addr;
                 }
-                /* for the parent directory, look up in the list that 
+                /* for the parent directory, look up in the list that
                  * is maintained in fafs_info */
                 else if (fs_name->name[1] == '.') {
                     size_t q;
                     uint8_t dir_found = 0;
+
+                    tsk_take_lock(&fatfs->dir_lock);
                     for (q = 0; q < fatfs->dir_buf_next; q++) {
                         if (fatfs->dir_buf[q] ==
                             a_fs_dir->fs_file->meta->addr) {
@@ -402,14 +409,8 @@ fatfs_dent_parse_buf(FATFS_INFO * fatfs, TSK_FS_DIR * a_fs_dir, char *buf,
                             break;
                         }
                     }
+                    tsk_release_lock(&fatfs->dir_lock);
 
-                    if ((dir_found == 0) && (fs->isOrphanHunting)) {
-                        /* if we are currently scanning the fs to determine the orphan files,
-                         * then we do not care about the value of '..' and this can only cause
-                         * infinite loop problems */
-                        inode = fs_name->meta_addr = 0;
-                        dir_found = 1;
-                    }
                     if ((dir_found == 0)
                         && (addrs[0] == fatfs->firstdatasect)) {
                         /* if we are currently in the root directory, we aren't going to find
@@ -438,6 +439,7 @@ fatfs_dent_parse_buf(FATFS_INFO * fatfs, TSK_FS_DIR * a_fs_dir, char *buf,
                             fprintf(stderr,
                                 "fatfs_dent_parse_buf: Finished walking directory to find parent\n");
 
+                        tsk_take_lock(&fatfs->dir_lock);
                         for (q = 0; q < fatfs->dir_buf_next; q++) {
                             if (fatfs->dir_buf[q] ==
                                 a_fs_dir->fs_file->meta->addr) {
@@ -447,7 +449,9 @@ fatfs_dent_parse_buf(FATFS_INFO * fatfs, TSK_FS_DIR * a_fs_dir, char *buf,
                                 break;
                             }
                         }
-                        // if we did not find it, then it was probably 
+                        tsk_release_lock(&fatfs->dir_lock);
+
+                        // if we did not find it, then it was probably
                         // from the orphan directory...
                         if (dir_found == 0)
                             inode = fs_name->meta_addr =
@@ -536,13 +540,13 @@ fatfs_dent_action(TSK_FS_FILE * fs_file, TSK_OFF_T a_off, TSK_DADDR_T addr,
     load->curdirptr = (char *) ((uintptr_t) load->curdirptr + len);
     load->dirleft -= len;
 
-    /* fill in the stack of addresses of sectors 
+    /* fill in the stack of addresses of sectors
      *
      * if we are at the last entry, then realloc more */
     if (load->addridx == load->addrsize) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "fatfs_dent_walk: Trying to put more sector address in stack than were allocated (%lu)",
             (long) load->addridx);
         return TSK_WALK_ERROR;
@@ -561,15 +565,15 @@ fatfs_dent_action(TSK_FS_FILE * fs_file, TSK_OFF_T a_off, TSK_DADDR_T addr,
 /** \internal
 * Process a directory and load up FS_DIR with the entries. If a pointer to
 * an already allocated FS_DIR struture is given, it will be cleared.  If no existing
-* FS_DIR structure is passed (i.e. NULL), then a new one will be created. If the return 
-* value is error or corruption, then the FS_DIR structure could  
-* have entries (depending on when the error occured). 
+* FS_DIR structure is passed (i.e. NULL), then a new one will be created. If the return
+* value is error or corruption, then the FS_DIR structure could
+* have entries (depending on when the error occured).
 *
 * @param a_fs File system to analyze
 * @param a_fs_dir Pointer to FS_DIR pointer. Can contain an already allocated
-* structure or a new structure. 
+* structure or a new structure.
 * @param a_addr Address of directory to process.
-* @returns error, corruption, ok etc. 
+* @returns error, corruption, ok etc.
 */
 
 TSK_RETVAL_ENUM
@@ -587,16 +591,16 @@ fatfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
 
     if ((a_addr < a_fs->first_inum) || (a_addr > a_fs->last_inum)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "fatfs_dir_open_meta: invalid a_addr value: %" PRIuINUM "\n",
             a_addr);
         return TSK_ERR;
     }
     else if (a_fs_dir == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "fatfs_dir_open_meta: NULL fs_attr argument given");
         return TSK_ERR;
     }
@@ -619,8 +623,8 @@ fatfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
     fs_dir->fs_file = tsk_fs_file_open_meta(a_fs, NULL, a_addr);
     if (fs_dir->fs_file == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_INODE_NUM;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
+        tsk_error_set_errstr(
             "fatfs_dir_open_meta: %" PRIuINUM " is not a valid inode",
             a_addr);
         return TSK_COR;
@@ -649,7 +653,7 @@ fatfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
     load.dirleft = (size_t) size;
 
     /* We are going to save the address of each sector in the directory
-     * in a stack - they are needed to determine the inode address.  
+     * in a stack - they are needed to determine the inode address.
      */
     load.addrsize = (size_t) (len / fatfs->ssize);
     addrbuf =
@@ -667,8 +671,7 @@ fatfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
     if (tsk_fs_file_walk(fs_dir->fs_file,
             TSK_FS_FILE_WALK_FLAG_SLACK,
             fatfs_dent_action, (void *) &load)) {
-        strncat(tsk_errstr2, " - fatfs_dir_open_meta",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- fatfs_dir_open_meta");
         free(dirbuf);
         free(addrbuf);
         return TSK_COR;
@@ -677,8 +680,8 @@ fatfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
     /* We did not copy the entire directory, which occurs if an error occured */
     if (load.dirleft > 0) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_FWALK;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_FWALK);
+        tsk_error_set_errstr(
             "fatfs_dir_open_meta: Error reading directory %" PRIuINUM,
             a_addr);
 
@@ -704,7 +707,7 @@ fatfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
         if (fs_name == NULL)
             return TSK_ERR;
 
-        // MBR Entry 
+        // MBR Entry
         strncpy(fs_name->name, FATFS_MBRNAME, fs_name->name_size);
         fs_name->meta_addr = FATFS_MBRINO(a_fs);
         fs_name->type = TSK_FS_NAME_TYPE_VIRT;
@@ -714,7 +717,7 @@ fatfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
             return TSK_ERR;
         }
 
-        // FAT1 Entry 
+        // FAT1 Entry
         strncpy(fs_name->name, FATFS_FAT1NAME, fs_name->name_size);
         fs_name->meta_addr = FATFS_FAT1INO(a_fs);
         fs_name->type = TSK_FS_NAME_TYPE_VIRT;
@@ -724,7 +727,7 @@ fatfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
             return TSK_ERR;
         }
 
-        // FAT2 Entry 
+        // FAT2 Entry
         strncpy(fs_name->name, FATFS_FAT2NAME, fs_name->name_size);
         fs_name->meta_addr = FATFS_FAT2INO(a_fs);
         fs_name->type = TSK_FS_NAME_TYPE_VIRT;
diff --git a/tsk3/fs/fatfs_meta.c b/tsk3/fs/fatfs_meta.c
index 269960990..b32be4b19 100644
--- a/tsk3/fs/fatfs_meta.c
+++ b/tsk3/fs/fatfs_meta.c
@@ -1,12 +1,12 @@
 /*
 ** fatfs
-** The Sleuth Kit 
+** The Sleuth Kit
 **
-** Content and meta data layer support for the FAT file system 
+** Content and meta data layer support for the FAT file system
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
-** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved 
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
 **
 ** TASK
 ** Copyright (c) 2002 Brian Carrier, @stake Inc.  All rights reserved
@@ -20,14 +20,12 @@
 
 /**
  * \file fatfs_meta.c
- * Contains the internal TSK FAT file system code to handle metadata structures. 
+ * Contains the internal TSK FAT file system code to handle metadata structures.
  */
 #include "tsk_fs_i.h"
 #include "tsk_fatfs.h"
 
-
-
-/* 
+/*
  * Identify if the dentry is a valid 8.3 name
  *
  * returns 1 if it is, 0 if it does not
@@ -116,8 +114,8 @@ is_83_name(fatfs_dentry * de)
 
     /* Ensure that if we get a "space", that the rest of the
      * name is spaces.  This is not in the spec, but is how
-     * windows operates and serves as a good check to remove 
-     * false positives.  We do not do this check for the 
+     * windows operates and serves as a good check to remove
+     * false positives.  We do not do this check for the
      * volume label though. */
     if ((de->attrib & FATFS_ATTR_VOLUME) != FATFS_ATTR_VOLUME) {
         if (((de->name[1] == 0x20) && (de->name[2] != 0x20)) ||
@@ -140,10 +138,10 @@ is_83_name(fatfs_dentry * de)
 
 /*
 ** Convert the DOS time to the UNIX version
-** 
+**
 ** UNIX stores the time in seconds from 1970 in UTC
 ** FAT dates are the actual date with the year relative to 1980
-** 
+**
 */
 static time_t
 dos2unixtime(uint16_t date, uint16_t time, uint8_t timetens)
@@ -180,7 +178,7 @@ dos2unixtime(uint16_t date, uint16_t time, uint8_t timetens)
         tm1.tm_mon = 0;
 
     /* There is a limit to the year because the UNIX time value is
-     * a 32-bit value 
+     * a 32-bit value
      * the maximum UNIX time is Tue Jan 19 03:14:07 2038
      */
     tm1.tm_year = ((date & FATFS_YEAR_MASK) >> FATFS_YEAR_SHIFT) + 80;
@@ -218,8 +216,8 @@ dos2nanosec(uint8_t timetens)
 }
 
 
-/* 
- * convert the attribute list in FAT to a UNIX mode 
+/*
+ * convert the attribute list in FAT to a UNIX mode
  */
 static TSK_FS_META_TYPE_ENUM
 attr2type(uint16_t attr)
@@ -255,7 +253,7 @@ attr2mode(uint16_t attr)
 }
 
 
-/** 
+/**
  * \internal
  * Copy the contents of a raw directry entry into a TSK_FS_INFO structure.
  *
@@ -268,7 +266,7 @@ attr2mode(uint16_t attr)
  *
  * @returns 1 on error and 0 on success.  Errors should only occur for
  * Unicode conversion problems and when this occurs the name will be
- * NULL terminated (but with unknown contents). 
+ * NULL terminated (but with unknown contents).
  *
  */
 TSK_RETVAL_ENUM
@@ -402,8 +400,8 @@ fatfs_dinode_copy(FATFS_INFO * fatfs, TSK_FS_META * fs_meta,
 
         if (retVal != TSKconversionOK) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_UNICODE;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_UNICODE);
+            tsk_error_set_errstr(
                 "fatfs_dinode_copy: Error converting FAT LFN (1) to UTF8: %d",
                 retVal);
             *name8 = '\0';
@@ -421,8 +419,8 @@ fatfs_dinode_copy(FATFS_INFO * fatfs, TSK_FS_META * fs_meta,
 
         if (retVal != TSKconversionOK) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_UNICODE;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_UNICODE);
+            tsk_error_set_errstr(
                 "fatfs_dinode_copy: Error converting FAT LFN (2) to UTF8: %d",
                 retVal);
             *name8 = '\0';
@@ -440,8 +438,8 @@ fatfs_dinode_copy(FATFS_INFO * fatfs, TSK_FS_META * fs_meta,
 
         if (retVal != TSKconversionOK) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_UNICODE;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_UNICODE);
+            tsk_error_set_errstr(
                 "fatfs_dinode_copy: Error converting FAT LFN (3) to UTF8: %d",
                 retVal);
             *name8 = '\0';
@@ -458,7 +456,7 @@ fatfs_dinode_copy(FATFS_INFO * fatfs, TSK_FS_META * fs_meta,
             *name8 = '\0';
     }
     /* If the entry is for a volume label, then copy the name and
-     * append a special label 
+     * append a special label
      */
     else if ((in->attrib & FATFS_ATTR_VOLUME) == FATFS_ATTR_VOLUME) {
         int a;
@@ -523,7 +521,7 @@ fatfs_dinode_copy(FATFS_INFO * fatfs, TSK_FS_META * fs_meta,
     }
 
     /* FAT does not store a size for its directories so make one based
-     * on the number of allocated sectors 
+     * on the number of allocated sectors
      */
     if ((in->attrib & FATFS_ATTR_DIRECTORY) &&
         ((in->attrib & FATFS_ATTR_LFN) != FATFS_ATTR_LFN)) {
@@ -567,7 +565,7 @@ fatfs_dinode_copy(FATFS_INFO * fatfs, TSK_FS_META * fs_meta,
         /* if the dir is unallocated, then assume 0 or cluster size
          * Ideally, we would have a smart algo here to do recovery
          * and look for dentries.  However, we do not have that right
-         * now and if we do not add this special check then it can 
+         * now and if we do not add this special check then it can
          * assume that an allocated file cluster chain belongs to the
          * directory */
         else {
@@ -588,7 +586,7 @@ fatfs_dinode_copy(FATFS_INFO * fatfs, TSK_FS_META * fs_meta,
  * \internal
  * Create an FS_INODE structure for the root directory.  FAT does
  * not have a directory entry for the root directory, but this
- * function collects the needed data to make one. 
+ * function collects the needed data to make one.
  *
  * @param fatfs File system to analyze
  * @param fs_meta Inode structure to copy root directory information into.
@@ -624,13 +622,13 @@ fatfs_make_root(FATFS_INFO * fatfs, TSK_FS_META * fs_meta)
     }
     addr_ptr = (TSK_DADDR_T *) fs_meta->content_ptr;
 
-    /* TSK_FS_TYPE_FAT12 and TSK_FS_TYPE_FAT16 don't use the FAT for root directory, so 
+    /* TSK_FS_TYPE_FAT12 and TSK_FS_TYPE_FAT16 don't use the FAT for root directory, so
      * we will have to fake it.
      */
     if (fatfs->fs_info.ftype != TSK_FS_TYPE_FAT32) {
         TSK_DADDR_T snum;
 
-        /* Other code will have to check this as a special condition 
+        /* Other code will have to check this as a special condition
          */
         addr_ptr[0] = 1;
 
@@ -683,7 +681,7 @@ fatfs_make_root(FATFS_INFO * fatfs, TSK_FS_META * fs_meta)
 
 /**
 * \internal
- * Create an FS_INODE structure for the master boot record. 
+ * Create an FS_INODE structure for the master boot record.
  *
  * @param fatfs File system to analyze
  * @param fs_meta Inode structure to copy file information into.
@@ -728,7 +726,7 @@ fatfs_make_mbr(FATFS_INFO * fatfs, TSK_FS_META * fs_meta)
 
 /**
 * \internal
- * Create an FS_INODE structure for the FAT tables. 
+ * Create an FS_INODE structure for the FAT tables.
  *
  * @param fatfs File system to analyze
  * @param a_which 1 or 2 to choose between defining FAT1 or FAT2
@@ -773,7 +771,7 @@ fatfs_make_fat(FATFS_INFO * fatfs, uint8_t a_which, TSK_FS_META * fs_meta)
         addr_ptr[0] = fatfs->firstfatsect + fatfs->sectperfat;
     }
     else {
-        ////@@@ 
+        ////@@@
     }
 
     fs_meta->attr_state = TSK_FS_META_ATTR_EMPTY;
@@ -788,8 +786,8 @@ fatfs_make_fat(FATFS_INFO * fatfs, uint8_t a_which, TSK_FS_META * fs_meta)
 
 
 
-/* 
- * Is the pointed to buffer a directory entry buffer? 
+/*
+ * Is the pointed to buffer a directory entry buffer?
  *
  * @param a_basic 1 if only basic tests should be performed. 
  * Returns 1 if it is, 0 if not
@@ -941,7 +939,7 @@ fatfs_isdentry(FATFS_INFO * fatfs, fatfs_dentry * de, uint8_t a_basic)
 /**************************************************************************
  *
  * INODE WALKING
- * 
+ *
  *************************************************************************/
 /* Mark the sector used in the bitmap */
 static TSK_WALK_RET_ENUM
@@ -971,7 +969,56 @@ inode_walk_dent_act(TSK_FS_FILE * fs_file, const char *a_path, void *a_ptr)
     return TSK_WALK_CONT;
 }
 
+/* fatfs_dinode_load - look up disk inode & load into fatfs_dentry structure
+ *
+ * return 1 on error and 0 on success
+ * */
+
+uint8_t
+fatfs_dinode_load(TSK_FS_INFO *fs, fatfs_dentry *dep, TSK_INUM_T inum){
+
+    FATFS_INFO *fatfs = (FATFS_INFO *)fs;
+    ssize_t cnt;
+    size_t off;
+    TSK_DADDR_T sect;
+
+   /*
+     * Sanity check.
+     * Account for virtual Orphan directory and virtual files
+     */
+    if ((inum < fs->first_inum) || (inum > fs->last_inum - FATFS_NUM_SPECFILE)) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
+        tsk_error_set_errstr(
+            "fatfs_dinode_load: address: %" PRIuINUM, inum);
+        return 1;
+    }    /* Get the sector that this inode would be in and its offset */
+    sect = FATFS_INODE_2_SECT(fatfs, inum);
+    off = FATFS_INODE_2_OFF(fatfs, inum);
+
+    if (sect > fs->last_block) {
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
+        tsk_error_set_errstr(
+            "fatfs_inode_load Inode %" PRIuINUM
+            " in sector too big for image: %" PRIuDADDR, inum, sect);
+        return 1;
+    }
 
+
+    cnt = tsk_fs_read(fs, sect*fs->block_size+off, (char *)dep, sizeof(fatfs_dentry)); //a_len = fatfs->ssize?
+    if (cnt != sizeof(fatfs_dentry)) {
+        if (cnt >= 0) {
+            tsk_error_reset();
+            tsk_error_set_errno(TSK_ERR_FS_READ);
+        }
+        tsk_error_set_errstr2(
+            "fatfs_inode_load: block: %" PRIuDADDR, sect);
+        return 1;
+    }
+
+    return 0;
+}
 /*
  * walk the inodes
  *
@@ -989,6 +1036,7 @@ fatfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
     TSK_INUM_T end_inum_tmp;
     TSK_FS_FILE *fs_file;
     TSK_DADDR_T sect, ssect, lsect;
+    char * dino_buf;
     fatfs_dentry *dep;
     unsigned int myflags, didx;
     uint8_t *sect_alloc;
@@ -1003,16 +1051,16 @@ fatfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
      */
     if (start_inum < fs->first_inum || start_inum > fs->last_inum) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "%s: Start inode:  %" PRIuINUM "", myname, start_inum);
         return 1;
     }
     else if (end_inum < fs->first_inum || end_inum > fs->last_inum
         || end_inum < start_inum) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "%s: End inode: %" PRIuINUM "", myname, end_inum);
         return 1;
     }
@@ -1036,7 +1084,7 @@ fatfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
             a_flags |= (TSK_FS_META_FLAG_ALLOC | TSK_FS_META_FLAG_UNALLOC);
         }
 
-        /* If neither of the USED or UNUSED a_flags are set, then set them 
+        /* If neither of the USED or UNUSED a_flags are set, then set them
          * both
          */
         if (((a_flags & TSK_FS_META_FLAG_USED) == 0) &&
@@ -1048,15 +1096,12 @@ fatfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
 
     /* If we are looking for orphan files and have not yet filled
      * in the list of unalloc inodes that are pointed to, then fill
-     * in the list 
+     * in the list
      */
-    if ((a_flags & TSK_FS_META_FLAG_ORPHAN)
-        && (fs->list_inum_named == NULL)) {
-
+    if ((a_flags & TSK_FS_META_FLAG_ORPHAN)) {
         if (tsk_fs_dir_load_inum_named(fs) != TSK_OK) {
-            strncat(tsk_errstr2,
-                " - fatfs_inode_walk: identifying inodes allocated by file names",
-                TSK_ERRSTR_L);
+            tsk_error_errstr2_concat(
+                "- fatfs_inode_walk: identifying inodes allocated by file names");
             return 1;
         }
     }
@@ -1095,7 +1140,7 @@ fatfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
         /* advance it so that it is a valid starting point */
         start_inum++;
 
-        // exit if that is all that was requested 
+        // exit if that is all that was requested
         if (start_inum == end_inum) {
             tsk_fs_file_close(fs_file);
             return 0;
@@ -1106,9 +1151,9 @@ fatfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
      * entries.  We can make mistakes and ignore sectors that have valid
      * entries in them.  To make sure we at least get all sectors that
      * are allocated by directories in the directory tree, we will
-     * run name_walk and then a file walk on each dir. 
+     * run name_walk and then a file walk on each dir.
      * We'll be make sure to print those.  We skip this for ORPHAN hunting
-     * because it doesn't help and can introduce infinite loop situations  
+     * because it doesn't help and can introduce infinite loop situations
      * inode_walk was called by the function that determines which inodes
      * are orphans. */
     if (tsk_verbose)
@@ -1138,31 +1183,30 @@ fatfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
             return 1;
         }
 
-        // now get the rest of the directories. 
+        // now get the rest of the directories.
         if (tsk_fs_dir_walk(fs, fs->root_inum,
                 TSK_FS_DIR_WALK_FLAG_ALLOC | TSK_FS_DIR_WALK_FLAG_RECURSE |
                 TSK_FS_DIR_WALK_FLAG_NOORPHAN, inode_walk_dent_act,
                 (void *) sect_alloc)) {
-            strncat(tsk_errstr2,
-                " - fatfs_inode_walk: mapping directories", TSK_ERRSTR_L);
+            tsk_error_errstr2_concat(
+                "- fatfs_inode_walk: mapping directories");
             tsk_fs_file_close(fs_file);
             free(sect_alloc);
             return 1;
         }
     }
 
-
     /* start analyzing each sector
      *
      * Perform a test on the first 32 bytes of each sector to identify if
      * the sector contains directory entries.  If it does, then continue
-     * to analyze it.  If not, then read the next sector 
+     * to analyze it.  If not, then read the next sector
      */
 
     /* identify the starting and ending inodes sector addrs */
 
-    /* we need to handle end_inum specially if it is for the 
-     * virtual ORPHANS directory or virtual FAT files.  
+    /* we need to handle end_inum specially if it is for the
+     * virtual ORPHANS directory or virtual FAT files.
      * Handle these outside of the loop. */
     if (end_inum > fs->last_inum - FATFS_NUM_SPECFILE)
         end_inum_tmp = fs->last_inum - FATFS_NUM_SPECFILE;
@@ -1174,8 +1218,8 @@ fatfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
 
     if (ssect > fs->last_block) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "fatfs_inode_walk: Starting inode in sector too big for image: %"
             PRIuDADDR, ssect);
         tsk_fs_file_close(fs_file);
@@ -1184,8 +1228,8 @@ fatfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
     }
     else if (lsect > fs->last_block) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "fatfs_inode_walk: Ending inode in sector too big for image: %"
             PRIuDADDR, lsect);
         tsk_fs_file_close(fs_file);
@@ -1194,13 +1238,18 @@ fatfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
     }
 
     sect = ssect;
+    if ((dino_buf = (char *) tsk_malloc(fatfs->csize << fatfs->ssize_sh)) == NULL){
+        tsk_fs_file_close(fs_file);
+        free(sect_alloc);
+        return 1;
+    }
     while (sect <= lsect) {
         int clustalloc;         // 1 if current sector / cluster is allocated
         size_t sect_proc;       // number of sectors read for this loop
         size_t sidx;            // sector index for loop
 
-        /* This occurs for the root directory of TSK_FS_TYPE_FAT12/16 
-         * 
+        /* This occurs for the root directory of TSK_FS_TYPE_FAT12/16
+         *
          * We are going to process the image in clusters, so take care of the root
          * directory seperately.
          */
@@ -1216,17 +1265,18 @@ fatfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
 
             /* read the sector */
             cnt =
-                tsk_fs_read_block(fs, sect, fatfs->dinodes, fatfs->ssize);
+                tsk_fs_read_block(fs, sect, dino_buf, fatfs->ssize);
             if (cnt != fatfs->ssize) {
                 if (cnt >= 0) {
                     tsk_error_reset();
-                    tsk_errno = TSK_ERR_FS_READ;
+                    tsk_error_set_errno(TSK_ERR_FS_READ);
                 }
-                snprintf(tsk_errstr2, TSK_ERRSTR_L,
+                tsk_error_set_errstr2(
                     "fatfs_inode_walk (root dir): sector: %" PRIuDADDR,
                     sect);
                 tsk_fs_file_close(fs_file);
                 free(sect_alloc);
+                free(dino_buf);
                 return 1;
             }
             sect_proc = 1;
@@ -1240,7 +1290,7 @@ fatfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
                 FATFS_CLUST_2_SECT(fatfs, (FATFS_SECT_2_CLUST(fatfs,
                         sect)));
 
-            /* if the cluster is not allocated, then do not go into it if we 
+            /* if the cluster is not allocated, then do not go into it if we
              * only want allocated/link entries
              * If it is allocated, then go into it no matter what
              */
@@ -1248,6 +1298,7 @@ fatfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
             if (clustalloc == -1) {
                 tsk_fs_file_close(fs_file);
                 free(sect_alloc);
+                free(dino_buf);
                 return 1;
             }
             else if ((clustalloc == 0)
@@ -1274,16 +1325,17 @@ fatfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
 
             /* read the full cluster */
             cnt = tsk_fs_read_block
-                (fs, sect, fatfs->dinodes, sect_proc << fatfs->ssize_sh);
+                (fs, sect, dino_buf, sect_proc << fatfs->ssize_sh);
             if (cnt != (sect_proc << fatfs->ssize_sh)) {
                 if (cnt >= 0) {
                     tsk_error_reset();
-                    tsk_errno = TSK_ERR_FS_READ;
+                    tsk_error_set_errno(TSK_ERR_FS_READ);
                 }
-                snprintf(tsk_errstr2, TSK_ERRSTR_L,
+                tsk_error_set_errstr2(
                     "fatfs_inode_walk: sector: %" PRIuDADDR, sect);
                 tsk_fs_file_close(fs_file);
                 free(sect_alloc);
+                free(dino_buf);
                 return 1;
             }
         }
@@ -1293,8 +1345,8 @@ fatfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
             TSK_INUM_T inum;
             uint8_t isInDir;
 
-            dep =
-                (fatfs_dentry *) & fatfs->dinodes[sidx << fatfs->ssize_sh];
+            dep = 
+               (fatfs_dentry *) & dino_buf[sidx << fatfs->ssize_sh];
 
             /* if we know it is not part of a directory and it is not valid dentires,
              * then skip it */
@@ -1320,6 +1372,7 @@ fatfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
                     " starting at inode %" PRIuINUM "\n", sect, inum);
 
             /* cycle through the directory entries */
+            
             for (didx = 0; didx < fatfs->dentry_cnt_se;
                 didx++, inum++, dep++) {
                 int retval;
@@ -1336,7 +1389,7 @@ fatfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
                 }
 
 
-                /* if this is a long file name entry, then skip it and 
+                /* if this is a long file name entry, then skip it and
                  * wait for the short name */
                 if ((dep->attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN)
                     continue;
@@ -1350,7 +1403,7 @@ fatfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
                     continue;
 
 
-                /* Allocation status 
+                /* Allocation status
                  * This is determined first by the sector allocation status
                  * an then the dentry flag.  When a directory is deleted, the
                  * contents are not always set to unallocated
@@ -1376,11 +1429,11 @@ fatfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
                     continue;
 
                 /* If we want only orphans, then check if this
-                 * inode is in the seen list 
+                 * inode is in the seen list
                  */
                 if ((myflags & TSK_FS_META_FLAG_UNALLOC) &&
                     (a_flags & TSK_FS_META_FLAG_ORPHAN) &&
-                    (tsk_list_find(fs->list_inum_named, inum))) {
+                    (tsk_fs_dir_find_inum_named(fs, inum))) {
                     continue;
                 }
 
@@ -1401,6 +1454,7 @@ fatfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
                     else {
                         tsk_fs_file_close(fs_file);
                         free(sect_alloc);
+                        free(dino_buf);
                         return 1;
                     }
                 }
@@ -1415,11 +1469,13 @@ fatfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
                 if (retval == TSK_WALK_STOP) {
                     tsk_fs_file_close(fs_file);
                     free(sect_alloc);
+                    free(dino_buf);
                     return 0;
                 }
                 else if (retval == TSK_WALK_ERROR) {
                     tsk_fs_file_close(fs_file);
                     free(sect_alloc);
+                    free(dino_buf);
                     return 1;
                 }
             }                   /* dentries */
@@ -1433,6 +1489,7 @@ fatfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
 
 
     free(sect_alloc);
+    free(dino_buf);
 
 
     // handle the virtual orphans folder and FAT files if they asked for them
@@ -1491,6 +1548,7 @@ fatfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
 }                               /* end of inode_walk */
 
 
+
 /*
  * return the contents of a specific inode
  *
@@ -1502,32 +1560,30 @@ fatfs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file,
     TSK_INUM_T inum)
 {
     FATFS_INFO *fatfs = (FATFS_INFO *) fs;
-    ssize_t cnt;
     TSK_DADDR_T sect;
-    size_t off;
     TSK_RETVAL_ENUM retval;
+    fatfs_dentry dep;
 
     // clean up any error messages that are lying around
     tsk_error_reset();
 
-    /* 
+    /*
      * Sanity check.
      */
     if (inum < fs->first_inum || inum > fs->last_inum) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_INODE_NUM;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
+        tsk_error_set_errstr(
             "fatfs_inode_lookup: %" PRIuINUM " too large/small", inum);
         return 1;
     }
 
     if (a_fs_file == NULL) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "fatfs_inode_lookup: fs_file is NULL");
         return 1;
     }
-
     if (a_fs_file->meta == NULL) {
         if ((a_fs_file->meta =
                 tsk_fs_meta_alloc(FATFS_FILE_CONTENT_LEN)) == NULL)
@@ -1539,35 +1595,30 @@ fatfs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file,
 
     /* As there is no real root inode in FAT, use the made up one */
     if (inum == FATFS_ROOTINO) {
-        fatfs->dep = NULL;
         if (fatfs_make_root(fatfs, a_fs_file->meta))
             return 1;
         else
             return 0;
     }
     else if (inum == FATFS_MBRINO(fs)) {
-        fatfs->dep = NULL;
         if (fatfs_make_mbr(fatfs, a_fs_file->meta))
             return 1;
         else
             return 0;
     }
     else if (inum == FATFS_FAT1INO(fs)) {
-        fatfs->dep = NULL;
         if (fatfs_make_fat(fatfs, 1, a_fs_file->meta))
             return 1;
         else
             return 0;
     }
     else if (inum == FATFS_FAT2INO(fs)) {
-        fatfs->dep = NULL;
         if (fatfs_make_fat(fatfs, 2, a_fs_file->meta))
             return 1;
         else
             return 0;
     }
     else if (inum == TSK_FS_ORPHANDIR_INUM(fs)) {
-        fatfs->dep = NULL;
         if (tsk_fs_dir_make_orphan_dir_meta(fs, a_fs_file->meta))
             return 1;
         else
@@ -1576,13 +1627,12 @@ fatfs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file,
 
     /* Get the sector that this inode would be in and its offset */
     sect = FATFS_INODE_2_SECT(fatfs, inum);
-    off = FATFS_INODE_2_OFF(fatfs, inum);
 
     if (sect > fs->last_block) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_INODE_NUM;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
-            "fatfs_inode_lookup: Inode %" PRIuINUM
+        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
+        tsk_error_set_errstr(
+            "fatfs_inode_lookup Inode %" PRIuINUM
             " in sector too big for image: %" PRIuDADDR, inum, sect);
         return 1;
     }
@@ -1593,23 +1643,17 @@ fatfs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file,
             "fatfs_inode_lookup: reading sector %" PRIuDADDR
             " for inode %" PRIuINUM "\n", sect, inum);
 
-    cnt = tsk_fs_read_block(fs, sect, fatfs->dinodes, fatfs->ssize);
-    if (cnt != fatfs->ssize) {
-        if (cnt >= 0) {
-            tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_READ;
-        }
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
-            "fatfs_inode_lookup: block: %" PRIuDADDR, sect);
+    if (fatfs_dinode_load(fs, &dep, inum)) {
         return 1;
     }
 
-    fatfs->dep = (fatfs_dentry *) & fatfs->dinodes[off];
-    if (fatfs_isdentry(fatfs, fatfs->dep, 1)) {
+
+    //dep = (fatfs_dentry *) & dino_buf[off];
+    if (fatfs_isdentry(fatfs, &dep, 1)) {
         if ((retval =
-                fatfs_dinode_copy(fatfs, a_fs_file->meta, fatfs->dep, sect,
+                fatfs_dinode_copy(fatfs, a_fs_file->meta, &dep, sect,
                     inum)) != TSK_OK) {
-            /* If there was a unicode conversion error, 
+            /* If there was a unicode conversion error,
              * then still return the inode */
             if (retval == TSK_ERR) {
                 return 1;
@@ -1622,18 +1666,18 @@ fatfs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file,
         }
         return 0;
     }
+
     else {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_INODE_NUM;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
+        tsk_error_set_errstr(
             "fatfs_inode_lookup: %" PRIuINUM " is not an inode", inum);
         return 1;
     }
 }
 
-
 /** \internal
- * Process the file and load up the clusters into the FS_DATA attribute 
+ * Process the file and load up the clusters into the FS_DATA attribute
  * in fs_meta. The run will list the starting sector and length in sectors
  *
  * @param a_fs_file File to process and structore to store results in
@@ -1652,8 +1696,8 @@ fatfs_make_data_run(TSK_FS_FILE * a_fs_file)
 
     if ((a_fs_file == NULL) || (a_fs_file->meta == NULL)
         || (a_fs_file->fs_info == NULL)) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "fatfs_make_data_run: called with NULL pointers");
         return 1;
     }
@@ -1686,17 +1730,17 @@ fatfs_make_data_run(TSK_FS_FILE * a_fs_file)
         fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
         tsk_error_reset();
         if (a_fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC)
-            tsk_errno = TSK_ERR_FS_RECOVER;
+            tsk_error_set_errno(TSK_ERR_FS_RECOVER);
         else
-            tsk_errno = TSK_ERR_FS_INODE_COR;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+        tsk_error_set_errstr(
             "fatfs_make_data_run: Starting cluster address too large: %"
             PRIuDADDR, clust);
         return 1;
     }
 
 
-    /* We need to handle the special files specially because they 
+    /* We need to handle the special files specially because they
      * are not in the FAT.  Except for FAT32 root dirs, those are normal.
      */
     if ((a_fs_file->meta->addr == FATFS_ROOTINO)
@@ -1772,7 +1816,7 @@ fatfs_make_data_run(TSK_FS_FILE * a_fs_file)
     }
 
 
-    /* A deleted file that we want to recover 
+    /* A deleted file that we want to recover
      * In this case, we could get a lot of errors because of inconsistent
      * data.  TO make it clear that these are from a recovery, we set most
      * error codes to _RECOVER so that they can be more easily suppressed.
@@ -1804,8 +1848,8 @@ fatfs_make_data_run(TSK_FS_FILE * a_fs_file)
 
         if (sbase > fs->last_block) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_RECOVER;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_RECOVER);
+            tsk_error_set_errstr(
                 "fatfs_make_data_run: Starting cluster address too large (recovery): %"
                 PRIuDADDR, sbase);
             fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
@@ -1823,7 +1867,7 @@ fatfs_make_data_run(TSK_FS_FILE * a_fs_file)
 
 
         /* Part 1 is to make sure there are enough unallocated clusters
-         * for the size of the file 
+         * for the size of the file
          */
         clust = startclust;
         size_remain = recoversize;
@@ -1833,7 +1877,7 @@ fatfs_make_data_run(TSK_FS_FILE * a_fs_file)
             int retval;
             sbase = FATFS_CLUST_2_SECT(fatfs, clust);
 
-            /* Are we past the end of the FS? 
+            /* Are we past the end of the FS?
              * that means we could not find enough unallocated clusters
              * for the file size */
             if (sbase > fs->last_block) {
@@ -1959,8 +2003,8 @@ fatfs_make_data_run(TSK_FS_FILE * a_fs_file)
                 fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
                 tsk_error_reset();
 
-                tsk_errno = TSK_ERR_FS_INODE_COR;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+                tsk_error_set_errstr(
                     "fatfs_make_data_run: Invalid sector address in FAT (too large): %"
                     PRIuDADDR, sbase);
                 return 1;
@@ -1999,7 +2043,7 @@ fatfs_make_data_run(TSK_FS_FILE * a_fs_file)
             if ((int64_t) size_remain > 0) {
                 TSK_DADDR_T nxt;
                 if (fatfs_getFAT(fatfs, clust, &nxt)) {
-                    snprintf(tsk_errstr2, TSK_ERRSTR_L,
+                    tsk_error_set_errstr2(
                         "file walk: Inode: %" PRIuINUM "  cluster: %"
                         PRIuDADDR, fs_meta->addr, clust);
                     fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
diff --git a/tsk3/fs/ffind_lib.c b/tsk3/fs/ffind_lib.c
index eadfbd51a..1099f7b7d 100644
--- a/tsk3/fs/ffind_lib.c
+++ b/tsk3/fs/ffind_lib.c
@@ -5,7 +5,7 @@
 ** Find the file that uses the specified inode (including deleted files)
 ** 
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved 
 **
 ** TASK
diff --git a/tsk3/fs/ffs.c b/tsk3/fs/ffs.c
index 60eece4a4..4befb529a 100644
--- a/tsk3/fs/ffs.c
+++ b/tsk3/fs/ffs.c
@@ -1,18 +1,18 @@
 /*
-** The Sleuth Kit 
+** The Sleuth Kit
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
-** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved 
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
 **
 ** TASK
 ** Copyright (c) 2002-2003 Brian Carrier, @stake Inc.  All rights reserved
-** 
-** Copyright (c) 1997,1998,1999, International Business Machines          
+**
+** Copyright (c) 1997,1998,1999, International Business Machines
 ** Corporation and others. All Rights Reserved.
 */
 
-/* TCT 
+/* TCT
  * LICENSE
  *	This software is distributed under the IBM Public License.
  * AUTHOR(S)
@@ -32,7 +32,9 @@
 
 
 
-/* ffs_group_load - load cylinder group descriptor info into cache 
+/* ffs_group_load - load cylinder group descriptor info into cache
+ *
+ * Note: This routine assumes &ffs->lock is locked by the caller.
  *
  * return 1 on error and 0 on success
  * */
@@ -47,8 +49,8 @@ ffs_group_load(FFS_INFO * ffs, FFS_GRPNUM_T grp_num)
      */
     if (grp_num < 0 || grp_num >= ffs->groups_count) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "ffs_group_load: invalid cylinder group number: %"
             PRI_FFSGRP "", grp_num);
         return 1;
@@ -60,8 +62,9 @@ ffs_group_load(FFS_INFO * ffs, FFS_GRPNUM_T grp_num)
      * 4.4BSD <ufs/ffs/fs.h> include file).
      */
     if (ffs->grp_buf == NULL) {
-        if ((ffs->grp_buf = tsk_malloc(ffs->ffsbsize_b)) == NULL)
+        if ((ffs->grp_buf = tsk_malloc(ffs->ffsbsize_b)) == NULL) {
             return 1;
+        }
     }
 
     addr = cgtod_lcl(fs, ffs->fs.sb1, grp_num);
@@ -72,9 +75,9 @@ ffs_group_load(FFS_INFO * ffs, FFS_GRPNUM_T grp_num)
         if (cnt != ffs->ffsbsize_b) {
             if (cnt >= 0) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_READ;
+                tsk_error_set_errno(TSK_ERR_FS_READ);
             }
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
+            tsk_error_set_errstr2(
                 "ffs_group_load: Group %" PRI_FFSGRP " at %" PRIuDADDR,
                 grp_num, addr);
             return 1;
@@ -86,8 +89,8 @@ ffs_group_load(FFS_INFO * ffs, FFS_GRPNUM_T grp_num)
         if ((tsk_gets32(fs->endian, cg->cg_iusedoff) > ffs->ffsbsize_b)
             || (tsk_gets32(fs->endian, cg->cg_freeoff) > ffs->ffsbsize_b)) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_CORRUPT;
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_CORRUPT);
+            tsk_error_set_errstr2(
                 "ffs_group_load: Group %" PRI_FFSGRP
                 " descriptor offsets too large at %" PRIuDADDR, grp_num,
                 addr);
@@ -100,13 +103,13 @@ ffs_group_load(FFS_INFO * ffs, FFS_GRPNUM_T grp_num)
 }
 
 
-/* 
- * ffs_dinode_load - read disk inode and load into local cache (ffs->dino_buf)
+/*
+ * ffs_dinode_load - read disk inode and load the data into ffs_inode structure
  *
  * Return 0 on success and 1 on error
  */
 static uint8_t
-ffs_dinode_load(FFS_INFO * ffs, TSK_INUM_T inum)
+ffs_dinode_load(FFS_INFO * ffs, TSK_INUM_T inum, ffs_inode * dino_buf)
 {
     TSK_DADDR_T addr;
     TSK_OFF_T offs;
@@ -118,8 +121,8 @@ ffs_dinode_load(FFS_INFO * ffs, TSK_INUM_T inum)
      */
     if (inum < fs->first_inum || inum > fs->last_inum - 1) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_INODE_NUM;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
+        tsk_error_set_errstr(
             "ffs_dinode_load: address: %" PRIuINUM, inum);
         return 1;
     }
@@ -127,9 +130,15 @@ ffs_dinode_load(FFS_INFO * ffs, TSK_INUM_T inum)
     /*
      * Allocate/read the inode table buffer on the fly.
      */
+
+    /* lock access to itbl_buf */
+    tsk_take_lock(&ffs->lock);
+
     if (ffs->itbl_buf == NULL) {
-        if ((ffs->itbl_buf = tsk_malloc(ffs->ffsbsize_b)) == NULL)
+        if ((ffs->itbl_buf = tsk_malloc(ffs->ffsbsize_b)) == NULL) {
+            tsk_release_lock(&ffs->lock);
             return 1;
+        }
     }
 
 
@@ -142,23 +151,18 @@ ffs_dinode_load(FFS_INFO * ffs, TSK_INUM_T inum)
         ffs_cgd2 *cg2;
         FFS_GRPNUM_T grp_num;
 
-        if (ffs->dino_buf == NULL) {
-            ffs->dino_buf = (char *) tsk_malloc(sizeof(ffs_inode2));
-            if (ffs->dino_buf == NULL)
-                return 1;
-        }
-        else if (ffs->dino_inum == inum) {
-            return 0;
+        if (dino_buf == NULL) {
+            tsk_release_lock(&ffs->lock);
+            return 1;
         }
 
         /* Lookup the cylinder group descriptor if it isn't
          * cached
          */
         grp_num = (FFS_GRPNUM_T) itog_lcl(fs, ffs->fs.sb1, inum);
-        if ((ffs->grp_buf == NULL) || (grp_num != ffs->grp_num)) {
-            if (ffs_group_load(ffs, grp_num)) {
-                return 1;
-            }
+        if (ffs_group_load(ffs, grp_num)) {
+            tsk_release_lock(&ffs->lock);
+            return 1;
         }
 
         cg2 = (ffs_cgd2 *) ffs->grp_buf;
@@ -167,7 +171,7 @@ ffs_dinode_load(FFS_INFO * ffs, TSK_INUM_T inum)
         if ((inum - grp_num * tsk_getu32(fs->endian,
                     ffs->fs.sb2->cg_inode_num)) >= tsk_getu32(fs->endian,
                 cg2->cg_initediblk)) {
-            memset((char *) ffs->dino_buf, 0, sizeof(ffs_inode2));
+            memset((char *)dino_buf, 0, sizeof(ffs_inode2));
         }
 
         else {
@@ -179,11 +183,12 @@ ffs_dinode_load(FFS_INFO * ffs, TSK_INUM_T inum)
                 cnt = tsk_fs_read_block
                     (fs, addr, ffs->itbl_buf, ffs->ffsbsize_b);
                 if (cnt != ffs->ffsbsize_b) {
+                    tsk_release_lock(&ffs->lock);
                     if (cnt >= 0) {
                         tsk_error_reset();
-                        tsk_errno = TSK_ERR_FS_READ;
+                        tsk_error_set_errno(TSK_ERR_FS_READ);
                     }
-                    snprintf(tsk_errstr2, TSK_ERRSTR_L,
+                    tsk_error_set_errstr2(
                         "ffs_dinode_load: FFS2 inode table at %"
                         PRIuDADDR, addr);
                     return 1;
@@ -193,18 +198,14 @@ ffs_dinode_load(FFS_INFO * ffs, TSK_INUM_T inum)
 
             offs = itoo_lcl(fs, ffs->fs.sb2, inum) * sizeof(ffs_inode2);
 
-            memcpy((char *) ffs->dino_buf, ffs->itbl_buf + offs,
+            memcpy((char *) dino_buf, ffs->itbl_buf + offs,
                 sizeof(ffs_inode2));
         }
     }
     else {
-        if (ffs->dino_buf == NULL) {
-            ffs->dino_buf = (char *) tsk_malloc(sizeof(ffs_inode1));
-            if (ffs->dino_buf == NULL)
-                return 1;
-        }
-        else if (ffs->dino_inum == inum) {
-            return 0;
+        if (dino_buf == NULL) {
+            tsk_release_lock(&ffs->lock);
+            return 1;
         }
 
         addr = itod_lcl(fs, ffs->fs.sb1, inum);
@@ -214,11 +215,12 @@ ffs_dinode_load(FFS_INFO * ffs, TSK_INUM_T inum)
                 tsk_fs_read_block(fs, addr, ffs->itbl_buf,
                 ffs->ffsbsize_b);
             if (cnt != ffs->ffsbsize_b) {
+                tsk_release_lock(&ffs->lock);
                 if (cnt >= 0) {
                     tsk_error_reset();
-                    tsk_errno = TSK_ERR_FS_READ;
+                    tsk_error_set_errno(TSK_ERR_FS_READ);
                 }
-                snprintf(tsk_errstr2, TSK_ERRSTR_L,
+                tsk_error_set_errstr2(
                     "ffs_dinode_load: FFS1 inode table at %"
                     PRIuDADDR, addr);
                 return 1;
@@ -228,10 +230,12 @@ ffs_dinode_load(FFS_INFO * ffs, TSK_INUM_T inum)
 
         offs = itoo_lcl(fs, ffs->fs.sb1, inum) * sizeof(ffs_inode1);
 
-        memcpy((char *) ffs->dino_buf, ffs->itbl_buf + offs,
+        memcpy((char *) dino_buf, ffs->itbl_buf + offs,
             sizeof(ffs_inode1));
     }
-    ffs->dino_inum = inum;
+
+    tsk_release_lock(&ffs->lock);
+
     return 0;
 }
 
@@ -299,12 +303,12 @@ ffsmode2tskmode(uint16_t a_mode)
     return mode;
 }
 
-/* ffs_dinode_copy - copy cached disk inode to generic inode  
+/* ffs_dinode_copy - copy cached disk inode to generic inode
  *
  * Return 1 on error and 0 on success
  */
 static uint8_t
-ffs_dinode_copy(FFS_INFO * ffs, TSK_FS_META * fs_meta)
+ffs_dinode_copy(FFS_INFO * ffs, TSK_FS_META * fs_meta, TSK_INUM_T dino_inum, const ffs_inode* dino_buf)
 {
     int i, j;
     unsigned int count;
@@ -314,10 +318,10 @@ ffs_dinode_copy(FFS_INFO * ffs, TSK_FS_META * fs_meta)
     unsigned char *inosused = NULL;
     TSK_INUM_T ibase;
 
-    if (ffs->dino_buf == NULL) {
+    if (dino_buf == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "ffs_dinode_copy: dino_buf is NULL");
         return 1;
     }
@@ -336,11 +340,11 @@ ffs_dinode_copy(FFS_INFO * ffs, TSK_FS_META * fs_meta)
         fs_meta->link = NULL;
     }
 
-    fs_meta->addr = ffs->dino_inum;
+    fs_meta->addr = dino_inum;
 
     /* OpenBSD and FreeBSD style */
     if (fs->ftype == TSK_FS_TYPE_FFS1) {
-        ffs_inode1 *in = (ffs_inode1 *) ffs->dino_buf;
+        ffs_inode1 *in = (ffs_inode1 *) dino_buf;
         TSK_DADDR_T *addr_ptr;
 
         fs_meta->mode =
@@ -377,7 +381,7 @@ ffs_dinode_copy(FFS_INFO * ffs, TSK_FS_META * fs_meta)
                 tsk_gets32(fs->endian, in->di_ib[i]);
 
 
-        /* set the link string (if the file is a link) 
+        /* set the link string (if the file is a link)
          * The size check is a sanity check so that we don't try and allocate
          * a huge amount of memory for a bad inode value
          */
@@ -447,9 +451,9 @@ ffs_dinode_copy(FFS_INFO * ffs, TSK_FS_META * fs_meta)
                     if (cnt != fs->block_size) {
                         if (cnt >= 0) {
                             tsk_error_reset();
-                            tsk_errno = TSK_ERR_FS_READ;
+                            tsk_error_set_errno(TSK_ERR_FS_READ);
                         }
-                        snprintf(tsk_errstr2, TSK_ERRSTR_L,
+                        tsk_error_set_errstr2(
                             "ffs_dinode_copy: FFS1A symlink dest at %"
                             PRIuDADDR, addr_ptr[i]);
                         free(buf);
@@ -477,7 +481,7 @@ ffs_dinode_copy(FFS_INFO * ffs, TSK_FS_META * fs_meta)
     }
     /* TSK_FS_TYPE_FFS1B - Solaris */
     else if (fs->ftype == TSK_FS_TYPE_FFS1B) {
-        ffs_inode1b *in = (ffs_inode1b *) ffs->dino_buf;
+        ffs_inode1b *in = (ffs_inode1b *) dino_buf;
         TSK_DADDR_T *addr_ptr;
 
         fs_meta->mode =
@@ -579,9 +583,9 @@ ffs_dinode_copy(FFS_INFO * ffs, TSK_FS_META * fs_meta)
                     if (cnt != fs->block_size) {
                         if (cnt >= 0) {
                             tsk_error_reset();
-                            tsk_errno = TSK_ERR_FS_READ;
+                            tsk_error_set_errno(TSK_ERR_FS_READ);
                         }
-                        snprintf(tsk_errstr2, TSK_ERRSTR_L,
+                        tsk_error_set_errstr2(
                             "ffs_dinode_copy: FFS1B symlink dest at %"
                             PRIuDADDR, addr_ptr[i]);
                         free(buf);
@@ -601,7 +605,7 @@ ffs_dinode_copy(FFS_INFO * ffs, TSK_FS_META * fs_meta)
         }
     }
     else if (fs->ftype == TSK_FS_TYPE_FFS2) {
-        ffs_inode2 *in = (ffs_inode2 *) ffs->dino_buf;
+        ffs_inode2 *in = (ffs_inode2 *)dino_buf;
         TSK_DADDR_T *addr_ptr;
 
         fs_meta->mode =
@@ -640,7 +644,7 @@ ffs_dinode_copy(FFS_INFO * ffs, TSK_FS_META * fs_meta)
                 tsk_gets64(fs->endian, in->di_ib[i]);
 
 
-        /* set the link string (if the file is a link) 
+        /* set the link string (if the file is a link)
          * The size check is a sanity check so that we don't try and allocate
          * a huge amount of memory for a bad inode value
          */
@@ -655,7 +659,7 @@ ffs_dinode_copy(FFS_INFO * ffs, TSK_FS_META * fs_meta)
 
             count = 0;          /* index into the link array */
 
-            /* it is located directly in the pointers  
+            /* it is located directly in the pointers
              * Only the new style inode has this "fast link"
              */
             if (fs_meta->size < 8 * (FFS_NDADDR + FFS_NIADDR)) {
@@ -709,9 +713,9 @@ ffs_dinode_copy(FFS_INFO * ffs, TSK_FS_META * fs_meta)
                     if (cnt != fs->block_size) {
                         if (cnt >= 0) {
                             tsk_error_reset();
-                            tsk_errno = TSK_ERR_FS_READ;
+                            tsk_error_set_errno(TSK_ERR_FS_READ);
                         }
-                        snprintf(tsk_errstr2, TSK_ERRSTR_L,
+                        tsk_error_set_errstr2(
                             "ffs_dinode_copy: FFS2 symlink dest at %"
                             PRIuDADDR, addr_ptr[i]);
                         free(buf);
@@ -731,17 +735,19 @@ ffs_dinode_copy(FFS_INFO * ffs, TSK_FS_META * fs_meta)
     }
     else {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "ffs_dinode_copy: Unknown FFS Type");
         return 1;
     }
 
     /* set the flags */
-    grp_num = (FFS_GRPNUM_T) itog_lcl(fs, ffs->fs.sb1, ffs->dino_inum);
-    if ((ffs->grp_buf == NULL) || (grp_num != ffs->grp_num)) {
-        if (ffs_group_load(ffs, grp_num))
-            return 1;
+    grp_num = (FFS_GRPNUM_T) itog_lcl(fs, ffs->fs.sb1, dino_inum);
+
+    tsk_take_lock(&ffs->lock);
+    if (ffs_group_load(ffs, grp_num)) {
+        tsk_release_lock(&ffs->lock);
+        return 1;
     }
 
     cg = (ffs_cgd *) ffs->grp_buf;
@@ -750,9 +756,11 @@ ffs_dinode_copy(FFS_INFO * ffs, TSK_FS_META * fs_meta)
     ibase = grp_num * tsk_gets32(fs->endian, ffs->fs.sb1->cg_inode_num);
 
     /* get the alloc flag */
-    fs_meta->flags = (isset(inosused, ffs->dino_inum - ibase) ?
+    fs_meta->flags = (isset(inosused, dino_inum - ibase) ?
         TSK_FS_META_FLAG_ALLOC : TSK_FS_META_FLAG_UNALLOC);
 
+    tsk_release_lock(&ffs->lock);
+
     /* used/unused */
     fs_meta->flags |= (fs_meta->ctime ?
         TSK_FS_META_FLAG_USED : TSK_FS_META_FLAG_UNUSED);
@@ -763,7 +771,7 @@ ffs_dinode_copy(FFS_INFO * ffs, TSK_FS_META * fs_meta)
 
 
 
-/* ffs_inode_lookup - lookup inode, external interface 
+/* ffs_inode_lookup - lookup inode, external interface
  *
  * Return 1 on error
  *
@@ -772,11 +780,12 @@ static uint8_t
 ffs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file,
     TSK_INUM_T inum)
 {
+    ffs_inode * dino_buf;
     FFS_INFO *ffs = (FFS_INFO *) fs;
 
     if (a_fs_file == NULL) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "ffs_inode_lookup: fs_file is NULL");
         return 1;
     }
@@ -800,11 +809,22 @@ ffs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file,
     }
 
     /* Lookup the inode and store it in ffs */
-    if (ffs_dinode_load(ffs, inum))
+    if ((dino_buf = (ffs_inode *) tsk_malloc(sizeof(ffs_inode2))) == NULL)
         return 1;
 
-    if (ffs_dinode_copy(ffs, a_fs_file->meta))
+    if (ffs_dinode_load(ffs, inum, dino_buf)){
+        tsk_fs_file_close(a_fs_file);
+        free(dino_buf);
         return 1;
+    }
+
+    if (ffs_dinode_copy(ffs, a_fs_file->meta, inum, dino_buf)){
+        tsk_fs_file_close(a_fs_file);
+        free(dino_buf);
+        return 1;
+    }
+
+    free (dino_buf);
 
     return 0;
 }
@@ -819,9 +839,9 @@ ffs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file,
 
 
 
-/* ffs_inode_walk - inode iterator 
+/* ffs_inode_walk - inode iterator
  *
- * flags used: TSK_FS_META_FLAG_USED, TSK_FS_META_FLAG_UNUSED, 
+ * flags used: TSK_FS_META_FLAG_USED, TSK_FS_META_FLAG_UNUSED,
  *  TSK_FS_META_FLAG_ALLOC, TSK_FS_META_FLAG_UNALLOC, TSK_FS_META_FLAG_ORPHAN
  *
  *  return 1 on error and 0 on success
@@ -841,6 +861,7 @@ ffs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
     int myflags;
     TSK_INUM_T ibase = 0;
     TSK_INUM_T end_inum_tmp;
+    ffs_inode * dino_buf;
 
     // clean up any error messages that are lying around
     tsk_error_reset();
@@ -850,16 +871,16 @@ ffs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
      */
     if (start_inum < fs->first_inum || start_inum > fs->last_inum) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "%s: Start inode: %" PRIuINUM "", myname, start_inum);
         return 1;
     }
     else if (end_inum < fs->first_inum || end_inum > fs->last_inum
         || end_inum < start_inum) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "%s: End inode: %" PRIuINUM "", myname, end_inum);
         return 1;
     }
@@ -892,13 +913,10 @@ ffs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
      * in the list of unalloc inodes that are pointed to, then fill
      * in the list
      * */
-    if ((a_flags & TSK_FS_META_FLAG_ORPHAN)
-        && (fs->list_inum_named == NULL)) {
-
+    if ((a_flags & TSK_FS_META_FLAG_ORPHAN)) {
         if (tsk_fs_dir_load_inum_named(fs) != TSK_OK) {
-            strncat(tsk_errstr2,
-                " - ffs_inode_walk: identifying inodes allocated by file names",
-                TSK_ERRSTR_L);
+            tsk_error_errstr2_concat(
+                "- ffs_inode_walk: identifying inodes allocated by file names");
             return 1;
         }
     }
@@ -909,13 +927,16 @@ ffs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
     if ((fs_file->meta = tsk_fs_meta_alloc(FFS_FILE_CONTENT_LEN)) == NULL)
         return 1;
 
-    // we need to handle fs->last_inum specially because it is for the 
+    // we need to handle fs->last_inum specially because it is for the
     // virtual ORPHANS directory.  Handle it outside of the loop.
     if (end_inum == TSK_FS_ORPHANDIR_INUM(fs))
         end_inum_tmp = end_inum - 1;
     else
         end_inum_tmp = end_inum;
 
+    if ((dino_buf = (ffs_inode *) tsk_malloc(sizeof(ffs_inode2))) == NULL)
+        return 1;
+
     /*
      * Iterate. This is easy because inode numbers are contiguous, unlike
      * data blocks which are interleaved with cylinder group blocks.
@@ -928,31 +949,33 @@ ffs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
          */
         grp_num = itog_lcl(fs, ffs->fs.sb1, inum);
 
-        if ((ffs->grp_buf == NULL) || (grp_num != ffs->grp_num)) {
-            if (ffs_group_load(ffs, grp_num))
-                return 1;
-            cg = NULL;
-        }
-
-        /* Load up the cached one if the needed one was already loaded or if a new was just loaded */
-        if (cg == NULL) {
-            cg = (ffs_cgd *) ffs->grp_buf;
-            inosused = (unsigned char *) cg_inosused_lcl(fs, cg);
-            ibase =
-                grp_num * tsk_gets32(fs->endian,
-                ffs->fs.sb1->cg_inode_num);
+        tsk_take_lock(&ffs->lock);
+        if (ffs_group_load(ffs, grp_num)){
+            tsk_release_lock(&ffs->lock);
+            free(dino_buf);
+            return 1;
         }
+        cg = (ffs_cgd *) ffs->grp_buf;
+        inosused = (unsigned char *) cg_inosused_lcl(fs, cg);
+        ibase =
+            grp_num * tsk_gets32(fs->endian,
+            ffs->fs.sb1->cg_inode_num);
 
         /*
          * Apply the allocated/unallocated restriction.
          */
         myflags = (isset(inosused, inum - ibase) ?
             TSK_FS_META_FLAG_ALLOC : TSK_FS_META_FLAG_UNALLOC);
+
+        tsk_release_lock(&ffs->lock);
+
         if ((a_flags & myflags) != myflags)
             continue;
+        
 
-        if (ffs_dinode_load(ffs, inum)) {
+        if (ffs_dinode_load(ffs, inum, dino_buf)) {
             tsk_fs_file_close(fs_file);
+            free(dino_buf);
             return 1;
         }
 
@@ -960,7 +983,7 @@ ffs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
         if ((fs->ftype == TSK_FS_TYPE_FFS1)
             || (fs->ftype == TSK_FS_TYPE_FFS1B)) {
             /* both inode forms are the same for the required fields */
-            ffs_inode1 *in1 = (ffs_inode1 *) ffs->dino_buf;
+            ffs_inode1 *in1 = (ffs_inode1 *) dino_buf;
 
             /*
              * Apply the used/unused restriction.
@@ -971,7 +994,7 @@ ffs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
                 continue;
         }
         else {
-            ffs_inode2 *in2 = (ffs_inode2 *) ffs->dino_buf;
+            ffs_inode2 *in2 = (ffs_inode2 *) dino_buf;
 
             /*
              * Apply the used/unused restriction.
@@ -987,7 +1010,7 @@ ffs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
          */
         if ((myflags & TSK_FS_META_FLAG_UNALLOC) &&
             (a_flags & TSK_FS_META_FLAG_ORPHAN) &&
-            (tsk_list_find(fs->list_inum_named, inum))) {
+            (tsk_fs_dir_find_inum_named(fs, inum))) {
             continue;
         }
 
@@ -996,18 +1019,21 @@ ffs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
          * Fill in a file system-independent inode structure and pass control
          * to the application.
          */
-        if (ffs_dinode_copy(ffs, fs_file->meta)) {
+        if (ffs_dinode_copy(ffs, fs_file->meta, inum, dino_buf)) {
             tsk_fs_file_close(fs_file);
+            free(dino_buf);
             return 1;
         }
 
         retval = action(fs_file, ptr);
         if (retval == TSK_WALK_STOP) {
             tsk_fs_file_close(fs_file);
+            free(dino_buf);
             return 0;
         }
         else if (retval == TSK_WALK_ERROR) {
             tsk_fs_file_close(fs_file);
+            free(dino_buf);
             return 1;
         }
     }
@@ -1020,16 +1046,19 @@ ffs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
 
         if (tsk_fs_dir_make_orphan_dir_meta(fs, fs_file->meta)) {
             tsk_fs_file_close(fs_file);
+            free(dino_buf);
             return 1;
         }
         /* call action */
         retval = action(fs_file, ptr);
         if (retval == TSK_WALK_STOP) {
             tsk_fs_file_close(fs_file);
+            free(dino_buf);
             return 0;
         }
         else if (retval == TSK_WALK_ERROR) {
             tsk_fs_file_close(fs_file);
+            free(dino_buf);
             return 1;
         }
     }
@@ -1038,6 +1067,8 @@ ffs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
      * Cleanup.
      */
     tsk_fs_file_close(fs_file);
+    free(dino_buf);
+
     return 0;
 }
 
@@ -1060,7 +1091,9 @@ ffs_block_getflags(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr)
 
     grp_num = dtog_lcl(a_fs, ffs->fs.sb1, a_addr);
 
+    tsk_take_lock(&ffs->lock);
     if (ffs_group_load(ffs, grp_num)) {
+        tsk_release_lock(&ffs->lock);
         return 0;
     }
 
@@ -1084,6 +1117,8 @@ ffs_block_getflags(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr)
     flags = (isset(freeblocks, a_addr - frag_base) ?
         TSK_FS_BLOCK_FLAG_UNALLOC : TSK_FS_BLOCK_FLAG_ALLOC);
 
+    tsk_release_lock(&ffs->lock);
+
     if (a_addr >= sblock_addr && a_addr < dblock_addr)
         flags |= TSK_FS_BLOCK_FLAG_META;
     else
@@ -1098,7 +1133,7 @@ ffs_block_getflags(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr)
  *
  **************************************************************************/
 
-/* ffs_block_walk - block iterator 
+/* ffs_block_walk - block iterator
  *
  * flags: TSK_FS_BLOCK_FLAG_ALLOC, TSK_FS_BLOCK_FLAG_UNALLOC, TSK_FS_BLOCK_FLAG_CONT,
  *  TSK_FS_BLOCK_FLAG_META
@@ -1128,8 +1163,8 @@ ffs_block_walk(TSK_FS_INFO * fs, TSK_DADDR_T a_start_blk,
      */
     if (a_start_blk < fs->first_block || a_start_blk > fs->last_block) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "%s: Start block: %" PRIuDADDR "", myname, a_start_blk);
         return 1;
     }
@@ -1137,8 +1172,8 @@ ffs_block_walk(TSK_FS_INFO * fs, TSK_DADDR_T a_start_blk,
     if (a_end_blk < fs->first_block || a_end_blk > fs->last_block
         || a_end_blk < a_start_blk) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "%s: End block: %" PRIuDADDR "", myname, a_end_blk);
         return 1;
     }
@@ -1208,9 +1243,9 @@ ffs_block_walk(TSK_FS_INFO * fs, TSK_DADDR_T a_start_blk,
             if (cnt != fs->block_size * frags) {
                 if (cnt >= 0) {
                     tsk_error_reset();
-                    tsk_errno = TSK_ERR_FS_READ;
+                    tsk_error_set_errno(TSK_ERR_FS_READ);
                 }
-                snprintf(tsk_errstr2, TSK_ERRSTR_L,
+                tsk_error_set_errstr2(
                     "ffs_block_walk: Block %" PRIuDADDR, addr);
                 tsk_fs_block_free(fs_block);
                 free(cache_blk_buf);
@@ -1250,19 +1285,19 @@ static uint8_t
 ffs_fscheck(TSK_FS_INFO * fs, FILE * hFile)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr(
         "fscheck not implemented for ffs yet");
     return 1;
 }
 
 
 /**
- * Print details about the file system to a file handle. 
+ * Print details about the file system to a file handle.
  *
  * @param fs File system to print details on
  * @param hFile File handle to print text to
- * 
+ *
  * @returns 1 on error and 0 on success
  */
 static uint8_t
@@ -1420,9 +1455,9 @@ ffs_fsstat(TSK_FS_INFO * fs, FILE * hFile)
             if (cnt != tsk_getu32(fs->endian, sb1->cg_ssize_b)) {
                 if (cnt >= 0) {
                     tsk_error_reset();
-                    tsk_errno = TSK_ERR_FS_READ;
+                    tsk_error_set_errno(TSK_ERR_FS_READ);
                 }
-                snprintf(tsk_errstr2, TSK_ERRSTR_L,
+                tsk_error_set_errstr2(
                     "ffs_fsstat: FFS1 group descriptor at %"
                     PRIu32, tsk_getu32(fs->endian, sb1->cg_saddr));
                 return 1;
@@ -1436,9 +1471,9 @@ ffs_fsstat(TSK_FS_INFO * fs, FILE * hFile)
             if (cnt != tsk_getu32(fs->endian, sb2->cg_ssize_b)) {
                 if (cnt >= 0) {
                     tsk_error_reset();
-                    tsk_errno = TSK_ERR_FS_READ;
+                    tsk_error_set_errno(TSK_ERR_FS_READ);
                 }
-                snprintf(tsk_errstr2, TSK_ERRSTR_L,
+                tsk_error_set_errstr2(
                     "ffs_fsstat: FFS2 group descriptor at %"
                     PRIu64, tsk_getu64(fs->endian, sb2->cg_saddr));
                 return 1;
@@ -1448,8 +1483,11 @@ ffs_fsstat(TSK_FS_INFO * fs, FILE * hFile)
 
     for (i = 0; i < ffs->groups_count; i++) {
 
-        if (ffs_group_load(ffs, i))
+        tsk_take_lock(&ffs->lock);
+        if (ffs_group_load(ffs, i)) {
+            tsk_release_lock(&ffs->lock);
             return 1;
+        }
         cgd = (ffs_cgd *) ffs->grp_buf;
 
         tsk_fprintf(hFile, "\nGroup %d:\n", i);
@@ -1465,6 +1503,7 @@ ffs_fsstat(TSK_FS_INFO * fs, FILE * hFile)
             tsk_fprintf(hFile, "  Last Written: %s",
                 (tmptime > 0) ? asctime(localtime(&tmptime)) : "empty");
         }
+        tsk_release_lock(&ffs->lock);
 
         tsk_fprintf(hFile, "  Inode Range: %" PRIu32 " - %" PRIu32 "\n",
             (tsk_gets32(fs->endian, sb1->cg_inode_num) * i),
@@ -1482,8 +1521,8 @@ ffs_fsstat(TSK_FS_INFO * fs, FILE * hFile)
                     i + 1) - 1) : fs->last_block);
 
         /* The first group is special because the first 16 sectors are
-         * reserved for the boot block.  
-         * the next contains the primary Super Block 
+         * reserved for the boot block.
+         * the next contains the primary Super Block
          */
         if (!i) {
             tsk_fprintf(hFile, "    Boot Block: 0 - %" PRIu32 "\n",
@@ -1631,14 +1670,14 @@ print_addr_act(TSK_FS_FILE * fs_file, TSK_OFF_T a_off, TSK_DADDR_T addr,
 
 
 /**
- * Print details on a specific file to a file handle. 
+ * Print details on a specific file to a file handle.
  *
  * @param fs File system file is located in
  * @param hFile File handle to print text to
  * @param inum Address of file in file system
  * @param numblock The number of blocks in file to force print (can go beyond file size)
  * @param sec_skew Clock skew in seconds to also print times in
- * 
+ *
  * @returns 1 on error and 0 on success
  */
 static uint8_t
@@ -1651,6 +1690,7 @@ ffs_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
     char ls[12];
     FFS_PRINT_ADDR print;
     const TSK_FS_ATTR *fs_attr_indir;
+    char * dino_buf;
 
     // clean up any error messages that are lying around
     tsk_error_reset();
@@ -1664,7 +1704,9 @@ ffs_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
     tsk_fprintf(hFile, "%sAllocated\n",
         (fs_meta->flags & TSK_FS_META_FLAG_ALLOC) ? "" : "Not ");
 
+    tsk_take_lock(&ffs->lock);
     tsk_fprintf(hFile, "Group: %" PRI_FFSGRP "\n", ffs->grp_num);
+    tsk_release_lock(&ffs->lock);
 
     if (fs_meta->link)
         tsk_fprintf(hFile, "symbolic link to: %s\n", fs_meta->link);
@@ -1704,9 +1746,11 @@ ffs_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
     tsk_fprintf(hFile, "File Modified:\t%s", ctime(&fs_meta->mtime));
     tsk_fprintf(hFile, "Inode Modified:\t%s", ctime(&fs_meta->ctime));
 
+    if ((dino_buf = (char *)tsk_malloc(sizeof(ffs_inode2))) == NULL)
+        return 1;
     // we won't have dino_buf for "virtual" files
-    if ((fs->ftype == TSK_FS_TYPE_FFS2) && (ffs->dino_buf)) {
-        ffs_inode2 *in = (ffs_inode2 *) ffs->dino_buf;
+    if ((fs->ftype == TSK_FS_TYPE_FFS2) && (dino_buf)) {
+        ffs_inode2 *in = (ffs_inode2 *) dino_buf;
         /* Are there extended attributes */
         if (tsk_getu32(fs->endian, in->di_extsize) > 0) {
             ffs_extattr *ea;
@@ -1716,6 +1760,7 @@ ffs_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
 
             if ((blk_buf = tsk_malloc(ffs->ffsbsize_b)) == NULL) {
                 tsk_fs_file_close(fs_file);
+                free(dino_buf);
                 return 1;
             }
 
@@ -1741,13 +1786,14 @@ ffs_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
                 if (cnt != ffs->ffsbsize_b) {
                     if (cnt >= 0) {
                         tsk_error_reset();
-                        tsk_errno = TSK_ERR_FS_READ;
+                        tsk_error_set_errno(TSK_ERR_FS_READ);
                     }
-                    snprintf(tsk_errstr2, TSK_ERRSTR_L,
+                    tsk_error_set_errstr2(
                         "ffs_istat: FFS2 extended attribute 0 at %"
                         PRIu64, tsk_getu64(fs->endian, in->di_extb[0]));
                     tsk_fs_file_close(fs_file);
                     free(blk_buf);
+                    free(dino_buf);
                     return 1;
                 }
 
@@ -1783,13 +1829,14 @@ ffs_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
                 if (cnt != ffs->ffsbsize_b) {
                     if (cnt >= 0) {
                         tsk_error_reset();
-                        tsk_errno = TSK_ERR_FS_INODE_COR;
+                        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
                     }
-                    snprintf(tsk_errstr2, TSK_ERRSTR_L,
+                    tsk_error_set_errstr2(
                         "ffs_istat: FFS2 extended attribute 1 at %"
                         PRIu64, tsk_getu64(fs->endian, in->di_extb[1]));
                     tsk_fs_file_close(fs_file);
                     free(blk_buf);
+                    free(dino_buf);
                     return 1;
                 }
 
@@ -1811,6 +1858,7 @@ ffs_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
             }
             free(blk_buf);
         }
+        free(dino_buf);
     }
 
 
@@ -1861,8 +1909,8 @@ uint8_t
 ffs_jopen(TSK_FS_INFO * fs, TSK_INUM_T inum)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-    snprintf(tsk_errstr, TSK_ERRSTR_L, "UFS does not have a journal");
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr("UFS does not have a journal");
     return 1;
 }
 
@@ -1871,8 +1919,8 @@ ffs_jentry_walk(TSK_FS_INFO * fs, int a_flags,
     TSK_FS_JENTRY_WALK_CB action, void *ptr)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-    snprintf(tsk_errstr, TSK_ERRSTR_L, "UFS does not have a journal");
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr("UFS does not have a journal");
     return 1;
 }
 
@@ -1882,8 +1930,8 @@ ffs_jblk_walk(TSK_FS_INFO * fs, TSK_DADDR_T start, TSK_DADDR_T end,
     int a_flags, TSK_FS_JBLK_WALK_CB action, void *ptr)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-    snprintf(tsk_errstr, TSK_ERRSTR_L, "UFS does not have a journal");
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr("UFS does not have a journal");
     return 1;
 }
 
@@ -1902,21 +1950,15 @@ ffs_close(TSK_FS_INFO * fs)
     if (ffs->itbl_buf)
         free(ffs->itbl_buf);
 
-    if (ffs->dino_buf)
-        free(ffs->dino_buf);
-
-    if (fs->list_inum_named) {
-        tsk_list_free(fs->list_inum_named);
-        fs->list_inum_named = NULL;
-    }
+    tsk_deinit_lock(&ffs->lock);
 
     free((char *) ffs->fs.sb1);
-    free(ffs);
+    tsk_fs_free(fs);
 }
 
 /**
  * \internal
- * Open part of a disk image as a FFS/UFS file system. 
+ * Open part of a disk image as a FFS/UFS file system.
  *
  * @param img_info Disk image to analyze
  * @param offset Byte offset where file system starts
@@ -1937,13 +1979,14 @@ ffs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset, TSK_FS_TYPE_ENUM ftype)
 
     if (TSK_FS_TYPE_ISFFS(ftype) == 0) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "Invalid FS Type in ffs_open");
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr("Invalid FS Type in ffs_open");
         return NULL;
     }
 
-    if ((ffs = (FFS_INFO *) tsk_malloc(sizeof(*ffs))) == NULL)
+    if ((ffs = (FFS_INFO *) tsk_fs_malloc(sizeof(*ffs))) == NULL)
         return NULL;
+
     fs = &(ffs->fs_info);
 
     fs->ftype = ftype;
@@ -1966,7 +2009,7 @@ ffs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset, TSK_FS_TYPE_ENUM ftype)
     /* check the magic and figure out the endian ordering */
 
     /* Try UFS2 first - I read somewhere that some upgrades
-     * kept the original UFS1 superblock in addition to 
+     * kept the original UFS1 superblock in addition to
      * the new one */
     cnt = tsk_fs_read
         (fs, (TSK_OFF_T) UFS2_SBOFF, (char *) ffs->fs.sb2,
@@ -1974,9 +2017,9 @@ ffs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset, TSK_FS_TYPE_ENUM ftype)
     if (cnt != sizeof(ffs_sb2)) {
         if (cnt >= 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_READ;
+            tsk_error_set_errno(TSK_ERR_FS_READ);
         }
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errstr(
             "%s: Superblock at %" PRIuDADDR, myname,
             (TSK_OFF_T) UFS2_SBOFF);
         fs->tag = 0;
@@ -1993,9 +2036,9 @@ ffs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset, TSK_FS_TYPE_ENUM ftype)
         if (cnt != sizeof(ffs_sb2)) {
             if (cnt >= 0) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_READ;
+                tsk_error_set_errno(TSK_ERR_FS_READ);
             }
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
+            tsk_error_set_errstr2(
                 "%s: Superblock at %" PRIuDADDR,
                 myname, (TSK_OFF_T) UFS2_SBOFF2);
             fs->tag = 0;
@@ -2011,9 +2054,9 @@ ffs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset, TSK_FS_TYPE_ENUM ftype)
             if (cnt != len) {
                 if (cnt >= 0) {
                     tsk_error_reset();
-                    tsk_errno = TSK_ERR_FS_READ;
+                    tsk_error_set_errno(TSK_ERR_FS_READ);
                 }
-                snprintf(tsk_errstr2, TSK_ERRSTR_L,
+                tsk_error_set_errstr2(
                     "%s: Superblock at %" PRIuDADDR,
                     myname, (TSK_OFF_T) UFS1_SBOFF);
                 fs->tag = 0;
@@ -2026,8 +2069,8 @@ ffs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset, TSK_FS_TYPE_ENUM ftype)
                 free(ffs->fs.sb1);
                 free(ffs);
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_MAGIC;
-                snprintf(tsk_errstr, TSK_ERRSTR_L, "No UFS Magic Found");
+                tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+                tsk_error_set_errstr("No UFS Magic Found");
                 return NULL;
             }
             else {
@@ -2082,8 +2125,8 @@ ffs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset, TSK_FS_TYPE_ENUM ftype)
         free(ffs->fs.sb1);
         free(ffs);
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr(
             "Not a UFS FS (invalid fragment or block size)");
         return NULL;
     }
@@ -2093,8 +2136,8 @@ ffs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset, TSK_FS_TYPE_ENUM ftype)
         free(ffs->fs.sb1);
         free(ffs);
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr(
             "Not a UFS FS (frag / block size mismatch)");
         return NULL;
     }
@@ -2141,12 +2184,8 @@ ffs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset, TSK_FS_TYPE_ENUM ftype)
     ffs->grp_num = 0xffffffff;
     ffs->grp_addr = 0;
 
-    ffs->dino_buf = NULL;
-    ffs->dino_inum = 0xffffffff;
-
     ffs->itbl_buf = NULL;
     ffs->itbl_addr = 0;
-    fs->list_inum_named = NULL;
 
     /*
      * Print some stats.
@@ -2157,5 +2196,7 @@ ffs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset, TSK_FS_TYPE_ENUM ftype)
             PRId32 " blocks %" PRIuDADDR "\n", fs->inum_count,
             fs->root_inum, ffs->groups_count, fs->block_count);
 
+    tsk_init_lock(&ffs->lock);
+
     return (fs);
 }
diff --git a/tsk3/fs/ffs_dent.c b/tsk3/fs/ffs_dent.c
index 677ec95db..020126cf0 100644
--- a/tsk3/fs/ffs_dent.c
+++ b/tsk3/fs/ffs_dent.c
@@ -1,12 +1,12 @@
 /*
 ** ffs_dent
-** The  Sleuth Kit 
+** The  Sleuth Kit
 **
-** File name layer for a FFS/UFS image 
+** File name layer for a FFS/UFS image
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
-** Copyright (c) 2003-2006 Brian Carrier.  All rights reserved 
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2003-2006 Brian Carrier.  All rights reserved
 **
 ** TASK
 ** Copyright (c) 2002 Brian Carrier, @stake Inc.  All rights reserved
@@ -97,8 +97,8 @@ ffs_dent_copy(FFS_INFO * ffs, char *ffs_dent, TSK_FS_NAME * fs_name)
     }
     else {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "ffs_dent_copy: Unknown FS type");
         return 1;
     }
@@ -109,7 +109,7 @@ ffs_dent_copy(FFS_INFO * ffs, char *ffs_dent, TSK_FS_NAME * fs_name)
 
 
 /*
- * @param a_is_del Set to 1 if block is from a deleted directory. 
+ * @param a_is_del Set to 1 if block is from a deleted directory.
  */
 static TSK_RETVAL_ENUM
 ffs_dent_parse_block(FFS_INFO * ffs, TSK_FS_DIR * fs_dir, uint8_t a_is_del,
@@ -127,7 +127,7 @@ ffs_dent_parse_block(FFS_INFO * ffs, TSK_FS_DIR * fs_dir, uint8_t a_is_del,
         return TSK_ERR;
 
     /* update each time by the actual length instead of the
-     ** recorded length so we can view the deleted entries 
+     ** recorded length so we can view the deleted entries
      */
     for (idx = 0; idx <= len - FFS_DIRSIZ_lcl(1); idx += minreclen) {
         unsigned int namelen = 0;
@@ -153,10 +153,10 @@ ffs_dent_parse_block(FFS_INFO * ffs, TSK_FS_DIR * fs_dir, uint8_t a_is_del,
         /* what is the minimum size needed for this entry */
         minreclen = FFS_DIRSIZ_lcl(namelen);
 
-        /* Perform a couple sanity checks 
+        /* Perform a couple sanity checks
          ** OpenBSD never zeros the inode number, but solaris
          ** does.  These checks will hopefully catch all non
-         ** entries 
+         ** entries
          */
         if ((inode > fs->last_inum) ||  // inode is unsigned
             (namelen > FFS_MAXNAMLEN) ||        // namelen is unsigned
@@ -215,15 +215,15 @@ ffs_dent_parse_block(FFS_INFO * ffs, TSK_FS_DIR * fs_dir, uint8_t a_is_del,
 /** \internal
  * Process a directory and load up FS_DIR with the entries. If a pointer to
  * an already allocated FS_DIR struture is given, it will be cleared.  If no existing
- * FS_DIR structure is passed (i.e. NULL), then a new one will be created. If the return 
- * value is error or corruption, then the FS_DIR structure could  
- * have entries (depending on when the error occured). 
+ * FS_DIR structure is passed (i.e. NULL), then a new one will be created. If the return
+ * value is error or corruption, then the FS_DIR structure could
+ * have entries (depending on when the error occured).
  *
  * @param a_fs File system to analyze
  * @param a_fs_dir Pointer to FS_DIR pointer. Can contain an already allocated
- * structure or a new structure. 
+ * structure or a new structure.
  * @param a_addr Address of directory to process.
- * @returns error, corruption, ok etc. 
+ * @returns error, corruption, ok etc.
  */
 TSK_RETVAL_ENUM
 ffs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
@@ -245,15 +245,15 @@ ffs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
 
     if (a_addr < a_fs->first_inum || a_addr > a_fs->last_inum) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "ffs_dir_open_meta: Invalid inode value: %" PRIuINUM, a_addr);
         return TSK_ERR;
     }
     else if (a_fs_dir == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "ffs_dir_open_meta: NULL fs_attr argument given");
         return TSK_ERR;
     }
@@ -281,8 +281,7 @@ ffs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
     if ((fs_dir->fs_file =
             tsk_fs_file_open_meta(a_fs, NULL, a_addr)) == NULL) {
         tsk_error_reset();
-        strncat(tsk_errstr2, " - ffs_dir_open_meta",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- ffs_dir_open_meta");
         return TSK_COR;
     }
 
@@ -300,8 +299,7 @@ ffs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
             TSK_FS_FILE_WALK_FLAG_SLACK,
             tsk_fs_load_file_action, (void *) &load_file)) {
         tsk_error_reset();
-        strncat(tsk_errstr2, " - ffs_dir_open_meta",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- ffs_dir_open_meta");
         free(dirbuf);
         return TSK_COR;
     }
@@ -309,8 +307,8 @@ ffs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
     /* Not all of the directory was copied, so we return */
     if (load_file.left > 0) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_FWALK;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_FWALK);
+        tsk_error_set_errstr(
             "ffs_dir_open_meta: Error reading directory %" PRIuINUM,
             a_addr);
         free(dirbuf);
diff --git a/tsk3/fs/fls_lib.c b/tsk3/fs/fls_lib.c
index ae47fcb36..89bf72b15 100644
--- a/tsk3/fs/fls_lib.c
+++ b/tsk3/fs/fls_lib.c
@@ -6,7 +6,7 @@
 ** directories that exist (both active and deleted)
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2003-2005 Brian Carier.  All rights reserved
 **
 ** TASK
@@ -201,8 +201,8 @@ tsk_fs_fls(TSK_FS_INFO * fs, TSK_FS_FLS_FLAG_ENUM lclflags,
                 (UTF8 *) ((uintptr_t) ptr8 + clen), TSKlenientConversion);
             if (retval != TSKconversionOK) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_UNICODE;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_FS_UNICODE);
+                tsk_error_set_errstr(
                     "Error converting fls mactime pre-text to UTF-8 %d\n",
                     retval);
                 return 1;
diff --git a/tsk3/fs/fs_attr.c b/tsk3/fs/fs_attr.c
index 2c09938f8..72d913401 100644
--- a/tsk3/fs/fs_attr.c
+++ b/tsk3/fs/fs_attr.c
@@ -3,7 +3,7 @@
 ** The Sleuth Kit 
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved 
 **
 ** TASK
@@ -108,8 +108,8 @@ tsk_fs_attr_alloc(TSK_FS_ATTR_FLAG_ENUM type)
     }
     else {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_attr_alloc: Invalid Type: %d\n", type);
         return NULL;
     }
@@ -220,8 +220,8 @@ tsk_fs_attr_set_str(TSK_FS_FILE * a_fs_file, TSK_FS_ATTR * a_fs_attr,
 {
     if (a_fs_attr == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "Null fs_attr in tsk_fs_attr_set_str");
         return 1;
     }
@@ -284,15 +284,15 @@ tsk_fs_attr_set_run(TSK_FS_FILE * a_fs_file, TSK_FS_ATTR * a_fs_attr,
 
     if ((a_fs_file == NULL) || (a_fs_file->meta == NULL)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "Null fs_file in tsk_fs_attr_set_run");
         return 1;
     }
     if (a_fs_attr == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "Null fs_attr in tsk_fs_attr_set_run");
         return 1;
     }
@@ -301,8 +301,8 @@ tsk_fs_attr_set_run(TSK_FS_FILE * a_fs_file, TSK_FS_ATTR * a_fs_attr,
 
     if (alloc_size < size) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_attr_set_run: alloc_size (%" PRIuOFF
             ") is less than size (%" PRIuOFF ")", alloc_size, size);
         return 1;
@@ -400,8 +400,8 @@ tsk_fs_attr_add_run(TSK_FS_INFO * a_fs, TSK_FS_ATTR * a_fs_attr,
 
     if (a_fs_attr == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_attr_add_run: Error, a_fs_attr is NULL");
         return 1;
     }
@@ -409,8 +409,8 @@ tsk_fs_attr_add_run(TSK_FS_INFO * a_fs, TSK_FS_ATTR * a_fs_attr,
     // we only support the case of a null run if it is the only run...
     if (a_data_run_new == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_attr_add_run: Error, a_data_run_new is NULL (%"
             PRIuINUM ")", a_fs_attr->fs_file->meta->addr);
         return 1;
@@ -456,8 +456,8 @@ tsk_fs_attr_add_run(TSK_FS_INFO * a_fs, TSK_FS_ATTR * a_fs_attr,
              * the filler to start from VCN 0 */
             if (data_run_cur->offset > a_data_run_new->offset) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_GENFS;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_FS_GENFS);
+                tsk_error_set_errstr(
                     "tsk_fs_attr_add_run: could not add data_run b.c. offset (%"
                     PRIuOFF ") is larger than FILLER (%" PRIuOFF ") (%"
                     PRIuINUM ")", a_data_run_new->offset,
@@ -564,8 +564,8 @@ tsk_fs_attr_add_run(TSK_FS_INFO * a_fs, TSK_FS_ATTR * a_fs_attr,
         }
 
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_GENFS;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_GENFS);
+        tsk_error_set_errstr(
             "fs_attr_add_run: error adding additional run (%" PRIuINUM
             "): No filler entry for %" PRIuDADDR ". Final: %" PRIuDADDR,
             a_fs_attr->fs_file->meta->addr, a_data_run_new->offset,
@@ -686,8 +686,8 @@ tsk_fs_attr_walk_res(const TSK_FS_ATTR * fs_attr,
     fs = fs_attr->fs_file->fs_info;
 
     if ((fs_attr->flags & TSK_FS_ATTR_RES) == 0) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_file_walk_res: called with non-resident data");
         return 1;
     }
@@ -766,8 +766,8 @@ tsk_fs_attr_walk_nonres(const TSK_FS_ATTR * fs_attr,
     uint8_t stop_loop = 0;
 
     if ((fs_attr->flags & TSK_FS_ATTR_NONRES) == 0) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_file_walk_nonres: called with non-non-resident data");
         return 1;
     }
@@ -804,10 +804,10 @@ tsk_fs_attr_walk_nonres(const TSK_FS_ATTR * fs_attr,
             if (addr + len_idx > fs->last_block) {
                 if (fs_attr->fs_file->meta->
                     flags & TSK_FS_META_FLAG_UNALLOC)
-                    tsk_errno = TSK_ERR_FS_RECOVER;
+                    tsk_error_set_errno(TSK_ERR_FS_RECOVER);
                 else
-                    tsk_errno = TSK_ERR_FS_BLK_NUM;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                    tsk_error_set_errno(TSK_ERR_FS_BLK_NUM);
+                tsk_error_set_errstr(
                     "Invalid address in run (too large): %"
                     PRIuDADDR "", addr + len_idx);
                 return 1;
@@ -845,9 +845,9 @@ tsk_fs_attr_walk_nonres(const TSK_FS_ATTR * fs_attr,
                     if (cnt != fs->block_size) {
                         if (cnt >= 0) {
                             tsk_error_reset();
-                            tsk_errno = TSK_ERR_FS_READ;
+                            tsk_error_set_errno(TSK_ERR_FS_READ);
                         }
-                        snprintf(tsk_errstr2, TSK_ERRSTR_L,
+                        tsk_error_set_errstr2(
                             "tsk_fs_file_walk: Error reading block at %"
                             PRIuDADDR, addr + len_idx);
                         return 1;
@@ -955,8 +955,8 @@ tsk_fs_attr_walk(const TSK_FS_ATTR * a_fs_attr,
     if ((a_fs_attr == NULL) || (a_fs_attr->fs_file == NULL)
         || (a_fs_attr->fs_file->meta == NULL)
         || (a_fs_attr->fs_file->fs_info == NULL)) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_attr_walk: called with NULL pointers");
         return 1;
     }
@@ -964,15 +964,15 @@ tsk_fs_attr_walk(const TSK_FS_ATTR * a_fs_attr,
 
     if (fs->tag != TSK_FS_INFO_TAG) {
 //        || (a_fs_attr->id != TSK_FS_ATTR_ID)) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_attr_walk: called with unallocated structures");
         return 1;
     }
     if (a_fs_attr->flags & TSK_FS_ATTR_COMP) {
         if (a_fs_attr->w == NULL) {
-            tsk_errno = TSK_ERR_FS_ARG;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_ARG);
+            tsk_error_set_errstr(
                 "tsk_fs_attr_walk: compressed attribute found, but special function not defined");
             return 1;
         }
@@ -988,8 +988,8 @@ tsk_fs_attr_walk(const TSK_FS_ATTR * a_fs_attr,
             a_ptr);
     }
 
-    tsk_errno = TSK_ERR_FS_ARG;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_FS_ARG);
+    tsk_error_set_errstr(
         "tsk_fs_attr_walk: called with unknown attribute type: %x",
         a_fs_attr->flags);
     return 1;
@@ -1017,8 +1017,8 @@ tsk_fs_attr_read(const TSK_FS_ATTR * a_fs_attr, TSK_OFF_T a_offset,
 
     if ((a_fs_attr == NULL) || (a_fs_attr->fs_file == NULL)
         || (a_fs_attr->fs_file->fs_info == NULL)) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_attr_read: Attribute has null pointers.");
         return -1;
     }
@@ -1027,8 +1027,8 @@ tsk_fs_attr_read(const TSK_FS_ATTR * a_fs_attr, TSK_OFF_T a_offset,
     /* for compressed data, call the specialized function */
     if (a_fs_attr->flags & TSK_FS_ATTR_COMP) {
         if (a_fs_attr->r == NULL) {
-            tsk_errno = TSK_ERR_FS_ARG;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_ARG);
+            tsk_error_set_errstr(
                 "tsk_fs_attr_read: Attribute has compressed type set, but no compressed read function defined");
             return -1;
         }
@@ -1041,8 +1041,8 @@ tsk_fs_attr_read(const TSK_FS_ATTR * a_fs_attr, TSK_OFF_T a_offset,
 
         if (a_offset >= a_fs_attr->size) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_READ_OFF;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_READ_OFF);
+            tsk_error_set_errstr(
                 "tsk_fs_attr_read - %" PRIuOFF, a_offset);
             return -1;
         }
@@ -1071,8 +1071,8 @@ tsk_fs_attr_read(const TSK_FS_ATTR * a_fs_attr, TSK_OFF_T a_offset,
             || (!(a_flags & TSK_FS_FILE_READ_FLAG_SLACK)
                 && (a_offset >= a_fs_attr->size))) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_READ_OFF;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_READ_OFF);
+            tsk_error_set_errstr(
                 "tsk_fs_attr_read - %" PRIuOFF, a_offset);
             return -1;
         }
@@ -1177,9 +1177,9 @@ tsk_fs_attr_read(const TSK_FS_ATTR * a_fs_attr, TSK_OFF_T a_offset,
                 if (cnt != len_inrun) {
                     if (cnt >= 0) {
                         tsk_error_reset();
-                        tsk_errno = TSK_ERR_FS_READ;
+                        tsk_error_set_errno(TSK_ERR_FS_READ);
                     }
-                    snprintf(tsk_errstr2, TSK_ERRSTR_L,
+                    tsk_error_set_errstr2(
                         "tsk_fs_attr_read_type: offset: %" PRIuOFF
                         "  Len: %" PRIuSIZE "", fs_offset_b, len_inrun);
                     return cnt;
@@ -1207,8 +1207,8 @@ tsk_fs_attr_read(const TSK_FS_ATTR * a_fs_attr, TSK_OFF_T a_offset,
         return (ssize_t) (len_toread - len_remain);
     }
 
-    tsk_errno = TSK_ERR_FS_ARG;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_FS_ARG);
+    tsk_error_set_errstr(
         "tsk_fs_attr_read: Unknown attribute type: %x", a_fs_attr->flags);
     return -1;
 }
diff --git a/tsk3/fs/fs_attrlist.c b/tsk3/fs/fs_attrlist.c
index 3169c3802..303f6d2ec 100644
--- a/tsk3/fs/fs_attrlist.c
+++ b/tsk3/fs/fs_attrlist.c
@@ -1,17 +1,17 @@
 /*
  ** fs_attrlist
- ** The Sleuth Kit 
+ ** The Sleuth Kit
  **
  ** Brian Carrier [carrier <at> sleuthkit [dot] org]
- ** Copyright (c) 2008 Brian Carrier.  All Rights reserved
+ ** Copyright (c) 2008-2011 Brian Carrier.  All Rights reserved
  **
  ** This software is distributed under the Common Public License 1.0
  */
 
 /**
  * \file fs_attrlist.c
- * File that contains functions to process TSK_FS_ATTRLIST structures, which 
- * hold a linked list of TSK_FS_ATTR attribute structures. 
+ * File that contains functions to process TSK_FS_ATTRLIST structures, which
+ * hold a linked list of TSK_FS_ATTR attribute structures.
  */
 
 #include "tsk_fs_i.h"
@@ -53,7 +53,7 @@ tsk_fs_attrlist_free(TSK_FS_ATTRLIST * a_fs_attrlist)
 }
 
 /** \internal
- * Add a new attribute to the list.  
+ * Add a new attribute to the list.
  *
  * @param a_fs_attrlist List structure to add to
  * @param a_fs_attr Data attribute to add
@@ -65,8 +65,8 @@ tsk_fs_attrlist_add(TSK_FS_ATTRLIST * a_fs_attrlist,
 {
     if (a_fs_attrlist == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "Null list in tsk_fs_attrlist_add");
         return 1;
     }
@@ -85,8 +85,8 @@ tsk_fs_attrlist_add(TSK_FS_ATTRLIST * a_fs_attrlist,
             if ((fs_attr_cur->type == a_fs_attr->type)
                 && (fs_attr_cur->id == a_fs_attr->id)) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_ARG;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_FS_ARG);
+                tsk_error_set_errstr(
                     "datalist_add: Type %d and Id %d already in list",
                     a_fs_attr->type, a_fs_attr->id);
                 return 1;
@@ -103,7 +103,7 @@ tsk_fs_attrlist_add(TSK_FS_ATTRLIST * a_fs_attrlist,
 
 
 
-/** 
+/**
  * \internal
  * Return either an empty element in the list or create a new one at the end
  *
@@ -124,16 +124,16 @@ tsk_fs_attrlist_getnew(TSK_FS_ATTRLIST * a_fs_attrlist,
 
     if (a_fs_attrlist == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "Null list in tsk_fs_attrlist_getnew()");
         return NULL;
     }
 
     if ((a_atype != TSK_FS_ATTR_NONRES) && (a_atype != TSK_FS_ATTR_RES)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "Invalid Type in tsk_fs_attrlist_getnew()");
         return NULL;
     }
@@ -197,9 +197,9 @@ tsk_fs_attrlist_markunused(TSK_FS_ATTRLIST * a_fs_attrlist)
 
 /**
  * \internal
- * Search the attribute list of TSK_FS_ATTR structures for an entry with a given 
- * type (no ID).  If more than one entry with the same type exists, the one with 
- * the lowest ID will be returned. 
+ * Search the attribute list of TSK_FS_ATTR structures for an entry with a given
+ * type (no ID).  If more than one entry with the same type exists, the one with
+ * the lowest ID will be returned.
  *
  * @param a_fs_attrlist Data list structure to search in
  * @param a_type Type of attribute to find
@@ -216,10 +216,9 @@ tsk_fs_attrlist_get(const TSK_FS_ATTRLIST * a_fs_attrlist,
 
     if (!a_fs_attrlist) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_attrlist_get: Null list pointer");
-        tsk_errstr2[0] = '\0';
         return NULL;
     }
 
@@ -228,7 +227,7 @@ tsk_fs_attrlist_get(const TSK_FS_ATTRLIST * a_fs_attrlist,
         if ((fs_attr_cur->flags & TSK_FS_ATTR_INUSE)
             && (fs_attr_cur->type == a_type)) {
 
-            /* If we are looking for NTFS $Data, 
+            /* If we are looking for NTFS $Data,
              * then return default when we see it */
             if ((fs_attr_cur->type == TSK_FS_ATTR_TYPE_NTFS_DATA) &&
                 (fs_attr_cur->name == NULL)) {
@@ -242,8 +241,8 @@ tsk_fs_attrlist_get(const TSK_FS_ATTRLIST * a_fs_attrlist,
     }
 
     if (!fs_attr_ok) {
-        tsk_errno = TSK_ERR_FS_ATTR_NOTFOUND;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ATTR_NOTFOUND);
+        tsk_error_set_errstr(
             "tsk_fs_attrlist_get: Attribute %d not found", a_type);
         return NULL;
     }
@@ -254,12 +253,12 @@ tsk_fs_attrlist_get(const TSK_FS_ATTRLIST * a_fs_attrlist,
 
 /**
  * \internal
- * Search the attribute list of TSK_FS_ATTR structures for an entry with a given 
- * type and id.  
+ * Search the attribute list of TSK_FS_ATTR structures for an entry with a given
+ * type and id.
  *
  * @param a_fs_attrlist Data list structure to search in
  * @param a_type Type of attribute to find
- * @param a_id Id of attribute to find. 
+ * @param a_id Id of attribute to find.
  *
  * @return NULL is returned on error and if an entry could not be found.
  * tsk_errno will be set to TSK_ERR_FS_ATTR_NOTFOUND if entry could not be found.
@@ -272,10 +271,9 @@ tsk_fs_attrlist_get_id(const TSK_FS_ATTRLIST * a_fs_attrlist,
 
     if (!a_fs_attrlist) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_attrlist_get_id: Null list pointer");
-        tsk_errstr2[0] = '\0';
         return NULL;
     }
 
@@ -286,8 +284,8 @@ tsk_fs_attrlist_get_id(const TSK_FS_ATTRLIST * a_fs_attrlist,
             return fs_attr_cur;
     }
 
-    tsk_errno = TSK_ERR_FS_ATTR_NOTFOUND;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_FS_ATTR_NOTFOUND);
+    tsk_error_set_errstr(
         "tsk_fs_attrlist_get_id: Attribute %d-%d not found", a_type, a_id);
     return NULL;
 }
@@ -318,10 +316,9 @@ tsk_fs_attrlist_get_name_type(const TSK_FS_ATTRLIST * a_fs_attrlist,
 
     if (!a_fs_attrlist) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_attrlist_get_name_type: Null list pointer");
-        tsk_errstr2[0] = '\0';
         return NULL;
     }
 
@@ -350,8 +347,8 @@ tsk_fs_attrlist_get_name_type(const TSK_FS_ATTRLIST * a_fs_attrlist,
     }
 
     if (!fs_attr_ok) {
-        tsk_errno = TSK_ERR_FS_ATTR_NOTFOUND;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ATTR_NOTFOUND);
+        tsk_error_set_errstr(
             "tsk_fs_attrlist_get: Attribute %d not found", a_type);
         return NULL;
     }
@@ -363,7 +360,7 @@ tsk_fs_attrlist_get_name_type(const TSK_FS_ATTRLIST * a_fs_attrlist,
 
 /**
  * \internal
- * Return the a_idx'th attribute in the attribute list. 
+ * Return the a_idx'th attribute in the attribute list.
  *
  * @param a_fs_attrlist Data list structure to search in
  * @param a_idx 0-based index of attribute to return
@@ -379,10 +376,9 @@ tsk_fs_attrlist_get_idx(const TSK_FS_ATTRLIST * a_fs_attrlist, int a_idx)
 
     if (!a_fs_attrlist) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_attrlist_get_idx: Null list pointer");
-        tsk_errstr2[0] = '\0';
         return NULL;
     }
 
@@ -396,8 +392,8 @@ tsk_fs_attrlist_get_idx(const TSK_FS_ATTRLIST * a_fs_attrlist, int a_idx)
         }
     }
 
-    tsk_errno = TSK_ERR_FS_ATTR_NOTFOUND;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_FS_ATTR_NOTFOUND);
+    tsk_error_set_errstr(
         "tsk_fs_attrlist_get_idx: Attribute index %d not found", a_idx);
     return NULL;
 }
@@ -405,7 +401,7 @@ tsk_fs_attrlist_get_idx(const TSK_FS_ATTRLIST * a_fs_attrlist, int a_idx)
 
 /**
  * \internal
- * Return the number of attributes in the attribute list 
+ * Return the number of attributes in the attribute list
  *
  * @param a_fs_attrlist Data list structure to analyze
  *
@@ -419,10 +415,9 @@ tsk_fs_attrlist_get_len(const TSK_FS_ATTRLIST * a_fs_attrlist)
 
     if (!a_fs_attrlist) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_attrlist_get_len: Null list pointer");
-        tsk_errstr2[0] = '\0';
         return 0;
     }
 
diff --git a/tsk3/fs/fs_block.c b/tsk3/fs/fs_block.c
index 679aa90c9..600831b0d 100644
--- a/tsk3/fs/fs_block.c
+++ b/tsk3/fs/fs_block.c
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2008 Brian Carrier, Basis Technology.  All Rights reserved
+ * Copyright (c) 2008-2011 Brian Carrier, Basis Technology.  All Rights reserved
  *
  * This software is distributed under the Common Public License 1.0
  */
@@ -79,8 +79,8 @@ tsk_fs_block_get(TSK_FS_INFO * a_fs, TSK_FS_BLOCK * a_fs_block,
 
     if (a_fs == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_READ;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_READ);
+        tsk_error_set_errstr(
             "tsk_fs_block_get: fs unallocated");
         return NULL;
     }
@@ -90,8 +90,8 @@ tsk_fs_block_get(TSK_FS_INFO * a_fs, TSK_FS_BLOCK * a_fs_block,
     else if ((a_fs_block->tag != TSK_FS_BLOCK_TAG)
         || (a_fs_block->buf == NULL)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_READ;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_READ);
+        tsk_error_set_errstr(
             "tsk_fs_block_get: fs_block unallocated");
         return NULL;
     }
@@ -100,13 +100,13 @@ tsk_fs_block_get(TSK_FS_INFO * a_fs, TSK_FS_BLOCK * a_fs_block,
 
     if (a_addr > a_fs->last_block_act) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_READ;
+        tsk_error_set_errno(TSK_ERR_FS_READ);
         if (a_addr <= a_fs->last_block)
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errstr(
                 "tsk_fs_block_get: Address missing in partial image: %"
                 PRIuDADDR ")", a_addr);
         else
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errstr(
                 "tsk_fs_block_get: Address is too large for image: %"
                 PRIuDADDR ")", a_addr);
         return NULL;
@@ -147,15 +147,15 @@ tsk_fs_block_set(TSK_FS_INFO * a_fs, TSK_FS_BLOCK * a_fs_block,
 {
     if ((a_fs == NULL) || (a_fs->tag != TSK_FS_INFO_TAG)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_READ;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_READ);
+        tsk_error_set_errstr(
             "tsk_fs_block_set: fs_info unallocated");
         return 1;
     }
     if ((a_fs_block->tag != TSK_FS_BLOCK_TAG) || (a_fs_block->buf == NULL)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_READ;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_READ);
+        tsk_error_set_errstr(
             "tsk_fs_block_set: fs_block unallocated");
         return 1;
     }
@@ -189,8 +189,8 @@ tsk_fs_block_walk(TSK_FS_INFO * a_fs,
 {
     if ((a_fs == NULL) || (a_fs->tag != TSK_FS_INFO_TAG)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_block_walk: FS_INFO structure is not allocated");
         return 1;
     }
diff --git a/tsk3/fs/fs_dir.c b/tsk3/fs/fs_dir.c
index d9ad64dc7..4b04314b3 100644
--- a/tsk3/fs/fs_dir.c
+++ b/tsk3/fs/fs_dir.c
@@ -1,9 +1,9 @@
 /*
  * fs_dir
- * The Sleuth Kit 
+ * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2008-2009 Brian Carrier.  All Rights reserved
+ * Copyright (c) 2008-2011 Brian Carrier.  All Rights reserved
  *
  * This software is distributed under the Common Public License 1.0
  *
@@ -11,16 +11,16 @@
 
 /**
  * \file fs_dir.c
- * Create, manage, etc. the TSK_FS_DIR structures. 
+ * Create, manage, etc. the TSK_FS_DIR structures.
  */
 
 #include "tsk_fs_i.h"
 #include "tsk_fatfs.h"
 
 /** \internal
-* Allocate a FS_DIR structure to load names into.  
-* 
-* @param a_addr Address of this directory. 
+* Allocate a FS_DIR structure to load names into.
+*
+* @param a_addr Address of this directory.
 * @param a_cnt target number of FS_DENT entries to fit in
 * @returns NULL on error
 */
@@ -56,7 +56,7 @@ tsk_fs_dir_alloc(TSK_FS_INFO * a_fs, TSK_INUM_T a_addr, size_t a_cnt)
 
 /** \internal
 * Make the buffer in the FS_DIR structure larger.
-* 
+*
 * @param a_fs_dir Structure to enhance
 * @param a_cnt target number of FS_DENT entries to fit in
 * @returns 1 on error and 0 on success
@@ -108,10 +108,10 @@ tsk_fs_dir_reset(TSK_FS_DIR * a_fs_dir)
 
 
 /** \internal
- * Copy the contents of one directory structure to another. 
+ * Copy the contents of one directory structure to another.
  * Note that this currently does not copy the FS_FILE info.
  * It is only used to make a copy of the orphan directory.
- * It does not check for duplicate entries. 
+ * It does not check for duplicate entries.
  * @returns 1 on error
  */
 static uint8_t
@@ -140,7 +140,7 @@ tsk_fs_dir_copy(const TSK_FS_DIR * a_src_dir, TSK_FS_DIR * a_dst_dir)
 
 /** \internal
  * Add a FS_DENT structure to a FS_DIR structure by copying its
- * contents into the internal buffer. Checks for 
+ * contents into the internal buffer. Checks for
  * duplicates and expands buffer as needed.
  * @param a_fs_dir DIR to add to
  * @param a_fs_name DENT to add
@@ -163,7 +163,7 @@ tsk_fs_dir_add(TSK_FS_DIR * a_fs_dir, const TSK_FS_NAME * a_fs_name)
                     PRIuINUM ")\n", a_fs_name->name, a_fs_name->meta_addr);
 
             /* We do not check type because then we cannot detect NTFS orphan file
-             * duplicates that are added as "-/r" while a similar entry exists as "r/r"  
+             * duplicates that are added as "-/r" while a similar entry exists as "r/r"
              (a_fs_name->type == a_fs_dir->names[i].type)) { */
 
             // if the one in the list is unalloc and we have an alloc, replace it
@@ -204,7 +204,7 @@ tsk_fs_dir_add(TSK_FS_DIR * a_fs_dir, const TSK_FS_NAME * a_fs_name)
     if (tsk_fs_name_copy(fs_name_dest, a_fs_name))
         return 1;
 
-    // add the parent address 
+    // add the parent address
     if (a_fs_dir->addr)
         fs_name_dest->par_addr = a_fs_dir->addr;
 
@@ -227,8 +227,8 @@ tsk_fs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_INUM_T a_addr)
 
     if ((a_fs == NULL) || (a_fs->tag != TSK_FS_INFO_TAG)
         || (a_fs->dir_open_meta == NULL)) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_dir_open_meta: called with NULL or unallocated structures");
         return NULL;
     }
@@ -256,8 +256,8 @@ tsk_fs_dir_open(TSK_FS_INFO * a_fs, const char *a_dir)
     TSK_FS_NAME *fs_name;
 
     if ((a_fs == NULL) || (a_fs->tag != TSK_FS_INFO_TAG)) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_dir_open: called with NULL or unallocated structures");
         return NULL;
     }
@@ -272,8 +272,8 @@ tsk_fs_dir_open(TSK_FS_INFO * a_fs, const char *a_dir)
         return NULL;
     }
     else if (retval == 1) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_dir_open: path not found: %s", a_dir);
         return NULL;
     }
@@ -332,8 +332,8 @@ size_t
 tsk_fs_dir_getsize(const TSK_FS_DIR * a_fs_dir)
 {
     if ((a_fs_dir == NULL) || (a_fs_dir->tag != TSK_FS_DIR_TAG)) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_dir_getsize: called with NULL or unallocated structures");
         return 0;
     }
@@ -341,7 +341,7 @@ tsk_fs_dir_getsize(const TSK_FS_DIR * a_fs_dir)
 }
 
 /** \ingroup fslib
-* Return a specific file or subdirectory from an open directory. 
+* Return a specific file or subdirectory from an open directory.
  * @param a_fs_dir Directory to analyze
  * @param a_idx Index of file in directory to open (0-based)
  * @returns NULL on error
@@ -354,14 +354,14 @@ tsk_fs_dir_get(const TSK_FS_DIR * a_fs_dir, size_t a_idx)
 
     if ((a_fs_dir == NULL) || (a_fs_dir->tag != TSK_FS_DIR_TAG)
         || (a_fs_dir->fs_info == NULL)) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_dir_get: called with NULL or unallocated structures");
         return NULL;
     }
     if (a_fs_dir->names_used <= a_idx) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_dir_get: Index (%" PRIuSIZE ") too large (%" PRIuSIZE
             ")", a_idx, a_fs_dir->names_used);
         return NULL;
@@ -421,6 +421,14 @@ typedef struct {
     /* Set to one to collect inode info that can be used for orphan listing */
     uint8_t save_inum_named;
 
+    /* We keep list_inum_named inside DENT_DINFO so different threads
+     * have their own copies.  On successful completion of the dir
+     * walk we reassigned ownership of this pointer into the shared
+     * TSK_FS_INFO list_inum_named field.  We're trading off the extra
+     * work in each thread for cleaner locking code.
+     */
+    TSK_LIST * list_inum_named;
+
 } DENT_DINFO;
 
 
@@ -435,7 +443,7 @@ tsk_fs_dir_walk_lcl(TSK_FS_INFO * a_fs, DENT_DINFO * a_dinfo,
     TSK_FS_FILE *fs_file;
     size_t i;
 
-    // get the list of entries in the directory 
+    // get the list of entries in the directory
     if ((fs_dir = tsk_fs_dir_open_meta(a_fs, a_addr)) == NULL) {
         return TSK_WALK_ERROR;
     }
@@ -467,7 +475,7 @@ tsk_fs_dir_walk_lcl(TSK_FS_INFO * a_fs, DENT_DINFO * a_dinfo,
             }
         }
 
-        // call the action if we have the right flags. 
+        // call the action if we have the right flags.
         if ((fs_file->name->flags & a_flags) == fs_file->name->flags) {
 
             retval = a_action(fs_file, a_dinfo->dirs, a_ptr);
@@ -480,8 +488,8 @@ tsk_fs_dir_walk_lcl(TSK_FS_INFO * a_fs, DENT_DINFO * a_dinfo,
                  * of knowing that we stopped early w/out error.
                  */
                 if (a_dinfo->save_inum_named) {
-                    tsk_list_free(a_fs->list_inum_named);
-                    a_fs->list_inum_named = NULL;
+                    tsk_list_free(a_dinfo->list_inum_named);
+                    a_dinfo->list_inum_named = NULL;
                     a_dinfo->save_inum_named = 0;
                 }
 
@@ -498,16 +506,17 @@ tsk_fs_dir_walk_lcl(TSK_FS_INFO * a_fs, DENT_DINFO * a_dinfo,
         // save the inode info for orphan finding - if requested
         if ((a_dinfo->save_inum_named) && (fs_file->meta)
             && (fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC)) {
-            if (tsk_list_add(&a_fs->list_inum_named, fs_file->meta->addr)) {
+
+            if (tsk_list_add(&a_dinfo->list_inum_named, fs_file->meta->addr)) {
 
                 // if there is an error, then clear the list
-                tsk_list_free(a_fs->list_inum_named);
-                a_fs->list_inum_named = NULL;
+                tsk_list_free(a_dinfo->list_inum_named);
+                a_dinfo->list_inum_named = NULL;
                 a_dinfo->save_inum_named = 0;
             }
         }
 
-        /* Recurse into a directory if: 
+        /* Recurse into a directory if:
          * - Both dir entry and inode have DIR type (or name is undefined)
          * - Recurse flag is set
          * - dir entry is allocated OR both are unallocated
@@ -570,7 +579,7 @@ tsk_fs_dir_walk_lcl(TSK_FS_INFO * a_fs, DENT_DINFO * a_dinfo,
                     a_dinfo, fs_file->name->meta_addr, a_flags,
                     a_action, a_ptr);
                 if (retval == TSK_WALK_ERROR) {
-                    /* If this fails because the directory could not be 
+                    /* If this fails because the directory could not be
                      * loaded, then we still continue */
                     if (tsk_verbose) {
                         tsk_fprintf(stderr,
@@ -619,7 +628,7 @@ tsk_fs_dir_walk_lcl(TSK_FS_INFO * a_fs, DENT_DINFO * a_dinfo,
 
 
 /** \ingroup fslib
-* Walk the file names in a directory and obtain the details of the files via a callback. 
+* Walk the file names in a directory and obtain the details of the files via a callback.
 *
 * @param a_fs File system to analyze
 * @param a_addr Metadata address of the directory to analyze
@@ -637,8 +646,8 @@ tsk_fs_dir_walk(TSK_FS_INFO * a_fs, TSK_INUM_T a_addr,
     TSK_RETVAL_ENUM retval;
 
     if ((a_fs == NULL) || (a_fs->tag != TSK_FS_INFO_TAG)) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_dir_walk: called with NULL or unallocated structures");
         return 1;
     }
@@ -657,22 +666,40 @@ tsk_fs_dir_walk(TSK_FS_INFO * a_fs, TSK_INUM_T a_addr,
 
     /* if the flags are right, we can collect info that may be needed
      * for an orphan walk.  If the walk fails or stops, the code that
-     * calls the action will clear this stuff. 
+     * calls the action will clear this stuff.
      */
+    tsk_take_lock(&a_fs->list_inum_named_lock);
     if ((a_fs->list_inum_named == NULL) && (a_addr == a_fs->root_inum)
         && (a_flags & TSK_FS_DIR_WALK_FLAG_RECURSE)) {
         dinfo.save_inum_named = 1;
     }
+    tsk_release_lock(&a_fs->list_inum_named_lock);
 
     retval = tsk_fs_dir_walk_lcl(a_fs, &dinfo, a_addr, a_flags,
         a_action, a_ptr);
 
-    /* If there was an error, then we stopped early and we should get
-     * rid of the partial list we were making.
-     */
-    if ((retval != TSK_WALK_CONT) && (dinfo.save_inum_named == 1)) {
-        tsk_list_free(a_fs->list_inum_named);
-        a_fs->list_inum_named = NULL;
+    if (dinfo.save_inum_named == 1) {
+        if (retval != TSK_WALK_CONT) {
+            /* There was an error and we stopped early, so we should get
+             * rid of the partial list we were making.
+             */
+            tsk_list_free(dinfo.list_inum_named);
+            dinfo.list_inum_named = NULL;
+        } else {
+            /* We finished the dir walk successfully, so reassign
+             * ownership of the dinfo's list_inum_named to the shared
+             * list_inum_named in TSK_FS_INFO, under a lock, if
+             * another thread hasn't already done so.
+             */
+            tsk_take_lock(&a_fs->list_inum_named_lock);
+            if (a_fs->list_inum_named == NULL) {
+                a_fs->list_inum_named = dinfo.list_inum_named;
+            } else {
+                tsk_list_free(dinfo.list_inum_named);
+            }
+            tsk_release_lock(&a_fs->list_inum_named_lock);
+            dinfo.list_inum_named = NULL;
+        }
     }
 
     tsk_stack_free(dinfo.stack_seen);
@@ -747,7 +774,25 @@ tsk_fs_dir_make_orphan_dir_meta(TSK_FS_INFO * a_fs,
     return 0;
 }
 
-
+/** \internal
+ * Searches the list of metadata addresses that are pointed to
+ * by unallocated names.  Used to find orphan files. 
+ * @param a_fs File system being analyzed.
+ * @param a_inum Metadata address to lookup in list.
+ * @returns 1 if metadata address is pointed to by an unallocated
+ * file name or 0 if not.
+ */
+uint8_t
+tsk_fs_dir_find_inum_named(TSK_FS_INFO *a_fs, TSK_INUM_T a_inum)
+{
+    uint8_t retval = 0;
+    tsk_take_lock(&a_fs->list_inum_named_lock);
+    // list can be null if no unallocated file names exist
+    if (a_fs->list_inum_named)
+        retval = tsk_list_find(a_fs->list_inum_named, a_inum);
+    tsk_release_lock(&a_fs->list_inum_named_lock);
+    return retval;
+}
 
 
 /* callback that is used by tsk_fs_dir_load_inum_named.  It does nothing
@@ -762,27 +807,30 @@ load_named_dir_walk_cb(TSK_FS_FILE * a_fs_file, const char *a_path,
 
 /** \internal
  * Proces a file system and populate a list of the metadata structures
- * that are reachable by file names. This is used to find orphan files. 
- * Each file system has code that does the populating. 
+ * that are reachable by file names. This is used to find orphan files.
+ * Each file system has code that does the populating.
  */
 TSK_RETVAL_ENUM
 tsk_fs_dir_load_inum_named(TSK_FS_INFO * a_fs)
 {
-    if (a_fs->list_inum_named != NULL)
+    tsk_take_lock(&a_fs->list_inum_named_lock);
+    if (a_fs->list_inum_named != NULL) {
+        tsk_release_lock(&a_fs->list_inum_named_lock);
         return TSK_OK;
+    }
+    tsk_release_lock(&a_fs->list_inum_named_lock);
 
     /* Do a dir_walk.  There is internal caching code that will populate
      * the structure.  The callback is really a dummy call.  This could
      * be made more effecient in the future (not do callbacks...).  We
-     * specify UNALLOC only as a flag on the assumption that there will 
-     * be fewer callbacks for UNALLOC than ALLOC. 
+     * specify UNALLOC only as a flag on the assumption that there will
+     * be fewer callbacks for UNALLOC than ALLOC.
      */
     if (tsk_fs_dir_walk(a_fs, a_fs->root_inum,
             TSK_FS_NAME_FLAG_UNALLOC | TSK_FS_DIR_WALK_FLAG_RECURSE |
             TSK_FS_DIR_WALK_FLAG_NOORPHAN, load_named_dir_walk_cb, NULL)) {
-        strncat(tsk_errstr2,
-            " - tsk_fs_dir_load_inum_named: identifying inodes allocated by file names",
-            TSK_ERRSTR_L);
+        tsk_error_errstr2_concat(
+            "- tsk_fs_dir_load_inum_named: identifying inodes allocated by file names");
         return TSK_ERR;
     }
 
@@ -805,11 +853,16 @@ load_orphan_dir_walk_cb(TSK_FS_FILE * a_fs_file, const char *a_path,
 {
     FIND_ORPHAN_DATA *data = (FIND_ORPHAN_DATA *) a_ptr;
 
+    // ignore DOT entries
+    if ((a_fs_file->name) && (a_fs_file->name->name) && 
+            (TSK_FS_ISDOT(a_fs_file->name->name)))
+        return TSK_WALK_CONT;
+
     // add this entry to the orphan list
     if (a_fs_file->meta) {
         tsk_list_add(&data->orphan_subdir_list, a_fs_file->meta->addr);
 
-        /* FAT file systems spend a lot of time hunting for parent 
+        /* FAT file systems spend a lot of time hunting for parent
          * directory addresses, so we put this code in here to save
          * the info when we have it. */
         if ((a_fs_file->meta->type == TSK_FS_META_TYPE_DIR)
@@ -831,12 +884,15 @@ find_orphan_meta_walk_cb(TSK_FS_FILE * a_fs_file, void *a_ptr)
     TSK_FS_INFO *fs = a_fs_file->fs_info;
 
     /* We want only orphans, then check if this
-     * inode is in the seen list 
+     * inode is in the seen list
      */
+    tsk_take_lock(&fs->list_inum_named_lock);
     if ((fs->list_inum_named)
         && (tsk_list_find(fs->list_inum_named, a_fs_file->meta->addr))) {
+        tsk_release_lock(&fs->list_inum_named_lock);
         return TSK_WALK_CONT;
     }
+    tsk_release_lock(&fs->list_inum_named_lock);
 
     // check if we have already added it as an orphan (in a subdirectory)
     if (tsk_list_find(data->orphan_subdir_list, a_fs_file->meta->addr)) {
@@ -859,7 +915,7 @@ find_orphan_meta_walk_cb(TSK_FS_FILE * a_fs_file, void *a_ptr)
     if (tsk_fs_dir_add(data->fs_dir, data->fs_name))
         return TSK_WALK_ERROR;
 
-    /* FAT file systems spend a lot of time hunting for parent 
+    /* FAT file systems spend a lot of time hunting for parent
      * directory addresses, so we put this code in here to save
      * the info when we have it. */
     if (TSK_FS_TYPE_ISFAT(fs->ftype)) {
@@ -875,9 +931,8 @@ find_orphan_meta_walk_cb(TSK_FS_FILE * a_fs_file, void *a_ptr)
                 TSK_FS_DIR_WALK_FLAG_UNALLOC | TSK_FS_DIR_WALK_FLAG_RECURSE
                 | TSK_FS_DIR_WALK_FLAG_NOORPHAN, load_orphan_dir_walk_cb,
                 data)) {
-            strncat(tsk_errstr2,
-                " - tsk_fs_dir_load_inum_named: identifying inodes allocated by file names",
-                TSK_ERRSTR_L);
+            tsk_error_errstr2_concat(
+                " - tsk_fs_dir_load_inum_named: identifying inodes allocated by file names");
             return TSK_ERR;
         }
     }
@@ -885,8 +940,33 @@ find_orphan_meta_walk_cb(TSK_FS_FILE * a_fs_file, void *a_ptr)
     return TSK_WALK_CONT;
 }
 
+
 /** \internal
- * Search the file system for orphan files and create the orphan file directory. 
+ * Adds the fake metadata entry in the FS_DIR->fs_file struct for the orphan files directory
+ *
+ * @returns 1 on error
+ */
+static uint8_t
+tsk_fs_dir_add_orphan_dir_meta(TSK_FS_INFO *a_fs, TSK_FS_DIR *a_fs_dir)
+{
+    // populate the fake FS_FILE structure for the "Orphan Directory"
+    if ((a_fs_dir->fs_file = tsk_fs_file_alloc(a_fs)) == NULL) {
+        return 1;
+    }
+    
+    if ((a_fs_dir->fs_file->meta =
+         tsk_fs_meta_alloc(sizeof(TSK_DADDR_T))) == NULL) {
+        return 1;
+    }
+    
+    if (tsk_fs_dir_make_orphan_dir_meta(a_fs, a_fs_dir->fs_file->meta)) {
+        return 1;
+    }
+    return 0;
+}
+
+/** \internal
+ * Search the file system for orphan files and create the orphan file directory.
  * @param a_fs File system to search
  * @param a_fs_dir Structure to store the orphan file directory info in.
  */
@@ -896,50 +976,55 @@ tsk_fs_dir_find_orphans(TSK_FS_INFO * a_fs, TSK_FS_DIR * a_fs_dir)
     FIND_ORPHAN_DATA data;
     size_t i;
 
+    tsk_take_lock(&a_fs->orphan_dir_lock);
+
     if (a_fs->orphan_dir != NULL) {
-        if (tsk_fs_dir_copy(a_fs->orphan_dir, a_fs_dir))
+        if (tsk_fs_dir_copy(a_fs->orphan_dir, a_fs_dir)) {
+            tsk_release_lock(&a_fs->orphan_dir_lock);
+            return TSK_ERR;
+        }
+        
+        if (tsk_fs_dir_add_orphan_dir_meta(a_fs, a_fs_dir)) {
+            tsk_release_lock(&a_fs->orphan_dir_lock);
             return TSK_ERR;
+        }
+        
+        tsk_release_lock(&a_fs->orphan_dir_lock);
         return TSK_OK;
     }
 
-    if (a_fs->isOrphanHunting) {
-        return TSK_OK;
-    }
-    a_fs->isOrphanHunting = 1;
     memset(&data, 0, sizeof(FIND_ORPHAN_DATA));
 
     /* We first need to determine which of the unallocated meta structures
-     * have a name pointing to them.  We cache this data, so see if it is 
+     * have a name pointing to them.  We cache this data, so see if it is
      * already known. */
-    if (a_fs->list_inum_named == NULL) {
-        a_fs->isOrphanHunting = 0;
-        if (tsk_fs_dir_load_inum_named(a_fs) != TSK_OK) {
-            return TSK_ERR;
-        }
-        a_fs->isOrphanHunting = 1;
-        // note that list_inum_named could still be NULL if there are no deleted names.
+    if (tsk_fs_dir_load_inum_named(a_fs) != TSK_OK) {
+        tsk_release_lock(&a_fs->orphan_dir_lock);
+        return TSK_ERR;
     }
+    // note that list_inum_named could still be NULL if there are no deleted names.
 
-    /* Now we walk the unallocated metadata structures and find ones that are 
-     * not named.  The callback will add the names to the FS_DIR structure. 
+    /* Now we walk the unallocated metadata structures and find ones that are
+     * not named.  The callback will add the names to the FS_DIR structure.
      */
     data.fs_dir = a_fs_dir;
 
     // allocate a name once so that we will reuse for each name we add to FS_DIR
     if ((data.fs_name = tsk_fs_name_alloc(256, 0)) == NULL) {
-        a_fs->isOrphanHunting = 0;
+        tsk_release_lock(&a_fs->orphan_dir_lock);
         return TSK_ERR;
     }
 
     if (tsk_fs_meta_walk(a_fs, a_fs->first_inum, a_fs->last_inum,
             TSK_FS_META_FLAG_UNALLOC | TSK_FS_META_FLAG_USED,
             find_orphan_meta_walk_cb, &data)) {
-        a_fs->isOrphanHunting = 0;
         tsk_fs_name_free(data.fs_name);
+        tsk_release_lock(&a_fs->orphan_dir_lock);
         return TSK_ERR;
     }
 
     tsk_fs_name_free(data.fs_name);
+    data.fs_name = NULL;
 
     /* do some cleanup on the final list. This cleanup will compare the
      * entries in the root orphan directory with files that can be accessed
@@ -956,41 +1041,30 @@ tsk_fs_dir_find_orphans(TSK_FS_INFO * a_fs, TSK_FS_DIR * a_fs_dir)
         }
     }
 
-    // make copy of this so that we don't need to do it again. 
-    if ((a_fs->orphan_dir =
-            tsk_fs_dir_alloc(a_fs, a_fs_dir->addr, a_fs_dir->names_used)) == NULL) {
-        a_fs->isOrphanHunting = 0;
-        return TSK_ERR;
+    if (data.orphan_subdir_list) {
+        tsk_list_free(data.orphan_subdir_list);
+        data.orphan_subdir_list = NULL;
     }
 
-    if (tsk_fs_dir_copy(a_fs_dir, a_fs->orphan_dir)) {
-        tsk_fs_dir_close(a_fs->orphan_dir);
-        a_fs->orphan_dir = NULL;
-        a_fs->isOrphanHunting = 0;
-        return TSK_ERR;
-    }
 
-    // populate the fake FS_FILE structure for the "Orphan Directory"
-    /* Get the inode and verify it has attributes */
-    if ((a_fs_dir->fs_file = tsk_fs_file_alloc(a_fs)) == NULL) {
-        a_fs->isOrphanHunting = 0;
+    // make copy of this so that we don't need to do it again.
+    if ((a_fs->orphan_dir =
+            tsk_fs_dir_alloc(a_fs, a_fs_dir->addr, a_fs_dir->names_used)) == NULL) {
+        tsk_release_lock(&a_fs->orphan_dir_lock);
         return TSK_ERR;
     }
 
-    if ((a_fs_dir->fs_file->meta =
-            tsk_fs_meta_alloc(sizeof(TSK_DADDR_T))) == NULL) {
-        a_fs->isOrphanHunting = 0;
+    if (tsk_fs_dir_copy(a_fs_dir, a_fs->orphan_dir)) {
+        tsk_release_lock(&a_fs->orphan_dir_lock);
         return TSK_ERR;
     }
 
-    if (tsk_fs_dir_make_orphan_dir_meta(a_fs, a_fs_dir->fs_file->meta)) {
-        a_fs->isOrphanHunting = 0;
+    // populate the fake FS_FILE structure in the struct to be returned for the "Orphan Directory"
+    if (tsk_fs_dir_add_orphan_dir_meta(a_fs, a_fs_dir)) {
+        tsk_release_lock(&a_fs->orphan_dir_lock);
         return TSK_ERR;
     }
-
-    if (data.orphan_subdir_list)
-        tsk_list_free(data.orphan_subdir_list);
-
-    a_fs->isOrphanHunting = 0;
+    
+    tsk_release_lock(&a_fs->orphan_dir_lock);
     return TSK_OK;
 }
diff --git a/tsk3/fs/fs_file.c b/tsk3/fs/fs_file.c
index 8df238734..80f94ce23 100644
--- a/tsk3/fs/fs_file.c
+++ b/tsk3/fs/fs_file.c
@@ -3,7 +3,7 @@
  * The Sleuth Kit 
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2008 Brian Carrier.  All Rights reserved
+ * Copyright (c) 2008-2011 Brian Carrier.  All Rights reserved
  *
  * This software is distributed under the Common Public License 1.0
  *
@@ -102,8 +102,8 @@ tsk_fs_file_open_meta(TSK_FS_INFO * a_fs,
     TSK_FS_FILE *fs_file;
 
     if ((a_fs == NULL) || (a_fs->tag != TSK_FS_INFO_TAG)) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_file_open_meta: called with NULL or unallocated structures");
         return NULL;
     }
@@ -155,8 +155,8 @@ tsk_fs_file_open(TSK_FS_INFO * a_fs,
     TSK_FS_NAME *fs_name = NULL;
 
     if ((a_fs == NULL) || (a_fs->tag != TSK_FS_INFO_TAG)) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_file_open: called with NULL or unallocated structures");
         return NULL;
     }
@@ -173,8 +173,8 @@ tsk_fs_file_open(TSK_FS_INFO * a_fs,
     }
     else if (retval == 1) {
         tsk_fs_name_free(fs_name);
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_file_open: path not found: %s", a_path);
         return NULL;
     }
@@ -207,14 +207,14 @@ tsk_fs_file_attr_check(TSK_FS_FILE * a_fs_file, char *a_func)
     // check the FS_INFO, FS_FILE structures
     if ((a_fs_file == NULL) || (a_fs_file->meta == NULL)
         || (a_fs_file->fs_info == NULL)) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "%s: called with NULL pointers", a_func);
         return 1;
     }
     else if (a_fs_file->meta->tag != TSK_FS_META_TAG) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "%s: called with unallocated structures", a_func);
         return 1;
     }
@@ -222,8 +222,8 @@ tsk_fs_file_attr_check(TSK_FS_FILE * a_fs_file, char *a_func)
 
     // If the attributes haven't been loaded, then load them.
     if (a_fs_file->meta->attr_state == TSK_FS_META_ATTR_ERROR) {
-        tsk_errno = TSK_ERR_FS_INODE_COR;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+        tsk_error_set_errstr(
             "%s: called for file with corrupt data", a_func);
         return 1;
     }
@@ -341,15 +341,15 @@ tsk_fs_file_walk_type(TSK_FS_FILE * a_fs_file,
     // check the FS_INFO, FS_FILE structures
     if ((a_fs_file == NULL) || (a_fs_file->meta == NULL)
         || (a_fs_file->fs_info == NULL)) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_file_walk: called with NULL pointers");
         return 1;
     }
     else if ((a_fs_file->fs_info->tag != TSK_FS_INFO_TAG)
         || (a_fs_file->meta->tag != TSK_FS_META_TAG)) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_file_walk: called with unallocated structures");
         return 1;
     }
@@ -397,15 +397,15 @@ tsk_fs_file_walk(TSK_FS_FILE * a_fs_file,
     // check the FS_INFO, FS_FILE structures
     if ((a_fs_file == NULL) || (a_fs_file->meta == NULL)
         || (a_fs_file->fs_info == NULL)) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_file_walk: called with NULL pointers");
         return 1;
     }
     else if ((a_fs_file->fs_info->tag != TSK_FS_INFO_TAG)
         || (a_fs_file->meta->tag != TSK_FS_META_TAG)) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_file_walk: called with unallocated structures");
         return 1;
     }
@@ -452,15 +452,15 @@ tsk_fs_file_read_type(TSK_FS_FILE * a_fs_file,
     // check the FS_INFO, FS_FILE structures
     if ((a_fs_file == NULL) || (a_fs_file->meta == NULL)
         || (a_fs_file->fs_info == NULL)) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_file_read: called with NULL pointers");
         return -1;
     }
     else if ((a_fs_file->fs_info->tag != TSK_FS_INFO_TAG)
         || (a_fs_file->meta->tag != TSK_FS_META_TAG)) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_file_read: called with unallocated structures");
         return -1;
     }
@@ -496,8 +496,8 @@ tsk_fs_file_read(TSK_FS_FILE * a_fs_file,
     const TSK_FS_ATTR *fs_attr;
 
     if ((a_fs_file == NULL) || (a_fs_file->fs_info == NULL)) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_file_read: fs_info is NULL");
         return -1;
     }
@@ -521,8 +521,8 @@ tsk_fs_file_get_owner_sid(TSK_FS_FILE * a_fs_file, char **sid_str)
 {
     if ((a_fs_file == NULL) || (a_fs_file->fs_info == NULL)
         || (a_fs_file->meta == NULL) || (sid_str == NULL)) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_file_get_owner_sid: fs_info is NULL");
         return 1;
     }
@@ -531,8 +531,8 @@ tsk_fs_file_get_owner_sid(TSK_FS_FILE * a_fs_file, char **sid_str)
     // This function will only work on NTFS filesystems. 
     if (!a_fs_file->fs_info->fread_owner_sid) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "Unsupported function");
+        tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+        tsk_error_set_errstr("Unsupported function");
         return 1;
     }
 
diff --git a/tsk3/fs/fs_inode.c b/tsk3/fs/fs_inode.c
index 44dcd3d51..6642f93e2 100644
--- a/tsk3/fs/fs_inode.c
+++ b/tsk3/fs/fs_inode.c
@@ -1,7 +1,7 @@
 /*
  * The Sleuth Kit
  *
- * Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+ * Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
  *
  * LICENSE
  *	This software is distributed under the IBM Public License.
diff --git a/tsk3/fs/fs_io.c b/tsk3/fs/fs_io.c
index c99dca2e2..030efed2a 100644
--- a/tsk3/fs/fs_io.c
+++ b/tsk3/fs/fs_io.c
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+ * Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
  * Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
  * 
  * Copyright (c) 1997,1998,1999, International Business Machines          
@@ -47,14 +47,14 @@ tsk_fs_read(TSK_FS_INFO * a_fs, TSK_OFF_T a_off, char *a_buf, size_t a_len)
         && ((TSK_DADDR_T) a_off >=
             ((a_fs->last_block_act + 1) * a_fs->block_size))) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_READ;
+        tsk_error_set_errno(TSK_ERR_FS_READ);
         if ((TSK_DADDR_T) a_off <
             ((a_fs->last_block + 1) * a_fs->block_size))
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errstr(
                 "tsk_fs_read: Offset missing in partial image: %" PRIuDADDR
                 ")", a_off);
         else
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errstr(
                 "tsk_fs_read: Offset is too large for image: %"
                 PRIuDADDR ")", a_off);
         return -1;
@@ -98,7 +98,6 @@ tsk_fs_read(TSK_FS_INFO * a_fs, TSK_OFF_T a_off, char *a_buf, size_t a_len)
 }
 
 
-
 /**
  * \ingroup fslib
  * Read a file system block into a char* buffer.  
@@ -112,15 +111,14 @@ tsk_fs_read(TSK_FS_INFO * a_fs, TSK_OFF_T a_off, char *a_buf, size_t a_len)
  * @return The number of bytes read or -1 on error. 
  */
 ssize_t
-tsk_fs_read_block(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr, char *a_buf,
-    size_t a_len)
+tsk_fs_read_block(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr, char *a_buf, size_t a_len)
 {
     TSK_OFF_T off = 0;
 
     if (a_len % a_fs->block_size) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_READ;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_READ);
+        tsk_error_set_errstr(
             "tsk_fs_read_block: length %" PRIuSIZE ""
             " not a multiple of %d", a_len, a_fs->block_size);
         return -1;
@@ -128,13 +126,13 @@ tsk_fs_read_block(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr, char *a_buf,
 
     if (a_addr > a_fs->last_block_act) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_READ;
+        tsk_error_set_errno(TSK_ERR_FS_READ);
         if (a_addr <= a_fs->last_block)
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errstr(
                 "tsk_fs_read_block: Address missing in partial image: %"
                 PRIuDADDR ")", a_addr);
         else
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errstr(
                 "tsk_fs_read_block: Address is too large for image: %"
                 PRIuDADDR ")", a_addr);
         return -1;
diff --git a/tsk3/fs/fs_load.c b/tsk3/fs/fs_load.c
index 6f4c9c8c2..491b069f7 100644
--- a/tsk3/fs/fs_load.c
+++ b/tsk3/fs/fs_load.c
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  * 
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2005-2008 Brian Carrier.  All rights reserved
+ * Copyright (c) 2005-2011 Brian Carrier.  All rights reserved
  *
  * This software is distributed under the Common Public License 1.0
  *
diff --git a/tsk3/fs/fs_name.c b/tsk3/fs/fs_name.c
index 558790fbb..7b6cf0424 100644
--- a/tsk3/fs/fs_name.c
+++ b/tsk3/fs/fs_name.c
@@ -7,7 +7,7 @@
 ** depending on the file system type.
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier.  All Rights reserved
 ** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved 
 **
 ** TASK
@@ -219,7 +219,7 @@ tsk_fs_name_copy(TSK_FS_NAME * a_fs_name_to,
  * @param a_len Length of buffer
  */
 uint8_t
-tsk_fs_meta_make_ls(TSK_FS_META * a_fs_meta, char *a_buf, size_t a_len)
+tsk_fs_meta_make_ls(const TSK_FS_META * a_fs_meta, char *a_buf, size_t a_len)
 {
     if (a_len < 12) {
         return 1;
diff --git a/tsk3/fs/fs_open.c b/tsk3/fs/fs_open.c
index d1c0076a9..df610876d 100644
--- a/tsk3/fs/fs_open.c
+++ b/tsk3/fs/fs_open.c
@@ -1,15 +1,15 @@
 /*
 ** tsk_fs_open_img
-** The Sleuth Kit 
+** The Sleuth Kit
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
-** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved 
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
 **
 ** TASK
 ** Copyright (c) 2002 Brian Carrier, @stake Inc.  All rights reserved
-** 
-** Copyright (c) 1997,1998,1999, International Business Machines          
+**
+** Copyright (c) 1997,1998,1999, International Business Machines
 ** Corporation and others. All Rights Reserved.
 */
 
@@ -29,19 +29,19 @@
 /**
  * \file fs_open.c
  * Contains the general code to open a file system -- this calls
- * the file system -specific opening routines. 
+ * the file system -specific opening routines.
  */
 
 
 /**
  * \ingroup fslib
- * Tries to process data in a volume as a file system. 
- * Returns a structure that can be used for analysis and reporting. 
+ * Tries to process data in a volume as a file system.
+ * Returns a structure that can be used for analysis and reporting.
  *
  * @param a_part_info Open volume to read from and analyze
  * @param a_ftype Type of file system (or autodetect)
  *
- * @return NULL on error 
+ * @return NULL on error
  */
 TSK_FS_INFO *
 tsk_fs_open_vol(const TSK_VS_PART_INFO * a_part_info,
@@ -50,15 +50,15 @@ tsk_fs_open_vol(const TSK_VS_PART_INFO * a_part_info,
     TSK_OFF_T offset;
     if (a_part_info == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_open_vol: Null vpart handle");
         return NULL;
     }
     else if (a_part_info->vs == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_open_vol: Null vs handle");
         return NULL;
     }
@@ -71,14 +71,14 @@ tsk_fs_open_vol(const TSK_VS_PART_INFO * a_part_info,
 
 /**
  * \ingroup fslib
- * Tries to process data in a disk image at a given offset as a file system. 
- * Returns a structure that can be used for analysis and reporting. 
+ * Tries to process data in a disk image at a given offset as a file system.
+ * Returns a structure that can be used for analysis and reporting.
  *
  * @param a_img_info Disk image to analyze
  * @param a_offset Byte offset to start analyzing from
  * @param a_ftype Type of file system (or autodetect)
  *
- * @return NULL on error 
+ * @return NULL on error
  */
 TSK_FS_INFO *
 tsk_fs_open_img(TSK_IMG_INFO * a_img_info, TSK_OFF_T a_offset,
@@ -86,13 +86,13 @@ tsk_fs_open_img(TSK_IMG_INFO * a_img_info, TSK_OFF_T a_offset,
 {
     if (a_img_info == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "tsk_fs_open_img: Null image handle");
         return NULL;
     }
 
-    /* We will try different file systems ... 
+    /* We will try different file systems ...
      * We need to try all of them in case more than one matches
      */
     if (a_ftype == TSK_FS_TYPE_DETECT) {
@@ -125,8 +125,8 @@ tsk_fs_open_img(TSK_IMG_INFO * a_img_info, TSK_OFF_T a_offset,
                 fs_set->close(fs_set);
                 fs_info->close(fs_info);
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_UNKTYPE;
-                snprintf(tsk_errstr, TSK_ERRSTR_L, "FAT or %s", set);
+                tsk_error_set_errno(TSK_ERR_FS_UNKTYPE);
+                tsk_error_set_errstr("FAT or %s", set);
                 return NULL;
             }
         }
@@ -145,8 +145,8 @@ tsk_fs_open_img(TSK_IMG_INFO * a_img_info, TSK_OFF_T a_offset,
                 fs_set->close(fs_set);
                 fs_info->close(fs_info);
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_UNKTYPE;
-                snprintf(tsk_errstr, TSK_ERRSTR_L, "EXT2/3 or %s", set);
+                tsk_error_set_errno(TSK_ERR_FS_UNKTYPE);
+                tsk_error_set_errstr("EXT2/3 or %s", set);
                 return NULL;
             }
         }
@@ -165,8 +165,8 @@ tsk_fs_open_img(TSK_IMG_INFO * a_img_info, TSK_OFF_T a_offset,
                 fs_set->close(fs_set);
                 fs_info->close(fs_info);
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_UNKTYPE;
-                snprintf(tsk_errstr, TSK_ERRSTR_L, "UFS or %s", set);
+                tsk_error_set_errno(TSK_ERR_FS_UNKTYPE);
+                tsk_error_set_errstr("UFS or %s", set);
                 return NULL;
             }
         }
@@ -187,8 +187,8 @@ tsk_fs_open_img(TSK_IMG_INFO * a_img_info, TSK_OFF_T a_offset,
                 fs_set->close(fs_set);
                 fs_info->close(fs_info);
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_UNKTYPE;
-                snprintf(tsk_errstr, TSK_ERRSTR_L, "HFS or %s", set);
+                tsk_error_set_errno(TSK_ERR_FS_UNKTYPE);
+                tsk_error_set_errstr("HFS or %s", set);
                 return NULL;
             }
         }
@@ -204,8 +204,8 @@ tsk_fs_open_img(TSK_IMG_INFO * a_img_info, TSK_OFF_T a_offset,
                 fs_set->close(fs_set);
                 fs_info->close(fs_info);
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_UNKTYPE;
-                snprintf(tsk_errstr, TSK_ERRSTR_L, "ISO9660 or %s", set);
+                tsk_error_set_errno(TSK_ERR_FS_UNKTYPE);
+                tsk_error_set_errstr("ISO9660 or %s", set);
                 return NULL;
             }
             fs_set = fs_info;
@@ -217,9 +217,7 @@ tsk_fs_open_img(TSK_IMG_INFO * a_img_info, TSK_OFF_T a_offset,
 
         if (fs_set == NULL) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_UNKTYPE;
-            tsk_errstr[0] = '\0';
-            tsk_errstr2[0] = '\0';
+            tsk_error_set_errno(TSK_ERR_FS_UNKTYPE);
             return NULL;
         }
         return fs_set;
@@ -243,8 +241,8 @@ tsk_fs_open_img(TSK_IMG_INFO * a_img_info, TSK_OFF_T a_offset,
             return swapfs_open(a_img_info, a_offset);
         else {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_UNSUPTYPE;
-            snprintf(tsk_errstr, TSK_ERRSTR_L, "%X", (int) a_ftype);
+            tsk_error_set_errno(TSK_ERR_FS_UNSUPTYPE);
+            tsk_error_set_errstr("%X", (int) a_ftype);
             return NULL;
         }
     }
@@ -262,3 +260,38 @@ tsk_fs_close(TSK_FS_INFO * a_fs)
         return;
     a_fs->close(a_fs);
 }
+
+/* tsk_fs_malloc - init lock after tsk_malloc 
+ * This is for fs module and all it's inheritances
+ */
+TSK_FS_INFO *
+tsk_fs_malloc(size_t a_len)
+{
+    TSK_FS_INFO * fs_info;
+    if ((fs_info =
+            (TSK_FS_INFO *) tsk_malloc(a_len)) == NULL)
+        return NULL;
+    tsk_init_lock(&fs_info->list_inum_named_lock);
+    tsk_init_lock(&fs_info->orphan_dir_lock);
+    
+    fs_info->list_inum_named = NULL;
+
+    return fs_info;
+}
+
+/* tsk_fs_free - deinit lock before free memory 
+ * This is for fs module and all it's inheritances
+ */
+void
+tsk_fs_free(TSK_FS_INFO * a_fs_info)
+{
+    if (a_fs_info->list_inum_named) {
+        tsk_list_free(a_fs_info->list_inum_named);
+        a_fs_info->list_inum_named = NULL;
+    }
+    
+    tsk_deinit_lock(&a_fs_info->list_inum_named_lock);
+    tsk_deinit_lock(&a_fs_info->orphan_dir_lock);
+
+    free(a_fs_info);
+}
diff --git a/tsk3/fs/fs_parse.c b/tsk3/fs/fs_parse.c
index 6faa6b9ff..5ce88a1fe 100644
--- a/tsk3/fs/fs_parse.c
+++ b/tsk3/fs/fs_parse.c
@@ -2,7 +2,7 @@
  * The Sleuth Kit 
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2005-2008 Brian Carrier.  All Rights reserved
+ * Copyright (c) 2005-2011 Brian Carrier.  All Rights reserved
  *
  *  This software is distributed under the Common Public License 1.0
  */
diff --git a/tsk3/fs/fs_types.c b/tsk3/fs/fs_types.c
index ad5b674f0..bd2582df4 100644
--- a/tsk3/fs/fs_types.c
+++ b/tsk3/fs/fs_types.c
@@ -5,7 +5,7 @@
 ** Identify the type of file system being used
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved 
 **
 ** TASK
diff --git a/tsk3/fs/hfs.c b/tsk3/fs/hfs.c
index d3e021472..d736ee9fe 100644
--- a/tsk3/fs/hfs.c
+++ b/tsk3/fs/hfs.c
@@ -8,13 +8,13 @@
 ** 14900 Conference Center Drive
 ** Chantilly, VA 20151
 **
-** Copyright (c) 2009 Brian Carrier.  All rights reserved.
+** Copyright (c) 2009-2011 Brian Carrier.  All rights reserved.
 **
 ** Judson Powers [jpowers@atc-nycorp.com]
 ** Copyright (c) 2008 ATC-NY.  All rights reserved.
 ** This file contains data developed with support from the National
 ** Institute of Justice, Office of Justice Programs, U.S. Department of Justice.
-** 
+**
 ** Wyatt Banks [wbanks@crucialsecurity.com]
 ** Copyright (c) 2005 Crucial Security Inc.  All rights reserved.
 **
@@ -85,7 +85,7 @@ hfs_checked_read_random(TSK_FS_INFO * fs, char *buf, size_t len,
     if (r != len) {
         if (r >= 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_READ;
+            tsk_error_set_errno(TSK_ERR_FS_READ);
         }
         return 1;
     }
@@ -172,7 +172,7 @@ hfs_ext_compare_keys(HFS_INFO * hfs, uint32_t cnid,
 /** \internal
  * Returns the length of an HFS+ B-tree INDEX key based on the tree header
  * structure and the length claimed in the record.  With some trees,
- * the length given in the record is not used. 
+ * the length given in the record is not used.
  * Note that this neither detects nor correctly handles 8-bit keys
  * (which should not be present in HFS+).
  * @param hfs File System
@@ -255,11 +255,11 @@ hfs_extents_to_attr(TSK_FS_INFO * a_fs, const hfs_ext_desc * a_extents,
 
 /**
  * Look in the extents catalog for entries for a given file. Add the runs
- * to the passed attribute structure. 
+ * to the passed attribute structure.
  *
  * @param hfs File system being analyzed
  * @param cnid file id of file to search for
- * @param a_attr Attribute to add extents runs to 
+ * @param a_attr Attribute to add extents runs to
  * @returns 1 on error and 0 on success
  */
 static uint8_t
@@ -294,9 +294,8 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid,
             tsk_fs_attrlist_get(hfs->extents_file->meta->attr,
             TSK_FS_ATTR_TYPE_DEFAULT);
         if (!hfs->extents_attr) {
-            strncat(tsk_errstr2,
-                " - Default Attribute not found in Extents File",
-                TSK_ERRSTR_L - strlen(tsk_errstr2));
+            tsk_error_errstr2_concat(
+                "- Default Attribute not found in Extents File");
             return 1;
         }
 
@@ -307,15 +306,15 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid,
         if (cnt != sizeof(hfs_btree_header_record)) {
             if (cnt >= 0) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_READ;
+                tsk_error_set_errno(TSK_ERR_FS_READ);
             }
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
+            tsk_error_set_errstr2(
                 "hfs_ext_find_extent_record_attr: Error reading header");
             return 1;
         }
     }
 
-    // allocate a node buffer 
+    // allocate a node buffer
     nodesize = tsk_getu16(fs->endian, hfs->extents_header.nodesize);
     if ((node = (char *) tsk_malloc(nodesize)) == NULL) {
         return 1;
@@ -349,11 +348,11 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid,
         ssize_t cnt;
         hfs_btree_node *node_desc;
 
-        // sanity check 
+        // sanity check
         if (cur_node > tsk_getu32(fs->endian,
                 hfs->extents_header.totalNodes)) {
-            tsk_errno = TSK_ERR_FS_GENFS;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_GENFS);
+            tsk_error_set_errstr(
                 "hfs_ext_find_extent_record_attr: Node %d too large for file",
                 cur_node);
             free(node);
@@ -372,9 +371,9 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid,
         if (cnt != nodesize) {
             if (cnt >= 0) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_READ;
+                tsk_error_set_errno(TSK_ERR_FS_READ);
             }
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
+            tsk_error_set_errstr2(
                 "hfs_ext_find_extent_record_attr: Error reading node %d at offset %"
                 PRIuOFF, cur_node, cur_off);
             free(node);
@@ -386,8 +385,8 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid,
         num_rec = tsk_getu16(fs->endian, node_desc->num_rec);
 
         if (num_rec == 0) {
-            tsk_errno = TSK_ERR_FS_GENFS;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_GENFS);
+            tsk_error_set_errstr(
                 "hfs_ext_find_extent_record: zero records in node %"
                 PRIu32, cur_node);
             free(node);
@@ -412,13 +411,13 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid,
                 size_t rec_off;
                 hfs_btree_key_ext *key;
 
-                // get the record offset in the node 
+                // get the record offset in the node
                 rec_off =
                     tsk_getu16(fs->endian,
                     &node[nodesize - (rec + 1) * 2]);
                 if (rec_off > nodesize) {
-                    tsk_errno = TSK_ERR_FS_GENFS;
-                    snprintf(tsk_errstr, TSK_ERRSTR_L,
+                    tsk_error_set_errno(TSK_ERR_FS_GENFS);
+                    tsk_error_set_errstr(
                         "hfs_ext_find_extent_record_attr: offset of record %d in index node %d too large (%zu vs %"
                         PRIu16 ")", rec, cur_node, rec_off, nodesize);
                     free(node);
@@ -445,8 +444,8 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid,
                         2 + hfs_get_idxkeylen(hfs, tsk_getu16(fs->endian,
                             key->key_len), &(hfs->extents_header));
                     if (rec_off + keylen > nodesize) {
-                        tsk_errno = TSK_ERR_FS_GENFS;
-                        snprintf(tsk_errstr, TSK_ERRSTR_L,
+                        tsk_error_set_errno(TSK_ERR_FS_GENFS);
+                        tsk_error_set_errstr(
                             "hfs_ext_find_extent_record_attr: offset and keylenth of record %d in index node %d too large (%zu vs %"
                             PRIu16 ")", rec, cur_node,
                             rec_off + keylen, nodesize);
@@ -501,8 +500,8 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid,
                     tsk_getu16(fs->endian,
                     &node[nodesize - (rec + 1) * 2]);
                 if (rec_off > nodesize) {
-                    tsk_errno = TSK_ERR_FS_GENFS;
-                    snprintf(tsk_errstr, TSK_ERRSTR_L,
+                    tsk_error_set_errno(TSK_ERR_FS_GENFS);
+                    tsk_error_set_errstr(
                         "hfs_ext_find_extent_record_attr: offset of record %d in leaf node %d too large (%zu vs %"
                         PRIu16 ")", rec, cur_node, rec_off, nodesize);
                     free(node);
@@ -534,8 +533,8 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid,
 
                 keylen = 2 + tsk_getu16(fs->endian, key->key_len);
                 if (rec_off + keylen > nodesize) {
-                    tsk_errno = TSK_ERR_FS_GENFS;
-                    snprintf(tsk_errstr, TSK_ERRSTR_L,
+                    tsk_error_set_errno(TSK_ERR_FS_GENFS);
+                    tsk_error_set_errstr(
                         "hfs_ext_find_extent_record_attr: offset and keylenth of record %d in leaf node %d too large (%zu vs %"
                         PRIu16 ")", rec, cur_node, rec_off + keylen,
                         nodesize);
@@ -551,18 +550,16 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid,
 
                 attr_run =
                     hfs_extents_to_attr(fs, extents->extents, ext_off);
-                if ((attr_run == NULL) && (tsk_errno)) {
-                    strncat(tsk_errstr2,
-                        " - hfs_ext_find_extent_record_attr",
-                        TSK_ERRSTR_L - strlen(tsk_errstr2));
+                if ((attr_run == NULL) && (tsk_error_get_errno() != 0)) {
+                    tsk_error_errstr2_concat(
+                        "- hfs_ext_find_extent_record_attr");
                     free(node);
                     return 1;
                 }
 
                 if (tsk_fs_attr_add_run(fs, a_attr, attr_run)) {
-                    strncat(tsk_errstr2,
-                        " - hfs_ext_find_extent_record_attr",
-                        TSK_ERRSTR_L - strlen(tsk_errstr2));
+                    tsk_error_errstr2_concat(
+                        "- hfs_ext_find_extent_record_attr");
                     free(node);
                     return 1;
                 }
@@ -574,8 +571,8 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid,
             }
         }
         else {
-            tsk_errno = TSK_ERR_FS_GENFS;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_GENFS);
+            tsk_error_set_errstr(
                 "hfs_ext_find_extent_record: btree node %" PRIu32
                 " (%" PRIuOFF ") is neither index nor leaf (%" PRIu8 ")",
                 cur_node, cur_off, node_desc->type);
@@ -617,7 +614,7 @@ hfs_cat_compare_keys(HFS_INFO * hfs, const hfs_btree_key_cat * key1,
 /** \internal
  * @param hfs File system
  * @param targ_data can be null
- * @param a_cb callback 
+ * @param a_cb callback
  * @param ptr Pointer to pass to callback
  * @returns 1 on error
  */
@@ -666,11 +663,11 @@ hfs_cat_traverse(HFS_INFO * hfs, const void *targ_data,
         ssize_t cnt;
         hfs_btree_node *node_desc;
 
-        // sanity check 
+        // sanity check
         if (cur_node > tsk_getu32(fs->endian,
                 hfs->catalog_header.totalNodes)) {
-            tsk_errno = TSK_ERR_FS_GENFS;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_GENFS);
+            tsk_error_set_errstr(
                 "hfs_cat_traverse: Node %d too large for file", cur_node);
             free(node);
             return 1;
@@ -683,9 +680,9 @@ hfs_cat_traverse(HFS_INFO * hfs, const void *targ_data,
         if (cnt != nodesize) {
             if (cnt >= 0) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_READ;
+                tsk_error_set_errno(TSK_ERR_FS_READ);
             }
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
+            tsk_error_set_errstr2(
                 "hfs_cat_traverse: Error reading node %d at offset %"
                 PRIuOFF, cur_node, cur_off);
             free(node);
@@ -702,8 +699,8 @@ hfs_cat_traverse(HFS_INFO * hfs, const void *targ_data,
                 cur_node, cur_off, num_rec);
 
         if (num_rec == 0) {
-            tsk_errno = TSK_ERR_FS_GENFS;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_GENFS);
+            tsk_error_set_errstr(
                 "hfs_cat_traverse: zero records in node %"
                 PRIu32, cur_node);
             free(node);
@@ -726,8 +723,8 @@ hfs_cat_traverse(HFS_INFO * hfs, const void *targ_data,
                     tsk_getu16(fs->endian,
                     &node[nodesize - (rec + 1) * 2]);
                 if (rec_off > nodesize) {
-                    tsk_errno = TSK_ERR_FS_GENFS;
-                    snprintf(tsk_errstr, TSK_ERRSTR_L,
+                    tsk_error_set_errno(TSK_ERR_FS_GENFS);
+                    tsk_error_set_errstr(
                         "hfs_cat_traverse: offset of record %d in index node %d too large (%zu vs %"
                         PRIu16 ")", rec, cur_node, rec_off, nodesize);
                     free(node);
@@ -749,8 +746,8 @@ hfs_cat_traverse(HFS_INFO * hfs, const void *targ_data,
                     a_cb(hfs, HFS_BT_NODE_TYPE_IDX, targ_data, key,
                     cur_off + rec_off, ptr);
                 if (retval == HFS_BTREE_CB_ERR) {
-                    tsk_errno = TSK_ERR_FS_GENFS;
-                    snprintf(tsk_errstr2, TSK_ERRSTR_L,
+                    tsk_error_set_errno(TSK_ERR_FS_GENFS);
+                    tsk_error_set_errstr2(
                         "hfs_cat_traverse: Callback returned error");
                     free(node);
                     return 1;
@@ -763,8 +760,8 @@ hfs_cat_traverse(HFS_INFO * hfs, const void *targ_data,
                         2 + hfs_get_idxkeylen(hfs, tsk_getu16(fs->endian,
                             key->key_len), &(hfs->catalog_header));
                     if (rec_off + keylen > nodesize) {
-                        tsk_errno = TSK_ERR_FS_GENFS;
-                        snprintf(tsk_errstr, TSK_ERRSTR_L,
+                        tsk_error_set_errno(TSK_ERR_FS_GENFS);
+                        tsk_error_set_errstr(
                             "hfs_cat_traverse: offset of record and keylength %d in index node %d too large (%zu vs %"
                             PRIu16 ")", rec, cur_node, rec_off + keylen,
                             nodesize);
@@ -783,8 +780,8 @@ hfs_cat_traverse(HFS_INFO * hfs, const void *targ_data,
             }
             // check if we found a relevant node
             if (next_node == 0) {
-                tsk_errno = TSK_ERR_FS_GENFS;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_FS_GENFS);
+                tsk_error_set_errstr(
                     "hfs_cat_traverse: did not find any keys in index node %d",
                     cur_node);
                 is_done = 1;
@@ -807,8 +804,8 @@ hfs_cat_traverse(HFS_INFO * hfs, const void *targ_data,
                     tsk_getu16(fs->endian,
                     &node[nodesize - (rec + 1) * 2]);
                 if (rec_off > nodesize) {
-                    tsk_errno = TSK_ERR_FS_GENFS;
-                    snprintf(tsk_errstr, TSK_ERRSTR_L,
+                    tsk_error_set_errno(TSK_ERR_FS_GENFS);
+                    tsk_error_set_errstr(
                         "hfs_cat_traverse: offset of record %d in leaf node %d too large (%zu vs %"
                         PRIu16 ")", rec, cur_node, rec_off, nodesize);
                     free(node);
@@ -834,8 +831,8 @@ hfs_cat_traverse(HFS_INFO * hfs, const void *targ_data,
                     break;
                 }
                 else if (retval == HFS_BTREE_CB_ERR) {
-                    tsk_errno = TSK_ERR_FS_GENFS;
-                    snprintf(tsk_errstr2, TSK_ERRSTR_L,
+                    tsk_error_set_errno(TSK_ERR_FS_GENFS);
+                    tsk_error_set_errstr2(
                         "hfs_cat_traverse: Callback returned error");
                     free(node);
                     return 1;
@@ -854,8 +851,8 @@ hfs_cat_traverse(HFS_INFO * hfs, const void *targ_data,
             }
         }
         else {
-            tsk_errno = TSK_ERR_FS_GENFS;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_GENFS);
+            tsk_error_set_errstr(
                 "hfs_cat_traverse: btree node %" PRIu32
                 " (%" PRIu64 ") is neither index nor leaf (%" PRIu8 ")",
                 cur_node, cur_off, node_desc->type);
@@ -909,7 +906,7 @@ hfs_cat_get_record_offset_cb(HFS_INFO * hfs, int8_t level_type,
 
 /** \internal
  * Find the byte offset (from the start of the catalog file) to a record
- * in the catalog file.  
+ * in the catalog file.
  * @param hfs File System being analyzed
  * @param needle Key to search for
  * @returns Byte offset or 0 on error. 0 is also returned if catalog
@@ -930,7 +927,7 @@ hfs_cat_get_record_offset(HFS_INFO * hfs, const hfs_btree_key_cat * needle)
 /** \internal
  * Given a byte offset to a leaf record in teh catalog file, read the data as
  * a thread record. This will zero the buffer and read in the size of the thread
- * data. 
+ * data.
  * @param hfs File System
  * @param off Byte offset of record in catalog file (not including key)
  * @param thread [out] Buffer to write thread data into.
@@ -948,9 +945,9 @@ hfs_cat_read_thread_record(HFS_INFO * hfs, TSK_OFF_T off,
     if (cnt != 10) {
         if (cnt >= 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_READ;
+            tsk_error_set_errno(TSK_ERR_FS_READ);
         }
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
+        tsk_error_set_errstr2(
             "hfs_cat_read_thread_record: Error reading catalog offset %"
             PRIuOFF " (header)", off);
         return 1;
@@ -958,8 +955,8 @@ hfs_cat_read_thread_record(HFS_INFO * hfs, TSK_OFF_T off,
 
     if ((tsk_getu16(fs->endian, thread->rec_type) != HFS_FOLDER_THREAD)
         && (tsk_getu16(fs->endian, thread->rec_type) != HFS_FILE_THREAD)) {
-        tsk_errno = TSK_ERR_FS_GENFS;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_GENFS);
+        tsk_error_set_errstr(
             "hfs_cat_read_thread_record: unexpected record type %" PRIu16,
             tsk_getu16(fs->endian, thread->rec_type));
         return 1;
@@ -968,8 +965,8 @@ hfs_cat_read_thread_record(HFS_INFO * hfs, TSK_OFF_T off,
     uni_len = tsk_getu16(fs->endian, thread->name.length);
 
     if (uni_len > 255) {
-        tsk_errno = TSK_ERR_FS_INODE_COR;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+        tsk_error_set_errstr(
             "hfs_cat_read_thread_record: invalid string length (%" PRIu16
             ")", uni_len);
         return 1;
@@ -981,9 +978,9 @@ hfs_cat_read_thread_record(HFS_INFO * hfs, TSK_OFF_T off,
     if (cnt != uni_len * 2) {
         if (cnt >= 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_READ;
+            tsk_error_set_errno(TSK_ERR_FS_READ);
         }
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
+        tsk_error_set_errstr2(
             "hfs_cat_read_thread_record: Error reading catalog offset %"
             PRIuOFF " (name)", off + 10);
         return 1;
@@ -994,7 +991,7 @@ hfs_cat_read_thread_record(HFS_INFO * hfs, TSK_OFF_T off,
 
 /** \internal
  * Read a catalog record into a local data structure.  This reads the
- * correct amount, depending on if it is a file or folder. 
+ * correct amount, depending on if it is a file or folder.
  * @param hfs File system being analyzed
  * @param off Byte offset (in catalog file) of record (not including key)
  * @param record [out] Structure to read data into
@@ -1014,9 +1011,9 @@ hfs_cat_read_file_folder_record(HFS_INFO * hfs, TSK_OFF_T off,
     if (cnt != 2) {
         if (cnt >= 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_READ;
+            tsk_error_set_errno(TSK_ERR_FS_READ);
         }
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
+        tsk_error_set_errstr2(
             "hfs_cat_read_file_folder_record: Error reading record type from catalog offset %"
             PRIuOFF " (header)", off);
         return 1;
@@ -1029,9 +1026,9 @@ hfs_cat_read_file_folder_record(HFS_INFO * hfs, TSK_OFF_T off,
         if (cnt != sizeof(hfs_folder)) {
             if (cnt >= 0) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_READ;
+                tsk_error_set_errno(TSK_ERR_FS_READ);
             }
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
+            tsk_error_set_errstr2(
                 "hfs_cat_read_file_folder_record: Error reading catalog offset %"
                 PRIuOFF " (folder)", off);
             return 1;
@@ -1044,17 +1041,17 @@ hfs_cat_read_file_folder_record(HFS_INFO * hfs, TSK_OFF_T off,
         if (cnt != sizeof(hfs_file)) {
             if (cnt >= 0) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_READ;
+                tsk_error_set_errno(TSK_ERR_FS_READ);
             }
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
+            tsk_error_set_errstr2(
                 "hfs_cat_read_file_folder_record: Error reading catalog offset %"
                 PRIuOFF " (file)", off);
             return 1;
         }
     }
     else {
-        tsk_errno = TSK_ERR_FS_GENFS;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_GENFS);
+        tsk_error_set_errstr(
             "hfs_cat_read_file_folder_record: unexpected record type %"
             PRIu16, tsk_getu16(fs->endian, rec_type));
         return 1;
@@ -1066,7 +1063,7 @@ hfs_cat_read_file_folder_record(HFS_INFO * hfs, TSK_OFF_T off,
 
 /** \internal
  * Lookup an entry in the catalog file and save it into the entry.  Do not
- * call this for the special files that do not have an entry in the catalog. 
+ * call this for the special files that do not have an entry in the catalog.
  * data structure.
  * @param hfs File system being analyzed
  * @param inum Address (cnid) of file to open
@@ -1095,8 +1092,8 @@ hfs_cat_file_lookup(HFS_INFO * hfs, TSK_INUM_T inum, HFS_ENTRY * entry)
         (inum == HFS_ALLOCATION_FILE_ID) ||
         (inum == HFS_STARTUP_FILE_ID) ||
         (inum == HFS_ATTRIBUTES_FILE_ID)) {
-        tsk_errno = TSK_ERR_FS_GENFS;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_GENFS);
+        tsk_error_set_errstr(
             "hfs_cat_file_lookup: Called on special file: %" PRIuINUM,
             inum);
         return 1;
@@ -1117,14 +1114,14 @@ hfs_cat_file_lookup(HFS_INFO * hfs, TSK_INUM_T inum, HFS_ENTRY * entry)
     off = hfs_cat_get_record_offset(hfs, &key);
     if (off == 0) {
         // no parsing error, just not found
-        if (tsk_errno == 0) {
-            tsk_errno = TSK_ERR_FS_INODE_NUM;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+        if (tsk_error_get_errno() == 0) {
+            tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
+            tsk_error_set_errstr(
                 "hfs_cat_file_lookup: Error finding thread node for file (%"
                 PRIuINUM ")", inum);
         }
         else {
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
+            tsk_error_set_errstr2(
                 " hfs_cat_file_lookup: thread for file (%" PRIuINUM ")",
                 inum);
         }
@@ -1133,7 +1130,7 @@ hfs_cat_file_lookup(HFS_INFO * hfs, TSK_INUM_T inum, HFS_ENTRY * entry)
 
     /* read the thread record */
     if (hfs_cat_read_thread_record(hfs, off, &thread)) {
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
+        tsk_error_set_errstr2(
             " hfs_cat_file_lookup: file (%" PRIuINUM ")", inum);
         return 1;
     }
@@ -1155,14 +1152,14 @@ hfs_cat_file_lookup(HFS_INFO * hfs, TSK_INUM_T inum, HFS_ENTRY * entry)
     off = hfs_cat_get_record_offset(hfs, &key);
     if (off == 0) {
         // no parsing error, just not found
-        if (tsk_errno == 0) {
-            tsk_errno = TSK_ERR_FS_INODE_NUM;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+        if (tsk_error_get_errno() == 0) {
+            tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
+            tsk_error_set_errstr(
                 "hfs_cat_file_lookup: Error finding record node %"
                 PRIuINUM, inum);
         }
         else {
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
+            tsk_error_set_errstr2(
                 " hfs_cat_file_lookup: file (%" PRIuINUM ")", inum);
         }
         return 1;
@@ -1170,7 +1167,7 @@ hfs_cat_file_lookup(HFS_INFO * hfs, TSK_INUM_T inum, HFS_ENTRY * entry)
 
     /* read the record */
     if (hfs_cat_read_file_folder_record(hfs, off, &record)) {
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
+        tsk_error_set_errstr2(
             " hfs_cat_file_lookup: file (%" PRIuINUM ")", inum);
         return 1;
     }
@@ -1330,7 +1327,7 @@ hfs_make_specialbase(TSK_FS_FILE * fs_file)
 
 /**
  * \internal
- * Create an FS_INODE structure for the catalog file. 
+ * Create an FS_INODE structure for the catalog file.
  *
  * @param hfs File system to analyze
  * @param fs_file Structure to copy file information into.
@@ -1358,20 +1355,18 @@ hfs_make_catalog(HFS_INFO * hfs, TSK_FS_FILE * fs_file)
         tsk_getu64(fs->endian, hfs->fs->cat_file.logic_sz);
 
 
-    // convert the  runs in the volume header to attribute runs 
+    // convert the  runs in the volume header to attribute runs
     if (((attr_run =
                 hfs_extents_to_attr(fs, hfs->fs->cat_file.extents,
-                    0)) == NULL) && (tsk_errno)) {
-        strncat(tsk_errstr2, " - hfs_make_catalog",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+                    0)) == NULL) && (tsk_error_get_errno() != 0)) {
+        tsk_error_errstr2_concat("- hfs_make_catalog");
         return 1;
     }
 
     if ((fs_attr =
             tsk_fs_attrlist_getnew(fs_file->meta->attr,
                 TSK_FS_ATTR_NONRES)) == NULL) {
-        strncat(tsk_errstr2, " - hfs_make_catalog",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- hfs_make_catalog");
         tsk_fs_attr_run_free(attr_run);
         return 1;
     }
@@ -1382,8 +1377,7 @@ hfs_make_catalog(HFS_INFO * hfs, TSK_FS_FILE * fs_file)
             tsk_getu64(fs->endian, hfs->fs->cat_file.logic_sz),
             tsk_getu64(fs->endian, hfs->fs->cat_file.logic_sz),
             tsk_getu64(fs->endian, hfs->fs->cat_file.logic_sz), 0, 0)) {
-        strncat(tsk_errstr2, " - hfs_make_catalog",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- hfs_make_catalog");
         tsk_fs_attr_free(fs_attr);
         tsk_fs_attr_run_free(attr_run);
         return 1;
@@ -1391,8 +1385,7 @@ hfs_make_catalog(HFS_INFO * hfs, TSK_FS_FILE * fs_file)
 
     // see if catalog file has additional runs
     if (hfs_ext_find_extent_record_attr(hfs, HFS_CATALOG_FILE_ID, fs_attr)) {
-        strncat(tsk_errstr2, " - hfs_make_catalog",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- hfs_make_catalog");
         fs_file->meta->attr_state = TSK_FS_META_ATTR_ERROR;
         return 1;
     }
@@ -1433,17 +1426,15 @@ hfs_make_extents(HFS_INFO * hfs, TSK_FS_FILE * fs_file)
 
     if (((attr_run =
                 hfs_extents_to_attr(fs, hfs->fs->ext_file.extents,
-                    0)) == NULL) && (tsk_errno)) {
-        strncat(tsk_errstr2, " - hfs_make_extents",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+                    0)) == NULL) && (tsk_error_get_errno() != 0)) {
+        tsk_error_errstr2_concat("- hfs_make_extents");
         return 1;
     }
 
     if ((fs_attr =
             tsk_fs_attrlist_getnew(fs_file->meta->attr,
                 TSK_FS_ATTR_NONRES)) == NULL) {
-        strncat(tsk_errstr2, " - hfs_make_extents",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- hfs_make_extents");
         tsk_fs_attr_run_free(attr_run);
         return 1;
     }
@@ -1454,8 +1445,7 @@ hfs_make_extents(HFS_INFO * hfs, TSK_FS_FILE * fs_file)
             tsk_getu64(fs->endian, hfs->fs->ext_file.logic_sz),
             tsk_getu64(fs->endian, hfs->fs->ext_file.logic_sz),
             tsk_getu64(fs->endian, hfs->fs->ext_file.logic_sz), 0, 0)) {
-        strncat(tsk_errstr2, " - hfs_make_extents",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- hfs_make_extents");
         tsk_fs_attr_free(fs_attr);
         tsk_fs_attr_run_free(attr_run);
         return 1;
@@ -1470,7 +1460,7 @@ hfs_make_extents(HFS_INFO * hfs, TSK_FS_FILE * fs_file)
 
 /**
  * \internal
- * Create an FS_INODE structure for the blockmap / allocation file. 
+ * Create an FS_INODE structure for the blockmap / allocation file.
  *
  * @param hfs File system to analyze
  * @param fs_file Structure to copy file information into.
@@ -1499,17 +1489,15 @@ hfs_make_blockmap(HFS_INFO * hfs, TSK_FS_FILE * fs_file)
 
     if (((attr_run =
                 hfs_extents_to_attr(fs, hfs->fs->alloc_file.extents,
-                    0)) == NULL) && (tsk_errno)) {
-        strncat(tsk_errstr2, " - hfs_make_blockmap",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+                    0)) == NULL) && (tsk_error_get_errno() != 0)) {
+        tsk_error_errstr2_concat("- hfs_make_blockmap");
         return 1;
     }
 
     if ((fs_attr =
             tsk_fs_attrlist_getnew(fs_file->meta->attr,
                 TSK_FS_ATTR_NONRES)) == NULL) {
-        strncat(tsk_errstr2, " - hfs_make_blockmap",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- hfs_make_blockmap");
         tsk_fs_attr_run_free(attr_run);
         return 1;
     }
@@ -1520,8 +1508,7 @@ hfs_make_blockmap(HFS_INFO * hfs, TSK_FS_FILE * fs_file)
             tsk_getu64(fs->endian, hfs->fs->alloc_file.logic_sz),
             tsk_getu64(fs->endian, hfs->fs->alloc_file.logic_sz),
             tsk_getu64(fs->endian, hfs->fs->alloc_file.logic_sz), 0, 0)) {
-        strncat(tsk_errstr2, " - hfs_make_blockmap",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- hfs_make_blockmap");
         tsk_fs_attr_free(fs_attr);
         tsk_fs_attr_run_free(attr_run);
         return 1;
@@ -1530,8 +1517,7 @@ hfs_make_blockmap(HFS_INFO * hfs, TSK_FS_FILE * fs_file)
     // see if catalog file has additional runs
     if (hfs_ext_find_extent_record_attr(hfs, HFS_ALLOCATION_FILE_ID,
             fs_attr)) {
-        strncat(tsk_errstr2, " - hfs_make_blockmap",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- hfs_make_blockmap");
         fs_file->meta->attr_state = TSK_FS_META_ATTR_ERROR;
         return 1;
     }
@@ -1542,7 +1528,7 @@ hfs_make_blockmap(HFS_INFO * hfs, TSK_FS_FILE * fs_file)
 
 /**
 * \internal
- * Create an FS_INODE structure for the startup / boot file. 
+ * Create an FS_INODE structure for the startup / boot file.
  *
  * @param hfs File system to analyze
  * @param fs_file Structure to copy file information into.
@@ -1571,17 +1557,15 @@ hfs_make_startfile(HFS_INFO * hfs, TSK_FS_FILE * fs_file)
 
     if (((attr_run =
                 hfs_extents_to_attr(fs, hfs->fs->start_file.extents,
-                    0)) == NULL) && (tsk_errno)) {
-        strncat(tsk_errstr2, " - hfs_make_startfile",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+                    0)) == NULL) && (tsk_error_get_errno() != 0)) {
+        tsk_error_errstr2_concat(" - hfs_make_startfile");
         return 1;
     }
 
     if ((fs_attr =
             tsk_fs_attrlist_getnew(fs_file->meta->attr,
                 TSK_FS_ATTR_NONRES)) == NULL) {
-        strncat(tsk_errstr2, " - hfs_make_startfile",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- hfs_make_startfile");
         tsk_fs_attr_run_free(attr_run);
         return 1;
     }
@@ -1592,8 +1576,7 @@ hfs_make_startfile(HFS_INFO * hfs, TSK_FS_FILE * fs_file)
             tsk_getu64(fs->endian, hfs->fs->start_file.logic_sz),
             tsk_getu64(fs->endian, hfs->fs->start_file.logic_sz),
             tsk_getu64(fs->endian, hfs->fs->start_file.logic_sz), 0, 0)) {
-        strncat(tsk_errstr2, " - hfs_make_startfile",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- hfs_make_startfile");
         tsk_fs_attr_free(fs_attr);
         tsk_fs_attr_run_free(attr_run);
         return 1;
@@ -1601,8 +1584,7 @@ hfs_make_startfile(HFS_INFO * hfs, TSK_FS_FILE * fs_file)
 
     // see if catalog file has additional runs
     if (hfs_ext_find_extent_record_attr(hfs, HFS_STARTUP_FILE_ID, fs_attr)) {
-        strncat(tsk_errstr2, " - hfs_make_startfile",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- hfs_make_startfile");
         fs_file->meta->attr_state = TSK_FS_META_ATTR_ERROR;
         return 1;
     }
@@ -1614,7 +1596,7 @@ hfs_make_startfile(HFS_INFO * hfs, TSK_FS_FILE * fs_file)
 
 /**
  * \internal
- * Create an FS_INODE structure for the attributes file. 
+ * Create an FS_INODE structure for the attributes file.
  *
  * @param hfs File system to analyze
  * @param fs_file Structure to copy file information into.
@@ -1643,17 +1625,15 @@ hfs_make_attrfile(HFS_INFO * hfs, TSK_FS_FILE * fs_file)
 
     if (((attr_run =
                 hfs_extents_to_attr(fs, hfs->fs->attr_file.extents,
-                    0)) == NULL) && (tsk_errno)) {
-        strncat(tsk_errstr2, " - hfs_make_attrfile",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+                    0)) == NULL) && (tsk_error_get_errno() != 0)) {
+        tsk_error_errstr2_concat("- hfs_make_attrfile");
         return 1;
     }
 
     if ((fs_attr =
             tsk_fs_attrlist_getnew(fs_file->meta->attr,
                 TSK_FS_ATTR_NONRES)) == NULL) {
-        strncat(tsk_errstr2, " - hfs_make_attrfile",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat(" - hfs_make_attrfile");
         tsk_fs_attr_run_free(attr_run);
         return 1;
     }
@@ -1664,8 +1644,7 @@ hfs_make_attrfile(HFS_INFO * hfs, TSK_FS_FILE * fs_file)
             tsk_getu64(fs->endian, hfs->fs->attr_file.logic_sz),
             tsk_getu64(fs->endian, hfs->fs->attr_file.logic_sz),
             tsk_getu64(fs->endian, hfs->fs->attr_file.logic_sz), 0, 0)) {
-        strncat(tsk_errstr2, " - hfs_make_attrfile",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- hfs_make_attrfile");
         tsk_fs_attr_free(fs_attr);
         tsk_fs_attr_run_free(attr_run);
         return 1;
@@ -1674,8 +1653,7 @@ hfs_make_attrfile(HFS_INFO * hfs, TSK_FS_FILE * fs_file)
     // see if catalog file has additional runs
     if (hfs_ext_find_extent_record_attr(hfs, HFS_ATTRIBUTES_FILE_ID,
             fs_attr)) {
-        strncat(tsk_errstr2, " - hfs_make_attrfile",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- hfs_make_attrfile");
         fs_file->meta->attr_state = TSK_FS_META_ATTR_ERROR;
         return 1;
     }
@@ -1686,7 +1664,7 @@ hfs_make_attrfile(HFS_INFO * hfs, TSK_FS_FILE * fs_file)
 
 /**
  * \internal
- * Create an FS_FILE structure for the BadBlocks file. 
+ * Create an FS_FILE structure for the BadBlocks file.
  *
  * @param hfs File system to analyze
  * @param fs_file Structure to copy file information into.
@@ -1713,18 +1691,16 @@ hfs_make_badblockfile(HFS_INFO * hfs, TSK_FS_FILE * fs_file)
     if ((fs_attr =
             tsk_fs_attrlist_getnew(fs_file->meta->attr,
                 TSK_FS_ATTR_NONRES)) == NULL) {
-        strncat(tsk_errstr2, " - hfs_make_attrfile",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- hfs_make_attrfile");
         return 1;
     }
 
-    // �dd the run to the file. 
+    // Add the run to the file.
     if (tsk_fs_attr_set_run(fs_file, fs_attr, NULL, NULL,
             TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT,
             fs_file->meta->size, fs_file->meta->size, fs_file->meta->size,
             0, 0)) {
-        strncat(tsk_errstr2, " - hfs_make_attrfile",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- hfs_make_attrfile");
         tsk_fs_attr_free(fs_attr);
         return 1;
     }
@@ -1732,13 +1708,12 @@ hfs_make_badblockfile(HFS_INFO * hfs, TSK_FS_FILE * fs_file)
     // see if catalog file has additional runs
     if (hfs_ext_find_extent_record_attr(hfs, HFS_BAD_BLOCK_FILE_ID,
             fs_attr)) {
-        strncat(tsk_errstr2, " - hfs_make_attrfile",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- hfs_make_attrfile");
         fs_file->meta->attr_state = TSK_FS_META_ATTR_ERROR;
         return 1;
     }
 
-    /* @@@ We have a chicken and egg problem here...  The current design of 
+    /* @@@ We have a chicken and egg problem here...  The current design of
      * fs_attr_set() requires the size to be set, but we dont' know the size
      * until we look into the extents file (which adds to an attribute...).
      * This does not seem to be the best design...  neeed a way to test this. */
@@ -1752,9 +1727,9 @@ hfs_make_badblockfile(HFS_INFO * hfs, TSK_FS_FILE * fs_file)
 
 
 /** \internal
- * Copy the catalog file or folder record entry into a TSK data structure. 
+ * Copy the catalog file or folder record entry into a TSK data structure.
  * @param a_hfs File system being analyzed
- * @param a_entry Catalog record entry 
+ * @param a_entry Catalog record entry
  * @param a_fs_meta Structure to copy data into
  * Returns 1 on error.
  */
@@ -1767,8 +1742,8 @@ hfs_dinode_copy(HFS_INFO * a_hfs, const hfs_file_folder * a_entry,
     uint16_t hfsmode;
 
     if (a_fs_meta == NULL) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "hfs_dinode_copy: a_fs_meta is NULL");
         return 1;
     }
@@ -1793,8 +1768,8 @@ hfs_dinode_copy(HFS_INFO * a_hfs, const hfs_file_folder * a_entry,
         tsk_fs_attrlist_markunused(a_fs_meta->attr);
     }
 
-    /* 
-     * Copy the file type specific stuff first 
+    /*
+     * Copy the file type specific stuff first
      */
     hfsmode = tsk_getu16(fs->endian, std->perm.mode);
 
@@ -1825,7 +1800,7 @@ hfs_dinode_copy(HFS_INFO * a_hfs, const hfs_file_folder * a_entry,
     }
 
     /*
-     * Copy the standard stuff.  
+     * Copy the standard stuff.
      * Use default values (as defined in spec) if mode is not defined.
      */
     if ((hfsmode & HFS_IN_IFMT) == 0) {
@@ -1859,7 +1834,7 @@ hfs_dinode_copy(HFS_INFO * a_hfs, const hfs_file_folder * a_entry,
 
     a_fs_meta->addr = tsk_getu32(fs->endian, std->cnid);
 
-    // All entries here are used.  
+    // All entries here are used.
     a_fs_meta->flags = TSK_FS_META_FLAG_ALLOC | TSK_FS_META_FLAG_USED;
 
     /* TODO @@@ could fill in name2 with this entry's name and parent inode
@@ -1870,8 +1845,8 @@ hfs_dinode_copy(HFS_INFO * a_hfs, const hfs_file_folder * a_entry,
        if (fs_file->meta->type == TSK_FS_META_TYPE_LNK) {
        @@@ Need to do this.  We need to read the file content,
        but we don't really have enough context (i.e. FS_FILE)
-       to simply use the existing load and read functions. 
-       Probably need to make a dummy TSK_FS_FILE. 
+       to simply use the existing load and read functions.
+       Probably need to make a dummy TSK_FS_FILE.
        }
      */
 
@@ -1880,12 +1855,12 @@ hfs_dinode_copy(HFS_INFO * a_hfs, const hfs_file_folder * a_entry,
 
 
 /** \internal
- * Load a catalog file entry and save it in the TSK_FS_FILE structure. 
- * 
+ * Load a catalog file entry and save it in the TSK_FS_FILE structure.
+ *
  * @param fs File system to read from.
- * @param a_fs_file Structure to read into. 
+ * @param a_fs_file Structure to read into.
  * @param inum File address to load
- * @returns 1 on error 
+ * @returns 1 on error
  */
 static uint8_t
 hfs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file,
@@ -1895,8 +1870,8 @@ hfs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file,
     HFS_ENTRY entry;
 
     if (a_fs_file == NULL) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "hfs_inode_lookup: fs_file is NULL");
         return 1;
     }
@@ -1989,14 +1964,14 @@ hfs_load_attrs(TSK_FS_FILE * fs_file)
 
     if ((fs_file == NULL) || (fs_file->meta == NULL)
         || (fs_file->fs_info == NULL)) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "hfs_load_attrs: fs_file or meta is NULL");
         return 1;
     }
     if (fs_file->meta->content_ptr == NULL) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "hfs_load_attrs: content_ptr is NULL");
         return 1;
     }
@@ -2027,8 +2002,7 @@ hfs_load_attrs(TSK_FS_FILE * fs_file)
     if ((fs_attr =
             tsk_fs_attrlist_getnew(fs_file->meta->attr,
                 TSK_FS_ATTR_NONRES)) == NULL) {
-        strncat(tsk_errstr2, " - hfs_load_attrs",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat(" - hfs_load_attrs");
         return 1;
     }
     /* NOTE that fs_attr is now tied to fs_file->meta->attr.
@@ -2041,8 +2015,7 @@ hfs_load_attrs(TSK_FS_FILE * fs_file)
         if (tsk_fs_attr_set_run(fs_file, fs_attr, NULL, NULL,
                 TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT, 0, 0, 0,
                 0, 0)) {
-            strncat(tsk_errstr2, " - hfs_load_attrs (non-file)",
-                TSK_ERRSTR_L - strlen(tsk_errstr2));
+            tsk_error_errstr2_concat("- hfs_load_attrs (non-file)");
             return 1;
         }
         fs_file->meta->attr_state = TSK_FS_META_ATTR_STUDIED;
@@ -2063,7 +2036,7 @@ hfs_load_attrs(TSK_FS_FILE * fs_file)
        //  Get the indirect node value
        tsk_getu32(fs->endian, entry.cat.std.perm.special.inum)
 
-       // Find the indirect node 
+       // Find the indirect node
        "/____HFS+ Private Data/iNodeXXXX"
        // Load its runs and look in extents for others (based on its CNID)
      */
@@ -2072,21 +2045,19 @@ hfs_load_attrs(TSK_FS_FILE * fs_file)
     // Get the data fork and convert it to the TSK format
     fork = (hfs_fork *) fs_file->meta->content_ptr;
     if (((attr_run = hfs_extents_to_attr(fs, fork->extents, 0)) == NULL)
-        && (tsk_errno)) {
-        strncat(tsk_errstr2, " - hfs_load_attrs",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        && (tsk_error_get_errno() != 0)) {
+        tsk_error_errstr2_concat("- hfs_load_attrs");
         return 1;
     }
 
-    // add the runs to the attribute and the attribute to the file. 
+    // add the runs to the attribute and the attribute to the file.
     if (tsk_fs_attr_set_run(fs_file, fs_attr, attr_run, NULL,
             TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT,
             tsk_getu64(fs->endian, fork->logic_sz),
             tsk_getu64(fs->endian, fork->logic_sz),
             (TSK_OFF_T) tsk_getu32(fs->endian,
                 fork->total_blk) * fs->block_size, 0, 0)) {
-        strncat(tsk_errstr2, " - hfs_load_attrs",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- hfs_load_attrs");
         tsk_fs_attr_run_free(attr_run);
         return 1;
     }
@@ -2094,8 +2065,7 @@ hfs_load_attrs(TSK_FS_FILE * fs_file)
     // see if extents file has additional runs
     if (hfs_ext_find_extent_record_attr(hfs,
             (uint32_t) fs_file->meta->addr, fs_attr)) {
-        strncat(tsk_errstr2, " - hfs_load_attrs",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- hfs_load_attrs");
         fs_file->meta->attr_state = TSK_FS_META_ATTR_ERROR;
         return 1;
     }
@@ -2116,7 +2086,7 @@ hfs_load_attrs(TSK_FS_FILE * fs_file)
 * http://developer.apple.com/technotes/tn/tn1150.html
 *
 * @param hfs File system being analyzed
-* @param b Block address 
+* @param b Block address
 * @returns 1 if allocated, 0 if not, -1 on error
 */
 static int8_t
@@ -2125,14 +2095,17 @@ hfs_block_is_alloc(HFS_INFO * hfs, TSK_DADDR_T a_addr)
     TSK_FS_INFO *fs = &(hfs->fs_info);
     TSK_OFF_T b;
     size_t b2;
+    int8_t ret;
+
+    tsk_take_lock(&hfs->lock);
 
     // lazy loading
     if (hfs->blockmap_file == NULL) {
         if ((hfs->blockmap_file =
                 tsk_fs_file_open_meta(fs, NULL,
                     HFS_ALLOCATION_FILE_ID)) == NULL) {
-            strncat(tsk_errstr2, " - Loading blockmap file",
-                TSK_ERRSTR_L - strlen(tsk_errstr2));
+            tsk_release_lock(&hfs->lock);
+            tsk_error_errstr2_concat("- Loading blockmap file");
             return -1;
         }
 
@@ -2141,9 +2114,9 @@ hfs_block_is_alloc(HFS_INFO * hfs, TSK_DADDR_T a_addr)
             tsk_fs_attrlist_get(hfs->blockmap_file->meta->attr,
             TSK_FS_ATTR_TYPE_DEFAULT);
         if (!hfs->blockmap_attr) {
-            strncat(tsk_errstr2,
-                " - Data Attribute not found in Blockmap File",
-                TSK_ERRSTR_L - strlen(tsk_errstr2));
+            tsk_release_lock(&hfs->lock);
+            tsk_error_errstr2_concat(
+                "- Data Attribute not found in blockmap File");
             return -1;
         }
         hfs->blockmap_cache_start = -1;
@@ -2153,8 +2126,9 @@ hfs_block_is_alloc(HFS_INFO * hfs, TSK_DADDR_T a_addr)
     // get the byte offset
     b = (TSK_OFF_T) a_addr / 8;
     if (b > hfs->blockmap_file->meta->size) {
-        tsk_errno = TSK_ERR_FS_CORRUPT;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_release_lock(&hfs->lock);
+        tsk_error_set_errno(TSK_ERR_FS_CORRUPT);
+        tsk_error_set_errstr(
             "hfs_block_is_alloc: block %" PRIuDADDR
             " is too large for bitmap (%" PRIuOFF ")", a_addr,
             hfs->blockmap_file->meta->size);
@@ -2169,7 +2143,8 @@ hfs_block_is_alloc(HFS_INFO * hfs, TSK_DADDR_T a_addr)
             hfs->blockmap_cache,
             sizeof(hfs->blockmap_cache), 0);
         if (cnt < 1) {
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
+            tsk_release_lock(&hfs->lock);
+            tsk_error_set_errstr2(
                 "hfs_block_is_alloc: Error reading block bitmap at offset %"
                 PRIuOFF, b);
             return -1;
@@ -2178,7 +2153,10 @@ hfs_block_is_alloc(HFS_INFO * hfs, TSK_DADDR_T a_addr)
         hfs->blockmap_cache_len = cnt;
     }
     b2 = (size_t) (b - hfs->blockmap_cache_start);
-    return (hfs->blockmap_cache[b2] & (1 << (7 - (a_addr % 8)))) != 0;
+
+    ret = (hfs->blockmap_cache[b2] & (1 << (7 - (a_addr % 8)))) != 0;
+    tsk_release_lock(&hfs->lock);
+    return ret;
 }
 
 
@@ -2213,15 +2191,15 @@ hfs_block_walk(TSK_FS_INFO * fs, TSK_DADDR_T start_blk,
      * Sanity checks.
      */
     if (start_blk < fs->first_block || start_blk > fs->last_block) {
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "%s: invalid start block number: %" PRIuDADDR "", myname,
             start_blk);
         return 1;
     }
     if (end_blk < fs->first_block || end_blk > fs->last_block) {
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "%s: invalid last block number: %" PRIuDADDR "", myname,
             end_blk);
         return 1;
@@ -2364,7 +2342,7 @@ hfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
 
         if (hfs_inode_lookup(fs, fs_file, inum)) {
             // deleted files may not exist in the catalog
-            if (tsk_errno == TSK_ERR_FS_INODE_NUM) {
+            if (tsk_error_get_errno() == TSK_ERR_FS_INODE_NUM) {
                 tsk_error_reset();
                 continue;
             }
@@ -2429,8 +2407,8 @@ print_parent_path(FILE * hFile, TSK_FS_INFO * fs, TSK_INUM_T inum)
         return 0;
 
     if (inum <= HFS_ROOT_INUM) {
-        tsk_errno = TSK_ERR_FS_INODE_NUM;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
+        tsk_error_set_errstr(
             "print_parent_path: out-of-range inode %" PRIuINUM, inum);
         return 1;
     }
@@ -2475,8 +2453,8 @@ static uint8_t
 hfs_fscheck(TSK_FS_INFO * fs, FILE * hFile)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr(
         "fscheck not implemented for HFS yet");
     return 1;
 }
@@ -2685,14 +2663,14 @@ print_addr_act(TSK_FS_FILE * fs_file, TSK_OFF_T a_off, TSK_DADDR_T addr,
 }
 
 /**
- * Print details on a specific file to a file handle. 
+ * Print details on a specific file to a file handle.
  *
  * @param fs File system file is located in
  * @param hFile File name to print text to
  * @param inum Address of file in file system
  * @param numblock The number of blocks in file to force print (can go beyond file size)
  * @param sec_skew Clock skew in seconds to also print times in
- * 
+ *
  * @returns 1 on error and 0 on success
  */
 static uint8_t
@@ -2711,8 +2689,7 @@ hfs_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
             inum, numblock);
 
     if ((fs_file = tsk_fs_file_open_meta(fs, NULL, inum)) == NULL) {
-        strncat(tsk_errstr2, " - istat",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- istat");
         return 1;
     }
 
@@ -2817,7 +2794,7 @@ hfs_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
                 entry.cat.std.u_info.flags) & HFS_FINDER_FLAG_IS_ALIAS)
             tsk_fprintf(hFile, "Is alias\n");
 
-        // @@@ The tech note has a table that converts nums to encoding names. 
+        // @@@ The tech note has a table that converts nums to encoding names.
         tsk_fprintf(hFile, "Text encoding:\t%" PRIx32 "\n",
             tsk_getu32(fs->endian, entry.cat.std.text_enc));
 
@@ -2907,7 +2884,9 @@ hfs_close(TSK_FS_INFO * fs)
         hfs->blockmap_attr = NULL;
     }
 
-    free(hfs);
+    tsk_deinit_lock(&hfs->lock);
+
+    tsk_fs_free(fs);
 }
 
 /* hfs_open - open an hfs file system
@@ -2927,12 +2906,12 @@ hfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
     tsk_error_reset();
 
     if (TSK_FS_TYPE_ISHFS(ftype) == 0) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "Invalid FS Type in hfs_open");
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr("Invalid FS Type in hfs_open");
         return NULL;
     }
 
-    if ((hfs = (HFS_INFO *) tsk_malloc(sizeof(HFS_INFO))) == NULL)
+    if ((hfs = (HFS_INFO *) tsk_fs_malloc(sizeof(HFS_INFO))) == NULL)
         return NULL;
 
     fs = &(hfs->fs_info);
@@ -2957,7 +2936,7 @@ hfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
 
     if (hfs_checked_read_random(fs, (char *) hfs->fs, len,
             (TSK_OFF_T) HFS_VH_OFF)) {
-        snprintf(tsk_errstr2, TSK_ERRSTR_L, "hfs_open: superblock");
+        tsk_error_set_errstr2( "hfs_open: superblock");
         fs->tag = 0;
         free(hfs->fs);
         free(hfs);
@@ -2974,15 +2953,15 @@ hfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
         fs->tag = 0;
         free(hfs->fs);
         free(hfs);
-        tsk_errno = TSK_ERR_FS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr(
             "not an HFS+ file system (magic)");
         return NULL;
     }
 
     /*
      * Handle an HFS-wrapped HFS+ image, which is a HFS volume that contains
-     * the HFS+ volume inside of it. 
+     * the HFS+ volume inside of it.
      */
     if (tsk_getu16(fs->endian, hfs->fs->signature) == HFS_VH_SIG_HFS) {
 
@@ -3003,7 +2982,7 @@ hfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
             uint32_t drAlBlkSiz =
                 tsk_getu32(fs->endian, wrapper_sb->drAlBlkSiz);
 
-            // start of embedded FS 
+            // start of embedded FS
             uint16_t startBlock = tsk_getu16(fs->endian,
                 wrapper_sb->drEmbedExtent_startBlock);
 
@@ -3029,14 +3008,16 @@ hfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
                 ((HFS_INFO *) fs_info2)->hfs_wrapper_offset =
                     hfsplus_offset;
 
+            tsk_init_lock(&hfs->lock);
+
             return fs_info2;
         }
         else {
             fs->tag = 0;
             free(hfs->fs);
             free(hfs);
-            tsk_errno = TSK_ERR_FS_MAGIC;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+            tsk_error_set_errstr(
                 "HFS file systems (other than wrappers HFS+/HFSX file systems) are not supported");
             return NULL;
         }
@@ -3110,8 +3091,7 @@ hfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
         tsk_fs_file_close(hfs->catalog_file);
         free(hfs->fs);
         free(hfs);
-        strncat(tsk_errstr2, " - Data Attribute not found in Catalog File",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- Data Attribute not found in Catalog File");
         return NULL;
     }
 
@@ -3122,9 +3102,9 @@ hfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
     if (cnt != sizeof(hfs_btree_header_record)) {
         if (cnt >= 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_READ;
+            tsk_error_set_errno(TSK_ERR_FS_READ);
         }
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
+        tsk_error_set_errstr2(
             "hfs_open: Error reading catalog header");
         fs->tag = 0;
         free(hfs->fs);
@@ -3169,5 +3149,7 @@ hfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
     fs->name_cmp = hfs_name_cmp;
     fs->journ_inum = 0;
 
+    tsk_init_lock(&hfs->lock);
+
     return fs;
 }
diff --git a/tsk3/fs/hfs_dent.c b/tsk3/fs/hfs_dent.c
index 626be875c..33ed3d920 100644
--- a/tsk3/fs/hfs_dent.c
+++ b/tsk3/fs/hfs_dent.c
@@ -8,13 +8,13 @@
 ** 14900 Conference Center Drive
 ** Chantilly, VA 20151
 **
-** Copyright (c) 2009 Brian Carrier.  All rights reserved.
+** Copyright (c) 2009-2011 Brian Carrier.  All rights reserved.
 **
 ** Judson Powers [jpowers@atc-nycorp.com]
 ** Copyright (c) 2008 ATC-NY.  All rights reserved.
 ** This file contains data developed with support from the National
 ** Institute of Justice, Office of Justice Programs, U.S. Department of Justice.
-** 
+**
 ** Wyatt Banks [wbanks@crucialsecurity.com]
 ** Copyright (c) 2005 Crucial Security Inc.  All rights reserved.
 **
@@ -128,8 +128,8 @@ hfs_uni2ascii(TSK_FS_INFO * fs, uint8_t * uni, int ulen, char *asc,
 
     free(uniclean);
     if (r != TSKconversionOK) {
-        tsk_errno = TSK_ERR_FS_UNICODE;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_UNICODE);
+        tsk_error_set_errstr(
             "hfs_uni2ascii: unicode conversion failed (%d)", (int) r);
         return 1;
     }
@@ -211,8 +211,8 @@ hfs_dir_open_meta_cb(HFS_INFO * hfs, int8_t level_type,
         // @@@ NEED TO REPLACE THIS SOMEHOW, but need to figure out the max length
         /*
            if (rec_off2 > nodesize) {
-           tsk_errno = TSK_ERR_FS_GENFS;
-           snprintf(tsk_errstr, TSK_ERRSTR_L,
+           tsk_error_set_errno(TSK_ERR_FS_GENFS);
+           tsk_error_set_errstr(
            "hfs_dir_open_meta: offset of record+keylen %d in leaf node %d too large (%zu vs %"
            PRIu16 ")", rec, cur_node, rec_off2, nodesize);
            tsk_fs_name_free(fs_name);
@@ -224,8 +224,8 @@ hfs_dir_open_meta_cb(HFS_INFO * hfs, int8_t level_type,
 
         // Catalog entry is for a file
         if (rec_type == HFS_FILE_THREAD) {
-            tsk_errno = TSK_ERR_FS_GENFS;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_GENFS);
+            tsk_error_set_errstr(
                 "hfs_dir_open_meta: Entry" " is a file, not a folder");
             return HFS_BTREE_CB_ERR;
         }
@@ -273,9 +273,9 @@ hfs_dir_open_meta_cb(HFS_INFO * hfs, int8_t level_type,
             }
         }
         else {
-            tsk_errno = TSK_ERR_FS_GENFS;
+            tsk_error_set_errno(TSK_ERR_FS_GENFS);
             // @@@ MAY NEED TO IMPROVE BELOW MESSAGE
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errstr(
                 "hfs_dir_open_meta: Unknown record type %d in leaf node",
                 rec_type);
             return HFS_BTREE_CB_ERR;
@@ -291,15 +291,15 @@ hfs_dir_open_meta_cb(HFS_INFO * hfs, int8_t level_type,
 /** \internal
 * Process a directory and load up FS_DIR with the entries. If a pointer to
 * an already allocated FS_DIR struture is given, it will be cleared.  If no existing
-* FS_DIR structure is passed (i.e. NULL), then a new one will be created. If the return 
-* value is error or corruption, then the FS_DIR structure could  
-* have entries (depending on when the error occured). 
+* FS_DIR structure is passed (i.e. NULL), then a new one will be created. If the return
+* value is error or corruption, then the FS_DIR structure could
+* have entries (depending on when the error occured).
 *
 * @param a_fs File system to analyze
 * @param a_fs_dir Pointer to FS_DIR pointer. Can contain an already allocated
-* structure or a new structure. 
+* structure or a new structure.
 * @param a_addr Address of directory to process.
-* @returns error, corruption, ok etc. 
+* @returns error, corruption, ok etc.
 */
 TSK_RETVAL_ENUM
 hfs_dir_open_meta(TSK_FS_INFO * fs, TSK_FS_DIR ** a_fs_dir,
@@ -321,15 +321,15 @@ hfs_dir_open_meta(TSK_FS_INFO * fs, TSK_FS_DIR ** a_fs_dir,
 
     if (a_addr < fs->first_inum || a_addr > fs->last_inum) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "hfs_dir_open_meta: Invalid inode value: %" PRIuINUM, a_addr);
         return TSK_ERR;
     }
     else if (a_fs_dir == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "hfs_dir_open_meta: NULL fs_dir argument given");
         return TSK_ERR;
     }
@@ -355,8 +355,7 @@ hfs_dir_open_meta(TSK_FS_INFO * fs, TSK_FS_DIR ** a_fs_dir,
 
     if ((fs_dir->fs_file =
             tsk_fs_file_open_meta(fs, NULL, a_addr)) == NULL) {
-        strncat(tsk_errstr2, " - hfs_dir_open_meta",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat(" - hfs_dir_open_meta");
         tsk_fs_name_free(fs_name);
         return TSK_ERR;
     }
@@ -404,7 +403,7 @@ hfs_dir_open_meta(TSK_FS_INFO * fs, TSK_FS_DIR ** a_fs_dir,
                    case 7:
                    strncpy(fs_name->name, HFS_BOGUS_EXTENT_FILE_NAME, fs_name->name_size);
                    fs_name->meta_addr = HFS_BOGUS_EXTENT_FILE_ID;
-                   break;                    
+                   break;
                  */
             }
             fs_name->type = TSK_FS_NAME_TYPE_REG;
diff --git a/tsk3/fs/icat_lib.c b/tsk3/fs/icat_lib.c
index 2372b4ab0..e38578b04 100644
--- a/tsk3/fs/icat_lib.c
+++ b/tsk3/fs/icat_lib.c
@@ -3,7 +3,7 @@
 ** The Sleuth Kit 
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved 
 **
 ** TASK
@@ -42,8 +42,8 @@ icat_action(TSK_FS_FILE * fs_file, TSK_OFF_T a_off, TSK_DADDR_T addr,
 
     if (fwrite(buf, size, 1, stdout) != 1) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WRITE;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WRITE);
+        tsk_error_set_errstr(
             "icat_action: error writing to stdout: %s", strerror(errno));
         return TSK_WALK_ERROR;
     }
@@ -62,8 +62,8 @@ tsk_fs_icat(TSK_FS_INFO * fs, TSK_INUM_T inum,
 #ifdef TSK_WIN32
     if (-1 == _setmode(_fileno(stdout), _O_BINARY)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WRITE;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WRITE);
+        tsk_error_set_errstr(
             "icat_lib: error setting stdout to binary: %s",
             strerror(errno));
         return 1;
diff --git a/tsk3/fs/ifind_lib.c b/tsk3/fs/ifind_lib.c
index fe3a5bfe0..21b65ea09 100644
--- a/tsk3/fs/ifind_lib.c
+++ b/tsk3/fs/ifind_lib.c
@@ -5,7 +5,7 @@
 ** Given an image  and block number, identify which inode it is used by
 ** 
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2010 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
 **
 ** TASK
@@ -140,7 +140,7 @@ tsk_fs_ifind_par(TSK_FS_INFO * fs, TSK_FS_IFIND_FLAG_ENUM lclflags,
 
     /* Walk unallocated MFT entries */
     if (fs->inode_walk(fs, fs->first_inum, fs->last_inum,
-            TSK_FS_META_FLAG_UNALLOC, ifind_par_act, &data)) {
+            TSK_FS_META_FLAG_UNALLOC,ifind_par_act, &data)) {
         return 1;
     }
 
@@ -432,8 +432,8 @@ tsk_fs_ifind_path(TSK_FS_INFO * fs, TSK_TCHAR * tpath, TSK_INUM_T * result)
             (UTF8 *) ((uintptr_t) ptr8 + clen), TSKlenientConversion);
         if (retval != TSKconversionOK) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_UNICODE;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_UNICODE);
+            tsk_error_set_errstr(
                 "tsk_fs_ifind_path: Error converting path to UTF-8: %d",
                 retval);
             free(cpath);
diff --git a/tsk3/fs/ils_lib.c b/tsk3/fs/ils_lib.c
index 805207e4f..407b5c4ff 100644
--- a/tsk3/fs/ils_lib.c
+++ b/tsk3/fs/ils_lib.c
@@ -2,7 +2,7 @@
 ** The Sleuth Kit 
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved 
 **
 ** TASK
diff --git a/tsk3/fs/iso9660.c b/tsk3/fs/iso9660.c
index 46b431452..215a067ce 100644
--- a/tsk3/fs/iso9660.c
+++ b/tsk3/fs/iso9660.c
@@ -13,7 +13,7 @@
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
 ** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
-** Copyright (c) 2007-2008 Brian Carrier.  All rights reserved
+** Copyright (c) 2007-2011 Brian Carrier.  All rights reserved
 **
 ** Copyright (c) 1997,1998,1999, International Business Machines
 ** Corporation and others. All Rights Reserved.
@@ -35,7 +35,7 @@
 **    ver 1.0; and
 ** b) the license agreement
 **     i) effectively disclaims on behalf of all Contributors all warranties
-**        and conditions, express and implied, including warranties or 
+**        and conditions, express and implied, including warranties or
 **        conditions of title and non-infringement, and implied warranties
 **        or conditions of merchantability and fitness for a particular
 **        purpose.
@@ -52,14 +52,14 @@
 ** When the Sleuth Kit or other software that incorporates part or all of
 ** the Sleuth Kit is made available in source code form:
 **     a) it must be made available under IBM Public License ver. 1.0; and
-**     b) a copy of the IBM Public License ver. 1.0 must be included with 
+**     b) a copy of the IBM Public License ver. 1.0 must be included with
 **        each copy of the program.
 */
 
 /**
  * \file iso9660.c
- * Contains the internal TSK ISO9660 file system code to handle basic file 
- * system processing for opening file system, processing sectors, and directory entries. 
+ * Contains the internal TSK ISO9660 file system code to handle basic file
+ * system processing for opening file system, processing sectors, and directory entries.
  */
 
 #include "tsk_fs_i.h"
@@ -231,8 +231,8 @@ parse_susp(TSK_FS_INFO * fs, char *buf, int count, FILE * hFile)
             buf += head->len;
         }
 
-        /* 
-         * Rock Ridge Extensions 
+        /*
+         * Rock Ridge Extensions
          */
 
         /* POSIX file attributes */
@@ -253,7 +253,7 @@ parse_susp(TSK_FS_INFO * fs, char *buf, int count, FILE * hFile)
             buf += head->len;
         }
 
-        // RR - device information 
+        // RR - device information
         else if ((head->sig[0] == 'P') && (head->sig[1] == 'N')) {
             iso9660_rr_pn_entry *rr_pn = (iso9660_rr_pn_entry *) buf;
             if (hFile) {
@@ -346,10 +346,10 @@ parse_susp(TSK_FS_INFO * fs, char *buf, int count, FILE * hFile)
 // The following functions are responsible for loading all of the file metadata into memory.
 // The process is that the Path table is processed first.  It contains an entry for each
 // directory.  That info is then used to locate the directory contents and those contents
-// are then processed. 
+// are then processed.
 //
 // Files do not have a corresponding metadata entry, so we assign them based
-// on the order that they are loaded. 
+// on the order that they are loaded.
 ///////////////////////////////////////////////////////////////////////////
 
 
@@ -361,9 +361,9 @@ parse_susp(TSK_FS_INFO * fs, char *buf, int count, FILE * hFile)
 /** \internal
  * Process the contents of a directory and load the
  * information about files in that directory into ISO_INFO.  This is called
- * by the methods that process the path table (which contains pointers to the 
+ * by the methods that process the path table (which contains pointers to the
  * various directories).  The results in ISO_INFO are used to identify the
- * inode address of files found from dent_walk and for file lookups. 
+ * inode address of files found from dent_walk and for file lookups.
  *
  * Type: ISO9660_TYPE_PVD for primary volume descriptor, ISO9660_TYPE_SVD for
  * supplementary volume descriptor (do Joliet utf-8 conversion).
@@ -401,13 +401,13 @@ iso9660_load_inodes_dir(TSK_FS_INFO * fs, TSK_OFF_T a_offs, int count,
         if (cnt1 != ISO9660_SSIZE_B) {
             if (cnt1 >= 0) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_READ;
+                tsk_error_set_errno(TSK_ERR_FS_READ);
             }
-            snprintf(tsk_errstr2, TSK_ERRSTR_L, "iso_get_dentries");
+            tsk_error_set_errstr2( "iso_get_dentries");
             return -1;
         }
 
-        // @@@@ We  need to add more checks when reading from buf to make sure b_off is still in the buffer 
+        // @@@@ We  need to add more checks when reading from buf to make sure b_off is still in the buffer
         /* process the directory entries */
         for (b_offs = 0; b_offs < ISO9660_SSIZE_B;) {
             iso9660_inode_node *in_node;
@@ -420,7 +420,7 @@ iso9660_load_inodes_dir(TSK_FS_INFO * fs, TSK_OFF_T a_offs, int count,
                 continue;
             }
 
-            /* when processing the other volume descriptor directories, we ignore the 
+            /* when processing the other volume descriptor directories, we ignore the
              * directories because we have no way of detecting if it is a duplicate of
              * a directory from the other volume descriptor (they use different blocks).
              * We will see the contents of this directory from the path table anyway. */
@@ -450,8 +450,8 @@ iso9660_load_inodes_dir(TSK_FS_INFO * fs, TSK_OFF_T a_offs, int count,
                 /* use the specified name instead of "." */
                 if (strlen(a_fn) > ISO9660_MAXNAMLEN_STD) {
                     tsk_error_reset();
-                    tsk_errno = TSK_ERR_FS_ARG;
-                    snprintf(tsk_errstr, TSK_ERRSTR_L,
+                    tsk_error_set_errno(TSK_ERR_FS_ARG);
+                    tsk_error_set_errstr(
                         "iso9660_load_inodes_dir: Name argument specified is too long");
                     return -1;
                 }
@@ -459,7 +459,7 @@ iso9660_load_inodes_dir(TSK_FS_INFO * fs, TSK_OFF_T a_offs, int count,
                     ISO9660_MAXNAMLEN_STD + 1);
 
                 /* for all directories except the root, we skip processing the "." and ".." entries because
-                 * they duplicate the other entires and the dent_walk code will rely on the offset 
+                 * they duplicate the other entires and the dent_walk code will rely on the offset
                  * for the entry in the parent directory. */
                 if (count != 0) {
                     free(in_node);
@@ -523,8 +523,8 @@ iso9660_load_inodes_dir(TSK_FS_INFO * fs, TSK_OFF_T a_offs, int count,
                 }
                 else {
                     tsk_error_reset();
-                    tsk_errno = TSK_ERR_FS_ARG;
-                    snprintf(tsk_errstr, TSK_ERRSTR_L,
+                    tsk_error_set_errno(TSK_ERR_FS_ARG);
+                    tsk_error_set_errstr(
                         "Invalid ctype in iso9660_load_inodes_dir");
                     return -1;
                 }
@@ -596,7 +596,7 @@ iso9660_load_inodes_dir(TSK_FS_INFO * fs, TSK_OFF_T a_offs, int count,
 
                 for (tmp = iso->in_list; tmp; tmp = tmp->next) {
                     /* When processing the "first" volume descriptor, all entries get added to the list.
-                     * for the later ones, we skip duplicate ones that overlap with entries from a 
+                     * for the later ones, we skip duplicate ones that overlap with entries from a
                      * previous volume descriptor. */
                     if ((in_node->offset == tmp->offset)
                         && (in_node->size == tmp->size)
@@ -655,7 +655,7 @@ iso9660_load_inodes_dir(TSK_FS_INFO * fs, TSK_OFF_T a_offs, int count,
  * Process the path table for a joliet secondary volume descriptor
  * and load all of the files pointed to it.
  * The path table contains an entry for each directory.  This code
- * then locates each of the diretories and proceses the contents. 
+ * then locates each of the diretories and proceses the contents.
  *
  * @param fs File system to process
  * @param svd Pointer to the secondary volume descriptor
@@ -692,9 +692,9 @@ iso9660_load_inodes_pt_joliet(TSK_FS_INFO * fs, iso9660_svd * svd,
         if (cnt != sizeof(dir)) {
             if (cnt >= 0) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_READ;
+                tsk_error_set_errno(TSK_ERR_FS_READ);
             }
-            snprintf(tsk_errstr2, TSK_ERRSTR_L, "iso9660_load_inodes_pt");
+            tsk_error_set_errstr2( "iso9660_load_inodes_pt");
             return -1;
         }
         pt_len -= cnt;
@@ -710,9 +710,9 @@ iso9660_load_inodes_pt_joliet(TSK_FS_INFO * fs, iso9660_svd * svd,
         if (cnt != dir.len_di) {
             if (cnt >= 0) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_READ;
+                tsk_error_set_errno(TSK_ERR_FS_READ);
             }
-            snprintf(tsk_errstr2, TSK_ERRSTR_L, "iso_find_inodes");
+            tsk_error_set_errstr2( "iso_find_inodes");
             return -1;
         }
         pt_len -= cnt;
@@ -767,8 +767,8 @@ iso9660_load_inodes_pt_joliet(TSK_FS_INFO * fs, iso9660_svd * svd,
 }
 
 /**
- * Proces the path table and the directories that are listed in it.  
- * The files in each directory will be stored in ISO_INFO. 
+ * Proces the path table and the directories that are listed in it.
+ * The files in each directory will be stored in ISO_INFO.
  *
  * @param iso File system to analyze and store results in
  * @returns -1 on error or count of inodes found.
@@ -832,9 +832,9 @@ iso9660_load_inodes_pt(ISO_INFO * iso)
             if (cnt != sizeof(dir)) {
                 if (cnt >= 0) {
                     tsk_error_reset();
-                    tsk_errno = TSK_ERR_FS_READ;
+                    tsk_error_set_errno(TSK_ERR_FS_READ);
                 }
-                snprintf(tsk_errstr2, TSK_ERRSTR_L, "iso_find_inodes");
+                tsk_error_set_errstr2( "iso_find_inodes");
                 return -1;
             }
             pt_len -= cnt;
@@ -849,9 +849,9 @@ iso9660_load_inodes_pt(ISO_INFO * iso)
             if (cnt != readlen) {
                 if (cnt >= 0) {
                     tsk_error_reset();
-                    tsk_errno = TSK_ERR_FS_READ;
+                    tsk_error_set_errno(TSK_ERR_FS_READ);
                 }
-                snprintf(tsk_errstr2, TSK_ERRSTR_L, "iso_find_inodes");
+                tsk_error_set_errstr2( "iso_find_inodes");
                 return -1;
             }
             fn[cnt] = '\0';
@@ -882,7 +882,7 @@ iso9660_load_inodes_pt(ISO_INFO * iso)
     return count;
 }
 
-/** 
+/**
  * Load the raw "inode" into the cached buffer (iso->dinode)
  *
  * dinode_load (for now) does not check for extended attribute records...
@@ -892,7 +892,7 @@ iso9660_load_inodes_pt(ISO_INFO * iso)
  * @returns 1 if not found and 0 on succuss
  */
 uint8_t
-iso9660_dinode_load(ISO_INFO * iso, TSK_INUM_T inum)
+iso9660_dinode_load(ISO_INFO * iso, TSK_INUM_T inum, iso9660_inode *dinode)
 {
     iso9660_inode_node *n;
 
@@ -901,8 +901,7 @@ iso9660_dinode_load(ISO_INFO * iso, TSK_INUM_T inum)
         n = n->next;
 
     if (n) {
-        memcpy(iso->dinode, &n->inode, sizeof(iso9660_inode));
-        iso->dinum = inum;
+        memcpy(dinode, &n->inode, sizeof(iso9660_inode));
         return 0;
     }
     else {
@@ -941,19 +940,19 @@ isomode2tskmode(uint16_t a_mode)
 }
 
 /**
- * Copies cached disk inode into generic structure. 
+ * Copies cached disk inode into generic structure.
  *
  * @returns 1 on error and 0 on success
  */
 static uint8_t
-iso9660_dinode_copy(ISO_INFO * iso, TSK_FS_META * fs_meta)
+iso9660_dinode_copy(ISO_INFO * iso, TSK_FS_META * fs_meta, TSK_INUM_T inum, iso9660_inode *dinode)
 {
     TSK_FS_INFO *fs = (TSK_FS_INFO *) & iso->fs_info;
     struct tm t;
 
     if (fs_meta == NULL) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "iso9660_dinode_copy: fs_file or meta is NULL");
         return 1;
     }
@@ -971,16 +970,16 @@ iso9660_dinode_copy(ISO_INFO * iso, TSK_FS_META * fs_meta)
         }
     }
 
-    fs_meta->addr = iso->dinum;
-    fs_meta->size = tsk_getu32(fs->endian, iso->dinode->dr.data_len_m);
+    fs_meta->addr =inum;
+    fs_meta->size = tsk_getu32(fs->endian, dinode->dr.data_len_m);
 
     memset(&t, 0, sizeof(struct tm));
-    t.tm_sec = iso->dinode->dr.rec_time.sec;
-    t.tm_min = iso->dinode->dr.rec_time.min;
-    t.tm_hour = iso->dinode->dr.rec_time.hour;
-    t.tm_mday = iso->dinode->dr.rec_time.day;
-    t.tm_mon = iso->dinode->dr.rec_time.month - 1;
-    t.tm_year = iso->dinode->dr.rec_time.year;
+    t.tm_sec = dinode->dr.rec_time.sec;
+    t.tm_min = dinode->dr.rec_time.min;
+    t.tm_hour = dinode->dr.rec_time.hour;
+    t.tm_mday = dinode->dr.rec_time.day;
+    t.tm_mon = dinode->dr.rec_time.month - 1;
+    t.tm_year = dinode->dr.rec_time.year;
     //gmt_hrdiff = iso->dinode->dr.rec_time.gmt_off * 15 / 60;
 
     fs_meta->crtime = mktime(&t);
@@ -988,16 +987,16 @@ iso9660_dinode_copy(ISO_INFO * iso, TSK_FS_META * fs_meta)
     fs_meta->crtime_nano = fs_meta->mtime_nano = fs_meta->atime_nano =
         fs_meta->ctime_nano = 0;
 
-    if (iso->dinode->dr.flags & ISO9660_FLAG_DIR)
+    if (dinode->dr.flags & ISO9660_FLAG_DIR)
         fs_meta->type = TSK_FS_META_TYPE_DIR;
     else
         fs_meta->type = TSK_FS_META_TYPE_REG;
 
-    if (iso->dinode->ea) {
-        fs_meta->uid = tsk_getu32(fs->endian, iso->dinode->ea->uid);
-        fs_meta->gid = tsk_getu32(fs->endian, iso->dinode->ea->gid);
+    if (dinode->ea) {
+        fs_meta->uid = tsk_getu32(fs->endian, dinode->ea->uid);
+        fs_meta->gid = tsk_getu32(fs->endian, dinode->ea->gid);
         fs_meta->mode =
-            isomode2tskmode(tsk_getu16(fs->endian, iso->dinode->ea->mode));
+            isomode2tskmode(tsk_getu16(fs->endian, dinode->ea->mode));
         fs_meta->nlink = 1;
     }
     else {
@@ -1008,17 +1007,39 @@ iso9660_dinode_copy(ISO_INFO * iso, TSK_FS_META * fs_meta)
     }
 
     ((TSK_DADDR_T *) fs_meta->content_ptr)[0] =
-        (TSK_DADDR_T) tsk_getu32(fs->endian, iso->dinode->dr.ext_loc_m);
+        (TSK_DADDR_T) tsk_getu32(fs->endian, dinode->dr.ext_loc_m);
 
-    // mark files that were found from other volume descriptors as unalloc so that they 
-    // come up as orphan files. 
-    if (iso->dinode->is_orphan)
+    // mark files that were found from other volume descriptors as unalloc so that they
+    // come up as orphan files.
+    if (dinode->is_orphan)
         fs_meta->flags = TSK_FS_META_FLAG_UNALLOC | TSK_FS_META_FLAG_USED;
     else
         fs_meta->flags = TSK_FS_META_FLAG_ALLOC | TSK_FS_META_FLAG_USED;
     return 0;
 }
 
+static void
+iso9660_close(TSK_FS_INFO * fs)
+{
+    ISO_INFO *iso = (ISO_INFO *) fs;
+    iso9660_pvd_node *p;
+    iso9660_svd_node *s;
+
+    fs->tag = 0;
+    while (iso->pvd != NULL) {
+        p = iso->pvd;
+        iso->pvd = iso->pvd->next;
+        free(p);
+    }
+
+    while (iso->svd != NULL) {
+        s = iso->svd;
+        iso->svd = iso->svd->next;
+        free(s);
+    }
+
+    tsk_fs_free(fs);
+}
 
 
 static uint8_t
@@ -1026,14 +1047,15 @@ iso9660_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file,
     TSK_INUM_T inum)
 {
     ISO_INFO *iso = (ISO_INFO *) fs;
+    iso9660_inode *dinode;
 
     if (tsk_verbose)
         tsk_fprintf(stderr, "iso9660_inode_lookup: iso: %lu"
             " inum: %" PRIuINUM "\n", (uintptr_t) iso, inum);
 
     if (a_fs_file == NULL) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "iso9660_inode_lookup: fs_file is NULL");
         return 1;
     }
@@ -1049,27 +1071,41 @@ iso9660_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file,
 
     // see if they are looking for the special "orphans" directory
     if (inum == TSK_FS_ORPHANDIR_INUM(fs)) {
-        if (tsk_fs_dir_make_orphan_dir_meta(fs, a_fs_file->meta))
+        if (tsk_fs_dir_make_orphan_dir_meta(fs, a_fs_file->meta)){
             return 1;
-        else
+        }
+        else{
             return 0;
+        }
     }
     else {
+        /* allocate cache buffers */
+        /* dinode */
+        dinode = (iso9660_inode *) tsk_malloc(sizeof(iso9660_inode));
+        if (dinode == NULL) {
+            fs->tag = 0;
+            iso9660_close(fs);
+            return 1;
+        }
+
         // load the inode into the ISO buffer
-        if (iso9660_dinode_load(iso, inum)) {
+        if (iso9660_dinode_load(iso, inum, dinode)) {
+            free(dinode);
             return 1;
         }
 
         // copy into the FS_META structure
-        if (iso9660_dinode_copy(iso, a_fs_file->meta)) {
+        if (iso9660_dinode_copy(iso, a_fs_file->meta, inum, dinode)) {
+            free(dinode);
             return 1;
         }
     }
 
+    free(dinode);
     return 0;
 }
 
-uint8_t
+static uint8_t
 iso9660_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start, TSK_INUM_T last,
     TSK_FS_META_FLAG_ENUM flags, TSK_FS_META_WALK_CB action, void *ptr)
 {
@@ -1078,6 +1114,7 @@ iso9660_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start, TSK_INUM_T last,
     TSK_INUM_T inum, end_inum_tmp;
     TSK_FS_FILE *fs_file;
     int myflags;
+    iso9660_inode *dinode;
 
     // clean up any error messages that are lying around
     tsk_error_reset();
@@ -1096,15 +1133,15 @@ iso9660_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start, TSK_INUM_T last,
      */
     if (start < fs->first_inum || start > fs->last_inum) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "%s: Start inode:  %" PRIuINUM "", myname, start);
         return 1;
     }
     if (last < fs->first_inum || last > fs->last_inum || last < start) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "%s: End inode: %" PRIuINUM "", myname, last);
         return 1;
     }
@@ -1133,12 +1170,10 @@ iso9660_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start, TSK_INUM_T last,
      * in the list of unalloc inodes that are pointed to, then fill
      * in the list
      * */
-    if ((flags & TSK_FS_META_FLAG_ORPHAN)
-        && (fs->list_inum_named == NULL)) {
+    if ((flags & TSK_FS_META_FLAG_ORPHAN)) {
         if (tsk_fs_dir_load_inum_named(fs) != TSK_OK) {
-            strncat(tsk_errstr2,
-                " - iso9660_inode_walk: identifying inodes allocated by file names",
-                TSK_ERRSTR_L);
+            tsk_error_errstr2_concat(
+                "- iso9660_inode_walk: identifying inodes allocated by file names");
             return 1;
         }
     }
@@ -1151,26 +1186,36 @@ iso9660_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start, TSK_INUM_T last,
             tsk_fs_meta_alloc(ISO9660_FILE_CONTENT_LEN)) == NULL)
         return 1;
 
-    // we need to handle fs->last_inum specially because it is for the 
+    // we need to handle fs->last_inum specially because it is for the
     // virtual ORPHANS directory.  Handle it outside of the loop.
     if (last == TSK_FS_ORPHANDIR_INUM(fs))
         end_inum_tmp = last - 1;
     else
         end_inum_tmp = last;
 
+    /* allocate cache buffers */
+    /* dinode */
+    dinode = (iso9660_inode *) tsk_malloc(sizeof(iso9660_inode));
+    if (dinode == NULL) {
+        fs->tag = 0;
+        iso9660_close(fs);
+        return 1;
+    }
     /*
      * Iterate.
      */
     for (inum = start; inum <= end_inum_tmp; inum++) {
         int retval;
-        if (iso9660_dinode_load(iso, inum)) {
+        if (iso9660_dinode_load(iso, inum, dinode)) {
             tsk_fs_file_close(fs_file);
+            free(dinode);
             return 1;
         }
 
-        if (iso9660_dinode_copy(iso, fs_file->meta))
+        if (iso9660_dinode_copy(iso, fs_file->meta, inum, dinode)){
+            free(dinode);
             return 1;
-
+        }
         myflags = fs_file->meta->flags;
 
         if ((flags & myflags) != myflags)
@@ -1181,13 +1226,14 @@ iso9660_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start, TSK_INUM_T last,
          * */
         if ((myflags & TSK_FS_META_FLAG_UNALLOC) &&
             (flags & TSK_FS_META_FLAG_ORPHAN) &&
-            (tsk_list_find(fs->list_inum_named, inum))) {
+            (tsk_fs_dir_find_inum_named(fs, inum))) {
             continue;
         }
 
         retval = action(fs_file, ptr);
         if (retval == TSK_WALK_ERROR) {
             tsk_fs_file_close(fs_file);
+            free(dinode);
             return 1;
         }
         else if (retval == TSK_WALK_STOP) {
@@ -1203,16 +1249,19 @@ iso9660_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start, TSK_INUM_T last,
 
         if (tsk_fs_dir_make_orphan_dir_meta(fs, fs_file->meta)) {
             tsk_fs_file_close(fs_file);
+            free(dinode);
             return 1;
         }
         /* call action */
         retval = action(fs_file, ptr);
         if (retval == TSK_WALK_STOP) {
             tsk_fs_file_close(fs_file);
+            free(dinode);
             return 0;
         }
         else if (retval == TSK_WALK_ERROR) {
             tsk_fs_file_close(fs_file);
+            free(dinode);
             return 1;
         }
     }
@@ -1222,6 +1271,8 @@ iso9660_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start, TSK_INUM_T last,
      * Cleanup.
      */
     tsk_fs_file_close(fs_file);
+    if (dinode != NULL)
+        free((char *)dinode);
     return 0;
 }
 
@@ -1255,7 +1306,7 @@ iso9660_is_block_alloc(TSK_FS_INFO * fs, TSK_DADDR_T blk_num)
 
 
 TSK_FS_BLOCK_FLAG_ENUM
-iso9660_block_getflags(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr)
+static iso9660_block_getflags(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr)
 {
     return (iso9660_is_block_alloc(a_fs, a_addr)) ?
         TSK_FS_BLOCK_FLAG_ALLOC : TSK_FS_BLOCK_FLAG_UNALLOC;
@@ -1266,7 +1317,7 @@ iso9660_block_getflags(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr)
  * ISO9660 has a LOT of very sparse meta, so in this function a block is only
  * checked to see if it is part of an inode's extent
  */
-uint8_t
+static uint8_t
 iso9660_block_walk(TSK_FS_INFO * fs, TSK_DADDR_T start, TSK_DADDR_T last,
     TSK_FS_BLOCK_WALK_FLAG_ENUM flags, TSK_FS_BLOCK_WALK_CB action,
     void *ptr)
@@ -1290,15 +1341,15 @@ iso9660_block_walk(TSK_FS_INFO * fs, TSK_DADDR_T start, TSK_DADDR_T last,
      */
     if (start < fs->first_block || start > fs->last_block) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "%s: Start block: %" PRIuDADDR "", myname, start);
         return 1;
     }
     if (last < fs->first_block || last > fs->last_block) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "%s: End block: %" PRIuDADDR "", myname, last);
         return 1;
     }
@@ -1339,7 +1390,7 @@ iso9660_block_walk(TSK_FS_INFO * fs, TSK_DADDR_T start, TSK_DADDR_T last,
             continue;
 
         if (tsk_fs_block_get(fs, fs_block, addr) == NULL) {
-            snprintf(tsk_errstr2, TSK_ERRSTR_L, "iso_block_walk");
+            tsk_error_set_errstr2( "iso_block_walk");
             tsk_fs_block_free(fs_block);
             return 1;
         }
@@ -1368,14 +1419,15 @@ iso9660_make_data_run(TSK_FS_FILE * a_fs_file)
     TSK_FS_INFO *fs = NULL;
     TSK_FS_ATTR *fs_attr = NULL;
     TSK_FS_ATTR_RUN *data_run = NULL;
+    iso9660_inode *dinode;
 
     // clean up any error messages that are lying around
     tsk_error_reset();
 
     if ((a_fs_file == NULL) || (a_fs_file->meta == NULL)
         || (a_fs_file->fs_info == NULL)) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "iso9660_make_data_run: fs_file or meta is NULL");
         return 1;
     }
@@ -1398,18 +1450,29 @@ iso9660_make_data_run(TSK_FS_FILE * a_fs_file)
         a_fs_file->meta->attr = tsk_fs_attrlist_alloc();
     }
 
+    /* allocate cache buffers */
+    /* dinode */
+    if ((dinode = (iso9660_inode *) tsk_malloc(sizeof(iso9660_inode))) == NULL){
+        fs->tag = 0;
+        iso9660_close(fs);
+        return 1;
+    }
+
     // copy the raw data
-    if (iso9660_dinode_load(iso, a_fs_file->meta->addr)) {
-        snprintf(tsk_errstr2, TSK_ERRSTR_L, "iso9660_make_data_run");
+    if (iso9660_dinode_load(iso, a_fs_file->meta->addr, dinode)) {
+        tsk_error_set_errstr2( "iso9660_make_data_run");
         a_fs_file->meta->attr_state = TSK_FS_META_ATTR_ERROR;
+        free(dinode);
         return 1;
     }
-    memcpy(&dd, &iso->dinode->dr, sizeof(iso9660_dentry));
+    memcpy(&dd, &dinode->dr, sizeof(iso9660_dentry));
+    free(dinode);
+    dinode = NULL;
 
     if (dd.gap_sz) {
         a_fs_file->meta->attr_state = TSK_FS_META_ATTR_ERROR;
-        tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+        tsk_error_set_errstr(
             "file %" PRIuINUM " has an interleave gap -- not supported",
             a_fs_file->meta->addr);
         return 1;
@@ -1418,14 +1481,14 @@ iso9660_make_data_run(TSK_FS_FILE * a_fs_file)
     if ((fs_attr =
             tsk_fs_attrlist_getnew(a_fs_file->meta->attr,
                 TSK_FS_ATTR_NONRES)) == NULL) {
-        return 1;
+       return 1;
     }
 
     // make a non-resident run
     data_run = tsk_fs_attr_run_alloc();
-    if (data_run == NULL)
+    if (data_run == NULL){
         return -1;
-
+    }
     data_run->addr = ((TSK_DADDR_T *) a_fs_file->meta->content_ptr)[0];
     data_run->len =
         (a_fs_file->meta->size + fs->block_size - 1) / fs->block_size;
@@ -1454,18 +1517,18 @@ static uint8_t
 iso9660_fscheck(TSK_FS_INFO * fs, FILE * hFile)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr(
         "fscheck not implemented for iso9660 yet");
     return 1;
 }
 
 /**
- * Print details about the file system to a file handle. 
+ * Print details about the file system to a file handle.
  *
  * @param fs File system to print details on
  * @param hFile File handle to print text to
- * 
+ *
  * @returns 1 on error and 0 on success
  */
 static uint8_t
@@ -1706,44 +1769,40 @@ iso9660_fsstat(TSK_FS_INFO * fs, FILE * hFile)
 
 
 /**
- * Make a unix-style permissions string based the flags in dentry
- * and the cached inode in fs
+ * Make a unix-style permissions string based the flags in dentry and
+ * the cached inode in fs, storing results in perm.  Caller must
+ * ensure perm can hold 10 chars plus one null char.
  */
-char *
-make_unix_perm(TSK_FS_INFO * fs, iso9660_dentry * dd)
+static char*
+make_unix_perm(TSK_FS_INFO * fs, iso9660_dentry * dd, iso9660_inode *dinode, char* perm)
 {
-    // @@@ we should change this design
-    static char perm[11];
-    ISO_INFO *iso = (ISO_INFO *) fs;
-
     if (tsk_verbose)
         tsk_fprintf(stderr, "make_unix_perm: fs: %lu"
             " dd: %lu\n", (uintptr_t) fs, (uintptr_t) dd);
 
+    memset(perm, '-', 10);
     perm[10] = '\0';
 
-    memset(perm, '-', 11);
-
     if (dd->flags & ISO9660_FLAG_DIR)
         perm[0] = 'd';
 
-    if (iso->dinode->ea) {
-        if (tsk_getu16(fs->endian, iso->dinode->ea->mode) & ISO9660_BIT_UR)
+    if (dinode->ea) {
+        if (tsk_getu16(fs->endian, dinode->ea->mode) & ISO9660_BIT_UR)
             perm[1] = 'r';
 
-        if (tsk_getu16(fs->endian, iso->dinode->ea->mode) & ISO9660_BIT_UX)
+        if (tsk_getu16(fs->endian, dinode->ea->mode) & ISO9660_BIT_UX)
             perm[3] = 'x';
 
-        if (tsk_getu16(fs->endian, iso->dinode->ea->mode) & ISO9660_BIT_GR)
+        if (tsk_getu16(fs->endian, dinode->ea->mode) & ISO9660_BIT_GR)
             perm[4] = 'r';
 
-        if (tsk_getu16(fs->endian, iso->dinode->ea->mode) & ISO9660_BIT_GX)
+        if (tsk_getu16(fs->endian, dinode->ea->mode) & ISO9660_BIT_GX)
             perm[6] = 'x';
 
-        if (tsk_getu16(fs->endian, iso->dinode->ea->mode) & ISO9660_BIT_AR)
+        if (tsk_getu16(fs->endian, dinode->ea->mode) & ISO9660_BIT_AR)
             perm[7] = 'r';
 
-        if (tsk_getu16(fs->endian, iso->dinode->ea->mode) & ISO9660_BIT_AX)
+        if (tsk_getu16(fs->endian, dinode->ea->mode) & ISO9660_BIT_AX)
             perm[9] = 'x';
     }
     else {
@@ -1837,14 +1896,14 @@ iso9660_print_rockridge(FILE * hFile, rockridge_ext * rr)
 #endif
 
 /**
- * Print details on a specific file to a file handle. 
+ * Print details on a specific file to a file handle.
  *
  * @param fs File system file is located in
  * @param hFile File handle to print text to
  * @param inum Address of file in file system
  * @param numblock The number of blocks in file to force print (can go beyond file size)
  * @param sec_skew Clock skew in seconds to also print times in
- * 
+ *
  * @returns 1 on error and 0 on success
  */
 static uint8_t
@@ -1854,6 +1913,7 @@ iso9660_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
     ISO_INFO *iso = (ISO_INFO *) fs;
     TSK_FS_FILE *fs_file;
     iso9660_dentry dd;
+    iso9660_inode *dinode;
 
     // clean up any error messages that are lying around
     tsk_error_reset();
@@ -1863,12 +1923,22 @@ iso9660_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
 
     tsk_fprintf(hFile, "Entry: %" PRIuINUM "\n", inum);
 
-    if (iso9660_dinode_load(iso, inum)) {
-        snprintf(tsk_errstr2, TSK_ERRSTR_L, "iso9660_istat");
+    /* allocate cache buffers */
+    /* dinode */
+    dinode = (iso9660_inode *) tsk_malloc(sizeof(iso9660_inode));
+    if (dinode == NULL) {
+        fs->tag = 0;
+        iso9660_close(fs);
+        return 1;
+    }
+
+    if (iso9660_dinode_load(iso, inum, dinode)) {
+        tsk_error_set_errstr2( "iso9660_istat");
         tsk_fs_file_close(fs_file);
+        free(dinode);
         return 1;
     }
-    memcpy(&dd, &iso->dinode->dr, sizeof(iso9660_dentry));
+    memcpy(&dd, &dinode->dr, sizeof(iso9660_dentry));
 
     tsk_fprintf(hFile, "Type: ");
     if (dd.flags & ISO9660_FLAG_DIR)
@@ -1908,27 +1978,28 @@ iso9660_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
         tsk_fprintf(hFile, "Non-final multi-extent entry");
     putchar('\n');
 
-    tsk_fprintf(hFile, "Name: %s\n", iso->dinode->fn);
+    tsk_fprintf(hFile, "Name: %s\n", dinode->fn);
     tsk_fprintf(hFile, "Size: %" PRIu32 "\n", tsk_getu32(fs->endian,
-            iso->dinode->dr.data_len_m));
+            dinode->dr.data_len_m));
 
-    if (iso->dinode->ea) {
+    if (dinode->ea) {
+        char perm_buf[11];
         tsk_fprintf(hFile, "\nEXTENDED ATTRIBUTE INFO\n");
         tsk_fprintf(hFile, "Owner-ID: %" PRIu32 "\n",
-            tsk_getu32(fs->endian, iso->dinode->ea->uid));
+            tsk_getu32(fs->endian, dinode->ea->uid));
         tsk_fprintf(hFile, "Group-ID: %" PRIu32 "\n",
-            tsk_getu32(fs->endian, iso->dinode->ea->gid));
-        tsk_fprintf(hFile, "Mode: %s\n", make_unix_perm(fs, &dd));
+            tsk_getu32(fs->endian, dinode->ea->gid));
+        tsk_fprintf(hFile, "Mode: %s\n", make_unix_perm(fs, &dd, dinode, perm_buf));
     }
-    else if (iso->dinode->susp_off) {
-        char *buf2 = (char *) tsk_malloc((size_t) iso->dinode->susp_len);
+    else if (dinode->susp_off) {
+        char *buf2 = (char *) tsk_malloc((size_t) dinode->susp_len);
         if (buf2 != NULL) {
             ssize_t cnt;
             fprintf(hFile, "\nRock Ridge Extension Data\n");
             cnt =
-                tsk_fs_read(fs, iso->dinode->susp_off, buf2,
-                (size_t) iso->dinode->susp_len);
-            if (cnt == iso->dinode->susp_len) {
+                tsk_fs_read(fs, dinode->susp_off, buf2,
+                (size_t) dinode->susp_len);
+            if (cnt == dinode->susp_len) {
                 parse_susp(fs, buf2, (int) cnt, hFile);
             }
             else {
@@ -1953,9 +2024,10 @@ iso9660_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
     //    iso9660_print_rockridge(hFile, iso->dinode->rr);
     //}
     else {
+        char perm_buf[11];
         tsk_fprintf(hFile, "Owner-ID: 0\n");
         tsk_fprintf(hFile, "Group-ID: 0\n");
-        tsk_fprintf(hFile, "Mode: %s\n", make_unix_perm(fs, &dd));
+        tsk_fprintf(hFile, "Mode: %s\n", make_unix_perm(fs, &dd, dinode, perm_buf));
     }
 
     if (sec_skew != 0) {
@@ -1986,7 +2058,7 @@ iso9660_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
     tsk_fprintf(hFile, "\nSectors:\n");
     /* since blocks are all contiguous, print them here to simplify file_walk */
     {
-        int block = tsk_getu32(fs->endian, iso->dinode->dr.ext_loc_m);
+        int block = tsk_getu32(fs->endian, dinode->dr.ext_loc_m);
         TSK_OFF_T size = fs_file->meta->size;
         int rowcount = 0;
 
@@ -2003,38 +2075,40 @@ iso9660_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
     }
 
     tsk_fs_file_close(fs_file);
+    if(dinode != NULL)
+        free((char *)dinode);
     return 0;
 }
 
 
 
 
-uint8_t
+static uint8_t
 iso9660_jopen(TSK_FS_INFO * fs, TSK_INUM_T inum)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-    snprintf(tsk_errstr, TSK_ERRSTR_L, "ISO9660 does not have a journal");
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr("ISO9660 does not have a journal");
     return 1;
 }
 
-uint8_t
+static uint8_t
 iso9660_jentry_walk(TSK_FS_INFO * fs, int flags,
     TSK_FS_JENTRY_WALK_CB action, void *ptr)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-    snprintf(tsk_errstr, TSK_ERRSTR_L, "ISO9660 does not have a journal");
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr("ISO9660 does not have a journal");
     return 1;
 }
 
-uint8_t
+static uint8_t
 iso9660_jblk_walk(TSK_FS_INFO * fs, TSK_DADDR_T start, TSK_DADDR_T end,
     int flags, TSK_FS_JBLK_WALK_CB action, void *ptr)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-    snprintf(tsk_errstr, TSK_ERRSTR_L, "ISO9660 does not have a journal");
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr("ISO9660 does not have a journal");
     return 1;
 }
 
@@ -2045,36 +2119,6 @@ iso9660_get_default_attr_type(const TSK_FS_FILE * a_file)
     return TSK_FS_ATTR_TYPE_DEFAULT;
 }
 
-static void
-iso9660_close(TSK_FS_INFO * fs)
-{
-    ISO_INFO *iso = (ISO_INFO *) fs;
-    iso9660_pvd_node *p;
-    iso9660_svd_node *s;
-
-    fs->tag = 0;
-    while (iso->pvd != NULL) {
-        p = iso->pvd;
-        iso->pvd = iso->pvd->next;
-        free(p);
-    }
-
-    while (iso->svd != NULL) {
-        s = iso->svd;
-        iso->svd = iso->svd->next;
-        free(s);
-    }
-
-    free((char *) iso->dinode);
-
-    if (fs->list_inum_named) {
-        tsk_list_free(fs->list_inum_named);
-        fs->list_inum_named = NULL;
-    }
-
-    free(fs);
-}
-
 /** Load the volume descriptors into save the raw data structures in
  * the file system state structure (fs).  Also determines the block size.
  *
@@ -2130,9 +2174,9 @@ load_vol_desc(TSK_FS_INFO * fs)
         if (cnt != sizeof(iso9660_gvd)) {
             if (cnt >= 0) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_READ;
+                tsk_error_set_errno(TSK_ERR_FS_READ);
             }
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
+            tsk_error_set_errstr2(
                 "iso_load_vol_desc: Error reading");
             free(vd);
             return -1;
@@ -2250,9 +2294,9 @@ load_vol_desc(TSK_FS_INFO * fs)
             if (cnt != sizeof(iso_bootrec)) {
                 if (cnt >= 0) {
                     tsk_error_reset();
-                    tsk_errno = TSK_ERR_FS_READ;
+                    tsk_error_set_errno(TSK_ERR_FS_READ);
                 }
-                snprintf(tsk_errstr2, TSK_ERRSTR_L,
+                tsk_error_set_errstr2(
                     "iso_load_vol_desc: Error reading");
                 return -1;
             }
@@ -2293,8 +2337,8 @@ load_vol_desc(TSK_FS_INFO * fs)
 
     if ((iso->pvd == NULL) && (iso->svd == NULL)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr(
             "load_vol_desc: primary and secondary volume descriptors null");
         return -1;
     }
@@ -2319,8 +2363,8 @@ iso9660_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
 
     if (TSK_FS_TYPE_ISISO9660(ftype) == 0) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "Invalid FS type in iso9660_open");
         return NULL;
     }
@@ -2331,7 +2375,7 @@ iso9660_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
             ftype, test);
     }
 
-    if ((iso = (ISO_INFO *) tsk_malloc(sizeof(ISO_INFO))) == NULL) {
+    if ((iso = (ISO_INFO *) tsk_fs_malloc(sizeof(ISO_INFO))) == NULL) {
         return NULL;
     }
     fs = &(iso->fs_info);
@@ -2370,8 +2414,8 @@ iso9660_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
             return NULL;
         else {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_MAGIC;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+            tsk_error_set_errstr(
                 "Invalid FS type in iso9660_open");
             return NULL;
         }
@@ -2440,18 +2484,5 @@ iso9660_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
     fs->jentry_walk = iso9660_jentry_walk;
     fs->jopen = iso9660_jopen;
 
-
-    /* allocate cache buffers */
-    /* dinode */
-    iso->dinode = (iso9660_inode *) tsk_malloc(sizeof(iso9660_inode));
-    if (iso->dinode == NULL) {
-        fs->tag = 0;
-        iso9660_close(fs);
-        return NULL;
-    }
-    iso->dinum = -1;
-
-    fs->list_inum_named = NULL;
-
     return fs;
 }
diff --git a/tsk3/fs/iso9660_dent.c b/tsk3/fs/iso9660_dent.c
index f5121a095..24fb00282 100644
--- a/tsk3/fs/iso9660_dent.c
+++ b/tsk3/fs/iso9660_dent.c
@@ -12,7 +12,7 @@
 ** 14900 Conference Center Drive
 ** Chantilly, VA 20151
 **
-** Copyright (c) 2007-2008 Brian Carrier.  All rights reserved
+** Copyright (c) 2007-2011 Brian Carrier.  All rights reserved
 **
 ** Wyatt Banks [wbanks@crucialsecurity.com]
 ** Copyright (c) 2005 Crucial Security Inc.  All rights reserved.
@@ -65,7 +65,7 @@
 
 /**
  * \file iso9660_dent.c
- * Contains the internal TSK ISO9660 file system code to handle the parsing of 
+ * Contains the internal TSK ISO9660 file system code to handle the parsing of
  * file names and directory structures.
  */
 
@@ -145,9 +145,9 @@ iso9660_proc_dir(TSK_FS_INFO * a_fs, TSK_FS_DIR * a_fs_dir, char *buf,
         if ((dd->entry_len) && (buf_idx + dd->entry_len < a_length)) {
 
             /* We need to find the data in the pre-processed list because that
-             * contains the meta data address that TSK assigned to this file.  
+             * contains the meta data address that TSK assigned to this file.
              * We find the entry by looking for one that was stored at the same
-             * byte offset that we now are.  We used to use the extent location, but 
+             * byte offset that we now are.  We used to use the extent location, but
              * we found an image
              * that had a file with 0 bytes with the same starting block as another
              * file. */
@@ -177,7 +177,7 @@ iso9660_proc_dir(TSK_FS_INFO * a_fs, TSK_FS_DIR * a_fs_dir, char *buf,
             buf_idx += dd->entry_len;
         }
         /* If the length was not defined, we are probably in a hole in the
-         * directory.  The contents are  block aligned. So, we 
+         * directory.  The contents are  block aligned. So, we
          * scan ahead until we get either a non-zero entry or the block boundary */
         else {
             buf_idx++;
@@ -206,15 +206,15 @@ iso9660_proc_dir(TSK_FS_INFO * a_fs, TSK_FS_DIR * a_fs_dir, char *buf,
 /** \internal
  * Process a directory and load up FS_DIR with the entries. If a pointer to
  * an already allocated FS_DIR struture is given, it will be cleared.  If no existing
- * FS_DIR structure is passed (i.e. NULL), then a new one will be created. If the return 
- * value is error or corruption, then the FS_DIR structure could  
- * have entries (depending on when the error occured). 
+ * FS_DIR structure is passed (i.e. NULL), then a new one will be created. If the return
+ * value is error or corruption, then the FS_DIR structure could
+ * have entries (depending on when the error occured).
  *
  * @param a_fs File system to analyze
  * @param a_fs_dir Pointer to FS_DIR pointer. Can contain an already allocated
- * structure or a new structure. 
+ * structure or a new structure.
  * @param a_addr Address of directory to process.
- * @returns error, corruption, ok etc. 
+ * @returns error, corruption, ok etc.
  */
 TSK_RETVAL_ENUM
 iso9660_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
@@ -228,16 +228,16 @@ iso9660_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
 
     if (a_addr < a_fs->first_inum || a_addr > a_fs->last_inum) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "iso9660_dir_open_meta: Invalid inode value: %" PRIuINUM,
             a_addr);
         return TSK_ERR;
     }
     else if (a_fs_dir == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "iso9660_dir_open_meta: NULL fs_attr argument given");
         return TSK_ERR;
     }
@@ -265,8 +265,8 @@ iso9660_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
     fs_dir->fs_file = tsk_fs_file_open_meta(a_fs, NULL, a_addr);
     if (fs_dir->fs_file == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_INODE_NUM;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
+        tsk_error_set_errstr(
             "iso9660_dir_open_meta: %" PRIuINUM " is not a valid inode",
             a_addr);
         return TSK_COR;
@@ -281,10 +281,9 @@ iso9660_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
     if (cnt != length) {
         if (cnt >= 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_READ;
-            tsk_errstr[0] = '\0';
+            tsk_error_set_errno(TSK_ERR_FS_READ);
         }
-        snprintf(tsk_errstr2, TSK_ERRSTR_L, "iso9660_dir_open_meta");
+        tsk_error_set_errstr2( "iso9660_dir_open_meta");
         return TSK_ERR;
     }
 
diff --git a/tsk3/fs/nofs_misc.c b/tsk3/fs/nofs_misc.c
index 7a34d0a3a..d48b18a29 100644
--- a/tsk3/fs/nofs_misc.c
+++ b/tsk3/fs/nofs_misc.c
@@ -2,7 +2,7 @@
 ** The Sleuth Kit 
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2004-2005 Brian Carrier.  All rights reserved 
 **
 **
@@ -55,8 +55,8 @@ uint8_t
 tsk_fs_nofs_make_data_run(TSK_FS_FILE * a_fs_file)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr(
         "Illegal analysis method for %s data ",
         (a_fs_file->fs_info) ? tsk_fs_type_toname(a_fs_file->fs_info->
             ftype) : "");
@@ -71,7 +71,7 @@ void
 tsk_fs_nofs_close(TSK_FS_INFO * a_fs)
 {
     a_fs->tag = 0;
-    free(a_fs);
+    tsk_fs_free(a_fs);
 }
 
 /************* BLOCKS *************/
@@ -105,8 +105,8 @@ tsk_fs_nofs_block_walk(TSK_FS_INFO * fs, TSK_DADDR_T a_start_blk,
      */
     if (a_start_blk < fs->first_block || a_start_blk > fs->last_block) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "nofs_block_walk: Start block number: %" PRIuDADDR,
             a_start_blk);
         return 1;
@@ -115,8 +115,8 @@ tsk_fs_nofs_block_walk(TSK_FS_INFO * fs, TSK_DADDR_T a_start_blk,
     if (a_end_blk < fs->first_block || a_end_blk > fs->last_block
         || a_end_blk < a_start_blk) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "nofs_block_walk: Last block number: %" PRIuDADDR, a_end_blk);
         return 1;
     }
@@ -142,7 +142,7 @@ tsk_fs_nofs_block_walk(TSK_FS_INFO * fs, TSK_DADDR_T a_start_blk,
         int retval;
 
         if (tsk_fs_block_get(fs, fs_block, addr) == NULL) {
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
+            tsk_error_set_errstr2(
                 "nofs_block_walk: Block %" PRIuDADDR, addr);
             tsk_fs_block_free(fs_block);
             return 1;
@@ -177,8 +177,8 @@ tsk_fs_nofs_inode_walk(TSK_FS_INFO * a_fs, TSK_INUM_T a_start_inum,
     TSK_FS_META_WALK_CB a_action, void *a_ptr)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr(
         "Illegal analysis method for %s data ",
         tsk_fs_type_toname(a_fs->ftype));
     return 1;
@@ -191,8 +191,8 @@ tsk_fs_nofs_file_add_meta(TSK_FS_INFO * a_fs, TSK_FS_FILE * a_fs_file,
     TSK_INUM_T inum)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr(
         "Illegal analysis method for %s data ",
         tsk_fs_type_toname(a_fs->ftype));
     return 1;
@@ -205,8 +205,8 @@ tsk_fs_nofs_istat(TSK_FS_INFO * a_fs, FILE * hFile, TSK_INUM_T inum,
     TSK_DADDR_T numblock, int32_t sec_skew)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr(
         "Illegal analysis method for %s data ",
         tsk_fs_type_toname(a_fs->ftype));
     return 1;
@@ -223,8 +223,8 @@ tsk_fs_nofs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
     TSK_INUM_T a_addr)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr(
         "Illegal analysis method for %s data ",
         tsk_fs_type_toname(a_fs->ftype));
     return TSK_ERR;
@@ -238,8 +238,8 @@ uint8_t
 tsk_fs_nofs_jopen(TSK_FS_INFO * a_fs, TSK_INUM_T inum)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr(
         "Illegal analysis method for %s data ",
         tsk_fs_type_toname(a_fs->ftype));
     return 1;
@@ -252,8 +252,8 @@ tsk_fs_nofs_jentry_walk(TSK_FS_INFO * a_fs, int a_flags,
     TSK_FS_JENTRY_WALK_CB a_action, void *a_ptr)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr(
         "Illegal analysis method for %s data ",
         tsk_fs_type_toname(a_fs->ftype));
     return 1;
@@ -267,8 +267,8 @@ tsk_fs_nofs_jblk_walk(TSK_FS_INFO * a_fs, TSK_INUM_T start, TSK_INUM_T end,
     int a_flags, TSK_FS_JBLK_WALK_CB a_action, void *a_ptr)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr(
         "Illegal analysis method for %s data ",
         tsk_fs_type_toname(a_fs->ftype));
     return 1;
diff --git a/tsk3/fs/ntfs.c b/tsk3/fs/ntfs.c
index 2a898b5d0..a637b3c59 100644
--- a/tsk3/fs/ntfs.c
+++ b/tsk3/fs/ntfs.c
@@ -1,11 +1,11 @@
 /*
 ** ntfs
-** The Sleuth Kit 
+** The Sleuth Kit
 **
 ** Content and meta data layer support for the NTFS file system
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2010 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
 **
 ** TASK
@@ -31,7 +31,7 @@
  * - multiple ".." entries may exist
  */
 
-/* 
+/*
  * How are we to handle the META flag? Is the MFT $Data Attribute META?
  */
 
@@ -48,24 +48,24 @@
  * a way to calculate the maximum MFT entry number (last_inum).
  *
  * Ok, that is simple, but getting the full $Data attribute can be tough
- * because $MFT may not fit into one MFT entry (i.e. an attribute list). 
+ * because $MFT may not fit into one MFT entry (i.e. an attribute list).
  * We need to process the attribute list attribute to find out which
  * other entries to process.  But, the attribute list attribute comes
  * before any $Data attribute (so it could refer to an MFT that has not
- * yet been 'defined').  Although, the $Data attribute seems to always 
+ * yet been 'defined').  Although, the $Data attribute seems to always
  * exist and define at least the run for the entry in the attribute list.
  *
  * So, the way this is solved is that generic mft_lookup is used to get
- * any MFT entry, even $MFT.  If $MFT is not cached then we calculate 
- * the address of where to read based on mutliplication and guessing.  
+ * any MFT entry, even $MFT.  If $MFT is not cached then we calculate
+ * the address of where to read based on mutliplication and guessing.
  * When we are loading the $MFT, we set 'loading_the_MFT' to 1 so
  * that we can update things as we go along.  When we read $MFT we
  * read all the attributes and save info about the $Data one.  If
  * there is an attribute list, we will have the location of the
- * additional MFT in the cached $Data location, which will be 
+ * additional MFT in the cached $Data location, which will be
  * updated as we process the attribute list.  After each MFT
  * entry that we process while loading the MFT, the 'final_inum'
- * value is updated to reflect what we can currently load so 
+ * value is updated to reflect what we can currently load so
  * that the sanity checks still work.
  */
 
@@ -100,7 +100,7 @@ nt2unixtime(uint64_t ntdate)
  * to only the nanoseconds
  *
  */
-uint32_t
+static uint32_t
 nt2nano(uint64_t ntdate)
 {
     return (uint32_t) (ntdate % 10000000);
@@ -122,13 +122,13 @@ nt2nano(uint64_t ntdate)
  * structure.
  *
  * @param a_ntfs File system to read from
- * @param a_mft Buffer to save raw data to
+ * @param a_buf Buffer to save raw data to.  Must be of size NTFS_INFO.mft_rsize_b
  * @param a_mftnum Address of MFT entry to read
  *
  * @returns Error value
  */
-static TSK_RETVAL_ENUM
-ntfs_dinode_lookup(NTFS_INFO * a_ntfs, ntfs_mft * a_mft,
+TSK_RETVAL_ENUM
+ntfs_dinode_lookup(NTFS_INFO * a_ntfs, char * a_buf,
     TSK_INUM_T a_mftnum)
 {
     TSK_OFF_T mftaddr_b, mftaddr2_b, offset;
@@ -138,31 +138,33 @@ ntfs_dinode_lookup(NTFS_INFO * a_ntfs, ntfs_mft * a_mft,
     TSK_FS_ATTR_RUN *data_run;
     ntfs_upd *upd;
     uint16_t sig_seq;
+    ntfs_mft *mft;
+
 
     /* sanity checks */
-    if (!a_mft) {
+    if (!a_buf) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "mft_lookup: null mft buffer");
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr("mft_lookup: null mft buffer");
         return TSK_ERR;
     }
 
     if (a_mftnum < fs->first_inum) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "mft_lookup: inode number is too small (%" PRIuINUM ")",
             a_mftnum);
         return TSK_ERR;
     }
 
-    /* Because this code reads teh actual MFT, we need to make sure we 
+    /* Because this code reads teh actual MFT, we need to make sure we
      * decrement the last_inum because the last value is a special value
      * for the ORPHANS directory */
     if (a_mftnum > fs->last_inum - 1) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "mft_lookup: inode number is too large (%" PRIuINUM ")",
             a_mftnum);
         return TSK_ERR;
@@ -174,7 +176,7 @@ ntfs_dinode_lookup(NTFS_INFO * a_ntfs, ntfs_mft * a_mft,
             "ntfs_dinode_lookup: Processing MFT %" PRIuINUM "\n",
             a_mftnum);
 
-    /* If mft_data (the cached $Data attribute of $MFT) is not there yet, 
+    /* If mft_data (the cached $Data attribute of $MFT) is not there yet,
      * then we have not started to load $MFT yet.  In that case, we will
      * 'cheat' and calculate where it goes.  This should only be for
      * $MFT itself, in which case the calculation is easy
@@ -187,8 +189,8 @@ ntfs_dinode_lookup(NTFS_INFO * a_ntfs, ntfs_mft * a_mft,
          */
         if (a_mftnum > NTFS_LAST_DEFAULT_INO) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_ARG;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_ARG);
+            tsk_error_set_errstr(
                 "Error trying to load a high MFT entry when the MFT itself has not been loaded (%"
                 PRIuINUM ")", a_mftnum);
             return TSK_ERR;
@@ -210,7 +212,7 @@ ntfs_dinode_lookup(NTFS_INFO * a_ntfs, ntfs_mft * a_mft,
         /* The byte offset within the $Data stream */
         offset = a_mftnum * a_ntfs->mft_rsize_b;
 
-        /* NOTE: data_run values are in clusters 
+        /* NOTE: data_run values are in clusters
          *
          * cycle through the runs in $Data and identify which
          * has the MFT entry that we want
@@ -243,8 +245,8 @@ ntfs_dinode_lookup(NTFS_INFO * a_ntfs, ntfs_mft * a_mft,
 
                     if (data_run->next == NULL) {
                         tsk_error_reset();
-                        tsk_errno = TSK_ERR_FS_INODE_COR;
-                        snprintf(tsk_errstr, TSK_ERRSTR_L,
+                        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+                        tsk_error_set_errstr(
                             "mft_lookup: MFT entry crosses a cluster and there are no more clusters!");
                         return TSK_COR;
                     }
@@ -271,8 +273,8 @@ ntfs_dinode_lookup(NTFS_INFO * a_ntfs, ntfs_mft * a_mft,
         /* Did we find it? */
         if (!mftaddr_b) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_INODE_NUM;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
+            tsk_error_set_errstr(
                 "mft_lookup: Error finding MFT entry %"
                 PRIuINUM " in $MFT", a_mftnum);
             return TSK_ERR;
@@ -285,14 +287,14 @@ ntfs_dinode_lookup(NTFS_INFO * a_ntfs, ntfs_mft * a_mft,
         ssize_t cnt;
         /* read the first part into mft */
         cnt =
-            tsk_fs_read(&a_ntfs->fs_info, mftaddr_b, (char *) a_mft,
+            tsk_fs_read(&a_ntfs->fs_info, mftaddr_b, a_buf,
             mftaddr_len);
         if (cnt != mftaddr_len) {
             if (cnt >= 0) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_READ;
+                tsk_error_set_errno(TSK_ERR_FS_READ);
             }
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
+            tsk_error_set_errstr2(
                 "ntfs_dinode_lookup: Error reading MFT Entry (part 1) at %"
                 PRIuOFF, mftaddr_b);
             return TSK_ERR;
@@ -301,14 +303,14 @@ ntfs_dinode_lookup(NTFS_INFO * a_ntfs, ntfs_mft * a_mft,
         /* read the second part into mft */
         cnt = tsk_fs_read
             (&a_ntfs->fs_info, mftaddr2_b,
-            (char *) ((uintptr_t) a_mft + (uintptr_t) mftaddr_len),
+            (char *) ((uintptr_t) a_buf + (uintptr_t) mftaddr_len),
             a_ntfs->mft_rsize_b - mftaddr_len);
         if (cnt != a_ntfs->mft_rsize_b - mftaddr_len) {
             if (cnt >= 0) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_READ;
+                tsk_error_set_errno(TSK_ERR_FS_READ);
             }
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
+            tsk_error_set_errstr2(
                 "ntfs_dinode_lookup: Error reading MFT Entry (part 2) at %"
                 PRIuOFF, mftaddr2_b);
             return TSK_ERR;
@@ -318,23 +320,20 @@ ntfs_dinode_lookup(NTFS_INFO * a_ntfs, ntfs_mft * a_mft,
         ssize_t cnt;
         /* read the raw entry into mft */
         cnt =
-            tsk_fs_read(&a_ntfs->fs_info, mftaddr_b, (char *) a_mft,
+            tsk_fs_read(&a_ntfs->fs_info, mftaddr_b, a_buf,
             a_ntfs->mft_rsize_b);
         if (cnt != a_ntfs->mft_rsize_b) {
             if (cnt >= 0) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_READ;
+                tsk_error_set_errno(TSK_ERR_FS_READ);
             }
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
+            tsk_error_set_errstr2(
                 "ntfs_dinode_lookup: Error reading MFT Entry at %"
                 PRIuOFF, mftaddr_b);
             return TSK_ERR;
         }
     }
 
-    /* if we are saving into the NTFS_INFO structure, assign mnum too */
-    if ((uintptr_t) a_mft == (uintptr_t) a_ntfs->mft)
-        a_ntfs->mnum = a_mftnum;
     /* Sanity Check */
 #if 0
     /* This is no longer applied because it caused too many problems
@@ -346,8 +345,8 @@ ntfs_dinode_lookup(NTFS_INFO * a_ntfs, ntfs_mft * a_mft,
     if ((tsk_getu32(fs->endian, mft->magic) != NTFS_MFT_MAGIC)
         && (tsk_getu32(fs->endian, mft->magic) != NTFS_MFT_MAGIC_BAAD)
         && (tsk_getu32(fs->endian, mft->magic) != NTFS_MFT_MAGIC_ZERO)) {
-        tsk_errno = TSK_ERR_FS_INODE_COR;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+        tsk_error_set_errstr(
             "entry %d has an invalid MFT magic: %x",
             mftnum, tsk_getu32(fs->endian, mft->magic));
         return 1;
@@ -355,53 +354,54 @@ ntfs_dinode_lookup(NTFS_INFO * a_ntfs, ntfs_mft * a_mft,
 #endif
     /* The MFT entries have error and integrity checks in them
      * called update sequences.  They must be checked and removed
-     * so that later functions can process the data as normal. 
+     * so that later functions can process the data as normal.
      * They are located in the last 2 bytes of each 512-byte sector
      *
      * We first verify that the the 2-byte value is a give value and
      * then replace it with what should be there
      */
     /* sanity check so we don't run over in the next loop */
-    if ((tsk_getu16(fs->endian, a_mft->upd_cnt) > 0) &&
+    mft = (ntfs_mft *)a_buf;
+    if ((tsk_getu16(fs->endian, mft->upd_cnt) > 0) &&
         (((uint32_t) (tsk_getu16(fs->endian,
-                        a_mft->upd_cnt) - 1) * a_ntfs->ssize_b) >
+                        mft->upd_cnt) - 1) * a_ntfs->ssize_b) >
             a_ntfs->mft_rsize_b)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_INODE_COR;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+        tsk_error_set_errstr(
             "dinode_lookup: More Update Sequence Entries than MFT size");
         return TSK_COR;
     }
-    if (tsk_getu16(fs->endian, a_mft->upd_off) > a_ntfs->mft_rsize_b) {
+    if (tsk_getu16(fs->endian, mft->upd_off) > a_ntfs->mft_rsize_b) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_INODE_COR;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+        tsk_error_set_errstr(
             "dinode_lookup: Update sequence offset larger than MFT size");
         return TSK_COR;
     }
 
     /* Apply the update sequence structure template */
     upd =
-        (ntfs_upd *) ((uintptr_t) a_mft + tsk_getu16(fs->endian,
-            a_mft->upd_off));
+        (ntfs_upd *) ((uintptr_t) a_buf + tsk_getu16(fs->endian,
+            mft->upd_off));
     /* Get the sequence value that each 16-bit value should be */
     sig_seq = tsk_getu16(fs->endian, upd->upd_val);
     /* cycle through each sector */
-    for (i = 1; i < tsk_getu16(fs->endian, a_mft->upd_cnt); i++) {
+    for (i = 1; i < tsk_getu16(fs->endian, mft->upd_cnt); i++) {
         uint8_t *new_val, *old_val;
         /* The offset into the buffer of the value to analyze */
         size_t offset = i * a_ntfs->ssize_b - 2;
         /* get the current sequence value */
         uint16_t cur_seq =
-            tsk_getu16(fs->endian, (uintptr_t) a_mft + offset);
+            tsk_getu16(fs->endian, (uintptr_t) a_buf + offset);
         if (cur_seq != sig_seq) {
             /* get the replacement value */
             uint16_t cur_repl =
                 tsk_getu16(fs->endian, &upd->upd_seq + (i - 1) * 2);
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_GENFS;
+            tsk_error_set_errno(TSK_ERR_FS_GENFS);
 
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errstr(
                 "Incorrect update sequence value in MFT entry\nSignature Value: 0x%"
                 PRIx16 " Actual Value: 0x%" PRIx16
                 " Replacement Value: 0x%" PRIx16
@@ -411,7 +411,7 @@ ntfs_dinode_lookup(NTFS_INFO * a_ntfs, ntfs_mft * a_mft,
         }
 
         new_val = &upd->upd_seq + (i - 1) * 2;
-        old_val = (uint8_t *) ((uintptr_t) a_mft + offset);
+        old_val = (uint8_t *) ((uintptr_t) a_buf + offset);
         /*
            if (tsk_verbose)
            tsk_fprintf(stderr,
@@ -438,20 +438,21 @@ is_clustalloc(NTFS_INFO * ntfs, TSK_DADDR_T addr)
 {
     int bits_p_clust, b;
     TSK_DADDR_T base;
+    int8_t ret;
     bits_p_clust = 8 * ntfs->fs_info.block_size;
 
     /* While we are loading the MFT, assume that everything
      * is allocated.  This should only be needed when we are
-     * dealing with an attribute list ... 
+     * dealing with an attribute list ...
      */
     if (ntfs->loading_the_MFT == 1) {
         return 1;
     }
     else if (ntfs->bmap == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
 
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errstr(
             "is_clustalloc: Bitmap pointer is null: %" PRIuDADDR
             "\n", addr);
         return -1;
@@ -460,8 +461,8 @@ is_clustalloc(NTFS_INFO * ntfs, TSK_DADDR_T addr)
     /* Is the cluster too big? */
     if (addr > ntfs->fs_info.last_block) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_INODE_COR;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+        tsk_error_set_errstr(
             "is_clustalloc: cluster too large");
         return -1;
     }
@@ -470,6 +471,8 @@ is_clustalloc(NTFS_INFO * ntfs, TSK_DADDR_T addr)
     base = addr / bits_p_clust;
     b = (int) (addr % bits_p_clust);
 
+    tsk_take_lock(&ntfs->lock);
+
     /* is this the same as in the cached buffer? */
     if (base != ntfs->bmap_buf_off) {
         TSK_DADDR_T c = base;
@@ -489,17 +492,19 @@ is_clustalloc(NTFS_INFO * ntfs, TSK_DADDR_T addr)
         }
 
         if (fsaddr == 0) {
+            tsk_release_lock(&ntfs->lock);
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_BLK_NUM;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_BLK_NUM);
+            tsk_error_set_errstr(
                 "is_clustalloc: cluster not found in bitmap: %"
                 PRIuDADDR "", c);
             return -1;
         }
         if (fsaddr > ntfs->fs_info.last_block) {
+            tsk_release_lock(&ntfs->lock);
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_BLK_NUM;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_BLK_NUM);
+            tsk_error_set_errstr(
                 "is_clustalloc: Cluster in bitmap too large for image: %"
                 PRIuDADDR, fsaddr);
             return -1;
@@ -509,11 +514,12 @@ is_clustalloc(NTFS_INFO * ntfs, TSK_DADDR_T addr)
             (&ntfs->fs_info, fsaddr, ntfs->bmap_buf,
             ntfs->fs_info.block_size);
         if (cnt != ntfs->fs_info.block_size) {
+            tsk_release_lock(&ntfs->lock);
             if (cnt >= 0) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_READ;
+                tsk_error_set_errno(TSK_ERR_FS_READ);
             }
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
+            tsk_error_set_errstr2(
                 "is_clustalloc: Error reading bitmap at %"
                 PRIuDADDR, fsaddr);
             return -1;
@@ -521,7 +527,10 @@ is_clustalloc(NTFS_INFO * ntfs, TSK_DADDR_T addr)
     }
 
     /* identify if the cluster is allocated or not */
-    return (isset(ntfs->bmap_buf, b)) ? 1 : 0;
+    ret = (isset(ntfs->bmap_buf, b)) ? 1 : 0;
+
+    tsk_release_lock(&ntfs->lock);
+    return ret;
 }
 
 
@@ -535,19 +544,20 @@ is_clustalloc(NTFS_INFO * ntfs, TSK_DADDR_T addr)
 
 /**
  * Process a non-resident runlist and convert its contents into the generic fs_attr_run
- * structure. 
+ * structure.
  * @param ntfs File system that attribute is located in.
- * @param start_vcn The starting VCN for this run. 
+ * @param start_vcn The starting VCN for this run.
  * @param runlist The raw runlist data from the MFT entry.
  * @param a_data_run_head [out] Pointer to pointer of run that is created. (NULL on error and for $BadClust - special case because it is a sparse file for the entire FS).
  * @param totlen [out] Pointer to location where total length of run (in bytes) can be returned (or NULL)
+ * @param mnum MFT entry address
  *
  * @returns Return status of error, corrupt, or OK (note a_data_run can be NULL even when OK is returned if $BadClust is encountered)
  */
 static TSK_RETVAL_ENUM
 ntfs_make_data_run(NTFS_INFO * ntfs, TSK_OFF_T start_vcn,
     ntfs_runlist * runlist_head, TSK_FS_ATTR_RUN ** a_data_run_head,
-    TSK_OFF_T * totlen)
+    TSK_OFF_T * totlen, TSK_INUM_T mnum)
 {
     TSK_FS_INFO *fs = (TSK_FS_INFO *) ntfs;
     ntfs_runlist *run;
@@ -563,7 +573,7 @@ ntfs_make_data_run(NTFS_INFO * ntfs, TSK_OFF_T start_vcn,
     if (totlen)
         *totlen = 0;
 
-    /* Cycle through each run in the runlist 
+    /* Cycle through each run in the runlist
      * We go until we find an entry with no length
      * An entry with offset of 0 is for a sparse run
      */
@@ -603,8 +613,8 @@ ntfs_make_data_run(NTFS_INFO * ntfs, TSK_OFF_T start_vcn,
         /* Sanity check on length */
         if (data_run->len > fs->block_count) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_INODE_COR;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+            tsk_error_set_errstr(
                 "ntfs_make_run: Run length is larger than file system");
             tsk_fs_attr_run_free(*a_data_run_head);
             *a_data_run_head = NULL;
@@ -665,13 +675,14 @@ ntfs_make_data_run(NTFS_INFO * ntfs, TSK_OFF_T start_vcn,
          * there is a special case though of the BOOT MFT entry which
          * is the super block and has a legit offset of 0.
          *
-         * The value given is a delta of the previous offset, so add 
+         * The value given is a delta of the previous offset, so add
          * them for non-sparse files
          *
-         * For sparse files the next run will have its offset relative 
+         * For sparse files the next run will have its offset relative
          * to the current "prev_addr" so skip that code
          */
-        else if ((addr_offset) || (ntfs->mnum == NTFS_MFT_BOOT)) {
+        // @@@ BC: we'll need to pass in an inode value for this check
+        else if ((addr_offset) || (mnum == NTFS_MFT_BOOT)) {
 
             data_run->addr = prev_addr + addr_offset;
             prev_addr = data_run->addr;
@@ -679,8 +690,8 @@ ntfs_make_data_run(NTFS_INFO * ntfs, TSK_OFF_T start_vcn,
             /* Sanity check on length and offset */
             if (data_run->addr + data_run->len > fs->block_count) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_INODE_COR;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+                tsk_error_set_errstr(
                     "ntfs_make_run: Run offset and length is larger than file system");
                 tsk_fs_attr_run_free(*a_data_run_head);
                 *a_data_run_head = NULL;
@@ -727,18 +738,18 @@ ntfs_make_data_run(NTFS_INFO * ntfs, TSK_OFF_T start_vcn,
  * compresses to something smaller than 16 clusters then the
  * compresed data is stored and the rest of the compression unit
  * is filled with sparse clusters. The entire compression unit
- * can also be sparse. 
+ * can also be sparse.
  *
  * When the data is compressed, it is broken up into 4k blocks. Each
  * of the blocks is compressed and the resulting data is stored with
- * a 2-byte header that identifies the compressed size.   The 
+ * a 2-byte header that identifies the compressed size.   The
  * compressed data contains token groups, which contain a 1 byte
  * header followed by 8 variable length tokens.  There are two types
  * of tokens. Symbol tokens are 1 byte in length and the 1byte value
  * should be directly copied into the uncompressed data.  Phrase tokens
- * identify a run of data in the same compression unit that should be 
+ * identify a run of data in the same compression unit that should be
  * copied to the current location.  Each bit in the 1-byte header identifies
- * the type of the 8 tokens in the group. 
+ * the type of the 8 tokens in the group.
  *
  */
 
@@ -753,7 +764,7 @@ typedef struct {
 
 
 /**
- * Reset the values in the NTFS_COMP_INFO structure.  We need to 
+ * Reset the values in the NTFS_COMP_INFO structure.  We need to
  * do this in between every compression unit that we process in the file.
  *
  * @param comp Structure to reset
@@ -768,7 +779,7 @@ ntfs_uncompress_reset(NTFS_COMP_INFO * comp)
 }
 
 /**
- * Setup the NTFS_COMP_INFO structure with a buffer and 
+ * Setup the NTFS_COMP_INFO structure with a buffer and
  * initialize the basic settings.
  *
  * @param fs File system state information
@@ -810,9 +821,9 @@ ntfs_uncompress_done(NTFS_COMP_INFO * comp)
 
 
  /**
-  * Uncompress the block of data in comp->comp_buf, 
+  * Uncompress the block of data in comp->comp_buf,
   * which has a size of comp->comp_len.
-  * Store the result in the comp->uncomp_buf. 
+  * Store the result in the comp->uncomp_buf.
   *
   * @param comp Compression unit structure
   *
@@ -827,13 +838,13 @@ ntfs_uncompress_compunit(NTFS_COMP_INFO * comp)
 
     comp->uncomp_idx = 0;
 
-    /* Cycle through the compressed data 
+    /* Cycle through the compressed data
      * We maintain state using different levels of loops.
      * We use +1 here because the size value at start of block is 2 bytes.
      */
     for (cl_index = 0; cl_index + 1 < comp->comp_len;) {
         size_t blk_end;         // index into the buffer to where block ends
-        size_t blk_size;        // size of the current block 
+        size_t blk_size;        // size of the current block
         uint8_t iscomp;         // set to 1 if block is compressed
         size_t blk_st_uncomp;   // index into uncompressed buffer where block started
 
@@ -848,8 +859,8 @@ ntfs_uncompress_compunit(NTFS_COMP_INFO * comp)
 
         blk_end = cl_index + blk_size;
         if (blk_end > comp->comp_len) {
-            tsk_errno = TSK_ERR_FS_FWALK;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_FWALK);
+            tsk_error_set_errstr(
                 "ntfs_uncompress_compunit: Block length longer than buffer length: %"
                 PRIuSIZE "", blk_end);
             return 1;
@@ -889,7 +900,7 @@ ntfs_uncompress_compunit(NTFS_COMP_INFO * comp)
 
                     /* Determine token type and parse appropriately. *
                      * Symbol tokens are the symbol themselves, so copy it
-                     * into the umcompressed buffer 
+                     * into the umcompressed buffer
                      */
                     if ((header & NTFS_TOKEN_MASK) == NTFS_SYMBOL_TOKEN) {
                         if (tsk_verbose)
@@ -898,8 +909,8 @@ ntfs_uncompress_compunit(NTFS_COMP_INFO * comp)
                                 PRIuSIZE "\n", cl_index);
 
                         if (comp->uncomp_idx >= comp->buf_size_b) {
-                            tsk_errno = TSK_ERR_FS_FWALK;
-                            snprintf(tsk_errstr, TSK_ERRSTR_L,
+                            tsk_error_set_errno(TSK_ERR_FS_FWALK);
+                            tsk_error_set_errstr(
                                 "ntfs_uncompress_compunit: Trying to write past end of uncompression buffer: %"
                                 PRIuSIZE "", comp->uncomp_idx);
                             return 1;
@@ -911,7 +922,7 @@ ntfs_uncompress_compunit(NTFS_COMP_INFO * comp)
                     }
 
                     /* Otherwise, it is a phrase token, which points back
-                     * to a previous sequence of bytes. 
+                     * to a previous sequence of bytes.
                      */
                     else {
                         size_t i;
@@ -923,8 +934,8 @@ ntfs_uncompress_compunit(NTFS_COMP_INFO * comp)
                         uint16_t pheader;
 
                         if (cl_index + 1 >= blk_end) {
-                            tsk_errno = TSK_ERR_FS_FWALK;
-                            snprintf(tsk_errstr, TSK_ERRSTR_L,
+                            tsk_error_set_errno(TSK_ERR_FS_FWALK);
+                            tsk_error_set_errstr(
                                 "ntfs_uncompress_compunit: Phrase token index is past end of block: %d",
                                 a);
                             return 1;
@@ -937,8 +948,8 @@ ntfs_uncompress_compunit(NTFS_COMP_INFO * comp)
                         cl_index += 2;
 
 
-                        /* The number of bits for the start and length              
-                         * in the 2-byte header change depending on the 
+                        /* The number of bits for the start and length
+                         * in the 2-byte header change depending on the
                          * location in the compression unit.  This identifies
                          * how many bits each has */
                         shift = 0;
@@ -965,8 +976,8 @@ ntfs_uncompress_compunit(NTFS_COMP_INFO * comp)
                         /* Sanity checks on values */
                         if (offset > comp->uncomp_idx) {
                             tsk_error_reset();
-                            tsk_errno = TSK_ERR_FS_FWALK;
-                            snprintf(tsk_errstr, TSK_ERRSTR_L,
+                            tsk_error_set_errno(TSK_ERR_FS_FWALK);
+                            tsk_error_set_errstr(
                                 "ntfs_uncompress_compunit: Phrase token offset is too large:  %d (max: %"
                                 PRIuSIZE ")", offset, comp->uncomp_idx);
                             return 1;
@@ -974,8 +985,8 @@ ntfs_uncompress_compunit(NTFS_COMP_INFO * comp)
                         else if (length + start_position_index >
                             comp->buf_size_b) {
                             tsk_error_reset();
-                            tsk_errno = TSK_ERR_FS_FWALK;
-                            snprintf(tsk_errstr, TSK_ERRSTR_L,
+                            tsk_error_set_errno(TSK_ERR_FS_FWALK);
+                            tsk_error_set_errstr(
                                 "ntfs_uncompress_compunit: Phrase token length is too large:  %d (max: %zu)",
                                 length,
                                 comp->buf_size_b - start_position_index);
@@ -985,8 +996,8 @@ ntfs_uncompress_compunit(NTFS_COMP_INFO * comp)
                             start_position_index + 1 >
                             comp->buf_size_b - comp->uncomp_idx) {
                             tsk_error_reset();
-                            tsk_errno = TSK_ERR_FS_FWALK;
-                            snprintf(tsk_errstr, TSK_ERRSTR_L,
+                            tsk_error_set_errno(TSK_ERR_FS_FWALK);
+                            tsk_error_set_errstr(
                                 "ntfs_uncompress_compunit: Phrase token length is too large for rest of uncomp buf:  %zu (max: %"
                                 PRIuSIZE ")",
                                 end_position_index - start_position_index +
@@ -1017,8 +1028,8 @@ ntfs_uncompress_compunit(NTFS_COMP_INFO * comp)
                  * when an unallocated file is being processed... */
                 if (comp->uncomp_idx >= comp->buf_size_b) {
                     tsk_error_reset();
-                    tsk_errno = TSK_ERR_FS_FWALK;
-                    snprintf(tsk_errstr, TSK_ERRSTR_L,
+                    tsk_error_set_errno(TSK_ERR_FS_FWALK);
+                    tsk_error_set_errstr(
                         "ntfs_uncompress_compunit: Trying to write past end of uncompression buffer (1) -- corrupt data?)");
                     return 1;
                 }
@@ -1036,7 +1047,7 @@ ntfs_uncompress_compunit(NTFS_COMP_INFO * comp)
 
 
 /**
- * Process a compression unit and return the decompressed data in a buffer in comp. 
+ * Process a compression unit and return the decompressed data in a buffer in comp.
  *
  * @param ntfs File system
  * @param comp Compression state info (output will be stored in here)
@@ -1101,9 +1112,9 @@ ntfs_proc_compunit(NTFS_INFO * ntfs, NTFS_COMP_INFO * comp,
             if (cnt != fs->block_size) {
                 if (cnt >= 0) {
                     tsk_error_reset();
-                    tsk_errno = TSK_ERR_FS_READ;
+                    tsk_error_set_errno(TSK_ERR_FS_READ);
                 }
-                snprintf(tsk_errstr2, TSK_ERRSTR_L,
+                tsk_error_set_errstr2(
                     "ntfs_proc_compunit: Error reading block at %"
                     PRIuDADDR, comp_unit[a]);
                 return 1;
@@ -1132,9 +1143,9 @@ ntfs_proc_compunit(NTFS_INFO * ntfs, NTFS_COMP_INFO * comp,
             if (cnt != fs->block_size) {
                 if (cnt >= 0) {
                     tsk_error_reset();
-                    tsk_errno = TSK_ERR_FS_READ;
+                    tsk_error_set_errno(TSK_ERR_FS_READ);
                 }
-                snprintf(tsk_errstr2, TSK_ERRSTR_L,
+                tsk_error_set_errstr2(
                     "ntfs_proc_compunit: Error reading block at %"
                     PRIuDADDR, comp_unit[a]);
                 return 1;
@@ -1150,7 +1161,7 @@ ntfs_proc_compunit(NTFS_INFO * ntfs, NTFS_COMP_INFO * comp,
 /**
  * Currently ignores the SPARSE flag
  */
-uint8_t
+static uint8_t
 ntfs_attr_walk_special(const TSK_FS_ATTR * fs_attr,
     int flags, TSK_FS_FILE_WALK_CB a_action, void *ptr)
 {
@@ -1162,8 +1173,8 @@ ntfs_attr_walk_special(const TSK_FS_ATTR * fs_attr,
     if ((fs_attr == NULL) || (fs_attr->fs_file == NULL)
         || (fs_attr->fs_file->meta == NULL)
         || (fs_attr->fs_file->fs_info == NULL)) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "ntfs_attr_walk_special: Null arguments given\n");
         return 1;
     }
@@ -1171,7 +1182,7 @@ ntfs_attr_walk_special(const TSK_FS_ATTR * fs_attr,
     fs = fs_attr->fs_file->fs_info;
     ntfs = (NTFS_INFO *) fs;
 
-    /* Process the compressed buffer 
+    /* Process the compressed buffer
      *
      * The compsize value equal to 0 can occur if we are processing an
      * isolated entry that is part of an attribute list.  The first
@@ -1193,8 +1204,8 @@ ntfs_attr_walk_special(const TSK_FS_ATTR * fs_attr,
         uint8_t stop_loop = 0;
 
         if (fs_attr->nrd.compsize <= 0) {
-            tsk_errno = TSK_ERR_FS_FWALK;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_FWALK);
+            tsk_error_set_errstr(
                 "ntfs_attrwalk_special: Compressed attribute has compsize of 0");
             return 1;
         }
@@ -1219,8 +1230,8 @@ ntfs_attr_walk_special(const TSK_FS_ATTR * fs_attr,
             size_t len_idx;
 
             /* We may get a FILLER entry at the beginning of the run
-             * if we are processing a non-base file record since 
-             * this $DATA attribute could not be the first sequence in the 
+             * if we are processing a non-base file record since
+             * this $DATA attribute could not be the first sequence in the
              * attribute. Therefore, do not error if it starts at 0 */
             if (fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) {
                 if (fs_attr_run->addr != 0) {
@@ -1228,10 +1239,10 @@ ntfs_attr_walk_special(const TSK_FS_ATTR * fs_attr,
 
                     if (fs_attr->fs_file->
                         meta->flags & TSK_FS_META_FLAG_UNALLOC)
-                        tsk_errno = TSK_ERR_FS_RECOVER;
+                        tsk_error_set_errno(TSK_ERR_FS_RECOVER);
                     else
-                        tsk_errno = TSK_ERR_FS_GENFS;
-                    snprintf(tsk_errstr, TSK_ERRSTR_L,
+                        tsk_error_set_errno(TSK_ERR_FS_GENFS);
+                    tsk_error_set_errstr(
                         "Filler Entry exists in fs_attr_run %"
                         PRIuDADDR "@%" PRIuDADDR
                         " - type: %" PRIu32 "  id: %d", fs_attr_run->len,
@@ -1256,10 +1267,10 @@ ntfs_attr_walk_special(const TSK_FS_ATTR * fs_attr,
 
                     if (fs_attr->fs_file->
                         meta->flags & TSK_FS_META_FLAG_UNALLOC)
-                        tsk_errno = TSK_ERR_FS_RECOVER;
+                        tsk_error_set_errno(TSK_ERR_FS_RECOVER);
                     else
-                        tsk_errno = TSK_ERR_FS_BLK_NUM;
-                    snprintf(tsk_errstr, TSK_ERRSTR_L,
+                        tsk_error_set_errno(TSK_ERR_FS_BLK_NUM);
+                    tsk_error_set_errstr(
                         "Invalid address in run (too large): %"
                         PRIuDADDR "", addr);
 
@@ -1297,7 +1308,7 @@ ntfs_attr_walk_special(const TSK_FS_ATTR * fs_attr,
                         if (retval == -1) {
                             if (fs_attr->fs_file->
                                 meta->flags & TSK_FS_META_FLAG_UNALLOC)
-                                tsk_errno = TSK_ERR_FS_RECOVER;
+                                tsk_error_set_errno(TSK_ERR_FS_RECOVER);
                             free(comp_unit);
                             ntfs_uncompress_done(&comp);
                             return 1;
@@ -1316,8 +1327,8 @@ ntfs_attr_walk_special(const TSK_FS_ATTR * fs_attr,
 
                         if (i * fs->block_size + read_len >
                             comp.uncomp_idx) {
-                            tsk_errno = TSK_ERR_FS_FWALK;
-                            snprintf(tsk_errstr, TSK_ERRSTR_L,
+                            tsk_error_set_errno(TSK_ERR_FS_FWALK);
+                            tsk_error_set_errstr(
                                 "ntfs_attrwalk_special: Trying to read past end of uncompressed buffer: %"
                                 PRIuSIZE " %" PRIuSIZE "",
                                 i * fs->block_size + read_len,
@@ -1372,8 +1383,8 @@ ntfs_attr_walk_special(const TSK_FS_ATTR * fs_attr,
             return 0;
     }
     else {
-        tsk_errno = TSK_ERR_FS_FWALK;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_FWALK);
+        tsk_error_set_errstr(
             "ntfs_attrwalk_special: called with non-special attribute: %x",
             fs_attr->flags);
         return 1;
@@ -1385,7 +1396,7 @@ ntfs_attr_walk_special(const TSK_FS_ATTR * fs_attr,
  *
  * @returns number of bytes read or -1 on error (incl if offset is past EOF)
  */
-ssize_t
+static ssize_t
 ntfs_file_read_special(const TSK_FS_ATTR * a_fs_attr,
     TSK_OFF_T a_offset, char *a_buf, size_t a_len)
 {
@@ -1395,8 +1406,8 @@ ntfs_file_read_special(const TSK_FS_ATTR * a_fs_attr,
     if ((a_fs_attr == NULL) || (a_fs_attr->fs_file == NULL)
         || (a_fs_attr->fs_file->meta == NULL)
         || (a_fs_attr->fs_file->fs_info == NULL)) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "ntfs_file_read_special: NULL parameters passed");
         return -1;
     }
@@ -1414,16 +1425,16 @@ ntfs_file_read_special(const TSK_FS_ATTR * a_fs_attr,
         size_t buf_idx = 0;
 
         if (a_fs_attr->nrd.compsize <= 0) {
-            tsk_errno = TSK_ERR_FS_FWALK;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_FWALK);
+            tsk_error_set_errstr(
                 "ntfs_file_read_special: Compressed attribute has compsize of 0");
             return -1;
         }
 
         if (a_offset >= a_fs_attr->size) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_READ_OFF;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_READ_OFF);
+            tsk_error_set_errstr(
                 "ntfs_file_read_special - %" PRIuOFF, a_offset);
             return -1;
         }
@@ -1536,7 +1547,7 @@ ntfs_file_read_special(const TSK_FS_ATTR * a_fs_attr,
                     memcpy(&a_buf[buf_idx], &comp.uncomp_buf[byteoffset],
                         cpylen);
 
-                    // reset this in case we need to also read from the next run 
+                    // reset this in case we need to also read from the next run
                     byteoffset = 0;
                     buf_idx += cpylen;
                     comp_unit_idx = 0;
@@ -1556,8 +1567,8 @@ ntfs_file_read_special(const TSK_FS_ATTR * a_fs_attr,
         return (ssize_t) buf_idx;
     }
     else {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "ntfs_file_read_special: called with non-special attribute: %x",
             a_fs_attr->flags);
         return -1;
@@ -1587,9 +1598,9 @@ typedef struct {
     uint32_t newId[256];
 } NTFS_ATTRLIST_MAP;
 
-/* 
+/*
  * Process an NTFS attribute sequence and load the data into data
- * structures. 
+ * structures.
  * An attribute sequence is a linked list of the attributes in an MFT entry.
  * This is called by copy_inode and proc_attrlist.
  *
@@ -1618,8 +1629,8 @@ ntfs_proc_attrseq(NTFS_INFO * ntfs,
 
     if (fs_file->meta->attr == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "Null attribute list in ntfs_proc_attrseq");
         return TSK_ERR;
     }
@@ -1721,20 +1732,19 @@ ntfs_proc_attrseq(NTFS_INFO * ntfs,
                             attr->c.r.ssize) + (uintptr_t) attr) >
                     ((uintptr_t) a_attrseq + len))) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_CORRUPT;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_FS_CORRUPT);
+                tsk_error_set_errstr(
                     "ntfs_attr_walk: Resident attribute %" PRIuINUM
                     "-%" PRIu32 " starting offset and length too large",
                     fs_file->meta->addr, type);
                 return TSK_COR;
             }
 
-            // Get a free fs_attr structure 
+            // Get a free fs_attr structure
             if ((fs_attr =
                     tsk_fs_attrlist_getnew(fs_file->meta->attr,
                         TSK_FS_ATTR_RES)) == NULL) {
-                strncat(tsk_errstr2, " - proc_attrseq",
-                    TSK_ERRSTR_L - strlen(tsk_errstr2));
+                tsk_error_errstr2_concat(" - proc_attrseq");
                 return TSK_ERR;
             }
 
@@ -1744,12 +1754,11 @@ ntfs_proc_attrseq(NTFS_INFO * ntfs,
                         tsk_getu16(fs->endian,
                             attr->c.r.soff)), tsk_getu32(fs->endian,
                         attr->c.r.ssize))) {
-                strncat(tsk_errstr2, " - proc_attrseq",
-                    TSK_ERRSTR_L - strlen(tsk_errstr2));
+                tsk_error_errstr2_concat("- proc_attrseq");
                 return TSK_ERR;
             }
 
-            // set the meta size if we find the relevant attribute 
+            // set the meta size if we find the relevant attribute
             if ((fs_file->meta->type == TSK_FS_META_TYPE_DIR)
                 && (type == NTFS_ATYPE_IDXROOT)) {
                 fs_file->meta->size =
@@ -1785,10 +1794,9 @@ ntfs_proc_attrseq(NTFS_INFO * ntfs,
                 tsk_getu64(fs->endian, attr->c.nr.start_vcn),
                 (ntfs_runlist *) ((uintptr_t)
                     attr + tsk_getu16(fs->endian,
-                        attr->c.nr.run_off)), &fs_attr_run, NULL);
+                        attr->c.nr.run_off)), &fs_attr_run, NULL, a_attrinum);
             if (retval != TSK_OK) {
-                strncat(tsk_errstr2, " - proc_attrseq",
-                    TSK_ERRSTR_L - strlen(tsk_errstr2));
+                tsk_error_errstr2_concat(" - proc_attrseq");
                 return retval;
             }
 
@@ -1805,14 +1813,14 @@ ntfs_proc_attrseq(NTFS_INFO * ntfs,
             if (tsk_getu16(fs->endian, attr->flags) & NTFS_ATTR_FLAG_SPAR)
                 data_flag |= TSK_FS_ATTR_SPARSE;
 
-            /* SPECIAL CASE 
+            /* SPECIAL CASE
              * We are in non-res section, so we know this
              * isn't $STD_INFO and $FNAME
              *
              * When we are processing a non-base entry, we may
              * find an attribute with an id of 0 and it is an
              * extention of a previous run (i.e. non-zero start VCN)
-             * 
+             *
              * We will lookup if we already have such an attribute
              * and get its ID
              *
@@ -1887,9 +1895,7 @@ ntfs_proc_attrseq(NTFS_INFO * ntfs,
                 if ((fs_attr =
                         tsk_fs_attrlist_getnew(fs_file->meta->attr,
                             TSK_FS_ATTR_RES)) == NULL) {
-                    strncat(tsk_errstr2, " - proc_attrseq: getnew",
-                        TSK_ERRSTR_L - strlen(tsk_errstr2));
-
+                    tsk_error_errstr2_concat(" - proc_attrseq: getnew");
                     // JRB: Coverity found leak.
                     if (fs_attr_run)
                         tsk_fs_attr_run_free(fs_attr_run);
@@ -1930,8 +1936,7 @@ ntfs_proc_attrseq(NTFS_INFO * ntfs,
                         type, id_new, ssize,
                         tsk_getu64(fs->endian, attr->c.nr.initsize),
                         alen, data_flag, compsize)) {
-                    strncat(tsk_errstr2, " - proc_attrseq: set run",
-                        TSK_ERRSTR_L - strlen(tsk_errstr2));
+                    tsk_error_errstr2_concat("- proc_attrseq: set run");
                     return TSK_ERR;
                 }
                 // set the special functions
@@ -1943,15 +1948,14 @@ ntfs_proc_attrseq(NTFS_INFO * ntfs,
             }
             else {
                 if (tsk_fs_attr_add_run(fs, fs_attr, fs_attr_run)) {
-                    strncat(tsk_errstr2, " - proc_attrseq: put run",
-                        TSK_ERRSTR_L - strlen(tsk_errstr2));
+                    tsk_error_errstr2_concat(" - proc_attrseq: put run");
                     return TSK_ERR;
                 }
             }
         }
 
 
-        /* 
+        /*
          * Special Cases, where we grab additional information
          * regardless if they are resident or not
          */
@@ -1961,8 +1965,8 @@ ntfs_proc_attrseq(NTFS_INFO * ntfs,
             ntfs_attr_si *si;
             if (attr->res != NTFS_MFT_RES) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_INODE_COR;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+                tsk_error_set_errstr(
                     "proc_attrseq: Standard Information Attribute is not resident!");
                 return TSK_COR;
             }
@@ -2010,8 +2014,8 @@ ntfs_proc_attrseq(NTFS_INFO * ntfs,
             UTF8 *name8;
             if (attr->res != NTFS_MFT_RES) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_INODE_COR;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+                tsk_error_set_errstr(
                     "proc_attr_seq: File Name Attribute is not resident!");
                 return TSK_COR;
             }
@@ -2078,15 +2082,15 @@ ntfs_proc_attrseq(NTFS_INFO * ntfs,
         /* If this is an attribute list than we need to process
          * it to get the list of other entries to read.  But, because
          * of the wierd scenario of the $MFT having an attribute list
-         * and not knowing where the other MFT entires are yet, we wait 
+         * and not knowing where the other MFT entires are yet, we wait
          * until the end of the attrseq to processes the list and then
          * we should have the $Data attribute loaded
          */
         else if (type == NTFS_ATYPE_ATTRLIST) {
             if (fs_attr_attrl) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+                tsk_error_set_errstr(
                     "Multiple instances of attribute lists in the same MFT\n"
                     "I didn't realize that could happen, contact the developers");
                 return TSK_ERR;
@@ -2094,9 +2098,8 @@ ntfs_proc_attrseq(NTFS_INFO * ntfs,
             fs_attr_attrl = tsk_fs_attrlist_get_id(fs_file->meta->attr,
                 NTFS_ATYPE_ATTRLIST, id_new);
             if (fs_attr_attrl == NULL) {
-                strncat(tsk_errstr2,
-                    " - proc_attrseq: getting attribute list",
-                    TSK_ERRSTR_L - strlen(tsk_errstr2));
+                tsk_error_errstr2_concat(
+                    "- proc_attrseq: getting attribute list");
                 return TSK_ERR;
             }
         }
@@ -2106,9 +2109,9 @@ ntfs_proc_attrseq(NTFS_INFO * ntfs,
     /* Are we currently in the process of loading $MFT? */
     if (ntfs->loading_the_MFT == 1) {
 
-        /* If we don't even have a mini cached version, get it now 
+        /* If we don't even have a mini cached version, get it now
          * Even if we are not done because of attribute lists, then we
-         * should at least have the head of the list 
+         * should at least have the head of the list
          */
         if (!ntfs->mft_data) {
             int cnt, i;
@@ -2132,14 +2135,14 @@ ntfs_proc_attrseq(NTFS_INFO * ntfs,
             // @@@ Is this needed here -- maybe it should be only in _open
             if (!ntfs->mft_data) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_GENFS;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_FS_GENFS);
+                tsk_error_set_errstr(
                     "$Data not found while loading the MFT");
                 return TSK_ERR;
             }
         }
 
-        /* Update the inode count based on the current size 
+        /* Update the inode count based on the current size
          * IF $MFT has an attribute list, this value will increase each
          * time
          */
@@ -2150,7 +2153,7 @@ ntfs_proc_attrseq(NTFS_INFO * ntfs,
     /* If there was an attribute list, process it now, we wait because
      * the list can contain MFT entries that are described in $Data
      * of this MFT entry.  For example, part of the $DATA attribute
-     * could follow the ATTRLIST entry, so we read it first and then 
+     * could follow the ATTRLIST entry, so we read it first and then
      * process the attribute list
      */
     if (fs_attr_attrl) {
@@ -2227,8 +2230,7 @@ ntfs_proc_attrlist(NTFS_INFO * ntfs,
     endaddr = (uintptr_t) buf + (uintptr_t) fs_attr_attrlist->size;
     if (tsk_fs_attr_walk(fs_attr_attrlist, 0, tsk_fs_load_file_action,
             (void *) &load_file)) {
-        strncat(tsk_errstr2, " - processing attrlist",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- processing attrlist");
         free(mft);
         free(map);
         return 1;
@@ -2239,9 +2241,9 @@ ntfs_proc_attrlist(NTFS_INFO * ntfs,
      */
     if (load_file.left > 0) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_FWALK;
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
-            "processing attrlist of entry %" PRIuINUM, ntfs->mnum);
+        tsk_error_set_errno(TSK_ERR_FS_FWALK);
+        tsk_error_set_errstr2(
+            "processing attrlist of entry %" PRIuINUM, fs_file->meta->addr);
         free(mft);
         free(buf);
         free(map);
@@ -2302,7 +2304,7 @@ ntfs_proc_attrlist(NTFS_INFO * ntfs,
         /* also check the todo list -- skip the base entry 
          * the goal here is to get a unique list of MFT entries
          * to later process. */
-        if (mftnum != ntfs->mnum) {
+        if (mftnum != fs_file->meta->addr) {
             found = 0;
             for (i = 0; i < mftToDoCnt; i++) {
                 if (mftToDo[i] == mftnum) {
@@ -2319,7 +2321,7 @@ ntfs_proc_attrlist(NTFS_INFO * ntfs,
     // update the map and assign unique IDs
     for (a = 0; a < map->num_used; a++) {
         // skip the base entry attributes -- they have unique attribute IDs
-        if (map->extMft[a] == ntfs->mnum)
+        if (map->extMft[a] == fs_file->meta->addr)
             continue;
         map->newId[a] = ++nextid;
     }
@@ -2340,12 +2342,12 @@ ntfs_proc_attrlist(NTFS_INFO * ntfs,
                     "Invalid MFT file reference (%"
                     PRIuINUM
                     ") in the unallocated attribute list of MFT %"
-                    PRIuINUM "", mftToDo[a], ntfs->mnum);
+                    PRIuINUM "", mftToDo[a], fs_file->meta->addr);
             }
             continue;
         }
 
-        if ((retval = ntfs_dinode_lookup(ntfs, mft, mftToDo[a])) != TSK_OK) {
+        if ((retval = ntfs_dinode_lookup(ntfs, (char *)mft, mftToDo[a])) != TSK_OK) {
             // if the entry is corrupt, then continue
             if (retval == TSK_COR) {
                 if (tsk_verbose)
@@ -2357,38 +2359,37 @@ ntfs_proc_attrlist(NTFS_INFO * ntfs,
             free(mft);
             free(map);
             free(buf);
-            strncat(tsk_errstr2, " - proc_attrlist",
-                TSK_ERRSTR_L - strlen(tsk_errstr2));
+            tsk_error_errstr2_concat(" - proc_attrlist");
             return 1;
         }
 
         /* verify that this entry refers to the original one */
-        if (tsk_getu48(fs->endian, mft->base_ref) != ntfs->mnum) {
+        if (tsk_getu48(fs->endian, mft->base_ref) != fs_file->meta->addr) {
 
             /* Before we raise alarms, check if the original was
-             * unallocated.  If so, then the list entry could 
+             * unallocated.  If so, then the list entry could
              * have been reallocated, so we will just ignore it
              */
             if ((tsk_getu16(fs->endian,
-                        ntfs->mft->flags) & NTFS_MFT_INUSE) == 0) {
+                        mft->flags) & NTFS_MFT_INUSE) == 0) {
                 continue;
             }
             else {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_INODE_COR;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+                tsk_error_set_errstr(
                     "Extension record %" PRIuINUM
                     " (file ref = %" PRIuINUM
                     ") is not for attribute list of %"
                     PRIuINUM "", mftToDo[a], tsk_getu48(fs->endian,
-                        mft->base_ref), ntfs->mnum);
+                        mft->base_ref), fs_file->meta->addr);
                 free(mft);
                 free(map);
                 free(buf);
                 return 1;
             }
         }
-         
+  
         /* Process the attribute seq for this MFT entry and add them
          * to the TSK_FS_META structure
          */
@@ -2404,8 +2405,7 @@ ntfs_proc_attrlist(NTFS_INFO * ntfs,
                 tsk_error_reset();
                 continue;
             }
-            strncat(tsk_errstr2, "- proc_attrlist",
-                TSK_ERRSTR_L - strlen(tsk_errstr2));
+            tsk_error_errstr2_concat("- proc_attrlist");
             free(mft);
             free(map);
             free(buf);
@@ -2422,31 +2422,33 @@ ntfs_proc_attrlist(NTFS_INFO * ntfs,
 
 
 /**
- * Copy the MFT entry saved in ntfs->mft into the generic structure.
- * 
+ * Copy the MFT entry saved in a_buf to the generic structure.
+ *
  * @param ntfs File system structure that contains entry to copy
  * @param fs_file Structure to copy processed data to.
- * 
+ * @param a_buf MFT structure to copy from. Must be of size NTFS_INFO.mft_rsize_b
+ * @param a_mnum MFT entry address
+ *
  * @returns error code
  */
 static TSK_RETVAL_ENUM
-ntfs_dinode_copy(NTFS_INFO * ntfs, TSK_FS_FILE * a_fs_file)
+ntfs_dinode_copy(NTFS_INFO * ntfs, TSK_FS_FILE * a_fs_file, char *a_buf, TSK_INUM_T a_mnum)
 {
-    ntfs_mft *mft = ntfs->mft;
     ntfs_attr *attr;
     TSK_FS_INFO *fs = (TSK_FS_INFO *) & ntfs->fs_info;
     TSK_RETVAL_ENUM retval;
+    ntfs_mft *mft = (ntfs_mft *)a_buf;
 
     if ((a_fs_file == NULL) || (a_fs_file->meta == NULL)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "ntfs_dinode_copy: NULL fs_file given");
         return TSK_ERR;
     }
 
-    /* if the attributes list has been used previously, then make sure the 
-     * flags are cleared 
+    /* if the attributes list has been used previously, then make sure the
+     * flags are cleared
      */
     if (a_fs_file->meta->attr) {
         tsk_fs_attrlist_markunused(a_fs_file->meta->attr);
@@ -2474,17 +2476,17 @@ ntfs_dinode_copy(NTFS_INFO * ntfs, TSK_FS_FILE * a_fs_file)
     /* Set the a_fs_file->meta values from mft */
     a_fs_file->meta->nlink = tsk_getu16(fs->endian, mft->link);
     a_fs_file->meta->seq = tsk_getu16(fs->endian, mft->seq);
-    a_fs_file->meta->addr = ntfs->mnum;
+    a_fs_file->meta->addr = a_mnum;
 
     /* Set the mode for file or directory */
-    if (tsk_getu16(fs->endian, ntfs->mft->flags) & NTFS_MFT_DIR)
+    if (tsk_getu16(fs->endian, mft->flags) & NTFS_MFT_DIR)
         a_fs_file->meta->type = TSK_FS_META_TYPE_DIR;
     else
         a_fs_file->meta->type = TSK_FS_META_TYPE_REG;
     a_fs_file->meta->mode = 0;  // will be set by proc_attrseq
 
     /* the following will be changed once we find the correct attribute,
-     * but initialize them now just in case 
+     * but initialize them now just in case
      */
     a_fs_file->meta->uid = 0;
     a_fs_file->meta->gid = 0;
@@ -2500,7 +2502,7 @@ ntfs_dinode_copy(NTFS_INFO * ntfs, TSK_FS_FILE * a_fs_file)
 
     /* add the flags */
     a_fs_file->meta->flags =
-        ((tsk_getu16(fs->endian, ntfs->mft->flags) &
+        ((tsk_getu16(fs->endian, mft->flags) &
             NTFS_MFT_INUSE) ? TSK_FS_META_FLAG_ALLOC :
         TSK_FS_META_FLAG_UNALLOC);
 
@@ -2529,27 +2531,6 @@ ntfs_dinode_copy(NTFS_INFO * ntfs, TSK_FS_FILE * a_fs_file)
 }
 
 
-/**
- * Read the mft entry and put it into the local ntfs->mft structure
- * Also sets the ntfs->mnum value. 
- *
- * @param ntfs NTFS file system to get inode from
- * @param mftnum Address of inode to copy
- * @returns TSK_OK, TSK_CORR, or TSK_ERR
- */
-static TSK_RETVAL_ENUM
-ntfs_dinode_load(NTFS_INFO * a_ntfs, TSK_INUM_T a_mftnum)
-{
-    TSK_RETVAL_ENUM retval;
-
-    /* mft_lookup does a sanity check, so we can skip it here */
-    if ((retval =
-            ntfs_dinode_lookup(a_ntfs, a_ntfs->mft, a_mftnum)) != TSK_OK)
-        return retval;
-    a_ntfs->mnum = a_mftnum;
-    return 0;
-}
-
 
 /** \internal
  * Load the attributes.  In NTFS, the attributes are already loaded
@@ -2561,8 +2542,8 @@ static uint8_t
 ntfs_load_attrs(TSK_FS_FILE * a_fs_file)
 {
     if ((a_fs_file == NULL) || (a_fs_file->meta == NULL)) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "ntfs_load_attrs: called with NULL pointers");
         return 1;
     }
@@ -2570,10 +2551,10 @@ ntfs_load_attrs(TSK_FS_FILE * a_fs_file)
     /* Verify the file has attributes */
     if (a_fs_file->meta->attr == NULL) {
         if (a_fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC)
-            tsk_errno = TSK_ERR_FS_RECOVER;
+            tsk_error_set_errno(TSK_ERR_FS_RECOVER);
         else
-            tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "ntfs_load_attrs: attributes are NULL");
         return 1;
     }
@@ -2582,23 +2563,24 @@ ntfs_load_attrs(TSK_FS_FILE * a_fs_file)
 
 /**
  * Read an MFT entry and save it in the generic TSK_FS_META format.
- * 
+ *
  * @param fs File system to read from.
  * @param mftnum Address of mft entry to read
- * @returns 1 on error 
+ * @returns 1 on error
  */
 static uint8_t
 ntfs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file,
     TSK_INUM_T mftnum)
 {
     NTFS_INFO *ntfs = (NTFS_INFO *) fs;
+    char *mft;
 
     // clean up any error messages that are lying around
     tsk_error_reset();
 
     if (a_fs_file == NULL) {
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "ntfs_inode_lookup: fs_file is NULL");
         return 1;
     }
@@ -2619,17 +2601,24 @@ ntfs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file,
         else
             return 0;
     }
+    
+     if ((mft = (char *) tsk_malloc(ntfs->mft_rsize_b)) == NULL) {
+        return 1;
+     }
 
     /* Lookup inode and store it in the ntfs structure */
-    if (ntfs_dinode_load(ntfs, mftnum) != TSK_OK) {
+    if (ntfs_dinode_lookup(ntfs, mft, mftnum) != TSK_OK) {
+        free(mft);
         return 1;
     }
 
     /* Copy the structure in ntfs to generic a_fs_file->meta */
-    if (ntfs_dinode_copy(ntfs, a_fs_file) != TSK_OK) {
+    if (ntfs_dinode_copy(ntfs, a_fs_file, mft, mftnum) != TSK_OK) {
+        free(mft);
         return 1;
     }
 
+    free((char *)mft);
     return 0;
 }
 
@@ -2642,11 +2631,11 @@ ntfs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file,
  *
  **********************************************************************/
 
-/* The attrdef structure defines the types of attributes and gives a 
+/* The attrdef structure defines the types of attributes and gives a
  * name value to the type number.
  *
  * We currently do not use this during the analysis (Because it has not
- * historically changed, but we do display it in fsstat 
+ * historically changed, but we do display it in fsstat
  *
  * Return 1 on error and 0 on success
  */
@@ -2685,8 +2674,7 @@ ntfs_load_attrdef(NTFS_INFO * ntfs)
 
     if (tsk_fs_attr_walk(fs_attr,
             0, tsk_fs_load_file_action, (void *) &load_file)) {
-        strncat(tsk_errstr2, " - load_attrdef",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat(" - load_attrdef");
         tsk_fs_file_close(fs_file);
         free(ntfs->attrdef);
         ntfs->attrdef = NULL;
@@ -2694,8 +2682,8 @@ ntfs_load_attrdef(NTFS_INFO * ntfs)
     }
     else if (load_file.left > 0) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_FWALK;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_FWALK);
+        tsk_error_set_errstr(
             "load_attrdef: space still left after walking $Attr data");
         tsk_fs_file_close(fs_file);
         free(ntfs->attrdef);
@@ -2709,7 +2697,7 @@ ntfs_load_attrdef(NTFS_INFO * ntfs)
 }
 
 
-/* 
+/*
  * return the name of the attribute type.  If the attribute has not
  * been loaded yet, it will be.
  *
@@ -2764,7 +2752,7 @@ ntfs_attrname_lookup(TSK_FS_INFO * fs, uint16_t type, char *name, int len)
 }
 
 
-/* Load the block bitmap $Data run  and allocate a buffer for a cache 
+/* Load the block bitmap $Data run  and allocate a buffer for a cache
  *
  * return 1 on error and 0 on success
  * */
@@ -2774,19 +2762,25 @@ ntfs_load_bmap(NTFS_INFO * ntfs)
     ssize_t cnt;
     ntfs_attr *attr;
     TSK_FS_INFO *fs = &ntfs->fs_info;
+    ntfs_mft *mft;
+
+    if ((mft = (ntfs_mft *) tsk_malloc(ntfs->mft_rsize_b)) == NULL) {
+        return 1;
+    }
 
     /* Get data on the bitmap */
-    if (ntfs_dinode_load(ntfs, NTFS_MFT_BMAP) != TSK_OK) {
+    if (ntfs_dinode_lookup(ntfs, (char *)mft, NTFS_MFT_BMAP) != TSK_OK) {
+        free(mft);
         return 1;
     }
 
-    attr = (ntfs_attr *) ((uintptr_t) ntfs->mft +
-        tsk_getu16(fs->endian, ntfs->mft->attr_off));
+    attr = (ntfs_attr *) ((uintptr_t) mft +
+        tsk_getu16(fs->endian, mft->attr_off));
 
     /* cycle through them */
-    while (((uintptr_t) attr >= (uintptr_t) ntfs->mft)
+    while (((uintptr_t) attr >= (uintptr_t) mft)
         && ((uintptr_t) attr <=
-            ((uintptr_t) ntfs->mft + (uintptr_t) ntfs->mft_rsize_b))
+            ((uintptr_t) mft + (uintptr_t) ntfs->mft_rsize_b))
         && (tsk_getu32(fs->endian, attr->len) > 0
             && (tsk_getu32(fs->endian, attr->type) != 0xffffffff)
             && (tsk_getu32(fs->endian, attr->type) != NTFS_ATYPE_DATA))) {
@@ -2798,9 +2792,10 @@ ntfs_load_bmap(NTFS_INFO * ntfs)
     /* did we get it? */
     if (tsk_getu32(fs->endian, attr->type) != NTFS_ATYPE_DATA) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_INODE_COR;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+        tsk_error_set_errstr(
             "Error Finding Bitmap Data Attribute");
+        free(mft);
         return 1;
     }
 
@@ -2810,12 +2805,14 @@ ntfs_load_bmap(NTFS_INFO * ntfs)
                 (ntfs_runlist
                     *) ((uintptr_t) attr + tsk_getu16(fs->endian,
                         attr->c.nr.run_off)), &(ntfs->bmap),
-                NULL)) != TSK_OK) {
+                NULL, NTFS_MFT_BMAP)) != TSK_OK) {
+        free(mft);
         return 1;
     }
 
     ntfs->bmap_buf = (char *) tsk_malloc(fs->block_size);
     if (ntfs->bmap_buf == NULL) {
+        free(mft);
         return 1;
     }
 
@@ -2823,10 +2820,11 @@ ntfs_load_bmap(NTFS_INFO * ntfs)
     ntfs->bmap_buf_off = 0;
     if (ntfs->bmap->addr > fs->last_block) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_GENFS;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_GENFS);
+        tsk_error_set_errstr(
             "ntfs_load_bmap: Bitmap too large for image size: %"
             PRIuDADDR "", ntfs->bmap->addr);
+        free(mft);
         return 1;
     }
     cnt =
@@ -2835,20 +2833,23 @@ ntfs_load_bmap(NTFS_INFO * ntfs)
     if (cnt != fs->block_size) {
         if (cnt >= 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_READ;
+            tsk_error_set_errno(TSK_ERR_FS_READ);
         }
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
+        tsk_error_set_errstr2(
             "ntfs_load_bmap: Error reading block at %"
             PRIuDADDR, ntfs->bmap->addr);
+        free(mft);
         return 1;
     }
+
+    free((char *)mft);
     return 0;
 }
 
 
 /*
  * Load the VOLUME MFT entry and the VINFO attribute so that we
- * can identify the volume version of this.  
+ * can identify the volume version of this.
  *
  * Return 1 on error and 0 on success
  */
@@ -2867,8 +2868,8 @@ ntfs_load_ver(NTFS_INFO * ntfs)
     fs_attr = tsk_fs_attrlist_get(fs_file->meta->attr, NTFS_ATYPE_VINFO);
     if (!fs_attr) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_INODE_COR;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+        tsk_error_set_errstr(
             "Volume Info attribute not found in $Volume");
         tsk_fs_file_close(fs_file);
         return 1;
@@ -2892,8 +2893,8 @@ ntfs_load_ver(NTFS_INFO * ntfs)
         }
         else {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_GENFS;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_GENFS);
+            tsk_error_set_errstr(
                 "unknown version: %d.%d\n",
                 vinfo->maj_ver, vinfo->min_ver);
             tsk_fs_file_close(fs_file);
@@ -2902,8 +2903,8 @@ ntfs_load_ver(NTFS_INFO * ntfs)
     }
     else {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_GENFS;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_GENFS);
+        tsk_error_set_errstr(
             "load_version: VINFO is a non-resident attribute");
         return 1;
     }
@@ -2916,7 +2917,7 @@ ntfs_load_ver(NTFS_INFO * ntfs)
 #if TSK_USE_SID
 /** \internal
  * Prints the value of sds into the a_sidstr string in ASCII form.  This will allocate a new buffer for the
- * string, so a_sidstr should not point to a buffer. 
+ * string, so a_sidstr should not point to a buffer.
  *
  * @param a_fs File system
  * @param a_sds SDS
@@ -2936,8 +2937,8 @@ ntfs_sds_to_str(TSK_FS_INFO * a_fs, const ntfs_attr_sds * a_sds,
 
     if ((a_fs == NULL) || (a_sds == NULL) || (a_sidstr == NULL)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "Invalid argument");
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr("Invalid argument");
         return 1;
     }
 
@@ -2947,8 +2948,8 @@ ntfs_sds_to_str(TSK_FS_INFO * a_fs, const ntfs_attr_sds * a_sds,
     if (((uintptr_t) & a_sds->self_rel_sec_desc + owner_offset) >
         ((uintptr_t) a_sds + tsk_getu32(a_fs->endian, a_sds->ent_size))) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_INODE_COR;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+        tsk_error_set_errstr(
             "ntfs_sds_to_str: owner offset larger than a_sds length");
         return 1;
     }
@@ -3000,8 +3001,8 @@ ntfs_sds_to_str(TSK_FS_INFO * a_fs, const ntfs_attr_sds * a_sds,
     }
     else {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_GENFS;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_GENFS);
+        tsk_error_set_errstr(
             "ntfs_sds_to_str: Invalid SID revision (%d)", sid->revision);
         return 1;               // Invalid revision number in the SID.
     }
@@ -3015,11 +3016,13 @@ ntfs_sds_to_str(TSK_FS_INFO * a_fs, const ntfs_attr_sds * a_sds,
 /** \internal
  * Maps a security id value from a file to its SDS structure
  *
+ * Note: This routine assumes &ntfs->sid_lock is locked by the caller.
+ *
  * @param fs File system
- * @param secid Security Id to find SDS for. 
- * @returns NULL on error 
+ * @param secid Security Id to find SDS for.
+ * @returns NULL on error
  */
-const ntfs_attr_sds *
+static const ntfs_attr_sds *
 ntfs_get_sds(TSK_FS_INFO * fs, uint32_t secid)
 {
     uint32_t i = 0;
@@ -3038,17 +3041,17 @@ ntfs_get_sds(TSK_FS_INFO * fs, uint32_t secid)
 
     if ((fs == NULL) || (secid == 0)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "Invalid argument");
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr("Invalid argument");
         return NULL;
     }
 
 
     // Loop through all the SII entries looking for the security id matching that found in the file.
     // This lookup is obviously O(n^2) for all n files. However, since so many files have the exact
-    // same security identifier, it is not really that bad. In reality, 100,000 files may only map to 
-    // 10,000 security identifiers. Since SII entries are 0x28 bytes each and security identifiers 
-    // increase incrementally, we could go directly to the entry in question ((secid * 0x28) + 256). 
+    // same security identifier, it is not really that bad. In reality, 100,000 files may only map to
+    // 10,000 security identifiers. Since SII entries are 0x28 bytes each and security identifiers
+    // increase incrementally, we could go directly to the entry in question ((secid * 0x28) + 256).
     // SII entries started at 256 on Vista; however, I did not look at the starting secid for other
     // versions of NTFS.
     for (i = 0; i < ntfs->sii_data.used; i++) {
@@ -3062,8 +3065,8 @@ ntfs_get_sds(TSK_FS_INFO * fs, uint32_t secid)
 
     if (sii == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_GENFS;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_GENFS);
+        tsk_error_set_errstr(
             "ntfs_get_sds: SII entry not found (%" PRIu32 ")", secid);
         return NULL;
     }
@@ -3076,16 +3079,16 @@ ntfs_get_sds(TSK_FS_INFO * fs, uint32_t secid)
     // Check that we do not go out of bounds.
     if ((uint32_t) sii_sds_file_off > ntfs->sds_data.size) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_GENFS;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_GENFS);
+        tsk_error_set_errstr(
             "ntfs_get_sds: SII offset too large (%" PRIu64 ")",
             sii_sds_file_off);
         return NULL;
     }
     else if (!sii_sds_ent_size) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_GENFS;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_GENFS);
+        tsk_error_set_errstr(
             "ntfs_get_sds: SII entry size is invalid (%" PRIu32 ")",
             sii_sds_ent_size);
         return NULL;
@@ -3118,8 +3121,8 @@ ntfs_get_sds(TSK_FS_INFO * fs, uint32_t secid)
     }
 
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_GENFS;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_FS_GENFS);
+    tsk_error_set_errstr(
         "ntfs_get_sds: Got to end w/out data");
     return NULL;
 }
@@ -3127,16 +3130,16 @@ ntfs_get_sds(TSK_FS_INFO * fs, uint32_t secid)
 
 /** \internal
  * NTFS-specific function (pointed to in FS_INFO) that maps a security ID
- * to an ASCII printable string. 
- * Read the contents of the STANDARD_INFORMATION attribute of a file 
+ * to an ASCII printable string.
+ * Read the contents of the STANDARD_INFORMATION attribute of a file
  * to get the security id. Once we have the security id, we will
  * search $Secure:$SII to find a matching security id. That $SII entry
- * will contain the offset within the $SDS stream for the $SDS entry, 
+ * will contain the offset within the $SDS stream for the $SDS entry,
  * which contains the owner SID
  *
  * @param a_fs_file File to get security info on
  * @param sid_str [out] location where string representation of security info will be stored.
- Caller must free the string. 
+ Caller must free the string.
  * @returns 1 on error
  */
 static uint8_t
@@ -3146,13 +3149,14 @@ ntfs_file_get_sidstr(TSK_FS_FILE * a_fs_file, char **sid_str)
     const TSK_FS_ATTR *fs_data;
     ntfs_attr_si *si;
     const ntfs_attr_sds *sds;
+    NTFS_INFO *ntfs = (NTFS_INFO*)a_fs_file->fs_info;
 
     *sid_str = NULL;
 
     if (!a_fs_file->meta->attr) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_GENFS;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_GENFS);
+        tsk_error_set_errstr(
             "ntfs_file_get_sidstr: file argument has no meta data");
         return 1;
     }
@@ -3161,7 +3165,7 @@ ntfs_file_get_sidstr(TSK_FS_FILE * a_fs_file, char **sid_str)
     fs_data = tsk_fs_attrlist_get(a_fs_file->meta->attr,
         TSK_FS_ATTR_TYPE_NTFS_SI);
     if (!fs_data) {
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
+        tsk_error_set_errstr2(
             "- ntfs_file_get_sidstr:SI attribute");
         return 1;
     }
@@ -3169,37 +3173,46 @@ ntfs_file_get_sidstr(TSK_FS_FILE * a_fs_file, char **sid_str)
     si = (ntfs_attr_si *) fs_data->rd.buf;
     if (!si) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_GENFS;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_GENFS);
+        tsk_error_set_errstr(
             "ntfs_file_get_sidstr: SI buf is NULL");
         return 1;
     }
+
+    tsk_take_lock(&ntfs->sid_lock);
+    // sds points inside ntfs->sds_data, which we've just locked
     sds =
         ntfs_get_sds(a_fs_file->fs_info,
         tsk_getu32(a_fs_file->fs_info->endian, si->sec_id));
     if (!sds) {
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
+        tsk_release_lock(&ntfs->sid_lock);
+        tsk_error_set_errstr2(
             "- ntfs_file_get_sidstr:SI attribute");
         return 1;
     }
     if (ntfs_sds_to_str(a_fs_file->fs_info, sds, sid_str)) {
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
+        tsk_release_lock(&ntfs->sid_lock);
+        tsk_error_set_errstr2(
             "- ntfs_file_get_sidstr:SI attribute");
         return 1;
     }
+    tsk_release_lock(&ntfs->sid_lock);
     return 0;
 #else
     *sid_str = NULL;
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-    snprintf(tsk_errstr, TSK_ERRSTR_L, "Unsupported function");
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr("Unsupported function");
     return 1;
 #endif
 }
 
 
 #if TSK_USE_SID
-void
+/*
+ * Note: This routine assumes &ntfs->sid_lock is locked by the caller.
+ */
+static void
 ntfs_proc_sii(TSK_FS_INFO * fs, NTFS_SXX_BUFFER * sii_buffer)
 {
     unsigned int total_bytes_processed = 0;
@@ -3230,7 +3243,7 @@ ntfs_proc_sii(TSK_FS_INFO * fs, NTFS_SXX_BUFFER * sii_buffer)
             (uint8_t) ((uintptr_t) sii - (uintptr_t) idxrec);
 
         do {
-/*		
+/*
 			if ((tsk_getu16(fs->endian,sii->size) == 0x14) &&
 				(tsk_getu16(fs->endian,sii->data_off) == 0x14) &&
 				(tsk_getu16(fs->endian,sii->ent_size) == 0x28)
@@ -3244,17 +3257,17 @@ ntfs_proc_sii(TSK_FS_INFO * fs, NTFS_SXX_BUFFER * sii_buffer)
             ntfs->sii_data.used += 1;
 
 /*
-				printf("Security id %d is at offset 0x%I64x for 0x%x bytes\n", tsk_getu32(fs->endian,sii->key_sec_id), 
-																		   tsk_getu64(fs->endian,sii->sec_desc_off), 
+				printf("Security id %d is at offset 0x%I64x for 0x%x bytes\n", tsk_getu32(fs->endian,sii->key_sec_id),
+																		   tsk_getu64(fs->endian,sii->sec_desc_off),
 																		   tsk_getu32(fs->endian,sii->sec_desc_size));
 			}
 			else
 			{
-				printf("\n\tOffset to data %x Size of data %x Size of Index entry %x\n", tsk_getu16(fs->endian,sii->data_off), 
-																					 tsk_getu16(fs->endian,sii->size), 
+				printf("\n\tOffset to data %x Size of data %x Size of Index entry %x\n", tsk_getu16(fs->endian,sii->data_off),
+																					 tsk_getu16(fs->endian,sii->size),
 																					 tsk_getu16(fs->endian,sii->ent_size));
-				printf("\tSecurity id %d is at offset 0x%I64x for 0x%x bytes\n\n", tsk_getu32(fs->endian,sii->key_sec_id), 
-																		   tsk_getu64(fs->endian,sii->sec_desc_off), 
+				printf("\tSecurity id %d is at offset 0x%I64x for 0x%x bytes\n\n", tsk_getu32(fs->endian,sii->key_sec_id),
+																		   tsk_getu64(fs->endian,sii->sec_desc_off),
 																		   tsk_getu32(fs->endian,sii->sec_desc_size));
 			}
 */
@@ -3268,7 +3281,11 @@ ntfs_proc_sii(TSK_FS_INFO * fs, NTFS_SXX_BUFFER * sii_buffer)
 
 /*
  * Load the $Secure attributes so that we can identify the user.
- * @returns 1 on error (which occurs only if malloc or other system error). 
+ *
+ * Note: This routine is called only from ntfs_open and therefore does
+ * not need to lock ntfs->sid_lock.
+ *
+ * @returns 1 on error (which occurs only if malloc or other system error).
  */
 static uint8_t
 ntfs_load_secure(NTFS_INFO * ntfs)
@@ -3297,7 +3314,7 @@ ntfs_load_secure(NTFS_INFO * ntfs)
         if (tsk_verbose)
             tsk_fprintf(stderr,
                 "ntfs_load_secure: error opening $Secure file: %s\n",
-                tsk_errstr);
+                tsk_error_get_errstr());
         tsk_error_reset();
         return 0;
     }
@@ -3342,7 +3359,7 @@ ntfs_load_secure(NTFS_INFO * ntfs)
     if ((sii_buffer.buffer = tsk_malloc(sii_buffer.size)) == NULL) {
         return 1;
     }
-    // We will use this structure to store the raw $SII entries since 
+    // We will use this structure to store the raw $SII entries since
     // they are a fixed size (x28 bytes)
     ntfs->sii_data.size = 0;
     ntfs->sii_data.used = 0;    // use this to count the number of $SII entries
@@ -3352,7 +3369,7 @@ ntfs_load_secure(NTFS_INFO * ntfs)
         return 1;
     }
 
-    // Allocate space for the entire $SDS stream with all the security 
+    // Allocate space for the entire $SDS stream with all the security
     // descriptors. We should be able to use the $SII offset to index
     // into the $SDS stream.
     sds_buffer.size = (size_t) roundup(fs_attr->size, fs->block_size);
@@ -3375,7 +3392,7 @@ ntfs_load_secure(NTFS_INFO * ntfs)
         if (tsk_verbose)
             tsk_fprintf(stderr,
                 "ntfs_load_secure: error reading $Secure:$SII attribute: %s\n",
-                tsk_errstr);
+                tsk_error_get_errstr());
         tsk_error_reset();
 
         free(sii_buffer.buffer);
@@ -3396,7 +3413,7 @@ ntfs_load_secure(NTFS_INFO * ntfs)
         if (tsk_verbose)
             tsk_fprintf(stderr,
                 "ntfs_load_secure: error reading $Secure:$SDS attribute: %s\n",
-                tsk_errstr);
+                tsk_error_get_errstr());
         tsk_error_reset();
 
         free(sii_buffer.buffer);
@@ -3431,7 +3448,7 @@ ntfs_load_secure(NTFS_INFO * ntfs)
  **********************************************************************/
 
 
-TSK_FS_BLOCK_FLAG_ENUM
+static TSK_FS_BLOCK_FLAG_ENUM
 ntfs_block_getflags(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr)
 {
     NTFS_INFO *ntfs = (NTFS_INFO *) a_fs;
@@ -3456,7 +3473,7 @@ ntfs_block_getflags(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr)
  * @@@ We should probably consider some data META, but it is tough with
  * the NTFS design ...
  */
-uint8_t
+static uint8_t
 ntfs_block_walk(TSK_FS_INFO * fs,
     TSK_DADDR_T a_start_blk, TSK_DADDR_T a_end_blk,
     TSK_FS_BLOCK_WALK_FLAG_ENUM a_flags, TSK_FS_BLOCK_WALK_CB a_action,
@@ -3475,15 +3492,15 @@ ntfs_block_walk(TSK_FS_INFO * fs,
      */
     if (a_start_blk < fs->first_block || a_start_blk > fs->last_block) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "%s: start block: %" PRIuDADDR "", myname, a_start_blk);
         return 1;
     }
     else if (a_end_blk < fs->first_block || a_end_blk > fs->last_block) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "%s: last block: %" PRIuDADDR "", myname, a_end_blk);
         return 1;
     }
@@ -3534,7 +3551,7 @@ ntfs_block_walk(TSK_FS_INFO * fs,
             continue;
 
         if (tsk_fs_block_get(fs, fs_block, addr) == NULL) {
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
+            tsk_error_set_errstr2(
                 "ntfs_block_walk: Error reading block at %"
                 PRIuDADDR, addr);
             tsk_fs_block_free(fs_block);
@@ -3560,16 +3577,16 @@ ntfs_block_walk(TSK_FS_INFO * fs,
 /*
  * inode_walk
  *
- * Flags: TSK_FS_META_FLAG_ALLOC, TSK_FS_META_FLAG_UNALLOC, 
+ * Flags: TSK_FS_META_FLAG_ALLOC, TSK_FS_META_FLAG_UNALLOC,
  * TSK_FS_META_FLAG_USED, TSK_FS_META_FLAG_UNUSED, TSK_FS_META_FLAG_ORPHAN
  *
  * Note that with ORPHAN, entries will be found that can also be
  * found by searching based on parent directories (if parent directory is
  * known)
  */
-uint8_t
+static uint8_t
 ntfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
-    TSK_INUM_T end_inum, TSK_FS_META_FLAG_ENUM flags,
+    TSK_INUM_T end_inum, TSK_FS_META_FLAG_ENUM flags, 
     TSK_FS_META_WALK_CB a_action, void *ptr)
 {
     NTFS_INFO *ntfs = (NTFS_INFO *) fs;
@@ -3577,38 +3594,38 @@ ntfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
     TSK_INUM_T mftnum;
     TSK_FS_FILE *fs_file;
     TSK_INUM_T end_inum_tmp;
-
+    ntfs_mft * mft;
     /*
      * Sanity checks.
      */
     if (start_inum < fs->first_inum) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "inode_walk: Starting inode number is too small (%"
             PRIuINUM ")", start_inum);
         return 1;
     }
     if (start_inum > fs->last_inum) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "inode_walk: Starting inode number is too large (%"
             PRIuINUM ")", start_inum);
         return 1;
     }
     if (end_inum < fs->first_inum) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "inode_walk: Ending inode number is too small (%"
             PRIuINUM ")", end_inum);
         return 1;
     }
     if (end_inum > fs->last_inum) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "Ending inode number is too large (%" PRIuINUM ")", end_inum);
         return 1;
     }
@@ -3642,12 +3659,10 @@ ntfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
      * in the list of unalloc inodes that are pointed to, then fill
      * in the list
      * */
-    if ((flags & TSK_FS_META_FLAG_ORPHAN)
-        && (fs->list_inum_named == NULL)) {
+    if ((flags & TSK_FS_META_FLAG_ORPHAN)) {
         if (tsk_fs_dir_load_inum_named(fs) != TSK_OK) {
-            strncat(tsk_errstr2,
-                " - ntfs_inode_walk: identifying inodes allocated by file names",
-                TSK_ERRSTR_L);
+            tsk_error_errstr2_concat(
+                "- ntfs_inode_walk: identifying inodes allocated by file names");
             return 1;
         }
     }
@@ -3656,13 +3671,17 @@ ntfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
         return 1;
 
     if ((fs_file->meta = tsk_fs_meta_alloc(NTFS_FILE_CONTENT_LEN)) == NULL) {
-        // JRB: Coverity CID: 348 
+        // JRB: Coverity CID: 348
         if (fs_file)
             tsk_fs_file_close(fs_file);
         return 1;
     }
 
-    // we need to handle fs->last_inum specially because it is for the 
+    if ((mft = (ntfs_mft *) tsk_malloc(ntfs->mft_rsize_b)) == NULL){
+       tsk_fs_file_close(fs_file);
+       return 1;
+    }
+    // we need to handle fs->last_inum specially because it is for the
     // virtual ORPHANS directory.  Handle it outside of the loop.
     if (end_inum == TSK_FS_ORPHANDIR_INUM(fs))
         end_inum_tmp = end_inum - 1;
@@ -3675,7 +3694,7 @@ ntfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
         TSK_RETVAL_ENUM retval2;
 
         /* read MFT entry in to NTFS_INFO */
-        if ((retval2 = ntfs_dinode_load(ntfs, mftnum)) != TSK_OK) {
+        if ((retval2 = ntfs_dinode_lookup(ntfs, (char *)mft, mftnum)) != TSK_OK) {
             // if the entry is corrupt, then skip to the next one
             if (retval2 == TSK_COR) {
                 if (tsk_verbose)
@@ -3684,13 +3703,14 @@ ntfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
                 continue;
             }
             tsk_fs_file_close(fs_file);
+            free(mft);
             return 1;
         }
 
-        /* we only want to look at base file records 
+        /* we only want to look at base file records
          * (extended are because the base could not fit into one)
          */
-        if (tsk_getu48(fs->endian, ntfs->mft->base_ref) != NTFS_MFT_BASE)
+        if (tsk_getu48(fs->endian, mft->base_ref) != NTFS_MFT_BASE)
             continue;
 
         /* NOTE: We could add a sanity check here with the MFT bitmap
@@ -3698,7 +3718,7 @@ ntfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
          */
         /* check flags */
         myflags =
-            ((tsk_getu16(fs->endian, ntfs->mft->flags) &
+            ((tsk_getu16(fs->endian, mft->flags) &
                 NTFS_MFT_INUSE) ? TSK_FS_META_FLAG_ALLOC :
             TSK_FS_META_FLAG_UNALLOC);
 
@@ -3707,12 +3727,12 @@ ntfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
          * */
         if ((myflags & TSK_FS_META_FLAG_UNALLOC) &&
             (flags & TSK_FS_META_FLAG_ORPHAN) &&
-            (tsk_list_find(fs->list_inum_named, mftnum))) {
+            (tsk_fs_dir_find_inum_named(fs, mftnum))) {
             continue;
         }
 
         /* copy into generic format */
-        if ((retval = ntfs_dinode_copy(ntfs, fs_file)) != TSK_OK) {
+        if ((retval = ntfs_dinode_copy(ntfs, fs_file, (char *)mft, mftnum)) != TSK_OK) {
             // continue on if there were only corruption problems
             if (retval == TSK_COR) {
                 if (tsk_verbose)
@@ -3721,6 +3741,7 @@ ntfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
                 continue;
             }
             tsk_fs_file_close(fs_file);
+            free(mft);
             return 1;
         }
 
@@ -3734,10 +3755,12 @@ ntfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
         retval = a_action(fs_file, ptr);
         if (retval == TSK_WALK_STOP) {
             tsk_fs_file_close(fs_file);
+            free(mft);
             return 0;
         }
         else if (retval == TSK_WALK_ERROR) {
             tsk_fs_file_close(fs_file);
+            free(mft);
             return 1;
         }
     }
@@ -3750,21 +3773,25 @@ ntfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
 
         if (tsk_fs_dir_make_orphan_dir_meta(fs, fs_file->meta)) {
             tsk_fs_file_close(fs_file);
+            free(mft);
             return 1;
         }
         /* call action */
         retval = a_action(fs_file, ptr);
         if (retval == TSK_WALK_STOP) {
             tsk_fs_file_close(fs_file);
+            free(mft);
             return 0;
         }
         else if (retval == TSK_WALK_ERROR) {
             tsk_fs_file_close(fs_file);
+            free(mft);
             return 1;
         }
     }
 
     tsk_fs_file_close(fs_file);
+    free((char *)mft);
     return 0;
 }
 
@@ -3774,19 +3801,19 @@ static uint8_t
 ntfs_fscheck(TSK_FS_INFO * fs, FILE * hFile)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr(
         "fscheck not implemented for NTFS yet");
     return 1;
 }
 
 
 /**
- * Print details about the file system to a file handle. 
+ * Print details about the file system to a file handle.
  *
  * @param fs File system to print details on
  * @param hFile File handle to print text to
- * 
+ *
  * @returns 1 on error and 0 on success
  */
 static uint8_t
@@ -3812,21 +3839,20 @@ ntfs_fsstat(TSK_FS_INFO * fs, FILE * hFile)
         ntfs->fs->oemname[4],
         ntfs->fs->oemname[5], ntfs->fs->oemname[6], ntfs->fs->oemname[7]);
     /*
-     * Volume 
+     * Volume
      */
     if ((fs_file = tsk_fs_file_open_meta(fs, NULL, NTFS_MFT_VOL)) == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_INODE_NUM;
-        strncat(tsk_errstr2, " - fsstat: Error finding Volume MFT Entry",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
+        tsk_error_errstr2_concat(" - fsstat: Error finding Volume MFT Entry");
         return 1;
     }
 
     fs_attr = tsk_fs_attrlist_get(fs_file->meta->attr, NTFS_ATYPE_VNAME);
     if (!fs_attr) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_INODE_COR;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+        tsk_error_set_errstr(
             "Volume Name attribute not found in $Volume");
         return 1;
     }
@@ -3900,8 +3926,8 @@ ntfs_fsstat(TSK_FS_INFO * fs, FILE * hFile)
     tsk_fprintf(hFile,
         "Total Sector Range: 0 - %" PRIu64
         "\n", tsk_getu64(fs->endian, ntfs->fs->vol_size_s) - 1);
-    /* 
-     * Attrdef Info 
+    /*
+     * Attrdef Info
      */
     tsk_fprintf(hFile, "\n$AttrDef Attribute Values:\n");
     if (!ntfs->attrdef) {
@@ -3992,30 +4018,39 @@ print_addr_act(TSK_FS_FILE * fs_file, TSK_OFF_T a_off, TSK_DADDR_T addr,
 }
 
 /**
- * Print details on a specific file to a file handle. 
+ * Print details on a specific file to a file handle.
  *
  * @param fs File system file is located in
  * @param hFile File name to print text to
  * @param inum Address of file in file system
  * @param numblock The number of blocks in file to force print (can go beyond file size)
  * @param sec_skew Clock skew in seconds to also print times in
- * 
+ *
  * @returns 1 on error and 0 on success
  */
 static uint8_t
-ntfs_istat(TSK_FS_INFO * fs, FILE * hFile,
+ntfs_istat(TSK_FS_INFO * fs, FILE * hFile, 
     TSK_INUM_T inum, TSK_DADDR_T numblock, int32_t sec_skew)
 {
     TSK_FS_FILE *fs_file;
     const TSK_FS_ATTR *fs_attr;
     NTFS_INFO *ntfs = (NTFS_INFO *) fs;
-
+    ntfs_mft *mft;
     // clean up any error messages that are lying around
     tsk_error_reset();
+    
+    if ((mft = (ntfs_mft *)tsk_malloc(ntfs->mft_rsize_b)) == NULL){
+        return 1;
+    }
+
+    if (ntfs_dinode_lookup(ntfs, (char *)mft, inum)) {
+        free(mft);
+        return 1;
+    }
 
     if ((fs_file = tsk_fs_file_open_meta(fs, NULL, inum)) == NULL) {
-        strncat(tsk_errstr2, " - istat",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat(" - istat");
+        free(mft);
         return 1;
     }
 
@@ -4023,15 +4058,15 @@ ntfs_istat(TSK_FS_INFO * fs, FILE * hFile,
     tsk_fprintf(hFile,
         "Entry: %" PRIuINUM
         "        Sequence: %" PRIu32 "\n", inum, fs_file->meta->seq);
-    if (tsk_getu48(fs->endian, ntfs->mft->base_ref) != 0) {
+    if (tsk_getu48(fs->endian, mft->base_ref) != 0) {
         tsk_fprintf(hFile,
             "Base File Record: %" PRIu64 "\n",
-            (uint64_t) tsk_getu48(fs->endian, ntfs->mft->base_ref));
+            (uint64_t) tsk_getu48(fs->endian, mft->base_ref));
     }
 
     tsk_fprintf(hFile,
         "$LogFile Sequence Number: %" PRIu64
-        "\n", tsk_getu64(fs->endian, ntfs->mft->lsn));
+        "\n", tsk_getu64(fs->endian, mft->lsn));
     tsk_fprintf(hFile, "%sAllocated %s\n",
         (fs_file->meta->flags & TSK_FS_META_FLAG_ALLOC) ? "" :
         "Not ",
@@ -4207,8 +4242,8 @@ ntfs_istat(TSK_FS_INFO * fs, FILE * hFile,
             "   \tActual Size: %" PRIu64 "\n",
             tsk_getu64(fs->endian, fname->alloc_fsize),
             tsk_getu64(fs->endian, fname->real_fsize));
-        /* 
-         * Times 
+        /*
+         * Times
          */
         cr_time = nt2unixtime(tsk_getu64(fs->endian, fname->crtime));
         /* altered - modified */
@@ -4321,6 +4356,7 @@ ntfs_istat(TSK_FS_INFO * fs, FILE * hFile,
         load_file.cur = load_file.base = buf =
             tsk_malloc((size_t) fs_attr->size);
         if (buf == NULL) {
+            free(mft);
             return 1;
         }
 
@@ -4428,6 +4464,7 @@ ntfs_istat(TSK_FS_INFO * fs, FILE * hFile,
     }
 
     tsk_fs_file_close(fs_file);
+    free(mft);
     return 0;
 }
 
@@ -4435,35 +4472,35 @@ ntfs_istat(TSK_FS_INFO * fs, FILE * hFile,
 
 /* JOURNAL CODE - MOVE TO NEW FILE AT SOME POINT */
 
-uint8_t
+static uint8_t
 ntfs_jopen(TSK_FS_INFO * fs, TSK_INUM_T inum)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr(
         "NTFS Journal is not yet supported\n");
     return 1;
 }
 
-uint8_t
+static uint8_t
 ntfs_jentry_walk(TSK_FS_INFO * fs, int flags,
     TSK_FS_JENTRY_WALK_CB a_action, void *ptr)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr(
         "NTFS Journal is not yet supported\n");
     return 1;
 }
 
 
-uint8_t
+static uint8_t
 ntfs_jblk_walk(TSK_FS_INFO * fs, TSK_DADDR_T start,
     TSK_DADDR_T end, int flags, TSK_FS_JBLK_WALK_CB a_action, void *ptr)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_FS_UNSUPFUNC;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
+    tsk_error_set_errstr(
         "NTFS Journal is not yet supported\n");
     return 1;
 }
@@ -4504,25 +4541,26 @@ ntfs_close(TSK_FS_INFO * fs)
 #endif
 
     fs->tag = 0;
-    free((char *) ntfs->mft);
     free((char *) ntfs->fs);
     tsk_fs_attr_run_free(ntfs->bmap);
     free(ntfs->bmap_buf);
     tsk_fs_file_close(ntfs->mft_file);
 
-    if (fs->list_inum_named) {
-        tsk_list_free(fs->list_inum_named);
-        fs->list_inum_named = NULL;
-    }
     if (ntfs->orphan_map)
         ntfs_orphan_map_free(ntfs);
 
-    free(fs);
+    tsk_deinit_lock(&ntfs->lock);
+    tsk_deinit_lock(&ntfs->orphan_map_lock);
+#if TSK_USE_SID
+    tsk_deinit_lock(&ntfs->sid_lock);
+#endif
+
+    tsk_fs_free(fs);
 }
 
 
 /**
- * Open part of a disk image as an NTFS file system. 
+ * Open part of a disk image as an NTFS file system.
  *
  * @param img_info Disk image to analyze
  * @param offset Byte offset where NTFS file system starts
@@ -4545,12 +4583,12 @@ ntfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
 
     if (TSK_FS_TYPE_ISNTFS(ftype) == 0) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "Invalid FS type in ntfs_open");
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr("Invalid FS type in ntfs_open");
         return NULL;
     }
 
-    if ((ntfs = (NTFS_INFO *) tsk_malloc(sizeof(*ntfs))) == NULL) {
+    if ((ntfs = (NTFS_INFO *) tsk_fs_malloc(sizeof(*ntfs))) == NULL) {
         return NULL;
     }
     fs = &(ntfs->fs_info);
@@ -4580,9 +4618,9 @@ ntfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
     if (cnt != len) {
         if (cnt >= 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_READ;
+            tsk_error_set_errno(TSK_ERR_FS_READ);
         }
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
+        tsk_error_set_errstr2(
             "%s: Error reading boot sector.", myname);
         fs->tag = 0;
         free(ntfs->fs);
@@ -4596,8 +4634,8 @@ ntfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
         free(ntfs->fs);
         free(ntfs);
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr(
             "Not a NTFS file system (magic)");
         return NULL;
     }
@@ -4614,8 +4652,8 @@ ntfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
         free(ntfs->fs);
         free(ntfs);
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr(
             "Not a NTFS file system (invalid sector size)");
         return NULL;
     }
@@ -4631,8 +4669,8 @@ ntfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
         free(ntfs->fs);
         free(ntfs);
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr(
             "Not a NTFS file system (invalid cluster size)");
         return NULL;
     }
@@ -4650,8 +4688,8 @@ ntfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
         free(ntfs->fs);
         free(ntfs);
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr(
             "Not a NTFS file system (volume size is 0)");
         return NULL;
     }
@@ -4677,8 +4715,8 @@ ntfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
         free(ntfs->fs);
         free(ntfs);
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr(
             "Not a NTFS file system (invalid MFT entry size)");
         return NULL;
     }
@@ -4694,8 +4732,8 @@ ntfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
         free(ntfs->fs);
         free(ntfs);
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr(
             "Not a NTFS file system (invalid idx record size)");
         return NULL;
     }
@@ -4707,8 +4745,8 @@ ntfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
         free(ntfs->fs);
         free(ntfs);
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
+        tsk_error_set_errstr(
             "Not a NTFS file system (invalid starting MFT clust)");
         return NULL;
     }
@@ -4743,16 +4781,6 @@ ntfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
      * inode
      */
 
-    /* allocate the buffer to hold mft entries */
-    ntfs->mft = (ntfs_mft *) tsk_malloc(ntfs->mft_rsize_b);
-    if (ntfs->mft == NULL) {
-        fs->tag = 0;
-        free(ntfs->fs);
-        free(ntfs);
-        return NULL;
-    }
-
-    ntfs->mnum = 0;
     fs->root_inum = NTFS_ROOTINO;
     fs->first_inum = NTFS_FIRSTINO;
     fs->last_inum = NTFS_LAST_DEFAULT_INO;
@@ -4763,13 +4791,12 @@ ntfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
     if ((ntfs->mft_file =
             tsk_fs_file_open_meta(fs, NULL, NTFS_MFT_MFT)) == NULL) {
         fs->tag = 0;
-        free(ntfs->mft);
         free(ntfs->fs);
         free(ntfs);
         return NULL;
     }
 
-    /* cache the data attribute 
+    /* cache the data attribute
      *
      * This will likely be done already by proc_attrseq, but this
      * should be quick
@@ -4779,11 +4806,9 @@ ntfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
     if (!ntfs->mft_data) {
         fs->tag = 0;
         tsk_fs_file_close(ntfs->mft_file);
-        free(ntfs->mft);
         free(ntfs->fs);
         free(ntfs);
-        strncat(tsk_errstr2, " - Data Attribute not found in $MFT",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat(" - Data Attribute not found in $MFT");
         return NULL;
     }
 
@@ -4803,7 +4828,6 @@ ntfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
     if (ntfs_load_ver(ntfs)) {
         fs->tag = 0;
         tsk_fs_file_close(ntfs->mft_file);
-        free(ntfs->mft);
         free(ntfs->fs);
         free(ntfs);
         return NULL;
@@ -4813,7 +4837,6 @@ ntfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
     if (ntfs_load_bmap(ntfs)) {
         fs->tag = 0;
         tsk_fs_file_close(ntfs->mft_file);
-        free(ntfs->mft);
         free(ntfs->fs);
         free(ntfs);
         return NULL;
@@ -4824,7 +4847,6 @@ ntfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
     if (ntfs_load_secure(ntfs)) {
         fs->tag = 0;
         tsk_fs_file_close(ntfs->mft_file);
-        free(ntfs->mft);
         free(ntfs->fs);
         free(ntfs);
         return NULL;
@@ -4834,7 +4856,6 @@ ntfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
 
     // initialize the caches
     ntfs->attrdef = NULL;
-    fs->list_inum_named = NULL;
     ntfs->orphan_map = NULL;
 
 
@@ -4853,5 +4874,11 @@ ntfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
                 ntfs->fs->mftm_clust));
     }
 
+    tsk_init_lock(&ntfs->lock);
+    tsk_init_lock(&ntfs->orphan_map_lock);
+#if TSK_USE_SID
+    tsk_init_lock(&ntfs->sid_lock);
+#endif
+
     return fs;
 }
diff --git a/tsk3/fs/ntfs_dent.c b/tsk3/fs/ntfs_dent.c
index ad0a7727c..5b8f4d584 100644
--- a/tsk3/fs/ntfs_dent.c
+++ b/tsk3/fs/ntfs_dent.c
@@ -5,7 +5,7 @@
 ** name layer support for the NTFS file system
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2009 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
 **
 ** TASK
@@ -76,10 +76,13 @@ ntfs_orphan_map_alloc()
 
 /** \internal
  * Add a parent and child pair to the map stored in NTFS_INFO
+ *
+ * Note: This routine assumes &ntfs->orhpan_map_lock is locked by the caller.
+ *
  * @param ntfs structure to add the pair to
  * @param par Parent address
  * @param child Child address
- * @returns 1 on error 
+ * @returns 1 on error
  */
 static uint8_t
 ntfs_orphan_map_add(NTFS_INFO * ntfs, TSK_INUM_T par, TSK_INUM_T child)
@@ -141,10 +144,13 @@ ntfs_orphan_map_add(NTFS_INFO * ntfs, TSK_INUM_T par, TSK_INUM_T child)
 }
 
 /** \internal
- * Look up a map entry by the parent address. 
+ * Look up a map entry by the parent address.
+ *
+ * Note: This routine assumes &ntfs->orhpan_map_lock is locked by the caller.
+ *
  * @param ntfs File system that has already been analyzed
  * @param par Parent inode to find child files for
- * @returns NULL on error 
+ * @returns NULL on error
  */
 static NTFS_PAR_MAP *
 ntfs_orphan_map_get(NTFS_INFO * ntfs, TSK_INUM_T par)
@@ -168,8 +174,17 @@ ntfs_orphan_map_free(NTFS_INFO * a_ntfs)
 {
     NTFS_PAR_MAP *tmp = NULL;
 
-    if (a_ntfs->orphan_map == NULL)
+    // This routine is only called from ntfs_close, so it wouldn't
+    // normally need a lock.  However, it's an extern function, so be
+    // safe in case someone else calls it.  (Perhaps it's extern by
+    // mistake?)
+
+    tsk_take_lock(&a_ntfs->orphan_map_lock);
+
+    if (a_ntfs->orphan_map == NULL) {
+        tsk_release_lock(&a_ntfs->orphan_map_lock);
         return;
+    }
 
     tmp = a_ntfs->orphan_map;
     while (tmp) {
@@ -180,6 +195,7 @@ ntfs_orphan_map_free(NTFS_INFO * a_ntfs)
         tmp = tmp2;
     }
     a_ntfs->orphan_map = NULL;
+    tsk_release_lock(&a_ntfs->orphan_map_lock);
 }
 
 
@@ -256,7 +272,7 @@ ntfs_dent_copy(NTFS_INFO * ntfs, ntfs_idxentry * idxe,
 
 
 /* This is a sanity check to see if the time is valid
- * it is divided by 100 to keep it in a 32-bit integer 
+ * it is divided by 100 to keep it in a 32-bit integer
  */
 
 static uint8_t
@@ -281,7 +297,7 @@ is_time(uint64_t t)
 
 
 
-/** 
+/**
  * Process a lsit of index entries and add to FS_DIR
  *
  * @param a_is_del Set to 1 if these entries are for a deleted directory
@@ -316,8 +332,8 @@ ntfs_proc_idxentry(NTFS_INFO * a_ntfs, TSK_FS_DIR * a_fs_dir,
     /* Sanity check */
     if (a_idxe_len < a_used_len) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "ntfs_proc_idxentry: Allocated length of index entries is larger than buffer length");
         return TSK_ERR;
     }
@@ -402,13 +418,13 @@ ntfs_proc_idxentry(NTFS_INFO * a_ntfs, TSK_FS_DIR * a_fs_dir,
             }
         }
 
-        /* For all fname entries, there will exist a DOS style 8.3 
+        /* For all fname entries, there will exist a DOS style 8.3
          * entry.  We don't process those because we already processed
-         * them before in their full version.  If the type is 
-         * full POSIX or WIN32 that does not satisfy DOS, then a 
+         * them before in their full version.  If the type is
+         * full POSIX or WIN32 that does not satisfy DOS, then a
          * type NTFS_FNAME_DOS will exist.  If the name is WIN32,
          * but already satisfies DOS, then a type NTFS_FNAME_WINDOS
-         * will exist 
+         * will exist
          *
          * Note that we could be missing some info from deleted files
          * if the windows version was deleted and the DOS wasn't...
@@ -434,10 +450,10 @@ ntfs_proc_idxentry(NTFS_INFO * a_ntfs, TSK_FS_DIR * a_fs_dir,
             goto incr_entry;
         }
 
-        /* 
+        /*
          * Check if this entry is deleted
          *
-         * The final check is to see if the end of this entry is 
+         * The final check is to see if the end of this entry is
          * within the space that the idxallocbuf claimed was valid OR
          * if the parent directory is deleted
          */
@@ -501,8 +517,8 @@ ntfs_proc_idxentry(NTFS_INFO * a_ntfs, TSK_FS_DIR * a_fs_dir,
 
 
 /*
- * remove the update sequence values that are changed in the last two 
- * bytes of each sector 
+ * remove the update sequence values that are changed in the last two
+ * bytes of each sector
  *
  * return 1 on error and 0 on success
  */
@@ -523,8 +539,8 @@ ntfs_fix_idxrec(NTFS_INFO * ntfs, ntfs_idxrec * idxrec, uint32_t len)
     if ((unsigned int) ((tsk_getu16(fs->endian, idxrec->upd_cnt) - 1) *
             ntfs->ssize_b) > len) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_INODE_COR;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+        tsk_error_set_errstr(
             "fix_idxrec: More Update Sequence Entries than idx record size");
         return 1;
     }
@@ -554,8 +570,8 @@ ntfs_fix_idxrec(NTFS_INFO * ntfs, ntfs_idxrec * idxrec, uint32_t len)
                 tsk_getu16(fs->endian, &upd->upd_seq + (i - 1) * 2);
 
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_INODE_COR;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+            tsk_error_set_errstr(
                 "fix_idxrec: Incorrect update sequence value in index buffer\nUpdate Value: 0x%"
                 PRIx16 " Actual Value: 0x%" PRIx16
                 " Replacement Value: 0x%" PRIx16
@@ -587,15 +603,15 @@ ntfs_fix_idxrec(NTFS_INFO * ntfs, ntfs_idxrec * idxrec, uint32_t len)
 /** \internal
 * Process a directory and load up FS_DIR with the entries. If a pointer to
 * an already allocated FS_DIR struture is given, it will be cleared.  If no existing
-* FS_DIR structure is passed (i.e. NULL), then a new one will be created. If the return 
-* value is error or corruption, then the FS_DIR structure could  
-* have entries (depending on when the error occured). 
+* FS_DIR structure is passed (i.e. NULL), then a new one will be created. If the return
+* value is error or corruption, then the FS_DIR structure could
+* have entries (depending on when the error occured).
 *
 * @param a_fs File system to analyze
 * @param a_fs_dir Pointer to FS_DIR pointer. Can contain an already allocated
-* structure or a new structure. 
+* structure or a new structure.
 * @param a_addr Address of directory to process.
-* @returns error, corruption, ok etc. 
+* @returns error, corruption, ok etc.
 */
 TSK_RETVAL_ENUM
 ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
@@ -617,7 +633,7 @@ ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
 
     /* In this function, we will return immediately if we get an error.
      * If we get corruption though, we will record that in 'retval_final'
-     * and continue processing. 
+     * and continue processing.
      */
     TSK_RETVAL_ENUM retval_final = TSK_OK;
     TSK_RETVAL_ENUM retval_tmp;
@@ -625,15 +641,15 @@ ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
     /* sanity check */
     if (a_addr < a_fs->first_inum || a_addr > a_fs->last_inum) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
+        tsk_error_set_errstr(
             "ntfs_dir_open_meta: inode value: %" PRIuINUM "\n", a_addr);
         return TSK_ERR;
     }
     else if (a_fs_dir == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "ntfs_dir_open_meta: NULL fs_attr argument given");
         return TSK_ERR;
     }
@@ -661,22 +677,21 @@ ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
     /* Get the inode and verify it has attributes */
     if ((fs_dir->fs_file =
             tsk_fs_file_open_meta(a_fs, NULL, a_addr)) == NULL) {
-        strncat(tsk_errstr2, " - ntfs_dir_open_meta",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- ntfs_dir_open_meta");
         return TSK_COR;
     }
 
     if (!(fs_dir->fs_file->meta->attr)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_INODE_COR;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+        tsk_error_set_errstr(
             "dent_walk: Error: Directory address %" PRIuINUM
             " has no attributes", a_addr);
         return TSK_COR;
     }
 
 
-    /* 
+    /*
      * Read the Index Root Attribute  -- we do some sanity checking here
      * to report errors before we start to make up data for the "." and ".."
      * entries
@@ -685,15 +700,14 @@ ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
         tsk_fs_attrlist_get(fs_dir->fs_file->meta->attr,
         NTFS_ATYPE_IDXROOT);
     if (!fs_attr_root) {
-        strncat(tsk_errstr2, " - dent_walk: $IDX_ROOT not found",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat(" - dent_walk: $IDX_ROOT not found");
         return TSK_COR;
     }
 
     if (fs_attr_root->flags & TSK_FS_ATTR_NONRES) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_INODE_COR;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+        tsk_error_set_errstr(
             "dent_walk: $IDX_ROOT is not resident - it should be");
         return TSK_COR;
     }
@@ -702,15 +716,15 @@ ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
     /* Verify that the attribute type is $FILE_NAME */
     if (tsk_getu32(a_fs->endian, idxroot->type) == 0) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_INODE_COR;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+        tsk_error_set_errstr(
             "dent_walk: Attribute type in index root is 0");
         return TSK_COR;
     }
     else if (tsk_getu32(a_fs->endian, idxroot->type) != NTFS_ATYPE_FNAME) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_INODE_COR;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+        tsk_error_set_errstr(
             "ERROR: Directory index is sorted by type: %" PRIu32
             ".\nOnly $FNAME is currently supported",
             tsk_getu32(a_fs->endian, idxroot->type));
@@ -724,10 +738,10 @@ ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
     idxe = (ntfs_idxentry *) ((uintptr_t) idxelist +
         tsk_getu32(a_fs->endian, idxelist->begin_off));
 
-    /* 
+    /*
      * NTFS does not have "." and ".." entries in the index trees
      * (except for a "." entry in the root directory)
-     * 
+     *
      * So, we'll make 'em up by making a TSK_FS_NAME structure for
      * a '.' and '..' entry and call the action
      */
@@ -742,8 +756,8 @@ ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
         if ((fs_name = tsk_fs_name_alloc(16, 0)) == NULL) {
             return TSK_ERR;
         }
-        /* 
-         * "." 
+        /*
+         * "."
          */
         fs_name->meta_addr = a_addr;
         fs_name->meta_seq = fs_dir->fs_file->meta->seq;
@@ -763,7 +777,7 @@ ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
         strcpy(fs_name->name, "..");
         fs_name->type = TSK_FS_NAME_TYPE_DIR;
 
-        /* The fs_name structure holds the parent inode value, so we 
+        /* The fs_name structure holds the parent inode value, so we
          * just cycle using those
          */
         for (fs_name_list = fs_dir->fs_file->meta->name2;
@@ -779,7 +793,7 @@ ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
         tsk_fs_name_free(fs_name);
         fs_name = NULL;
     }
-    
+
 
     /* Now we return to processing the Index Root Attribute */
     if (tsk_verbose)
@@ -797,8 +811,8 @@ ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
             ((uintptr_t) fs_attr_root->rd.buf +
                 fs_attr_root->rd.buf_size))) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_INODE_COR;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+        tsk_error_set_errstr(
             "Error: Index list offsets are invalid on entry: %" PRIuINUM,
             fs_dir->fs_file->meta->addr);
         return TSK_COR;
@@ -820,9 +834,9 @@ ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
         retval_final = TSK_COR;
     }
 
-    /* 
-     * get the index allocation attribute if it exists (it doesn't for 
-     * small directories 
+    /*
+     * get the index allocation attribute if it exists (it doesn't for
+     * small directories
      */
     fs_attr_idx =
         tsk_fs_attrlist_get(fs_dir->fs_file->meta->attr,
@@ -830,14 +844,14 @@ ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
 
 
     /* if we don't have an index alloc then return, we have processed
-     * all of the entries 
+     * all of the entries
      */
     if (!fs_attr_idx) {
         if (tsk_getu32(a_fs->endian,
                 idxelist->flags) & NTFS_IDXELIST_CHILD) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_INODE_COR;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+            tsk_error_set_errstr(
                 "Error: $IDX_ROOT says there should be children, but there isn't");
             return TSK_COR;
         }
@@ -846,13 +860,13 @@ ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
 
         if (fs_attr_idx->flags & TSK_FS_ATTR_RES) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_INODE_COR;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+            tsk_error_set_errstr(
                 "$IDX_ALLOC is Resident - it shouldn't be");
             return TSK_COR;
         }
 
-        /* 
+        /*
          * Copy the index allocation run into a big buffer
          */
         idxalloc_len = fs_attr_idx->nrd.allocsize;
@@ -872,8 +886,7 @@ ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
                 TSK_FS_FILE_WALK_FLAG_SLACK, tsk_fs_load_file_action,
                 (void *) &load_file)) {
             free(idxalloc);
-            strncat(tsk_errstr2, " - ntfs_dir_open_meta",
-                TSK_ERRSTR_L - strlen(tsk_errstr2));
+            tsk_error_errstr2_concat(" - ntfs_dir_open_meta");
             return TSK_COR;     // this could be an error though
         }
 
@@ -882,8 +895,8 @@ ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
             free(idxalloc);
 
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_FWALK;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_FWALK);
+            tsk_error_set_errstr(
                 "Error reading directory contents: %" PRIuINUM "\n",
                 a_addr);
             return TSK_COR;
@@ -891,7 +904,7 @@ ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
 
         /*
          * The idxalloc is a big buffer that contains one or more
-         * idx buffer structures.  Each idxrec is a node in the B-Tree.  
+         * idx buffer structures.  Each idxrec is a node in the B-Tree.
          * We do not process the tree as a tree because then we could
          * not find the deleted file names.
          *
@@ -923,7 +936,7 @@ ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
                 continue;
 
 
-            /* idxrec_p is only NULL for the first time 
+            /* idxrec_p is only NULL for the first time
              * Set it and start again to find the next one */
             if (idxrec_p == NULL) {
                 idxrec_p = idxrec;
@@ -967,8 +980,8 @@ ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
                     tsk_getu32(a_fs->endian,
                         idxelist->seqend_off) > (uintptr_t) idxrec)) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_INODE_COR;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+                tsk_error_set_errstr(
                     "Error: Index list offsets are invalid on entry: %"
                     PRIuINUM, fs_dir->fs_file->meta->addr);
                 free(idxalloc);
@@ -1033,8 +1046,8 @@ ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
                     tsk_getu32(a_fs->endian, idxelist->seqend_off) >
                     (uintptr_t) idxalloc + idxalloc_len)) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_INODE_COR;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+                tsk_error_set_errstr(
                     "Error: Index list offsets are invalid on entry: %"
                     PRIuINUM, fs_dir->fs_file->meta->addr);
                 free(idxalloc);
@@ -1064,9 +1077,11 @@ ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
 
     // get the orphan files
     // load and cache the map if it has not already been done
+    tsk_take_lock(&ntfs->orphan_map_lock);
     if (ntfs->orphan_map == NULL) {
         if (a_fs->inode_walk(a_fs, a_fs->first_inum, a_fs->last_inum,
                 TSK_FS_META_FLAG_UNALLOC, ntfs_orphan_act, NULL)) {
+            tsk_release_lock(&ntfs->orphan_map_lock);
             return TSK_ERR;
         }
     }
@@ -1085,7 +1100,7 @@ ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
         fs_name->type = TSK_FS_NAME_TYPE_UNDEF;
 
         for (a = 0; a < map->used_cnt; a++) {
-            /* Fill in the basics of the fs_name entry 
+            /* Fill in the basics of the fs_name entry
              * so we can print in the fls formats */
             fs_name->meta_addr = map->addrs[a];
 
@@ -1107,6 +1122,7 @@ ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
         }
         tsk_fs_name_free(fs_name);
     }
+    tsk_release_lock(&ntfs->orphan_map_lock);
 
     // if we are listing the root directory, add the Orphan directory entry
     if (a_addr == a_fs->root_inum) {
@@ -1156,8 +1172,8 @@ typedef struct {
 } NTFS_DINFO;
 
 
-/* 
- * Looks up the parent inode described in fs_name. 
+/*
+ * Looks up the parent inode described in fs_name.
  *
  * fs_name was filled in by ntfs_find_file and will get the final path
  * added to it before action is called
@@ -1180,8 +1196,8 @@ ntfs_find_file_rec(TSK_FS_INFO * fs, NTFS_DINFO * dinfo,
     if (fs_name_list->par_inode < fs->first_inum ||
         fs_name_list->par_inode > fs->last_inum) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "invalid inode value: %" PRIuINUM "\n",
             fs_name_list->par_inode);
         return 1;
@@ -1189,15 +1205,14 @@ ntfs_find_file_rec(TSK_FS_INFO * fs, NTFS_DINFO * dinfo,
 
     fs_file_par = tsk_fs_file_open_meta(fs, NULL, fs_name_list->par_inode);
     if (fs_file_par == NULL) {
-        strncat(tsk_errstr2, " - ntfs_find_file_rec",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat(" - ntfs_find_file_rec");
         return 1;
     }
 
-    /* 
+    /*
      * Orphan File
      * This occurs when the file is deleted and either:
-     * - The parent is no longer a directory 
+     * - The parent is no longer a directory
      * - The sequence number of the parent is no longer correct
      */
     if ((fs_file_par->meta->type != TSK_FS_META_TYPE_DIR)
@@ -1205,7 +1220,7 @@ ntfs_find_file_rec(TSK_FS_INFO * fs, NTFS_DINFO * dinfo,
         char *str = TSK_FS_ORPHAN_STR;
         len = strlen(str);
 
-        /* @@@ There should be a sanity check here to verify that the 
+        /* @@@ There should be a sanity check here to verify that the
          * previous name was unallocated ... but how do I get it again?
          */
         if ((((uintptr_t) dinfo->didx[dinfo->depth - 1] - len) >=
@@ -1236,7 +1251,7 @@ ntfs_find_file_rec(TSK_FS_INFO * fs, NTFS_DINFO * dinfo,
 
         len = strlen(fs_name_list_par->name);
 
-        /* do some length checks on the dir structure 
+        /* do some length checks on the dir structure
          * if we can't fit it then forget about it */
         if ((((uintptr_t) dinfo->didx[dinfo->depth - 1] - len - 1) >=
                 (uintptr_t) & dinfo->dirs[0])
@@ -1259,11 +1274,11 @@ ntfs_find_file_rec(TSK_FS_INFO * fs, NTFS_DINFO * dinfo,
 
 
         /* if we are at the root, then fill out the rest of fs_name with
-         * the full path and call the action 
+         * the full path and call the action
          */
         if (fs_name_list_par->par_inode == NTFS_ROOTINO) {
             /* increase the path by one so that we do not pass the '/'
-             * if we do then the printed result will have '//' at 
+             * if we do then the printed result will have '//' at
              * the beginning
              */
             if (TSK_WALK_ERROR == action(fs_file,
@@ -1292,8 +1307,8 @@ ntfs_find_file_rec(TSK_FS_INFO * fs, NTFS_DINFO * dinfo,
     return 0;
 }
 
-/* 
- * this is a much faster way of doing it in NTFS 
+/*
+ * this is a much faster way of doing it in NTFS
  *
  * the inode that is passed in this case is the one to find the name
  * for
@@ -1309,27 +1324,36 @@ ntfs_find_file(TSK_FS_INFO * fs, TSK_INUM_T inode_toid, uint32_t type_toid,
     void *ptr)
 {
     TSK_FS_META_NAME_LIST *fs_name_list;
-    NTFS_INFO *ntfs = (NTFS_INFO *) fs;
     char *attr = NULL;
     NTFS_DINFO dinfo;
     TSK_FS_FILE *fs_file;
+    ntfs_mft *mft;
+    TSK_RETVAL_ENUM r_enum;
+    NTFS_INFO *ntfs = (NTFS_INFO *)fs;
 
     /* sanity check */
     if (inode_toid < fs->first_inum || inode_toid > fs->last_inum) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_ARG);
+        tsk_error_set_errstr(
             "ntfs_find_file: invalid inode value: %" PRIuINUM "\n",
             inode_toid);
         return 1;
     }
-
+    if ((mft = (ntfs_mft *) tsk_malloc(ntfs->mft_rsize_b)) == NULL) {
+        return 1;
+    }
+    r_enum = ntfs_dinode_lookup(ntfs, (char *)mft, inode_toid);
+    if (r_enum == TSK_ERR){
+        free(mft);
+        return 1;
+    }
     // open the file to ID
     fs_file = tsk_fs_file_open_meta(fs, NULL, inode_toid);
     if (fs_file == NULL) {
-        strncat(tsk_errstr2, " - ntfs_find_file",
-            TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_error_errstr2_concat("- ntfs_find_file");
         tsk_fs_file_close(fs_file);
+        free(mft);
         return 1;
     }
 
@@ -1337,11 +1361,13 @@ ntfs_find_file(TSK_FS_INFO * fs, TSK_INUM_T inode_toid, uint32_t type_toid,
     if ((fs_file->meta->flags & TSK_FS_META_FLAG_ALLOC)
         && ((dir_walk_flags & TSK_FS_DIR_WALK_FLAG_ALLOC) == 0)) {
         tsk_fs_file_close(fs_file);
+        free(mft);
         return 1;
     }
     else if ((fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC)
         && ((dir_walk_flags & TSK_FS_DIR_WALK_FLAG_UNALLOC) == 0)) {
         tsk_fs_file_close(fs_file);
+        free(mft);
         return 1;
     }
 
@@ -1349,14 +1375,14 @@ ntfs_find_file(TSK_FS_INFO * fs, TSK_INUM_T inode_toid, uint32_t type_toid,
     /* Allocate a name and fill in some details  */
     if ((fs_file->name =
             tsk_fs_name_alloc(NTFS_MAXNAMLEN_UTF8, 0)) == NULL) {
+        free(mft);
         return 1;
     }
     fs_file->name->meta_addr = inode_toid;
     fs_file->name->meta_seq = 0;
     fs_file->name->flags =
         ((tsk_getu16(fs->endian,
-                ntfs->mft->
-                flags) & NTFS_MFT_INUSE) ? TSK_FS_NAME_FLAG_ALLOC :
+                mft->flags) & NTFS_MFT_INUSE) ? TSK_FS_NAME_FLAG_ALLOC :
         TSK_FS_NAME_FLAG_UNALLOC);
 
     memset(&dinfo, 0, sizeof(NTFS_DINFO));
@@ -1387,12 +1413,13 @@ ntfs_find_file(TSK_FS_INFO * fs, TSK_INUM_T inode_toid, uint32_t type_toid,
 
         if (!fs_attr) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_FS_INODE_COR;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+            tsk_error_set_errstr(
                 "find_file: Type %" PRIu32 " Id %" PRIu16
                 " not found in MFT %" PRIuINUM "", type_toid, id_toid,
                 inode_toid);
             tsk_fs_file_close(fs_file);
+            free(mft);
             return 1;
         }
 
@@ -1422,10 +1449,12 @@ ntfs_find_file(TSK_FS_INFO * fs, TSK_INUM_T inode_toid, uint32_t type_toid,
             retval = action(fs_file, dinfo.didx[0], ptr);
             if (retval == TSK_WALK_STOP) {
                 tsk_fs_file_close(fs_file);
+                free(mft);
                 return 0;
             }
             else if (retval == TSK_WALK_ERROR) {
                 tsk_fs_file_close(fs_file);
+                free(mft);
                 return 1;
             }
         }
@@ -1434,12 +1463,14 @@ ntfs_find_file(TSK_FS_INFO * fs, TSK_INUM_T inode_toid, uint32_t type_toid,
             if (ntfs_find_file_rec(fs, &dinfo, fs_file, fs_name_list,
                     action, ptr)) {
                 tsk_fs_file_close(fs_file);
+                free(mft);
                 return 1;
             }
         }
     }                           /* end of name loop */
 
     tsk_fs_file_close(fs_file);
+    free(mft);
     return 0;
 }
 
diff --git a/tsk3/fs/rawfs.c b/tsk3/fs/rawfs.c
index 13bea92f4..79142761d 100644
--- a/tsk3/fs/rawfs.c
+++ b/tsk3/fs/rawfs.c
@@ -2,7 +2,7 @@
 ** The Sleuth Kit
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2004-2005 Brian Carrier.  All rights reserved 
 **
 **
@@ -41,7 +41,7 @@ rawfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset)
     // clean up any error messages that are lying around
     tsk_error_reset();
 
-    fs = (TSK_FS_INFO *) tsk_malloc(sizeof(TSK_FS_INFO));
+    fs = (TSK_FS_INFO *) tsk_fs_malloc(sizeof(TSK_FS_INFO));
     if (fs == NULL)
         return NULL;
 
diff --git a/tsk3/fs/swapfs.c b/tsk3/fs/swapfs.c
index a2ea14c9c..722d2636b 100644
--- a/tsk3/fs/swapfs.c
+++ b/tsk3/fs/swapfs.c
@@ -2,7 +2,7 @@
 ** The Sleuth Kit 
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
 ** Copyright (c) 2004-2005 Brian Carrier.  All rights reserved 
 **
 **
@@ -40,7 +40,7 @@ swapfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset)
     // clean up any error messages that are lying around
     tsk_error_reset();
 
-    fs = (TSK_FS_INFO *) tsk_malloc(sizeof(*fs));
+    fs = (TSK_FS_INFO *) tsk_fs_malloc(sizeof(*fs));
     if (fs == NULL)
         return NULL;
 
diff --git a/tsk3/fs/tsk_ext2fs.h b/tsk3/fs/tsk_ext2fs.h
index 433424fea..e7fc7ef49 100644
--- a/tsk3/fs/tsk_ext2fs.h
+++ b/tsk3/fs/tsk_ext2fs.h
@@ -2,7 +2,7 @@
 ** The Sleuth Kit 
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2003-2008 Brian Carrier.  All rights reserved
+** Copyright (c) 2003-2011 Brian Carrier.  All rights reserved
 **
 ** TASK
 ** Copyright (c) 2002 Brian Carrier, @stake Inc.  All rights reserved
@@ -409,17 +409,17 @@ extern "C" {
         TSK_FS_INFO fs_info;    /* super class */
         ext2fs_sb *fs;          /* super block */
 
-        ext2fs_gd *grp_buf;     /* cached group descriptor */
-        EXT2_GRPNUM_T grp_num;  /* cached group number */
+        /* lock protects grp_buf, grp_num, bmap_buf, bmap_grp_num, imap_buf, imap_grp_num */
+        tsk_lock_t lock;
 
-        uint8_t *bmap_buf;      /* cached block allocation bitmap */
-        EXT2_GRPNUM_T bmap_grp_num;     /* cached block bitmap nr */
+        ext2fs_gd *grp_buf;     /* cached group descriptor r/w shared - lock */ 
+        EXT2_GRPNUM_T grp_num;  /* cached group number r/w shared - lock */
 
-        uint8_t *imap_buf;      /* cached inode allocation bitmap */
-        EXT2_GRPNUM_T imap_grp_num;     /* cached inode bitmap nr */
+        uint8_t *bmap_buf;      /* cached block allocation bitmap r/w shared - lock */
+        EXT2_GRPNUM_T bmap_grp_num;     /* cached block bitmap nr r/w shared - lock*/
 
-        ext2fs_inode *dino_buf; /* cached disk inode */
-        TSK_INUM_T dino_inum;   /* cached inode number */
+        uint8_t *imap_buf;      /* cached inode allocation bitmap r/w shared - lock*/
+        EXT2_GRPNUM_T imap_grp_num;     /* cached inode bitmap nr r/w shared - lock */
 
         TSK_OFF_T groups_offset;        /* offset to first group desc */
         EXT2_GRPNUM_T groups_count;     /* nr of descriptor group blocks */
diff --git a/tsk3/fs/tsk_fatfs.h b/tsk3/fs/tsk_fatfs.h
index 5c8bdbe82..9681dfa95 100644
--- a/tsk3/fs/tsk_fatfs.h
+++ b/tsk3/fs/tsk_fatfs.h
@@ -2,7 +2,7 @@
 ** The Sleuth Kit 
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2003-2008 Brian Carrier.  All rights reserved
+** Copyright (c) 2003-2011 Brian Carrier.  All rights reserved
 **
 ** TASK
 ** Copyright (c) 2002 @stake Inc.  All rights reserved
@@ -302,17 +302,14 @@ extern "C" {
         //TSK_DATA_BUF *table;      /* cached section of file allocation table */
 
         /* FAT cache */
-        char fatc_buf[FAT_CACHE_N][FAT_CACHE_B];
-        TSK_DADDR_T fatc_addr[FAT_CACHE_N];
-        uint8_t fatc_ttl[FAT_CACHE_N];  // ttl of 0 means is not in use
+        /* cache_lock protects fatc_buf, fatc_addr, fatc_ttl */
+        tsk_lock_t cache_lock;
+        char fatc_buf[FAT_CACHE_N][FAT_CACHE_B]; //r/w shared - lock
+        TSK_DADDR_T fatc_addr[FAT_CACHE_N]; // r/w shared - lock
+        uint8_t fatc_ttl[FAT_CACHE_N]; //r/w shared - lock
 
-
-        char *dinodes;          /* cluster size buffer of inode list */
         fatfs_sb *sb;
 
-        fatfs_dentry *dep;
-
-
         /* FIrst sector of FAT */
         TSK_DADDR_T firstfatsect;
 
@@ -346,10 +343,12 @@ extern "C" {
         uint16_t numroot;       /* number of 32-byte dentries in root dir */
         uint32_t mask;          /* the mask to use for the sectors */
 
-        TSK_INUM_T *dir_buf;    // array that holds inode address of directories
-        TSK_INUM_T *par_buf;    // array that holds parent directory address of corresponding dir_buf entry
-        size_t dir_buf_size;    // number of entries in both dif_buf and par_buf
-        size_t dir_buf_next;    // index to the next place to store an address in dir_buf and par_buf
+        /* dir_lock protects dir_buf, par_buf, dir_buf_size, dir_buf_next */
+        tsk_lock_t dir_lock;
+        TSK_INUM_T *dir_buf;    // array that holds inode address of directories r/w shared - lock
+        TSK_INUM_T *par_buf;    // array that holds parent directory address of corresponding dir_buf entry r/w shared - lock
+        size_t dir_buf_size;    // number of entries in both dif_buf and par_buf r/w shared - lock
+        size_t dir_buf_next;    // index to the next place to store an address in dir_buf and par_buf r/w shared - lock
     } FATFS_INFO;
 
 
@@ -361,6 +360,7 @@ extern "C" {
     extern uint8_t fatfs_make_root(FATFS_INFO *, TSK_FS_META *);
     extern TSK_RETVAL_ENUM fatfs_dinode_copy(FATFS_INFO *, TSK_FS_META *,
         fatfs_dentry *, TSK_DADDR_T, TSK_INUM_T);
+    extern uint8_t fatfs_dinode_load(TSK_FS_INFO *, fatfs_dentry *, TSK_INUM_T);
 
     extern uint8_t fatfs_inode_lookup(TSK_FS_INFO * fs,
         TSK_FS_FILE * a_fs_file, TSK_INUM_T inum);
diff --git a/tsk3/fs/tsk_ffs.h b/tsk3/fs/tsk_ffs.h
index 7a176b80b..08f553415 100644
--- a/tsk3/fs/tsk_ffs.h
+++ b/tsk3/fs/tsk_ffs.h
@@ -2,7 +2,7 @@
 ** The Sleuth Kit 
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2003-2008 Brian Carrier.  All rights reserved
+** Copyright (c) 2003-2011 Brian Carrier.  All rights reserved
 **
 ** TASK
 ** Copyright (c) 2002 @stake Inc.  All rights reserved
@@ -309,6 +309,14 @@ extern "C" {
         uint8_t f2[24];         /* s32 */
     } ffs_inode2;
 
+    typedef struct {
+        union {
+            ffs_inode1 in1;
+            ffs_inode1b in1b;
+            ffs_inode2 in2;
+        } in;
+    } ffs_inode;
+
 #define FFS_IN_FMT       0170000        /* Mask of file type. */
 #define FFS_IN_FIFO      0010000        /* Named pipe (fifo). */
 #define FFS_IN_CHR       0020000        /* Character device. */
@@ -473,15 +481,15 @@ extern "C" {
             ffs_sb2 *sb2;       /* super block buffer */
         } fs;
 
-        char *dino_buf;         /* cached disk inode */
-        TSK_INUM_T dino_inum;   /* address of cached disk inode */
+        /* lock protects itbl_buf, itbl_addr, grp_buf, grp_num, grp_addr */
+        tsk_lock_t lock;
 
-        char *itbl_buf;         ///< Cached inode block buffer
-        TSK_DADDR_T itbl_addr;  ///< Address where inode block buf was read from:w
+        char *itbl_buf;         ///< Cached inode block buffer (r/w shared - lock)
+        TSK_DADDR_T itbl_addr;  ///< Address where inode block buf was read from (r/w shared - lock)
 
-        char *grp_buf;          ///< Cached cylinder group buffer
-        FFS_GRPNUM_T grp_num;   ///< Cyl grp num that is cached
-        TSK_DADDR_T grp_addr;   ///< Address where cached cyl grp data was read from
+        char *grp_buf;          ///< Cached cylinder group buffer (r/w shared - lock)
+        FFS_GRPNUM_T grp_num;   ///< Cyl grp num that is cached (r/w shared - lock)
+        TSK_DADDR_T grp_addr;   ///< Address where cached cyl grp data was read from (r/w shared - lock)
 
         FFS_GRPNUM_T groups_count;      /* nr of descriptor group blocks */
 
diff --git a/tsk3/fs/tsk_fs.h b/tsk3/fs/tsk_fs.h
index 96197d3e0..a8c9f6351 100644
--- a/tsk3/fs/tsk_fs.h
+++ b/tsk3/fs/tsk_fs.h
@@ -2,7 +2,7 @@
 ** The Sleuth Kit 
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2003-2008 Brian Carrier.  All rights reserved
+** Copyright (c) 2003-2011 Brian Carrier.  All rights reserved
 **
 ** TASK
 ** Copyright (c) 2002 @stake Inc.  All rights reserved
@@ -12,10 +12,10 @@
 */
 
 /** \file tsk_fs.h
- * External header file for file system support.
- * Note that this file is not meant to be directly included.  
- * It is included by both libtsk.h and tsk_fs_i.h.
- */
+* External header file for file system support.
+* Note that this file is not meant to be directly included.  
+* It is included by both libtsk.h and tsk_fs_i.h.
+*/
 
 /* LICENSE
 * .ad
@@ -29,7 +29,8 @@
 --*/
 
 /**
- * \defgroup fslib File System Functions
+* \defgroup fslib C File System Functions
+* \defgroup fslib_cpp C++ File System Classes
  */
 
 #ifndef _TSK_FS_H
@@ -47,17 +48,17 @@ extern "C" {
 
 
 
-/**************** BLOCK Structure *******************/
+    /**************** BLOCK Structure *******************/
 
     /** \name Generic File System Block Data Structure */
     //@{
 
     /** Flags that are used in TSK_FS_BLOCK and in callback of file_walk. 
-        * Note that some of these are dependent. A block can be either TSK_FS_BLOCK_FLAG_ALLOC
-        * or TSK_FS_BLOCK_FLAG_UNALLOC.  It can be one of TSK_FS_BLOCK_FLAG_RAW, TSK_FS_BLOCK_FLAG_BAD,
-        * TSK_FS_BLOCK_FLAG_RES, TSK_FS_BLOCK_FLAG_SPARSE, or TSK_FS_BLOCK_FLAG_COMP.  Note that some of 
-        * these are set only by file_walk because they are file-level details, such as compression and sparse.
-        */
+    * Note that some of these are dependent. A block can be either TSK_FS_BLOCK_FLAG_ALLOC
+    * or TSK_FS_BLOCK_FLAG_UNALLOC.  It can be one of TSK_FS_BLOCK_FLAG_RAW, TSK_FS_BLOCK_FLAG_BAD,
+    * TSK_FS_BLOCK_FLAG_RES, TSK_FS_BLOCK_FLAG_SPARSE, or TSK_FS_BLOCK_FLAG_COMP.  Note that some of 
+    * these are set only by file_walk because they are file-level details, such as compression and sparse.
+    */
     enum TSK_FS_BLOCK_FLAG_ENUM {
         TSK_FS_BLOCK_FLAG_UNUSED = 0x0000,      ///< Used to show that TSK_FS_BLOCK structure has no data in it
         TSK_FS_BLOCK_FLAG_ALLOC = 0x0001,       ///< Block is allocated (and not TSK_FS_BLOCK_FLAG_UNALLOC)
@@ -75,7 +76,7 @@ extern "C" {
 
     /**
     * Flags that are used to specify which blocks to call the tsk_fs_block_walk() callback function with.
-     */
+    */
     enum TSK_FS_BLOCK_WALK_FLAG_ENUM {
         TSK_FS_BLOCK_WALK_FLAG_NONE = 0x00,     ///< No Flags
         TSK_FS_BLOCK_WALK_FLAG_ALLOC = 0x01,    ///< Allocated blocks
@@ -101,11 +102,11 @@ extern "C" {
 
     /**
     * Function definition used for callback to tsk_fs_block_walk(). 
-     *
-     * @param a_block Pointer to block structure that holds block content and flags
-     * @param a_ptr Pointer that was supplied by the caller who called tsk_fs_block_walk
-     * @returns Value to identify if walk should continue, stop, or stop because of error
-     */
+    *
+    * @param a_block Pointer to block structure that holds block content and flags
+    * @param a_ptr Pointer that was supplied by the caller who called tsk_fs_block_walk
+    * @returns Value to identify if walk should continue, stop, or stop because of error
+    */
     typedef TSK_WALK_RET_ENUM(*TSK_FS_BLOCK_WALK_CB) (const TSK_FS_BLOCK *
         a_block, void *a_ptr);
 
@@ -121,18 +122,18 @@ extern "C" {
 
     //@}
 
-/**************** DATA and DATA_LIST Structures ************/
+    /**************** DATA and DATA_LIST Structures ************/
 
     /** \name Generic File System File Content Data Structures */
     //@{
 
     /* The location of "most" file content is stored in the generic TSK 
-     * data structures as runs (starting address and length). 
-     */
+    * data structures as runs (starting address and length). 
+    */
 
     /** 
-     * Flags used for a TSK_FS_ATTR_RUN entry. 
-     */
+    * Flags used for a TSK_FS_ATTR_RUN entry. 
+    */
     typedef enum {
         TSK_FS_ATTR_RUN_FLAG_NONE = 0x00,       ///< No Flag
         TSK_FS_ATTR_RUN_FLAG_FILLER = 0x01,     ///< Entry is a filler for a run that has not been seen yet in the processing (or has been lost)
@@ -143,11 +144,11 @@ extern "C" {
     typedef struct TSK_FS_ATTR_RUN TSK_FS_ATTR_RUN;
 
     /**
-     * Holds information about a single data run, which has a starting address and length.
-     * A run describes a consecutive list of blocks that have been allocated to a file. 
-     * A file may have many such runs and they are stringed together in a linked list.
-     * The entries in the list must be stored in sequential order (based on offset in file).
-     */
+    * Holds information about a single data run, which has a starting address and length.
+    * A run describes a consecutive list of blocks that have been allocated to a file. 
+    * A file may have many such runs and they are stringed together in a linked list.
+    * The entries in the list must be stored in sequential order (based on offset in file).
+    */
     struct TSK_FS_ATTR_RUN {
         TSK_FS_ATTR_RUN *next;  ///< Pointer to the next run in the attribute (or NULL)
         TSK_DADDR_T offset;     ///< Offset (in blocks) of this run in the file
@@ -159,9 +160,9 @@ extern "C" {
 
 
     /**
-     * Flags used for the TSK_FS_ATTR structure, which is used to 
-     * store file content metadata. 
-     */
+    * Flags used for the TSK_FS_ATTR structure, which is used to 
+    * store file content metadata. 
+    */
     typedef enum {
         TSK_FS_ATTR_FLAG_NONE = 0x00,   ///< No Flag
         TSK_FS_ATTR_INUSE = 0x01,       ///< data structure is in use
@@ -174,24 +175,24 @@ extern "C" {
     } TSK_FS_ATTR_FLAG_ENUM;
 
     /** 
-        * File walk callback function definition.  This is called for 
-        * chunks of content in the file being processed.  
-        * @param a_fs_file Pointer to file being processed
-        * @param a_off Byte offset in file that this data is for
-        * @param a_addr Address of data being passed (valid only if a_flags have RAW set)
-        * @param a_buf Pointer to buffer with file content
-        * @param a_len Size of data in buffer (in bytes)
-        * @param a_flags Flags about the file content
-        * @param a_ptr Pointer that was specified by caller to inode_walk
-        * @returns Value that tells file walk to continue or stop
-        */
+    * File walk callback function definition.  This is called for 
+    * chunks of content in the file being processed.  
+    * @param a_fs_file Pointer to file being processed
+    * @param a_off Byte offset in file that this data is for
+    * @param a_addr Address of data being passed (valid only if a_flags have RAW set)
+    * @param a_buf Pointer to buffer with file content
+    * @param a_len Size of data in buffer (in bytes)
+    * @param a_flags Flags about the file content
+    * @param a_ptr Pointer that was specified by caller to inode_walk
+    * @returns Value that tells file walk to continue or stop
+    */
     typedef TSK_WALK_RET_ENUM(*TSK_FS_FILE_WALK_CB) (TSK_FS_FILE *
         a_fs_file, TSK_OFF_T a_off, TSK_DADDR_T a_addr, char *a_buf,
         size_t a_len, TSK_FS_BLOCK_FLAG_ENUM a_flags, void *a_ptr);
 
     /**
-     * Flags used by tsk_fs_file_walk to determine when the callback function should
-     * be used. */
+    * Flags used by tsk_fs_file_walk to determine when the callback function should
+    * be used. */
     typedef enum {
         TSK_FS_FILE_WALK_FLAG_NONE = 0x00,      ///< No Flag
         TSK_FS_FILE_WALK_FLAG_SLACK = 0x01,     ///< Include the file's slack space in the callback.
@@ -202,8 +203,8 @@ extern "C" {
 
 
     /**
-     * These are based on the NTFS type values. 
-     */
+    * These are based on the NTFS type values. 
+    */
     typedef enum {
         TSK_FS_ATTR_TYPE_DEFAULT = 0x01,        // 1
         TSK_FS_ATTR_TYPE_NTFS_SI = 0x10,        // 16
@@ -231,24 +232,24 @@ extern "C" {
 
     typedef struct TSK_FS_ATTR TSK_FS_ATTR;
     /**
-     * Holds information about the location of file content (or a file attribute). For most file systems, a file
-     * has only a single attribute that stores the file content. 
-     * Other file systems, such as NTFS, have multiple
-     * attributes.  If multiple attributes exist, they are stored in a linked list.
-     * Attributes can be "resident", which means the data is stored
-     * in a small buffer instead of being stored in a full file system block.
-     * "Non-resident" attributes store data in blocks and they are stored in 
-     * the data structure as a series of runs.  
-     * This structure is used to represent both of these cases.
-     *
-     * The non-resident data has several size values. 
-     * \verbatim
-     * |--------------------------------------------------------------------|
-     * |skiplen|---------------allocsize------------------------------------|
-     * |skiplen|---------------size-----------------------------------|
-     * |skiplen|---------------initsize------------|
-     * \endverbatim
-     */
+    * Holds information about the location of file content (or a file attribute). For most file systems, a file
+    * has only a single attribute that stores the file content. 
+    * Other file systems, such as NTFS, have multiple
+    * attributes.  If multiple attributes exist, they are stored in a linked list.
+    * Attributes can be "resident", which means the data is stored
+    * in a small buffer instead of being stored in a full file system block.
+    * "Non-resident" attributes store data in blocks and they are stored in 
+    * the data structure as a series of runs.  
+    * This structure is used to represent both of these cases.
+    *
+    * The non-resident data has several size values. 
+    * \verbatim
+    * |--------------------------------------------------------------------|
+    * |skiplen|---------------allocsize------------------------------------|
+    * |skiplen|---------------size-----------------------------------|
+    * |skiplen|---------------initsize------------|
+    * \endverbatim
+    */
     struct TSK_FS_ATTR {
         TSK_FS_ATTR *next;      ///< Pointer to next attribute in list
         TSK_FS_FILE *fs_file;   ///< Pointer to the file that this is from
@@ -261,9 +262,9 @@ extern "C" {
         TSK_OFF_T size;         ///< Size in bytes of attribute (does not include skiplen for non-resident)
 
         /**
-         * Data associated with a non-resident file / attribute. 
-         * The data is stored in one or more data runs. 
-         */
+        * Data associated with a non-resident file / attribute. 
+        * The data is stored in one or more data runs. 
+        */
         struct {
             TSK_FS_ATTR_RUN *run;       ///< Linked list of runs for non-resident attributes
             TSK_FS_ATTR_RUN *run_end;   ///< Pointer to final run in the list
@@ -274,25 +275,25 @@ extern "C" {
         } nrd;
 
         /**
-         * Data associated with a resident attribute / file.  
-         * The data is stored in a buffer. 
-         */
+        * Data associated with a resident attribute / file.  
+        * The data is stored in a buffer. 
+        */
         struct {
             uint8_t *buf;       ///< Buffer for resident data
             size_t buf_size;    ///< Number of bytes allocated to buf
         } rd;
 
         /* Special file (compressed, encrypted, etc.) */
-         ssize_t(*r) (const TSK_FS_ATTR * fs_attr,
+        ssize_t(*r) (const TSK_FS_ATTR * fs_attr,
             TSK_OFF_T a_offset, char *a_buf, size_t a_len);
-         uint8_t(*w) (const TSK_FS_ATTR * fs_attr,
+        uint8_t(*w) (const TSK_FS_ATTR * fs_attr,
             int flags, TSK_FS_FILE_WALK_CB, void *);
     };
 
 
     /**
-     * Structure used as the head of an attribute list.  
-     */
+    * Structure used as the head of an attribute list.  
+    */
     typedef struct {
         TSK_FS_ATTR *head;
     } TSK_FS_ATTRLIST;
@@ -304,25 +305,25 @@ extern "C" {
     //@}
 
 
-/**************** META_NAME_LIST Structure *******************/
+    /**************** META_NAME_LIST Structure *******************/
 
     /** \name Generic File System File Metadata Data Structures */
     //@{
 
     /**
-     * Size of name array in TSK_FS_META_NAME_LIST structure
-     */
+    * Size of name array in TSK_FS_META_NAME_LIST structure
+    */
 #define TSK_FS_META_NAME_LIST_NSIZE    512
 
 
     typedef struct TSK_FS_META_NAME_LIST TSK_FS_META_NAME_LIST;
     /**
-     * Relatively generic structure to hold file names that are stored with
-     * the file metadata.  Note that this is different from the
-     * file name stored in the directory heirarchy, which is 
-     * part of the tsk_fs_name_... code.  This is currently
-     * used for NTFS and FAT file systems only.
-     */
+    * Relatively generic structure to hold file names that are stored with
+    * the file metadata.  Note that this is different from the
+    * file name stored in the directory heirarchy, which is 
+    * part of the tsk_fs_name_... code.  This is currently
+    * used for NTFS and FAT file systems only.
+    */
     struct TSK_FS_META_NAME_LIST {
         TSK_FS_META_NAME_LIST *next;    ///< Pointer to next name (or NULL)
         char name[TSK_FS_META_NAME_LIST_NSIZE]; ///< Name in UTF-8 (does not include parent directory name)
@@ -332,11 +333,11 @@ extern "C" {
 
 
 
-/****************** META Structure ***************/
+    /****************** META Structure ***************/
 
     /**
-     * Metadata flags used in TSK_FS_META.flags and in request to inode_walk
-     */
+    * Metadata flags used in TSK_FS_META.flags and in request to inode_walk
+    */
     enum TSK_FS_META_FLAG_ENUM {
         TSK_FS_META_FLAG_ALLOC = 0x01,  ///< Metadata structure is currently in an allocated state
         TSK_FS_META_FLAG_UNALLOC = 0x02,        ///< Metadata structure is currently in an unallocated state
@@ -356,9 +357,9 @@ extern "C" {
 
 
     /**
-     * Values for the mode field -- which identifies the file type 
-     * and permissions.
-     */
+    * Values for the mode field -- which identifies the file type 
+    * and permissions.
+    */
     enum TSK_FS_META_TYPE_ENUM {
         TSK_FS_META_TYPE_UNDEF = 0x00,
         TSK_FS_META_TYPE_REG = 0x01,    ///< Regular file
@@ -401,15 +402,15 @@ extern "C" {
 
 #define TSK_FS_META_TAG 0x13524635
     /** 
-     * TSK data structure to store general file and directory metadata. 
-     * Note that the file in the file
-     * system may have more metadata than is stored here.  
-     * For performance reasons, the run list of the file content is not always known
-     * when the file is loaded.  It may be loaded only when needed by the internal code. 
-     * The TSK_FS_META::content_ptr pointer contains file system-specific data that will be
-     * used to determine the full run. After it has been loaded, the TSK_FS_META::attr field
-     * will contain that info.  
-     */
+    * TSK data structure to store general file and directory metadata. 
+    * Note that the file in the file
+    * system may have more metadata than is stored here.  
+    * For performance reasons, the run list of the file content is not always known
+    * when the file is loaded.  It may be loaded only when needed by the internal code. 
+    * The TSK_FS_META::content_ptr pointer contains file system-specific data that will be
+    * used to determine the full run. After it has been loaded, the TSK_FS_META::attr field
+    * will contain that info.  
+    */
     typedef struct {
         int tag;                ///< \internal Will be set to TSK_FS_META_TAG if structure is allocated
 
@@ -451,9 +452,9 @@ extern "C" {
         uint32_t seq;           ///< Sequence number for file (NTFS only, is incremented when entry is reallocated) 
 
         /** Contains run data on the file content (specific locations where content is stored).  
-         * Check attr_state to determine if data in here is valid because not all file systems 
-         * load this data when a file is loaded.  It may not be loaded until needed by one
-         * of the APIs. Most file systems will have only one attribute, but NTFS will have several. */
+        * Check attr_state to determine if data in here is valid because not all file systems 
+        * load this data when a file is loaded.  It may not be loaded until needed by one
+        * of the APIs. Most file systems will have only one attribute, but NTFS will have several. */
         TSK_FS_ATTRLIST *attr;
         TSK_FS_META_ATTR_FLAG_ENUM attr_state;  ///< State of the data in the TSK_FS_META::attr structure
 
@@ -463,25 +464,25 @@ extern "C" {
 
 
 
-/** String that is prepended to orphan FAT & NTFS files when the file
- * name is known, but the parent is not */
+    /** String that is prepended to orphan FAT & NTFS files when the file
+    * name is known, but the parent is not */
 #define TSK_FS_ORPHAN_STR "-ORPHAN_FILE-"
 
     /* we are using the last inode as the special inode for the orphan directory.  Note that this
-     * macro is defined to abstract this convention, but there are many places in the code where
-     * there is implied logic about this convention. For example, inode_walks will stop before
-     * this value so that special handling can occur. */
+    * macro is defined to abstract this convention, but there are many places in the code where
+    * there is implied logic about this convention. For example, inode_walks will stop before
+    * this value so that special handling can occur. */
 #define TSK_FS_ORPHANDIR_INUM(fs_info) \
-(fs_info->last_inum)
+    (fs_info->last_inum)
 
 
     /** 
-        * inode walk callback function definition.  This is called for every file
-        * that meets the critera specified when inode_walk was called. 
-        * @param a_fs_file Pointer to the current file
-        * @param a_ptr Pointer that was specified by caller to inode_walk
-        * @returns Value that tells inode walk to continue or stop
-        */
+    * inode walk callback function definition.  This is called for every file
+    * that meets the critera specified when inode_walk was called. 
+    * @param a_fs_file Pointer to the current file
+    * @param a_ptr Pointer that was specified by caller to inode_walk
+    * @returns Value that tells inode walk to continue or stop
+    */
     typedef TSK_WALK_RET_ENUM(*TSK_FS_META_WALK_CB) (TSK_FS_FILE *
         a_fs_file, void *a_ptr);
 
@@ -490,7 +491,7 @@ extern "C" {
         TSK_INUM_T a_end, TSK_FS_META_FLAG_ENUM a_flags,
         TSK_FS_META_WALK_CB a_cb, void *a_ptr);
 
-    extern uint8_t tsk_fs_meta_make_ls(TSK_FS_META * a_fs_meta,
+    extern uint8_t tsk_fs_meta_make_ls(const TSK_FS_META * a_fs_meta,
         char *a_buf, size_t a_len);
 
     //@}
@@ -502,8 +503,8 @@ extern "C" {
 
     /**
     * File name flags that are used when specifying the status of
-     * a name in the TSK_FS_NAME structure
-     */
+    * a name in the TSK_FS_NAME structure
+    */
     typedef enum {
         TSK_FS_NAME_FLAG_ALLOC = 0x01,  ///< Name is in an allocated state
         TSK_FS_NAME_FLAG_UNALLOC = 0x02,        ///< Name is in an unallocated state
@@ -511,8 +512,8 @@ extern "C" {
 
 
     /**
-        * File type values -- as specified in the directory entry structure.
-     */
+    * File type values -- as specified in the directory entry structure.
+    */
     typedef enum {
         TSK_FS_NAME_TYPE_UNDEF = 0,     ///< Unknown type
         TSK_FS_NAME_TYPE_FIFO = 1,      ///< Named pipe 
@@ -534,11 +535,11 @@ extern "C" {
 
 #define  TSK_FS_NAME_TAG 0x23147869
     /**
-     * Generic structure to store the file name information that is stored in
-     * a directory. Most file systems seperate the file name from the metadata, but
-     * some do not (such as FAT). This structure contains the name and address of the 
-     * metadata.
-     */
+    * Generic structure to store the file name information that is stored in
+    * a directory. Most file systems seperate the file name from the metadata, but
+    * some do not (such as FAT). This structure contains the name and address of the 
+    * metadata.
+    */
     typedef struct {
         int tag;                ///< \internal Set to TSK_FS_NAME_ID if allocated, 0 if not
 
@@ -558,21 +559,21 @@ extern "C" {
 
 
     /**
-     * Definition of callback function that is used by tsk_fs_dir_walk().  This is
-     * is called for each file in a directory. 
-     * @param a_fs_file Pointer to the current file in the directory
-     * @param a_path Path of the file
-     * @param a_ptr Pointer that was originally passed by caller to tsk_fs_dir_walk.
-     * @returns Value to signal if tsk_fs_dir_walk should stop or continue. 
-     */
+    * Definition of callback function that is used by tsk_fs_dir_walk().  This is
+    * is called for each file in a directory. 
+    * @param a_fs_file Pointer to the current file in the directory
+    * @param a_path Path of the file
+    * @param a_ptr Pointer that was originally passed by caller to tsk_fs_dir_walk.
+    * @returns Value to signal if tsk_fs_dir_walk should stop or continue. 
+    */
     typedef TSK_WALK_RET_ENUM(*TSK_FS_DIR_WALK_CB) (TSK_FS_FILE *
         a_fs_file, const char *a_path, void *a_ptr);
 
 
 #define TSK_FS_DIR_TAG  0x97531246
     /**
-     * A handle to a directory so that its files can be individually accessed.
-     */
+    * A handle to a directory so that its files can be individually accessed.
+    */
     typedef struct {
         int tag;                ///< \internal Will be set to TSK_FS_DIR_TAG if structure is still allocated, 0 if not
 
@@ -588,9 +589,9 @@ extern "C" {
     } TSK_FS_DIR;
 
     /**
-     * Flags that are used when walking names in directories.  These are used to identify
-     * which files to call the callback function on. 
-     */
+    * Flags that are used when walking names in directories.  These are used to identify
+    * which files to call the callback function on. 
+    */
     typedef enum {
         TSK_FS_DIR_WALK_FLAG_NONE = 0x00,       ///< No Flags
         TSK_FS_DIR_WALK_FLAG_ALLOC = 0x01,      ///< Return allocated names in callback
@@ -623,12 +624,12 @@ extern "C" {
 
 #define  TSK_FS_FILE_TAG 0x11212212
     /**
-     * Generic structure used to refer to files in the file system.  A file will
-     * typically have a name and metadata.  This structure holds that type of information.
-     * When deleted files are being processed, this structure may have the name defined
-     * but not metadata because it no longer exists. Or, if you are calling meta_walk
-     * and are not processing at the name level, then the name will not be defined.  
-     * always check these to make sure they are not null before they are read. */
+    * Generic structure used to refer to files in the file system.  A file will
+    * typically have a name and metadata.  This structure holds that type of information.
+    * When deleted files are being processed, this structure may have the name defined
+    * but not metadata because it no longer exists. Or, if you are calling meta_walk
+    * and are not processing at the name level, then the name will not be defined.  
+    * always check these to make sure they are not null before they are read. */
     struct TSK_FS_FILE {
         int tag;                ///< \internal Will be set to TSK_FS_FILE_TAG if structure is allocated
 
@@ -639,7 +640,7 @@ extern "C" {
     };
 
     /**
-     * Flags used by tsk_fs_file_read */
+    * Flags used by tsk_fs_file_read */
     typedef enum {
         TSK_FS_FILE_READ_FLAG_NONE = 0x00,      ///< No Flags
         TSK_FS_FILE_READ_FLAG_SLACK = 0x01,     ///< Allow read access into slack space
@@ -682,7 +683,7 @@ extern "C" {
     //@}
 
 
-/****************** Journal Structures *************/
+    /****************** Journal Structures *************/
 
     /** \name Generic File System Journal Data Structures */
     //@{
@@ -700,7 +701,7 @@ extern "C" {
     //@}
 
 
-/******************************* TSK_FS_INFO ******************/
+    /******************************* TSK_FS_INFO ******************/
 
     /** \name Generic File System Handle Data Structure */
     //@{
@@ -737,65 +738,65 @@ extern "C" {
     typedef enum TSK_FS_TYPE_ENUM TSK_FS_TYPE_ENUM;
 
     /**
-        * \ingroup fslib
-     * Macro that takes a file system type and returns 1 if the type
-     * is for an NTFS file system. */
+    * \ingroup fslib
+    * Macro that takes a file system type and returns 1 if the type
+    * is for an NTFS file system. */
 #define TSK_FS_TYPE_ISNTFS(ftype) \
     (((ftype) & TSK_FS_TYPE_NTFS_DETECT)?1:0)
 
-        /**
-        * \ingroup fslib
-         * Macro that takes a file system type and returns 1 if the type
-         * is for a FAT file system. */
+    /**
+    * \ingroup fslib
+    * Macro that takes a file system type and returns 1 if the type
+    * is for a FAT file system. */
 #define TSK_FS_TYPE_ISFAT(ftype) \
-        (((ftype) & TSK_FS_TYPE_FAT_DETECT)?1:0)
+    (((ftype) & TSK_FS_TYPE_FAT_DETECT)?1:0)
 
-        /**
-        * \ingroup fslib
-         * Macro that takes a file system type and returns 1 if the type
-         * is for a FFS file system. */
+    /**
+    * \ingroup fslib
+    * Macro that takes a file system type and returns 1 if the type
+    * is for a FFS file system. */
 #define TSK_FS_TYPE_ISFFS(ftype) \
-        (((ftype) & TSK_FS_TYPE_FFS_DETECT)?1:0)
+    (((ftype) & TSK_FS_TYPE_FFS_DETECT)?1:0)
 
-        /**
-        * \ingroup fslib
-         * Macro that takes a file system type and returns 1 if the type
-         * is for a ExtX file system. */
+    /**
+    * \ingroup fslib
+    * Macro that takes a file system type and returns 1 if the type
+    * is for a ExtX file system. */
 #define TSK_FS_TYPE_ISEXT(ftype) \
-        (((ftype) & TSK_FS_TYPE_EXT_DETECT)?1:0)
+    (((ftype) & TSK_FS_TYPE_EXT_DETECT)?1:0)
 
-        /**
-        * \ingroup fslib
-         * Macro that takes a file system type and returns 1 if the type
-         * is for a ISO9660 file system. */
+    /**
+    * \ingroup fslib
+    * Macro that takes a file system type and returns 1 if the type
+    * is for a ISO9660 file system. */
 #define TSK_FS_TYPE_ISISO9660(ftype) \
-        (((ftype) & TSK_FS_TYPE_ISO9660_DETECT)?1:0)
+    (((ftype) & TSK_FS_TYPE_ISO9660_DETECT)?1:0)
 
-        /**
-        * \ingroup fslib
-         * Macro that takes a file system type and returns 1 if the type
-         * is for a HFS file system. */
+    /**
+    * \ingroup fslib
+    * Macro that takes a file system type and returns 1 if the type
+    * is for a HFS file system. */
 #define TSK_FS_TYPE_ISHFS(ftype) \
-        (((ftype) & TSK_FS_TYPE_HFS_DETECT)?1:0)
+    (((ftype) & TSK_FS_TYPE_HFS_DETECT)?1:0)
 
-        /**
-        * \ingroup fslib
-         * Macro that takes a file system type and returns 1 if the type
-         * is for a swap "file system". */
+    /**
+    * \ingroup fslib
+    * Macro that takes a file system type and returns 1 if the type
+    * is for a swap "file system". */
 #define TSK_FS_TYPE_ISSWAP(ftype) \
-        (((ftype) & TSK_FS_TYPE_SWAP_DETECT)?1:0)
+    (((ftype) & TSK_FS_TYPE_SWAP_DETECT)?1:0)
 
-        /**
-        * \ingroup fslib
-         * Macro that takes a file system type and returns 1 if the type
-         * is for a raw "file system". */
+    /**
+    * \ingroup fslib
+    * Macro that takes a file system type and returns 1 if the type
+    * is for a raw "file system". */
 #define TSK_FS_TYPE_ISRAW(ftype) \
-        (((ftype) & TSK_FS_TYPE_RAW_DETECT)?1:0)
+    (((ftype) & TSK_FS_TYPE_RAW_DETECT)?1:0)
 
 
     /**
-     * Flags for the FS_INFO structure 
-     */
+    * Flags for the FS_INFO structure 
+    */
     enum TSK_FS_INFO_FLAG_ENUM {
         TSK_FS_INFO_FLAG_NONE = 0x00,   ///< No Flags
         TSK_FS_INFO_FLAG_HAVE_SEQ = 0x01        ///< File system has sequence numbers in the inode addresses.
@@ -805,12 +806,15 @@ extern "C" {
 #define TSK_FS_INFO_TAG  0x10101010
 #define TSK_FS_INFO_FS_ID_LEN   32      // set based on largest file system / volume ID supported
 
-/**
- * Stores state information for an open file system. 
- * One of these are generated for each open files system and it contains
- * file system-type specific data.  These values are all filled in by
- * the file system code and not the caller functions. 
- */
+    /**
+    * Stores state information for an open file system. 
+    * One of these are generated for each open files system and it contains
+    * file system-type specific data.  These values are all filled in by
+    * the file system code and not the caller functions.  This struct
+    * (and its subclasses) should be allocated only by tsk_fs_malloc
+    * and deallocated only by tsk_fs_free, which handle init/deinit
+    * of the locks.
+    */
     struct TSK_FS_INFO {
         int tag;                ///< \internal Will be set to TSK_FS_INFO_TAG if structure is still allocated, 0 if not
         TSK_IMG_INFO *img_info; ///< Pointer to the image layer state
@@ -848,59 +852,63 @@ extern "C" {
 
         TSK_ENDIAN_ENUM endian; ///< Endian order of data
 
+        /* list_inum_named_lock protects list_inum_named */
+        tsk_lock_t list_inum_named_lock; // taken when r/w the list_inum_named list
         TSK_LIST *list_inum_named;      /**< List of unallocated inodes that
-					 * are pointed to by a file name -- 
-					 * Used to find orphan files.  Is filled when looking for orphans
-                     * or when a full name_walk is performed. 
-					 */
+                                        * are pointed to by a file name -- 
+                                        * Used to find orphan files.  Is filled 
+                                        * after looking for orphans
+                                        * or afer a full name_walk is performed.
+                                        * (r/w shared - lock) */
 
-        TSK_FS_DIR *orphan_dir; ///< Files and dirs in the top level of the $OrphanFiles directory.  NULL if orphans have not been hunted for yet. 
-        uint8_t isOrphanHunting;        ///< Set to 1 if TSK is currently looking for Orphan files
+        /* orphan_hunt_lock protects orphan_dir */
+        tsk_lock_t orphan_dir_lock; // taken for the duration of orphan hunting (not just when updating orphan_dir)
+        TSK_FS_DIR *orphan_dir; ///< Files and dirs in the top level of the $OrphanFiles directory.  NULL if orphans have not been hunted for yet. (r/w shared - lock) 
 
-         uint8_t(*block_walk) (TSK_FS_INFO * fs, TSK_DADDR_T start, TSK_DADDR_T end, TSK_FS_BLOCK_WALK_FLAG_ENUM flags, TSK_FS_BLOCK_WALK_CB cb, void *ptr);    ///< FS-specific function: Call tsk_fs_block_walk() instead. 
+        uint8_t(*block_walk) (TSK_FS_INFO * fs, TSK_DADDR_T start, TSK_DADDR_T end, TSK_FS_BLOCK_WALK_FLAG_ENUM flags, TSK_FS_BLOCK_WALK_CB cb, void *ptr);    ///< FS-specific function: Call tsk_fs_block_walk() instead. 
 
-         TSK_FS_BLOCK_FLAG_ENUM(*block_getflags) (TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr);      ///< \internal
+        TSK_FS_BLOCK_FLAG_ENUM(*block_getflags) (TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr);      ///< \internal
 
-         uint8_t(*inode_walk) (TSK_FS_INFO * fs, TSK_INUM_T start, TSK_INUM_T end, TSK_FS_META_FLAG_ENUM flags, TSK_FS_META_WALK_CB cb, void *ptr);     ///< FS-specific function: Call tsk_fs_meta_walk() instead. 
+        uint8_t(*inode_walk) (TSK_FS_INFO * fs, TSK_INUM_T start, TSK_INUM_T end, TSK_FS_META_FLAG_ENUM flags, TSK_FS_META_WALK_CB cb, void *ptr);     ///< FS-specific function: Call tsk_fs_meta_walk() instead. 
 
-         uint8_t(*file_add_meta) (TSK_FS_INFO * fs, TSK_FS_FILE * fs_file, TSK_INUM_T addr);    ///< \internal
+        uint8_t(*file_add_meta) (TSK_FS_INFO * fs, TSK_FS_FILE * fs_file, TSK_INUM_T addr);    ///< \internal
 
-         TSK_FS_ATTR_TYPE_ENUM(*get_default_attr_type) (const TSK_FS_FILE *);   ///< \internal
+        TSK_FS_ATTR_TYPE_ENUM(*get_default_attr_type) (const TSK_FS_FILE *);   ///< \internal
 
-         uint8_t(*load_attrs) (TSK_FS_FILE *);  ///< \internal
+        uint8_t(*load_attrs) (TSK_FS_FILE *);  ///< \internal
 
 
         /**
-         * Pointer to file system specific function that prints details on a specific file to a file handle. 
-         *
-         * @param fs File system file is located in
-         * @param hFile File handle to print text to
-         * @param inum Address of file in file system
-         * @param numblock The number of blocks in file to force print (can go beyond file size)
-         * @param sec_skew Clock skew in seconds to also print times in
-         * 
-         * @returns 1 on error and 0 on success
-         */
-         uint8_t(*istat) (TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
+        * Pointer to file system specific function that prints details on a specific file to a file handle. 
+        *
+        * @param fs File system file is located in
+        * @param hFile File handle to print text to
+        * @param inum Address of file in file system
+        * @param numblock The number of blocks in file to force print (can go beyond file size)
+        * @param sec_skew Clock skew in seconds to also print times in
+        * 
+        * @returns 1 on error and 0 on success
+        */
+        uint8_t(*istat) (TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
             TSK_DADDR_T numblock, int32_t sec_skew);
 
-         TSK_RETVAL_ENUM(*dir_open_meta) (TSK_FS_INFO * fs, TSK_FS_DIR ** a_fs_dir, TSK_INUM_T inode);  ///< \internal Call tsk_fs_dir_open_meta() instead. 
+        TSK_RETVAL_ENUM(*dir_open_meta) (TSK_FS_INFO * fs, TSK_FS_DIR ** a_fs_dir, TSK_INUM_T inode);  ///< \internal Call tsk_fs_dir_open_meta() instead. 
 
-         uint8_t(*jopen) (TSK_FS_INFO *, TSK_INUM_T);   ///< \internal
+        uint8_t(*jopen) (TSK_FS_INFO *, TSK_INUM_T);   ///< \internal
 
-         uint8_t(*jblk_walk) (TSK_FS_INFO *, TSK_DADDR_T, TSK_DADDR_T, int, TSK_FS_JBLK_WALK_CB, void *);       ///< \internal
+        uint8_t(*jblk_walk) (TSK_FS_INFO *, TSK_DADDR_T, TSK_DADDR_T, int, TSK_FS_JBLK_WALK_CB, void *);       ///< \internal
 
-         uint8_t(*jentry_walk) (TSK_FS_INFO *, int, TSK_FS_JENTRY_WALK_CB, void *);     ///< \internal
+        uint8_t(*jentry_walk) (TSK_FS_INFO *, int, TSK_FS_JENTRY_WALK_CB, void *);     ///< \internal
 
-         uint8_t(*fsstat) (TSK_FS_INFO * fs, FILE * hFile);     ///< \internal
+        uint8_t(*fsstat) (TSK_FS_INFO * fs, FILE * hFile);     ///< \internal
 
         int (*name_cmp) (TSK_FS_INFO *, const char *, const char *);    ///< \internal
 
-         uint8_t(*fscheck) (TSK_FS_INFO *, FILE *);     ///< \internal
+        uint8_t(*fscheck) (TSK_FS_INFO *, FILE *);     ///< \internal
 
         void (*close) (TSK_FS_INFO * fs);       ///< FS-specific function: Call tsk_fs_close() instead. 
 
-         uint8_t(*fread_owner_sid) (TSK_FS_FILE *, char **);    // FS-specific function. Call tsk_fs_file_get_owner_sid() instead.
+        uint8_t(*fread_owner_sid) (TSK_FS_FILE *, char **);    // FS-specific function. Call tsk_fs_file_get_owner_sid() instead.
     };
 
 
@@ -924,7 +932,7 @@ extern "C" {
     //@}
 
 
-/***** LIBRARY ROUTINES FOR COMMAND LINE FUNCTIONS */
+    /***** LIBRARY ROUTINES FOR COMMAND LINE FUNCTIONS */
     enum TSK_FS_BLKCALC_FLAG_ENUM {
         TSK_FS_BLKCALC_DD = 0x01,
         TSK_FS_BLKCALC_BLKLS = 0x02,
@@ -1019,10 +1027,10 @@ extern "C" {
         TSK_FS_META_FLAG_ENUM flags, int32_t skew, const TSK_TCHAR * img);
 
     /*
-     ** Is this string a "." or ".."
-     */
+    ** Is this string a "." or ".."
+    */
 #define TSK_FS_ISDOT(str) ( ((str[0] == '.') && \
-                             ( ((str[1] == '.') && (str[2] == '\0')) || (str[1] == '\0') ) ) ? 1 : 0 )
+    ( ((str[1] == '.') && (str[2] == '\0')) || (str[1] == '\0') ) ) ? 1 : 0 )
 
 
     extern int tsk_fs_parse_inum(const TSK_TCHAR * str, TSK_INUM_T *,
@@ -1030,5 +1038,1907 @@ extern "C" {
 
 #ifdef __cplusplus
 }
+#endif
+#ifdef __cplusplus
+
+/**
+ * \ingroup fslib_cpp
+ */
+class TskFsJEntry{
+private:
+    TSK_FS_JENTRY *m_jEntry;
+    TskFsJEntry(const TskFsJEntry& rhs); 
+    TskFsJEntry& operator=(const TskFsJEntry& rhs);
+    
+public:
+    TskFsJEntry(TSK_FS_JENTRY *a_jEntry) {
+        m_jEntry = a_jEntry;
+    };
+    
+    ~TskFsJEntry() {
+    };
+};
+
+/**
+* \ingroup fslib_cpp
+* Contains information about a single data run, which has a starting address and length.
+* A run describes a consecutive list of blocks that have been allocated to a file. 
+* A file may have many such runs and they are stringed together in a linked list.
+* The entries in the list must be stored in sequential order (based on offset in file).
+* See TSK_FS_ATTR_RUN for more details.
+*/
+class TskFsAttrRun{
+private:
+    TSK_FS_ATTR_RUN * m_fsAttrRun; 
+    TskFsAttrRun(const TskFsAttrRun& rhs); 
+    TskFsAttrRun& operator=(const TskFsAttrRun& rhs);
+
+public:
+    /**
+        * construct a TskFsAttrRun object.
+    * @param a_fsAttrRun pointer of TSK_FS_ATTR_RUN. If NULL, then the 
+    * getX() method return values are undefined. 
+    */
+    TskFsAttrRun(TSK_FS_ATTR_RUN * a_fsAttrRun) {
+        m_fsAttrRun = a_fsAttrRun;
+    };
+
+    ~TskFsAttrRun() {
+    };
+
+    /**
+    * get offset (in blocks) of this run in the file
+    * @return offset of run
+    */
+    TSK_DADDR_T getOffset() const {
+        if (m_fsAttrRun != NULL)
+            return m_fsAttrRun->offset;
+        else
+            return 0;
+    };
+
+    /**
+        * get starting block address (in file system) of run
+    * @return starting block address
+    */
+    TSK_DADDR_T getAddr() const {
+        if (m_fsAttrRun != NULL)
+            return m_fsAttrRun->addr;
+        else
+            return 0;
+    };
+
+    /**
+    * get number of blocks in run (0 when entry is not in use)
+    * @return offset
+    */
+    TSK_DADDR_T length() const {
+        if (m_fsAttrRun != NULL)
+            return m_fsAttrRun->len;
+        else
+            return 0;
+    };
+
+    /**
+        * get flags for run
+    * @return flags for run
+    */
+    TSK_FS_ATTR_RUN_FLAG_ENUM getFlags() const {
+        if (m_fsAttrRun != NULL)
+            return m_fsAttrRun->flags;
+        else
+            return (TSK_FS_ATTR_RUN_FLAG_ENUM)0;
+    };
+};//TskFsAttrRun
+
+/**
+* \ingroup fslib_cpp
+* Stores the file name information that is stored in
+* a directory. Most file systems seperate the file name from the metadata, but
+* some do not (such as FAT). This structure contains the file name and the 
+* address of the  metadata. See TSK_FS_NAME for more details.
+*/
+class TskFsName {
+    friend class TskFsInfo;
+    
+private:
+    TSK_FS_NAME *m_fsName;
+    TskFsName(const TskFsName& rhs); 
+    TskFsName& operator=(const TskFsName& rhs);
+
+public:
+    /**
+    * construct a TskFsName object
+    * @param a_fsName a pointer of TSK_FS_NAME. If NULL, the getX() return values are undefined. 
+    */
+    TskFsName(TSK_FS_NAME * a_fsName) {
+        m_fsName = a_fsName;
+    };
+
+    ~TskFsName() {
+    };
+
+    /**
+    * Return the name of the file (in UTF-8)
+    * @return the name of the file
+    */
+    const char * getName() const {
+        if (m_fsName != NULL)
+            return m_fsName->name;
+        else
+            return NULL;
+    };
+    /**
+        * Return the short name of the file or null (in UTF-8)
+    * @return the short name of the file
+    */
+    const char * getShortName() const {
+        if (m_fsName != NULL)
+            return m_fsName->shrt_name;
+        else
+            return NULL;
+    };
+
+    /**
+        * Return the address of the metadata structure that the name points to. 
+    * @return address of the metadata structure that the name points to 
+    */
+    TSK_INUM_T getMetaAddr() const {
+        if (m_fsName != NULL)
+            return m_fsName->meta_addr;
+        else
+            return 0;
+    };
+
+    /**
+        * Return the sequence number for metadata structure (NTFS only) 
+    * @return sequence number for metadata structure
+    */
+    uint32_t getMetaSeq() const {
+        if (m_fsName != NULL)
+            return m_fsName->meta_seq;
+        else
+            return 0;
+    };
+
+    /**
+        * Return the metadata address of the parent directory (equal to meta_addr if this entry is for root directory). 
+    * @return metadata address of parent directory 
+    */
+    TSK_INUM_T getParentAddr() const  {
+        if (m_fsName != NULL)
+            return m_fsName->par_addr;
+        else
+            return 0;
+    };
+
+    /**
+        * Return the file type information (directory, file, etc.) 
+    * @return file type information 
+    */
+    TSK_FS_NAME_TYPE_ENUM  getType() const {
+        if (m_fsName != NULL)
+            return m_fsName->type;
+        else 
+            return (TSK_FS_NAME_TYPE_ENUM)0;
+    };
+
+    /**
+        * Return flags that describe allocation status etc. 
+    * @return flags that describe allocation status 
+    */
+    TSK_FS_NAME_FLAG_ENUM  getFlags() const {
+        if (m_fsName != NULL)
+            return m_fsName->flags;
+        else
+            return (TSK_FS_NAME_FLAG_ENUM)0;
+    };
+};
+
+class TskFsFile;
+/** 
+* File walk callback function definition.  This is called for 
+* chunks of content in the file being processed.  
+* @param a_fs_file Pointer to file being processed
+* @param a_off Byte offset in file that this data is for
+* @param a_addr Address of data being passed (valid only if a_flags have RAW set)
+* @param a_buf Pointer to buffer with file content
+* @param a_len Size of data in buffer (in bytes)
+* @param a_flags Flags about the file content
+* @param a_ptr Pointer that was specified by caller to inode_walk
+* @returns Value that tells file walk to continue or stop
+*/
+typedef TSK_WALK_RET_ENUM(*TSK_FS_FILE_WALK_CPP_CB) (TskFsFile *
+                                                     a_fs_file, TSK_OFF_T a_off, TSK_DADDR_T a_addr, char *a_buf,
+                                                     size_t a_len, TSK_FS_BLOCK_FLAG_ENUM a_flags, void *a_ptr);
+
+/** \internal
+* Internal structure to pass C++ file walk data into C file walk call back.
+*/
+typedef struct {
+    TSK_FS_FILE_WALK_CPP_CB cppAction;  // pointer C++ callback
+    void *cPtr; // pointer to data that was passed into C++ walk method
+} TSK_FS_FILE_WALK_CPP_DATA;
+
+/** \internal
+* Internal function used to call C++ file Walk callback from C callback.
+*/
+extern TSK_WALK_RET_ENUM tsk_fs_file_cpp_c_cb (TSK_FS_FILE *a_file, TSK_OFF_T a_off, TSK_DADDR_T a_addr, char *a_buf,
+                                        size_t a_len, TSK_FS_BLOCK_FLAG_ENUM a_flags, void *a_ptr);
+/**
+* \ingroup fslib_cpp
+* Stores information about a file attribute.  File attributes store data for a file.
+* Most files have at least one attribute that stores the file content.  See TSK_FS_ATTR for 
+* details on attributes. 
+*/
+class TskFsAttribute{
+private:
+    const TSK_FS_ATTR *m_fsAttr;
+    TskFsAttribute(const TskFsAttribute& rhs); 
+    TskFsAttribute& operator=(const TskFsAttribute& rhs);
+    
+public:
+    /**
+        * construct a TskFsAttribute object
+    * @param a_fsAttr a pointer of TSK_FS_ATTR.  If NULL, the getX() return values are undefi
+    ned.
+    */
+    TskFsAttribute(const TSK_FS_ATTR *a_fsAttr) {
+        m_fsAttr = a_fsAttr;
+    };
+
+    ~TskFsAttribute() {
+    };
+
+    /**
+        * Process an attribute and call a callback function with its contents. The callback will be 
+    * called with chunks of data that are fs->block_size or less.  The address given in the callback
+    * will be correct only for raw files (when the raw file contents were stored in the block).  For
+    * compressed and sparse attributes, the address may be zero.
+    *
+    * See tsk_fs_attr_walk() for details
+    * @param a_flags Flags to use while processing attribute
+    * @param a_action Callback action to call with content
+    * @param a_ptr Pointer that will passed to callback
+    * @returns 1 on error and 0 on success.
+    */
+    uint8_t walk(TSK_FS_FILE_WALK_FLAG_ENUM a_flags, TSK_FS_FILE_WALK_CPP_CB a_action,
+        void *a_ptr) {
+        TSK_FS_FILE_WALK_CPP_DATA fileData;
+        fileData.cppAction = a_action;
+        fileData.cPtr = a_ptr;
+        if (m_fsAttr)
+            return tsk_fs_attr_walk(m_fsAttr, a_flags, tsk_fs_file_cpp_c_cb, &fileData);
+        else
+            return 1;
+    };
+
+    /**
+        * Read the contents of this attribute using a typical read() type interface.
+    * 0s are returned for missing runs. 
+    * 
+    * See tsk_fs_attr_read() for details
+    * @param a_offset The byte offset to start reading from.
+    * @param a_buf The buffer to read the data into.
+    * @param a_len The number of bytes to read from the file.
+    * @param a_flags Flags to use while reading
+    * @returns The number of bytes read or -1 on error (incl if offset is past end of file).
+    */
+    ssize_t read(TSK_OFF_T a_offset,char *a_buf, size_t a_len,
+        TSK_FS_FILE_READ_FLAG_ENUM a_flags) {
+        if (m_fsAttr != NULL)
+            return tsk_fs_attr_read(m_fsAttr, a_offset,a_buf, a_len,a_flags);
+        else
+            return -1;
+    };
+    
+    /**
+        * get the attribute's flags 
+    * @return flags for attribute
+    */
+    TSK_FS_ATTR_FLAG_ENUM getFlags() const {
+        if ( m_fsAttr !=NULL )
+            return m_fsAttr->flags;
+        else 
+            return (TSK_FS_ATTR_FLAG_ENUM)0;
+    };
+    /**
+        * get the attributes's name (in UTF-8).  
+    * @return name of attribute (or NULL if attribute doesn't have one)
+    */
+    const char* getName() const {
+        if ( m_fsAttr !=NULL )
+            return m_fsAttr->name;
+        else
+            return NULL;
+    };
+
+    /**
+        * get type of attribute
+    * @return type of attribute
+    */
+    TSK_FS_ATTR_TYPE_ENUM getType() const {
+        if ( m_fsAttr !=NULL )
+            return m_fsAttr->type;
+        else
+            return (TSK_FS_ATTR_TYPE_ENUM)0;
+    };
+
+    /**
+        * get id of attribute
+    * @return id of attribute
+    */
+    uint16_t getId() const {
+        if ( m_fsAttr !=NULL )
+            return m_fsAttr->id;
+        else
+            return 0;
+    };
+
+    /**
+        * get size in bytes of attribute (does not include skiplen for non-resident)
+    * @return size in bytes of attribute
+    */
+    TSK_OFF_T getSize() const {
+        if ( m_fsAttr !=NULL )
+            return m_fsAttr->size;
+        else
+            return 0;
+    };
+
+    /**
+        * get a run for a non-resident attribute. 
+    * It's caller's responsibility to free memory of TskFsAttrRun
+    * @param a_idx The index of the run to return.
+    * @return A run in the attribute.
+    */
+    const TskFsAttrRun * getRun(int a_idx) const {
+        if (m_fsAttr != NULL) {
+            TSK_FS_ATTR_RUN *run =m_fsAttr->nrd.run;
+            int i = 0;
+            while(run != NULL) {
+                if (i == a_idx)
+                    return new TskFsAttrRun(run);
+                i++;
+                run = run->next;
+            }
+        }
+        return NULL;
+    };
+
+    /**
+          * gets the number of runs in a non-resident attribute. 
+     * @return number of runs.
+     */
+    int getRunCount() const {
+        int size = 0;
+        if (m_fsAttr !=NULL) {
+            TSK_FS_ATTR_RUN *run = m_fsAttr->nrd.run;
+            while (run != NULL) {
+                size++;
+                run = run->next;
+            }
+        }
+        return size; 
+    }
+
+    /**
+        * get number of initial bytes in run to skip before content begins. 
+    * The size field does not include this length. 
+    * @return number of initial bytes in run to skip before content begins
+    */
+    uint32_t getSkipLen() const {
+        if ( m_fsAttr !=NULL )
+            return m_fsAttr->nrd.skiplen;
+        else 
+            return 0;
+    };
+
+    /**
+        * get number of bytes that are allocated in all clusters of non-resident run 
+    * (will be larger than size - does not include skiplen).  
+    * This is defined when the attribute is created and used to determine slack space. 
+    * @return number of bytes that are allocated in all clusters of non-resident run
+    */
+    TSK_OFF_T getAllocSize() const {
+        if ( m_fsAttr !=NULL )
+            return m_fsAttr->nrd.allocsize;
+        else 
+            return 0;
+    };
+
+    /**
+        * get number of bytes (starting from offset 0) that have data 
+    * (including FILLER) saved for them (smaller then or equal to size).  
+    * This is defined when the attribute is created. 
+    * @return number of bytes (starting from offset 0) that have data 
+    */
+    TSK_OFF_T getInitSize() const {
+        if ( m_fsAttr !=NULL )
+            return m_fsAttr->nrd.initsize;
+        else
+            return 0;
+    };
+
+    /**
+        * get size of compression units (needed only if NTFS file is compressed)
+    * @return size of compression units (needed only if NTFS file is compressed)
+    */
+    uint32_t getCompSize() const {
+        if ( m_fsAttr !=NULL )
+            return m_fsAttr->nrd.compsize;
+        return 0;
+    };
+
+    /**
+        * Pointer to buffer with resident data.  Only getSize() bytes will be valid.
+    * @return pointer to buffer with resident data.
+    */
+    const uint8_t * getBuf() const {
+        if ( m_fsAttr !=NULL )
+            return m_fsAttr->rd.buf;
+        else
+            return NULL;
+    };
+
+};//TskfsAttr
+
+
+class TskFsBlock;
+class TskFsInfo;
+/**
+* Function definition used for callback to blockWalk(). 
+*
+* @param a_block Pointer to TskFsBlock object that holds block content and flags
+* @param a_ptr Pointer that was supplied by the caller who called tsk_fs_block_walk
+* @returns Value to identify if walk should continue, stop, or stop because of error
+*/
+typedef TSK_WALK_RET_ENUM(*TSK_FS_BLOCK_WALK_CPP_CB) (const TskFsBlock *a_block, void *a_ptr);
+
+
+/** \internal
+* Internal structure to pass C++ block walk data into C block walk call back.
+*/
+typedef struct {
+    TSK_FS_BLOCK_WALK_CPP_CB cppAction;  // pointer C++ callback
+    void *cPtr; // pointer to data that was passed into C++ walk method
+} TSK_FS_BLOCK_WALK_CPP_DATA;
+
+/** \internal
+* Internal function used to call C++ Block Walk callback from C callback.
+*/
+extern TSK_WALK_RET_ENUM tsk_fs_block_cpp_c_cb (const TSK_FS_BLOCK *a_block, void *a_ptr);
+/**
+* Function definition for callback in TskFsInfo.jblkWalk(). 
+*
+* @param a_fsInfo File system being analyzed 
+* @param a_string 
+* @param a_num 
+* @param a_ptr Pointer that was supplied by the caller 
+* @returns Value to identify if walk should continue, stop, or stop because of error
+*/
+typedef TSK_WALK_RET_ENUM(*TSK_FS_JBLK_WALK_CPP_CB) (TskFsInfo *a_fsInfo, char *a_string,
+                                                     int a_num, void *a_ptr);
+
+/** \internal
+* Internal structure to pass C++ JBLK walk data into C JBLK walk call back.
+*/
+typedef struct {
+    TSK_FS_JBLK_WALK_CPP_CB cppAction;  // pointer C++ callback
+    void *cPtr; // pointer to data that was passed into C++ walk method
+} TSK_FS_JBLK_WALK_CPP_DATA;
+
+/** \internal
+* Internal function used to call C++ JBLK Walk callback from C callback.
+*/
+extern TSK_WALK_RET_ENUM tsk_fs_jblk_walk_cpp_c_cb (TSK_FS_INFO *a_fsInfo, char *a_string,
+                                        int a_num, void *a_ptr);
+
+/**
+* Function definition  for callback in TskFsInfo.jentryWalk().
+*
+* @param a_fsInfo File system being analyzed
+* @param a_jentry journal entry
+* @param a_num 
+* @param a_ptr Pointer that was supplied by the caller. 
+* @returns Value to identify if walk should continue, stop, or stop because of error
+*/
+    typedef TSK_WALK_RET_ENUM(*TSK_FS_JENTRY_WALK_CPP_CB) (TskFsInfo *a_fsInfo,
+        TskFsJEntry *a_jentry, int a_num, void * a_ptr);
+
+/** \internal
+* Internal structure to pass C++ JENTRY walk data into C JENTRY walk call back.
+*/
+typedef struct {
+    TSK_FS_JENTRY_WALK_CPP_CB cppAction;  // pointer C++ callback
+    void *cPtr; // pointer to data that was passed into C++ walk method
+} TSK_FS_JENTRY_WALK_CPP_DATA;
+
+/** \internal
+* Internal function used to call C++ JENTRY Walk callback from C callback.
+*/
+extern TSK_WALK_RET_ENUM tsk_fs_jentry_walk_cpp_c_cb (TSK_FS_INFO *a_fsInfo, TSK_FS_JENTRY *a_jentry,
+                                        int a_num, void *a_ptr);
+/** 
+* inode walk callback function definition.  This is called for every file
+* that meets the critera specified when inode_walk was called. 
+* @param a_fs_file Pointer to the current file
+* @param a_ptr Pointer that was specified by caller to inode_walk
+* @returns Value that tells inode walk to continue or stop
+*/
+typedef TSK_WALK_RET_ENUM(*TSK_FS_META_WALK_CPP_CB) (TskFsFile *
+                                                     a_fs_file, void *a_ptr);
+/** \internal
+* Internal structure to pass C++ metadata walk data into C metadata walk call back.
+*/
+typedef struct {
+    TSK_FS_META_WALK_CPP_CB cppAction;  // pointer C++ callback
+    void *cPtr; // pointer to data that was passed into C++ walk method
+} TSK_FS_META_WALK_CPP_DATA;
+
+/** \internal
+* Internal function used to call C++ Meta Walk callback from C callback.
+*/
+extern TSK_WALK_RET_ENUM tsk_fs_meta_walk_cpp_c_cb (TSK_FS_FILE *a_file, void *a_ptr);
+/**
+* Definition of callback function that is used by tsk_fs_dir_walk().  This is
+* is called for each file in a directory. 
+* @param a_fs_file Pointer to the current file in the directory
+* @param a_path Path of the file
+* @param a_ptr Pointer that was originally passed by caller to tsk_fs_dir_walk.
+* @returns Value to signal if tsk_fs_dir_walk should stop or continue. 
+*/
+typedef TSK_WALK_RET_ENUM(*TSK_FS_DIR_WALK_CPP_CB) (TskFsFile *
+                                                    a_fs_file, const char *a_path, void *a_ptr);
+
+/** \internal
+* Internal structure to pass C++ dir walk data into C block walk call back.
+*/
+typedef struct {
+    TSK_FS_DIR_WALK_CPP_CB cppAction;  // pointer C++ callback
+    void *cPtr; // pointer to data that was passed into C++ walk method
+} TSK_FS_DIR_WALK_CPP_DATA;
+
+/** \internal
+* Internal function used to call C++ Dir Walk callback from C callback.
+*/
+extern TSK_WALK_RET_ENUM tsk_fs_dir_walk_cpp_c_cb (TSK_FS_FILE *a_file,  const char *a_path, void *a_ptr);
+
+/**
+* \ingroup fslib_cpp
+* Stores information about an open file system.  One of the open() 
+* commands needs to be used before any of the getX() or read() methods will return
+* valid data.  See TSK_FS_INFO for more details.
+*/
+class TskFsInfo {
+    friend class TskFsBlock;
+    friend class TskFsFile;
+    friend class TskFsDir;
+    
+private:
+    TSK_FS_INFO * m_fsInfo;
+    TskFsInfo(const TskFsInfo& rhs); 
+    TskFsInfo& operator=(const TskFsInfo& rhs);
+
+public:
+    TskFsInfo(TSK_FS_INFO *a_fsInfo) {
+        m_fsInfo = a_fsInfo;
+    };
+    
+    TskFsInfo() {
+        m_fsInfo = NULL;
+    };
+    
+    ~TskFsInfo() {
+        close();
+    }
+
+    /**
+        * Read arbitrary data from inside of the file system. 
+    * See tsk_fs_block_free() for details
+    * @param a_off The byte offset to start reading from (relative to start of file system)
+    * @param a_buf The buffer to store the block in.
+    * @param a_len The number of bytes to read
+    * @return The number of bytes read or -1 on error. 
+    */
+    ssize_t read(TSK_OFF_T a_off, char *a_buf, size_t a_len) {
+        if (m_fsInfo)
+            return tsk_fs_read(m_fsInfo, a_off, a_buf, a_len);
+        else
+            return -1;
+    };
+
+    /**
+        * Read a file system block.
+    * See tsk_fs_read_block() for details
+    * @param a_addr The starting block file system address. 
+    * @param a_buf The char * buffer to store the block data in.
+    * @param a_len The number of bytes to read (must be a multiple of the block size)
+    * @return The number of bytes read or -1 on error. 
+    */
+    ssize_t readBlock(TSK_DADDR_T a_addr, char *a_buf,
+        size_t a_len) {
+        if (m_fsInfo)
+            return tsk_fs_read_block(m_fsInfo, a_addr, a_buf,a_len);
+        else
+            return -1;
+    };
+
+    /**
+        * Walk a range of metadata structures and call a callback for each
+    * structure that matches the flags supplied.   For example, it can
+    * call the callback on only allocated or unallocated entries. 
+    * See tsk_fs_meta_walk() for details
+    * @param a_start Metadata address to start walking from
+    * @param a_end Metadata address to walk to
+    * @param a_flags Flags that specify the desired metadata features
+    * @param a_cb Callback function to call
+    * @param a_ptr Pointer to pass to the callback
+    * @returns 1 on error and 0 on success
+    */
+    uint8_t metaWalk(TSK_INUM_T a_start,
+        TSK_INUM_T a_end, TSK_FS_META_FLAG_ENUM a_flags,
+        TSK_FS_META_WALK_CPP_CB a_cb, void *a_ptr) {
+        TSK_FS_META_WALK_CPP_DATA metaData;
+        metaData.cppAction = a_cb;
+        metaData.cPtr = a_ptr;
+        if (m_fsInfo)
+            return tsk_fs_meta_walk(m_fsInfo, a_start,
+                a_end, a_flags, tsk_fs_meta_walk_cpp_c_cb, &metaData);
+        else
+            return 1;
+    };
+
+    /*    * Walk the file names in a directory and obtain the details of the files via a callback. 
+    * See tsk_fs_dir_walk() for details
+    * @param a_addr Metadata address of the directory to analyze
+    * @param a_flags Flags used during analysis
+    * @param a_action Callback function that is called for each file name
+    * @param a_ptr Pointer to data that is passed to the callback function each time
+    * @returns 1 on error and 0 on success
+    */
+    uint8_t dirWalk(TSK_INUM_T a_addr,
+        TSK_FS_DIR_WALK_FLAG_ENUM a_flags, TSK_FS_DIR_WALK_CPP_CB a_action,
+        void *a_ptr) {
+            TSK_FS_DIR_WALK_CPP_DATA dirData;
+            dirData.cppAction = a_action;
+            dirData.cPtr = a_ptr;
+            if (m_fsInfo!=NULL)
+                return tsk_fs_dir_walk(m_fsInfo, a_addr,
+                a_flags, tsk_fs_dir_walk_cpp_c_cb, &dirData);
+            else
+                return 1;
+    };
+
+    /** 
+        *
+    * Walk a range of file system blocks and call the callback function
+    * with the contents and allocation status of each.
+    * See tsk_fs_block_walk() for details.
+    * @param a_start_blk Block address to start walking from
+    * @param a_end_blk Block address to walk to
+    * @param a_flags Flags used during walk to determine which blocks to call callback with
+    * @param a_action Callback function
+    * @param a_ptr Pointer that will be passed to callback
+    * @returns 1 on error and 0 on success
+    */
+    uint8_t blockWalk(TSK_DADDR_T a_start_blk, 
+        TSK_DADDR_T a_end_blk, TSK_FS_BLOCK_WALK_FLAG_ENUM a_flags, 
+        TSK_FS_BLOCK_WALK_CPP_CB a_action, void *a_ptr) {
+
+            TSK_FS_BLOCK_WALK_CPP_DATA blockData;
+            blockData.cppAction = a_action;
+            blockData.cPtr = a_ptr;
+
+            return tsk_fs_block_walk(m_fsInfo, a_start_blk, a_end_blk,
+                a_flags, tsk_fs_block_cpp_c_cb, &blockData);//tsk_fs_block_walk will check the input data
+
+    };
+
+    /**
+        * Opens a file system that is inside of a Volume.
+    * Returns a structure that can be used for analysis and reporting. 
+    * See tsk_fs_open_vol() for details
+    * @param a_part_info Open volume to read from and analyze
+    * @param a_ftype Type of file system (or autodetect)
+    *
+    * @return 1 on error 0 on success.
+    */
+    uint8_t open(const TskVsPartInfo * a_part_info,
+        TSK_FS_TYPE_ENUM a_ftype) {
+            if ((m_fsInfo = tsk_fs_open_vol(a_part_info->m_vsPartInfo, a_ftype))
+                != NULL)
+                return 0;
+            return 1;
+    };
+
+    /**
+        * Opens a file system at a given offset in a disk image.
+    * Returns a structure that can be used for analysis and reporting. 
+    * See tsk_fs_open_img() for details
+    * @param a_img_info Disk image to analyze
+    * @param a_offset Byte offset to start analyzing from
+    * @param a_ftype Type of file system (or autodetect)
+    *
+    * @return 1 on error 0 on success.
+    */
+    uint8_t open(TskImgInfo * a_img_info, TSK_OFF_T a_offset,TSK_FS_TYPE_ENUM a_ftype) {
+        if ((m_fsInfo = tsk_fs_open_img(a_img_info->m_imgInfo, a_offset, a_ftype))
+            != NULL)
+            return 0;
+        return 1;
+    };
+
+
+
+    /**
+    * \internal
+    */
+    uint8_t jopen (TSK_INUM_T a_inum) {
+        if (m_fsInfo == NULL)
+            return 0;
+
+        return m_fsInfo->jopen(m_fsInfo, a_inum);
+    }
+    
+    /**
+    * \internal 
+    */
+    uint8_t jblkWalk (TSK_DADDR_T a_addr1, TSK_DADDR_T a_addr2, int a_num, TSK_FS_JBLK_WALK_CPP_CB a_action, void *a_ptr) {
+        if (m_fsInfo == NULL)
+            return 0;
+        TSK_FS_JBLK_WALK_CPP_DATA jblkData;
+        jblkData.cppAction = a_action;
+        jblkData.cPtr = a_ptr;
+        return m_fsInfo->jblk_walk(m_fsInfo, a_addr1, a_addr2, a_num, tsk_fs_jblk_walk_cpp_c_cb, &jblkData);
+    };
+
+    /**
+    * \internal
+    */
+    uint8_t jentryWalk(int a_num, TSK_FS_JENTRY_WALK_CPP_CB a_action, void *a_ptr) {
+        if (m_fsInfo == NULL)
+            return 0;
+        TSK_FS_JENTRY_WALK_CPP_DATA jentryData;
+        jentryData.cppAction = a_action;
+        jentryData.cPtr = a_ptr;
+        return m_fsInfo->jentry_walk(m_fsInfo, a_num, tsk_fs_jentry_walk_cpp_c_cb, &jentryData);
+
+    };
+
+    /**
+        * Parse a string with the file system type and return its internal ID.
+    * See tsk_fs_type_toid() for details
+    * @param a_str String to parse.
+    * @returns ID of string (or unsupported if the name is unknown)
+    */
+    static const TSK_FS_TYPE_ENUM typeToId(const TSK_TCHAR * a_str) {
+        return tsk_fs_type_toid(a_str);
+    };
+
+    /**
+        * Return the string name of a file system type id.
+    * See tsk_fs_type_toname() for details
+    * @param a_ftype File system type id
+    * @returns Name or NULL on error
+    */
+    static const char * typeToName(TSK_FS_TYPE_ENUM a_ftype) {
+        return tsk_fs_type_toname(a_ftype);
+    };
+
+    /**
+        * Return the supported file system types. 
+    * See tsk_fs_type_supported() for details
+    * @returns The bit in the return value is 1 if the type is supported.
+    */
+    static TSK_FS_TYPE_ENUM typeSupported() {
+        return tsk_fs_type_supported();
+    };
+
+    /**
+        * Print the supported file system types to a file handle
+    * See tsk_fs_type_print() for details
+    * @param a_hFile File handle to print to
+    */
+    static void typePrint(FILE * a_hFile) {
+        tsk_fs_type_print(a_hFile);
+    };
+
+    /**
+        * 
+    * Find the meta data address for a given file name (UTF-8).
+    * See tsk_fs_path2inum() for details
+
+    * @param a_path UTF-8 path of file to search for
+    * @param [out] a_result Meta data address of file
+    * @param [out] a_fs_name Copy of name details (or NULL if details not wanted)
+    * @returns -1 on (system) error, 0 if found, and 1 if not found
+    */
+    int8_t path2INum(const char *a_path,
+        TSK_INUM_T * a_result, TskFsName * a_fs_name) {
+            if (m_fsInfo!=NULL)
+                return tsk_fs_path2inum(m_fsInfo, a_path, a_result, a_fs_name->m_fsName);
+            else
+                return -1;
+    };
+
+    /**
+        * Parse a TSK_TCHAR string of an inode, type, and id pair (not all parts
+    * need to be there).  This assumes the string is either:
+    * INUM, INUM-TYPE, or INUM-TYPE-ID.  Return the values in integer form. 
+    * See tsk_fs_parse_inum() for details
+    * @param [in] a_str Input string to parse
+    * @param [out] a_inum Pointer to location where inode can be stored.
+    * @param [out] a_type Pointer to location where type can be stored (or NULL)
+    * @param [out] a_type_used Pointer to location where the value can be set
+    * to 1 if the type was set (to differentiate between meanings of 0) (or NULL).
+    * @param [out] a_id Pointer to location where id can be stored (or NULL)
+    * @param [out] a_id_used Pointer to location where the value can be set
+    * to 1 if the id was set (to differentiate between meanings of 0) (or NULL).
+    *
+    * @return 1 on error or if not an inode and 0 on success
+    */
+    static int parseINum(const TSK_TCHAR * a_str, TSK_INUM_T * a_inum,
+        TSK_FS_ATTR_TYPE_ENUM * a_type, uint8_t * a_type_used, uint16_t * a_id,
+        uint8_t * a_id_used) {
+            return tsk_fs_parse_inum(a_str, a_inum, a_type, a_type_used, a_id, a_id_used);
+    };
+
+    /**
+        * return byte offset in image that fs starts 
+    * @return offset in bytes.
+    */
+    TSK_OFF_T getOffset() const{
+        if (m_fsInfo!=NULL)
+            return m_fsInfo->offset;
+        else
+            return 0;
+    };
+    
+    /**
+        * return number of metadata addresses in FS
+    * @return number of metatdata addresses 
+    */
+    TSK_INUM_T getINumCount() const {
+        if (m_fsInfo!=NULL)
+            return m_fsInfo->inum_count;
+        else
+            return 0;
+    };
+    
+    /**
+        * return metadata address of root directory 
+    * @return metadata address of root directory  
+    */
+    TSK_INUM_T getRootINum() const {
+        if (m_fsInfo!=NULL)
+            return m_fsInfo->root_inum;
+        else
+            return 0;
+    };
+    /**
+        * return first valid metadata address
+    * @return first valid metadata address 
+    */
+    TSK_INUM_T getFirstINum() const {
+        if (m_fsInfo!=NULL)
+            return m_fsInfo->first_inum;
+        else
+            return 0;
+    };
+    /**
+        * return last valid metadata address
+    * @return last valid metadata address 
+    */
+    TSK_INUM_T getLastINum() const {
+        if (m_fsInfo!=NULL)
+            return m_fsInfo->last_inum;
+        else
+            return 0;
+    };
+    /**
+        * return address of journal inode
+    * @return address of journal inode 
+    */
+    TSK_INUM_T getJournalINum() const { 
+        if (m_fsInfo!=NULL)
+            return m_fsInfo->journ_inum;
+        else
+            return 0;
+    };
+    
+    /**
+        * return number of blocks in fs
+    * @return number of blocks in fs 
+    */
+    TSK_DADDR_T getBlockCount() const {
+        if (m_fsInfo!=NULL)
+            return m_fsInfo->block_count;
+        else
+            return 0;
+    };
+    /**
+        * return address of first block
+    * @return address of first block 
+    */
+    TSK_DADDR_T getFirstBlock() const {
+        if (m_fsInfo!=NULL)
+            return m_fsInfo->first_block;
+        else
+            return 0;
+    };
+    /**
+        * return address of last block as reported by file system
+    * (it is equal to the last block in the image or volume (if image is not complete)
+    * @return address of last block 
+    */
+    TSK_DADDR_T getLastBlockAct() const {
+        if (m_fsInfo!=NULL)
+            return m_fsInfo->last_block_act;
+        else
+            return 0;
+    };
+    /**
+        * return address of last block that is adjusted so that 
+    * (could be larger than last_block in image if end of image does not exist)
+    * @return address of last block 
+    */
+    TSK_DADDR_T getLastBlock() const {
+        if (m_fsInfo!=NULL)
+            return m_fsInfo->last_block;
+        else
+            return 0;
+    };
+    /**
+        * return size of each file system block (in bytes)
+    * @return size of each block 
+    */
+    unsigned int getBlockSize() const {
+        if (m_fsInfo!=NULL)
+            return m_fsInfo->block_size;
+        else
+            return 0;
+    };
+    /**
+        * return size of device block (typically always 512)
+    * @return size of device block 
+    */
+    unsigned int getDeviceSize() const {
+        if (m_fsInfo!=NULL)
+            return m_fsInfo->dev_bsize;
+        else
+            return 0;
+    };
+
+    /**
+        * return type of file system 
+    * @return type of file system 
+    */
+    TSK_FS_TYPE_ENUM  getFsType() const {
+        if (m_fsInfo!=NULL)
+            return m_fsInfo->ftype;
+        else 
+            return (TSK_FS_TYPE_ENUM)0;
+    };
+    /**
+        * return the "name" of data unit type  as a string ("Cluster", for example)
+    * @return string "name" of data unit type 
+    */
+    const char * getDataUnitName() const {
+        if (m_fsInfo!=NULL)
+            return m_fsInfo->duname;
+        else
+            return NULL;
+    };
+
+    /**
+        * return flags for file system
+    * @return flags for file system 
+    */
+    TSK_FS_INFO_FLAG_ENUM  getFlags() const {
+        if (m_fsInfo!=NULL)
+            return m_fsInfo->flags;
+        else
+            return (TSK_FS_INFO_FLAG_ENUM)0;
+    };
+    /**
+        * return file system id (as reported in boot sector).  Use getFsIdLen() to determine how many byts in buffer are used. 
+    * @return Buffer with file system id 
+    */
+    const uint8_t *getFsId() const {
+        if (m_fsInfo!=NULL)
+            return m_fsInfo->fs_id;
+        else
+            return 0;
+    };
+
+    /**
+        * return the number of bytes used in the buffer returned by getFsId().
+    * @return number of bytes used.
+    */
+    size_t getFsIdLen() const {
+        if (m_fsInfo == NULL)
+            return 0;
+
+        return m_fsInfo->fs_id_used;
+    };
+    
+    /**
+      * Close an open file system. See tsk_fs_close() for details.
+     */
+    void close()
+    {
+        tsk_fs_close(m_fsInfo);
+    };
+
+
+private:
+    const TSK_IMG_INFO * getTskImgInfo() const {
+        if (m_fsInfo!=NULL)
+            return m_fsInfo->img_info;
+        else
+            return NULL;
+    }
+};//TskFsInfo
+
+
+
+/**
+* \ingroup fslib_cpp
+* Stores information about a file system block.  Must be created by either
+* allocating an empty block and opening one or by passing in a TSK_FS_BLOCK struct.
+* If NULL is passed to the contstructor and open() is not called, the other methods
+* return undefined data. See TSK_FS_BLOCK for more details.
+*/
+class TskFsBlock{
+private:
+    TSK_FS_BLOCK * m_fsBlock;
+    bool m_opened; // true if open() was called and we need to free it
+    
+    TskFsBlock(const TskFsBlock& rhs); 
+    TskFsBlock& operator=(const TskFsBlock& rhs);
+    
+public:
+    /**
+    * constuct a TskFsBlock using a TSK_FS_BLOCK structure
+    * @param a_fsBlock a pointer of TSK_FS_BLOCK.  If NULL, the getX() methods return undefined data. 
+    */
+    TskFsBlock(const TSK_FS_BLOCK * a_fsBlock) {
+        m_fsBlock = const_cast<TSK_FS_BLOCK *> (a_fsBlock);
+        m_opened = false;
+    };
+
+    /**
+    * default constructor to constuct a TskFsBlock.  Must call open() before using other methods.
+    */
+    TskFsBlock() {
+        m_fsBlock = NULL;
+    };
+
+    /**
+        * Free the memory associated with the TSK_FS_BLOCK structure. 
+    * See tsk_fs_block_free() for details
+    */
+    ~TskFsBlock() {
+        if (m_opened)
+            tsk_fs_block_free(m_fsBlock);
+        m_fsBlock = NULL;
+    };
+
+    /**
+        * Open a block (use only if created with default constructor).
+    *
+    * @param a_fs The file system to read the block from.
+    * @param a_addr The file system address to read.
+    * @return 1 on error and 0 on success.
+    */
+    uint8_t open(TskFsInfo * a_fs, TSK_DADDR_T a_addr) {
+        if (m_fsBlock)
+            return 1;
+
+        if (( m_fsBlock = tsk_fs_block_get(a_fs->m_fsInfo,m_fsBlock, a_addr)) != NULL) {
+            m_opened = true;
+            return 0;
+        }
+        else  {
+            return 1;
+        }
+    };
+
+    /**
+        * Get buffer with block data (of size TSK_FS_INFO::block_size)
+    *
+    * @return buffer with block data
+    */
+    const char * getBuf() const {
+        if (m_fsBlock!=NULL)
+            return m_fsBlock->buf;
+        else
+            return NULL;
+    };
+
+    /**
+        * Get address of block
+    * @return address of block
+    */
+    TSK_DADDR_T getAddr() const {
+        if (m_fsBlock!=NULL)
+            return m_fsBlock->addr;
+        else
+            return 0;
+    };
+
+    /**
+        * Get flags for block (alloc or unalloc)
+    * @return flags for block 
+    */
+    TSK_FS_BLOCK_FLAG_ENUM getFlags() const {
+        if (m_fsBlock!=NULL)
+            return m_fsBlock->flags;
+        else
+            return (TSK_FS_BLOCK_FLAG_ENUM)0;
+    };
+
+private:
+    /**
+        * Get pointer to file system that block is from
+    * @return pointer to file system that block is from
+    */
+    const TSK_FS_INFO * getFsInfo() const {
+        if (m_fsBlock!=NULL)
+            return m_fsBlock->fs_info;
+        else
+            return NULL;
+    };
+};
+
+
+
+/**
+ * \ingroup fslib_cpp
+ * Stores information about names that are located in metadata structures.  See
+ * TSK_FS_META_NAME for more details.
+ */
+class TskFsMetaName {
+private:
+    TSK_FS_META_NAME_LIST *m_fsMetaNameList;
+    TskFsMetaName(const TskFsMetaName& rhs); 
+    TskFsMetaName& operator=(const TskFsMetaName& rhs);
+    
+public:
+    /**
+     * Allocates an object based on a C struct.
+     * @param a_fsMetaNameList C struct of name list. If NULL, get() methods return undefined values.
+     */
+    TskFsMetaName (TSK_FS_META_NAME_LIST *a_fsMetaNameList) {
+        m_fsMetaNameList = a_fsMetaNameList;
+    };
+    
+    /**
+     * Get the text name in UTF-8 (does not include parent directory name).
+     * @returns name of file.
+     */
+    const char *getName() const {
+        if (m_fsMetaNameList != NULL)
+            return m_fsMetaNameList->name;
+        else
+            return NULL;
+    };
+    
+    /**
+     * Get the parent inode (NTFS Only)
+     * @return Address of parent directory.
+     */
+    TSK_INUM_T getParInode() const {
+        if (m_fsMetaNameList != NULL)
+            return m_fsMetaNameList->par_inode;
+        else
+            return 0;
+    };
+    
+    /** 
+     * get the parent sequence (NTFS Only)
+     * @return Sequence of parent directory.
+     */
+    uint32_t getParSeq() const {
+        return m_fsMetaNameList->par_seq;
+    };
+};
+
+/**
+ * \ingroup fslib_cpp
+ * Stores metadata about a file. See TSK_FS_META for more details.
+ */
+class TskFsMeta{
+private:
+    TSK_FS_META *m_fsMeta;
+    TskFsMeta(const TskFsMeta& rhs); 
+    TskFsMeta& operator=(const TskFsMeta& rhs);
+    
+public:
+    /**
+          * construct a TskFsMeta object.  If NULL is passed as an argument, the getX() behavior
+     * is not defined.
+     * @param a_fsMeta a pointer of TSK_FS_META
+     */
+    TskFsMeta(TSK_FS_META * a_fsMeta) {
+        m_fsMeta = a_fsMeta;
+#if 0
+        if (m_fsMeta != NULL) {
+            m_nameList = m_fsMeta->name2;
+            size_t numOfList = 0;
+            TSK_FS_META_NAME_LIST * nameList = m_nameList;
+            while(nameList != NULL) {
+                nameList = nameList->next;
+                numOfList += 1;
+            }
+            m_nameListLen = numOfList;
+        }
+        else{
+            m_nameList = NULL;
+            m_nameListLen = 0;
+        }
+#endif
+    };
+    
+    ~TskFsMeta() {
+    };
+    
+    /**
+          * Makes the "ls -l" permissions string for a file. 
+     * See tsk_fs_meta_make_ls() for details
+     * @param a_buf [out] Buffer to write results to (must be 12 bytes or longer)
+     * @param a_len Length of buffer
+     */
+    uint8_t getLs(char *a_buf, size_t a_len) const {
+        if (m_fsMeta != NULL)
+            return tsk_fs_meta_make_ls(m_fsMeta, a_buf, a_len);
+        else
+            return 0;
+    };
+    /**
+          * get flags for this file for its allocation status etc.
+     * @return flags for this file
+     */
+    TSK_FS_META_FLAG_ENUM getFlags() const {
+        if (m_fsMeta != NULL)
+           return m_fsMeta->flags;
+        else
+            return (TSK_FS_META_FLAG_ENUM)0;
+    }
+    /**
+          * get address of the meta data structure for this file
+     * @return address of the meta data structure for this file
+     */
+    TSK_INUM_T getAddr() const {
+        if (m_fsMeta != NULL)
+            return m_fsMeta->addr;
+        else
+            return 0;
+    };
+    /**
+          * get file type
+     * @return file type
+     */
+    TSK_FS_META_TYPE_ENUM getType() const {
+        if (m_fsMeta != NULL)
+            return m_fsMeta->type;
+        else
+            return (TSK_FS_META_TYPE_ENUM)0;
+    };
+    /**
+          * get Unix-style permissions
+     * @return Unix-style permissions mode
+     */
+    TSK_FS_META_MODE_ENUM getMode() const {
+        if (m_fsMeta != NULL)
+            return m_fsMeta->mode;
+        else
+            return (TSK_FS_META_MODE_ENUM)0;
+    };
+    /**
+          * get link count (number of file names pointing to this)
+     * @return link count
+     */
+    int getNLink() const {
+        if (m_fsMeta != NULL)
+            return m_fsMeta->nlink;
+        else
+            return 0;
+    };
+    /**
+          * get file size (in bytes)
+     * @return file size
+     */
+    TSK_OFF_T getSize() const {
+        if (m_fsMeta != NULL)
+            return m_fsMeta->size;
+        else 
+            return 0;
+    };
+    /**
+          * get owner id
+     * @return owner id
+     */
+    TSK_UID_T  getUid() const {
+        if (m_fsMeta != NULL)
+            return m_fsMeta->uid;
+        else
+            return 0;
+    };
+    
+    /**
+          * get group id
+     * @return group id
+     */
+    TSK_GID_T getGid() const {
+        if (m_fsMeta != NULL)
+            return m_fsMeta->gid;
+        else
+            return 0;
+    };
+    
+    /**
+          * get last file content modification time (stored in number of seconds since Jan 1, 1970 UTC)
+     * @return last file content modification time
+     */
+    time_t getMTime() const {
+        if (m_fsMeta != NULL)
+            return m_fsMeta->mtime;
+        else
+            return 0;
+    };
+    
+    /**
+          * get nano-second resolution of modification time
+     * @return nano-second resolution of modification time
+     */
+    uint32_t getMTimeNano() const {
+        if (m_fsMeta != NULL)
+            return m_fsMeta->mtime_nano;
+        else
+            return 0;
+    };
+    
+    /**
+          * get last file content accessed time (stored in number of seconds since Jan 1, 1970 UTC)
+     * @return last file content accessed time
+     */
+    time_t getATime() const {
+        if (m_fsMeta != NULL)
+            return m_fsMeta->atime;
+        else
+            return 0;
+    };
+    
+    /**
+          * get nano-second resolution of accessed time
+     * @return nano-second resolution of accessed time
+     */
+    uint32_t getATimeNano() const {
+        if (m_fsMeta != NULL)
+            return m_fsMeta->atime_nano;
+        else
+            return 0;
+    };
+    
+    /**
+          * get last file / metadata status change time (stored in number of seconds since Jan 1, 1970 UTC)
+     * @return last file / metadata status change time
+     */
+    time_t getCTime() const {
+        if (m_fsMeta != NULL)
+            return m_fsMeta->ctime;
+        else
+            return 0;
+    };
+    
+    /**
+          * get nano-second resolution of change time
+     * @return nano-second resolution of change time
+     */
+    uint32_t getCTimeNano() const {
+        if (m_fsMeta != NULL)
+            return m_fsMeta->ctime_nano;
+        else
+            return 0;
+    };
+    
+    /**
+          * get created time (stored in number of seconds since Jan 1, 1970 UTC)
+     * @return created time
+     */
+    time_t getCrTime() const {
+        if (m_fsMeta != NULL)
+            return m_fsMeta->crtime;
+        else
+            return 0;
+    };
+    
+    /**
+          * get nano-second resolution of created time
+     * @return nano-second resolution of created time
+     */
+    uint32_t getCrTimeNano() const {
+        if (m_fsMeta != NULL)
+            return m_fsMeta->crtime_nano;
+        else
+            return 0;
+    };
+    
+    /**
+          * get linux deletion time
+     * @return linux deletion time
+     */
+    time_t getDTime() const {
+        if (m_fsMeta != NULL)
+            return m_fsMeta->time2.ext2.dtime;
+        else
+            return 0;
+    };
+    
+    /**
+          * get nano-second resolution of deletion time
+     * @return nano-second resolution of deletion time
+     */
+    uint32_t getDTimeNano() const {
+        if (m_fsMeta != NULL)
+            return m_fsMeta->time2.ext2.dtime_nano;
+        else
+            return 0;
+    };
+    
+    /**
+          * get HFS+ backup time
+     * @return HFS+ backup time
+     */
+    time_t getBackUpTime() const {
+        if (m_fsMeta != NULL)
+            return m_fsMeta->time2.hfs.bkup_time;
+        else
+            return 0;
+    };
+    
+    /**
+          * get nano-second resolution of HFS+ backup time
+     * @return nano-second resolution of HFS+ backup time
+     */
+    uint32_t getBackUpTimeNano() const {
+        if (m_fsMeta != NULL)
+            return m_fsMeta->time2.hfs.bkup_time_nano;
+        else
+            return 0;
+    };
+    
+    /**
+          * get sequence number for file (NTFS only, is incremented when entry is reallocated) 
+     * @return sequence number for file
+     */
+    uint32_t getSeq() const {
+        if (m_fsMeta != NULL)
+            return m_fsMeta->seq;
+        //zli: should we throw err msg
+    };
+    
+    /**
+          * get name of target file if this is a symbolic link
+     * @return name of target file if this is a symbolic link
+     */
+    const char * getLink() const {
+        if (m_fsMeta != NULL)
+            return m_fsMeta->link;
+        else
+            return NULL;
+    };
+    
+    /**
+     * Return the number of names that are stored in the metadata.
+     * @returns number of names.
+     */
+    int getName2Count() const {
+        int size = 0;
+        if (m_fsMeta != NULL) {
+            TSK_FS_META_NAME_LIST *name = m_fsMeta->name2;
+            while(name != NULL) {
+                size++;
+                name = name->next;
+            }
+        }
+        return size;
+    };
+    
+    /**
+     * Return a name that is stored in the metadata.  
+     * @param a_idx Index of the name to return
+     * @returns NULL on error.  Caller must free this memory.
+     */
+    const TskFsMetaName *getName2(int a_idx) const {
+        if (m_fsMeta != NULL) {
+            TSK_FS_META_NAME_LIST *name = m_fsMeta->name2;
+            int i = 0;
+            while(name != NULL) {
+                if (i == a_idx)
+                    return new TskFsMetaName(name);
+                i++;
+                name = name->next;
+            }
+        }
+        return NULL;
+    };
+    
+private:
+    /**
+          * get structure used as the head of an attribute list
+     * @return structure used as the head of an attribute list
+     */
+    const TSK_FS_ATTRLIST * getAttr() const {
+        if (m_fsMeta != NULL)
+            return m_fsMeta->attr;
+        else
+            return NULL;
+    };
+};
+
+
+/**
+ * \ingroup fslib_cpp
+* Class that represents an allocated or deleted file. The non-default constructor or
+* open method must be called first.  otherwise, the results of the getX() methods are
+* undefined. See TSK_FS_FILE for more details. 
+*/
+class TskFsFile{
+private:
+    TSK_FS_FILE * m_fsFile;
+    bool m_opened;
+    TskFsFile(const TskFsFile& rhs); 
+    TskFsFile& operator=(const TskFsFile& rhs);
+    
+public:
+    /**
+        * Construct a TskFsFile object from a C struct
+    * @param a_fsFile a pointer of TSK_FS_FILE
+    */
+    TskFsFile(TSK_FS_FILE *a_fsFile) {
+        m_fsFile = a_fsFile;
+        m_opened = false;
+    };
+
+    /**
+        * default constructor to construct a TskFsFile object
+    */
+    TskFsFile() {
+        m_fsFile = NULL;
+        m_opened = false;
+    };
+
+    /**
+        * Close an open file.
+    */
+    ~TskFsFile() {
+        close();
+    };
+
+    /**
+        * Close an open file.
+    * See tsk_fs_file_close() for details.
+    */
+    void close() {
+        if (m_opened)
+            tsk_fs_file_close(m_fsFile);
+        m_fsFile = NULL;
+    };
+
+    /** 
+        *
+    * Open a file given its metadata address. This function loads the metadata
+    * and returns a handle that can be used to read and process the file.   Note
+    * that the returned class will not have the file name set because
+    * it was not used to load the file and this function does not search the 
+    * directory structure to find the name that points to the address.   In general,
+    * if you know the metadata address of a file, this function is more effecient 
+    * then tsk_fs_file_open, which first maps a file name to the metadata address 
+    * and then open the file using this function. 
+    * See tsk_fs_file_open_meta() for details
+    * @param a_fs File system to analyze
+    * @param a_fs_file object to store file data in or NULL to have one allocated. 
+    * @param a_addr Metadata address of file to lookup
+    * @returns 1 on error and 0 on success.
+    */
+    uint8_t open(TskFsInfo * a_fs, TskFsFile * a_fs_file, TSK_INUM_T a_addr) {
+        if ((m_fsFile = tsk_fs_file_open_meta(a_fs->m_fsInfo,
+            a_fs_file->m_fsFile, a_addr)) != NULL) {
+            m_opened = true;
+            return 0;
+        }
+        else {
+            return 1;
+        }
+    };
+    
+    /** 
+        * Return the handle structure for a specific file, given its full path. Note that
+    * if you have the metadata address fo the file, then tsk_fs_file_open_meta() is a
+    * more effecient approach. 
+    * See tsk_fs_file_open() for details
+    * @param a_fs File system to analyze
+    * @param a_fs_file Structure to store file data in or NULL to have one allocated. 
+    * @param a_path Path of file to open
+    * @returns 1 on error and 0 on success.
+    */
+    uint8_t open(TskFsInfo * a_fs, TskFsFile * a_fs_file, const char *a_path) {
+        if ((m_fsFile = tsk_fs_file_open(a_fs->m_fsInfo, a_fs_file->m_fsFile, a_path))
+            != NULL) {
+            m_opened = true;
+            return 0;
+        }
+        else {
+            return 1;
+        }
+    };
+
+    /*    * Return the number of attributes in the file. 
+    * See tsk_fs_file_attr_getsize() for details
+    * @returns number of attributes in file
+    */
+    int getAttrSize() {
+        return tsk_fs_file_attr_getsize(m_fsFile);//m_fsFile is checked by this C function
+    };
+
+    /*    * Get a file's attribute based on the 0-based index in the list (and not type, id pair).
+    * It's caller's responsibility to free TskFsAttribute*
+    * See tsk_fs_file_attr_get_idx() for details
+    * @param a_idx 0-based index of attribute to return.
+    * @returns Pointer to attribute or NULL on error
+    */
+    const TskFsAttribute * getAttr(int a_idx) {
+        TskFsAttribute * fsAttr = new TskFsAttribute(tsk_fs_file_attr_get_idx(m_fsFile, a_idx));//m_fsFile is checked by this C function
+        return fsAttr;
+    };
+
+    /*    * Return the default attribute for the file
+    * It's caller's responsibility to free TskFsAttribute*
+    * See tsk_fs_file_attr_get() for details
+    * @returns Pointer to attribute or NULL on error
+    */
+    const TskFsAttribute * getAttrDefault() {
+        TskFsAttribute * fsAttr = new TskFsAttribute(tsk_fs_file_attr_get(m_fsFile));//m_fsFile is checked by this C function
+        return fsAttr;
+    };
+
+    /*    * Return a specific type and id attribute for the file.
+    * It's caller's responsibility to free TskFsAttribute*
+    * See tsk_fs_file_attr_get_type() for details
+    * @param a_type Type of attribute to load
+    * @param a_id Id of attribute to load 
+    * @param a_id_used Set to 1 if ID is actually set or 0 to use default
+    * @returns Pointer to attribute or NULL on error
+    */
+    const TskFsAttribute * getAttr(TSK_FS_ATTR_TYPE_ENUM a_type,
+        uint16_t a_id, uint8_t a_id_used) {
+            TskFsAttribute * fsAttr = new TskFsAttribute(tsk_fs_file_attr_get_type(m_fsFile,//m_fsFile is checked by this C function
+                a_type, a_id, a_id_used));
+            return fsAttr;
+    };
+
+    /**
+        * Process a specific attribute in a file and call a callback function with the file contents. 
+    * See tsk_fs_file_walk_type() for details
+    * @param a_type Attribute type to process
+    * @param a_id Id if attribute to process 
+    * @param a_flags Flags to use while processing file
+    * @param a_action Callback action to call with content
+    * @param a_ptr Pointer that will passed to callback
+    * @returns 1 on error and 0 on success.
+    */
+    uint8_t walk(TSK_FS_ATTR_TYPE_ENUM a_type, uint16_t a_id,
+        TSK_FS_FILE_WALK_FLAG_ENUM a_flags, TSK_FS_FILE_WALK_CPP_CB a_action, void *a_ptr) {
+            TSK_FS_FILE_WALK_CPP_DATA fileData;
+            fileData.cppAction = a_action;
+            fileData.cPtr = a_ptr;
+            return tsk_fs_file_walk_type(m_fsFile,a_type,  a_id,
+                a_flags, tsk_fs_file_cpp_c_cb,&fileData);//m_fsFile is checked by this C function
+    };
+
+    /**
+     * Process the default attribute for the file and call a callback function with the file contents. 
+    * See tsk_fs_file_walk_type() for details
+    * @param a_flags Flags to use while processing file
+    * @param a_action Callback action to call with content
+    * @param a_ptr Pointer that will passed to callback
+    * @returns 1 on error and 0 on success.
+    */
+    uint8_t walk(TSK_FS_FILE_WALK_FLAG_ENUM a_flags,
+        TSK_FS_FILE_WALK_CPP_CB a_action, void *a_ptr) {
+            TSK_FS_FILE_WALK_CPP_DATA fileData;
+            fileData.cppAction = a_action;
+            fileData.cPtr = a_ptr;
+            return tsk_fs_file_walk(m_fsFile, a_flags, tsk_fs_file_cpp_c_cb, &fileData);//m_fsFile is checked by this C function
+    };
+
+    /**
+     * Read the contents of a specific attribute of a file using a typical read() type interface.
+    * 0s are returned for missing runs of files. 
+    * See tsk_fs_file_read_type() for details
+    * @param a_type The type of attribute to load
+    * @param a_id The id of attribute to load (use 0 and set a_flags if you do not care)
+    * @param a_offset The byte offset to start reading from.
+    * @param a_buf The buffer to read the data into.
+    * @param a_len The number of bytes to read from the file.
+    * @param a_flags Flags to use while reading
+    * @returns The number of bytes read or -1 on error (incl if offset is past EOF).
+    */
+    ssize_t read(TSK_FS_ATTR_TYPE_ENUM a_type, uint16_t a_id, TSK_OFF_T a_offset,
+        char *a_buf, size_t a_len, TSK_FS_FILE_READ_FLAG_ENUM a_flags) {
+            return tsk_fs_file_read_type(m_fsFile,a_type, a_id, a_offset,
+                a_buf, a_len, a_flags);//m_fsFile is checked by this C function
+    };
+    /**
+     * Read the contents of the default attribute of a file using a typical read() type interface.
+    * 0s are returned for missing runs of files. 
+    * See tsk_fs_file_read() for details
+    * @param a_offset The byte offset to start reading from.
+    * @param a_buf The buffer to read the data into.
+    * @param a_len The number of bytes to read from the file.
+    * @param a_flags Flags to use while reading
+    * @returns The number of bytes read or -1 on error (incl if offset is past EOF).
+    */
+    ssize_t read(TSK_OFF_T a_offset, char *a_buf, size_t a_len,
+        TSK_FS_FILE_READ_FLAG_ENUM a_flags) {
+            return tsk_fs_file_read(m_fsFile, a_offset,a_buf, a_len, a_flags);//m_fsFile is checked by this C function
+    };
+
+    /**
+     * Return pointer to the file's name (or NULL if file was opened using metadata address)
+    * @returns pointer to name of file.  It is the caller's responsibility to free this.
+    */
+     TskFsName * getName() {
+        if (m_fsFile != NULL)
+            return new TskFsName(m_fsFile->name);
+        else
+            return NULL;
+    };
+
+    /**
+     * Return pointer to the file's metadata (or NULL if name has invalid metadata address)
+    * @returns pointer metadata of file. It is the caller's responsibility to free this.
+    */
+     TskFsMeta * getMeta() {
+        if (m_fsFile != NULL)
+            return new TskFsMeta(m_fsFile->meta);
+        else
+            return NULL;
+    };
+
+    /**
+    * Return pointer file system that the file is located in.
+    * @returns pointer to file system that the file is located in.
+    */
+    TskFsInfo * getFsInfo() {
+        if (m_fsFile != NULL)
+            return new TskFsInfo(m_fsFile->fs_info);
+        else
+            return NULL;
+    };
+};//TskFsFile
+
+/**
+ * \ingroup fslib_cpp
+* Stores information about a directory in the file system. The open() method
+* must be called before any of hte other methods return defined data. See
+* TSK_FS_DIR for more details.
+*/
+class TskFsDir{
+private:
+    TSK_FS_DIR *m_fsDir;
+    TskFsDir(const TskFsDir& rhs); 
+    TskFsDir& operator=(const TskFsDir& rhs);
+
+public:
+    TskFsDir() {
+        m_fsDir = NULL;
+    };
+    /*    
+     * Close the directory that was opened with tsk_fs_dir_open()
+    */
+    ~TskFsDir() {
+        close();
+    }
+
+    /*    
+     * Open a directory (using its metadata addr) so that each of the files in it can be accessed.
+    * See for tsk_fs_dir_open_meta() details.
+    * @param a_fs File system to analyze
+    * @param a_addr Metadata address of the directory to open
+    * @returns 1 on error and 0 on success
+    */
+    uint8_t open(TskFsInfo * a_fs, TSK_INUM_T a_addr) {
+        if ((m_fsDir = tsk_fs_dir_open_meta(a_fs->m_fsInfo, a_addr)) != NULL)
+            return 0;
+        else
+            return 1;
+    };
+
+    /*    
+     * Open a directory (using its path) so that each of the files in it can be accessed.
+    * See for tsk_fs_dir_open() details.
+    * @param a_fs File system to analyze
+    * @param a_dir Path of the directory to open
+    * @returns 1 on error and 0 on success
+    */
+    uint8_t open(TskFsInfo * a_fs, const char *a_dir) {
+        if ((m_fsDir = tsk_fs_dir_open(a_fs->m_fsInfo, a_dir)) != NULL)
+            return 0;
+        else
+            return 1;
+    };
+
+    /*    
+     * Close the directory that was opened with tsk_fs_dir_open()
+    * See tsk_fs_dir_close() for details
+    */
+    void close() {
+        tsk_fs_dir_close(m_fsDir);
+    };
+
+    /*    
+     * Returns the number of files and subdirectories in a directory.
+    * See tsk_fs_dir_getsize() for details
+    * @returns Number of files and subdirectories (or 0 on error)
+    */
+    size_t getSize() const {
+        return tsk_fs_dir_getsize(m_fsDir);//m_fsDir is checked by this C function
+    };
+
+    /*    
+     * Return a specific file or subdirectory from an open directory. 
+    * It's caller's responsibility to free TskFsFile*
+    * See tsk_fs_dir_getsize() for details
+    * @param a_idx Index of file in directory to open (0-based)
+    * @returns NULL on error
+    */
+    TskFsFile * getFile(size_t a_idx) const {
+        TSK_FS_FILE *fs_file = tsk_fs_dir_get(m_fsDir, a_idx);
+        if (fs_file != NULL) 
+            return new TskFsFile(fs_file);
+        else
+            return NULL;
+    };
+
+    /*    
+     * Return metadata address of this directory 
+    * @returns metadata address of this directory 
+    */
+     TSK_INUM_T getMetaAddr() const {
+        if (m_fsDir != NULL)
+            return m_fsDir->addr;
+        else 
+            return 0;
+    };
+
+    /*    
+     * Return pointer to the file structure for the directory.
+    * @returns NULL on error. it is the caller's responsibility to free this object.
+    */
+    const TskFsFile * getFsFile() const {
+        if (m_fsDir != NULL)
+            return new TskFsFile(m_fsDir->fs_file);
+        else
+            return NULL;
+    };
+
+private:
+
+    /*    
+     * Return pointer to file system the directory is located in
+    * @returns NULL on error
+    */
+    const TSK_FS_INFO * getFsInfo() const {
+        if (m_fsDir != NULL)
+            return m_fsDir->fs_info;
+        else
+            return NULL;
+    };
+};
+ 
 #endif
 #endif
diff --git a/tsk3/fs/tsk_fs_i.h b/tsk3/fs/tsk_fs_i.h
index 980ba4b21..3c5c14e8e 100644
--- a/tsk3/fs/tsk_fs_i.h
+++ b/tsk3/fs/tsk_fs_i.h
@@ -1,242 +1,249 @@
-/*
-** The Sleuth Kit 
-**
-** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2003-2008 Brian Carrier.  All rights reserved
-**
-** TASK
-** Copyright (c) 2002 @stake Inc.  All rights reserved
-** 
-** Copyright (c) 1997,1998,1999, International Business Machines          
-** Corporation and others. All Rights Reserved.
-*/
-
-/* LICENSE
-* .ad
-* .fi
-*	This software is distributed under the IBM Public License.
-* AUTHOR(S)
-*	Wietse Venema
-*	IBM T.J. Watson Research
-*	P.O. Box 704
-*	Yorktown Heights, NY 10598, USA
---*/
-
-/**
- * \file tsk_fs_i.h
- * Contains the internal library definitions for the file system functions.  This should
- * be included by the code in the file system library.
- */
-
-#ifndef _TSK_FS_I_H
-#define _TSK_FS_I_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// Include the other internal TSK header files
-#include "tsk3/base/tsk_base_i.h"
-#include "tsk3/img/tsk_img_i.h"
-#include "tsk3/vs/tsk_vs_i.h"
-
-// Include the external file 
-#include "tsk_fs.h"
-
-#include <time.h>
-#include <locale.h>
-
-#if !defined (TSK_WIN32)
-#include <sys/fcntl.h>
-#include <sys/time.h>
-#endif
-
-// set to 1 to open HFS+ file systems -- which is not fully tested
-#ifndef TSK_USE_HFS
-#define TSK_USE_HFS 1
-#endif
-
-
-#ifndef NBBY
-#define NBBY 8
-#endif
-
-#ifndef isset
-#define isset(a,i)	(((uint8_t *)(a))[(i)/NBBY] &  (1<<((i)%NBBY)))
-#endif
-
-#ifndef setbit
-#define setbit(a,i)     (((uint8_t *)(a))[(i)/NBBY] |= (1<<((i)%NBBY)))
-#endif
-
-
-/* Data structure and action to internally load a file */
-    typedef struct {
-        char *base;
-        char *cur;
-        size_t total;
-        size_t left;
-    } TSK_FS_LOAD_FILE;
-
-    extern TSK_WALK_RET_ENUM tsk_fs_load_file_action(TSK_FS_FILE * fs_file,
-        TSK_OFF_T, TSK_DADDR_T, char *, size_t, TSK_FS_BLOCK_FLAG_ENUM,
-        void *);
-
-
-    /* BLOCK */
-    extern TSK_FS_BLOCK *tsk_fs_block_alloc(TSK_FS_INFO * fs);
-    extern int tsk_fs_block_set(TSK_FS_INFO * fs, TSK_FS_BLOCK * fs_block,
-        TSK_DADDR_T a_addr, TSK_FS_BLOCK_FLAG_ENUM a_flags, char *a_buf);
-
-    /* FS_DATA */
-    extern TSK_FS_ATTR *tsk_fs_attr_alloc(TSK_FS_ATTR_FLAG_ENUM);
-    extern void tsk_fs_attr_free(TSK_FS_ATTR *);
-    extern void tsk_fs_attr_clear(TSK_FS_ATTR *);
-    extern uint8_t tsk_fs_attr_set_str(TSK_FS_FILE *, TSK_FS_ATTR *,
-        const char *, TSK_FS_ATTR_TYPE_ENUM, uint16_t, void *, size_t);
-    extern uint8_t tsk_fs_attr_set_run(TSK_FS_FILE *,
-        TSK_FS_ATTR * a_fs_attr, TSK_FS_ATTR_RUN * data_run_new,
-        const char *name, TSK_FS_ATTR_TYPE_ENUM type, uint16_t id,
-        TSK_OFF_T size, TSK_OFF_T initsize, TSK_OFF_T allocsize,
-        TSK_FS_ATTR_FLAG_ENUM flags, uint32_t compsize);
-    extern uint8_t tsk_fs_attr_add_run(TSK_FS_INFO * fs,
-        TSK_FS_ATTR * a_fs_attr, TSK_FS_ATTR_RUN * data_run_new);
-    extern void tsk_fs_attr_append_run(TSK_FS_INFO * fs,
-        TSK_FS_ATTR * a_fs_attr, TSK_FS_ATTR_RUN * a_data_run);
-
-    /* FS_DATALIST */
-    extern TSK_FS_ATTRLIST *tsk_fs_attrlist_alloc();
-    extern void tsk_fs_attrlist_free(TSK_FS_ATTRLIST *);
-
-    extern uint8_t tsk_fs_attrlist_add(TSK_FS_ATTRLIST *, TSK_FS_ATTR *);
-    extern TSK_FS_ATTR *tsk_fs_attrlist_getnew(TSK_FS_ATTRLIST *,
-        TSK_FS_ATTR_FLAG_ENUM a_atype);
-    extern void tsk_fs_attrlist_markunused(TSK_FS_ATTRLIST *);
-    extern const TSK_FS_ATTR *tsk_fs_attrlist_get(const TSK_FS_ATTRLIST *,
-        TSK_FS_ATTR_TYPE_ENUM);
-    extern const TSK_FS_ATTR *tsk_fs_attrlist_get_id(const TSK_FS_ATTRLIST
-        *, TSK_FS_ATTR_TYPE_ENUM, uint16_t);
-    extern const TSK_FS_ATTR *tsk_fs_attrlist_get_name_type(const
-        TSK_FS_ATTRLIST *, TSK_FS_ATTR_TYPE_ENUM, const char *);
-    extern const TSK_FS_ATTR *tsk_fs_attrlist_get_idx(const TSK_FS_ATTRLIST
-        *, int);
-    extern int tsk_fs_attrlist_get_len(const TSK_FS_ATTRLIST *
-        a_fs_attrlist);
-
-    /* FS_DATA_RUN */
-    extern TSK_FS_ATTR_RUN *tsk_fs_attr_run_alloc();
-    extern void tsk_fs_attr_run_free(TSK_FS_ATTR_RUN *);
-
-    /* FS_META */
-    extern TSK_FS_META *tsk_fs_meta_alloc(size_t);
-    extern TSK_FS_META *tsk_fs_meta_realloc(TSK_FS_META *, size_t);
-    extern void tsk_fs_meta_reset(TSK_FS_META *);
-    extern void tsk_fs_meta_close(TSK_FS_META * fs_meta);
-
-    /* FS_FILE */
-    extern TSK_FS_FILE *tsk_fs_file_alloc(TSK_FS_INFO *);
-
-    /* FS_DIR */
-    extern TSK_FS_DIR *tsk_fs_dir_alloc(TSK_FS_INFO * a_fs, TSK_INUM_T a_addr, size_t a_cnt);
-    extern uint8_t tsk_fs_dir_realloc(TSK_FS_DIR * a_fs_dir, size_t a_cnt);
-    extern uint8_t tsk_fs_dir_add(TSK_FS_DIR * a_fs_dir,
-        const TSK_FS_NAME * a_fs_dent);
-    extern void tsk_fs_dir_reset(TSK_FS_DIR * a_fs_dir);
-
-    /* Orphan Directory Support */
-    TSK_RETVAL_ENUM tsk_fs_dir_load_inum_named(TSK_FS_INFO * a_fs);
-    extern uint8_t tsk_fs_dir_make_orphan_dir_meta(TSK_FS_INFO * a_fs,
-        TSK_FS_META * a_fs_meta);
-    extern uint8_t tsk_fs_dir_make_orphan_dir_name(TSK_FS_INFO * a_fs,
-        TSK_FS_NAME * a_fs_name);
-    extern TSK_RETVAL_ENUM tsk_fs_dir_find_orphans(TSK_FS_INFO * a_fs,
-        TSK_FS_DIR * a_fs_dir);
-
-
-
-    /* FS_DENT */
-    extern TSK_FS_NAME *tsk_fs_name_alloc(size_t, size_t);
-    extern uint8_t tsk_fs_name_realloc(TSK_FS_NAME *, size_t);
-    extern void tsk_fs_name_free(TSK_FS_NAME *);
-
-    extern void tsk_fs_name_print(FILE *, const TSK_FS_FILE *,
-        const char *, TSK_FS_INFO *, const TSK_FS_ATTR *, uint8_t);
-    extern void tsk_fs_name_print_long(FILE *, const TSK_FS_FILE *,
-        const char *, TSK_FS_INFO *, const TSK_FS_ATTR *, uint8_t,
-        int32_t);
-    extern void tsk_fs_name_print_mac(FILE *, const TSK_FS_FILE *,
-        const char *, const TSK_FS_ATTR * fs_attr, const char *, int32_t);
-    extern uint8_t tsk_fs_name_copy(TSK_FS_NAME * a_fs_name_to,
-        const TSK_FS_NAME * a_fs_name_from);
-    extern void tsk_fs_name_reset(TSK_FS_NAME * a_fs_name);
-
-    /* Utilities */
-    extern uint8_t tsk_fs_unix_make_data_run(TSK_FS_FILE * fs_file);
-    extern TSK_FS_ATTR_TYPE_ENUM tsk_fs_unix_get_default_attr_type(const
-        TSK_FS_FILE * a_file);
-    extern int tsk_fs_unix_name_cmp(TSK_FS_INFO * a_fs_info,
-        const char *s1, const char *s2);
-
-    /* Specific file system routines */
-    extern TSK_FS_INFO *ext2fs_open(TSK_IMG_INFO *, TSK_OFF_T,
-        TSK_FS_TYPE_ENUM, uint8_t);
-    extern TSK_FS_INFO *fatfs_open(TSK_IMG_INFO *, TSK_OFF_T,
-        TSK_FS_TYPE_ENUM, uint8_t);
-    extern TSK_FS_INFO *ffs_open(TSK_IMG_INFO *, TSK_OFF_T,
-        TSK_FS_TYPE_ENUM);
-    extern TSK_FS_INFO *ntfs_open(TSK_IMG_INFO *, TSK_OFF_T,
-        TSK_FS_TYPE_ENUM, uint8_t);
-    extern TSK_FS_INFO *rawfs_open(TSK_IMG_INFO *, TSK_OFF_T);
-    extern TSK_FS_INFO *swapfs_open(TSK_IMG_INFO *, TSK_OFF_T);
-    extern TSK_FS_INFO *iso9660_open(TSK_IMG_INFO *, TSK_OFF_T,
-        TSK_FS_TYPE_ENUM, uint8_t);
-    extern TSK_FS_INFO *hfs_open(TSK_IMG_INFO *, TSK_OFF_T,
-        TSK_FS_TYPE_ENUM, uint8_t);
-
-    /* Generic functions for swap and raw -- many say "not supported" */
-    extern uint8_t tsk_fs_nofs_fsstat(TSK_FS_INFO * fs, FILE * hFile);
-    extern void tsk_fs_nofs_close(TSK_FS_INFO * fs);
-    extern TSK_FS_ATTR_TYPE_ENUM tsk_fs_nofs_get_default_attr_type(const
-        TSK_FS_FILE * a_file);
-    extern uint8_t tsk_fs_nofs_make_data_run(TSK_FS_FILE *);
-    extern int tsk_fs_nofs_name_cmp(TSK_FS_INFO *, const char *,
-        const char *);
-
-    extern TSK_FS_BLOCK_FLAG_ENUM tsk_fs_nofs_block_getflags(TSK_FS_INFO *
-        a_fs, TSK_DADDR_T a_addr);
-    extern uint8_t tsk_fs_nofs_block_walk(TSK_FS_INFO * fs,
-        TSK_DADDR_T a_start_blk, TSK_DADDR_T a_end_blk,
-        TSK_FS_BLOCK_WALK_FLAG_ENUM a_flags, TSK_FS_BLOCK_WALK_CB a_action,
-        void *a_ptr);
-
-    extern uint8_t tsk_fs_nofs_file_add_meta(TSK_FS_INFO * fs,
-        TSK_FS_FILE * a_fs_file, TSK_INUM_T inum);
-    extern uint8_t tsk_fs_nofs_inode_walk(TSK_FS_INFO * fs,
-        TSK_INUM_T a_start_inum, TSK_INUM_T a_end_inum,
-        TSK_FS_META_FLAG_ENUM a_flags, TSK_FS_META_WALK_CB a_action,
-        void *a_ptr);
-    extern uint8_t tsk_fs_nofs_istat(TSK_FS_INFO * a_fs, FILE * hFile,
-        TSK_INUM_T inum, TSK_DADDR_T numblock, int32_t sec_skew);
-
-    extern TSK_RETVAL_ENUM tsk_fs_nofs_dir_open_meta(TSK_FS_INFO * a_fs,
-        TSK_FS_DIR ** a_fs_dir, TSK_INUM_T a_addr);
-
-    extern uint8_t tsk_fs_nofs_jopen(TSK_FS_INFO * a_fs, TSK_INUM_T inum);
-    extern uint8_t tsk_fs_nofs_jentry_walk(TSK_FS_INFO * a_fs, int a_flags,
-        TSK_FS_JENTRY_WALK_CB a_action, void *a_ptr);
-    extern uint8_t tsk_fs_nofs_jblk_walk(TSK_FS_INFO * a_fs,
-        TSK_INUM_T start, TSK_INUM_T end, int a_flags,
-        TSK_FS_JBLK_WALK_CB a_action, void *a_ptr);
-
-// Endian macros - actual functions in misc/
-
-#define tsk_fs_guessu16(fs, x, mag)   \
-	tsk_guess_end_u16(&(fs->endian), (x), (mag))
-
-#define tsk_fs_guessu32(fs, x, mag)   \
-	tsk_guess_end_u32(&(fs->endian), (x), (mag))
-
-#ifdef __cplusplus
-}
-#endif
-#endif
+/*
+** The Sleuth Kit 
+**
+** Brian Carrier [carrier <at> sleuthkit [dot] org]
+** Copyright (c) 2003-2011 Brian Carrier.  All rights reserved
+**
+** TASK
+** Copyright (c) 2002 @stake Inc.  All rights reserved
+** 
+** Copyright (c) 1997,1998,1999, International Business Machines          
+** Corporation and others. All Rights Reserved.
+*/
+
+/* LICENSE
+* .ad
+* .fi
+*	This software is distributed under the IBM Public License.
+* AUTHOR(S)
+*	Wietse Venema
+*	IBM T.J. Watson Research
+*	P.O. Box 704
+*	Yorktown Heights, NY 10598, USA
+--*/
+
+/**
+ * \file tsk_fs_i.h
+ * Contains the internal library definitions for the file system functions.  This should
+ * be included by the code in the file system library.
+ */
+
+#ifndef _TSK_FS_I_H
+#define _TSK_FS_I_H
+
+
+// Include the other internal TSK header files
+#include "tsk3/base/tsk_base_i.h"
+#include "tsk3/img/tsk_img_i.h"
+#include "tsk3/vs/tsk_vs_i.h"
+
+// Include the external file 
+#include "tsk_fs.h"
+
+#include <time.h>
+#include <locale.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if !defined (TSK_WIN32)
+#include <sys/fcntl.h>
+#include <sys/time.h>
+#endif
+
+// set to 1 to open HFS+ file systems -- which is not fully tested
+#ifndef TSK_USE_HFS
+#define TSK_USE_HFS 1
+#endif
+
+
+#ifndef NBBY
+#define NBBY 8
+#endif
+
+#ifndef isset
+#define isset(a,i)	(((uint8_t *)(a))[(i)/NBBY] &  (1<<((i)%NBBY)))
+#endif
+
+#ifndef setbit
+#define setbit(a,i)     (((uint8_t *)(a))[(i)/NBBY] |= (1<<((i)%NBBY)))
+#endif
+
+
+/* Data structure and action to internally load a file */
+    typedef struct {
+        char *base;
+        char *cur;
+        size_t total;
+        size_t left;
+    } TSK_FS_LOAD_FILE;
+
+    extern TSK_WALK_RET_ENUM tsk_fs_load_file_action(TSK_FS_FILE * fs_file,
+        TSK_OFF_T, TSK_DADDR_T, char *, size_t, TSK_FS_BLOCK_FLAG_ENUM,
+        void *);
+
+
+    /* BLOCK */
+    extern TSK_FS_BLOCK *tsk_fs_block_alloc(TSK_FS_INFO * fs);
+    extern int tsk_fs_block_set(TSK_FS_INFO * fs, TSK_FS_BLOCK * fs_block,
+        TSK_DADDR_T a_addr, TSK_FS_BLOCK_FLAG_ENUM a_flags, char *a_buf);
+
+    /* FS_DATA */
+    extern TSK_FS_ATTR *tsk_fs_attr_alloc(TSK_FS_ATTR_FLAG_ENUM);
+    extern void tsk_fs_attr_free(TSK_FS_ATTR *);
+    extern void tsk_fs_attr_clear(TSK_FS_ATTR *);
+    extern uint8_t tsk_fs_attr_set_str(TSK_FS_FILE *, TSK_FS_ATTR *,
+        const char *, TSK_FS_ATTR_TYPE_ENUM, uint16_t, void *, size_t);
+    extern uint8_t tsk_fs_attr_set_run(TSK_FS_FILE *,
+        TSK_FS_ATTR * a_fs_attr, TSK_FS_ATTR_RUN * data_run_new,
+        const char *name, TSK_FS_ATTR_TYPE_ENUM type, uint16_t id,
+        TSK_OFF_T size, TSK_OFF_T initsize, TSK_OFF_T allocsize,
+        TSK_FS_ATTR_FLAG_ENUM flags, uint32_t compsize);
+    extern uint8_t tsk_fs_attr_add_run(TSK_FS_INFO * fs,
+        TSK_FS_ATTR * a_fs_attr, TSK_FS_ATTR_RUN * data_run_new);
+    extern void tsk_fs_attr_append_run(TSK_FS_INFO * fs,
+        TSK_FS_ATTR * a_fs_attr, TSK_FS_ATTR_RUN * a_data_run);
+
+    /* FS_DATALIST */
+    extern TSK_FS_ATTRLIST *tsk_fs_attrlist_alloc();
+    extern void tsk_fs_attrlist_free(TSK_FS_ATTRLIST *);
+
+    extern uint8_t tsk_fs_attrlist_add(TSK_FS_ATTRLIST *, TSK_FS_ATTR *);
+    extern TSK_FS_ATTR *tsk_fs_attrlist_getnew(TSK_FS_ATTRLIST *,
+        TSK_FS_ATTR_FLAG_ENUM a_atype);
+    extern void tsk_fs_attrlist_markunused(TSK_FS_ATTRLIST *);
+    extern const TSK_FS_ATTR *tsk_fs_attrlist_get(const TSK_FS_ATTRLIST *,
+        TSK_FS_ATTR_TYPE_ENUM);
+    extern const TSK_FS_ATTR *tsk_fs_attrlist_get_id(const TSK_FS_ATTRLIST
+        *, TSK_FS_ATTR_TYPE_ENUM, uint16_t);
+    extern const TSK_FS_ATTR *tsk_fs_attrlist_get_name_type(const
+        TSK_FS_ATTRLIST *, TSK_FS_ATTR_TYPE_ENUM, const char *);
+    extern const TSK_FS_ATTR *tsk_fs_attrlist_get_idx(const TSK_FS_ATTRLIST
+        *, int);
+    extern int tsk_fs_attrlist_get_len(const TSK_FS_ATTRLIST *
+        a_fs_attrlist);
+
+    /* FS_DATA_RUN */
+    extern TSK_FS_ATTR_RUN *tsk_fs_attr_run_alloc();
+    extern void tsk_fs_attr_run_free(TSK_FS_ATTR_RUN *);
+
+    /* FS_META */
+    extern TSK_FS_META *tsk_fs_meta_alloc(size_t);
+    extern TSK_FS_META *tsk_fs_meta_realloc(TSK_FS_META *, size_t);
+    extern void tsk_fs_meta_reset(TSK_FS_META *);
+    extern void tsk_fs_meta_close(TSK_FS_META * fs_meta);
+
+    /* FS_FILE */
+    extern TSK_FS_FILE *tsk_fs_file_alloc(TSK_FS_INFO *);
+
+    /* FS_DIR */
+    extern TSK_FS_DIR *tsk_fs_dir_alloc(TSK_FS_INFO * a_fs, TSK_INUM_T a_addr, size_t a_cnt);
+    extern uint8_t tsk_fs_dir_realloc(TSK_FS_DIR * a_fs_dir, size_t a_cnt);
+    extern uint8_t tsk_fs_dir_add(TSK_FS_DIR * a_fs_dir,
+        const TSK_FS_NAME * a_fs_dent);
+    extern void tsk_fs_dir_reset(TSK_FS_DIR * a_fs_dir);
+
+    /* Orphan Directory Support */
+    TSK_RETVAL_ENUM tsk_fs_dir_load_inum_named(TSK_FS_INFO * a_fs);
+    uint8_t tsk_fs_dir_find_inum_named(TSK_FS_INFO *a_fs, TSK_INUM_T a_inum);
+    extern uint8_t tsk_fs_dir_make_orphan_dir_meta(TSK_FS_INFO * a_fs,
+        TSK_FS_META * a_fs_meta);
+    extern uint8_t tsk_fs_dir_make_orphan_dir_name(TSK_FS_INFO * a_fs,
+        TSK_FS_NAME * a_fs_name);
+    extern TSK_RETVAL_ENUM tsk_fs_dir_find_orphans(TSK_FS_INFO * a_fs,
+        TSK_FS_DIR * a_fs_dir);
+
+
+
+    /* FS_DENT */
+    extern TSK_FS_NAME *tsk_fs_name_alloc(size_t, size_t);
+    extern uint8_t tsk_fs_name_realloc(TSK_FS_NAME *, size_t);
+    extern void tsk_fs_name_free(TSK_FS_NAME *);
+
+    extern void tsk_fs_name_print(FILE *, const TSK_FS_FILE *,
+        const char *, TSK_FS_INFO *, const TSK_FS_ATTR *, uint8_t);
+    extern void tsk_fs_name_print_long(FILE *, const TSK_FS_FILE *,
+        const char *, TSK_FS_INFO *, const TSK_FS_ATTR *, uint8_t,
+        int32_t);
+    extern void tsk_fs_name_print_mac(FILE *, const TSK_FS_FILE *,
+        const char *, const TSK_FS_ATTR * fs_attr, const char *, int32_t);
+    extern uint8_t tsk_fs_name_copy(TSK_FS_NAME * a_fs_name_to,
+        const TSK_FS_NAME * a_fs_name_from);
+    extern void tsk_fs_name_reset(TSK_FS_NAME * a_fs_name);
+
+    /* Utilities */
+    extern uint8_t tsk_fs_unix_make_data_run(TSK_FS_FILE * fs_file);
+    extern TSK_FS_ATTR_TYPE_ENUM tsk_fs_unix_get_default_attr_type(const
+        TSK_FS_FILE * a_file);
+    extern int tsk_fs_unix_name_cmp(TSK_FS_INFO * a_fs_info,
+        const char *s1, const char *s2);
+
+    /* Specific file system routines */
+    extern TSK_FS_INFO *ext2fs_open(TSK_IMG_INFO *, TSK_OFF_T,
+        TSK_FS_TYPE_ENUM, uint8_t);
+    extern TSK_FS_INFO *fatfs_open(TSK_IMG_INFO *, TSK_OFF_T,
+        TSK_FS_TYPE_ENUM, uint8_t);
+    extern TSK_FS_INFO *ffs_open(TSK_IMG_INFO *, TSK_OFF_T,
+        TSK_FS_TYPE_ENUM);
+    extern TSK_FS_INFO *ntfs_open(TSK_IMG_INFO *, TSK_OFF_T,
+        TSK_FS_TYPE_ENUM, uint8_t);
+    extern TSK_FS_INFO *rawfs_open(TSK_IMG_INFO *, TSK_OFF_T);
+    extern TSK_FS_INFO *swapfs_open(TSK_IMG_INFO *, TSK_OFF_T);
+    extern TSK_FS_INFO *iso9660_open(TSK_IMG_INFO *, TSK_OFF_T,
+        TSK_FS_TYPE_ENUM, uint8_t);
+    extern TSK_FS_INFO *hfs_open(TSK_IMG_INFO *, TSK_OFF_T,
+        TSK_FS_TYPE_ENUM, uint8_t);
+
+    /* Generic functions for swap and raw -- many say "not supported" */
+    extern uint8_t tsk_fs_nofs_fsstat(TSK_FS_INFO * fs, FILE * hFile);
+    extern void tsk_fs_nofs_close(TSK_FS_INFO * fs);
+    extern TSK_FS_ATTR_TYPE_ENUM tsk_fs_nofs_get_default_attr_type(const
+        TSK_FS_FILE * a_file);
+    extern uint8_t tsk_fs_nofs_make_data_run(TSK_FS_FILE *);
+    extern int tsk_fs_nofs_name_cmp(TSK_FS_INFO *, const char *,
+        const char *);
+
+    extern TSK_FS_BLOCK_FLAG_ENUM tsk_fs_nofs_block_getflags(TSK_FS_INFO *
+        a_fs, TSK_DADDR_T a_addr);
+    extern uint8_t tsk_fs_nofs_block_walk(TSK_FS_INFO * fs,
+        TSK_DADDR_T a_start_blk, TSK_DADDR_T a_end_blk,
+        TSK_FS_BLOCK_WALK_FLAG_ENUM a_flags, TSK_FS_BLOCK_WALK_CB a_action,
+        void *a_ptr);
+
+    extern uint8_t tsk_fs_nofs_file_add_meta(TSK_FS_INFO * fs,
+        TSK_FS_FILE * a_fs_file, TSK_INUM_T inum);
+    extern uint8_t tsk_fs_nofs_inode_walk(TSK_FS_INFO * fs,
+        TSK_INUM_T a_start_inum, TSK_INUM_T a_end_inum,
+        TSK_FS_META_FLAG_ENUM a_flags, TSK_FS_META_WALK_CB a_action,
+        void *a_ptr);
+    extern uint8_t tsk_fs_nofs_istat(TSK_FS_INFO * a_fs, FILE * hFile,
+        TSK_INUM_T inum, TSK_DADDR_T numblock, int32_t sec_skew);
+
+    extern TSK_RETVAL_ENUM tsk_fs_nofs_dir_open_meta(TSK_FS_INFO * a_fs,
+        TSK_FS_DIR ** a_fs_dir, TSK_INUM_T a_addr);
+
+    extern uint8_t tsk_fs_nofs_jopen(TSK_FS_INFO * a_fs, TSK_INUM_T inum);
+    extern uint8_t tsk_fs_nofs_jentry_walk(TSK_FS_INFO * a_fs, int a_flags,
+        TSK_FS_JENTRY_WALK_CB a_action, void *a_ptr);
+    extern uint8_t tsk_fs_nofs_jblk_walk(TSK_FS_INFO * a_fs,
+        TSK_INUM_T start, TSK_INUM_T end, int a_flags,
+        TSK_FS_JBLK_WALK_CB a_action, void *a_ptr);
+
+    /* malloc/free with lock init/deinit */
+    extern TSK_FS_INFO *tsk_fs_malloc(size_t);
+    extern void tsk_fs_free(TSK_FS_INFO*);
+
+
+// Endian macros - actual functions in misc/
+
+#define tsk_fs_guessu16(fs, x, mag)   \
+	tsk_guess_end_u16(&(fs->endian), (x), (mag))
+
+#define tsk_fs_guessu32(fs, x, mag)   \
+	tsk_guess_end_u32(&(fs->endian), (x), (mag))
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/tsk3/fs/tsk_hfs.h b/tsk3/fs/tsk_hfs.h
index 06a945393..d5ae09bd7 100644
--- a/tsk3/fs/tsk_hfs.h
+++ b/tsk3/fs/tsk_hfs.h
@@ -8,7 +8,7 @@
 ** 14900 Conference Center Drive
 ** Chantilly, VA 20151
 **
-** Copyright (c) 2009 Brian Carrier.  All rights reserved.
+** Copyright (c) 2009-2011 Brian Carrier.  All rights reserved.
 ** 
 ** Judson Powers [jpowers@atc-nycorp.com]
 ** Copyright (c) 2008 ATC-NY.  All rights reserved.
@@ -516,11 +516,14 @@ typedef struct {
 
     char is_case_sensitive;
 
-    TSK_FS_FILE *blockmap_file;
-    const TSK_FS_ATTR *blockmap_attr;
-    char blockmap_cache[4096];  ///< Cache for blockmap
-    TSK_OFF_T blockmap_cache_start;     ///< Byte offset of blockmap where cache starts
-    size_t blockmap_cache_len;  ///< Length of cache that is being used
+    /* lock protects blockmap_file, blockmap_attr, blockmap_cache, blockmap_cache_start, blockmap_cache_len */
+    tsk_lock_t lock;
+
+    TSK_FS_FILE *blockmap_file; //(r/w shared - lock) 
+    const TSK_FS_ATTR *blockmap_attr; // (r/w shared - lock) 
+    char blockmap_cache[4096];  ///< Cache for blockmap (r/w shared - lock) 
+    TSK_OFF_T blockmap_cache_start;     ///< Byte offset of blockmap where cache starts (r/w shared - lock) 
+    size_t blockmap_cache_len;  ///< Length of cache that is being used (r/w shared - lock) 
 
     TSK_FS_FILE *catalog_file;
     const TSK_FS_ATTR *catalog_attr;
diff --git a/tsk3/fs/tsk_iso9660.h b/tsk3/fs/tsk_iso9660.h
index 6c04db95b..61e24df69 100644
--- a/tsk3/fs/tsk_iso9660.h
+++ b/tsk3/fs/tsk_iso9660.h
@@ -12,7 +12,7 @@
 ** Copyright (c) 2005 Crucial Security Inc.  All rights reserved.
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
+** Copyright (c) 2003-2011 Brian Carrier.  All rights reserved
 **
 ** Copyright (c) 1997,1998,1999, International Business Machines
 ** Corporation and others. All Rights Reserved.
@@ -382,8 +382,6 @@ typedef struct iso9660_inode_node {
 /* The all important ISO_INFO struct */
 typedef struct {
     TSK_FS_INFO fs_info;        /* SUPER CLASS */
-    TSK_INUM_T dinum;           /* cached inode number */
-    iso9660_inode *dinode;      /* cached disk inode */
     uint32_t path_tab_addr;     /* address of path table */
     uint32_t root_addr;         /* address of root dir extent */
     iso9660_pvd_node *pvd;      ///< Head of primary volume descriptor list (there should be only one...)
@@ -395,7 +393,7 @@ typedef struct {
 extern TSK_RETVAL_ENUM iso9660_dir_open_meta(TSK_FS_INFO * a_fs,
     TSK_FS_DIR ** a_fs_dir, TSK_INUM_T a_addr);
 
-extern uint8_t iso9660_dinode_load(ISO_INFO * iso, TSK_INUM_T inum);
+extern uint8_t iso9660_dinode_load(ISO_INFO * iso, TSK_INUM_T inum, iso9660_inode *dinode);
 
 extern int iso9660_name_cmp(TSK_FS_INFO *, const char *, const char *);
 
diff --git a/tsk3/fs/tsk_ntfs.h b/tsk3/fs/tsk_ntfs.h
index 5543c12f3..86d2c5c8d 100644
--- a/tsk3/fs/tsk_ntfs.h
+++ b/tsk3/fs/tsk_ntfs.h
@@ -2,7 +2,7 @@
 ** The Sleuth Kit
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2003-2009 Brian Carrier.  All rights reserved
+** Copyright (c) 2003-2011 Brian Carrier.  All rights reserved
 **
 ** TASK
 ** Copyright (c) 2002 @stake Inc.  All rights reserved
@@ -621,8 +621,6 @@ extern "C" {
         uint8_t ver;            /* version of NTFS - uses the VINFO flag */
         TSK_FS_FILE *mft_file;  /* contains the data for the mft entry for the mft */
         const TSK_FS_ATTR *mft_data;    /* Data run for MFT entry for MFT */
-        ntfs_mft *mft;          /* cache for on-disk inode */
-        TSK_INUM_T mnum;        /* number of above cached mft */
         uint32_t csize_b;       /* number of bytes in a cluster */
         uint16_t ssize_b;       /* number of bytes in a sector */
         uint32_t mft_rsize_b;   /* number of bytes per mft record */
@@ -632,15 +630,24 @@ extern "C" {
         uint8_t loading_the_MFT;        /* set to 1 when initializing the setup */
 
         TSK_FS_ATTR_RUN *bmap;  /* Run of bitmap for clusters (linked list) */
-        char *bmap_buf;         /* buffer to hold cached copy of bitmap */
-        TSK_DADDR_T bmap_buf_off;       /* offset cluster in cached bitmap */
+
+        /* lock protects bmap_buf, bmap_buf_off */
+        tsk_lock_t lock;
+        char *bmap_buf;         /* buffer to hold cached copy of bitmap (r/w shared - lock)  */
+        TSK_DADDR_T bmap_buf_off;       /* offset cluster in cached bitmap  (r/w shared - lock) */
+
         ntfs_attrdef *attrdef;  // buffer of attrdef file contents
         size_t attrdef_len;     // length of addrdef buffer
-        NTFS_PAR_MAP *orphan_map;       // map that lists par directory to its orphans.
+
+        /* orphan_map_lock protects orphan_map */
+        tsk_lock_t orphan_map_lock;
+        NTFS_PAR_MAP *orphan_map;       // map that lists par directory to its orphans. (r/w shared - lock) 
 
 #if TSK_USE_SID
-        NTFS_SXX_BUFFER sii_data;
-        NTFS_SXX_BUFFER sds_data;
+        /* sid_lock protects sii_data, sds_data */
+        tsk_lock_t sid_lock;
+        NTFS_SXX_BUFFER sii_data; // (r/w shared - lock) 
+        NTFS_SXX_BUFFER sds_data; // (r/w shared - lock) 
 #endif
     } NTFS_INFO;
 
@@ -648,6 +655,7 @@ extern "C" {
     extern uint32_t nt2unixtime(uint64_t ntdate);
     extern uint8_t ntfs_attrname_lookup(TSK_FS_INFO *, uint16_t, char *,
         int);
+    extern TSK_RETVAL_ENUM ntfs_dinode_lookup(NTFS_INFO *, char *, TSK_INUM_T);
     extern TSK_RETVAL_ENUM
         ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
         TSK_INUM_T a_addr);
diff --git a/tsk3/fs/unix_misc.c b/tsk3/fs/unix_misc.c
index c70a29455..c90af3cd0 100644
--- a/tsk3/fs/unix_misc.c
+++ b/tsk3/fs/unix_misc.c
@@ -2,7 +2,7 @@
  * The Sleuth Kit 
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2008 Brian Carrier.  All Rights reserved
+ * Copyright (c) 2008-2011 Brian Carrier.  All Rights reserved
  *
  * This software is distributed under the Common Public License 1.0
  */
@@ -148,8 +148,8 @@ unix_make_data_run_indirect(TSK_FS_INFO * fs, TSK_FS_ATTR * fs_attr,
 
     if (addr > fs->last_block) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_FS_INODE_COR;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+        tsk_error_set_errstr(
             "unix: Indirect block address too large: %" PRIuDADDR "",
             addr);
         return -1;
@@ -178,9 +178,9 @@ unix_make_data_run_indirect(TSK_FS_INFO * fs, TSK_FS_ATTR * fs_attr,
         if (cnt != fs_bufsize) {
             if (cnt >= 0) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_FS_READ;
+                tsk_error_set_errno(TSK_ERR_FS_READ);
             }
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
+            tsk_error_set_errstr2(
                 "unix_make_data_run_indir: Block %" PRIuDADDR, addr);
             return -1;
         }
@@ -281,8 +281,8 @@ tsk_fs_unix_make_data_run(TSK_FS_FILE * fs_file)
 
     if ((TSK_FS_TYPE_ISFFS(fs->ftype) == 0)
         && (TSK_FS_TYPE_ISEXT(fs->ftype) == 0)) {
-        tsk_errno = TSK_ERR_FS_INODE_COR;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
+        tsk_error_set_errstr(
             "unix_make_run: Called with non-Unix file system: %x",
             fs->ftype);
         return 1;
@@ -310,7 +310,7 @@ tsk_fs_unix_make_data_run(TSK_FS_FILE * fs_file)
     if (read_b == -1) {
         fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
         if (fs_meta->flags & TSK_FS_META_FLAG_UNALLOC)
-            tsk_errno = TSK_ERR_FS_RECOVER;
+            tsk_error_set_errno(TSK_ERR_FS_RECOVER);
         return 1;
     }
     length -= read_b;
@@ -437,7 +437,7 @@ tsk_fs_unix_make_data_run(TSK_FS_FILE * fs_file)
     if (read_b == -1) {
         fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
         if (fs_meta->flags & TSK_FS_META_FLAG_UNALLOC)
-            tsk_errno = TSK_ERR_FS_RECOVER;
+            tsk_error_set_errno(TSK_ERR_FS_RECOVER);
         return 1;
     }
 
diff --git a/tsk3/fs/walk_cpp.cpp b/tsk3/fs/walk_cpp.cpp
new file mode 100644
index 000000000..b6d4cc6cd
--- /dev/null
+++ b/tsk3/fs/walk_cpp.cpp
@@ -0,0 +1,80 @@
+#include "tsk_fs_i.h"
+#include "tsk3/vs/tsk_vs_i.h"
+
+/**
+* \internal
+* call back function 
+*/
+TSK_WALK_RET_ENUM tsk_fs_block_cpp_c_cb (const TSK_FS_BLOCK *a_block, void *a_ptr)
+{
+    TSK_FS_BLOCK_WALK_CPP_DATA *data = (TSK_FS_BLOCK_WALK_CPP_DATA *)a_ptr;
+    TskFsBlock block(a_block);
+    return data->cppAction(&block, data->cPtr);
+}
+
+/**
+* \internal
+* call back function 
+*/
+TSK_WALK_RET_ENUM tsk_fs_file_cpp_c_cb (TSK_FS_FILE *a_file, TSK_OFF_T a_off, TSK_DADDR_T a_addr, char *a_buf,
+                                        size_t a_len, TSK_FS_BLOCK_FLAG_ENUM a_flags, void *a_ptr)
+{
+    TSK_FS_FILE_WALK_CPP_DATA *data = (TSK_FS_FILE_WALK_CPP_DATA *)a_ptr;
+    TskFsFile fsFile(a_file);
+    return data->cppAction(&fsFile,a_off,a_addr,a_buf,a_len,a_flags,data->cPtr);
+}
+
+/**
+* \internal
+* call back function 
+*/
+TSK_WALK_RET_ENUM tsk_fs_jblk_cpp_c_cb (TSK_FS_INFO *a_fsInfo, char *a_string,
+                                        int a_num, void *a_ptr){
+    TSK_FS_JBLK_WALK_CPP_DATA *data = (TSK_FS_JBLK_WALK_CPP_DATA *)a_ptr;
+    TskFsInfo fsInfo(a_fsInfo);
+    return data->cppAction(&fsInfo, a_string, a_num, data->cPtr);                                         
+}
+
+/**
+* \internal
+* call back function 
+*/
+TSK_WALK_RET_ENUM tsk_fs_jentry_cpp_c_cb (TSK_FS_INFO *a_fsInfo, TSK_FS_JENTRY *a_jentry,
+                                          int a_num, void *a_ptr){
+    TSK_FS_JENTRY_WALK_CPP_DATA *data = (TSK_FS_JENTRY_WALK_CPP_DATA *)a_ptr;
+    TskFsInfo fsInfo(a_fsInfo);
+    TskFsJEntry fsJEntry(a_jentry);
+    return data->cppAction(&fsInfo, &fsJEntry, a_num, data->cPtr);
+}
+
+/**
+* \internal
+* call back function 
+*/
+TSK_WALK_RET_ENUM tsk_fs_meta_walk_cpp_c_cb (TSK_FS_FILE *a_file, void *a_ptr){
+    TSK_FS_META_WALK_CPP_DATA *data = (TSK_FS_META_WALK_CPP_DATA *)a_ptr;
+    TskFsFile fsFile(a_file);
+    return data->cppAction(&fsFile, data->cPtr);
+}
+
+/**
+* \internal
+* call back function 
+*/
+TSK_WALK_RET_ENUM tsk_fs_dir_walk_cpp_c_cb (TSK_FS_FILE *a_file,  const char *a_path, void *a_ptr){
+    TSK_FS_DIR_WALK_CPP_DATA *data = (TSK_FS_DIR_WALK_CPP_DATA *)a_ptr;
+    TskFsFile fsFile(a_file);
+    return data->cppAction(&fsFile, a_path, data->cPtr);
+}
+
+/**
+* \internal
+* call back function 
+*/
+TSK_WALK_RET_ENUM tsk_vs_part_walk_cpp_c_cb (TSK_VS_INFO *a_vs, const TSK_VS_PART_INFO * a_vs_part, void *a_ptr){
+    TSK_VS_PART_WALK_CPP_DATA *data = (TSK_VS_PART_WALK_CPP_DATA *)a_ptr;
+    TskVsInfo vsInfo(a_vs);
+    TskVsPartInfo vsPartInfo(const_cast<TSK_VS_PART_INFO *>(a_vs_part));
+    return data->cppAction(&vsInfo, &vsPartInfo, data->cPtr);
+}
+
diff --git a/tsk3/hashdb/hk_index.c b/tsk3/hashdb/hk_index.c
index b4b67bf6d..c610fc460 100644
--- a/tsk3/hashdb/hk_index.c
+++ b/tsk3/hashdb/hk_index.c
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2003-2008 Brian Carrier.  All rights reserved
+ * Copyright (c) 2003-2011 Brian Carrier.  All rights reserved
  *
  *
  * This software is distributed under the Common Public License 1.0
@@ -266,7 +266,7 @@ hk_makeindex(TSK_HDB_INFO * hdb_info, TSK_TCHAR * dbtype)
     int db_cnt = 0, idx_cnt = 0, ig_cnt = 0;
 
     if (tsk_hdb_idxinitialize(hdb_info, dbtype)) {
-        snprintf(tsk_errstr2, TSK_ERRSTR_L, "hk_makeindex");
+        tsk_error_set_errstr2( "hk_makeindex");
         return 1;
     }
     fseek(hdb_info->hDb, 0, SEEK_SET);
@@ -301,7 +301,7 @@ hk_makeindex(TSK_HDB_INFO * hdb_info, TSK_TCHAR * dbtype)
 
         /* Add the entry to the index */
         if (tsk_hdb_idxaddentry(hdb_info, hash, offset)) {
-            snprintf(tsk_errstr2, TSK_ERRSTR_L, "hk_makeindex");
+            tsk_error_set_errstr2( "hk_makeindex");
             return 1;
         }
 
@@ -323,14 +323,14 @@ hk_makeindex(TSK_HDB_INFO * hdb_info, TSK_TCHAR * dbtype)
 
         /* Finish the index making process */
         if (tsk_hdb_idxfinalize(hdb_info)) {
-            snprintf(tsk_errstr2, TSK_ERRSTR_L, "hk_makeindex");
+            tsk_error_set_errstr2( "hk_makeindex");
             return 1;
         }
     }
     else {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_CORRUPT;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+        tsk_error_set_errstr(
                  "hk_makeindex: No valid entries found in database");
         return 1;
     }
@@ -346,6 +346,8 @@ hk_makeindex(TSK_HDB_INFO * hdb_info, TSK_TCHAR * dbtype)
  * if they have the same hash value and their name is different. 
  * The callback is called for each entry. 
  *
+ * Note: This routine assumes that &hdb_info->lock is locked by the caller.
+ *
  * @param hdb_info Data base to get data from.
  * @param hash MD5 hash value that was searched for
  * @param offset Byte offset where hash value should be located in db_file
@@ -371,8 +373,8 @@ hk_getentry(TSK_HDB_INFO * hdb_info, const char *hash, TSK_OFF_T offset,
 
     if (strlen(hash) != TSK_HDB_HTYPE_MD5_LEN) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_ARG);
+        tsk_error_set_errstr(
                  "hk_getentry: Invalid hash value: %s", hash);
         return 1;
     }
@@ -385,8 +387,8 @@ hk_getentry(TSK_HDB_INFO * hdb_info, const char *hash, TSK_OFF_T offset,
 
         if (0 != fseeko(hdb_info->hDb, offset, SEEK_SET)) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_READDB;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_READDB);
+            tsk_error_set_errstr(
                      "hk_getentry: Error seeking to get file name: %lu",
                      (unsigned long) offset);
             return 1;
@@ -398,8 +400,8 @@ hk_getentry(TSK_HDB_INFO * hdb_info, const char *hash, TSK_OFF_T offset,
                 break;
             }
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_READDB;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_READDB);
+            tsk_error_set_errstr(
                      "hk_getentry: Error reading database");
             return 1;
         }
@@ -407,8 +409,8 @@ hk_getentry(TSK_HDB_INFO * hdb_info, const char *hash, TSK_OFF_T offset,
         len = strlen(buf);
         if (len < TSK_HDB_HTYPE_MD5_LEN) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_CORRUPT;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+            tsk_error_set_errstr(
                      "hk_getentry: Invalid entry in database (too short): %s",
                      buf);
             return 1;
@@ -419,8 +421,8 @@ hk_getentry(TSK_HDB_INFO * hdb_info, const char *hash, TSK_OFF_T offset,
                          ((flags & TSK_HDB_FLAG_EXT) ? TSK_HDB_MAXLEN :
                           0))) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_CORRUPT;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+            tsk_error_set_errstr(
                      "hk_getentry: Invalid entry in database: %s", buf);
             return 1;
         }
@@ -454,8 +456,8 @@ hk_getentry(TSK_HDB_INFO * hdb_info, const char *hash, TSK_OFF_T offset,
 
     if (found == 0) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_ARG);
+        tsk_error_set_errstr(
                  "hk_getentry: Hash not found in file at offset: %lu",
                  (unsigned long) offset);
         return 1;
diff --git a/tsk3/hashdb/idxonly_index.c b/tsk3/hashdb/idxonly_index.c
index 46edc1e57..f46f3c953 100644
--- a/tsk3/hashdb/idxonly_index.c
+++ b/tsk3/hashdb/idxonly_index.c
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2007 Brian Carrier.  All rights reserved
+ * Copyright (c) 2007-2011 Brian Carrier.  All rights reserved
  *
  * This software is distributed under the Common Public License 1.0
  */
@@ -33,8 +33,8 @@ uint8_t
 idxonly_makeindex(TSK_HDB_INFO * hdb_info, TSK_TCHAR * dbtype)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_HDB_ARG;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_HDB_ARG);
+    tsk_error_set_errstr(
              "idxonly_makeindex: Make index not supported when INDEX ONLY option is used");
     return 1;
 }
@@ -60,8 +60,8 @@ idxonly_getentry(TSK_HDB_INFO * hdb_info, const char *hash,
                  TSK_HDB_LOOKUP_FN action, void *cb_ptr)
 {
     tsk_error_reset();
-    tsk_errno = TSK_ERR_HDB_ARG;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_HDB_ARG);
+    tsk_error_set_errstr(
              "idxonly_getentry: Not supported when INDEX ONLY option is used");
     return 1;
 }
diff --git a/tsk3/hashdb/md5sum_index.c b/tsk3/hashdb/md5sum_index.c
index 606317fbd..e79f2e8f3 100644
--- a/tsk3/hashdb/md5sum_index.c
+++ b/tsk3/hashdb/md5sum_index.c
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2003-2008 Brian Carrier.  All rights reserved
+ * Copyright (c) 2003-2011 Brian Carrier.  All rights reserved
  *
  *
  * This software is distributed under the Common Public License 1.0
@@ -71,8 +71,8 @@ md5sum_parse_md5(char *str, char **md5, char **name)
 
     if (strlen(str) < TSK_HDB_HTYPE_MD5_LEN + 1) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_CORRUPT;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+        tsk_error_set_errstr(
                  "md5sum_parse_md5: String is too short: %s", str);
         return 1;
     }
@@ -131,8 +131,8 @@ md5sum_parse_md5(char *str, char **md5, char **name)
 
         if (NULL == (ptr = strchr(ptr, ')'))) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_CORRUPT;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+            tsk_error_set_errstr(
                      "md5sum_parse_md5: Missing ) in name: %s", str);
             return 1;
         }
@@ -142,8 +142,8 @@ md5sum_parse_md5(char *str, char **md5, char **name)
 
         if (4 + TSK_HDB_HTYPE_MD5_LEN > strlen(ptr)) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_CORRUPT;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+            tsk_error_set_errstr(
                      "md5sum_parse_md5: Invalid MD5 value: %s", ptr);
             return 1;
         }
@@ -152,8 +152,8 @@ md5sum_parse_md5(char *str, char **md5, char **name)
             (*(++ptr) != ' ') || (!isxdigit((int) *(++ptr))) ||
             (ptr[TSK_HDB_HTYPE_MD5_LEN] != '\n')) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_CORRUPT;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+            tsk_error_set_errstr(
                      "md5sum_parse_md5: Invalid hash value %s", ptr);
             return 1;
         }
@@ -164,8 +164,8 @@ md5sum_parse_md5(char *str, char **md5, char **name)
 
     else {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_CORRUPT;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+        tsk_error_set_errstr(
                  "md5sum_parse_md5: Invalid md5sum format in file: %s\n",
                  str);
         return 1;
@@ -197,7 +197,7 @@ md5sum_makeindex(TSK_HDB_INFO * hdb_info, TSK_TCHAR * dbtype)
 
     /* Initialize the TSK index file */
     if (tsk_hdb_idxinitialize(hdb_info, dbtype)) {
-        snprintf(tsk_errstr2, TSK_ERRSTR_L, "md5sum_makeindex");
+        tsk_error_set_errstr2( "md5sum_makeindex");
         return 1;
     }
 
@@ -230,7 +230,7 @@ md5sum_makeindex(TSK_HDB_INFO * hdb_info, TSK_TCHAR * dbtype)
 
         /* Add the entry to the index */
         if (tsk_hdb_idxaddentry(hdb_info, hash, offset)) {
-            snprintf(tsk_errstr2, TSK_ERRSTR_L, "md5sum_makeindex");
+            tsk_error_set_errstr2( "md5sum_makeindex");
             return 1;
         }
 
@@ -253,14 +253,14 @@ md5sum_makeindex(TSK_HDB_INFO * hdb_info, TSK_TCHAR * dbtype)
 
         /* Close and sort the index */
         if (tsk_hdb_idxfinalize(hdb_info)) {
-            snprintf(tsk_errstr2, TSK_ERRSTR_L, "md5sum_makeindex");
+            tsk_error_set_errstr2( "md5sum_makeindex");
             return 1;
         }
     }
     else {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_CORRUPT;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+        tsk_error_set_errstr(
                  "md5sum_makeindex: No valid entries found in database");
         return 1;
     }
@@ -300,8 +300,8 @@ md5sum_getentry(TSK_HDB_INFO * hdb_info, const char *hash,
 
     if (strlen(hash) != TSK_HDB_HTYPE_MD5_LEN) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_ARG);
+        tsk_error_set_errstr(
                  "md5sum_getentry: Invalid hash value: %s", hash);
         return 1;
     }
@@ -314,8 +314,8 @@ md5sum_getentry(TSK_HDB_INFO * hdb_info, const char *hash,
 
         if (0 != fseeko(hdb_info->hDb, offset, SEEK_SET)) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_READDB;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_READDB);
+            tsk_error_set_errstr(
                      "md5sum_getentry: Error seeking to get file name: %lu",
                      (unsigned long) offset);
             return 1;
@@ -326,8 +326,8 @@ md5sum_getentry(TSK_HDB_INFO * hdb_info, const char *hash,
                 break;
             }
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_READDB;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_READDB);
+            tsk_error_set_errstr(
                      "md5sum_getentry: Error reading database");
             return 1;
         }
@@ -335,8 +335,8 @@ md5sum_getentry(TSK_HDB_INFO * hdb_info, const char *hash,
         len = strlen(buf);
         if (len < TSK_HDB_HTYPE_MD5_LEN) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_CORRUPT;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+            tsk_error_set_errstr(
                      "md5sum_getentry: Invalid entry in database (too short): %s",
                      buf);
             return 1;
@@ -344,8 +344,8 @@ md5sum_getentry(TSK_HDB_INFO * hdb_info, const char *hash,
 
         if (md5sum_parse_md5(buf, &ptr, &name)) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_CORRUPT;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+            tsk_error_set_errstr(
                      "md5sum_getentry: Invalid entry in database: %s",
                      buf);
             return 1;
@@ -375,8 +375,8 @@ md5sum_getentry(TSK_HDB_INFO * hdb_info, const char *hash,
 
     if (found == 0) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_ARG);
+        tsk_error_set_errstr(
                  "md5sum_getentry: Hash not found in file at offset: %lu",
                  (unsigned long) offset);
         return 1;
diff --git a/tsk3/hashdb/nsrl_index.c b/tsk3/hashdb/nsrl_index.c
index 884099357..a4d924145 100644
--- a/tsk3/hashdb/nsrl_index.c
+++ b/tsk3/hashdb/nsrl_index.c
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2003-2008 Brian Carrier.  All rights reserved
+ * Copyright (c) 2003-2011 Brian Carrier.  All rights reserved
  *
  *
  * This software is distributed under the Common Public License 1.0
@@ -52,8 +52,8 @@ lCode"
         return TSK_HDB_NSRL_FORM2;
 
     tsk_error_reset();
-    tsk_errno = TSK_ERR_HDB_CORRUPT;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+    tsk_error_set_errstr(
              "nsrl: Unknown header format: %s\n", str);
     return -1;
 }
@@ -121,8 +121,8 @@ nsrl_parse_sha1(char *str, char **sha1, char **name, int ver)
     /* Sanity check */
     if (is_valid_nsrl(str) == 0) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_CORRUPT;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+        tsk_error_set_errstr(
                  "nsrl_parse_sha1: Invalid string to parse: %s", str);
         return 1;
     }
@@ -136,8 +136,8 @@ nsrl_parse_sha1(char *str, char **sha1, char **name, int ver)
         /* Final sanity check to make sure there are no ',' in hash */
         if (NULL != strchr(ptr, ',')) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_CORRUPT;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+            tsk_error_set_errstr(
                      "nsrl_parse_sha1: Invalid string to parse (commas after SHA1): %s",
                      ptr);
             return 1;
@@ -156,8 +156,8 @@ nsrl_parse_sha1(char *str, char **sha1, char **name, int ver)
 
             if (NULL == (ptr = strchr(ptr, ','))) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_HDB_CORRUPT;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+                tsk_error_set_errstr(
                          "nsrl_parse_sha1: Invalid string to parse (commas after name): %s",
                          ptr);
                 return 1;
@@ -176,8 +176,8 @@ nsrl_parse_sha1(char *str, char **sha1, char **name, int ver)
 
             if (NULL == (ptr = strchr(ptr, ','))) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_HDB_CORRUPT;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+                tsk_error_set_errstr(
                          "nsrl_parse_sha1: Invalid string to parse (commas after name): %s",
                          ptr);
                 return 1;
@@ -211,8 +211,8 @@ nsrl_parse_md5(char *str, char **md5, char **name, int ver)
     /* Sanity check */
     if (is_valid_nsrl(str) == 0) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_CORRUPT;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+        tsk_error_set_errstr(
                  "nsrl_parse_md5: Invalid string to parse: %s", str);
         return 1;
     }
@@ -246,8 +246,8 @@ nsrl_parse_md5(char *str, char **md5, char **name, int ver)
             else if ((cnt == 2) && (name != NULL)) {
                 if (ptr[-1] != '"') {
                     tsk_error_reset();
-                    tsk_errno = TSK_ERR_HDB_CORRUPT;
-                    snprintf(tsk_errstr, TSK_ERRSTR_L,
+                    tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+                    tsk_error_set_errstr(
                              "nsrl_parse_md5: Missing Quote after name: %s",
                              (char *) name);
                     return 1;
@@ -265,8 +265,8 @@ nsrl_parse_md5(char *str, char **md5, char **name, int ver)
                     || (ptr[1] != '"')
                     || (ptr[2 + TSK_HDB_HTYPE_MD5_LEN] != '"')) {
                     tsk_error_reset();
-                    tsk_errno = TSK_ERR_HDB_CORRUPT;
-                    snprintf(tsk_errstr, TSK_ERRSTR_L,
+                    tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+                    tsk_error_set_errstr(
                              "nsrl_parse_md5: Invalid MD5 value: %s", ptr);
                     return 1;
                 }
@@ -279,8 +279,8 @@ nsrl_parse_md5(char *str, char **md5, char **name, int ver)
                 /* Final sanity check */
                 if (NULL != strchr(ptr, ',')) {
                     tsk_error_reset();
-                    tsk_errno = TSK_ERR_HDB_CORRUPT;
-                    snprintf(tsk_errstr, TSK_ERRSTR_L,
+                    tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+                    tsk_error_set_errstr(
                              "nsrl_parse_md5: Missing comma after MD5: %s",
                              (char *) md5);
                     return 1;
@@ -295,8 +295,8 @@ nsrl_parse_md5(char *str, char **md5, char **name, int ver)
             if (ptr[1] == '"') {
                 if (NULL == (ptr = strchr(&ptr[2], '"'))) {
                     tsk_error_reset();
-                    tsk_errno = TSK_ERR_HDB_CORRUPT;
-                    snprintf(tsk_errstr, TSK_ERRSTR_L,
+                    tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+                    tsk_error_set_errstr(
                              "nsrl_parse_md5: Error advancing past quote");
                     return 1;
                 }
@@ -316,8 +316,8 @@ nsrl_parse_md5(char *str, char **md5, char **name, int ver)
             /* Final sanity check to make sure there are no ',' in hash */
             if (NULL != strchr(ptr, ',')) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_HDB_CORRUPT;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+                tsk_error_set_errstr(
                          "nsrl_parse_md5: Comma in MD5 value: %s", ptr);
                 return 1;
             }
@@ -335,8 +335,8 @@ nsrl_parse_md5(char *str, char **md5, char **name, int ver)
 
             if (NULL == (ptr = strchr(ptr, ','))) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_HDB_CORRUPT;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+                tsk_error_set_errstr(
                          "nsrl_parse_md5: Missing comma after name: %s",
                          (char *) name);
                 return 1;
@@ -349,8 +349,8 @@ nsrl_parse_md5(char *str, char **md5, char **name, int ver)
         return 0;
     }
     tsk_error_reset();
-    tsk_errno = TSK_ERR_HDB_ARG;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_HDB_ARG);
+    tsk_error_set_errstr(
              "nsrl_parse_md5: Invalid version: %d\n", ver);
     return 1;
 }
@@ -379,7 +379,7 @@ nsrl_makeindex(TSK_HDB_INFO * hdb_info, TSK_TCHAR * dbtype)
     int db_cnt = 0, idx_cnt = 0, ig_cnt = 0;
 
     if (tsk_hdb_idxinitialize(hdb_info, dbtype)) {
-        snprintf(tsk_errstr2, TSK_ERRSTR_L, "nsrl_makeindex");
+        tsk_error_set_errstr2( "nsrl_makeindex");
         return 1;
     }
 
@@ -430,7 +430,7 @@ nsrl_makeindex(TSK_HDB_INFO * hdb_info, TSK_TCHAR * dbtype)
 
         /* Add the entry to the index */
         if (tsk_hdb_idxaddentry(hdb_info, hash, offset)) {
-            snprintf(tsk_errstr2, TSK_ERRSTR_L, "nsrl_makeindex");
+            tsk_error_set_errstr2( "nsrl_makeindex");
             return 1;
         }
 
@@ -452,14 +452,14 @@ nsrl_makeindex(TSK_HDB_INFO * hdb_info, TSK_TCHAR * dbtype)
 
         /* Close and sort the index */
         if (tsk_hdb_idxfinalize(hdb_info)) {
-            snprintf(tsk_errstr2, TSK_ERRSTR_L, "nsrl_makeindex");
+            tsk_error_set_errstr2( "nsrl_makeindex");
             return 1;
         }
     }
     else {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_CORRUPT;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+        tsk_error_set_errstr(
                  "nsrl_makeindex: No valid entries found in database");
         return 1;
     }
@@ -502,8 +502,8 @@ nsrl_getentry(TSK_HDB_INFO * hdb_info, const char *hash, TSK_OFF_T offset,
     if ((hdb_info->hash_type == TSK_HDB_HTYPE_MD5_ID)
         && (strlen(hash) != TSK_HDB_HTYPE_MD5_LEN)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_ARG);
+        tsk_error_set_errstr(
                  "nsrl_getentry: Invalid hash value (expected to be MD5): %s\n",
                  hash);
         return 1;
@@ -511,8 +511,8 @@ nsrl_getentry(TSK_HDB_INFO * hdb_info, const char *hash, TSK_OFF_T offset,
     else if ((hdb_info->hash_type == TSK_HDB_HTYPE_SHA1_ID)
              && (strlen(hash) != TSK_HDB_HTYPE_SHA1_LEN)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_ARG);
+        tsk_error_set_errstr(
                  "nsrl_getentry: Invalid hash value (expected to be SHA1): %s\n",
                  hash);
         return 1;
@@ -522,14 +522,14 @@ nsrl_getentry(TSK_HDB_INFO * hdb_info, const char *hash, TSK_OFF_T offset,
     fseeko(hdb_info->hDb, 0, SEEK_SET);
     if (NULL == fgets(buf, TSK_HDB_MAXLEN, hdb_info->hDb)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_READDB;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_READDB);
+        tsk_error_set_errstr(
                  "nsrl_getentry: Error reading NSRLFile.txt header\n");
         return 1;
     }
 
     if ((ver = get_format_ver(buf)) == -1) {
-        snprintf(tsk_errstr2, TSK_ERRSTR_L, "nsrl_getentry");
+        tsk_error_set_errstr2( "nsrl_getentry");
         return 1;
     }
 
@@ -541,8 +541,8 @@ nsrl_getentry(TSK_HDB_INFO * hdb_info, const char *hash, TSK_OFF_T offset,
 
         if (0 != fseeko(hdb_info->hDb, offset, SEEK_SET)) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_READDB;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_READDB);
+            tsk_error_set_errstr(
                      "nsrl_getentry: Error seeking to get file name: %lu",
                      (unsigned long) offset);
             return 1;
@@ -553,8 +553,8 @@ nsrl_getentry(TSK_HDB_INFO * hdb_info, const char *hash, TSK_OFF_T offset,
                 break;
 
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_READDB;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_READDB);
+            tsk_error_set_errstr(
                      "nsrl_getentry: Error reading database");
             return 1;
         }
@@ -562,8 +562,8 @@ nsrl_getentry(TSK_HDB_INFO * hdb_info, const char *hash, TSK_OFF_T offset,
         len = strlen(buf);
         if (len < TSK_HDB_HTYPE_SHA1_LEN + 5) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_CORRUPT;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+            tsk_error_set_errstr(
                      "nsrl_getentry: Invalid entry in database (too short): %s",
                      buf);
             return 1;
@@ -573,8 +573,8 @@ nsrl_getentry(TSK_HDB_INFO * hdb_info, const char *hash, TSK_OFF_T offset,
         if (hdb_info->hash_type == TSK_HDB_HTYPE_SHA1_ID) {
             if (nsrl_parse_sha1(buf, &cur_hash, &name, ver)) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_HDB_CORRUPT;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+                tsk_error_set_errstr(
                          "nsrl_getentry: Invalid entry in database: %s",
                          buf);
                 return 1;
@@ -583,8 +583,8 @@ nsrl_getentry(TSK_HDB_INFO * hdb_info, const char *hash, TSK_OFF_T offset,
         else if (hdb_info->hash_type == TSK_HDB_HTYPE_MD5_ID) {
             if (nsrl_parse_md5(buf, &cur_hash, &name, ver)) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_HDB_CORRUPT;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+                tsk_error_set_errstr(
                          "nsrl_getentry: Invalid entry in database: %s",
                          buf);
                 return 1;
@@ -615,8 +615,8 @@ nsrl_getentry(TSK_HDB_INFO * hdb_info, const char *hash, TSK_OFF_T offset,
 
     if (found == 0) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_ARG);
+        tsk_error_set_errstr(
                  "nsrl_getentry: Hash not found in file at offset: %lu",
                  (unsigned long) offset);
         return 1;
diff --git a/tsk3/hashdb/tm_lookup.c b/tsk3/hashdb/tm_lookup.c
index df51f455f..94d8aa7d6 100644
--- a/tsk3/hashdb/tm_lookup.c
+++ b/tsk3/hashdb/tm_lookup.c
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2003-2008 Brian Carrier.  All rights reserved
+ * Copyright (c) 2003-2011 Brian Carrier.  All rights reserved
  *
  *
  * This software is distributed under the Common Public License 1.0
@@ -63,8 +63,8 @@ hdb_setuphash(TSK_HDB_INFO * hdb_info, uint8_t htype)
     }
 
     tsk_error_reset();
-    tsk_errno = TSK_ERR_HDB_ARG;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_HDB_ARG);
+    tsk_error_set_errstr(
              "hdb_setuphash: Invalid hash type as argument: %d", htype);
     return 1;
 }
@@ -101,8 +101,8 @@ tsk_hdb_idxinitialize(TSK_HDB_INFO * hdb_info, TSK_TCHAR * htype)
 
         if (hdb_info->db_type != TSK_HDB_DBTYPE_NSRL_ID) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_ARG;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_ARG);
+            tsk_error_set_errstr(
                      "hdb_idxinitialize: database detected as: %d index creation as: %d",
                      hdb_info->db_type, TSK_HDB_DBTYPE_NSRL_ID);
             return 1;
@@ -112,8 +112,8 @@ tsk_hdb_idxinitialize(TSK_HDB_INFO * hdb_info, TSK_TCHAR * htype)
     else if (strcmp(dbtmp, TSK_HDB_DBTYPE_NSRL_SHA1_STR) == 0) {
         if (hdb_info->db_type != TSK_HDB_DBTYPE_NSRL_ID) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_ARG;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_ARG);
+            tsk_error_set_errstr(
                      "hdb_idxinitialize: database detected as: %d index creation as: %d",
                      hdb_info->db_type, TSK_HDB_DBTYPE_NSRL_ID);
             return 1;
@@ -123,8 +123,8 @@ tsk_hdb_idxinitialize(TSK_HDB_INFO * hdb_info, TSK_TCHAR * htype)
     else if (strcmp(dbtmp, TSK_HDB_DBTYPE_MD5SUM_STR) == 0) {
         if (hdb_info->db_type != TSK_HDB_DBTYPE_MD5SUM_ID) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_ARG;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_ARG);
+            tsk_error_set_errstr(
                      "hdb_idxinitialize: database detected as: %d index creation as: %d",
                      hdb_info->db_type, TSK_HDB_DBTYPE_MD5SUM_ID);
             return 1;
@@ -134,8 +134,8 @@ tsk_hdb_idxinitialize(TSK_HDB_INFO * hdb_info, TSK_TCHAR * htype)
     else if (strcmp(dbtmp, TSK_HDB_DBTYPE_HK_STR) == 0) {
         if (hdb_info->db_type != TSK_HDB_DBTYPE_HK_ID) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_ARG;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_ARG);
+            tsk_error_set_errstr(
                      "hdb_idxinitialize: database detected as: %d index creation as: %d",
                      hdb_info->db_type, TSK_HDB_DBTYPE_HK_ID);
             return 1;
@@ -144,8 +144,8 @@ tsk_hdb_idxinitialize(TSK_HDB_INFO * hdb_info, TSK_TCHAR * htype)
     }
     else {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_ARG);
+        tsk_error_set_errstr(
                  "hdb_idxinitialize: Unknown database type request: %s",
                  dbtmp);
         return 1;
@@ -177,8 +177,8 @@ tsk_hdb_idxinitialize(TSK_HDB_INFO * hdb_info, TSK_TCHAR * htype)
                                0, 0, CREATE_ALWAYS, 0, 0)) ==
             INVALID_HANDLE_VALUE) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_CREATE;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_CREATE);
+            tsk_error_set_errstr(
                      "hdb_idxinitialize: %"PRIttocTSK" GetFileSize: %d",
                      hdb_info->uns_fname, (int)GetLastError());
             return 1;
@@ -188,8 +188,8 @@ tsk_hdb_idxinitialize(TSK_HDB_INFO * hdb_info, TSK_TCHAR * htype)
             _fdopen(_open_osfhandle((intptr_t) hWin, _O_WRONLY), "wb");
         if (hdb_info->hIdxTmp == NULL) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_OPEN;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_OPEN);
+            tsk_error_set_errstr(
                      "hdb_idxinitialize: Error converting Windows handle to C handle");
             free(hdb_info);
             return 1;
@@ -198,8 +198,8 @@ tsk_hdb_idxinitialize(TSK_HDB_INFO * hdb_info, TSK_TCHAR * htype)
 #else
     if (NULL == (hdb_info->hIdxTmp = fopen(hdb_info->uns_fname, "w"))) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_CREATE;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_CREATE);
+        tsk_error_set_errstr(
                  "Error creating temp index file: %s",
                  hdb_info->uns_fname);
         return 1;
@@ -224,8 +224,8 @@ tsk_hdb_idxinitialize(TSK_HDB_INFO * hdb_info, TSK_TCHAR * htype)
     case TSK_HDB_DBTYPE_IDXONLY_ID:
     default:
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_CREATE;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "idxinit: Invalid db type\n");
+        tsk_error_set_errno(TSK_ERR_HDB_CREATE);
+        tsk_error_set_errstr("idxinit: Invalid db type\n");
         return 1;
     }
 
@@ -297,8 +297,8 @@ tsk_hdb_idxfinalize(TSK_HDB_INFO * hdb_info)
     }
     else {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_MISSING;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "Cannot find sort executable");
+        tsk_error_set_errno(TSK_ERR_HDB_MISSING);
+        tsk_error_set_errstr("Cannot find sort executable");
         return 1;
     }
 
@@ -308,24 +308,24 @@ tsk_hdb_idxfinalize(TSK_HDB_INFO * hdb_info)
         CreateProcess(NULL, buf, NULL, NULL, FALSE, 0, NULL, NULL,
                       &myStartInfo, &pinfo)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_PROC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_PROC);
+        tsk_error_set_errstr(
                  "Error starting sorting index file using %S", buf);
         return 1;
     }
 
     if (WAIT_FAILED == WaitForSingleObject(pinfo.hProcess, INFINITE)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_PROC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_PROC);
+        tsk_error_set_errstr(
                  "Error (waiting) sorting index file using %S", buf);
         return 1;
     }
 
     if (FALSE == DeleteFile(hdb_info->uns_fname)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_DELETE;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_DELETE);
+        tsk_error_set_errstr(
                  "Error deleting temp file: %d", (int)GetLastError());
         return 1;
     }
@@ -363,15 +363,15 @@ tsk_hdb_idxfinalize(TSK_HDB_INFO * hdb_info)
     }
     else {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_MISSING;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "Cannot find sort executable");
+        tsk_error_set_errno(TSK_ERR_HDB_MISSING);
+        tsk_error_set_errstr("Cannot find sort executable");
         return 1;
     }
 
     if (0 != system(buf)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_PROC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_PROC);
+        tsk_error_set_errstr(
                  "Error sorting index file using %s", buf);
         return 1;
     }
@@ -397,18 +397,27 @@ hdb_setupindex(TSK_HDB_INFO * hdb_info, uint8_t htype)
 {
     char head[TSK_HDB_MAXLEN];
     char *ptr;
+ 
+    // Lock for lazy load of hIdx and lazy alloc of idx_lbuf.
+    tsk_take_lock(&hdb_info->lock);
 
+    if (hdb_info->hIdx != NULL) {
+        tsk_release_lock(&hdb_info->lock);
+        return 0;
+    }
 
     if ((htype != TSK_HDB_HTYPE_MD5_ID)
         && (htype != TSK_HDB_HTYPE_SHA1_ID)) {
+        tsk_release_lock(&hdb_info->lock);
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_ARG);
+        tsk_error_set_errstr(
                  "hdb_setupindex: Invalid hash type : %d", htype);
         return 1;
     }
 
     if (hdb_setuphash(hdb_info, htype)) {
+        tsk_release_lock(&hdb_info->lock);
         return 1;
     }
 
@@ -419,9 +428,10 @@ hdb_setupindex(TSK_HDB_INFO * hdb_info, uint8_t htype)
         DWORD szLow, szHi;
 
         if (-1 == GetFileAttributes(hdb_info->idx_fname)) {
+            tsk_release_lock(&hdb_info->lock);
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_MISSING;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_MISSING);
+            tsk_error_set_errstr(
                      "hdb_setupindex: Error finding index file: %"PRIttocTSK,
                      hdb_info->idx_fname);
             return 1;
@@ -430,9 +440,10 @@ hdb_setupindex(TSK_HDB_INFO * hdb_info, uint8_t htype)
         if ((hWin = CreateFile(hdb_info->idx_fname, GENERIC_READ,
                                FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0)) ==
             INVALID_HANDLE_VALUE) {
+            tsk_release_lock(&hdb_info->lock);
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_OPEN;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_OPEN);
+            tsk_error_set_errstr(
                      "hdb_setupindex: Error opening index file: %"PRIttocTSK,
                      hdb_info->idx_fname);
             return 1;
@@ -440,18 +451,20 @@ hdb_setupindex(TSK_HDB_INFO * hdb_info, uint8_t htype)
         hdb_info->hIdx =
             _fdopen(_open_osfhandle((intptr_t) hWin, _O_RDONLY), "r");
         if (hdb_info->hIdx == NULL) {
+            tsk_release_lock(&hdb_info->lock);
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_OPEN;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_OPEN);
+            tsk_error_set_errstr(
                      "hdb_setupindex: Error converting Windows handle to C handle");
             return 1;
         }
 
         szLow = GetFileSize(hWin, &szHi);
         if (szLow == 0xffffffff) {
+            tsk_release_lock(&hdb_info->lock);
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_OPEN;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_OPEN);
+            tsk_error_set_errstr(
                      "hdb_setupindex: Error getting size of index file: %"PRIttocTSK" - %d",
                      hdb_info->idx_fname, (int)GetLastError());
             return 1;
@@ -463,9 +476,10 @@ hdb_setupindex(TSK_HDB_INFO * hdb_info, uint8_t htype)
     {
         struct stat sb;
         if (stat(hdb_info->idx_fname, &sb) < 0) {
+            tsk_release_lock(&hdb_info->lock);
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_MISSING;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_MISSING);
+            tsk_error_set_errstr(
                      "hdb_setupindex: Error finding index file: %s",
                      hdb_info->idx_fname);
             return 1;
@@ -473,9 +487,10 @@ hdb_setupindex(TSK_HDB_INFO * hdb_info, uint8_t htype)
         hdb_info->idx_size = sb.st_size;
 
         if (NULL == (hdb_info->hIdx = fopen(hdb_info->idx_fname, "r"))) {
+            tsk_release_lock(&hdb_info->lock);
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_OPEN;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_OPEN);
+            tsk_error_set_errstr(
                      "hdb_setupindex: Error opening index file: %s",
                      hdb_info->idx_fname);
             return 1;
@@ -485,18 +500,20 @@ hdb_setupindex(TSK_HDB_INFO * hdb_info, uint8_t htype)
 
     /* Do some testing on the first line */
     if (NULL == fgets(head, TSK_HDB_MAXLEN, hdb_info->hIdx)) {
+        tsk_release_lock(&hdb_info->lock);
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_READIDX;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_READIDX);
+        tsk_error_set_errstr(
                  "hdb_setupindex: Header line of index file");
         return 1;
     }
 
     if (strncmp(head, TSK_HDB_IDX_HEAD_STR, strlen(TSK_HDB_IDX_HEAD_STR))
         != 0) {
+        tsk_release_lock(&hdb_info->lock);
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_UNKTYPE;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_UNKTYPE);
+        tsk_error_set_errstr(
                  "hdb_setupindex: Invalid index file: Missing header line");
         return 1;
     }
@@ -517,9 +534,10 @@ hdb_setupindex(TSK_HDB_INFO * hdb_info, uint8_t htype)
     if (strcmp(ptr, TSK_HDB_DBTYPE_NSRL_STR) == 0) {
         if ((hdb_info->db_type != TSK_HDB_DBTYPE_NSRL_ID) &&
             (hdb_info->db_type != TSK_HDB_DBTYPE_IDXONLY_ID)) {
+            tsk_release_lock(&hdb_info->lock);
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_UNKTYPE;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_UNKTYPE);
+            tsk_error_set_errstr(
                      "hdb_indexsetup: DB detected as %s, index type has NSRL",
                      ptr);
             return 1;
@@ -528,9 +546,10 @@ hdb_setupindex(TSK_HDB_INFO * hdb_info, uint8_t htype)
     else if (strcmp(ptr, TSK_HDB_DBTYPE_MD5SUM_STR) == 0) {
         if ((hdb_info->db_type != TSK_HDB_DBTYPE_MD5SUM_ID) &&
             (hdb_info->db_type != TSK_HDB_DBTYPE_IDXONLY_ID)) {
+            tsk_release_lock(&hdb_info->lock);
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_UNKTYPE;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_UNKTYPE);
+            tsk_error_set_errstr(
                      "hdb_indexsetup: DB detected as %s, index type has MD5SUM",
                      ptr);
             return 1;
@@ -539,18 +558,20 @@ hdb_setupindex(TSK_HDB_INFO * hdb_info, uint8_t htype)
     else if (strcmp(ptr, TSK_HDB_DBTYPE_HK_STR) == 0) {
         if ((hdb_info->db_type != TSK_HDB_DBTYPE_HK_ID) &&
             (hdb_info->db_type != TSK_HDB_DBTYPE_IDXONLY_ID)) {
+            tsk_release_lock(&hdb_info->lock);
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_UNKTYPE;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_UNKTYPE);
+            tsk_error_set_errstr(
                      "hdb_indexsetup: DB detected as %s, index type has hashkeeper",
                      ptr);
             return 1;
         }
     }
     else if (hdb_info->db_type != TSK_HDB_DBTYPE_IDXONLY_ID) {
+        tsk_release_lock(&hdb_info->lock);
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_UNKTYPE;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_UNKTYPE);
+        tsk_error_set_errstr(
                  "hdb_setupindex: Unknown Database Type in index header: %s",
                  ptr);
         return 1;
@@ -559,16 +580,21 @@ hdb_setupindex(TSK_HDB_INFO * hdb_info, uint8_t htype)
     /* Do some sanity checking */
     if (((hdb_info->idx_size - hdb_info->idx_off) % hdb_info->idx_llen) !=
         0) {
+        tsk_release_lock(&hdb_info->lock);
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_CORRUPT;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+        tsk_error_set_errstr(
                  "hdb_setupindex: Error, size of index file is not a multiple of row size");
         return 1;
     }
 
     /* allocate a buffer for a row */
-    if ((hdb_info->idx_lbuf = tsk_malloc(hdb_info->idx_llen + 1)) == NULL)
+    if ((hdb_info->idx_lbuf = tsk_malloc(hdb_info->idx_llen + 1)) == NULL) {
+        tsk_release_lock(&hdb_info->lock);
         return 1;
+    }
+
+    tsk_release_lock(&hdb_info->lock);
 
     return 0;
 }
@@ -616,8 +642,8 @@ tsk_hdb_lookup_str(TSK_HDB_INFO * hdb_info, const char *hash,
     }
     else {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_ARG);
+        tsk_error_set_errstr(
                  "hdb_lookup: Invalid hash length: %s", hash);
         return -1;
     }
@@ -625,26 +651,23 @@ tsk_hdb_lookup_str(TSK_HDB_INFO * hdb_info, const char *hash,
     for (i = 0; i < strlen(hash); i++) {
         if (isxdigit((int) hash[i]) == 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_ARG;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_ARG);
+            tsk_error_set_errstr(
                      "hdb_lookup: Invalid hash value (hex only): %s",
                      hash);
             return -1;
         }
     }
 
+    if (hdb_setupindex(hdb_info, htype))
+        return -1;
 
-    /* See if we have had a lookup yet -- and therefore initialized the variables */
-    if (hdb_info->hIdx == NULL) {
-        if (hdb_setupindex(hdb_info, htype))
-            return -1;
-    }
 
     /* Sanity check */
     if (hdb_info->hash_len != strlen(hash)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_ARG);
+        tsk_error_set_errstr(
                  "hdb_lookup: Hash passed is different size than expected (%d vs %Zd)",
                  hdb_info->hash_len, strlen(hash));
         return -1;
@@ -655,11 +678,17 @@ tsk_hdb_lookup_str(TSK_HDB_INFO * hdb_info, const char *hash,
     up = hdb_info->idx_size;
 
     poffset = 0;
+
+    // We have to lock access to idx_lbuf, but since we're in a loop,
+    // I'm assuming one lock up front is better than many inside.
+    tsk_take_lock(&hdb_info->lock);
+
     while (1) {
         TSK_OFF_T offset;
 
         /* If top and bottom are the same, it's not there */
         if (up == low) {
+            tsk_release_lock(&hdb_info->lock);
             return 0;
         }
 
@@ -668,9 +697,10 @@ tsk_hdb_lookup_str(TSK_HDB_INFO * hdb_info, const char *hash,
 
         /* Sanity Check */
         if ((offset % hdb_info->idx_llen) != 0) {
+            tsk_release_lock(&hdb_info->lock);
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_CORRUPT;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+            tsk_error_set_errstr(
                      "hdb_lookup: Error, new offset is not a multiple of the line length");
             return -1;
         }
@@ -680,14 +710,16 @@ tsk_hdb_lookup_str(TSK_HDB_INFO * hdb_info, const char *hash,
 
         /* If we didn't move, then it's not there */
         if (poffset == offset) {
+            tsk_release_lock(&hdb_info->lock);
             return 0;
         }
 
         /* Seek to the offset and read it */
         if (0 != fseeko(hdb_info->hIdx, offset, SEEK_SET)) {
+            tsk_release_lock(&hdb_info->lock);
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_READIDX;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_READIDX);
+            tsk_error_set_errstr(
                      "hdb_lookup: Error seeking in search: %" PRIuOFF,
                      offset);
             return -1;
@@ -697,11 +729,13 @@ tsk_hdb_lookup_str(TSK_HDB_INFO * hdb_info, const char *hash,
             fgets(hdb_info->idx_lbuf, (int) hdb_info->idx_llen + 1,
                   hdb_info->hIdx)) {
             if (feof(hdb_info->hIdx)) {
+                tsk_release_lock(&hdb_info->lock);
                 return 0;
             }
+            tsk_release_lock(&hdb_info->lock);
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_READIDX;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_READIDX);
+            tsk_error_set_errstr(
                      "Error reading index file: %lu",
                      (unsigned long) offset);
             return -1;
@@ -710,9 +744,10 @@ tsk_hdb_lookup_str(TSK_HDB_INFO * hdb_info, const char *hash,
         /* Sanity Check */
         if ((strlen(hdb_info->idx_lbuf) < hdb_info->idx_llen) ||
             (hdb_info->idx_lbuf[hdb_info->hash_len] != '|')) {
+            tsk_release_lock(&hdb_info->lock);
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_CORRUPT;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+            tsk_error_set_errstr(
                      "Invalid line in index file: %lu (%s)",
                      (unsigned long) (offset / hdb_info->idx_llen),
                      hdb_info->idx_lbuf);
@@ -741,6 +776,7 @@ tsk_hdb_lookup_str(TSK_HDB_INFO * hdb_info, const char *hash,
 
             if ((flags & TSK_HDB_FLAG_QUICK)
                 || (hdb_info->db_type == TSK_HDB_DBTYPE_IDXONLY_ID)) {
+                tsk_release_lock(&hdb_info->lock);
                 return 1;
             }
             else {
@@ -758,7 +794,8 @@ tsk_hdb_lookup_str(TSK_HDB_INFO * hdb_info, const char *hash,
                 /* Print the one that we found first */
                 if (hdb_info->
                     getentry(hdb_info, hash, db_off, flags, action, ptr)) {
-                    snprintf(tsk_errstr2, TSK_ERRSTR_L, "hdb_lookup");
+                    tsk_release_lock(&hdb_info->lock);
+                    tsk_error_set_errstr2( "hdb_lookup");
                     return -1;
                 }
 
@@ -776,9 +813,10 @@ tsk_hdb_lookup_str(TSK_HDB_INFO * hdb_info, const char *hash,
                         break;
 
                     if (0 != fseeko(hdb_info->hIdx, tmpoff, SEEK_SET)) {
+                        tsk_release_lock(&hdb_info->lock);
                         tsk_error_reset();
-                        tsk_errno = TSK_ERR_HDB_READIDX;
-                        snprintf(tsk_errstr, TSK_ERRSTR_L,
+                        tsk_error_set_errno(TSK_ERR_HDB_READIDX);
+                        tsk_error_set_errstr(
                                  "hdb_lookup: Error seeking for prev entries: %"
                                  PRIuOFF, tmpoff);
                         return -1;
@@ -788,18 +826,20 @@ tsk_hdb_lookup_str(TSK_HDB_INFO * hdb_info, const char *hash,
                         fgets(hdb_info->idx_lbuf,
                               (int) hdb_info->idx_llen + 1,
                               hdb_info->hIdx)) {
+                        tsk_release_lock(&hdb_info->lock);
                         tsk_error_reset();
-                        tsk_errno = TSK_ERR_HDB_READIDX;
-                        snprintf(tsk_errstr, TSK_ERRSTR_L,
+                        tsk_error_set_errno(TSK_ERR_HDB_READIDX);
+                        tsk_error_set_errstr(
                                  "Error reading index file (prev): %lu",
                                  (unsigned long) tmpoff);
                         return -1;
                     }
                     else if (strlen(hdb_info->idx_lbuf) <
                              hdb_info->idx_llen) {
+                        tsk_release_lock(&hdb_info->lock);
                         tsk_error_reset();
-                        tsk_errno = TSK_ERR_HDB_CORRUPT;
-                        snprintf(tsk_errstr, TSK_ERRSTR_L,
+                        tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+                        tsk_error_set_errstr(
                                  "Invalid index file line (prev): %lu",
                                  (unsigned long) tmpoff);
                         return -1;
@@ -824,6 +864,7 @@ tsk_hdb_lookup_str(TSK_HDB_INFO * hdb_info, const char *hash,
                     if (hdb_info->
                         getentry(hdb_info, hash, db_off, flags, action,
                                  ptr)) {
+                        tsk_release_lock(&hdb_info->lock);
                         return -1;
                     }
                     tmpoff -= hdb_info->idx_llen;
@@ -834,9 +875,10 @@ tsk_hdb_lookup_str(TSK_HDB_INFO * hdb_info, const char *hash,
                 while (tmpoff < up) {
 
                     if (0 != fseeko(hdb_info->hIdx, tmpoff, SEEK_SET)) {
+                        tsk_release_lock(&hdb_info->lock);
                         tsk_error_reset();
-                        tsk_errno = TSK_ERR_HDB_READIDX;
-                        snprintf(tsk_errstr, TSK_ERRSTR_L,
+                        tsk_error_set_errno(TSK_ERR_HDB_READIDX);
+                        tsk_error_set_errstr(
                                  "hdb_lookup: Error seeking for next entries: %"
                                  PRIuOFF, tmpoff);
                         return -1;
@@ -848,18 +890,20 @@ tsk_hdb_lookup_str(TSK_HDB_INFO * hdb_info, const char *hash,
                               hdb_info->hIdx)) {
                         if (feof(hdb_info->hIdx))
                             break;
+                        tsk_release_lock(&hdb_info->lock);
                         tsk_error_reset();
-                        tsk_errno = TSK_ERR_HDB_READIDX;
-                        snprintf(tsk_errstr, TSK_ERRSTR_L,
+                        tsk_error_set_errno(TSK_ERR_HDB_READIDX);
+                        tsk_error_set_errstr(
                                  "Error reading index file (next): %lu",
                                  (unsigned long) tmpoff);
                         return -1;
                     }
                     else if (strlen(hdb_info->idx_lbuf) <
                              hdb_info->idx_llen) {
+                        tsk_release_lock(&hdb_info->lock);
                         tsk_error_reset();
-                        tsk_errno = TSK_ERR_HDB_CORRUPT;
-                        snprintf(tsk_errstr, TSK_ERRSTR_L,
+                        tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
+                        tsk_error_set_errstr(
                                  "Invalid index file line (next): %lu",
                                  (unsigned long) tmpoff);
                         return -1;
@@ -882,6 +926,7 @@ tsk_hdb_lookup_str(TSK_HDB_INFO * hdb_info, const char *hash,
                     if (hdb_info->
                         getentry(hdb_info, hash, db_off, flags, action,
                                  ptr)) {
+                        tsk_release_lock(&hdb_info->lock);
                         return -1;
                     }
 
@@ -892,6 +937,7 @@ tsk_hdb_lookup_str(TSK_HDB_INFO * hdb_info, const char *hash,
         }
         poffset = offset;
     }
+    tsk_release_lock(&hdb_info->lock);
 
     return wasFound;
 }
@@ -917,12 +963,12 @@ tsk_hdb_lookup_raw(TSK_HDB_INFO * hdb_info, uint8_t * hash, uint8_t len,
 {
     char hashbuf[TSK_HDB_HTYPE_SHA1_LEN + 1];
     int i;
-    static char hex[] = "0123456789abcdef";
+    static const char hex[] = "0123456789abcdef";
 
     if (2 * len > TSK_HDB_HTYPE_SHA1_LEN) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_HDB_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_HDB_ARG);
+        tsk_error_set_errstr(
                  "tsk_hdb_lookup_raw: hash value too long\n");
         return -1;
     }
@@ -950,13 +996,10 @@ tsk_hdb_hasindex(TSK_HDB_INFO * hdb_info, uint8_t htype)
 {
     /* Check if the index is already open, and 
      * try to open it if not */
-    if (hdb_info->idx_size == 0) {
-        if (hdb_setupindex(hdb_info, htype))
-            return 0;
-        else
-            return 1;
-    }
-    return 1;
+    if (hdb_setupindex(hdb_info, htype))
+        return 0;
+    else
+        return 1;
 }
 
 
@@ -988,8 +1031,8 @@ tsk_hdb_open(TSK_TCHAR * db_file, TSK_HDB_OPEN_ENUM flags)
                                    FILE_SHARE_READ, 0, OPEN_EXISTING, 0,
                                    0)) == INVALID_HANDLE_VALUE) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_HDB_OPEN;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_HDB_OPEN);
+                tsk_error_set_errstr(
                          "hdb_open: Error opening database file: %S",
                          db_file);
                 return NULL;
@@ -998,8 +1041,8 @@ tsk_hdb_open(TSK_TCHAR * db_file, TSK_HDB_OPEN_ENUM flags)
                 _fdopen(_open_osfhandle((intptr_t) hWin, _O_RDONLY), "r");
             if (hDb == NULL) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_HDB_OPEN;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_HDB_OPEN);
+                tsk_error_set_errstr(
                          "hdb_open: Error converting Windows handle to C handle");
                 return NULL;
             }
@@ -1007,8 +1050,8 @@ tsk_hdb_open(TSK_TCHAR * db_file, TSK_HDB_OPEN_ENUM flags)
 #else
         if (NULL == (hDb = fopen(db_file, "r"))) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_OPEN;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_OPEN);
+            tsk_error_set_errstr(
                      "hdb_open: Error opening database file: %s", db_file);
             return NULL;
         }
@@ -1021,8 +1064,8 @@ tsk_hdb_open(TSK_TCHAR * db_file, TSK_HDB_OPEN_ENUM flags)
         if (md5sum_test(hDb)) {
             if (dbtype != 0) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_HDB_UNKTYPE;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_HDB_UNKTYPE);
+                tsk_error_set_errstr(
                          "hdb_open: Error determining DB type (MD5sum)");
                 return NULL;
             }
@@ -1031,8 +1074,8 @@ tsk_hdb_open(TSK_TCHAR * db_file, TSK_HDB_OPEN_ENUM flags)
         if (hk_test(hDb)) {
             if (dbtype != 0) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_HDB_UNKTYPE;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_HDB_UNKTYPE);
+                tsk_error_set_errstr(
                          "hdb_open: Error determining DB type (HK)");
                 return NULL;
             }
@@ -1040,8 +1083,8 @@ tsk_hdb_open(TSK_TCHAR * db_file, TSK_HDB_OPEN_ENUM flags)
         }
         if (dbtype == 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_HDB_UNKTYPE;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_HDB_UNKTYPE);
+            tsk_error_set_errstr(
                      "hdb_open: Error determining DB type");
             return NULL;
         }
@@ -1110,6 +1153,8 @@ tsk_hdb_open(TSK_TCHAR * db_file, TSK_HDB_OPEN_ENUM flags)
     }
     TSTRNCPY(hdb_info->db_fname, db_file, flen);
 
+    tsk_init_lock(&hdb_info->lock);
+
     return hdb_info;
 }
 
@@ -1144,6 +1189,8 @@ tsk_hdb_close(TSK_HDB_INFO * hdb_info)
     if (hdb_info->hDb)
         fclose(hdb_info->hDb);
 
+    tsk_deinit_lock(&hdb_info->lock);
+
     free(hdb_info);
 }
 
diff --git a/tsk3/hashdb/tsk_hashdb.h b/tsk3/hashdb/tsk_hashdb.h
index 00c0ff677..e370e753d 100644
--- a/tsk3/hashdb/tsk_hashdb.h
+++ b/tsk3/hashdb/tsk_hashdb.h
@@ -1,20 +1,21 @@
 /*
- * The Sleuth Kit
- *
- * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2003-2008 Brian Carrier.  All rights reserved
- */
+* The Sleuth Kit
+*
+* Brian Carrier [carrier <at> sleuthkit [dot] org]
+* Copyright (c) 2003-2011 Brian Carrier.  All rights reserved
+*/
 
 /**
- * \file tsk_hashdb.h
- * External header file for hash database support.
- * Note that this file is not meant to be directly included.  
- * It is included by both libtsk.h and tsk_hashdb_i.h.
- */
+* \file tsk_hashdb.h
+* External header file for hash database support.
+* Note that this file is not meant to be directly included.  
+* It is included by both libtsk.h and tsk_hashdb_i.h.
+*/
 
 /**
- * \defgroup hashdblib Hash Database Functions
- */
+* \defgroup hashdblib C Hash Database Functions
+ * \defgroup hashdblib_cpp C++ Hash Database Classes
+*/
 
 #ifndef _TSK_HDB_H
 #define _TSK_HDB_H
@@ -25,9 +26,9 @@ extern "C" {
 #endif
 
 
-/**
- * Flags used for lookups
- */
+    /**
+    * Flags used for lookups
+    */
     enum TSK_HDB_FLAG_ENUM {
         TSK_HDB_FLAG_QUICK = 0x01,      ///< Quickly return if hash is found (do not return file name etc.)
         TSK_HDB_FLAG_EXT = 0x02 ///< Return other details besides only file name (not used
@@ -35,9 +36,9 @@ extern "C" {
     typedef enum TSK_HDB_FLAG_ENUM TSK_HDB_FLAG_ENUM;
 
 
-/**
- * Hash algorithm types
- */
+    /**
+    * Hash algorithm types
+    */
     enum TSK_HDB_HTYPE_ENUM {
         TSK_HDB_HTYPE_MD5_ID = 1,       ///< MD5 Algorithm
         TSK_HDB_HTYPE_SHA1_ID = 2,      ///< SHA1 Algorithm
@@ -52,25 +53,25 @@ extern "C" {
 #define TSK_HDB_HTYPE_CRC32_LEN 8       ///< Length of CRC hash
 
 
-/**
- * Return the name of the hash algorithm, given its ID
- */
+    /**
+    * Return the name of the hash algorithm, given its ID
+    */
 #define TSK_HDB_HTYPE_STR(x) \
     ( ((x) & TSK_HDB_HTYPE_MD5_ID) ? (TSK_HDB_HTYPE_MD5_STR) : ( \
-	( ((x) & TSK_HDB_HTYPE_SHA1_ID) ? TSK_HDB_HTYPE_SHA1_STR : "") ) )
+    ( ((x) & TSK_HDB_HTYPE_SHA1_ID) ? TSK_HDB_HTYPE_SHA1_STR : "") ) )
 
-/**
- * Return the length of a hash, given its ID
- */
+    /**
+    * Return the length of a hash, given its ID
+    */
 #define TSK_HDB_HTYPE_LEN(x) \
     ( ((x) & TSK_HDB_HTYPE_MD5_ID) ? (TSK_HDB_HTYPE_MD5_LEN) : ( \
-	( ((x) & TSK_HDB_HTYPE_SHA1_ID) ? TSK_HDB_HTYPE_SHA1_LEN : 0) ) )
+    ( ((x) & TSK_HDB_HTYPE_SHA1_ID) ? TSK_HDB_HTYPE_SHA1_LEN : 0) ) )
 
 
 
-/**
- * Hash Database types
- */
+    /**
+    * Hash Database types
+    */
     enum TSK_HDB_DBTYPE_ENUM {
         TSK_HDB_DBTYPE_NSRL_ID = 1,     ///< NIST NSRL format
         TSK_HDB_DBTYPE_MD5SUM_ID = 2,   ///< md5sum format
@@ -80,27 +81,27 @@ extern "C" {
     typedef enum TSK_HDB_DBTYPE_ENUM TSK_HDB_DBTYPE_ENUM;
 
 
-/* String versions of DB types */
+    /* String versions of DB types */
 #define TSK_HDB_DBTYPE_NSRL_STR		        "nsrl"  ///< NSRL String name
 #define TSK_HDB_DBTYPE_NSRL_MD5_STR		"nsrl-md5"      ///< NSRL md5 string name
 #define TSK_HDB_DBTYPE_NSRL_SHA1_STR		"nsrl-sha1"     ///< NSRL SHA1 string name
 #define TSK_HDB_DBTYPE_MD5SUM_STR		"md5sum"        ///< md5sum db string n ame
 #define TSK_HDB_DBTYPE_HK_STR			"hk"    ///< hash keeper string name
-/// List of supported data base types
+    /// List of supported data base types
 #define TSK_HDB_DBTYPE_SUPPORT_STR		"nsrl-md5, nsrl-sha1, md5sum, hk"
 
 
     typedef struct TSK_HDB_INFO TSK_HDB_INFO;
 
     typedef TSK_WALK_RET_ENUM(*TSK_HDB_LOOKUP_FN) (TSK_HDB_INFO *,
-                                                   const char *hash,
-                                                   const char *name,
-                                                   void *);
+        const char *hash,
+        const char *name,
+        void *);
 
-/**
- * Holds information about an open hash database. Created by 
- * hdb_open and used for making an index and looking up values.
- */
+    /**
+    * Holds information about an open hash database. Created by 
+    * hdb_open and used for making an index and looking up values.
+    */
     struct TSK_HDB_INFO {
 
         TSK_TCHAR *db_fname;    ///< Name of the database
@@ -109,12 +110,15 @@ extern "C" {
 
         FILE *hDb;              ///< File handle to database (always open)
         FILE *hIdxTmp;          ///< File handle to temp (unsorted) index file (only open during index creation)
-        FILE *hIdx;             ///< File handle to index (only open during lookups)
+        FILE *hIdx;             ///< File handle to index (only open during lookups) 
 
         TSK_OFF_T idx_size;     ///< Size of index file
         uint16_t idx_off;       ///< Offset in index file to first index entry
         size_t idx_llen;        ///< Length of each line in index
-        char *idx_lbuf;         ///< Buffer to hold a line from the index
+
+        /* lock protects idx_lbuf and lazy loading of hIdx */
+        tsk_lock_t lock;        ///< Lock for lazy loading and idx_lbuf
+        char *idx_lbuf;         ///< Buffer to hold a line from the index  (r/w shared - lock) 
         TSK_TCHAR *idx_fname;   ///< Name of index file
 
         TSK_HDB_HTYPE_ENUM hash_type;   ///< Type of hash used in index
@@ -122,13 +126,13 @@ extern "C" {
 
         TSK_HDB_DBTYPE_ENUM db_type;    ///< Type of database
 
-         uint8_t(*getentry) (TSK_HDB_INFO *, const char *, TSK_OFF_T, TSK_HDB_FLAG_ENUM, TSK_HDB_LOOKUP_FN, void *);    ///< \internal Database-specific function to find entry at a given offset
-         uint8_t(*makeindex) (TSK_HDB_INFO *, TSK_TCHAR *);     ///< \internal Database-specific function to make index
+        uint8_t(*getentry) (TSK_HDB_INFO *, const char *, TSK_OFF_T, TSK_HDB_FLAG_ENUM, TSK_HDB_LOOKUP_FN, void *);    ///< \internal Database-specific function to find entry at a given offset
+        uint8_t(*makeindex) (TSK_HDB_INFO *, TSK_TCHAR *);     ///< \internal Database-specific function to make index
     };
 
     /**
-     * Options for opening a hash database
-     */
+    * Options for opening a hash database
+    */
     enum TSK_HDB_OPEN_ENUM {
         TSK_HDB_OPEN_NONE = 0,  ///< No special flags
         TSK_HDB_OPEN_IDXONLY = (0x1 << 0)       ///< Open only the index -- do not look for the original DB
@@ -137,23 +141,161 @@ extern "C" {
 
 
     extern TSK_HDB_INFO *tsk_hdb_open(TSK_TCHAR * db_file,
-                                      TSK_HDB_OPEN_ENUM flags);
+        TSK_HDB_OPEN_ENUM flags);
     extern void tsk_hdb_close(TSK_HDB_INFO * hdb);
 
     extern uint8_t tsk_hdb_hasindex(TSK_HDB_INFO *, uint8_t htype);
     extern uint8_t tsk_hdb_makeindex(TSK_HDB_INFO *, TSK_TCHAR *);
 
 
-/* Functions */
+    /* Functions */
     extern int8_t tsk_hdb_lookup_str(TSK_HDB_INFO *, const char *,
-                                     TSK_HDB_FLAG_ENUM, TSK_HDB_LOOKUP_FN,
-                                     void *);
+        TSK_HDB_FLAG_ENUM, TSK_HDB_LOOKUP_FN,
+        void *);
 
     extern int8_t tsk_hdb_lookup_raw(TSK_HDB_INFO *, uint8_t * hash,
-                                     uint8_t len, TSK_HDB_FLAG_ENUM,
-                                     TSK_HDB_LOOKUP_FN, void *);
+        uint8_t len, TSK_HDB_FLAG_ENUM,
+        TSK_HDB_LOOKUP_FN, void *);
 
 #ifdef __cplusplus
 }
 #endif
+
+#ifdef __cplusplus
+
+
+/** 
+ * \ingroup hashdblib_cpp
+* Stores information about an open hash database.
+* To use this object, open() should be called first. Otherwise, the other
+* functions will have undefined return values. 
+*/
+class TskHdbInfo{
+private:
+    TSK_HDB_INFO * m_hdbInfo;
+    TskHdbInfo(const TskHdbInfo& rhs); 
+    TskHdbInfo& operator=(const TskHdbInfo& rhs);
+    
+public:
+    /**
+    * Close an open hash database.
+    */
+    ~TskHdbInfo() {
+        tsk_hdb_close(m_hdbInfo);
+    };
+    
+    /**
+    * Open a hash database. See tsk_hdb_open() for details.
+    *
+    * @param a_dbFile Path to database.
+    * @param a_flags Flags for opening the database.  
+    *
+    * @return 1 on error and 0 on success
+    */
+    uint8_t open(TSK_TCHAR * a_dbFile, TSK_HDB_OPEN_ENUM a_flags) {
+        if ((m_hdbInfo = tsk_hdb_open(a_dbFile, a_flags)) != NULL)
+            return 0;
+        else
+            return 1;
+    };
+    
+    /**
+    * Search the index for a text/ASCII hash value
+    * See tsk_hdb_lookup_str() for details.
+    * @param a_hash Hash value to search for (NULL terminated string)
+
+    * @param a_flags Flags to use in lookup
+    * @param a_action Callback function to call for each hash db entry 
+    * (not called if QUICK flag is given)
+    * @param a_ptr Pointer to data to pass to each callback
+    *
+    * @return -1 on error, 0 if hash value not found, and 1 if value was found.
+    */
+    int8_t lookupStr(const char *a_hash,
+                     TSK_HDB_FLAG_ENUM a_flags, TSK_HDB_LOOKUP_FN a_action, void *a_ptr) {
+            if (m_hdbInfo != NULL)
+                return tsk_hdb_lookup_str(m_hdbInfo, a_hash,
+                a_flags, a_action, a_ptr);
+            else
+                return 0;
+    };
+    
+    /**
+    * Search the index for the given hash value given (in binary form).
+    * See tsk_hdb_lookup_raw() for details.
+    * @param a_hash Array with binary hash value to search for
+    * @param a_len Number of bytes in binary hash value
+    * @param a_flags Flags to use in lookup
+    * @param a_action Callback function to call for each hash db entry 
+    * (not called if QUICK flag is given)
+    * @param a_ptr Pointer to data to pass to each callback
+    *
+    * @return -1 on error, 0 if hash value not found, and 1 if value was found.
+    */
+    int8_t lookupRaw(uint8_t * a_hash, uint8_t a_len,
+                     TSK_HDB_FLAG_ENUM a_flags, TSK_HDB_LOOKUP_FN a_action, void *a_ptr) {
+            if (m_hdbInfo != NULL)
+                return tsk_hdb_lookup_raw(m_hdbInfo, a_hash, a_len, a_flags,
+                a_action, a_ptr);
+            else
+                return 0;
+    };
+    
+    /**
+    * Create an index for an open hash database.
+    * See tsk_hdb_makeindex() for details.
+    * @param a_type Text of hash database type
+    * @return 1 on error
+    */
+    uint8_t createIndex(TSK_TCHAR * a_type) {
+        if (m_hdbInfo != NULL)
+            return tsk_hdb_makeindex(m_hdbInfo, a_type);
+        else
+            return 0;
+    };
+    
+    /**
+    * Determine if the open hash database has an index.
+    * See tsk_hdb_hasindex for details.
+    * @param a_htype Hash type that index should be of
+    *
+    * @return 1 if index exists and 0 if not
+    */
+    uint8_t hasIndex(uint8_t a_htype) {
+        if (m_hdbInfo != NULL)
+            return tsk_hdb_hasindex(m_hdbInfo, a_htype);
+        else
+            return 0;
+    };
+    
+    /**
+    * get type of hash used in index
+    * @return type of hash used in index
+    */
+    TSK_HDB_HTYPE_ENUM getHashType() const {
+        if (m_hdbInfo != NULL)
+            return m_hdbInfo->hash_type;
+    };
+    
+    /**
+    * get length of hash
+    * @return length of hash
+    */
+    uint16_t getHashLen() const {
+        if (m_hdbInfo != NULL)
+            return m_hdbInfo->hash_len;
+        else
+            return 0;
+    };
+    
+    /**
+    * get type of database
+    * @return type of database
+    */
+    TSK_HDB_DBTYPE_ENUM getDbType() const {
+        if (m_hdbInfo != NULL)
+            return m_hdbInfo->db_type;
+    };
+};
+#endif
 #endif
diff --git a/tsk3/hashdb/tsk_hashdb_i.h b/tsk3/hashdb/tsk_hashdb_i.h
index efb530a9d..fdf247162 100644
--- a/tsk3/hashdb/tsk_hashdb_i.h
+++ b/tsk3/hashdb/tsk_hashdb_i.h
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2003-2008 Brian Carrier.  All rights reserved
+ * Copyright (c) 2003-2011 Brian Carrier.  All rights reserved
  */
 
 /**
diff --git a/tsk3/img/aff.c b/tsk3/img/aff.c
index a230ffea8..5e756bb5f 100644
--- a/tsk3/img/aff.c
+++ b/tsk3/img/aff.c
@@ -1,6 +1,6 @@
 /*
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All rights reserved
+ * Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All rights reserved
  *
  * This software is distributed under the Common Public License 1.0
  */
@@ -18,6 +18,7 @@ typedef int bool;
 
 #include "aff.h"
 
+/* Note: The routine -assumes- we are under a lock on &(img_info->cache_lock)) */
 static ssize_t
 aff_read(TSK_IMG_INFO * img_info, TSK_OFF_T offset, char *buf, size_t len)
 {
@@ -31,8 +32,8 @@ aff_read(TSK_IMG_INFO * img_info, TSK_OFF_T offset, char *buf, size_t len)
 
     if (offset > img_info->size) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_IMG_READ_OFF;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "aff_read - %" PRIuOFF, offset);
+        tsk_error_set_errno(TSK_ERR_IMG_READ_OFF);
+        tsk_error_set_errstr("aff_read - %" PRIuOFF, offset);
         return -1;
     }
 
@@ -40,8 +41,8 @@ aff_read(TSK_IMG_INFO * img_info, TSK_OFF_T offset, char *buf, size_t len)
         if (af_seek(aff_info->af_file, offset, SEEK_SET) != offset) {
             tsk_error_reset();
             // @@@ ADD more specific error messages
-            tsk_errno = TSK_ERR_IMG_SEEK;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_IMG_SEEK);
+            tsk_error_set_errstr(
                 "aff_read - %" PRIuOFF " - %s", offset, strerror(errno));
             return -1;
 
@@ -53,8 +54,8 @@ aff_read(TSK_IMG_INFO * img_info, TSK_OFF_T offset, char *buf, size_t len)
     if (cnt < 0) {
         // @@@ Add more specific error message
         tsk_error_reset();
-        tsk_errno = TSK_ERR_IMG_READ;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_IMG_READ);
+        tsk_error_set_errstr(
             "aff_read - offset: %" PRIuOFF " - len: %" PRIuSIZE " - %s",
             offset, len, strerror(errno));
         return -1;
@@ -216,7 +217,7 @@ aff_close(TSK_IMG_INFO * img_info)
 {
     IMG_AFF_INFO *aff_info = (IMG_AFF_INFO *) img_info;
     af_close(aff_info->af_file);
-    free(aff_info);
+    tsk_img_free(aff_info);
 }
 
 
@@ -228,12 +229,11 @@ aff_open(const char *const images[], unsigned int a_ssize)
     int type;
 
     if ((aff_info =
-            (IMG_AFF_INFO *) tsk_malloc(sizeof(IMG_AFF_INFO))) == NULL) {
+            (IMG_AFF_INFO *) tsk_img_malloc(sizeof(IMG_AFF_INFO))) == NULL) {
         return NULL;
     }
 
     img_info = (TSK_IMG_INFO *) aff_info;
-
     img_info->read = aff_read;
     img_info->close = aff_close;
     img_info->imgstat = aff_imgstat;
@@ -251,12 +251,11 @@ aff_open(const char *const images[], unsigned int a_ssize)
             perror("aff_open");
         }
         tsk_error_reset();
-        tsk_errno = TSK_ERR_IMG_OPEN;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_IMG_OPEN);
+        tsk_error_set_errstr(
             "aff_open file: %" PRIttocTSK ": Error checking type",
             images[0]);
-        tsk_errstr2[0] = '\0';
-        free(aff_info);
+        tsk_img_free(aff_info);
         return NULL;
     }
     else if (type == AF_IDENTIFY_AFF) {
@@ -276,11 +275,11 @@ aff_open(const char *const images[], unsigned int a_ssize)
     if (!aff_info->af_file) {
         // @@@ Need to check here if the open failed because of an incorrect password. 
         tsk_error_reset();
-        tsk_errno = TSK_ERR_IMG_OPEN;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_IMG_OPEN);
+        tsk_error_set_errstr(
             "aff_open file: %" PRIttocTSK ": Error opening - %s",
             images[0], strerror(errno));
-        free(aff_info);
+        tsk_img_free(aff_info);
         if (tsk_verbose) {
             tsk_fprintf(stderr, "Error opening AFF/AFD/AFM file\n");
             perror("aff_open");
@@ -290,10 +289,10 @@ aff_open(const char *const images[], unsigned int a_ssize)
     // verify that a password was given and we can read encrypted data. 
     if (af_cannot_decrypt(aff_info->af_file)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_IMG_PASSWD;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_IMG_PASSWD);
+        tsk_error_set_errstr(
                  "aff_open file: %" PRIttocTSK, images[0]);
-        free(aff_info);
+        tsk_img_free(aff_info);
         if (tsk_verbose) {
             tsk_fprintf(stderr, "Error opening AFF/AFD/AFM file (incorrect password)\n");
         }
@@ -306,7 +305,6 @@ aff_open(const char *const images[], unsigned int a_ssize)
 
     af_seek(aff_info->af_file, 0, SEEK_SET);
     aff_info->seek_pos = 0;
-
     return img_info;
 }
 #endif
diff --git a/tsk3/img/aff.h b/tsk3/img/aff.h
index 1ae7e1e03..c96132553 100644
--- a/tsk3/img/aff.h
+++ b/tsk3/img/aff.h
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2005-2008 Brian Carrier.  All rights reserved 
+ * Copyright (c) 2005-2011 Brian Carrier.  All rights reserved 
  *
  * This software is distributed under the Common Public License 1.0
  */
@@ -28,7 +28,7 @@ extern TSK_IMG_INFO *aff_open(const char *const images[],
 typedef struct {
     TSK_IMG_INFO img_info;
     AFFILE *af_file;
-    TSK_OFF_T seek_pos;
+    TSK_OFF_T seek_pos; // shared and protected by cache_lock in IMG_INFO
     uint16_t type;              /* TYPE - uses AF_IDENTIFY_x values */
 } IMG_AFF_INFO;
 
diff --git a/tsk3/img/ewf.c b/tsk3/img/ewf.c
index 613cb5142..dc86d60c3 100644
--- a/tsk3/img/ewf.c
+++ b/tsk3/img/ewf.c
@@ -25,14 +25,14 @@ ewf_image_read(TSK_IMG_INFO * img_info, TSK_OFF_T offset, char *buf,
 
     if (tsk_verbose)
         tsk_fprintf(stderr,
-            "ewf_read: byte offset: %" PRIuOFF " len: %" PRIuSIZE "\n",
+            "ewf_image_read: byte offset: %" PRIuOFF " len: %" PRIuSIZE "\n",
             offset, len);
 
     if (offset > img_info->size) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_IMG_READ_OFF;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
-            "split_read - %" PRIuOFF, offset);
+        tsk_error_set_errno(TSK_ERR_IMG_READ_OFF);
+        tsk_error_set_errstr(
+            "ewf_image_read - %" PRIuOFF, offset);
         return -1;
     }
 
@@ -41,9 +41,9 @@ ewf_image_read(TSK_IMG_INFO * img_info, TSK_OFF_T offset, char *buf,
         tsk_error_reset();
         // @@@ Add more specific error message
         tsk_error_reset();
-        tsk_errno = TSK_ERR_IMG_READ;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
-            "ewf_read - offset: %" PRIuOFF " - len: %" PRIuSIZE " - %s",
+        tsk_error_set_errno(TSK_ERR_IMG_READ);
+        tsk_error_set_errstr(
+            "ewf_image_read - offset: %" PRIuOFF " - len: %" PRIuSIZE " - %s",
             offset, len, strerror(errno));
         return -1;
     }
@@ -100,8 +100,8 @@ img_file_header_signature_ncmp(const char *filename,
 
     if ((fd = open(filename, O_RDONLY | O_BINARY)) < 0) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_IMG_OPEN;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "ewf magic testing: %s",
+        tsk_error_set_errno(TSK_ERR_IMG_OPEN);
+        tsk_error_set_errstr("ewf magic testing: %s",
             filename);
         return -1;
     }
@@ -109,8 +109,8 @@ img_file_header_signature_ncmp(const char *filename,
 
     if (read_count != 512) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_IMG_READ;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "ewf magic testing: %s",
+        tsk_error_set_errno(TSK_ERR_IMG_READ);
+        tsk_error_set_errstr("ewf magic testing: %s",
             filename);
         return -1;
     }
@@ -132,9 +132,8 @@ ewf_open(int num_img, const TSK_TCHAR * const images[],
 #if !defined( LIBEWF_STRING_DIGEST_HASH_LENGTH_MD5 )
     uint8_t md5_hash[16];
 #endif
-
     if ((ewf_info =
-            (IMG_EWF_INFO *) tsk_malloc(sizeof(IMG_EWF_INFO))) == NULL) {
+            (IMG_EWF_INFO *) tsk_img_malloc(sizeof(IMG_EWF_INFO))) == NULL) {
         return NULL;
     }
 
@@ -149,8 +148,8 @@ ewf_open(int num_img, const TSK_TCHAR * const images[],
     if (libewf_check_file_signature(images[0]) == 0) {
 #endif
         tsk_error_reset();
-        tsk_errno = TSK_ERR_IMG_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "ewf_open: Not an EWF file");
+        tsk_error_set_errno(TSK_ERR_IMG_MAGIC);
+        tsk_error_set_errstr("ewf_open: Not an EWF file");
         free(ewf_info);
         if (tsk_verbose)
             tsk_fprintf(stderr, "Not an EWF file\n");
@@ -167,8 +166,8 @@ ewf_open(int num_img, const TSK_TCHAR * const images[],
 #endif
     if (ewf_info->handle == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_IMG_OPEN;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_IMG_OPEN);
+        tsk_error_set_errstr(
             "ewf_open file: %" PRIttocTSK ": Error opening", images[0]);
         free(ewf_info);
         if (tsk_verbose) {
@@ -188,8 +187,8 @@ ewf_open(int num_img, const TSK_TCHAR * const images[],
             (size64_t *) & (img_info->size))
         != 1) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_IMG_OPEN;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_IMG_OPEN);
+        tsk_error_set_errstr(
             "ewf_open file: %" PRIttocTSK ": Error getting size of image",
             images[0]);
         free(ewf_info);
diff --git a/tsk3/img/img_io.c b/tsk3/img/img_io.c
index 5103eeeae..0865fc897 100644
--- a/tsk3/img/img_io.c
+++ b/tsk3/img/img_io.c
@@ -1,6 +1,6 @@
 /*
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2008 Brian Carrier.  All Rights reserved
+ * Copyright (c) 2011 Brian Carrier.  All Rights reserved
  *
  * This software is distributed under the Common Public License 1.0
  */
@@ -33,21 +33,30 @@ tsk_img_read(TSK_IMG_INFO * a_img_info, TSK_OFF_T a_off,
 
     if (a_img_info == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_IMG_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_IMG_ARG);
+        tsk_error_set_errstr(
             "tsk_img_read: pointer is NULL");
         return -1;
     }
 
+    /* cache_lock is used for both the cache in IMG_INFO and 
+     * the shared variables in the img type specific INFO structs.
+     * grab it now so that it is held before any reads.
+     */
+    tsk_take_lock(&(a_img_info->cache_lock));
+
     // if they ask for more than the cache length, skip the cache
     if (a_len > TSK_IMG_INFO_CACHE_LEN) {
-        return a_img_info->read(a_img_info, a_off, a_buf, a_len);
+        ssize_t nbytes = a_img_info->read(a_img_info, a_off, a_buf, a_len);
+        tsk_release_lock(&(a_img_info->cache_lock));
+        return nbytes;
     }
 
     if (a_off >= a_img_info->size) {
+        tsk_release_lock(&(a_img_info->cache_lock));
         tsk_error_reset();
-        tsk_errno = TSK_ERR_IMG_READ_OFF;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "tsk_img_read - %" PRIuOFF,
+        tsk_error_set_errno(TSK_ERR_IMG_READ_OFF);
+        tsk_error_set_errstr("tsk_img_read - %" PRIuOFF,
             a_off);
         return -1;
     }
@@ -153,5 +162,6 @@ tsk_img_read(TSK_IMG_INFO * a_img_info, TSK_OFF_T a_off,
         }
     }
 
+    tsk_release_lock(&(a_img_info->cache_lock));
     return retval;
 }
diff --git a/tsk3/img/img_open.c b/tsk3/img/img_open.c
index 65f4ff485..1c80a4a77 100644
--- a/tsk3/img/img_open.c
+++ b/tsk3/img/img_open.c
@@ -1,6 +1,6 @@
 /*
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All Rights reserved
+ * Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
  * Copyright (c) 2005 Brian Carrier.  All rights reserved
  *
  * tsk_img_open
@@ -32,12 +32,12 @@ typedef int bool;
 
 /**
  * \ingroup imglib
- * Opens a single (non-split) disk image file so that it can be read.  This is a 
+ * Opens a single (non-split) disk image file so that it can be read.  This is a
  * wrapper around tsk_img_open().  See it for more details on detection etc. See
  * tsk_img_open_sing_utf8() for a version of this function that always takes
- * UTF-8 as input. 
+ * UTF-8 as input.
  *
- * @param a_image The path to the image file 
+ * @param a_image The path to the image file
  * @param type The disk image type (can be autodetection)
  * @param a_ssize Size of device sector in bytes (or 0 for default)
  *
@@ -57,10 +57,10 @@ tsk_img_open_sing(const TSK_TCHAR * a_image, TSK_IMG_TYPE_ENUM type,
  * Opens one or more disk image files so that they can be read.  If a file format
  * type is specified, this function will call the specific routine to open the file.
  * Otherwise, it will detect the type (it will default to raw if no specific type can
- * be detected).   This function must be called before a disk image can be read from. 
+ * be detected).   This function must be called before a disk image can be read from.
  * Note that the data type used to store the image paths is a TSK_TCHAR, which changes
  * depending on a Unix or Windows build.  If you will always have UTF8, then consider
- * using tsk_img_open_utf8(). 
+ * using tsk_img_open_utf8().
  *
  * @param num_img The number of images to open (will be > 1 for split images).
  * @param images The path to the image files (the number of files must
@@ -82,34 +82,34 @@ tsk_img_open(int num_img,
 
     if ((num_img == 0) || (images[0] == NULL)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_IMG_NOFILE;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "tsk_img_open");
+        tsk_error_set_errno(TSK_ERR_IMG_NOFILE);
+        tsk_error_set_errstr("tsk_img_open");
         return NULL;
     }
-    
+
     if ((a_ssize > 0) && (a_ssize < 512)) {
-        tsk_error_reset(); 
-        tsk_errno = TSK_ERR_IMG_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "sector size is less than 512 bytes (%d)", a_ssize);
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_IMG_ARG);
+        tsk_error_set_errstr("sector size is less than 512 bytes (%d)", a_ssize);
         return NULL;
     }
-    
+
     if ((a_ssize % 512) != 0) {
-        tsk_error_reset(); 
-        tsk_errno = TSK_ERR_IMG_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "sector size is not a multiple of 512 (%d)", a_ssize);
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_IMG_ARG);
+        tsk_error_set_errstr("sector size is not a multiple of 512 (%d)", a_ssize);
         return NULL;
     }
-    
+
 
     if (tsk_verbose)
         TFPRINTF(stderr,
             _TSK_T("tsk_img_open: Type: %d   NumImg: %d  Img1: %s\n"),
             type, num_img, images[0]);
 
-    /* If no type is given, then we use the autodetection methods 
+    /* If no type is given, then we use the autodetection methods
      * In case the image file matches the signatures of multiple formats,
-     * we try all of the embedded formats 
+     * we try all of the embedded formats
      */
     if (type == TSK_IMG_TYPE_DETECT) {
         TSK_IMG_INFO *img_set = NULL;
@@ -136,7 +136,7 @@ tsk_img_open(int num_img,
         }
         else {
             // If AFF is otherwise happy except for a password, stop trying to guess
-            if (tsk_errno == TSK_ERR_IMG_PASSWD) {
+            if (tsk_error_get_errno() == TSK_ERR_IMG_PASSWD) {
                 return NULL;
             }
             tsk_error_reset();
@@ -153,8 +153,8 @@ tsk_img_open(int num_img,
                 img_set->close(img_set);
                 img_info->close(img_info);
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_IMG_UNKTYPE;
-                snprintf(tsk_errstr, TSK_ERRSTR_L, "EWF or %s", set);
+                tsk_error_set_errno(TSK_ERR_IMG_UNKTYPE);
+                tsk_error_set_errstr("EWF or %s", set);
                 return NULL;
             }
         }
@@ -162,7 +162,7 @@ tsk_img_open(int num_img,
             tsk_error_reset();
         }
 #endif
-        // if any of the non-raw formats were detected, then use it. 
+        // if any of the non-raw formats were detected, then use it.
         if (img_set != NULL)
             return img_set;
 
@@ -171,7 +171,7 @@ tsk_img_open(int num_img,
             if ((img_info = raw_open(images[0], a_ssize)) != NULL) {
                 return img_info;
             }
-            else if (tsk_errno) {
+            else if (tsk_error_get_errno() != 0) {
                 return NULL;
             }
         }
@@ -179,7 +179,7 @@ tsk_img_open(int num_img,
             if ((img_info = split_open(num_img, images, a_ssize)) != NULL) {
                 return img_info;
             }
-            else if (tsk_errno) {
+            else if (tsk_error_get_errno() != 0) {
                 return NULL;
             }
         }
@@ -198,18 +198,16 @@ tsk_img_open(int num_img,
             else {
 #endif
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_IMG_STAT;
-                snprintf(tsk_errstr, TSK_ERRSTR_L, "%" PRIttocTSK " : %s",
+                tsk_error_set_errno(TSK_ERR_IMG_STAT);
+                tsk_error_set_errstr("%" PRIttocTSK " : %s",
                     images[0], strerror(errno));
                 return NULL;
 #if defined(TSK_WIN32) || defined(__CYGWIN__)
             }
 #endif
         }
-
-        tsk_errno = TSK_ERR_IMG_UNKTYPE;
-        tsk_errstr[0] = '\0';
-        tsk_errstr2[0] = '\0';
+        tsk_error_reset();
+        tsk_error_set_errno(TSK_ERR_IMG_UNKTYPE);
         return NULL;
     }
 
@@ -256,8 +254,8 @@ tsk_img_open(int num_img,
 
     default:
         tsk_error_reset();
-        tsk_errno = TSK_ERR_IMG_UNSUPTYPE;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "%d", type);
+        tsk_error_set_errno(TSK_ERR_IMG_UNSUPTYPE);
+        tsk_error_set_errstr("%d", type);
         return NULL;
     }
 
@@ -269,10 +267,10 @@ tsk_img_open(int num_img,
 * \ingroup imglib
  * Opens a single (non-split) disk image file so that it can be read.  This version
  * always takes a UTF-8 encoding of the disk image.  See tsk_img_open_sing() for a
- * version that takes a wchar_t or char depending on the platform. 
- * This is a wrapper around tsk_img_open().  See it for more details on detection etc. 
+ * version that takes a wchar_t or char depending on the platform.
+ * This is a wrapper around tsk_img_open().  See it for more details on detection etc.
  *
- * @param a_image The UTF-8 path to the image file 
+ * @param a_image The UTF-8 path to the image file
  * @param type The disk image type (can be autodetection)
  * @param a_ssize Size of device sector in bytes (or 0 for default)
  *
@@ -290,8 +288,8 @@ tsk_img_open_utf8_sing(const char *a_image, TSK_IMG_TYPE_ENUM type,
 /**
  * \ingroup imglib
  * Opens one or more disk image files so that they can be read.  This is a wrapper
- * around tsk_img_open() and this version always takes a UTF-8 encoding of the 
- * image files.  See its description for more details. 
+ * around tsk_img_open() and this version always takes a UTF-8 encoding of the
+ * image files.  See its description for more details.
  *
  * @param num_img The number of images to open (will be > 1 for split images).
  * @param images The path to the UTF-8 encoded image files (the number of files must
@@ -314,7 +312,7 @@ tsk_img_open_utf8(int num_img, const char *const images[],
         wchar_t **images16;
         int i;
 
-        // allocate a buffer to store the UTF-16 version of the images. 
+        // allocate a buffer to store the UTF-16 version of the images.
         if ((images16 =
                 (wchar_t **) tsk_malloc(sizeof(wchar_t *) * num_img)) ==
             NULL) {
@@ -342,8 +340,8 @@ tsk_img_open_utf8(int num_img, const char *const images[],
                 tsk_UTF8toUTF16((const UTF8 **) &utf8, &utf8[ilen],
                 &utf16, &utf16[ilen], TSKlenientConversion);
             if (retval2 != TSKconversionOK) {
-                tsk_errno = TSK_ERR_IMG_CONVERT;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_IMG_CONVERT);
+                tsk_error_set_errstr(
                     "tsk_img_open_utf8: Error converting image %s %d",
                     images[i], retval2);
                 goto tsk_utf8_cleanup;
@@ -372,7 +370,7 @@ tsk_img_open_utf8(int num_img, const char *const images[],
 #if 0
 /* This interface needs some more thought because the size of wchar is not standard.
  * If the goal i to provide a constant wchar interface, then we need to incorporate
- * UTF-32 to UTF-8 support as well.  If the goal is to provide a standard UTF-16 
+ * UTF-32 to UTF-8 support as well.  If the goal is to provide a standard UTF-16
  * interface, we should use another type besiddes wchar_t.
  */
 TSK_IMG_INFO *
@@ -413,7 +411,7 @@ tsk_img_open_utf16(int num_img,
             TSKConversionResult retval2;
 
 
-            // we allocate the buffer to be four times the utf-16 length. 
+            // we allocate the buffer to be four times the utf-16 length.
             ilen = wcslen(images[i]);
             ilen <<= 2;
 
@@ -429,8 +427,8 @@ tsk_img_open_utf16(int num_img,
                 &utf16[wcslen(images[i]) + 1], &utf8,
                 &utf8[ilen + 1], TSKlenientConversion);
             if (retval2 != TSKconversionOK) {
-                tsk_errno = TSK_ERR_IMG_CONVERT;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_IMG_CONVERT);
+                tsk_error_set_errstr(
                     "tsk_img_open_utf16: Error converting image %d %d", i,
                     retval2);
                 return NULL;
diff --git a/tsk3/img/img_types.c b/tsk3/img/img_types.c
index f1c0059ff..90a47d24f 100644
--- a/tsk3/img/img_types.c
+++ b/tsk3/img/img_types.c
@@ -5,7 +5,7 @@
 ** Identify the type of image file being used
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
-** Copyright (c) 2006-2008 Brian Carrier.  All rights reserved 
+** Copyright (c) 2006-2011 Brian Carrier.  All rights reserved 
 **
 ** This software is distributed under the Common Public License 1.0
 */
diff --git a/tsk3/img/raw.c b/tsk3/img/raw.c
index 91b134852..76dc7ae70 100644
--- a/tsk3/img/raw.c
+++ b/tsk3/img/raw.c
@@ -1,6 +1,6 @@
 /*
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All rights reserved
+ * Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All rights reserved
  * Copyright (c) 2005 Brian Carrier.  All rights reserved
  *
  * raw
@@ -30,7 +30,7 @@
 /**
  * Read an arbitrary amount of data from a specific location in a raw image file.
  * This takes two offsets are arguments.  The first is the offset of the volume in the
- * image file and the second is the offset in the volume.  Both are added to find the 
+ * image file and the second is the offset in the volume.  Both are added to find the
  * actual offset.
  *
  * @param img_info The image to read from.
@@ -52,8 +52,8 @@ raw_read(TSK_IMG_INFO * img_info, TSK_OFF_T offset, char *buf, size_t len)
 
     if (offset > img_info->size) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_IMG_READ_OFF;
-        snprintf(tsk_errstr, TSK_ERRSTR_L, "raw_read - %" PRIuOFF, offset);
+        tsk_error_set_errno(TSK_ERR_IMG_READ_OFF);
+        tsk_error_set_errstr("raw_read - %" PRIuOFF, offset);
         return -1;
     }
 
@@ -71,8 +71,8 @@ raw_read(TSK_IMG_INFO * img_info, TSK_OFF_T offset, char *buf, size_t len)
             if ((li.LowPart == INVALID_SET_FILE_POINTER) && (GetLastError()
                     != NO_ERROR)) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_IMG_SEEK;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_IMG_SEEK);
+                tsk_error_set_errstr(
                     "raw_read - %" PRIuOFF, offset);
                 return -1;
             }
@@ -82,8 +82,8 @@ raw_read(TSK_IMG_INFO * img_info, TSK_OFF_T offset, char *buf, size_t len)
         if (FALSE == ReadFile(raw_info->fd, buf, (DWORD) len,
                 &nread, NULL)) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_IMG_READ;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_IMG_READ);
+            tsk_error_set_errstr(
                 "raw_read - offset: %" PRIuOFF " - len: %zu", offset, len);
             return -1;
         }
@@ -93,8 +93,8 @@ raw_read(TSK_IMG_INFO * img_info, TSK_OFF_T offset, char *buf, size_t len)
     if (raw_info->seek_pos != offset) {
         if (lseek(raw_info->fd, offset, SEEK_SET) != offset) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_IMG_SEEK;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_IMG_SEEK);
+            tsk_error_set_errstr(
                 "raw_read - %" PRIuOFF " - %s", offset, strerror(errno));
             return -1;
         }
@@ -104,8 +104,8 @@ raw_read(TSK_IMG_INFO * img_info, TSK_OFF_T offset, char *buf, size_t len)
     cnt = read(raw_info->fd, buf, len);
     if (cnt < 0) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_IMG_READ;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_IMG_READ);
+        tsk_error_set_errstr(
             "raw_read - offset: %" PRIuOFF " - len: %" PRIuSIZE " - %s",
             offset, len, strerror(errno));
         return -1;
@@ -134,13 +134,13 @@ raw_close(TSK_IMG_INFO * img_info)
 #else
     close(raw_info->fd);
 #endif
-    free(raw_info);
+    tsk_img_free(raw_info);
 }
 
 
 /**
  * \internal
- * Open the file as a raw image.  
+ * Open the file as a raw image.
  * @param image Path to disk image to open.
  * @param a_ssize Size of device sector in bytes (or 0 for default)
  * @returns NULL on error.
@@ -154,7 +154,7 @@ raw_open(const TSK_TCHAR * image, unsigned int a_ssize)
     int is_winobj = 0;
 
     if ((raw_info =
-            (IMG_RAW_INFO *) tsk_malloc(sizeof(IMG_RAW_INFO))) == NULL)
+            (IMG_RAW_INFO *) tsk_img_malloc(sizeof(IMG_RAW_INFO))) == NULL)
         return NULL;
 
     img_info = (TSK_IMG_INFO *) raw_info;
@@ -181,8 +181,8 @@ raw_open(const TSK_TCHAR * image, unsigned int a_ssize)
         /* Exit if we are given a directory */
         if (TSTAT(image, &stat_buf) < 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_IMG_STAT;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_IMG_STAT);
+            tsk_error_set_errstr(
                 "raw_open: %s", strerror(errno));
             return NULL;
         }
@@ -192,8 +192,8 @@ raw_open(const TSK_TCHAR * image, unsigned int a_ssize)
                     _TSK_T("raw_open: image %s is a directory\n"), image);
 
             tsk_error_reset();
-            tsk_errno = TSK_ERR_IMG_MAGIC;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_IMG_MAGIC);
+            tsk_error_set_errstr(
                 "raw_open: path is for a directory");
             return NULL;
         }
@@ -221,25 +221,25 @@ raw_open(const TSK_TCHAR * image, unsigned int a_ssize)
 
             if (raw_info->fd == INVALID_HANDLE_VALUE) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_IMG_OPEN;
+                tsk_error_set_errno(TSK_ERR_IMG_OPEN);
                 // print string of commonly found errors
                 if (GetLastError() == ERROR_ACCESS_DENIED) {
-                    snprintf(tsk_errstr, TSK_ERRSTR_L,
+                    tsk_error_set_errstr(
                         "raw_open file: %" PRIttocTSK " (Access Denied)",
                         image);
                 }
                 else if (GetLastError() == ERROR_SHARING_VIOLATION) {
-                    snprintf(tsk_errstr, TSK_ERRSTR_L,
+                    tsk_error_set_errstr(
                         "raw_open file: %" PRIttocTSK
                         " (Sharing Violation)", image);
                 }
                 else if (GetLastError() == ERROR_FILE_NOT_FOUND) {
-                    snprintf(tsk_errstr, TSK_ERRSTR_L,
+                    tsk_error_set_errstr(
                         "raw_open file: %" PRIttocTSK " (File not found)",
                         image);
                 }
                 else {
-                    snprintf(tsk_errstr, TSK_ERRSTR_L,
+                    tsk_error_set_errstr(
                         "raw_open file: %" PRIttocTSK " (%d)", image,
                         (int) GetLastError());
                 }
@@ -254,8 +254,8 @@ raw_open(const TSK_TCHAR * image, unsigned int a_ssize)
             dwLo = GetFileSize(raw_info->fd, &dwHi);
             if (dwLo == 0xffffffff) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_IMG_OPEN;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_IMG_OPEN);
+                tsk_error_set_errstr(
                     "raw_open file: %" PRIttocTSK " GetFileSize: %d",
                     image, (int) GetLastError());
                 return NULL;
@@ -271,8 +271,8 @@ raw_open(const TSK_TCHAR * image, unsigned int a_ssize)
                     NULL, 0, &pdg, sizeof(pdg), &junk,
                     (LPOVERLAPPED) NULL)) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_IMG_OPEN;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_IMG_OPEN);
+                tsk_error_set_errstr(
                     "raw_open file: %" PRIttocTSK
                     " DeviceIoControl: %d", image, (int) GetLastError());
                 return NULL;
@@ -288,8 +288,8 @@ raw_open(const TSK_TCHAR * image, unsigned int a_ssize)
 #else
     if ((raw_info->fd = open(image, O_RDONLY | O_BINARY)) < 0) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_IMG_OPEN;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_IMG_OPEN);
+        tsk_error_set_errstr(
             "raw_open file: %" PRIttocTSK " msg: %s", image,
             strerror(errno));
         return NULL;
@@ -324,3 +324,33 @@ raw_open(const TSK_TCHAR * image, unsigned int a_ssize)
 
     return img_info;
 }
+
+/* tsk_img_malloc - init lock after tsk_malloc 
+ * This is for img module and all it's inheritances
+ */
+void *
+tsk_img_malloc(size_t a_len)
+{
+    TSK_IMG_INFO * imgInfo;
+    if ((imgInfo =
+            (TSK_IMG_INFO *) tsk_malloc(a_len)) == NULL)
+        return NULL;
+    //init lock
+    tsk_init_lock(&(imgInfo->cache_lock));
+
+    return (void*) imgInfo;
+}
+
+/* tsk_img_free - deinit lock  before free memory 
+ * This is for img module and all it's inheritances
+ */
+void
+tsk_img_free(void * a_ptr)
+{
+    TSK_IMG_INFO * imgInfo = (TSK_IMG_INFO *)a_ptr;
+
+    //deinit lock
+    tsk_deinit_lock(&(imgInfo->cache_lock));
+
+    free(imgInfo);
+}
diff --git a/tsk3/img/raw.h b/tsk3/img/raw.h
index 7d2512cc3..60bf9104d 100644
--- a/tsk3/img/raw.h
+++ b/tsk3/img/raw.h
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2005 Brian Carrier.  All rights reserved 
+ * Copyright (c) 2005-2011 Brian Carrier.  All rights reserved 
  *
  * This software is distributed under the Common Public License 1.0
  */
@@ -27,7 +27,7 @@ extern "C" {
 #else
         int fd;
 #endif
-        TSK_OFF_T seek_pos;
+        TSK_OFF_T seek_pos; // shared and protected by cache_lock in IMG_INFO
     } IMG_RAW_INFO;
 
 #ifdef __cplusplus
diff --git a/tsk3/img/split.c b/tsk3/img/split.c
index d76942866..2c2a31057 100644
--- a/tsk3/img/split.c
+++ b/tsk3/img/split.c
@@ -1,6 +1,6 @@
 /*
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All rights reserved
+ * Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All rights reserved
  * Copyright (c) 2005 Brian Carrier.  All rights reserved
  *
  * This software is distributed under the Common Public License 1.0
@@ -63,8 +63,8 @@ split_read_segment(IMG_SPLIT_INFO * split_info, int idx, char *buf,
                     FILE_SHARE_READ, NULL, OPEN_EXISTING, 0,
                     NULL)) == INVALID_HANDLE_VALUE) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_IMG_OPEN;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_IMG_OPEN);
+            tsk_error_set_errstr(
                 "split_read file: %" PRIttocTSK " msg: %d",
                 split_info->images[idx], (int) GetLastError());
             return -1;
@@ -73,8 +73,8 @@ split_read_segment(IMG_SPLIT_INFO * split_info, int idx, char *buf,
         if ((cimg->fd =
                 open(split_info->images[idx], O_RDONLY | O_BINARY)) < 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_IMG_OPEN;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_IMG_OPEN);
+            tsk_error_set_errstr(
                 "split_read file: %" PRIttocTSK " msg: %s",
                 split_info->images[idx], strerror(errno));
             return -1;
@@ -104,8 +104,8 @@ split_read_segment(IMG_SPLIT_INFO * split_info, int idx, char *buf,
             if ((li.LowPart == INVALID_SET_FILE_POINTER) &&
                 (GetLastError() != NO_ERROR)) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_IMG_SEEK;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_IMG_SEEK);
+                tsk_error_set_errstr(
                     "split_read - %" PRIuOFF, rel_offset);
                 return -1;
             }
@@ -114,8 +114,8 @@ split_read_segment(IMG_SPLIT_INFO * split_info, int idx, char *buf,
 
         if (FALSE == ReadFile(cimg->fd, buf, (DWORD) len, &nread, NULL)) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_IMG_READ;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_IMG_READ);
+            tsk_error_set_errstr(
                 "split_read - offset: %" PRIuOFF " - len: %" PRIuSIZE "",
                 rel_offset, len);
             return -1;
@@ -126,8 +126,8 @@ split_read_segment(IMG_SPLIT_INFO * split_info, int idx, char *buf,
     if (cimg->seek_pos != rel_offset) {
         if (lseek(cimg->fd, rel_offset, SEEK_SET) != rel_offset) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_IMG_SEEK;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_IMG_SEEK);
+            tsk_error_set_errstr(
                 "split_read - %s - %" PRIuOFF " - %s",
                 split_info->images[idx], rel_offset, strerror(errno));
             return -1;
@@ -138,8 +138,8 @@ split_read_segment(IMG_SPLIT_INFO * split_info, int idx, char *buf,
     cnt = read(cimg->fd, buf, len);
     if (cnt < 0) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_IMG_READ;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_IMG_READ);
+        tsk_error_set_errstr(
             "split_read - offset: %" PRIuOFF
             " - len: %" PRIuSIZE " - %s", rel_offset, len,
             strerror(errno));
@@ -156,6 +156,8 @@ split_read_segment(IMG_SPLIT_INFO * split_info, int idx, char *buf,
  * Read data from a split disk image.  The offset to start reading from is 
  * equal to the volume offset plus the read offset.
  *
+ * Note: The routine -assumes- we are under a lock on &(img_info->cache_lock))
+ *
  * @param img_info Disk image to read from
  * @param offset Byte offset in image to start reading from
  * @param buf [out] Buffer to write data to
@@ -176,8 +178,8 @@ split_read(TSK_IMG_INFO * img_info, TSK_OFF_T offset, char *buf,
 
     if (offset > img_info->size) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_IMG_READ_OFF;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_IMG_READ_OFF);
+        tsk_error_set_errstr(
             "split_read - %" PRIuOFF, offset);
         return -1;
     }
@@ -215,11 +217,12 @@ split_read(TSK_IMG_INFO * img_info, TSK_OFF_T offset, char *buf,
             cnt =
                 split_read_segment(split_info, i, buf, read_len,
                 rel_offset);
-            if (cnt < 0)
+            if (cnt < 0){
                 return -1;
-
-            if ((TSK_OFF_T) cnt != read_len)
+            }
+            if ((TSK_OFF_T) cnt != read_len){
                 return cnt;
+            }
 
             /* Go to the next image(s) */
             if (((TSK_OFF_T) cnt == read_len) && (read_len != len)) {
@@ -248,24 +251,25 @@ split_read(TSK_IMG_INFO * img_info, TSK_OFF_T offset, char *buf,
                     cnt2 =
                         split_read_segment(split_info, i, &buf[cnt],
                         read_len, 0);
-                    if (cnt2 < 0)
+                    if (cnt2 < 0){
                         return -1;
+                    }
                     cnt += cnt2;
 
-                    if ((TSK_OFF_T) cnt2 != read_len)
+                    if ((TSK_OFF_T) cnt2 != read_len){
                         return cnt;
+                    }
 
                     len -= cnt2;
                 }
             }
-
             return cnt;
         }
     }
 
     tsk_error_reset();
-    tsk_errno = TSK_ERR_IMG_READ_OFF;
-    snprintf(tsk_errstr, TSK_ERRSTR_L,
+    tsk_error_set_errno(TSK_ERR_IMG_READ_OFF);
+    tsk_error_set_errstr(
         "split_read - %" PRIuOFF " - %s", offset, strerror(errno));
     return -1;
 }
@@ -320,6 +324,7 @@ split_close(TSK_IMG_INFO * img_info)
             close(split_info->cache[i].fd);
 #endif
     }
+    free(split_info->max_off);
     free(split_info->cptr);
     free(split_info);
 }
@@ -344,7 +349,7 @@ split_open(int num_img, const TSK_TCHAR * const images[],
     int i;
 
     if ((split_info =
-            (IMG_SPLIT_INFO *) tsk_malloc(sizeof(IMG_SPLIT_INFO))) == NULL)
+            (IMG_SPLIT_INFO *) tsk_img_malloc(sizeof(IMG_SPLIT_INFO))) == NULL)
         return NULL;
 
     img_info = (TSK_IMG_INFO *) split_info;
@@ -361,7 +366,7 @@ split_open(int num_img, const TSK_TCHAR * const images[],
     /* Open the files */
     if ((split_info->cptr =
             (int *) tsk_malloc(num_img * sizeof(int))) == NULL) {
-        free(split_info);
+        tsk_img_free(split_info);
         return NULL;
     }
 
@@ -373,7 +378,7 @@ split_open(int num_img, const TSK_TCHAR * const images[],
         (TSK_OFF_T *) tsk_malloc(num_img * sizeof(TSK_OFF_T));
     if (split_info->max_off == NULL) {
         free(split_info->cptr);
-        free(split_info);
+        tsk_img_free(split_info);
         return NULL;
     }
     img_info->size = 0;
@@ -391,13 +396,13 @@ split_open(int num_img, const TSK_TCHAR * const images[],
         split_info->cptr[i] = -1;
         if (TSTAT(images[i], &sb) < 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_IMG_STAT;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_IMG_STAT);
+            tsk_error_set_errstr(
                 "split_open - %" PRIttocTSK " - %s", images[i],
                 strerror(errno));
             free(split_info->max_off);
             free(split_info->cptr);
-            free(split_info);
+            tsk_img_free(split_info);
             return NULL;
         }
         else if ((sb.st_mode & S_IFMT) == S_IFDIR) {
@@ -407,9 +412,12 @@ split_open(int num_img, const TSK_TCHAR * const images[],
                     images[i]);
 
             tsk_error_reset();
-            tsk_errno = TSK_ERR_IMG_MAGIC;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_IMG_MAGIC);
+            tsk_error_set_errstr(
                 "split_open: Image is a directory");
+            free(split_info->max_off);
+            free(split_info->cptr);
+            tsk_img_free(split_info);
             return NULL;
         }
 
diff --git a/tsk3/img/split.h b/tsk3/img/split.h
index e189af69e..c9d0af23e 100644
--- a/tsk3/img/split.h
+++ b/tsk3/img/split.h
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2005-2008 Brian Carrier.  All rights reserved 
+ * Copyright (c) 2005-2011 Brian Carrier.  All rights reserved 
  *
  * This software is distributed under the Common Public License 1.0
  */
@@ -36,10 +36,12 @@ extern "C" {
     typedef struct {
         TSK_IMG_INFO img_info;
         int num_img;
-        const TSK_TCHAR *const *images;
+
+        // the following are protected by cache_lock in IMG_INFO
+        const TSK_TCHAR *const *images;  
         TSK_OFF_T *max_off;
-        int *cptr;              /* exists for each image - points to entry in cache */
-        IMG_SPLIT_CACHE cache[SPLIT_CACHE];     /* small number of fds for open images */
+        int *cptr;              /* exists for each image - points to entry in cache */ 
+        IMG_SPLIT_CACHE cache[SPLIT_CACHE];     /* small number of fds for open images */ 
         int next_slot;
     } IMG_SPLIT_INFO;
 
diff --git a/tsk3/img/tsk_img.h b/tsk3/img/tsk_img.h
index 5bdb09fa2..4e0471fc6 100644
--- a/tsk3/img/tsk_img.h
+++ b/tsk3/img/tsk_img.h
@@ -2,29 +2,28 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2005-2008 Brian Carrier.  All rights reserved 
+ * Copyright (c) 2005-2011 Brian Carrier.  All rights reserved
  *
  * This software is distributed under the Common Public License 1.0
  */
 #ifndef _TSK_IMG_H
 #define _TSK_IMG_H
 
-
 /**
  * \file tsk_img.h
- * Contains the external library definitions for the disk image functions.  
- * Note that this file is not meant to be directly included.  
+ * Contains the external library definitions for the disk image functions.
+ * Note that this file is not meant to be directly included.
  * It is included by both libtsk.h and tsk_img_i.h.
  */
 
 /**
- * \defgroup imglib Disk Image Functions
+ * \defgroup imglib C Disk Image Functions
+ * \defgroup imglib_cpp C++ Disk Image Classes
  */
 
 #ifdef __cplusplus
 extern "C" {
 #endif
-
     /**
      * \ingroup imglib
      * Macro that takes a image type and returns 1 if the type
@@ -48,7 +47,7 @@ extern "C" {
     ((((t) & TSK_IMG_TYPE_EWF_EWF))?1:0)
 
 
-    /** 
+    /**
      * Flag values for the disk image format type.  Each type has a
      * bit associated with it.  There are TSK_IMG_TYPE_ISXXX macros
      * to determine the broad group of the type (raw vs aff etc.)
@@ -83,12 +82,13 @@ extern "C" {
         TSK_OFF_T size;         ///< Total size of image in bytes
         unsigned int sector_size;       ///< sector size of device in bytes (typically 512)
 
-        char cache[TSK_IMG_INFO_CACHE_NUM][TSK_IMG_INFO_CACHE_LEN];     ///< read cache
-        TSK_OFF_T cache_off[TSK_IMG_INFO_CACHE_NUM];    ///< starting byte offset of corresponding cache entry
-        int cache_age[TSK_IMG_INFO_CACHE_NUM];  ///< "Age" of corresponding cache entry, higher means more recently used
-        size_t cache_len[TSK_IMG_INFO_CACHE_NUM];       ///< Length of cache entry used (0 if never used)
+        tsk_lock_t cache_lock; ///< Lock for cache and associated values
+        char cache[TSK_IMG_INFO_CACHE_NUM][TSK_IMG_INFO_CACHE_LEN];     ///< read cache (r/w shared - lock) 
+        TSK_OFF_T cache_off[TSK_IMG_INFO_CACHE_NUM];    ///< starting byte offset of corresponding cache entry (r/w shared - lock) 
+        int cache_age[TSK_IMG_INFO_CACHE_NUM];  ///< "Age" of corresponding cache entry, higher means more recently used (r/w shared - lock) 
+        size_t cache_len[TSK_IMG_INFO_CACHE_NUM];       ///< Length of cache entry used (0 if never used) (r/w shared - lock) 
 
-         ssize_t(*read) (TSK_IMG_INFO * img, TSK_OFF_T off, char *buf, size_t len);     ///< \internal External progs should call tsk_img_read() 
+        ssize_t(*read) (TSK_IMG_INFO * img, TSK_OFF_T off, char *buf, size_t len);     ///< \internal External progs should call tsk_img_read()
         void (*close) (TSK_IMG_INFO *); ///< \internal Progs should call tsk_img_close()
         void (*imgstat) (TSK_IMG_INFO *, FILE *);       ///< Pointer to file type specific function
     };
@@ -97,14 +97,13 @@ extern "C" {
     extern TSK_IMG_INFO *tsk_img_open_sing(const TSK_TCHAR * a_image,
         TSK_IMG_TYPE_ENUM type, unsigned int a_ssize);
     extern TSK_IMG_INFO *tsk_img_open(int,
-        const TSK_TCHAR * const images[], TSK_IMG_TYPE_ENUM, 
+        const TSK_TCHAR * const images[], TSK_IMG_TYPE_ENUM,
         unsigned int a_ssize);
     extern TSK_IMG_INFO *tsk_img_open_utf8_sing(const char *a_image,
         TSK_IMG_TYPE_ENUM type, unsigned int a_ssize);
     extern TSK_IMG_INFO *tsk_img_open_utf8(int num_img,
-        const char *const images[], TSK_IMG_TYPE_ENUM type, 
+        const char *const images[], TSK_IMG_TYPE_ENUM type,
         unsigned int a_ssize);
-
     extern void tsk_img_close(TSK_IMG_INFO *);
 
     // read functions
@@ -121,4 +120,229 @@ extern "C" {
 #ifdef __cplusplus
 }
 #endif
+
+#ifdef __cplusplus
+/**
+ \ingroup imglib_cpp
+* Stores information about an image that is open and being analyzed.
+* To use this object, open() should be called first.  Otherwise, the get()
+* methods will return undefined values.
+*/
+class TskImgInfo {
+    friend class TskFsInfo;
+    friend class TskVsInfo;
+
+private:
+    TSK_IMG_INFO *m_imgInfo;
+    bool m_opened; // true if open() was called and we need to free it    
+    TskImgInfo(const TskImgInfo& rhs); 
+    TskImgInfo& operator=(const TskImgInfo& rhs);
+    
+public:
+    TskImgInfo() {
+        m_imgInfo = NULL;
+        m_opened = false;
+    };
+
+    ~TskImgInfo() {
+        if (m_imgInfo == NULL) {
+            return;
+        }
+        m_imgInfo->close(m_imgInfo);
+    }; 
+
+    TskImgInfo(TSK_IMG_INFO * a_imgInfo) {
+        m_imgInfo = a_imgInfo;
+        m_opened = false;
+    };
+
+    /**
+    * Opens a single (non-split) disk image file so that it can be read.
+    * See tsk_img_open_sing() for more details.
+    *
+    * @param a_image The path to the image file
+    * @param a_type The disk image type (can be autodetection)
+    * @param a_ssize Size of device sector in bytes (or 0 for default)
+    *
+    * @return 1 on error and 0 on success
+    */
+    uint8_t open(const TSK_TCHAR * a_image, TSK_IMG_TYPE_ENUM a_type, unsigned int a_ssize) {
+        if ((m_imgInfo = tsk_img_open_sing(a_image, a_type, a_ssize)) !=NULL) {
+            m_opened = true;
+            return 0;
+        }
+        else{
+            return 1;
+        }
+    };
+
+    /**
+    * Opens one or more disk image files so that they can be read. e UTF8, then consider
+    * See tsk_img_open() for more details.
+    *
+    * @param a_num_img The number of images to open (will be > 1 for split images).
+    * @param a_images The path to the image files (the number of files must
+    * be equal to num_img and they must be in a sorted order)
+    * @param a_type The disk image type (can be autodetection)
+    * @param a_ssize Size of device sector in bytes (or 0 for default)
+    *
+    * @return 1 on error and 0 on success
+    */
+    uint8_t open(int a_num_img, const TSK_TCHAR * const a_images[],
+                 TSK_IMG_TYPE_ENUM a_type, unsigned int a_ssize) {
+            if ((m_imgInfo = tsk_img_open(a_num_img, a_images, a_type, a_ssize)) !=NULL) {
+                m_opened = true;
+                return 0;
+            }
+            else{
+                return 1;
+            }
+
+    };
+    
+#ifdef TSK_WIN32
+    /**
+    * Opens a single (non-split) disk image file so that it can be read.  This version
+    * always takes a UTF-8 encoding of the disk image.  See tsk_img_open_utf8_sing() for details.
+    *
+    * @param a_image The UTF-8 path to the image file
+    * @param a_type The disk image type (can be autodetection)
+    * @param a_ssize Size of device sector in bytes (or 0 for default)
+    *
+    * @return 1 on error and 0 on success
+    */
+    uint8_t open(const char *a_image, TSK_IMG_TYPE_ENUM a_type, unsigned int a_ssize) {
+        if ((m_imgInfo = tsk_img_open_utf8_sing(a_image, a_type, a_ssize)) !=NULL) {
+            m_opened = true;
+            return 0;
+        }
+        else {
+            return 1;
+        }
+
+    };
+    
+    /**
+    * Opens one or more disk image files so that they can be read.  This
+    * version always takes a UTF-8 encoding of the image files.  See tsk_img_open_utf8()
+    * for more details.
+    *
+    * @param a_num_img The number of images to open (will be > 1 for split images).
+    * @param a_images The path to the UTF-8 encoded image files (the number of files must
+    * be equal to a_num_img and they must be in a sorted order)
+    * @param a_type The disk image type (can be autodetection)
+    * @param a_ssize Size of device sector in bytes (or 0 for default)
+    *
+    * @return 1 on error and 0 on success
+    */
+    uint8_t open(int a_num_img, const char *const a_images[],
+                 TSK_IMG_TYPE_ENUM a_type, unsigned int a_ssize) {
+        if ((m_imgInfo = tsk_img_open_utf8(a_num_img, a_images, a_type, a_ssize)) !=NULL) {
+            m_opened = true;
+            return 0;
+        }
+        else{
+            return 1;
+        }
+
+    };
 #endif
+
+    /**
+    * Reads data from an open disk image
+    *
+    * @param a_off Byte offset to start reading from
+    * @param a_buf Buffer to read into
+    * @param a_len Number of bytes to read into buffer
+    * @returns number of bytes read or -1 on error
+    */
+   ssize_t read(TSK_OFF_T a_off, char *a_buf, size_t a_len) {
+       return tsk_img_read(m_imgInfo, a_off, a_buf, a_len);
+   };
+
+
+   /**
+    * returns the image format type.
+    * @returns image format type
+    */
+   TSK_IMG_TYPE_ENUM getType() const {
+       if (m_imgInfo != NULL)
+           return m_imgInfo->itype;
+       else
+           return (TSK_IMG_TYPE_ENUM)0;
+   };
+
+    /**
+    * Returns the size of the image.
+    * @returns total size of image in bytes
+    */
+   TSK_OFF_T getSize() const {
+       if (m_imgInfo != NULL)
+           return m_imgInfo->size;
+       else
+           return 0;
+   };
+
+    /**
+    * Returns the sector size of the disk
+    * @returns sector size of original device in bytes
+    */
+   unsigned int getSectorSize() const {
+       if (m_imgInfo != NULL)
+           return m_imgInfo->sector_size;
+       else
+           return 0;
+   };
+
+
+    /**
+    * Parses a string that specifies an image format to determine the
+    * associated type ID.  This is used by the TSK command line tools to
+    * parse the type given on the command line.
+    *
+    * @param a_str String of image format type
+    * @return ID of image type
+    */
+    static TSK_IMG_TYPE_ENUM typeToId(const TSK_TCHAR * a_str) {
+        return tsk_img_type_toid(a_str);
+    };
+
+    /**
+    * Returns the name of an image format type, given its type ID.
+    * @param a_type ID of image type
+    * @returns Pointer to string of the name.
+    */
+    static const char *typeToName(TSK_IMG_TYPE_ENUM a_type) {
+        return tsk_img_type_toname(a_type);
+    };
+
+    /**
+    * Returns the description of an image format type, given its type ID.
+    * @param a_type ID of image type
+    * @returns Pointer to string of the description
+    */
+    static const char *typeToDesc(TSK_IMG_TYPE_ENUM a_type) {
+        return tsk_img_type_todesc(a_type);
+    };
+
+    /**
+    * Returns the supported file format types.
+    * @returns A bit in the return value is set to 1 if the type is supported.
+    */
+    static TSK_IMG_TYPE_ENUM typeSupported() {
+        return tsk_img_type_supported();
+    };
+
+    /**
+    * Prints the name and description of the supported image types to a handle.
+    * This is used by the TSK command line tools to print the supported types
+    * to the console.
+    * @param a_file Handle to print names and descriptions to.
+    */
+    static void typePrint(FILE * a_file) {
+        tsk_img_type_print(a_file); 
+    };
+};
+
+#endif //__cplusplus
+#endif //_TSK_IMG_H
diff --git a/tsk3/img/tsk_img_i.h b/tsk3/img/tsk_img_i.h
index e643c2e0f..724a2ffa6 100644
--- a/tsk3/img/tsk_img_i.h
+++ b/tsk3/img/tsk_img_i.h
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2005-2008 Brian Carrier.  All rights reserved 
+ * Copyright (c) 2005-2011 Brian Carrier.  All rights reserved 
  *
  * This software is distributed under the Common Public License 1.0
  */
@@ -31,5 +31,6 @@
 #ifndef O_BINARY
 #define O_BINARY 0
 #endif
-
+    extern void *tsk_img_malloc(size_t);
+    extern void tsk_img_free(void *);
 #endif
diff --git a/tsk3/tsk_config.h.in b/tsk3/tsk_config.h.in
index fa9af71fe..bf831deeb 100644
--- a/tsk3/tsk_config.h.in
+++ b/tsk3/tsk_config.h.in
@@ -1,4 +1,4 @@
-/* tsk/tsk_config.h.in.  Generated from configure.ac by autoheader.  */
+/* tsk3/tsk_config.h.in.  Generated from configure.ac by autoheader.  */
 
 /* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
    systems. This function is required for `alloca.c' support on those systems.
@@ -8,6 +8,9 @@
 /* Define to 1 if using `alloca.c'. */
 #undef C_ALLOCA
 
+/* Define to 1 if you have the <afflib/afflib.h> header file. */
+#undef HAVE_AFFLIB_AFFLIB_H
+
 /* Define to 1 if you have `alloca', as a function or macro. */
 #undef HAVE_ALLOCA
 
@@ -33,6 +36,9 @@
 /* Define to 1 if you have the `ewf' library (-lewf). */
 #undef HAVE_LIBEWF
 
+/* Define to 1 if you have the <libewf.h> header file. */
+#undef HAVE_LIBEWF_H
+
 /* Define to 1 if `lstat' has the bug that it succeeds when given the
    zero-length file name argument. */
 #undef HAVE_LSTAT_EMPTY_STRING_BUG
@@ -40,6 +46,13 @@
 /* Define to 1 if you have the <memory.h> header file. */
 #undef HAVE_MEMORY_H
 
+/* Define if you have POSIX threads libraries and header files. */
+#undef HAVE_PTHREAD
+
+/* Define to 1 if your system has a GNU libc compatible `realloc' function,
+   and to 0 otherwise. */
+#undef HAVE_REALLOC
+
 /* Define to 1 if stdbool.h conforms to C99. */
 #undef HAVE_STDBOOL_H
 
@@ -73,6 +86,9 @@
 /* Define to 1 if you have the <unistd.h> header file. */
 #undef HAVE_UNISTD_H
 
+/* Define to 1 if you have the <utime.h> header file. */
+#undef HAVE_UTIME_H
+
 /* Define to 1 if `utime(file, NULL)' sets file's timestamp to the present. */
 #undef HAVE_UTIME_NULL
 
@@ -86,6 +102,10 @@
    slash. */
 #undef LSTAT_FOLLOWS_SLASHED_SYMLINK
 
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#undef LT_OBJDIR
+
 /* Name of package */
 #undef PACKAGE
 
@@ -104,6 +124,10 @@
 /* Define to the version of this package. */
 #undef PACKAGE_VERSION
 
+/* Define to necessary symbol if this constant uses a non-standard name on
+   your system. */
+#undef PTHREAD_CREATE_JOINABLE
+
 /* Define to the type of arg 1 for `select'. */
 #undef SELECT_TYPE_ARG1
 
@@ -115,7 +139,7 @@
 
 /* If using the C implementation of alloca, define if you know the
    direction of stack growth for your system; otherwise it will be
-   automatically deduced at run-time.
+   automatically deduced at runtime.
 	STACK_DIRECTION > 0 => grows toward higher addresses
 	STACK_DIRECTION < 0 => grows toward lower addresses
 	STACK_DIRECTION = 0 => direction of growth unknown */
@@ -145,10 +169,13 @@
 /* Define to `int' if <sys/types.h> does not define. */
 #undef mode_t
 
-/* Define to `long' if <sys/types.h> does not define. */
+/* Define to `long int' if <sys/types.h> does not define. */
 #undef off_t
 
-/* Define to `unsigned' if <sys/types.h> does not define. */
+/* Define to rpl_realloc if the replacement function should be used. */
+#undef realloc
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
 #undef size_t
 
 /* Define to `int' if <sys/types.h> doesn't define. */
diff --git a/tsk3/vs/bsd.c b/tsk3/vs/bsd.c
index 8a60c46d2..28a19d9cf 100644
--- a/tsk3/vs/bsd.c
+++ b/tsk3/vs/bsd.c
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All rights reserved
+ * Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All rights reserved
  * Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
  *
  * This software is distributed under the Common Public License 1.0
@@ -81,8 +81,8 @@ bsd_get_desc(uint8_t fstype)
     return str;
 }
 
-/* 
- * Process the partition table at the sector address 
+/*
+ * Process the partition table at the sector address
  *
  * Return 1 on error and 0 if no error
  */
@@ -111,10 +111,9 @@ bsd_load_table(TSK_VS_INFO * a_vs)
     if (cnt != a_vs->block_size) {
         if (cnt >= 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_VS_READ;
+            tsk_error_set_errno(TSK_ERR_VS_READ);
         }
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
-            "BSD Disk Label in Sector: %" PRIuDADDR, laddr);
+        tsk_error_set_errstr2("BSD Disk Label in Sector: %" PRIuDADDR, laddr);
         free(sect_buf);
         return 1;
     }
@@ -122,9 +121,8 @@ bsd_load_table(TSK_VS_INFO * a_vs)
     /* Check the magic  */
     if (tsk_vs_guessu32(a_vs, dlabel->magic, BSD_MAGIC)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_VS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
-            "BSD partition table (magic #1) (Sector: %"
+        tsk_error_set_errno(TSK_ERR_VS_MAGIC);
+        tsk_error_set_errstr("BSD partition table (magic #1) (Sector: %"
             PRIuDADDR ") %" PRIx32, laddr, tsk_getu32(a_vs->endian,
                 dlabel->magic));
         free(sect_buf);
@@ -134,8 +132,8 @@ bsd_load_table(TSK_VS_INFO * a_vs)
     /* Check the second magic value */
     if (tsk_getu32(a_vs->endian, dlabel->magic2) != BSD_MAGIC) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_VS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_VS_MAGIC);
+        tsk_error_set_errstr(
             "BSD disk label (magic #2) (Sector: %"
             PRIuDADDR ")  %" PRIx32, laddr, tsk_getu32(a_vs->endian,
                 dlabel->magic2));
@@ -177,8 +175,8 @@ bsd_load_table(TSK_VS_INFO * a_vs)
         // make sure the first couple are in the image bounds
         if ((idx < 2) && (part_start > max_addr)) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_VS_BLK_NUM;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_VS_BLK_NUM);
+            tsk_error_set_errstr(
                 "bsd_load_table: Starting sector too large for image");
             free(sect_buf);
             return 1;
@@ -210,7 +208,7 @@ bsd_close(TSK_VS_INFO * a_vs)
  * analyze the image in img_info and process it as BSD
  * Initialize the TSK_VS_INFO structure
  *
- * Return TSK_VS_INFO or NULL if not BSD or an error 
+ * Return TSK_VS_INFO or NULL if not BSD or an error
  */
 TSK_VS_INFO *
 tsk_vs_bsd_open(TSK_IMG_INFO * img_info, TSK_DADDR_T offset)
diff --git a/tsk3/vs/dos.c b/tsk3/vs/dos.c
index c1c26c9c0..9a113a1a9 100644
--- a/tsk3/vs/dos.c
+++ b/tsk3/vs/dos.c
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All rights reserved
+ * Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All rights reserved
  * Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
  *
  * This software is distributed under the Common Public License 1.0
@@ -20,12 +20,12 @@
 #define dos_is_ext(x)	\
 	((((x) == 0x05) || ((x) == 0x0F) || ((x) == 0x85)) ? 1 : 0)
 
-/* 
+/*
  * dos_get_desc
  *
  * Return a buffer with a string description of the partition type
- * 
- * From: http://www.win.tue.nl/~aeb/partitions/partition_types-1.html 
+ *
+ * From: http://www.win.tue.nl/~aeb/partitions/partition_types-1.html
  */
 static char *
 dos_get_desc(uint8_t ptype)
@@ -257,7 +257,7 @@ dos_get_desc(uint8_t ptype)
     case 0x38:
         snprintf(str, DESC_LEN, "THEOS v3.2 2gb (0x38)");
         break;
-        /*      
+        /*
            case 0x39:
            snprintf(str, DESC_LEN, "Plan 9 (0x39)");
            break;
@@ -284,7 +284,7 @@ dos_get_desc(uint8_t ptype)
         snprintf(str, DESC_LEN,
             "Linux/MINIX (Sharing Disk with DR-DOS) (0x41)");
         break;
-        /*      
+        /*
            case 0x41:
            snprintf(str, DESC_LEN, "Personal RISC Boot (0x41)");
            break;
@@ -654,16 +654,16 @@ dos_get_desc(uint8_t ptype)
     return str;
 }
 
-/* 
+/*
  * Load an extended partition table into the structure in TSK_VS_INFO.
  *
  * sect_cur: The sector where the extended table is located
  * sect_ext_base: The sector of the primary extended table (this does
  *   not change for recursive calls)
- * table: a counter that identifies the table depth 
+ * table: a counter that identifies the table depth
  *   (increases by 1 for each recursive call)
- * 
- * For the primary extended table, sect_cur == sect_ext_base 
+ *
+ * For the primary extended table, sect_cur == sect_ext_base
  *
  * Return 1 on error and 0 on success
  *
@@ -694,9 +694,9 @@ dos_load_ext_table(TSK_VS_INFO * vs, TSK_DADDR_T sect_cur,
     if (cnt != vs->block_size) {
         if (cnt >= 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_VS_READ;
+            tsk_error_set_errno(TSK_ERR_VS_READ);
         }
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
+        tsk_error_set_errstr2(
             "Extended DOS table sector %" PRIuDADDR, sect_cur);
         free(sect_buf);
         return 1;
@@ -705,8 +705,8 @@ dos_load_ext_table(TSK_VS_INFO * vs, TSK_DADDR_T sect_cur,
     /* Sanity Check */
     if (tsk_getu16(vs->endian, sect->magic) != DOS_MAGIC) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_VS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_VS_MAGIC);
+        tsk_error_set_errstr(
             "Extended DOS partition table in sector %"
             PRIuDADDR, sect_cur);
         free(sect_buf);
@@ -727,7 +727,7 @@ dos_load_ext_table(TSK_VS_INFO * vs, TSK_DADDR_T sect_cur,
         return 1;
     }
 
-    /* Cycle through the four partitions in the table 
+    /* Cycle through the four partitions in the table
      *
      * When another extended partition is found, it is processed
      * inside of the loop
@@ -749,7 +749,7 @@ dos_load_ext_table(TSK_VS_INFO * vs, TSK_DADDR_T sect_cur,
         if (part_size == 0)
             continue;
 
-        /* partitions are addressed differently 
+        /* partitions are addressed differently
          * in extended partitions */
         if (dos_is_ext(part->ptype)) {
 
@@ -781,11 +781,11 @@ dos_load_ext_table(TSK_VS_INFO * vs, TSK_DADDR_T sect_cur,
         }
 
         else {
-            /* part_start is added to the start of the 
+            /* part_start is added to the start of the
              * current partition for the actual
              * starting location */
 
-            // we ignore the max_addr checks on extended partitions... 
+            // we ignore the max_addr checks on extended partitions...
 
             if (NULL == tsk_vs_part_add(vs,
                     (TSK_DADDR_T) (sect_cur + part_start),
@@ -802,7 +802,7 @@ dos_load_ext_table(TSK_VS_INFO * vs, TSK_DADDR_T sect_cur,
 }
 
 
-/* 
+/*
  * Load the primary partition table (MBR) into the internal
  * data structures in TSK_VS_INFO
  *
@@ -839,9 +839,9 @@ dos_load_prim_table(TSK_VS_INFO * vs, uint8_t test)
     if (cnt != vs->block_size) {
         if (cnt >= 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_VS_READ;
+            tsk_error_set_errno(TSK_ERR_VS_READ);
         }
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
+        tsk_error_set_errstr2(
             "Primary DOS table sector %" PRIuDADDR, taddr);
         free(sect_buf);
         return 1;
@@ -851,8 +851,8 @@ dos_load_prim_table(TSK_VS_INFO * vs, uint8_t test)
     /* Sanity Check */
     if (tsk_vs_guessu16(vs, sect->magic, DOS_MAGIC)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_VS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_VS_MAGIC);
+        tsk_error_set_errstr(
             "File is not a DOS partition (invalid primary magic) (Sector: %"
             PRIuDADDR ")", taddr);
         free(sect_buf);
@@ -860,8 +860,8 @@ dos_load_prim_table(TSK_VS_INFO * vs, uint8_t test)
     }
 
     /* Because FAT and NTFS use the same magic - check for a
-     * standard MS OEM name and sizes.  Not a great check, but we can't 
-     * really test the table entries.  
+     * standard MS OEM name and sizes.  Not a great check, but we can't
+     * really test the table entries.
      */
     if (test) {
         if (tsk_verbose)
@@ -870,8 +870,8 @@ dos_load_prim_table(TSK_VS_INFO * vs, uint8_t test)
 
         if (strncmp("MSDOS", sect->oemname, 5) == 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_VS_MAGIC;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_VS_MAGIC);
+            tsk_error_set_errstr(
                 "dos_load_prim_table: MSDOS OEM name exists");
             if (tsk_verbose)
                 tsk_fprintf(stderr,
@@ -881,8 +881,8 @@ dos_load_prim_table(TSK_VS_INFO * vs, uint8_t test)
         }
         else if (strncmp("MSWIN", sect->oemname, 5) == 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_VS_MAGIC;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_VS_MAGIC);
+            tsk_error_set_errstr(
                 "dos_load_prim_table: MSWIN OEM name exists");
             if (tsk_verbose)
                 tsk_fprintf(stderr,
@@ -892,8 +892,8 @@ dos_load_prim_table(TSK_VS_INFO * vs, uint8_t test)
         }
         else if (strncmp("NTFS", sect->oemname, 4) == 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_VS_MAGIC;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_VS_MAGIC);
+            tsk_error_set_errstr(
                 "dos_load_prim_table: NTFS OEM name exists");
             if (tsk_verbose)
                 tsk_fprintf(stderr,
@@ -903,8 +903,8 @@ dos_load_prim_table(TSK_VS_INFO * vs, uint8_t test)
         }
         else if (strncmp("FAT", sect->oemname, 4) == 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_VS_MAGIC;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_VS_MAGIC);
+            tsk_error_set_errstr(
                 "dos_load_prim_table: FAT OEM name exists");
             if (tsk_verbose)
                 tsk_fprintf(stderr,
@@ -946,8 +946,8 @@ dos_load_prim_table(TSK_VS_INFO * vs, uint8_t test)
         // make sure the first couple are in the image bounds
         if ((i < 2) && (part_start > max_addr)) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_VS_BLK_NUM;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_VS_BLK_NUM);
+            tsk_error_set_errstr(
                 "dos_load_prim_table: Starting sector too large for image");
             if (tsk_verbose)
                 tsk_fprintf(stderr,
@@ -960,8 +960,8 @@ dos_load_prim_table(TSK_VS_INFO * vs, uint8_t test)
 // I'm not sure if this is too strict ...
         else if ((part_start + part_size) > max_addr) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_VS_BLK_NUM
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_VS_BLK_NUM);
+                tsk_error_set_errstr(
                 "dos_load_prim_table: Partition ends after image");
             return 1;
         }
@@ -969,7 +969,7 @@ dos_load_prim_table(TSK_VS_INFO * vs, uint8_t test)
 
         added = 1;
 
-        /* Add the partition to the internal structure 
+        /* Add the partition to the internal structure
          * If it is an extended partition, process it now */
         if (dos_is_ext(part->ptype)) {
             if (NULL == tsk_vs_part_add(vs, (TSK_DADDR_T) part_start,
@@ -1000,8 +1000,8 @@ dos_load_prim_table(TSK_VS_INFO * vs, uint8_t test)
             tsk_fprintf(stderr, "dos_load_prim: No valid entries\n");
 
         tsk_error_reset();
-        tsk_errno = TSK_ERR_VS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_VS_MAGIC);
+        tsk_error_set_errstr(
             "dos_load_prim_table: No valid entries in primary table");
         return 1;
     }
@@ -1017,15 +1017,15 @@ dos_close(TSK_VS_INFO * vs)
 }
 
 
-/* 
+/*
  * Given the path to the file, open it and load the internal
  * partition table structure
  *
  * offset is the byte offset to the start of the volume system
  *
- * If test is 1 then additional tests are performed to make sure 
+ * If test is 1 then additional tests are performed to make sure
  * it isn't a FAT or NTFS file system. This is used when autodetection
- * is being used to detect the volume system type. 
+ * is being used to detect the volume system type.
  */
 TSK_VS_INFO *
 tsk_vs_dos_open(TSK_IMG_INFO * img_info, TSK_DADDR_T offset, uint8_t test)
diff --git a/tsk3/vs/gpt.c b/tsk3/vs/gpt.c
index b7e539deb..1b80dbb3d 100644
--- a/tsk3/vs/gpt.c
+++ b/tsk3/vs/gpt.c
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All rights reserved
+ * Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All rights reserved
  * Copyright (c) 2004-2005 Brian Carrier.  All rights reserved
  *
  * This software is distributed under the Common Public License 1.0
@@ -49,9 +49,9 @@ gpt_load_table(TSK_VS_INFO * vs)
     if (cnt != vs->block_size) {
         if (cnt >= 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_VS_READ;
+            tsk_error_set_errno(TSK_ERR_VS_READ);
         }
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
+        tsk_error_set_errstr2(
             "Error reading DOS safety partition table in Sector: %"
             PRIuDADDR, taddr);
         free(sect_buf);
@@ -61,8 +61,8 @@ gpt_load_table(TSK_VS_INFO * vs)
     /* Sanity Check */
     if (tsk_vs_guessu16(vs, dos_part->magic, DOS_MAGIC)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_VS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_VS_MAGIC);
+        tsk_error_set_errstr(
             "Missing DOS safety partition (invalid magic) (Sector: %"
             PRIuDADDR ")", taddr);
         free(sect_buf);
@@ -71,8 +71,8 @@ gpt_load_table(TSK_VS_INFO * vs)
 
     if (dos_part->ptable[0].ptype != GPT_DOS_TYPE) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_VS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_VS_MAGIC);
+        tsk_error_set_errstr(
             "Missing DOS safety partition (invalid type in table: %d)",
             dos_part->ptable[0].ptype);
         free(sect_buf);
@@ -99,9 +99,9 @@ gpt_load_table(TSK_VS_INFO * vs)
     if (cnt != vs->block_size) {
         if (cnt >= 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_VS_READ;
+            tsk_error_set_errno(TSK_ERR_VS_READ);
         }
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
+        tsk_error_set_errstr2(
             "GPT Header structure in Sector: %" PRIuDADDR, taddr + 1);
         free(sect_buf);
         return 1;
@@ -110,8 +110,8 @@ gpt_load_table(TSK_VS_INFO * vs)
 
     if (tsk_getu64(vs->endian, &head->signature) != GPT_HEAD_SIG) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_VS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_VS_MAGIC);
+        tsk_error_set_errstr(
             "GPT Header: %" PRIx64, tsk_getu64(vs->endian,
                 &head->signature));
         free(sect_buf);
@@ -136,8 +136,8 @@ gpt_load_table(TSK_VS_INFO * vs)
     ent_size = tsk_getu32(vs->endian, &head->tab_size_b);
     if (ent_size < sizeof(gpt_entry)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_VS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_VS_MAGIC);
+        tsk_error_set_errstr(
             "Header reports partition entry size of %" PRIu32
             " and not %" PRIuSIZE "", ent_size, sizeof(gpt_entry));
         free(sect_buf);
@@ -177,9 +177,9 @@ gpt_load_table(TSK_VS_INFO * vs)
         if (cnt != vs->block_size) {
             if (cnt >= 0) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_VS_READ;
+                tsk_error_set_errno(TSK_ERR_VS_READ);
             }
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
+            tsk_error_set_errstr2(
                 "Error reading GPT partition table sector : %"
                 PRIuDADDR, tsk_getu64(vs->endian,
                     &head->tab_start_lba) + a);
@@ -215,8 +215,8 @@ gpt_load_table(TSK_VS_INFO * vs)
             if ((i < 2)
                 && (tsk_getu64(vs->endian, ent->start_lba) > max_addr)) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_VS_BLK_NUM;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_VS_BLK_NUM);
+                tsk_error_set_errstr(
                     "gpt_load_table: Starting sector too large for image");
                 free(sect_buf);
                 free(ent_buf);
diff --git a/tsk3/vs/mac.c b/tsk3/vs/mac.c
index 161a9c280..9620085c9 100644
--- a/tsk3/vs/mac.c
+++ b/tsk3/vs/mac.c
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All rights reserved
+ * Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All rights reserved
  * Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
  *
  * This software is distributed under the Common Public License 1.0
@@ -60,9 +60,9 @@ mac_load_table(TSK_VS_INFO * vs)
         if (cnt != vs->block_size) {
             if (cnt >= 0) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_VS_READ;
+                tsk_error_set_errno(TSK_ERR_VS_READ);
             }
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
+            tsk_error_set_errstr2(
                 "MAC Partition entry %" PRIuDADDR, taddr + idx);
             free(part_buf);
             return 1;
@@ -74,8 +74,8 @@ mac_load_table(TSK_VS_INFO * vs)
             /* Set the endian ordering the first time around */
             if (tsk_vs_guessu16(vs, part->magic, MAC_MAGIC)) {
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_VS_MAGIC;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_VS_MAGIC);
+                tsk_error_set_errstr(
                     "Mac partition table entry (Sector: %"
                     PRIuDADDR ") %" PRIx16,
                     (taddr + idx), tsk_getu16(vs->endian, part->magic));
@@ -91,8 +91,8 @@ mac_load_table(TSK_VS_INFO * vs)
         }
         else if (tsk_getu16(vs->endian, part->magic) != MAC_MAGIC) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_VS_MAGIC;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_VS_MAGIC);
+            tsk_error_set_errstr(
                 "Mac partition table entry (Sector: %"
                 PRIuDADDR ") %" PRIx16, (taddr + idx),
                 tsk_getu16(vs->endian, part->magic));
@@ -120,8 +120,8 @@ mac_load_table(TSK_VS_INFO * vs)
         // make sure the first couple are within the bounds of the image.
         if ((idx < 2) && (part_start > max_addr)) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_VS_BLK_NUM;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_VS_BLK_NUM);
+            tsk_error_set_errstr(
                 "mac_load_table: Starting sector too large for image");
             if (tsk_verbose)
                 tsk_fprintf(stderr,
diff --git a/tsk3/vs/mm_io.c b/tsk3/vs/mm_io.c
index 499d69a0d..521ba780c 100644
--- a/tsk3/vs/mm_io.c
+++ b/tsk3/vs/mm_io.c
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All rights reserved
+ * Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All rights reserved
  * Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
  *
  * This software is distributed under the Common Public License 1.0
@@ -33,8 +33,8 @@ tsk_vs_read_block(TSK_VS_INFO * a_vs, TSK_DADDR_T a_addr, char *a_buf,
 {
     if (a_len % a_vs->block_size) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_VS_READ;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_VS_READ);
+        tsk_error_set_errstr(
             "tsk_vs_read_block: length %" PRIuSIZE ""
             " not a multiple of %d", a_len, a_vs->block_size);
         return -1;
@@ -85,8 +85,8 @@ tsk_vs_part_read_block(const TSK_VS_PART_INFO * a_vs_part,
 
     if (a_len % vs->block_size) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_VS_READ;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_VS_READ);
+        tsk_error_set_errstr(
             "tsk_vs_part_read_block: length %" PRIuSIZE ""
             " not a multiple of %d", a_len, vs->block_size);
         return -1;
diff --git a/tsk3/vs/mm_open.c b/tsk3/vs/mm_open.c
index ad82a167f..f39cf72e7 100644
--- a/tsk3/vs/mm_open.c
+++ b/tsk3/vs/mm_open.c
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2003-2008 Brian Carrier.  All rights reserved
+ * Copyright (c) 2003-2011 Brian Carrier.  All rights reserved
  *
  * tsk_vs_open - wrapper function for specific partition type
  *
@@ -64,8 +64,8 @@ tsk_vs_open(TSK_IMG_INFO * img_info, TSK_DADDR_T offset,
                vs_set->close(vs_set);
                vs->close(vs);
                tsk_error_reset();
-               tsk_errno = TSK_ERR_VS_UNKTYPE;
-               snprintf(tsk_errstr, TSK_ERRSTR_L,
+               tsk_error_set_errno(TSK_ERR_VS_UNKTYPE);
+               tsk_error_set_errstr(
                "BSD or %s at %" PRIuDADDR, set, offset);
                tsk_errstr2[0] = '\0';
                return NULL;
@@ -112,8 +112,8 @@ tsk_vs_open(TSK_IMG_INFO * img_info, TSK_DADDR_T offset,
                     vs_set->close(vs_set);
                     vs->close(vs);
                     tsk_error_reset();
-                    tsk_errno = TSK_ERR_VS_UNKTYPE;
-                    snprintf(tsk_errstr, TSK_ERRSTR_L,
+                    tsk_error_set_errno(TSK_ERR_VS_UNKTYPE);
+                    tsk_error_set_errstr(
                         "GPT or %s at %" PRIuDADDR, set, offset);
                     return NULL;
                 }
@@ -134,8 +134,8 @@ tsk_vs_open(TSK_IMG_INFO * img_info, TSK_DADDR_T offset,
                 vs_set->close(vs_set);
                 vs->close(vs);
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_VS_UNKTYPE;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_VS_UNKTYPE);
+                tsk_error_set_errstr(
                     "Sun or %s at %" PRIuDADDR, set, offset);
                 return NULL;
             }
@@ -153,8 +153,8 @@ tsk_vs_open(TSK_IMG_INFO * img_info, TSK_DADDR_T offset,
                 vs_set->close(vs_set);
                 vs->close(vs);
                 tsk_error_reset();
-                tsk_errno = TSK_ERR_VS_UNKTYPE;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                tsk_error_set_errno(TSK_ERR_VS_UNKTYPE);
+                tsk_error_set_errstr(
                     "Mac or %s at %" PRIuDADDR, set, offset);
                 return NULL;
             }
@@ -165,7 +165,7 @@ tsk_vs_open(TSK_IMG_INFO * img_info, TSK_DADDR_T offset,
 
         if (vs_set == NULL) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_VS_UNKTYPE;
+            tsk_error_set_errno(TSK_ERR_VS_UNKTYPE);
             return NULL;
         }
 
@@ -187,8 +187,8 @@ tsk_vs_open(TSK_IMG_INFO * img_info, TSK_DADDR_T offset,
         case TSK_VS_TYPE_UNSUPP:
         default:
             tsk_error_reset();
-            tsk_errno = TSK_ERR_VS_UNSUPTYPE;
-            snprintf(tsk_errstr, TSK_ERRSTR_L, "%d", type);
+            tsk_error_set_errno(TSK_ERR_VS_UNSUPTYPE);
+            tsk_error_set_errstr("%d", type);
             return NULL;
         }
     }
diff --git a/tsk3/vs/mm_part.c b/tsk3/vs/mm_part.c
index 9ce3bf16c..deac4b651 100644
--- a/tsk3/vs/mm_part.c
+++ b/tsk3/vs/mm_part.c
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2003-2008 Brian Carrier.  All rights reserved
+ * Copyright (c) 2003-2011 Brian Carrier.  All rights reserved
  *
  * This software is distributed under the Common Public License 1.0
  */
@@ -205,16 +205,16 @@ tsk_vs_part_get(const TSK_VS_INFO * a_vs, TSK_PNUM_T a_idx)
 
     if (a_vs == NULL) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_VS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_VS_ARG);
+        tsk_error_set_errstr(
             "tsk_vs_part_get: pointer is NULL");
         return NULL;
     }
 
     if (a_idx >= a_vs->part_count) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_VS_ARG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_VS_ARG);
+        tsk_error_set_errstr(
             "tsk_vs_part_get: Volume address is too big");
         return NULL;
     }
@@ -249,8 +249,8 @@ tsk_vs_part_walk(TSK_VS_INFO * a_vs, TSK_PNUM_T a_start, TSK_PNUM_T a_last,
 
     if (a_start >= a_vs->part_count) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_VS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_VS_WALK_RNG);
+        tsk_error_set_errstr(
             "tsk_vs_part_walk: Start partition too large: %" PRIuPNUM "",
             a_start);
         return 1;
@@ -258,8 +258,8 @@ tsk_vs_part_walk(TSK_VS_INFO * a_vs, TSK_PNUM_T a_start, TSK_PNUM_T a_last,
 
     if (a_last >= a_vs->part_count) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_VS_WALK_RNG;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_VS_WALK_RNG);
+        tsk_error_set_errstr(
             "tsk_vs_part_walk: End partition too large: %" PRIuPNUM "",
             a_last);
         return 1;
diff --git a/tsk3/vs/mm_types.c b/tsk3/vs/mm_types.c
index a01e8931d..33a7a0d0a 100644
--- a/tsk3/vs/mm_types.c
+++ b/tsk3/vs/mm_types.c
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2003-2008 Brian Carrier.  All rights reserved
+ * Copyright (c) 2003-2011 Brian Carrier.  All rights reserved
  *
  * This software is distributed under the Common Public License 1.0
  */
diff --git a/tsk3/vs/sun.c b/tsk3/vs/sun.c
index f30947dd5..2b453af29 100644
--- a/tsk3/vs/sun.c
+++ b/tsk3/vs/sun.c
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2006-2008 Brian Carrier, Basis Technology.  All rights reserved
+ * Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All rights reserved
  * Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
  *
  * SUN - Sun VTOC code
@@ -106,8 +106,8 @@ sun_load_table_i386(TSK_VS_INFO * vs, sun_dlabel_i386 * dlabel_x86)
         if ((idx < 2) && (tsk_getu32(vs->endian,
                     dlabel_x86->part[idx].start_sec) > max_addr)) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_VS_BLK_NUM;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_VS_BLK_NUM);
+            tsk_error_set_errstr(
                 "sun_load_i386: Starting sector too large for image");
             return 1;
         }
@@ -175,8 +175,8 @@ sun_load_table_sparc(TSK_VS_INFO * vs, sun_dlabel_sparc * dlabel_sp)
         // make sure the first couple are in the image bounds
         if ((idx < 2) && (part_start > max_addr)) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_VS_BLK_NUM;
-            snprintf(tsk_errstr, TSK_ERRSTR_L,
+            tsk_error_set_errno(TSK_ERR_VS_BLK_NUM);
+            tsk_error_set_errstr(
                 "sun_load_sparc: Starting sector too large for image");
             return 1;
         }
@@ -222,8 +222,8 @@ sun_load_table(TSK_VS_INFO * vs)
     if ((sizeof(*dlabel_sp) > vs->block_size) ||
         (sizeof(*dlabel_x86) > vs->block_size)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_VS_BUF;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_VS_BUF);
+        tsk_error_set_errstr(
             "sun_load_table: disk labels bigger than block size");
         return 1;
     }
@@ -243,9 +243,9 @@ sun_load_table(TSK_VS_INFO * vs)
     if (cnt != vs->block_size) {
         if (cnt >= 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_VS_READ;
+            tsk_error_set_errno(TSK_ERR_VS_READ);
         }
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
+        tsk_error_set_errstr2(
             "SUN Disk Label in Sector: %" PRIuDADDR, taddr);
         free(buf);
         return 1;
@@ -286,9 +286,9 @@ sun_load_table(TSK_VS_INFO * vs)
     if (cnt != vs->block_size) {
         if (cnt >= 0) {
             tsk_error_reset();
-            tsk_errno = TSK_ERR_VS_READ;
+            tsk_error_set_errno(TSK_ERR_VS_READ);
         }
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
+        tsk_error_set_errstr2(
             "SUN (Intel) Disk Label in Sector: %" PRIuDADDR, taddr);
         free(buf);
         return 1;
@@ -297,8 +297,8 @@ sun_load_table(TSK_VS_INFO * vs)
     dlabel_x86 = (sun_dlabel_i386 *) buf;
     if (tsk_vs_guessu16(vs, dlabel_x86->magic, SUN_MAGIC)) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_VS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_VS_MAGIC);
+        tsk_error_set_errstr(
             "SUN (intel) partition table (Sector: %"
             PRIuDADDR ") %x", taddr, tsk_getu16(vs->endian,
                 dlabel_sp->magic));
@@ -308,8 +308,8 @@ sun_load_table(TSK_VS_INFO * vs)
 
     if (tsk_getu32(vs->endian, dlabel_x86->sanity) != SUN_SANITY) {
         tsk_error_reset();
-        tsk_errno = TSK_ERR_VS_MAGIC;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
+        tsk_error_set_errno(TSK_ERR_VS_MAGIC);
+        tsk_error_set_errstr(
             "SUN (intel) sanity value (Sector: %" PRIuDADDR
             ") %x", taddr, tsk_getu16(vs->endian, dlabel_sp->magic));
         free(buf);
diff --git a/tsk3/vs/tsk_bsd.h b/tsk3/vs/tsk_bsd.h
index 912e4e915..74ab3bc31 100644
--- a/tsk3/vs/tsk_bsd.h
+++ b/tsk3/vs/tsk_bsd.h
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2003-2008 Brian Carrier.  All rights reserved
+ * Copyright (c) 2003-2011 Brian Carrier.  All rights reserved
  * 
  * ** This software is distributed under the Common Public License 1.0
  */
diff --git a/tsk3/vs/tsk_dos.h b/tsk3/vs/tsk_dos.h
index 8b105a762..08d70e0c3 100644
--- a/tsk3/vs/tsk_dos.h
+++ b/tsk3/vs/tsk_dos.h
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2003-2008 Brian Carrier.  All rights reserved
+ * Copyright (c) 2003-2011 Brian Carrier.  All rights reserved
  *
  * This software is distributed under the Common Public License 1.0
  */
diff --git a/tsk3/vs/tsk_gpt.h b/tsk3/vs/tsk_gpt.h
index 0e466f7e3..b265bcc2b 100644
--- a/tsk3/vs/tsk_gpt.h
+++ b/tsk3/vs/tsk_gpt.h
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2004-2008 Brian Carrier.  All rights reserved
+ * Copyright (c) 2004-2011 Brian Carrier.  All rights reserved
  *
  * This software is distributed under the Common Public License 1.0
  */
diff --git a/tsk3/vs/tsk_mac.h b/tsk3/vs/tsk_mac.h
index 939061798..cf86fc94c 100644
--- a/tsk3/vs/tsk_mac.h
+++ b/tsk3/vs/tsk_mac.h
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2003-2008 Brian Carrier.  All rights reserved
+ * Copyright (c) 2003-2011 Brian Carrier.  All rights reserved
  *
  * This software is distributed under the Common Public License 1.0
  */
diff --git a/tsk3/vs/tsk_sun.h b/tsk3/vs/tsk_sun.h
index d16a80a52..c627fb2b3 100644
--- a/tsk3/vs/tsk_sun.h
+++ b/tsk3/vs/tsk_sun.h
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2003-2008 Brian Carrier.  All rights reserved
+ * Copyright (c) 2003-2011 Brian Carrier.  All rights reserved
  *
  * This software is distributed under the Common Public License 1.0
  * 
diff --git a/tsk3/vs/tsk_vs.h b/tsk3/vs/tsk_vs.h
index 9a8e63587..be21fe3f4 100644
--- a/tsk3/vs/tsk_vs.h
+++ b/tsk3/vs/tsk_vs.h
@@ -1,23 +1,24 @@
 /*
- * The Sleuth Kit
- *
- * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2003-2008 Brian Carrier.  All rights reserved
- *
- * This software is distributed under the Common Public License 1.0
- *
- */
+* The Sleuth Kit
+*
+* Brian Carrier [carrier <at> sleuthkit [dot] org]
+* Copyright (c) 2003-2011 Brian Carrier.  All rights reserved
+*
+* This software is distributed under the Common Public License 1.0
+*
+*/
 
 /**
- * \file tsk_vs.h
- * External header file for media management (volume system) support.
- * Note that this file is not meant to be directly included.  
- * It is included by both libtsk.h and tsk_vs_i.h.
- */
+* \file tsk_vs.h
+* External header file for media management (volume system) support.
+* Note that this file is not meant to be directly included.  
+* It is included by both libtsk.h and tsk_vs_i.h.
+*/
 
 /**
- * \defgroup vslib Volume System Functions
- */
+* \defgroup vslib C Volume System Functions
+ * \defgroup vslib_cpp C++ Volume System Classes
+*/
 #ifndef _TSK_VS_H
 #define _TSK_VS_H
 
@@ -25,26 +26,26 @@
 extern "C" {
 #endif
 
-/* Structures */
+    /* Structures */
     typedef struct TSK_VS_INFO TSK_VS_INFO;
     typedef struct TSK_VS_PART_INFO TSK_VS_PART_INFO;
 
     /** 
-     * Definition for callback function that vs_part_walk() calls for
-     * each partition that it walks.  
-     *
-     * @param a_vs Pointer to volume system being analyzed
-     * @param a_vs_part Pointer to current partition in the walk
-     * @param a_ptr Pointer that was passed to vs_part_walk by caller
-     * @returns Status on whether the vs_part_walk() function should 
-     * continue, stop, or error. 
-     */
+    * Definition for callback function that vs_part_walk() calls for
+    * each partition that it walks.  
+    *
+    * @param a_vs Pointer to volume system being analyzed
+    * @param a_vs_part Pointer to current partition in the walk
+    * @param a_ptr Pointer that was passed to vs_part_walk by caller
+    * @return Status on whether the vs_part_walk() function should 
+    * continue, stop, or error. 
+    */
     typedef TSK_WALK_RET_ENUM(*TSK_VS_PART_WALK_CB) (TSK_VS_INFO *
         a_vs, const TSK_VS_PART_INFO * a_vs_part, void *a_ptr);
 
     /**
-     * Flags for the partition type.  
-     */
+    * Flags for the partition type.  
+    */
     typedef enum {
         TSK_VS_TYPE_DETECT = 0x0000,    ///< Use autodetection methods
         TSK_VS_TYPE_DOS = 0x0001,       ///< DOS Partition table
@@ -57,9 +58,9 @@ extern "C" {
     } TSK_VS_TYPE_ENUM;
 
     /**
-     * Data structure used to store state and basic information
-     * for open volume systems.
-     */
+    * Data structure used to store state and basic information
+    * for open volume systems.
+    */
     struct TSK_VS_INFO {
         TSK_IMG_INFO *img_info; ///< Pointer to disk image that VS is in
         TSK_VS_TYPE_ENUM vstype;        ///< Type of volume system / media management
@@ -78,14 +79,14 @@ extern "C" {
 
 
 
-/***************************************************************
- * Generic structures  for partitions / slices
- */
+    /***************************************************************
+    * Generic structures  for partitions / slices
+    */
 
     /** 
-     * Flag values that describe the partitions in the VS.  Refer
-     * to \ref vs_open2 for more details. 
-     */
+    * Flag values that describe the partitions in the VS.  Refer
+    * to \ref vs_open2 for more details. 
+    */
     typedef enum {
         TSK_VS_PART_FLAG_ALLOC = 0x01,  ///< Sectors are allocated to a volume in the volume system
         TSK_VS_PART_FLAG_UNALLOC = 0x02,        ///< Sectors are not allocated to a volume 
@@ -94,8 +95,8 @@ extern "C" {
     } TSK_VS_PART_FLAG_ENUM;
 
     /**
-     * Linked list entry that describes a volume in a generic way. 
-     */
+    * Linked list entry that describes a volume in a generic way. 
+    */
     struct TSK_VS_PART_INFO {
         TSK_VS_PART_INFO *prev; ///< Pointer to previous partition (or NULL)
         TSK_VS_PART_INFO *next; ///< Pointer to next partition (or NULL)
@@ -142,5 +143,372 @@ extern "C" {
 
 #ifdef __cplusplus
 }
+#endif
+#ifdef __cplusplus
+class TskVsInfo;
+class TskVsPartInfo;
+
+/** 
+* Definition for callback function that vs_part_walk() calls for
+* each partition that it walks.  
+*
+* @param a_vs Pointer to volume system being analyzed
+* @param a_vs_part Pointer to current partition in the walk
+* @param a_ptr Pointer that was passed to vs_part_walk by caller
+* @return Status on whether the vs_part_walk() function should 
+* continue, stop, or error. 
+*/
+typedef TSK_WALK_RET_ENUM(*TSK_VS_PART_WALK_CPP_CB) (TskVsInfo *
+                                                     a_vs, const TskVsPartInfo * a_vs_part, void *a_ptr);
+/** \internal
+* Internal structure to pass C++ volume system part walk data into C block walk call back.
+*/
+typedef struct {
+    TSK_VS_PART_WALK_CPP_CB cppAction;  // pointer C++ callback
+    void *cPtr; // pointer to data that was passed into C++ walk method
+} TSK_VS_PART_WALK_CPP_DATA;
+
+/** \internal
+* Internal function used to call C++ Block Walk callback from C callback.
+*/
+extern TSK_WALK_RET_ENUM tsk_vs_part_walk_cpp_c_cb (TSK_VS_INFO *a_vs,const TSK_VS_PART_INFO * a_vs_part, void *a_ptr);
+
+/** 
+ * \ingroup vslib_cpp
+* Stores information about a volume / partition inside of an open volume
+* system. 
+*/
+class TskVsPartInfo {
+    friend class TskFsInfo;
+    
+private:
+    TSK_VS_PART_INFO *m_vsPartInfo;
+    TskVsPartInfo(const TskVsPartInfo& rhs); 
+    TskVsPartInfo& operator=(const TskVsPartInfo& rhs);
+    
+public:
+
+    /**
+     * Create an object from its C struct.
+     * @param a_vsPartInfo Pointer to C struct for partition.  If NULL, the
+     * remaining getX() methods will be undefined.
+     */
+    TskVsPartInfo (TSK_VS_PART_INFO *a_vsPartInfo) {
+        m_vsPartInfo = a_vsPartInfo;
+    };
+
+    /**
+    * Reads data starting at a byte address relative to the start of a VOLUME in a volume system.
+    * See tsk_vs_part_read() for details.
+    * @param a_off Byte offset to read from, relative to start of VOLUME in volume system.
+    * @param a_buf Buffer to store data in
+    * @param a_len Amount of data to read (in bytes)
+    * @return Number of bytes read or -1 on error 
+    */
+    ssize_t read(TSK_OFF_T a_off, char *a_buf, size_t a_len) {
+        if (m_vsPartInfo != NULL)
+            return tsk_vs_part_read(m_vsPartInfo, a_off, a_buf, a_len);
+        else 
+            return 0;
+    };
+    
+    /**
+    * Reads one or more blocks of data with an address relative to the start of a VOLUME in a volume system.
+    * See tsk_vs_part_read_block() for details.
+    * @param a_addr Block address to start reading from, relative to start of VOLUME in volume system.
+    * @param a_buf Buffer to store data in
+    * @param a_len Amount of data to read (in bytes - must be a multiple of block_size)
+    * @return Number of bytes read or -1 on error 
+    */
+    ssize_t readBlock(TSK_DADDR_T a_addr, char *a_buf, size_t a_len) {
+        if (m_vsPartInfo != NULL)
+            return tsk_vs_part_read_block(m_vsPartInfo, a_addr, a_buf, a_len);
+        else 
+            return 0;
+    };
+    
+    /**
+    * Return sector offset of start of partition
+    * @return sector offset of start of partition
+    */
+    TSK_DADDR_T getStart() const {
+        if (m_vsPartInfo != NULL)
+            return m_vsPartInfo->start; 
+        else
+            return 0;
+    };
+    
+    /**
+    * Return number of sectors in partition
+    * @return number of sectors in partition
+    */
+    TSK_DADDR_T getLen() const {
+        if (m_vsPartInfo != NULL)
+            return m_vsPartInfo->len; 
+        else
+            return 0;
+    };
+    
+    /**
+    * Return UTF-8 description of partition (volume system type-specific)
+    * @return description of partition
+    */
+    const char * getDesc() const {
+        if (m_vsPartInfo != NULL)
+            return m_vsPartInfo->desc; 
+        else 
+            return NULL;
+    };
+    
+    /**
+    * Return table address that describes this partition
+    * @return table address that describes this partition
+    */
+    int8_t getTableNum() const {
+        if (m_vsPartInfo != NULL)
+            return m_vsPartInfo->table_num; 
+        else 
+            return 0;
+    };
+    
+    /**
+    * Return entry in the table that describes this partition
+    * @return entry in the table that describes this partition
+    */
+    int8_t getSlotNum() const {
+        if (m_vsPartInfo != NULL)
+            return m_vsPartInfo->slot_num; 
+        else 
+            return 0;
+    };
+    
+    /**
+    * Return address of this partition
+    * @return address of this partition
+    */
+    TSK_PNUM_T getAddr() const {
+        if (m_vsPartInfo != NULL)
+            return m_vsPartInfo->addr; 
+        else
+            return 0;
+    };
+    
+    /**
+    * Return flags for partition
+    * @return flags for partition
+    */
+    TSK_VS_PART_FLAG_ENUM getFlags() const {
+        if (m_vsPartInfo != NULL)
+            return m_vsPartInfo->flags; 
+        else
+            return (TSK_VS_PART_FLAG_ENUM)0;
+    };
+};
+
+
+/**
+ * \ingroup vslib_cpp
+* Stores information about an open volume system. 
+* To use this object, open() should be called first.
+*/
+class TskVsInfo{
+private:
+    TSK_VS_INFO *m_vsInfo;
+    bool m_opened; // true if open() was called and we need to free it
+    TskVsInfo(const TskVsInfo& rhs); 
+    TskVsInfo& operator=(const TskVsInfo& rhs);
+    
+public:
+    TskVsInfo(TSK_VS_INFO *a_vsInfo) {
+        m_vsInfo = a_vsInfo;
+        m_opened = false;
+    };
+    
+    TskVsInfo() {
+        m_vsInfo = NULL;
+        m_opened = false;
+    };
+    
+   ~TskVsInfo() {
+        close();
+    };
+
+    /** 
+    * Walk a range of partitions and pass the data to a callback function. 
+    * See tsk_vs_part_walk() for details.
+    * @param a_start Address of first partition to walk from.
+    * @param a_last Address of last partition to walk to.
+    * @param a_flags Flags that are used to identify which of the partitions in the range should be returned (if 0, all partitions will be returned).
+    * @param a_action Callback action to call for each partition.
+    * @param a_ptr Pointer to data that will be passed to callback.
+    * @return 1 on error and 0 on success
+    */
+    uint8_t vsPartWalk(TSK_PNUM_T a_start, TSK_PNUM_T a_last,
+                       TSK_VS_PART_FLAG_ENUM a_flags, TSK_VS_PART_WALK_CPP_CB a_action, void *a_ptr) {
+            TSK_VS_PART_WALK_CPP_DATA vsPartData;
+            vsPartData.cppAction = a_action;
+            vsPartData.cPtr = a_ptr;
+            return tsk_vs_part_walk(m_vsInfo, a_start, a_last,
+                a_flags, tsk_vs_part_walk_cpp_c_cb, &vsPartData);
+    };
+    
+    /**
+    * Open a disk image and process the media management system
+    * data. See tsk_vs_open() for details.
+    *
+    * @param a_imgInfo The opened disk image.
+    * @param a_offset Byte offset in the disk image to start analyzing from.
+    * @param a_type Type of volume system (including auto detect)
+    *
+    * @return 1 on error and 0 on success. 
+    */
+    uint8_t open(TskImgInfo * a_imgInfo, TSK_DADDR_T a_offset,
+        TSK_VS_TYPE_ENUM a_type) {        
+        if ((m_vsInfo = tsk_vs_open(a_imgInfo->m_imgInfo, a_offset,a_type)) != NULL) {
+            m_opened = true;
+            return 0;
+        }
+        else {
+            return 1;
+        }
+    };
+    
+    /**
+    * Reads one or more blocks of data with an address relative to the start of the volume system.
+    * See tsk_vs_read_block() for details.
+    * @param a_addr Sector address to read from, relative to start of VOLUME SYSTEM.
+    * @param a_buf Buffer to store data in
+    * @param a_len Amount of data to read (in bytes - must be a multiple of block_size)
+    * @return Number of bytes read or -1 on error 
+    */
+    ssize_t readBlock(TSK_DADDR_T a_addr, char *a_buf, size_t a_len) {
+        if (m_vsInfo != NULL)
+            return tsk_vs_read_block(m_vsInfo, a_addr,a_buf, a_len);
+        else 
+            return 0;
+    };
+    
+    /**
+    * Closes an open volume system. See for tsk_vs_close() details.
+    */
+    void close() {
+        if ((m_vsInfo) && (m_opened))
+            tsk_vs_close(m_vsInfo);
+        m_vsInfo = NULL;
+    };
+
+    /**
+    * Return the byte offset where volume system starts in disk image
+    * @return byte offset
+    */
+    TSK_DADDR_T getOffset() const {
+        if (m_vsInfo != NULL)
+            return m_vsInfo->offset;
+        else
+            return 0;
+    };
+    
+    /**
+    * Return size of volume system blocks in bytes
+    * @return size of a block in bytes
+    */
+    unsigned int getBlockSize() const {
+        if (m_vsInfo != NULL)
+            return m_vsInfo->block_size; 
+        else 
+            return 0;
+    };
+    
+    /**
+    * Return number of partitions
+    * @return number of partitions
+    */
+    TSK_PNUM_T getPartCount() const {
+        if (m_vsInfo != NULL)
+            return m_vsInfo->part_count;
+        else
+            return 0;
+    };
+
+    /**
+    * Get reference to a volume in the volume system.
+    * See tsk_vs_part_get() for details.
+    * @param a_idx Index for volume to return (0-based)
+    * @return Pointer to partition or NULL on error.  Caller is responsible for freeing object.
+    */    
+    const TskVsPartInfo *getPart(TSK_PNUM_T a_idx) const {
+        // @@@ Error handling.
+        return new TskVsPartInfo(const_cast<TSK_VS_PART_INFO *>(tsk_vs_part_get(m_vsInfo, a_idx)));
+    };
+
+    /**
+    * Get a reference to the parent image object. 
+    * @return Pointer to object or NULL on error.  Caller is responsible for freeing object.
+    */    
+    const TskImgInfo *getImgInfo() const {
+        if (m_vsInfo == NULL)
+            return 0;
+        return new TskImgInfo(m_vsInfo->img_info);
+    };
+
+    /**
+    * Return type of volume system / media management
+    * @return type of volume system / media management
+    */
+    TSK_VS_TYPE_ENUM getVsType() const {
+        if (m_vsInfo != NULL)
+            return m_vsInfo->vstype;
+        else
+            return (TSK_VS_TYPE_ENUM)0;
+    };
+    
+    /**
+     * Parse a string with the volume system type and return its internal ID.
+     * See tsk_vs_type_toid() for details.
+     * @param a_str String to parse.
+     * @return ID of string (or unsupported if the name is unknown)
+     */
+    static TSK_VS_TYPE_ENUM typeToId(const TSK_TCHAR * a_str) {
+        return tsk_vs_type_toid(a_str);
+    };
+    /**
+     * Print the supported volume system type names to an open handle.
+     * See tsk_vs_type_print() for details.
+     * @param a_hFile Handle to print to.
+     */
+    static void typePrint(FILE * a_hFile) {
+        tsk_vs_type_print(a_hFile);
+    };
+    
+    /**
+     * Return the supported volume system types. 
+     * See tsk_vs_type_supported() for details.
+     * @return The bit in the return value is 1 if the type is supported.
+     */
+    static TSK_VS_TYPE_ENUM typeSupported() {
+        return tsk_vs_type_supported();
+    };
+    
+    /**
+     * Return the string name of a partition type ID.
+     * See tsk_vs_type_toname() for details.
+     * @param a_type Volume system type
+     * @return name of type or NULL on error
+     */
+    static const char * typeToName(TSK_VS_TYPE_ENUM a_type) {
+        return tsk_vs_type_toname(a_type);
+    };
+    
+    /**
+     * Return the string description of a partition type ID.
+     * See tsk_vs_type_todesc() for details.
+     * @param a_type Volume system type
+     * @return description of type or NULL on error
+     */
+    static const char * typeToDesc(TSK_VS_TYPE_ENUM a_type) {
+        return tsk_vs_type_todesc(a_type);
+    };
+};
+
 #endif
 #endif
diff --git a/tsk3/vs/tsk_vs_i.h b/tsk3/vs/tsk_vs_i.h
index dd3efe10d..01cdeb6a6 100644
--- a/tsk3/vs/tsk_vs_i.h
+++ b/tsk3/vs/tsk_vs_i.h
@@ -2,7 +2,7 @@
  * The Sleuth Kit
  *
  * Brian Carrier [carrier <at> sleuthkit [dot] org]
- * Copyright (c) 2003-2008 Brian Carrier.  All rights reserved
+ * Copyright (c) 2003-2011 Brian Carrier.  All rights reserved
  *
  * This software is distributed under the Common Public License 1.0
  */
diff --git a/unit_tests/Makefile b/unit_tests/Makefile
new file mode 100644
index 000000000..358c67565
--- /dev/null
+++ b/unit_tests/Makefile
@@ -0,0 +1,498 @@
+# Makefile.in generated by automake 1.10 from Makefile.am.
+# unit_tests/Makefile.  Generated from Makefile.in by configure.
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006  Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+
+
+pkgdatadir = $(datadir)/sleuthkit
+pkglibdir = $(libdir)/sleuthkit
+pkgincludedir = $(includedir)/sleuthkit
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = i386-apple-darwin10.7.0
+host_triplet = i386-apple-darwin10.7.0
+subdir = unit_tests
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/ax_pthread.m4 \
+	$(top_srcdir)/cppunit.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/tsk3/tsk_config.h
+CONFIG_CLEAN_FILES =
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
+	html-recursive info-recursive install-data-recursive \
+	install-dvi-recursive install-exec-recursive \
+	install-html-recursive install-info-recursive \
+	install-pdf-recursive install-ps-recursive install-recursive \
+	installcheck-recursive installdirs-recursive pdf-recursive \
+	ps-recursive uninstall-recursive
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\
+  distclean-recursive maintainer-clean-recursive
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = ${SHELL} /Users/brianc/proj/sleuthkit/trunk/config/missing --run aclocal-1.10
+ALLOCA = 
+AMTAR = ${SHELL} /Users/brianc/proj/sleuthkit/trunk/config/missing --run tar
+AR = ar
+AUTOCONF = ${SHELL} /Users/brianc/proj/sleuthkit/trunk/config/missing --run autoconf
+AUTOHEADER = ${SHELL} /Users/brianc/proj/sleuthkit/trunk/config/missing --run autoheader
+AUTOMAKE = ${SHELL} /Users/brianc/proj/sleuthkit/trunk/config/missing --run automake-1.10
+AWK = awk
+CC = gcc
+CCDEPMODE = depmode=gcc3
+CFLAGS = -g -O2 -D_THREAD_SAFE -pthread -I/usr/local/include
+CPP = gcc -E
+CPPFLAGS = 
+CPPUNIT_CFLAGS = 
+CPPUNIT_CONFIG = no
+CPPUNIT_LIBS = 
+CXX = g++
+CXXCPP = g++ -E
+CXXDEPMODE = depmode=gcc3
+CXXFLAGS = -g -O2
+CYGPATH_W = echo
+DEFS = -DHAVE_CONFIG_H
+DEPDIR = .deps
+DSYMUTIL = dsymutil
+DUMPBIN = 
+ECHO_C = \c
+ECHO_N = 
+ECHO_T = 
+EGREP = /usr/bin/grep -E
+EXEEXT = 
+FGREP = /usr/bin/grep -F
+GREP = /usr/bin/grep
+INSTALL = /usr/bin/install -c
+INSTALL_DATA = ${INSTALL} -m 644
+INSTALL_PROGRAM = ${INSTALL}
+INSTALL_SCRIPT = ${INSTALL}
+INSTALL_STRIP_PROGRAM = $(install_sh) -c -s
+LD = /usr/libexec/gcc/i686-apple-darwin10/4.2.1/ld
+LDFLAGS =  -D_THREAD_SAFE -pthread -L/usr/local/lib
+LIBOBJS =  ${LIBOBJDIR}error$U.o ${LIBOBJDIR}lstat$U.o
+LIBS = -lewf -ldl -lafflib 
+LIBTOOL = $(SHELL) $(top_builddir)/libtool
+LIPO = lipo
+LN_S = ln -s
+LTLIBOBJS =  ${LIBOBJDIR}error$U.lo ${LIBOBJDIR}lstat$U.lo
+MAINT = #
+MAKEINFO = ${SHELL} /Users/brianc/proj/sleuthkit/trunk/config/missing --run makeinfo
+MKDIR_P = ../config/install-sh -c -d
+NM = /usr/bin/nm
+NMEDIT = nmedit
+OBJEXT = o
+OTOOL = otool
+OTOOL64 = :
+PACKAGE = sleuthkit
+PACKAGE_BUGREPORT = 
+PACKAGE_NAME = sleuthkit
+PACKAGE_STRING = sleuthkit 0.0.0
+PACKAGE_TARNAME = sleuthkit
+PACKAGE_VERSION = 0.0.0
+PATH_SEPARATOR = :
+PERL = /usr/bin/perl
+PTHREAD_CC = gcc
+PTHREAD_CFLAGS = -D_THREAD_SAFE -pthread
+PTHREAD_LIBS = 
+RANLIB = ranlib
+SED = /usr/bin/sed
+SET_MAKE = 
+SHELL = /bin/sh
+STRIP = strip
+VERSION = 0.0.0
+abs_builddir = /Users/brianc/proj/sleuthkit/trunk/unit_tests
+abs_srcdir = /Users/brianc/proj/sleuthkit/trunk/unit_tests
+abs_top_builddir = /Users/brianc/proj/sleuthkit/trunk
+abs_top_srcdir = /Users/brianc/proj/sleuthkit/trunk
+ac_ct_CC = gcc
+ac_ct_CXX = g++
+ac_ct_DUMPBIN = 
+am__include = include
+am__leading_dot = .
+am__quote = 
+am__tar = ${AMTAR} chof - "$$tardir"
+am__untar = ${AMTAR} xf -
+ax_pthread_config = 
+bindir = ${exec_prefix}/bin
+build = i386-apple-darwin10.7.0
+build_alias = 
+build_cpu = i386
+build_os = darwin10.7.0
+build_vendor = apple
+builddir = .
+datadir = ${datarootdir}
+datarootdir = ${prefix}/share
+docdir = ${datarootdir}/doc/${PACKAGE_TARNAME}
+dvidir = ${docdir}
+exec_prefix = ${prefix}
+host = i386-apple-darwin10.7.0
+host_alias = 
+host_cpu = i386
+host_os = darwin10.7.0
+host_vendor = apple
+htmldir = ${docdir}
+includedir = ${prefix}/include
+infodir = ${datarootdir}/info
+install_sh = $(SHELL) /Users/brianc/proj/sleuthkit/trunk/config/install-sh
+libdir = ${exec_prefix}/lib
+libexecdir = ${exec_prefix}/libexec
+localedir = ${datarootdir}/locale
+localstatedir = ${prefix}/var
+lt_ECHO = /bin/echo
+mandir = ${datarootdir}/man
+mkdir_p = $(top_builddir)/config/install-sh -c -d
+oldincludedir = /usr/include
+pdfdir = ${docdir}
+prefix = /usr/local
+program_transform_name = s,x,x,
+psdir = ${docdir}
+sbindir = ${exec_prefix}/sbin
+sharedstatedir = ${prefix}/com
+srcdir = .
+sysconfdir = ${prefix}/etc
+target_alias = 
+top_builddir = ..
+top_srcdir = ..
+SUBDIRS = base
+all: all-recursive
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: # $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+		&& exit 0; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign  unit_tests/Makefile'; \
+	cd $(top_srcdir) && \
+	  $(AUTOMAKE) --foreign  unit_tests/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: # $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): # $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+#     (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+$(RECURSIVE_TARGETS):
+	@failcom='exit 1'; \
+	for f in x $$MAKEFLAGS; do \
+	  case $$f in \
+	    *=* | --[!k]*);; \
+	    *k*) failcom='fail=yes';; \
+	  esac; \
+	done; \
+	dot_seen=no; \
+	target=`echo $@ | sed s/-recursive//`; \
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    dot_seen=yes; \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	  || eval $$failcom; \
+	done; \
+	if test "$$dot_seen" = "no"; then \
+	  $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+	fi; test -z "$$fail"
+
+$(RECURSIVE_CLEAN_TARGETS):
+	@failcom='exit 1'; \
+	for f in x $$MAKEFLAGS; do \
+	  case $$f in \
+	    *=* | --[!k]*);; \
+	    *k*) failcom='fail=yes';; \
+	  esac; \
+	done; \
+	dot_seen=no; \
+	case "$@" in \
+	  distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+	  *) list='$(SUBDIRS)' ;; \
+	esac; \
+	rev=''; for subdir in $$list; do \
+	  if test "$$subdir" = "."; then :; else \
+	    rev="$$subdir $$rev"; \
+	  fi; \
+	done; \
+	rev="$$rev ."; \
+	target=`echo $@ | sed s/-recursive//`; \
+	for subdir in $$rev; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	  || eval $$failcom; \
+	done && test -z "$$fail"
+tags-recursive:
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+	done
+ctags-recursive:
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+	done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	tags=; \
+	here=`pwd`; \
+	if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+	  include_option=--etags-include; \
+	  empty_fix=.; \
+	else \
+	  include_option=--include; \
+	  empty_fix=; \
+	fi; \
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    test ! -f $$subdir/TAGS || \
+	      tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \
+	  fi; \
+	done; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	    $$tags $$unique; \
+	fi
+ctags: CTAGS
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	tags=; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	test -z "$(CTAGS_ARGS)$$tags$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$tags $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && cd $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+	    fi; \
+	    cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+	  else \
+	    test -f $(distdir)/$$file \
+	    || cp -p $$d/$$file $(distdir)/$$file \
+	    || exit 1; \
+	  fi; \
+	done
+	list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    test -d "$(distdir)/$$subdir" \
+	    || $(MKDIR_P) "$(distdir)/$$subdir" \
+	    || exit 1; \
+	    distdir=`$(am__cd) $(distdir) && pwd`; \
+	    top_distdir=`$(am__cd) $(top_distdir) && pwd`; \
+	    (cd $$subdir && \
+	      $(MAKE) $(AM_MAKEFLAGS) \
+	        top_distdir="$$top_distdir" \
+	        distdir="$$distdir/$$subdir" \
+		am__remove_distdir=: \
+		am__skip_length_check=: \
+	        distdir) \
+	      || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-recursive
+all-am: Makefile
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-recursive
+	-rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-info: install-info-recursive
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-ps: install-ps-recursive
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) install-am \
+	install-strip
+
+.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
+	all all-am check check-am clean clean-generic clean-libtool \
+	ctags ctags-recursive distclean distclean-generic \
+	distclean-libtool distclean-tags distdir dvi dvi-am html \
+	html-am info info-am install install-am install-data \
+	install-data-am install-dvi install-dvi-am install-exec \
+	install-exec-am install-html install-html-am install-info \
+	install-info-am install-man install-pdf install-pdf-am \
+	install-ps install-ps-am install-strip installcheck \
+	installcheck-am installdirs installdirs-am maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-generic \
+	mostlyclean-libtool pdf pdf-am ps ps-am tags tags-recursive \
+	uninstall uninstall-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/unit_tests/Makefile.am b/unit_tests/Makefile.am
new file mode 100644
index 000000000..c42559c15
--- /dev/null
+++ b/unit_tests/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS= base
diff --git a/unit_tests/Makefile.in b/unit_tests/Makefile.in
new file mode 100644
index 000000000..b41efa73f
--- /dev/null
+++ b/unit_tests/Makefile.in
@@ -0,0 +1,498 @@
+# Makefile.in generated by automake 1.10 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006  Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = unit_tests
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/ax_pthread.m4 \
+	$(top_srcdir)/cppunit.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/tsk3/tsk_config.h
+CONFIG_CLEAN_FILES =
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
+	html-recursive info-recursive install-data-recursive \
+	install-dvi-recursive install-exec-recursive \
+	install-html-recursive install-info-recursive \
+	install-pdf-recursive install-ps-recursive install-recursive \
+	installcheck-recursive installdirs-recursive pdf-recursive \
+	ps-recursive uninstall-recursive
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\
+  distclean-recursive maintainer-clean-recursive
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@
+CPPUNIT_CONFIG = @CPPUNIT_CONFIG@
+CPPUNIT_LIBS = @CPPUNIT_LIBS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+ax_pthread_config = @ax_pthread_config@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = base
+all: all-recursive
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+		&& exit 0; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign  unit_tests/Makefile'; \
+	cd $(top_srcdir) && \
+	  $(AUTOMAKE) --foreign  unit_tests/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+#     (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+$(RECURSIVE_TARGETS):
+	@failcom='exit 1'; \
+	for f in x $$MAKEFLAGS; do \
+	  case $$f in \
+	    *=* | --[!k]*);; \
+	    *k*) failcom='fail=yes';; \
+	  esac; \
+	done; \
+	dot_seen=no; \
+	target=`echo $@ | sed s/-recursive//`; \
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    dot_seen=yes; \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	  || eval $$failcom; \
+	done; \
+	if test "$$dot_seen" = "no"; then \
+	  $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+	fi; test -z "$$fail"
+
+$(RECURSIVE_CLEAN_TARGETS):
+	@failcom='exit 1'; \
+	for f in x $$MAKEFLAGS; do \
+	  case $$f in \
+	    *=* | --[!k]*);; \
+	    *k*) failcom='fail=yes';; \
+	  esac; \
+	done; \
+	dot_seen=no; \
+	case "$@" in \
+	  distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+	  *) list='$(SUBDIRS)' ;; \
+	esac; \
+	rev=''; for subdir in $$list; do \
+	  if test "$$subdir" = "."; then :; else \
+	    rev="$$subdir $$rev"; \
+	  fi; \
+	done; \
+	rev="$$rev ."; \
+	target=`echo $@ | sed s/-recursive//`; \
+	for subdir in $$rev; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	  || eval $$failcom; \
+	done && test -z "$$fail"
+tags-recursive:
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+	done
+ctags-recursive:
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+	done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	tags=; \
+	here=`pwd`; \
+	if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+	  include_option=--etags-include; \
+	  empty_fix=.; \
+	else \
+	  include_option=--include; \
+	  empty_fix=; \
+	fi; \
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    test ! -f $$subdir/TAGS || \
+	      tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \
+	  fi; \
+	done; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	    $$tags $$unique; \
+	fi
+ctags: CTAGS
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	tags=; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	test -z "$(CTAGS_ARGS)$$tags$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$tags $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && cd $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+	    fi; \
+	    cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+	  else \
+	    test -f $(distdir)/$$file \
+	    || cp -p $$d/$$file $(distdir)/$$file \
+	    || exit 1; \
+	  fi; \
+	done
+	list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    test -d "$(distdir)/$$subdir" \
+	    || $(MKDIR_P) "$(distdir)/$$subdir" \
+	    || exit 1; \
+	    distdir=`$(am__cd) $(distdir) && pwd`; \
+	    top_distdir=`$(am__cd) $(top_distdir) && pwd`; \
+	    (cd $$subdir && \
+	      $(MAKE) $(AM_MAKEFLAGS) \
+	        top_distdir="$$top_distdir" \
+	        distdir="$$distdir/$$subdir" \
+		am__remove_distdir=: \
+		am__skip_length_check=: \
+	        distdir) \
+	      || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-recursive
+all-am: Makefile
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-recursive
+	-rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-info: install-info-recursive
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-ps: install-ps-recursive
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) install-am \
+	install-strip
+
+.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
+	all all-am check check-am clean clean-generic clean-libtool \
+	ctags ctags-recursive distclean distclean-generic \
+	distclean-libtool distclean-tags distdir dvi dvi-am html \
+	html-am info info-am install install-am install-data \
+	install-data-am install-dvi install-dvi-am install-exec \
+	install-exec-am install-html install-html-am install-info \
+	install-info-am install-man install-pdf install-pdf-am \
+	install-ps install-ps-am install-strip installcheck \
+	installcheck-am installdirs installdirs-am maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-generic \
+	mostlyclean-libtool pdf pdf-am ps ps-am tags tags-recursive \
+	uninstall uninstall-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/unit_tests/base/.deps/errors_test.Po b/unit_tests/base/.deps/errors_test.Po
new file mode 100644
index 000000000..9ce06a81e
--- /dev/null
+++ b/unit_tests/base/.deps/errors_test.Po
@@ -0,0 +1 @@
+# dummy
diff --git a/unit_tests/base/.deps/test_base.Po b/unit_tests/base/.deps/test_base.Po
new file mode 100644
index 000000000..9ce06a81e
--- /dev/null
+++ b/unit_tests/base/.deps/test_base.Po
@@ -0,0 +1 @@
+# dummy
diff --git a/unit_tests/base/Makefile b/unit_tests/base/Makefile
new file mode 100644
index 000000000..54009d704
--- /dev/null
+++ b/unit_tests/base/Makefile
@@ -0,0 +1,462 @@
+# Makefile.in generated by automake 1.10 from Makefile.am.
+# unit_tests/base/Makefile.  Generated from Makefile.in by configure.
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006  Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+
+
+
+pkgdatadir = $(datadir)/sleuthkit
+pkglibdir = $(libdir)/sleuthkit
+pkgincludedir = $(includedir)/sleuthkit
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = i386-apple-darwin10.7.0
+host_triplet = i386-apple-darwin10.7.0
+noinst_PROGRAMS = test_base$(EXEEXT)
+subdir = unit_tests/base
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/ax_pthread.m4 \
+	$(top_srcdir)/cppunit.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/tsk3/tsk_config.h
+CONFIG_CLEAN_FILES =
+PROGRAMS = $(noinst_PROGRAMS)
+am_test_base_OBJECTS = test_base.$(OBJEXT) errors_test.$(OBJEXT)
+test_base_OBJECTS = $(am_test_base_OBJECTS)
+test_base_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+test_base_DEPENDENCIES = ../../tsk3/libtsk3.la $(am__DEPENDENCIES_1)
+DEFAULT_INCLUDES = -I. -I$(top_builddir)/tsk3
+depcomp = $(SHELL) $(top_srcdir)/config/depcomp
+am__depfiles_maybe = depfiles
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \
+	$(LDFLAGS) -o $@
+SOURCES = $(test_base_SOURCES)
+DIST_SOURCES = $(test_base_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = ${SHELL} /Users/brianc/proj/sleuthkit/trunk/config/missing --run aclocal-1.10
+ALLOCA = 
+AMTAR = ${SHELL} /Users/brianc/proj/sleuthkit/trunk/config/missing --run tar
+AR = ar
+AUTOCONF = ${SHELL} /Users/brianc/proj/sleuthkit/trunk/config/missing --run autoconf
+AUTOHEADER = ${SHELL} /Users/brianc/proj/sleuthkit/trunk/config/missing --run autoheader
+AUTOMAKE = ${SHELL} /Users/brianc/proj/sleuthkit/trunk/config/missing --run automake-1.10
+AWK = awk
+CC = gcc
+CCDEPMODE = depmode=gcc3
+CFLAGS = -g -O2 -D_THREAD_SAFE -pthread -I/usr/local/include
+CPP = gcc -E
+CPPFLAGS = 
+CPPUNIT_CFLAGS = 
+CPPUNIT_CONFIG = no
+CPPUNIT_LIBS = 
+CXX = g++
+CXXCPP = g++ -E
+CXXDEPMODE = depmode=gcc3
+CXXFLAGS = -g -O2
+CYGPATH_W = echo
+DEFS = -DHAVE_CONFIG_H
+DEPDIR = .deps
+DSYMUTIL = dsymutil
+DUMPBIN = 
+ECHO_C = \c
+ECHO_N = 
+ECHO_T = 
+EGREP = /usr/bin/grep -E
+EXEEXT = 
+FGREP = /usr/bin/grep -F
+GREP = /usr/bin/grep
+INSTALL = /usr/bin/install -c
+INSTALL_DATA = ${INSTALL} -m 644
+INSTALL_PROGRAM = ${INSTALL}
+INSTALL_SCRIPT = ${INSTALL}
+INSTALL_STRIP_PROGRAM = $(install_sh) -c -s
+LD = /usr/libexec/gcc/i686-apple-darwin10/4.2.1/ld
+LDFLAGS = -static 
+LIBOBJS =  ${LIBOBJDIR}error$U.o ${LIBOBJDIR}lstat$U.o
+LIBS = -lewf -ldl -lafflib 
+LIBTOOL = $(SHELL) $(top_builddir)/libtool
+LIPO = lipo
+LN_S = ln -s
+LTLIBOBJS =  ${LIBOBJDIR}error$U.lo ${LIBOBJDIR}lstat$U.lo
+MAINT = #
+MAKEINFO = ${SHELL} /Users/brianc/proj/sleuthkit/trunk/config/missing --run makeinfo
+MKDIR_P = ../../config/install-sh -c -d
+NM = /usr/bin/nm
+NMEDIT = nmedit
+OBJEXT = o
+OTOOL = otool
+OTOOL64 = :
+PACKAGE = sleuthkit
+PACKAGE_BUGREPORT = 
+PACKAGE_NAME = sleuthkit
+PACKAGE_STRING = sleuthkit 0.0.0
+PACKAGE_TARNAME = sleuthkit
+PACKAGE_VERSION = 0.0.0
+PATH_SEPARATOR = :
+PERL = /usr/bin/perl
+PTHREAD_CC = gcc
+PTHREAD_CFLAGS = -D_THREAD_SAFE -pthread
+PTHREAD_LIBS = 
+RANLIB = ranlib
+SED = /usr/bin/sed
+SET_MAKE = 
+SHELL = /bin/sh
+STRIP = strip
+VERSION = 0.0.0
+abs_builddir = /Users/brianc/proj/sleuthkit/trunk/unit_tests/base
+abs_srcdir = /Users/brianc/proj/sleuthkit/trunk/unit_tests/base
+abs_top_builddir = /Users/brianc/proj/sleuthkit/trunk
+abs_top_srcdir = /Users/brianc/proj/sleuthkit/trunk
+ac_ct_CC = gcc
+ac_ct_CXX = g++
+ac_ct_DUMPBIN = 
+am__include = include
+am__leading_dot = .
+am__quote = 
+am__tar = ${AMTAR} chof - "$$tardir"
+am__untar = ${AMTAR} xf -
+ax_pthread_config = 
+bindir = ${exec_prefix}/bin
+build = i386-apple-darwin10.7.0
+build_alias = 
+build_cpu = i386
+build_os = darwin10.7.0
+build_vendor = apple
+builddir = .
+datadir = ${datarootdir}
+datarootdir = ${prefix}/share
+docdir = ${datarootdir}/doc/${PACKAGE_TARNAME}
+dvidir = ${docdir}
+exec_prefix = ${prefix}
+host = i386-apple-darwin10.7.0
+host_alias = 
+host_cpu = i386
+host_os = darwin10.7.0
+host_vendor = apple
+htmldir = ${docdir}
+includedir = ${prefix}/include
+infodir = ${datarootdir}/info
+install_sh = $(SHELL) /Users/brianc/proj/sleuthkit/trunk/config/install-sh
+libdir = ${exec_prefix}/lib
+libexecdir = ${exec_prefix}/libexec
+localedir = ${datarootdir}/locale
+localstatedir = ${prefix}/var
+lt_ECHO = /bin/echo
+mandir = ${datarootdir}/man
+mkdir_p = $(top_builddir)/config/install-sh -c -d
+oldincludedir = /usr/include
+pdfdir = ${docdir}
+prefix = /usr/local
+program_transform_name = s,x,x,
+psdir = ${docdir}
+sbindir = ${exec_prefix}/sbin
+sharedstatedir = ${prefix}/com
+srcdir = .
+sysconfdir = ${prefix}/etc
+target_alias = 
+top_builddir = ../..
+top_srcdir = ../..
+AM_CPPFLAGS = -I../.. -Wall $(CPPUNIT_CFLAGS) 
+LDADD = ../../tsk3/libtsk3.la $(CPPUNIT_LIBS)
+test_base_SOURCES = test_base.cpp errors_test.cpp
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .cpp .lo .o .obj
+$(srcdir)/Makefile.in: # $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+		&& exit 0; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign  unit_tests/base/Makefile'; \
+	cd $(top_srcdir) && \
+	  $(AUTOMAKE) --foreign  unit_tests/base/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: # $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): # $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+clean-noinstPROGRAMS:
+	@list='$(noinst_PROGRAMS)'; for p in $$list; do \
+	  f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+	  echo " rm -f $$p $$f"; \
+	  rm -f $$p $$f ; \
+	done
+test_base$(EXEEXT): $(test_base_OBJECTS) $(test_base_DEPENDENCIES) 
+	@rm -f test_base$(EXEEXT)
+	$(CXXLINK) $(test_base_OBJECTS) $(test_base_LDADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+include ./$(DEPDIR)/errors_test.Po
+include ./$(DEPDIR)/test_base.Po
+
+.cpp.o:
+	$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+	mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+#	source='$<' object='$@' libtool=no \
+#	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) \
+#	$(CXXCOMPILE) -c -o $@ $<
+
+.cpp.obj:
+	$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+	mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+#	source='$<' object='$@' libtool=no \
+#	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) \
+#	$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cpp.lo:
+	$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+	mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+#	source='$<' object='$@' libtool=yes \
+#	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) \
+#	$(LTCXXCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	tags=; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	    $$tags $$unique; \
+	fi
+ctags: CTAGS
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	tags=; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	test -z "$(CTAGS_ARGS)$$tags$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$tags $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && cd $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+	    fi; \
+	    cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+	  else \
+	    test -f $(distdir)/$$file \
+	    || cp -p $$d/$$file $(distdir)/$$file \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-local clean-noinstPROGRAMS \
+	mostlyclean-am
+
+distclean: distclean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-info: install-info-am
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-ps: install-ps-am
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+	clean-libtool clean-local clean-noinstPROGRAMS ctags distclean \
+	distclean-compile distclean-generic distclean-libtool \
+	distclean-tags distdir dvi dvi-am html html-am info info-am \
+	install install-am install-data install-data-am install-dvi \
+	install-dvi-am install-exec install-exec-am install-html \
+	install-html-am install-info install-info-am install-man \
+	install-pdf install-pdf-am install-ps install-ps-am \
+	install-strip installcheck installcheck-am installdirs \
+	maintainer-clean maintainer-clean-generic mostlyclean \
+	mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+	pdf pdf-am ps ps-am tags uninstall uninstall-am
+
+
+indent:
+	indent *.cpp *.h
+
+clean-local:
+	-rm -f *.cpp~ *.h~
+
+check:
+	./test_base
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/unit_tests/base/Makefile.am b/unit_tests/base/Makefile.am
new file mode 100644
index 000000000..c82dd88f2
--- /dev/null
+++ b/unit_tests/base/Makefile.am
@@ -0,0 +1,15 @@
+AM_CPPFLAGS = -I../.. -Wall $(CPPUNIT_CFLAGS) 
+LDADD = ../../tsk3/libtsk3.la $(CPPUNIT_LIBS)
+LDFLAGS = -static 
+
+noinst_PROGRAMS = test_base
+test_base_SOURCES= test_base.cpp errors_test.cpp
+
+indent:
+	indent *.cpp *.h
+
+clean-local:
+	-rm -f *.cpp~ *.h~
+	
+check:
+	./test_base
diff --git a/unit_tests/base/Makefile.in b/unit_tests/base/Makefile.in
new file mode 100644
index 000000000..36e5658e4
--- /dev/null
+++ b/unit_tests/base/Makefile.in
@@ -0,0 +1,462 @@
+# Makefile.in generated by automake 1.10 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006  Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+noinst_PROGRAMS = test_base$(EXEEXT)
+subdir = unit_tests/base
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/ax_pthread.m4 \
+	$(top_srcdir)/cppunit.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/tsk3/tsk_config.h
+CONFIG_CLEAN_FILES =
+PROGRAMS = $(noinst_PROGRAMS)
+am_test_base_OBJECTS = test_base.$(OBJEXT) errors_test.$(OBJEXT)
+test_base_OBJECTS = $(am_test_base_OBJECTS)
+test_base_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+test_base_DEPENDENCIES = ../../tsk3/libtsk3.la $(am__DEPENDENCIES_1)
+DEFAULT_INCLUDES = -I. -I$(top_builddir)/tsk3@am__isrc@
+depcomp = $(SHELL) $(top_srcdir)/config/depcomp
+am__depfiles_maybe = depfiles
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \
+	$(LDFLAGS) -o $@
+SOURCES = $(test_base_SOURCES)
+DIST_SOURCES = $(test_base_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@
+CPPUNIT_CONFIG = @CPPUNIT_CONFIG@
+CPPUNIT_LIBS = @CPPUNIT_LIBS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = -static 
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+ax_pthread_config = @ax_pthread_config@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = -I../.. -Wall $(CPPUNIT_CFLAGS) 
+LDADD = ../../tsk3/libtsk3.la $(CPPUNIT_LIBS)
+test_base_SOURCES = test_base.cpp errors_test.cpp
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .cpp .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+		&& exit 0; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign  unit_tests/base/Makefile'; \
+	cd $(top_srcdir) && \
+	  $(AUTOMAKE) --foreign  unit_tests/base/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+clean-noinstPROGRAMS:
+	@list='$(noinst_PROGRAMS)'; for p in $$list; do \
+	  f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+	  echo " rm -f $$p $$f"; \
+	  rm -f $$p $$f ; \
+	done
+test_base$(EXEEXT): $(test_base_OBJECTS) $(test_base_DEPENDENCIES) 
+	@rm -f test_base$(EXEEXT)
+	$(CXXLINK) $(test_base_OBJECTS) $(test_base_LDADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/errors_test.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_base.Po@am__quote@
+
+.cpp.o:
+@am__fastdepCXX_TRUE@	$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@	mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(CXXCOMPILE) -c -o $@ $<
+
+.cpp.obj:
+@am__fastdepCXX_TRUE@	$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCXX_TRUE@	mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cpp.lo:
+@am__fastdepCXX_TRUE@	$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@	mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(LTCXXCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	tags=; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	    $$tags $$unique; \
+	fi
+ctags: CTAGS
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	tags=; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	test -z "$(CTAGS_ARGS)$$tags$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$tags $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && cd $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+	    fi; \
+	    cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+	  else \
+	    test -f $(distdir)/$$file \
+	    || cp -p $$d/$$file $(distdir)/$$file \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-local clean-noinstPROGRAMS \
+	mostlyclean-am
+
+distclean: distclean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-info: install-info-am
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-ps: install-ps-am
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+	clean-libtool clean-local clean-noinstPROGRAMS ctags distclean \
+	distclean-compile distclean-generic distclean-libtool \
+	distclean-tags distdir dvi dvi-am html html-am info info-am \
+	install install-am install-data install-data-am install-dvi \
+	install-dvi-am install-exec install-exec-am install-html \
+	install-html-am install-info install-info-am install-man \
+	install-pdf install-pdf-am install-ps install-ps-am \
+	install-strip installcheck installcheck-am installdirs \
+	maintainer-clean maintainer-clean-generic mostlyclean \
+	mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+	pdf pdf-am ps ps-am tags uninstall uninstall-am
+
+
+indent:
+	indent *.cpp *.h
+
+clean-local:
+	-rm -f *.cpp~ *.h~
+
+check:
+	./test_base
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/unit_tests/base/errors_test.cpp b/unit_tests/base/errors_test.cpp
new file mode 100644
index 000000000..b2be07fb1
--- /dev/null
+++ b/unit_tests/base/errors_test.cpp
@@ -0,0 +1,196 @@
+/*
+ * errors_test.cpp
+ *
+ *  Created on: Oct 22, 2010
+ *      Author: benson
+ */
+
+#include <libtsk.h>
+#include <tsk_config.h>
+#include <string>
+#ifdef HAVE_PTHREAD
+#ifdef __APPLE__
+#include <mach/semaphore.h>
+#include <mach/task.h>
+extern "C" mach_port_t mach_task_self(void);
+#define SEM_TYPE semaphore_t
+#else
+#include <errno.h>
+#include <semaphore.h>
+#define SEM_TYPE sem_t
+#endif
+#endif
+#include "errors_test.h"
+
+// Registers the fixture into the 'registry'
+CPPUNIT_TEST_SUITE_REGISTRATION( ErrorsTest );
+
+void ErrorsTest::setUp() {}
+void ErrorsTest::tearDown() {}
+
+void ErrorsTest::testInitialState() {
+	TSK_ERROR_INFO *ei;
+
+	ei = tsk_error_get_info();
+	CPPUNIT_ASSERT(0 == ei->t_errno);
+	CPPUNIT_ASSERT(0 == ei->errstr[0]);
+	CPPUNIT_ASSERT(0 == ei->errstr2[0]);
+}
+
+void ErrorsTest::testLengthChecks() {
+	TSK_ERROR_INFO *ei;
+
+	ei = tsk_error_get_info();
+	std::string s;
+	for (unsigned x = 0; x < 4096; x ++) {
+		s = s + std::string("x");
+	}
+	tsk_error_set_errstr("%s", s.c_str());
+	std::string es(tsk_error_get_errstr());
+	CPPUNIT_ASSERT(es.size() < 1025);
+}
+
+#ifdef HAVE_PTHREAD
+
+struct xErrorsTestShared {
+	SEM_TYPE sync_barrier;
+	bool errno_check_failed;
+	bool errstr_check_failed;
+	bool errstr2_check_failed;
+	bool failure;
+
+	xErrorsTestShared() {
+		failure = false;
+		errno_check_failed = false;
+		errstr_check_failed = false;
+		errstr2_check_failed = false;
+	}
+};
+
+/*
+ * This thread sets error variables, updates the semaphore,
+ * waits on the semaphore, and reads them back.
+ */
+void * thread_1(void *arg) {
+	xErrorsTestShared * shared = (xErrorsTestShared*) arg;
+	// wait to be told to start.
+#ifdef __APPLE__
+	kern_return_t se = semaphore_wait(shared->sync_barrier);
+	if (se != 0) {
+		fprintf(stderr, "sem_wait failed: %d\n", se);
+		shared->failure = true;
+	}
+#else
+	if (sem_wait(&shared->sync_barrier) != 0) {
+		fprintf(stderr, "sem_wait failed: %s\n", strerror(errno));
+		shared->failure = true;
+	}
+#endif
+	tsk_error_set_errno(42);
+	tsk_error_set_errstr("I just set errno to %d.", 42);
+	tsk_error_set_errstr2("Indeed, I just set errno to %d.", 42);
+#ifdef __APPLE__
+	se = semaphore_signal(shared->sync_barrier);
+	if (se != 0) {
+		fprintf(stderr, "sem_signal failed: %d\n", se);
+		shared->failure = true;
+	}
+	se = semaphore_wait(shared->sync_barrier);
+	if (se != 0) {
+		fprintf(stderr, "sem_wait failed: %d\n", se);
+		shared->failure = true;
+	}
+#else
+	sem_post(&shared->sync_barrier);
+	sem_wait(&shared->sync_barrier);
+#endif
+	shared->errno_check_failed = 42 != tsk_error_get_errno();
+	char const * s = tsk_error_get_errstr();
+	shared->errstr_check_failed = 0 != strcmp("I just set errno to 42.", s);
+	s = tsk_error_get_errstr2();
+	shared->errstr2_check_failed = 0 != strcmp("Indeed, I just set errno to 42.", s);
+	return 0;
+}
+
+void ErrorsTest::testMultithreaded()
+{
+	xErrorsTestShared shared;
+	tsk_error_reset();
+	// start semaphore unlocked. Thread will lock.
+#ifdef __APPLE__
+	kern_return_t se;
+	se = semaphore_create(mach_task_self(), &shared.sync_barrier, SYNC_POLICY_FIFO, 0);
+	if (se != 0) {
+		fprintf(stderr, "sem_init failed: %d\n", se);
+				CPPUNIT_FAIL("Could not initialize semaphore");
+	}
+#else
+	if (sem_init(&shared.sync_barrier, 0, 0)) {
+		fprintf(stderr, "sem_init failed: %s\n", strerror(errno));
+		CPPUNIT_FAIL("Could not initialize semaphore");
+	}
+#endif
+
+	pthread_t thread1;
+
+	int pte = pthread_create(&thread1, 0, thread_1, &shared);
+	if (pte != 0) {
+		fprintf(stderr, "pthread_create failed: %d\n", pte);
+		CPPUNIT_FAIL("pthread_create failed.");
+	}
+
+#ifdef __APPLE__
+	se = semaphore_signal(shared.sync_barrier);
+	if (se != 0) {
+		fprintf(stderr, "semaphore_signal failed: %d\n", se);
+			CPPUNIT_FAIL("Could not post to semaphore");
+
+	}
+	se = semaphore_wait(shared.sync_barrier);
+	if (se != 0) {
+		fprintf(stderr, "semaphore_wait failed: %d\n", se);
+			CPPUNIT_FAIL("Could not post to semaphore");
+
+	}
+#else
+	// give thread permission to proceed
+	if (sem_post(&shared.sync_barrier) != 0) {
+		fprintf(stderr, "sem_post failed: %s\n", strerror(errno));
+		CPPUNIT_FAIL("Could not post to semaphore");
+	}
+	// wait for thread to set some things.
+	if (sem_wait(&shared.sync_barrier) != 0) {
+		fprintf(stderr, "sem_wait failed: %s\n", strerror(errno));
+		CPPUNIT_FAIL("Could not wait on semaphore");
+	}
+#endif
+	CPPUNIT_ASSERT(0 == tsk_error_get_errno());
+	CPPUNIT_ASSERT(0 == tsk_error_get_errstr()[0]);
+	CPPUNIT_ASSERT(0 == tsk_error_get_errstr2()[0]);
+	// give thread permission to proceed
+#ifdef __APPLE__
+	se = semaphore_signal(shared.sync_barrier);
+	if (se != 0) {
+		fprintf(stderr, "semaphore_signal failed: %d\n", se);
+		CPPUNIT_FAIL("Could not post to semaphore");
+	}
+#else
+	if (sem_post(&shared.sync_barrier) != 0) {
+		fprintf(stderr, "sem_post failed: %s\n", strerror(errno));
+		CPPUNIT_FAIL("Could not post to semaphore");
+	}
+#endif
+
+	void *exitval = 0;
+	pte = pthread_join(thread1, &exitval);
+	if (pte != 0) {
+		fprintf(stderr, "pthread_join failed: %d\n", pte);
+	    CPPUNIT_FAIL("pthread_join failed.");
+	}
+	CPPUNIT_ASSERT(!shared.errno_check_failed);
+	CPPUNIT_ASSERT(!shared.errstr_check_failed);
+	CPPUNIT_ASSERT(!shared.errstr2_check_failed);
+}
+#endif
+
+
diff --git a/unit_tests/base/test_base.cpp b/unit_tests/base/test_base.cpp
new file mode 100644
index 000000000..403222fab
--- /dev/null
+++ b/unit_tests/base/test_base.cpp
@@ -0,0 +1,39 @@
+/*
+ * The Sleuth Kit
+ *
+ *
+ * Copyright (c) 2010 Basis Technology Corp.  All Rights reserved
+ *
+ * This software is distributed under the Common Public License 1.0
+ */
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <libtsk.h>
+#include <cppunit/CompilerOutputter.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <cppunit/ui/text/TestRunner.h>
+
+
+
+int main(int argc, char **argv) {
+	// Get the top level suite from the registry
+	  CppUnit::Test *suite = CppUnit::TestFactoryRegistry::getRegistry().makeTest();
+
+	  // Adds the test to the list of test to run
+	  CppUnit::TextUi::TestRunner runner;
+	  runner.addTest( suite );
+
+	  // Change the default outputter to a compiler error format outputter
+	  runner.setOutputter( new CppUnit::CompilerOutputter( &runner.result(),
+	                                                       std::cerr ) );
+	  // Run the tests.
+	  bool wasSucessful = runner.run();
+
+	  // Return error code 1 if the one of test failed.
+	  return wasSucessful ? 0 : 1;
+}
+
+
+
+
diff --git a/win32/libauxtools/libauxtools.vcproj b/win32/libauxtools/libauxtools.vcproj
index 2d8e3b158..309eeba4a 100644
--- a/win32/libauxtools/libauxtools.vcproj
+++ b/win32/libauxtools/libauxtools.vcproj
@@ -229,10 +229,18 @@
 				RelativePath="$(ProjectDir)\..\..\tsk3\base\tsk_error.c"
 				>
 			</File>
+			<File
+				RelativePath="$(ProjectDir)\..\..\tsk3\base\tsk_error_win32.cpp"
+				>
+			</File>
 			<File
 				RelativePath="$(ProjectDir)\..\..\tsk3\base\tsk_list.c"
 				>
 			</File>
+			<File
+				RelativePath="..\..\tsk3\base\tsk_lock.c"
+				>
+			</File>
 			<File
 				RelativePath="$(ProjectDir)\..\..\tsk3\base\tsk_parse.c"
 				>
diff --git a/win32/libfstools/libfstools.vcproj b/win32/libfstools/libfstools.vcproj
index f04cf1c06..62f25d84b 100644
--- a/win32/libfstools/libfstools.vcproj
+++ b/win32/libfstools/libfstools.vcproj
@@ -376,6 +376,10 @@
 				RelativePath="..\..\tsk3\fs\unix_misc.c"
 				>
 			</File>
+			<File
+				RelativePath="..\..\tsk3\fs\walk_cpp.cpp"
+				>
+			</File>
 		</Filter>
 		<Filter
 			Name="Header Files"
diff --git a/win32/tsk-win.sln b/win32/tsk-win.sln
index 87c91e0e6..6310c0f59 100644
--- a/win32/tsk-win.sln
+++ b/win32/tsk-win.sln
@@ -195,9 +195,29 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tsk_comparedir", "tsk_compa
 		{2D0B32C2-B035-4873-8E9A-4D4221D93F68} = {2D0B32C2-B035-4873-8E9A-4D4221D93F68}
 	EndProjectSection
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "callback-cpp-sample", "callback-cpp-sample\callback-cpp-sample.vcproj", "{3B32F1BE-9686-4DC9-8197-F734D146E9F8}"
+	ProjectSection(ProjectDependencies) = postProject
+		{F12AA46A-EA91-4AB6-8246-7800DF6128A4} = {F12AA46A-EA91-4AB6-8246-7800DF6128A4}
+		{47901C6F-77EC-4753-A83A-F6AE975851B9} = {47901C6F-77EC-4753-A83A-F6AE975851B9}
+		{47E72886-0B51-4098-BAF4-68B4494F0872} = {47E72886-0B51-4098-BAF4-68B4494F0872}
+		{2D0B32C2-B035-4873-8E9A-4D4221D93F68} = {2D0B32C2-B035-4873-8E9A-4D4221D93F68}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "posix-cpp-sample", "posix-cpp-sample\posix-cpp-sample.vcproj", "{5594DC0E-191C-4F2A-83FE-97F53A9C1222}"
+	ProjectSection(ProjectDependencies) = postProject
+		{F12AA46A-EA91-4AB6-8246-7800DF6128A4} = {F12AA46A-EA91-4AB6-8246-7800DF6128A4}
+		{47901C6F-77EC-4753-A83A-F6AE975851B9} = {47901C6F-77EC-4753-A83A-F6AE975851B9}
+		{47E72886-0B51-4098-BAF4-68B4494F0872} = {47E72886-0B51-4098-BAF4-68B4494F0872}
+		{2D0B32C2-B035-4873-8E9A-4D4221D93F68} = {2D0B32C2-B035-4873-8E9A-4D4221D93F68}
+	EndProjectSection
+EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tsk_gettimes", "tsk_gettimes\tsk_gettimes.vcproj", "{11A8927C-F971-4104-A286-5DC11C25E2EC}"
 	ProjectSection(ProjectDependencies) = postProject
+		{F12AA46A-EA91-4AB6-8246-7800DF6128A4} = {F12AA46A-EA91-4AB6-8246-7800DF6128A4}
+		{47901C6F-77EC-4753-A83A-F6AE975851B9} = {47901C6F-77EC-4753-A83A-F6AE975851B9}
+		{47E72886-0B51-4098-BAF4-68B4494F0872} = {47E72886-0B51-4098-BAF4-68B4494F0872}
 		{88B5F49E-6EBE-4FEA-8E5B-BE1F0DF7E8E8} = {88B5F49E-6EBE-4FEA-8E5B-BE1F0DF7E8E8}
+		{2D0B32C2-B035-4873-8E9A-4D4221D93F68} = {2D0B32C2-B035-4873-8E9A-4D4221D93F68}
 	EndProjectSection
 EndProject
 Global
@@ -387,8 +407,20 @@ Global
 		{8EE881F4-78DC-49C7-8845-E842358AC0FA}.Debug|Win32.Build.0 = Debug|Win32
 		{8EE881F4-78DC-49C7-8845-E842358AC0FA}.Release|Win32.ActiveCfg = Release|Win32
 		{8EE881F4-78DC-49C7-8845-E842358AC0FA}.Release|Win32.Build.0 = Release|Win32
-		{11A8927C-F971-4104-A286-5DC11C25E2EC}.Debug_NoLibs|Win32.ActiveCfg = Debug|Win32
-		{11A8927C-F971-4104-A286-5DC11C25E2EC}.Debug_NoLibs|Win32.Build.0 = Debug|Win32
+		{3B32F1BE-9686-4DC9-8197-F734D146E9F8}.Debug_NoLibs|Win32.ActiveCfg = Debug_NoLibs|Win32
+		{3B32F1BE-9686-4DC9-8197-F734D146E9F8}.Debug_NoLibs|Win32.Build.0 = Debug_NoLibs|Win32
+		{3B32F1BE-9686-4DC9-8197-F734D146E9F8}.Debug|Win32.ActiveCfg = Debug|Win32
+		{3B32F1BE-9686-4DC9-8197-F734D146E9F8}.Debug|Win32.Build.0 = Debug|Win32
+		{3B32F1BE-9686-4DC9-8197-F734D146E9F8}.Release|Win32.ActiveCfg = Release|Win32
+		{3B32F1BE-9686-4DC9-8197-F734D146E9F8}.Release|Win32.Build.0 = Release|Win32
+		{5594DC0E-191C-4F2A-83FE-97F53A9C1222}.Debug_NoLibs|Win32.ActiveCfg = Debug_NoLibs|Win32
+		{5594DC0E-191C-4F2A-83FE-97F53A9C1222}.Debug_NoLibs|Win32.Build.0 = Debug_NoLibs|Win32
+		{5594DC0E-191C-4F2A-83FE-97F53A9C1222}.Debug|Win32.ActiveCfg = Debug|Win32
+		{5594DC0E-191C-4F2A-83FE-97F53A9C1222}.Debug|Win32.Build.0 = Debug|Win32
+		{5594DC0E-191C-4F2A-83FE-97F53A9C1222}.Release|Win32.ActiveCfg = Release|Win32
+		{5594DC0E-191C-4F2A-83FE-97F53A9C1222}.Release|Win32.Build.0 = Release|Win32
+		{11A8927C-F971-4104-A286-5DC11C25E2EC}.Debug_NoLibs|Win32.ActiveCfg = Debug_NoLibs|Win32
+		{11A8927C-F971-4104-A286-5DC11C25E2EC}.Debug_NoLibs|Win32.Build.0 = Debug_NoLibs|Win32
 		{11A8927C-F971-4104-A286-5DC11C25E2EC}.Debug|Win32.ActiveCfg = Debug|Win32
 		{11A8927C-F971-4104-A286-5DC11C25E2EC}.Debug|Win32.Build.0 = Debug|Win32
 		{11A8927C-F971-4104-A286-5DC11C25E2EC}.Release|Win32.ActiveCfg = Release|Win32
diff --git a/win32/tsk_comparedir/tsk_compare.vcproj b/win32/tsk_comparedir/tsk_compare.vcproj
index b0c705958..db5d37781 100644
--- a/win32/tsk_comparedir/tsk_compare.vcproj
+++ b/win32/tsk_comparedir/tsk_compare.vcproj
@@ -41,6 +41,7 @@
 				Name="VCCLCompilerTool"
 				Optimization="0"
 				AdditionalIncludeDirectories="$(ProjectDir)\..\..\"
+				PreprocessorDefinitions="_CRT_SECURE_NO_DEPRECATE"
 				MinimalRebuild="true"
 				BasicRuntimeChecks="3"
 				RuntimeLibrary="3"
@@ -182,6 +183,7 @@
 			<Tool
 				Name="VCCLCompilerTool"
 				AdditionalIncludeDirectories="$(ProjectDir)\..\..\"
+				PreprocessorDefinitions="_CRT_SECURE_NO_DEPRECATE"
 				MinimalRebuild="true"
 				BasicRuntimeChecks="3"
 				RuntimeLibrary="3"
diff --git a/win32/tsk_gettimes/tsk_gettimes.vcproj b/win32/tsk_gettimes/tsk_gettimes.vcproj
index 3730725c6..626b3c846 100755
--- a/win32/tsk_gettimes/tsk_gettimes.vcproj
+++ b/win32/tsk_gettimes/tsk_gettimes.vcproj
@@ -43,7 +43,7 @@
 				Name="VCCLCompilerTool"
 				Optimization="0"
 				AdditionalIncludeDirectories="$(ProjectDir)\..\..\"
-				PreprocessorDefinitions="WIN32;_DEBUG"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CRT_SECURE_NO_DEPRECATE"
 				RuntimeLibrary="3"
 				UsePrecompiledHeader="0"
 				WarningLevel="3"
@@ -116,7 +116,7 @@
 			<Tool
 				Name="VCCLCompilerTool"
 				AdditionalIncludeDirectories="$(ProjectDir)\..\..\"
-				PreprocessorDefinitions="WIN32;NDEBUG"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CRT_SECURE_NO_DEPRECATE"
 				RuntimeLibrary="2"
 				UsePrecompiledHeader="0"
 				WarningLevel="3"
@@ -161,6 +161,78 @@
 				Name="VCPostBuildEventTool"
 			/>
 		</Configuration>
+		<Configuration
+			Name="Debug_NoLibs|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			ManagedExtensions="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="$(ProjectDir)\..\..\"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CRT_SECURE_NO_DEPRECATE"
+				RuntimeLibrary="3"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				AdditionalLibraryDirectories=""
+				GenerateDebugInformation="true"
+				AssemblyDebug="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
 	</Configurations>
 	<References>
 		<AssemblyReference
diff --git a/win32/tsk_recover/tsk_recover.vcproj b/win32/tsk_recover/tsk_recover.vcproj
index 55eb604a3..a3d967214 100644
--- a/win32/tsk_recover/tsk_recover.vcproj
+++ b/win32/tsk_recover/tsk_recover.vcproj
@@ -42,7 +42,7 @@
 				Name="VCCLCompilerTool"
 				Optimization="0"
 				AdditionalIncludeDirectories="$(ProjectDir)\..\..\"
-				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE"
 				MinimalRebuild="true"
 				BasicRuntimeChecks="3"
 				RuntimeLibrary="3"
@@ -118,7 +118,7 @@
 				Optimization="2"
 				EnableIntrinsicFunctions="true"
 				AdditionalIncludeDirectories="$(ProjectDir)\..\..\"
-				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE"
 				RuntimeLibrary="2"
 				EnableFunctionLevelLinking="true"
 				UsePrecompiledHeader="0"
@@ -172,7 +172,7 @@
 			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
 			IntermediateDirectory="$(ConfigurationName)"
 			ConfigurationType="1"
-            ManagedExtensions="0"
+			ManagedExtensions="0"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -192,11 +192,11 @@
 			<Tool
 				Name="VCCLCompilerTool"
 				AdditionalIncludeDirectories="$(ProjectDir)\..\..\"
-                PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
-                MinimalRebuild="true"
-                BasicRuntimeChecks="3"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
 				RuntimeLibrary="3"
-                DebugInformationFormat="4"
+				DebugInformationFormat="4"
 			/>
 			<Tool
 				Name="VCManagedResourceCompilerTool"
@@ -212,9 +212,6 @@
 			/>
 			<Tool
 				Name="VCALinkTool"
-                LinkIncremental="2"
-                GenerateDebugInformation="true"
-                AssemblyDebug="1"
 			/>
 			<Tool
 				Name="VCManifestTool"
diff --git a/xcode/sleuthkit.xcodeproj/project.pbxproj b/xcode/sleuthkit.xcodeproj/project.pbxproj
index e728a1fdd..d2a7a6097 100644
--- a/xcode/sleuthkit.xcodeproj/project.pbxproj
+++ b/xcode/sleuthkit.xcodeproj/project.pbxproj
@@ -157,6 +157,7 @@
 		02F9DE670D9BFCF2009C3F0E /* mmls.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = mmls.cpp; sourceTree = "<group>"; };
 		02F9DE680D9BFCF2009C3F0E /* mmstat.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = mmstat.cpp; sourceTree = "<group>"; };
 		D721CF8A126B5A3500AA2B16 /* tsk_gettimes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tsk_gettimes.cpp; sourceTree = "<group>"; };
+		D7864D7A134E2EFC00036A41 /* tsk_lock.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tsk_lock.c; sourceTree = "<group>"; };
 		D79CAB5F1215B2A4004F70CE /* tsk_auto_i.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tsk_auto_i.h; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
@@ -271,6 +272,7 @@
 		026FB38C0D19C867000434C7 /* base */ = {
 			isa = PBXGroup;
 			children = (
+				D7864D7A134E2EFC00036A41 /* tsk_lock.c */,
 				026FB3B20D19C868000434C7 /* md5c.c */,
 				026FB3B50D19C868000434C7 /* mymalloc.c */,
 				026FB3B80D19C868000434C7 /* sha1c.c */,
-- 
GitLab