[Forensics-changes] [SCM] debian-forensics/reglookup branch, upstream, updated. upstream/0.12.0-1-ge565885
Elías Alejandro
ealmdz at gmail.com
Sat Oct 8 21:11:23 UTC 2011
The following commit has been merged in the upstream branch:
commit e565885c4076c1428c14ba99bb12d67918cb880b
Author: Elías Alejandro <ealmdz at gmail.com>
Date: Sat Oct 8 15:28:08 2011 -0500
Imported Upstream version 1.0.1
diff --git a/.sconsign.dblite b/.sconsign.dblite
new file mode 100644
index 0000000..ca20cd0
Binary files /dev/null and b/.sconsign.dblite differ
diff --git a/INSTALL b/INSTALL
index 468ffc5..d3bef5a 100644
--- a/INSTALL
+++ b/INSTALL
@@ -4,11 +4,13 @@ RegLookup Installation
Prerequisites
-------------
-This package doesn't require much for installation. Just what typically
-comes with any free operating system. Be sure you have:
+RegLookup and its associated libraries have the following build dependencies:
- - GNU Make
+ - SCons (package "scons" in most popular distributions or from http://www.scons.org/)
- GCC
+ - talloc 2.x (under Debian, "libtalloc2" and "libtalloc-dev")
+ - Python 2 (2.6+) or Python 3 (Python is required for SCons anyway)
+ - Doxygen (optional, only needed to build developer documentation)
Note that iconv support is required, as specified in IEEE Std 1003.1
(POSIX.1-2001). Some platforms still do not contain support for this
@@ -19,36 +21,15 @@ natively, in which case you may need to install libiconv from:
Survival Commands
-----------------
-make
+scons
# and as root
-make install
-
-If GNU Make isn't your default, you may need to run `gmake' instead.
+scons install
Advanced Installation
---------------------
-To install in a custom directory, simply change one or more of the
-following make variables to suit your needs:
-
- PREFIX Top level install directory. Used as base for rest unless
- they too are overridden. (This defaults to /usr/local)
-
- BIN_PREFIX Location for executable programs to be installed.
-
- DOC_PREFIX Location for documentation.
-
- MAN_PREFIX A path in the MANPATH.
-
-
-Use the following syntax (with GNU Make) to override a variable:
-
-# as root
-make VAR=value install
-
-
-For more information, see the Makefiles.
+By default, most RegLookup files are installed in the appropriate
+directories under /usr/local. In order to override this behavior, set
+the PREFIX environment variable to the desired path. For example:
-Primitive build features have been added to support cross-compiling to
-Windows binaries using MinGW. For more information on this, see:
- doc/mingw-build.txt
+$ PREFIX=/home/myuser/reglookup scons install
diff --git a/Makefile b/Makefile
deleted file mode 100644
index a19a61f..0000000
--- a/Makefile
+++ /dev/null
@@ -1,76 +0,0 @@
-# $Id: Makefile 159 2009-12-06 20:09:01Z tim $
-
-# Installation prefixes. Change to install elsewhere.
-
-PREFIX=/usr/local
-BIN_PREFIX=$(PREFIX)/bin
-DOC_PREFIX=$(PREFIX)/share/doc/reglookup
-MAN_PREFIX=$(PREFIX)/man
-
-################################################################################
-
-CC=gcc
-OPTS=-std=gnu99 -pedantic -Wall -ggdb
-#OPTS=-std=gnu99 -pedantic -Wall
-INC:=-I$(PWD)/include -I/usr/local/include
-LIB=-L/usr/local/lib -lm
-BIN_EXT=
-EXTRA_OBJ=
-
-UNAME := $(shell uname)
-ifneq ($(UNAME),Linux)
- LIB:=$(LIB) -liconv
-endif
-
-
-################################################################################
-# MinGW cross-compiling build settings
-ifdef BUILD_MINGW
-
-## These may need to be changed
-CC=i586-mingw32msvc-cc
-LIBICONV_PATH=/usr/local/src/libiconv-1.9.2-1-lib
-
-## These probably do not need to be changed
-BIN_EXT=.exe
-INC:=$(INC) -I$(LIBICONV_PATH)/include
-EXTRA_OBJ=$(LIBICONV_PATH)/lib/libiconv.dll.a
-
-endif
-################################################################################
-
-
-BUILD=$(CURDIR)/build
-BUILD_BIN=$(BUILD)/bin
-BUILD_DOC=$(BUILD)/doc
-
-BUILD_TREE=$(BUILD_BIN) $(BUILD_DOC)
-SUB_DIRS=lib src doc bin
-
-FILES=$(REGLOOKUP)
-.PHONY: $(SUB_DIRS) clean
-export
-
-
-all: $(BUILD_TREE) $(SUB_DIRS)
-
-#XXX: This should be more generalized.
-install: all
- mkdir -p $(BIN_PREFIX)
- mkdir -p $(DOC_PREFIX)
- mkdir -p $(MAN_PREFIX)/man1
- $(MAKE) -C bin install
- $(MAKE) -C src install
- $(MAKE) -C doc install
-
-
-$(SUB_DIRS):
- $(MAKE) -C $@
-
-$(BUILD_TREE):
- mkdir -p $@
-
-clean:
- $(MAKE) -C src clean
- $(MAKE) -C lib clean
- rm -rf $(BUILD)/*
diff --git a/SConstruct b/SConstruct
new file mode 100644
index 0000000..3222a2d
--- /dev/null
+++ b/SConstruct
@@ -0,0 +1,97 @@
+import sys
+import os
+sys.dont_write_bytecode = True
+from regfi_version import REGFI_VERSION
+
+cflags = '-std=gnu99 -pedantic -Wall -D_FILE_OFFSET_BITS=64 -fvisibility=hidden'
+cflags += ' -DREGFI_VERSION=\'"%s"\'' % REGFI_VERSION
+cflags += ' -ggdb'
+
+lib_src = ['lib/regfi.c',
+ 'lib/winsec.c',
+ 'lib/range_list.c',
+ 'lib/lru_cache.c',
+ 'lib/void_stack.c']
+
+cc=os.environ.get('CC', 'gcc')
+env = Environment(ENV=os.environ,
+ CC=cc,
+ CFLAGS=cflags,
+ CPPPATH=['include', '/usr/local/include'],
+ LIBPATH=['lib', '/usr/local/lib'],
+ LIBS=['m', 'pthread', 'regfi', 'talloc'])
+
+# Libraries
+libregfi_static = env.Library(lib_src)
+libregfi = env.SharedLibrary(lib_src, LIBS=['m','pthread', 'talloc'])
+
+
+# Executables
+reglookup = env.Program(['src/reglookup.c'])
+reglookup_recover = env.Program(['src/reglookup-recover.c'])
+
+
+# Documentation
+# This only needs to be run during the release/packaging process
+man_fixup = "|sed 's/.SH DESCRIPTION/\\n.SH DESCRIPTION/'"
+man_builder = Builder(action='docbook2x-man --to-stdout $SOURCE'
+ + man_fixup + '| gzip -9 > $TARGET',
+ suffix = '.gz',
+ src_suffix = '.docbook')
+env['BUILDERS']['ManPage'] = man_builder
+
+man_reglookup = env.ManPage('doc/reglookup.1.docbook')
+man_reglookup_recover = env.ManPage('doc/reglookup-recover.1.docbook')
+man_reglookup_timeline = env.ManPage('doc/reglookup-timeline.1.docbook')
+
+# Installation
+prefix='/usr/local/'
+if 'PREFIX' in os.environ:
+ prefix = os.environ['PREFIX']+'/'
+
+install_items = [prefix+'bin',
+ prefix+'lib',
+ prefix+'include/regfi',
+ prefix+'man']
+
+env.Install(prefix+'bin', [reglookup, reglookup_recover, 'bin/reglookup-timeline'])
+libinstall = env.Install(prefix+'lib', [libregfi, libregfi_static])
+env.Install(prefix+'include/regfi', Glob('include/*.h'))
+env.Install(prefix+'man/man1', [man_reglookup, man_reglookup_recover,
+ man_reglookup_timeline])
+env.AddPostAction(libinstall, 'ldconfig')
+
+if sys.version_info[0] == 2:
+ install_items.append('pyregfi2-install.log')
+ env.Command('pyregfi2-install.log', ['python/pyregfi/__init__.py',
+ 'python/pyregfi/structures.py',
+ 'python/pyregfi/winsec.py'],
+ "python pyregfi-distutils.py install | tee pyregfi2-install.log")
+
+python_path = os.popen('which python3').read()
+if python_path != '':
+ install_items.append('pyregfi3-install.log')
+ env.Command('pyregfi3-install.log', ['python/pyregfi/__init__.py',
+ 'python/pyregfi/structures.py',
+ 'python/pyregfi/winsec.py'],
+ "python3 pyregfi-distutils.py install | tee pyregfi3-install.log")
+
+# API documentation
+regfi_doc = env.Command('doc/devel/regfi/index.html',
+ Glob('lib/*.c')+Glob('include/*.h')+['doc/devel/Doxyfile.regfi'],
+ 'doxygen doc/devel/Doxyfile.regfi')
+pyregfi_doc = env.Command('doc/devel/pyregfi/index.html',
+ Glob('python/pyregfi/*.py')+['doc/devel/Doxyfile.pyregfi', regfi_doc],
+ 'doxygen doc/devel/Doxyfile.pyregfi')
+
+
+# User Friendly Targets
+env.Alias('libregfi', libregfi)
+env.Alias('reglookup', reglookup)
+env.Alias('reglookup-recover', reglookup_recover)
+env.Alias('bin', [reglookup_recover, reglookup])
+env.Alias('doc', [man_reglookup,man_reglookup_recover,man_reglookup_timeline])
+env.Alias('doc-devel', [regfi_doc, pyregfi_doc])
+env.Alias('install', install_items)
+
+Default('bin', libregfi)
diff --git a/bin/Makefile b/bin/Makefile
deleted file mode 100644
index 96420a7..0000000
--- a/bin/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-# $Id: Makefile 90 2007-03-28 19:22:38Z tim $
-
-################################################################################
-
-TIMELINE=$(BUILD_BIN)/reglookup-timeline
-FILES=$(TIMELINE)
-
-all: $(FILES)
-
-$(TIMELINE):
- cp reglookup-timeline $@
-
-install:
- install -m 0755 $(FILES) $(BIN_PREFIX)
-
-clean:
- rm -f *~
diff --git a/doc/Makefile b/doc/Makefile
deleted file mode 100644
index 39ef109..0000000
--- a/doc/Makefile
+++ /dev/null
@@ -1,35 +0,0 @@
-# $Id: Makefile 155 2009-06-03 23:45:58Z tim $
-
-BUILD_FILES=$(BUILD_DOC)/man/man1/reglookup.1.gz\
- $(BUILD_DOC)/man/man1/reglookup-timeline.1.gz\
- $(BUILD_DOC)/man/man1/reglookup-recover.1.gz
-
-default: $(BUILD_FILES)
-
-$(BUILD_DOC)/man/man1:
- mkdir -p $(BUILD_DOC)/man/man1
-
-$(BUILD_DOC)/man/man1/reglookup.1.gz: man/man1/reglookup.1.gz $(BUILD_DOC)/man/man1
- cp man/man1/reglookup.1.gz $@
-
-$(BUILD_DOC)/man/man1/reglookup-timeline.1.gz: man/man1/reglookup-timeline.1.gz $(BUILD_DOC)/man/man1
- cp man/man1/reglookup-timeline.1.gz $@
-
-$(BUILD_DOC)/man/man1/reglookup-recover.1.gz: man/man1/reglookup-recover.1.gz $(BUILD_DOC)/man/man1
- cp man/man1/reglookup-recover.1.gz $@
-
-install:
- cp -r $(BUILD_DOC)/* $(DOC_PREFIX)
- ln -sf $(DOC_PREFIX)/man/man1/* $(MAN_PREFIX)/man1
-
-
-#XXX: Used during release only
-release:
- #XXX: The sed filter is a temporary hack to work around a bug in docbook2x-man
- # See: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=516165
- docbook2x-man --to-stdout reglookup.1.docbook | sed 's/.SH DESCRIPTION/\n.SH DESCRIPTION/' > man/man1/reglookup.1
- docbook2x-man --to-stdout reglookup-timeline.1.docbook | sed 's/.SH DESCRIPTION/\n.SH DESCRIPTION/' > man/man1/reglookup-timeline.1
- docbook2x-man --to-stdout reglookup-recover.1.docbook | sed 's/.SH DESCRIPTION/\n.SH DESCRIPTION/' > man/man1/reglookup-recover.1
- cd man/man1 && gzip -9 -f reglookup.1
- cd man/man1 && gzip -9 -f reglookup-timeline.1
- cd man/man1 && gzip -9 -f reglookup-recover.1
diff --git a/doc/devel/Doxyfile.pyregfi b/doc/devel/Doxyfile.pyregfi
new file mode 100644
index 0000000..e22446b
--- /dev/null
+++ b/doc/devel/Doxyfile.pyregfi
@@ -0,0 +1,1570 @@
+# Doxyfile 1.6.2-20100208
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = pyregfi
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER =
+
+# 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 = doc/devel/pyregfi
+
+# 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
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = YES
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it parses.
+# With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this tag.
+# The format is ext=language, where ext is a file extension, and language is one of
+# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
+# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
+# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C. Note that for custom extensions you also need to set
+# FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = YES
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penality.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will rougly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = YES
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = YES
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = NO
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort
+# the (brief and detailed) documentation of class members so that constructors
+# and destructors are listed first. If set to NO (the default) the
+# constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief
+# docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if
+# SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
+# doxygen. The layout file controls the global structure of the generated output files
+# in an output format independent way. The create the layout file that represents
+# doxygen's defaults, run doxygen with the -l option. You can optionally specify a
+# file name after the option, if omitted DoxygenLayout.xml will be used as the name
+# of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = python/pyregfi
+
+# 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
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS = *.py
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS = talloc*
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = .
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = NO
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for
+# more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
+# are set, an additional index file will be generated that can be used as input for
+# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
+# HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
+# For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add.For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted
+# before the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using HTML
+# help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client using
+# Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server based approach is
+# that it scales better to large projects and allows full text search. The
+# disadvances is that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include source code
+# with syntax highlighting in the LaTeX output. Note that which sources are
+# shown also depends on other settings such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES = doc/devel/regfi/regfi.tags=../regfi
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = YES
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = FreeSans
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = NO
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = NO
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = NO
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = YES
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
diff --git a/doc/devel/Doxyfile.regfi b/doc/devel/Doxyfile.regfi
new file mode 100644
index 0000000..78efa53
--- /dev/null
+++ b/doc/devel/Doxyfile.regfi
@@ -0,0 +1,1570 @@
+# Doxyfile 1.6.2-20100208
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = regfi
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER =
+
+# 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 = doc/devel/regfi
+
+# 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
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it parses.
+# With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this tag.
+# The format is ext=language, where ext is a file extension, and language is one of
+# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
+# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
+# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C. Note that for custom extensions you also need to set
+# FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = YES
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penality.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will rougly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = YES
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = YES
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = NO
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort
+# the (brief and detailed) documentation of class members so that constructors
+# and destructors are listed first. If set to NO (the default) the
+# constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief
+# docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if
+# SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
+# doxygen. The layout file controls the global structure of the generated output files
+# in an output format independent way. The create the layout file that represents
+# doxygen's defaults, run doxygen with the -l option. You can optionally specify a
+# file name after the option, if omitted DoxygenLayout.xml will be used as the name
+# of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = lib include
+
+# 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
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS = *.c *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS = talloc*
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = .
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = NO
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for
+# more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
+# are set, an additional index file will be generated that can be used as input for
+# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
+# HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
+# For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add.For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted
+# before the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using HTML
+# help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client using
+# Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server based approach is
+# that it scales better to large projects and allows full text search. The
+# disadvances is that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include source code
+# with syntax highlighting in the LaTeX output. Note that which sources are
+# shown also depends on other settings such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE = doc/devel/regfi/regfi.tags
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = YES
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = FreeSans
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = NO
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = NO
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = YES
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
diff --git a/doc/devel/README b/doc/devel/README
index 2960276..e0040ec 100644
--- a/doc/devel/README
+++ b/doc/devel/README
@@ -1,4 +1,5 @@
-Developer's documentation previously stored here has been removed from the
-main software distribution trunk. It is still available in the Subversion
-repository, however, and may be obtained here:
- http://code.google.com/p/reglookup/source/browse/
+This directory contains technical information on the registry format,
+RegLookup design goals and programmer's API usage information. For
+information on how to use the RegLookup tools, refer to the doc
+directory and man pages included in the main release distribution or
+Subversion trunk directory.
diff --git a/doc/devel/TODO b/doc/devel/TODO
new file mode 100644
index 0000000..176545b
--- /dev/null
+++ b/doc/devel/TODO
@@ -0,0 +1,62 @@
+$Id: TODO 262 2011-06-17 17:51:31Z tim $
+
+If you are interested in contributing to this project, here's a few
+things you could look into:
+
+ - Currently there is no way on the command line to search for exotic
+ paths/types. For instance, if reglookup encounters an unknown VK
+ type, it just prints it out in Hex. However, if you wanted to search
+ specifically for that type, there is no way to do it. Similarly, it
+ isn't possible to specify certain binary or weird characters in
+ paths. Reglookup should take the user path and unquote each path
+ component using the \xQQ syntax prior to searching.
+
+ - It might be nice to have a way to filter results by security
+ descriptor information. Maybe by MTIME as well.
+
+ - reglookup-timeline needs to be replaced with something cross-platform.
+ Perhaps a python script that provides MTIME range filtering capabilities.
+
+ - Need to integrate much of reglookup-recover's algorithms into regfi
+ and then expose them from the bottom-up to provide building blocks
+ through regfi and pyregfi. This should be addressed along with code
+ to support handling of partial/fragmented registry hives.
+
+ - Testing, testing, and more testing. reglookup needs to be more
+ heavily tested on all recent Windows platforms. A regression test
+ suite would be nice too. Some thoughts on this include a script
+ which randomly fuzzes an existing registry file, and tries to detect
+ crashes of reglookup when parsing it. Another test script might
+ randomly truncate an existing registry file, which will help improve
+ reglookup's parsing on fragmentary files.
+
+ - Unicode support still needs improvement. While parsing strings seems
+ to be decent, UTF-8 output would be nice.
+
+ - Continue to improve regfi/pyregfi APIs as needed. winsec library needs more
+ flexibility and documentation.
+
+ - Consider adding regfi wrappers for other high-level languages (perl? ruby?).
+
+ - Documentation. The security descriptor output format needs to be
+ documented. Also, function contracts should be added to the
+ lower-level functions of regfi.c. Continue adding
+
+ - Consider switching from libiconv to Joachim Metz's libuna for
+ increased portability and easier builds.
+
+ - Grep through the source for 'XXX', and you'll find more.
+
+ - Consider integrating packaging rules for debian/other platforms into trunk.
+
+ - Investigate why file descriptors can't be directly used in Windows
+
+
+1.0 RELEASE
+===========
+
+Testing
+ Full diffs
+ regfi and pyregfi threading
+ valgrind in multiple scenarios for reglookup, reglookup-recover
+ double check man pages
diff --git a/doc/mingw-build.txt b/doc/devel/mingw-build-OBSOLETE.txt
similarity index 100%
rename from doc/mingw-build.txt
rename to doc/devel/mingw-build-OBSOLETE.txt
diff --git a/doc/devel/references.txt b/doc/devel/references.txt
new file mode 100644
index 0000000..f63a0b2
--- /dev/null
+++ b/doc/devel/references.txt
@@ -0,0 +1,34 @@
+- The Windows NT Registry File Format
+ (A work in progress to support this tool.)
+ http://sentinelchicken.com/research/registry_format/
+
+- Recovering Deleted Data From the Windows Registry
+ (The research that is implemented as a PoC in reglookup-recover.)
+ http://sentinelchicken.com/research/registry_recovery/
+
+- Petter Nordahl-Hagen. Windows NT registry file format description.
+ (The file 'winntreg.txt' included in this distribution is derived from this.)
+ http://home.eunet.no/~pnordahl/ntpasswd/WinReg.txt
+
+- Nigel Williams. Much of the same information as provided in 'winntreg.txt',
+ but with some code:
+ http://www.wednesday.demon.co.uk/dosreg.html
+
+- Some useful information on how Windows reads from and writes to registry
+ hives:
+ http://www.microsoft.com/technet/archive/winntas/tips/winntmag/inreg.mspx
+
+- Registry key, value, and depth limits:
+ http://msdn2.microsoft.com/en-us/library/ms724872.aspx
+
+- Misc references for windows registry permissions and ownership:
+ http://msdn2.microsoft.com/en-gb/library/ms724878.aspx
+ http://technet2.microsoft.com/WindowsServer/en/library/86cf2457-4f17-43f8-a2ab-7f4e2e5659091033.mspx?mfr=true
+ http://msdn2.microsoft.com/en-gb/library/aa374892.aspx
+
+- ACL/ACE flags information
+ http://support.microsoft.com/kb/220167
+ http://msdn2.microsoft.com/en-us/library/aa772242.aspx
+
+- Info on SAM hive, syskey, and hash extraction (with tools bkhive and samdump2):
+ http://www.studenti.unina.it/~ncuomo/syskey/
diff --git a/doc/devel/winntreg.txt b/doc/devel/winntreg.txt
new file mode 100644
index 0000000..31bbdf1
--- /dev/null
+++ b/doc/devel/winntreg.txt
@@ -0,0 +1,272 @@
+The windows NT registry has 2 different blocks, where one can occur many
+times...
+
+the "regf"-Block
+================
+
+"regf" is obviosly the abbreviation for "Registry file". "regf" is the
+signature of the header-block which is always 4kb in size, although only
+the first 64 bytes seem to be used and a checksum is calculated over
+the first 0x200 bytes only!
+
+Offset Size Contents
+0x00000000 D-Word ID: ASCII-"regf" = 0x66676572
+0x00000004 D-Word ???? //see struct REGF
+0x00000008 D-Word ???? Always the same value as at 0x00000004
+0x0000000C Q-Word last modify date in WinNT date-format
+0x00000014 D-Word 1
+0x00000018 D-Word 3
+0x0000001C D-Word 0
+0x00000020 D-Word 1
+0x00000024 D-Word Offset of 1st key record
+0x00000028 D-Word Size of the data-blocks (Filesize-4kb)
+0x0000002C D-Word 1
+0x000001FC D-Word Sum of all D-Words from 0x00000000 to
+ 0x000001FB //XOR of all words. Nigel
+
+I have analyzed more registry files (from multiple machines running
+NT 4.0 german version) and could not find an explanation for the values
+marked with ???? the rest of the first 4kb page is not important...
+
+the "hbin"-Block
+================
+I dont know what "hbin" stands for, but this block is always a multiple
+of 4kb in size.
+
+Inside these hbin-blocks the different records are placed. The memory-
+management looks like a C-compiler heap management to me...
+
+hbin-Header
+===========
+Offset Size Contents
+0x0000 D-Word ID: ASCII-"hbin" = 0x6E696268
+0x0004 D-Word Offset from the 1st hbin-Block
+0x0008 D-Word Offset to the next hbin-Block
+0x001C D-Word Block-size
+
+The values in 0x0008 and 0x001C should be the same, so I dont know
+if they are correct or swapped...
+
+From offset 0x0020 inside a hbin-block data is stored with the following
+format:
+
+Offset Size Contents
+0x0000 D-Word Data-block size //this size must be a
+multiple of 8. Nigel
+0x0004 ???? Data
+
+If the size field is negative (bit 31 set), the corresponding block
+is free and has a size of -blocksize!
+
+That does not seem to be true. All block lengths seem to be negative!
+(Richard Sharpe)
+
+The data is stored as one record per block. Block size is a multiple
+of 4 and the last block reaches the next hbin-block, leaving no room.
+
+(That also seems incorrect, in that the block size if a multiple of 8.
+That is, the block, including the 4 byte header, is always a multiple of
+8 bytes. Richard Sharpe.)
+
+Records in the hbin-blocks
+==========================
+
+nk-Record
+
+ The nk-record can be treated as a kombination of tree-record and
+ key-record of the win 95 registry.
+
+lf-Record
+
+ The lf-record is the counterpart to the RGKN-record (the
+ hash-function)
+
+vk-Record
+
+ The vk-record consists information to a single value.
+
+sk-Record
+
+ sk (? Security Key ?) is the ACL of the registry.
+
+Value-Lists
+
+ The value-lists contain information about which values are inside a
+ sub-key and dont have a header.
+
+Datas
+
+ The datas of the registry are (like the value-list) stored without a
+ header.
+
+All offset-values are relative to the first hbin-block and point to the
+block-size field of the record-entry. to get the file offset, you have to add
+the header size (4kb) and the size field (4 bytes)...
+
+the nk-Record
+=============
+Offset Size Contents
+0x0000 Word ID: ASCII-"nk" = 0x6B6E
+0x0002 Word for the root-key: 0x2C, otherwise 0x20 //key symbolic links 0x10. Nigel
+0x0004 Q-Word write-date/time in windows nt notation
+0x000C D-Word UNKNOWN // added by TDM
+0x0010 D-Word Offset of Owner/Parent key
+0x0014 D-Word number of sub-Keys
+0x0018 D-Word UNKNOWN // added by TDM
+0x001C D-Word Offset of the sub-key lf-Records
+0x0020 D-Word UNKNOWN // added by TDM
+0x0024 D-Word number of values
+0x0028 D-Word Offset of the Value-List
+0x002C D-Word Offset of the sk-Record
+
+0x0030 D-Word Offset of the Class-Name //see NK structure for the use of these fields. Nigel
+0x0044 D-Word Unused (data-trash) //some kind of run time index. Does not appear to be important. Nigel
+0x0048 Word name-length
+0x004A Word class-name length
+0x004C ???? key-name
+
+the Value-List
+==============
+Offset Size Contents
+0x0000 D-Word Offset 1st Value
+0x0004 D-Word Offset 2nd Value
+0x???? D-Word Offset nth Value
+
+To determine the number of values, you have to look at the owner-nk-record!
+
+Der vk-Record
+=============
+Offset Size Contents
+0x0000 Word ID: ASCII-"vk" = 0x6B76
+0x0002 Word name length
+0x0004 D-Word length of the data //if top bit is set when offset contains data. Nigel
+0x0008 D-Word Offset of Data
+0x000C D-Word Type of value
+0x0010 Word Flag
+0x0012 Word Unused (data-trash)
+0x0014 ???? Name
+
+If bit 0 of the flag-word is set, a name is present, otherwise the value has no name (=default)
+
+If the data-size is lower 5, the data-offset value is used to store the data itself!
+
+The data-types
+==============
+Wert Beteutung
+0x0001 RegSZ: character string (in UNICODE!)
+0x0002 ExpandSZ: string with "%var%" expanding (UNICODE!)
+0x0003 RegBin: raw-binary value
+0x0004 RegDWord: Dword
+0x0007 RegMultiSZ: multiple strings, seperated with 0
+ (UNICODE!)
+
+The "lf"/"lh"/"ri"-record (hash list header)
+===============
+Offset Size Contents
+0x0000 Word ID: ASCII-"lf" = 0x666C (or "lh" or "ri")
+0x0002 Word number of keys
+0x0004 ???? Hash-Records
+
+Hash-Record
+===========
+Offset Size Contents
+0x0000 D-Word Offset of corresponding "nk"-Record
+0x0004 D-Word ASCII: the first 4 characters of the key-name, padded with 0-s. Case sensitive!
+ (the hash value may be computed differently for the various header types
+ "lf"/"lh"/"ri"/etc)
+Keep in mind, that the value at 0x0004 is used for checking the data-consistency! If you change the
+key-name you have to change the hash-value too!
+
+//These hashrecords must be sorted low to high within the lf record. Nigel.
+
+The "sk"-block
+==============
+(due to the complexity of the SAM-info, not clear jet)
+(This is just a self-relative security descriptor in the data. R Sharpe.)
+
+
+Offset Size Contents
+0x0000 Word ID: ASCII-"sk" = 0x6B73
+0x0002 Word Unused
+0x0004 D-Word Offset of previous "sk"-Record
+0x0008 D-Word Offset of next "sk"-Record
+0x000C D-Word usage-counter
+0x0010 D-Word Size of "sk"-record in bytes
+???? //standard self
+relative security desciptor. Nigel
+???? ???? Security and auditing settings...
+????
+
+The usage counter counts the number of references to this
+"sk"-record. You can use one "sk"-record for the entire registry!
+
+Windows nt date/time format
+===========================
+The time-format is a 64-bit integer which is incremented every
+0,0000001 seconds by 1 (I dont know how accurate it really is!)
+It starts with 0 at the 1st of january 1601 0:00! All values are
+stored in GMT time! The time-zone is important to get the real
+time!
+
+Common values for win95 and win-nt
+==================================
+Offset values marking an "end of list", are either 0 or -1 (0xFFFFFFFF).
+If a value has no name (length=0, flag(bit 0)=0), it is treated as the
+"Default" entry...
+If a value has no data (length=0), it is displayed as empty.
+
+simplyfied win-3.?? registry:
+=============================
+
++-----------+
+| next rec. |---+ +----->+------------+
+| first sub | | | | Usage cnt. |
+| name | | +-->+------------+ | | length |
+| value | | | | next rec. | | | text |------->+-------+
++-----------+ | | | name rec. |--+ +------------+ | xxxxx |
+ +------------+ | | value rec. |-------->+------------+ +-------+
+ v | +------------+ | Usage cnt. |
++-----------+ | | length |
+| next rec. | | | text |------->+-------+
+| first sub |------+ +------------+ | xxxxx |
+| name | +-------+
+| value |
++-----------+
+
+Greatly simplyfied structure of the nt-registry:
+================================================
+
++---------------------------------------------------------------+
+| |
+v |
++---------+ +---------->+-----------+ +----->+---------+ |
+| "nk" | | | lf-rec. | | | nk-rec. | |
+| ID | | | # of keys | | | parent |---+
+| Date | | | 1st key |--+ | .... |
+| parent | | +-----------+ +---------+
+| suk-keys|-----+
+| values |--------------------->+----------+
+| SK-rec. |---------------+ | 1. value |--> +----------+
+| class |--+ | +----------+ | vk-rec. |
++---------+ | | | .... |
+ v | | data |--> +-------+
+ +------------+ | +----------+ | xxxxx |
+ | Class name | | +-------+
+ +------------+ |
+ v
+ +---------+ +---------+
+ +----->| next sk |--->| Next sk |--+
+ | +---| prev sk |<---| prev sk | |
+ | | | .... | | ... | |
+ | | +---------+ +---------+ |
+ | | ^ |
+ | | | |
+ | +--------------------+ |
+ +----------------------------------+
+
+---------------------------------------------------------------------------
+
+Hope this helps.... (Although it was *fun* for me to uncover this things,
+ it took me several sleepless nights ;)
+
+ B.D.
diff --git a/doc/man/man1/reglookup-recover.1.gz b/doc/man/man1/reglookup-recover.1.gz
deleted file mode 100644
index 82e8ad0..0000000
Binary files a/doc/man/man1/reglookup-recover.1.gz and /dev/null differ
diff --git a/doc/man/man1/reglookup-timeline.1.gz b/doc/man/man1/reglookup-timeline.1.gz
deleted file mode 100644
index ddbb3df..0000000
Binary files a/doc/man/man1/reglookup-timeline.1.gz and /dev/null differ
diff --git a/doc/man/man1/reglookup.1.gz b/doc/man/man1/reglookup.1.gz
deleted file mode 100644
index 872fb55..0000000
Binary files a/doc/man/man1/reglookup.1.gz and /dev/null differ
diff --git a/doc/reglookup-recover.1.docbook b/doc/reglookup-recover.1.docbook
index 95f47f5..672409c 100644
--- a/doc/reglookup-recover.1.docbook
+++ b/doc/reglookup-recover.1.docbook
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<refentry id='reglookup-recover.1'>
- <!-- $Id: reglookup-recover.1.docbook 138 2009-02-08 19:53:48Z tim $ -->
+ <!-- $Id: reglookup-recover.1.docbook 264 2011-06-20 01:13:35Z tim $ -->
<refmeta>
<refentrytitle>reglookup</refentrytitle>
<manvolnum>1</manvolnum>
@@ -199,6 +199,8 @@
<para>
For more information on registry format details and the recovery
algorithm, see:
+ </para>
+ <para>
http://sentinelchicken.com/research/registry_format/
http://sentinelchicken.com/research/registry_recovery/
</para>
diff --git a/doc/reglookup-recover.1.gz b/doc/reglookup-recover.1.gz
new file mode 100644
index 0000000..f72c0cb
Binary files /dev/null and b/doc/reglookup-recover.1.gz differ
diff --git a/doc/reglookup-timeline.1.gz b/doc/reglookup-timeline.1.gz
new file mode 100644
index 0000000..0bdecee
Binary files /dev/null and b/doc/reglookup-timeline.1.gz differ
diff --git a/doc/reglookup.1.docbook b/doc/reglookup.1.docbook
index 7bf3a83..c1480eb 100644
--- a/doc/reglookup.1.docbook
+++ b/doc/reglookup.1.docbook
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<refentry id='reglookup.1'>
- <!-- $Id: reglookup.1.docbook 176 2010-03-09 03:10:10Z tim $ -->
+ <!-- $Id: reglookup.1.docbook 264 2011-06-20 01:13:35Z tim $ -->
<refmeta>
<refentrytitle>reglookup</refentrytitle>
<manvolnum>1</manvolnum>
@@ -16,7 +16,7 @@
<para>
<command>
reglookup [options] <replaceable>registry-file</replaceable>
- </command>
+ </command>
</para>
</refsect1>
@@ -67,7 +67,7 @@
and
<command>
KEY
- </command>
+ </command>.
</para>
</listitem>
</varlistentry>
@@ -185,7 +185,7 @@
and writes them to stdout. The format is designed to simplify parsing
algorithms of other tools by quoting CSV special characters using a
common hexadecimal format. Specifically, special characters or non-ascii
- bytes are converted to "\xQQ" where QQ is the hexadecimal value for
+ bytes are converted to "%XX" where XX is the hexadecimal value for
the byte.
</para>
<para>
@@ -325,11 +325,6 @@
this field is very poor.
</para>
<para>
- Backslashes are currently considered special characters, to make
- parsing easier for automated tools. However, this causes paths
- to be difficult to read by mere mortals.
- </para>
- <para>
For more information on registry format details, see:
http://sentinelchicken.com/research/registry_format/
</para>
diff --git a/doc/reglookup.1.gz b/doc/reglookup.1.gz
new file mode 100644
index 0000000..e7496fb
Binary files /dev/null and b/doc/reglookup.1.gz differ
diff --git a/include/compat.h b/include/compat.h
new file mode 100644
index 0000000..576dce1
--- /dev/null
+++ b/include/compat.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010-2011 Timothy D. Morgan
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: regfi.h 252 2011-05-08 17:33:49Z tim $
+ */
+
+#ifndef _COMPAT_H
+#define _COMPAT_H
+
+/* GCC-specific macro for library exports */
+#ifdef _EXPORT
+#undef _EXPORT
+#endif
+#ifdef REGFI_WIN32
+#define _EXPORT() __declspec(dllexport)
+#else
+#define _EXPORT() __attribute__((visibility("default")))
+#endif
+
+#ifndef EOVERFLOW
+# define EOVERFLOW E2BIG
+#endif
+
+#endif /*_COMPAT_H*/
diff --git a/include/lru_cache.h b/include/lru_cache.h
index b718bca..6dfe467 100644
--- a/include/lru_cache.h
+++ b/include/lru_cache.h
@@ -14,7 +14,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: lru_cache.h 169 2010-03-03 19:24:58Z tim $
+ * $Id: lru_cache.h 253 2011-06-13 02:27:42Z tim $
*/
/**
@@ -34,7 +34,10 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
-#include "talloc.h"
+#include <talloc.h>
+
+#include "compat.h"
+
struct lru_cache_element;
typedef struct lru_cache_element lru_cache_element;
@@ -67,12 +70,14 @@ typedef struct _lru_cache
/**
* XXX: finish documenting.
*/
+_EXPORT()
lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret);
/**
* XXX: finish documenting.
*/
+_EXPORT()
lru_cache* lru_cache_create_ctx(void* talloc_ctx, uint32_t max_keys,
uint32_t secret, bool talloc_data);
@@ -80,12 +85,14 @@ lru_cache* lru_cache_create_ctx(void* talloc_ctx, uint32_t max_keys,
/**
* XXX: finish documenting.
*/
+_EXPORT()
void lru_cache_destroy(lru_cache* ht);
/**
* XXX: finish documenting.
*/
+_EXPORT()
bool lru_cache_update(lru_cache* ht, const void* index,
uint32_t index_len, void* data);
@@ -95,6 +102,7 @@ bool lru_cache_update(lru_cache* ht, const void* index,
* @return A pointer to data previously stored at index.
* If no data was found at index, NULL is returned.
*/
+_EXPORT()
void* lru_cache_find(lru_cache* ht, const void* index,
uint32_t index_len);
@@ -106,6 +114,7 @@ void* lru_cache_find(lru_cache* ht, const void* index,
* @return A pointer to data that was there previously or NULL if no entry is
* at index.
*/
+_EXPORT()
bool lru_cache_remove(lru_cache* ht, const void* index,
uint32_t index_len);
diff --git a/include/range_list.h b/include/range_list.h
index 65c4643..356c08e 100644
--- a/include/range_list.h
+++ b/include/range_list.h
@@ -14,7 +14,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: range_list.h 169 2010-03-03 19:24:58Z tim $
+ * $Id: range_list.h 253 2011-06-13 02:27:42Z tim $
*/
/**
@@ -35,7 +35,9 @@
#include <stdint.h>
#include <string.h>
#include <math.h>
-#include "talloc.h"
+#include <talloc.h>
+
+#include "compat.h"
typedef struct _range_list_element
{
@@ -58,6 +60,7 @@ typedef struct _range_list
*
* @return A newly allocated range_list, or NULL if an error occurred.
*/
+_EXPORT()
range_list* range_list_new();
@@ -68,6 +71,7 @@ range_list* range_list_new();
*
* @param rl the range_list to be free()d.
*/
+_EXPORT()
void range_list_free(range_list* rl);
@@ -77,6 +81,7 @@ void range_list_free(range_list* rl);
*
* @return The number of elements currently in the list.
*/
+_EXPORT()
uint32_t range_list_size(const range_list* rl);
@@ -96,6 +101,7 @@ uint32_t range_list_size(const range_list* rl);
* or if the submitted range overlaps with an existing element. Other
* errors may also be possible.
*/
+_EXPORT()
bool range_list_add(range_list* rl, uint32_t offset, uint32_t length, void* data);
@@ -108,6 +114,7 @@ bool range_list_add(range_list* rl, uint32_t offset, uint32_t length, void* data
*
* @return true if the element was successfully removed, false otherwise.
*/
+_EXPORT()
bool range_list_remove(range_list* rl, uint32_t index);
@@ -119,6 +126,7 @@ bool range_list_remove(range_list* rl, uint32_t index);
* @return The element for a given index, or NULL if the element is not
* available.
*/
+_EXPORT()
const range_list_element* range_list_get(const range_list* rl, uint32_t index);
@@ -129,6 +137,7 @@ const range_list_element* range_list_get(const range_list* rl, uint32_t index);
*
* @return A matching element index or a negative value if none could be found.
*/
+_EXPORT()
int32_t range_list_find(const range_list* rl, uint32_t offset);
@@ -143,6 +152,7 @@ int32_t range_list_find(const range_list* rl, uint32_t offset);
* NOTE: May also return NULL if an element matched but the data
* element was never set.
*/
+_EXPORT()
void* range_list_find_data(const range_list* rl, uint32_t offset);
@@ -164,6 +174,7 @@ void* range_list_find_data(const range_list* rl, uint32_t offset);
*
* @return true if the element was successfully split, false otherwise.
*/
+_EXPORT()
bool range_list_split_element(range_list* rl, uint32_t index, uint32_t offset);
@@ -176,6 +187,7 @@ bool range_list_split_element(range_list* rl, uint32_t index, uint32_t offset);
*
* @return true if the specified range exists and is complete, false otherwise.
*/
+_EXPORT()
bool range_list_has_range(range_list* rl, uint32_t start, uint32_t length);
#endif
diff --git a/include/regfi.h b/include/regfi.h
index 2f3ae08..962ad22 100644
--- a/include/regfi.h
+++ b/include/regfi.h
@@ -1,5 +1,6 @@
/*
- * Copyright (C) 2005-2010 Timothy D. Morgan
+ * Copyright (C) 2005-2011 Timothy D. Morgan
+ * Copyright (C) 2010 Michael Cohen
* Copyright (C) 2005 Gerald (Jerry) Carter
*
* This program is free software; you can redistribute it and/or modify
@@ -15,7 +16,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: regfi.h 172 2010-03-08 03:04:34Z tim $
+ * $Id: regfi.h 263 2011-06-18 00:06:51Z tim $
*/
/**
@@ -47,7 +48,7 @@
* @li @ref regfiParseLayer
*
* Most users will find that a combination of the Base Layer and the Iterator Layer
- * will be sufficient for accessing registry hive files. Those who are wiling
+ * will be sufficient for accessing registry hive files. Those who are willing
* to dive deep into registry data structures, for instance to recover deleted
* data structures or to research Windows registry behavior in detail, will
* find the Parse Layer to be quite useful.
@@ -68,59 +69,68 @@
#include <sys/types.h>
#include <unistd.h>
#include <iconv.h>
+#include <pthread.h>
+#include <talloc.h>
+/* regfi headers */
+#include "compat.h"
#include "byteorder.h"
-#include "talloc.h"
#include "winsec.h"
#include "void_stack.h"
#include "range_list.h"
#include "lru_cache.h"
/******************************************************************************/
+/* Constants for use while interacting with the library */
+/******************************************************************************/
/* regfi library error message types */
-#define REGFI_MSG_INFO 0x0001
-#define REGFI_MSG_WARN 0x0004
-#define REGFI_MSG_ERROR 0x0010
+#define REGFI_LOG_INFO 0x0001
+#define REGFI_LOG_WARN 0x0004
+#define REGFI_LOG_ERROR 0x0010
+#define REGFI_DEFAULT_LOG_MASK REGFI_LOG_ERROR|REGFI_LOG_WARN
-typedef uint8_t REGFI_ENCODING;
/* regfi library supported character encodings */
-#define REGFI_ENCODING_ASCII 0
-#define REGFI_ENCODING_UTF8 1
-#define REGFI_ENCODING_DEFAULT REGFI_ENCODING_ASCII
/* UTF16LE is not supported for output */
-#define REGFI_ENCODING_UTF16LE 2
-
-#define REGFI_NUM_ENCODINGS 3
-
-/* Windows is lame */
-#ifdef O_BINARY
-#define REGFI_OPEN_FLAGS O_RDONLY|O_BINARY
-#else
-#define REGFI_OPEN_FLAGS O_RDONLY
-#endif
+typedef enum {
+ REGFI_ENCODING_DEFAULT = 0,
+ REGFI_ENCODING_ASCII = 0,
+ REGFI_ENCODING_UTF8 = 1,
+ REGFI_ENCODING_UTF16LE = 2,
+ REGFI_NUM_ENCODINGS = 3
+} REGFI_ENCODING;
/* Registry data types */
-#define REG_NONE 0
-#define REG_SZ 1
-#define REG_EXPAND_SZ 2
-#define REG_BINARY 3
-#define REG_DWORD 4
-#define REG_DWORD_LE 4 /* DWORD, little endian */
-#define REG_DWORD_BE 5 /* DWORD, big endian */
-#define REG_LINK 6
-#define REG_MULTI_SZ 7
-#define REG_RESOURCE_LIST 8
-#define REG_FULL_RESOURCE_DESCRIPTOR 9
-#define REG_RESOURCE_REQUIREMENTS_LIST 10
-#define REG_QWORD 11 /* 64-bit little endian */
+typedef enum {
+ REG_NONE = 0,
+ REG_SZ = 1,
+ REG_EXPAND_SZ = 2,
+ REG_BINARY = 3,
+ REG_DWORD = 4,
+ REG_DWORD_LE = 4 , /* DWORD, little endian */
+ REG_DWORD_BE = 5 , /* DWORD, big endian */
+ REG_LINK = 6,
+ REG_MULTI_SZ = 7,
+ REG_RESOURCE_LIST = 8,
+ REG_FULL_RESOURCE_DESCRIPTOR= 9,
+ REG_RESOURCE_REQUIREMENTS_LIST= 10,
+ REG_QWORD = 11, /* 64-bit little endian */
/* XXX: Has MS defined a REG_QWORD_BE? */
/* Not a real type in the registry */
-#define REG_KEY 0x7FFFFFFF
-
+ REG_KEY = 0x7FFFFFFF
+} REGFI_DATA_TYPE;
#define REGFI_OFFSET_NONE 0xffffffff
+
+/******************************************************************************/
+/* Various resource limits and related constants */
+/******************************************************************************/
+
+/* Flags determining how many records to cache internally */
+#define REGFI_CACHE_SK_MAX 64
+#define REGFI_CACHE_NK_MAX 1024
+
/* This maximum depth is described here:
* http://msdn.microsoft.com/en-us/library/ms724872%28VS.85%29.aspx
*/
@@ -135,6 +145,13 @@ typedef uint8_t REGFI_ENCODING;
#define REGFI_MAX_SUBKEY_DEPTH 255
+/******************************************************************************/
+/* Symbols for internal use */
+/******************************************************************************/
+
+/* Global thread-local storage key */
+pthread_key_t regfi_log_key;
+
/* Header sizes and magic number lengths for various records */
#define REGFI_HBIN_ALLOC 0x1000 /* Minimum allocation unit for HBINs */
#define REGFI_REGF_SIZE 0x1000 /* "regf" header block size */
@@ -160,13 +177,12 @@ typedef uint8_t REGFI_ENCODING;
* perhaps conservatively implement a check.
*/
/* Minimum time is Jan 1, 1990 00:00:00 */
-#define REGFI_MTIME_MIN_HIGH 0x01B41E6D
-#define REGFI_MTIME_MIN_LOW 0x26F98000
+#define REGFI_MTIME_MIN 0x01B41E6D00000000L
+
/* Maximum time is Jan 1, 2290 00:00:00
* (We hope no one is using Windows by then...)
*/
-#define REGFI_MTIME_MAX_HIGH 0x03047543
-#define REGFI_MTIME_MAX_LOW 0xC80A4000
+#define REGFI_MTIME_MAX 0x0304754300000000L
/* Flags for the vk records */
@@ -233,17 +249,32 @@ typedef uint8_t REGFI_ENCODING;
| REGFI_NK_FLAG_UNKNOWN3)
+#ifndef CHAR_BIT
#define CHAR_BIT 8
+#endif
+
#define TIME_T_MIN ((time_t)0 < (time_t) -1 ? (time_t) 0 \
: ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1))
#define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
-#define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
+#define REGFI_TIME_FIXUP (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
+
-typedef struct _regfi_nttime
+
+/******************************************************************************/
+/* Structures */
+/******************************************************************************/
+
+typedef uint64_t REGFI_NTTIME;
+
+typedef struct _regfi_log
{
- uint32_t low;
- uint32_t high;
-} REGFI_NTTIME;
+ /* Error/warning/info messages returned by lower layer functions */
+ char* messages;
+
+ /* Mask for error message types that will be stored. */
+ uint16_t msg_mask;
+
+} REGFI_LOG;
/** HBIN block information
@@ -297,16 +328,16 @@ typedef struct _regfi_subkey_list
uint32_t cell_size;
/* Number of immediate children */
- uint32_t num_children;
+ uint32_t num_children;
- /* Total number of keys referenced by this list and it's children */
- uint32_t num_keys;
+ /* Total number of keys referenced by this list and its children */
+ uint32_t num_keys;
REGFI_SUBKEY_LIST_ELEM* elements;
uint8_t magic[REGFI_CELL_MAGIC_SIZE];
/* Set if the magic indicates this subkey list points to child subkey lists */
- bool recursive_type;
+ bool recursive_type;
} REGFI_SUBKEY_LIST;
@@ -316,6 +347,11 @@ typedef uint32_t REGFI_VALUE_LIST_ELEM;
*/
typedef struct _regfi_value_list
{
+ /* Real offset of this record's cell in the file */
+ uint32_t offset;
+
+ uint32_t cell_size;
+
/* Actual number of values referenced by this list.
* May differ from parent key's num_values if there were parsing errors.
*/
@@ -330,6 +366,9 @@ typedef struct _regfi_value_list
*/
typedef struct _regfi_classname
{
+ /** Real offset of this record's cell in the file */
+ uint32_t offset;
+
/** As converted to requested REGFI_ENCODING */
char* interpreted;
@@ -352,8 +391,13 @@ typedef struct _regfi_classname
*/
typedef struct _regfi_data
{
+ /* XXX: this isn't populated yet. Should set it to start of data cell
+ * or big data cell.
+ */
+ uint32_t offset;
+
/** Data type of this data, as indicated by the referencing VK record. */
- uint32_t type;
+ REGFI_DATA_TYPE type;
/** Length of the raw data. */
uint32_t size;
@@ -361,7 +405,9 @@ typedef struct _regfi_data
/** This is always present, representing the raw data cell contents. */
uint8_t* raw;
- /** Represents the length of the interpreted value. Meaning is type-specific. */
+ /** Represents the length of the interpreted value. Meaning is type-specific.
+ * Will be 0 if interpretation failed for any reason.
+ */
uint32_t interpreted_size;
/** These items represent interpreted versions of the REGFI_DATA::raw field.
@@ -454,7 +500,7 @@ typedef struct _regfi_data
/** Value structure
* @ingroup regfiBase
*/
-typedef struct
+typedef struct _regfi_vk
{
/** Real offset of this record's cell in the file */
uint32_t offset;
@@ -462,23 +508,20 @@ typedef struct
/** ((start_offset - end_offset) & 0xfffffff8) */
uint32_t cell_size;
- /* XXX: deprecated */
- REGFI_DATA* data;
-
/** The name of this value converted to desired REGFI_ENCODING.
*
* This conversion typically occurs automatically through REGFI_ITERATOR
* settings. String is NUL terminated.
*/
- char* valuename;
+ char* name;
/** The raw value name
*
* Length of the buffer is stored in name_length.
*/
- uint8_t* valuename_raw;
+ uint8_t* name_raw;
- /** Length of valuename_raw */
+ /** Length of name_raw */
uint16_t name_length;
/** Offset from beginning of this hbin block */
@@ -494,7 +537,7 @@ typedef struct
uint32_t data_off;
/** Value's data type */
- uint32_t type;
+ REGFI_DATA_TYPE type;
/** VK record's magic number (should be "vk") */
uint8_t magic[REGFI_CELL_MAGIC_SIZE];
@@ -510,16 +553,20 @@ typedef struct
* This information is derived from the high bit of the raw data size field.
*/
bool data_in_offset;
-} REGFI_VK_REC;
+
+ /* XXX: deprecated */
+ REGFI_DATA* data;
+
+} REGFI_VK;
/* Key Security */
-struct _regfi_sk_rec;
+struct _regfi_sk;
/** Security structure
* @ingroup regfiBase
*/
-typedef struct _regfi_sk_rec
+typedef struct _regfi_sk
{
/** Real file offset of this record */
uint32_t offset;
@@ -550,13 +597,13 @@ typedef struct _regfi_sk_rec
/** The magic number for this record (should be "sk") */
uint8_t magic[REGFI_CELL_MAGIC_SIZE];
-} REGFI_SK_REC;
+} REGFI_SK;
/** Key structure
* @ingroup regfiBase
*/
-typedef struct
+typedef struct _regfi_nk
{
/** Real offset of this record's cell in the file */
uint32_t offset;
@@ -588,7 +635,7 @@ typedef struct
/** Key's last modification time */
REGFI_NTTIME mtime;
- /** Length of keyname_raw */
+ /** Length of name_raw */
uint16_t name_length;
/** Length of referenced classname */
@@ -599,18 +646,18 @@ typedef struct
* This conversion typically occurs automatically through REGFI_ITERATOR
* settings. String is NUL terminated.
*/
- char* keyname;
+ char* name;
/** The raw key name
*
* Length of the buffer is stored in name_length.
*/
- uint8_t* keyname_raw;
+ uint8_t* name_raw;
- /** Virutal offset of parent key */
+ /** Virtual offset of parent key */
uint32_t parent_off;
- /** Virutal offset of classname key */
+ /** Virtual offset of classname key */
uint32_t classname_off;
/* XXX: max subkey name * 2 */
@@ -619,7 +666,7 @@ typedef struct
/* XXX: max subkey classname length (as if) */
uint32_t max_bytes_subkeyclassname;
- /* XXX: max valuename * 2 */
+ /* XXX: max value name * 2 */
uint32_t max_bytes_valuename;
/* XXX: max value data size */
@@ -645,8 +692,18 @@ typedef struct
/** Virtual offset of SK record */
uint32_t sk_off;
-} REGFI_NK_REC;
+} REGFI_NK;
+
+
+typedef struct _regfi_raw_file
+{
+ int64_t (* seek)(); /* (REGFI_RAW_FILE* self, uint64_t offset, int whence) */
+ ssize_t (* read)(); /* (REGFI_RAW_FILE* self, void* buf, size_t count) */
+ uint64_t cur_off;
+ uint64_t size;
+ void* state;
+} REGFI_RAW_FILE;
/** Registry hive file data structure
@@ -654,39 +711,18 @@ typedef struct
* This essential structure stores run-time information about a single open
* registry hive as well as file header (REGF block) data. This structure
* also stores a list of warnings and error messages generated while parsing
- * the registry hive. These can be tuned using @ref regfi_set_message_mask.
- * Messages may be retrieved using @ref regfi_get_messages.
+ * the registry hive. These can be tuned using @ref regfi_log_set_mask.
+ * Messages may be retrieved using @ref regfi_log_get_str.
*
* @note If the message mask is set to record any messages, dependent code
- * must use @ref regfi_get_messages periodically to clear the message
+ * must use @ref regfi_log_get_str periodically to clear the message
* queue. Otherwise, this structure will grow in size over time as
* messages queue up.
*
* @ingroup regfiBase
*/
-typedef struct
+typedef struct _regfi_file
{
- /* Run-time information */
- /************************/
- /* file descriptor */
- int fd;
-
- /* For sanity checking (not part of the registry header) */
- uint32_t file_length;
-
- /* Metadata about hbins */
- range_list* hbins;
-
- /* SK record cached since they're repeatedly reused */
- lru_cache* sk_cache;
-
- /* Error/warning/info messages returned by lower layer functions */
- char* last_message;
-
- /* Mask for error message types that will be stored. */
- uint16_t msg_mask;
-
-
/* Data parsed from file header */
/********************************/
uint8_t magic[REGFI_REGF_MAGIC_SIZE];/* "regf" */
@@ -737,9 +773,68 @@ typedef struct
* or doing research. */
uint8_t reserved2[REGFI_REGF_RESERVED2_SIZE];
+
+ /* Run-time information */
+ /************************/
+ /* For sanity checking (not part of the registry header) */
+ uint32_t file_length;
+
+ /** The encoding that all strings are converted to during interpretation.
+ */
+ REGFI_ENCODING string_encoding;
+
+ /* Functions for accessing the file */
+ REGFI_RAW_FILE* cb;
+
+ /* Mutex for all cb access. This is done to prevent one thread from moving
+ * the file offset while another thread is in the middle of a multi-read
+ * parsing transaction */
+ pthread_mutex_t cb_lock;
+
+ /* Metadata about hbins */
+ range_list* hbins;
+
+ /* Multiple read access allowed, write access is exclusive */
+ pthread_rwlock_t hbins_lock;
+
+ /* Small number of SK records cached */
+ lru_cache* sk_cache;
+
+ /* Need exclusive access for LRUs, since lookups make changes */
+ pthread_mutex_t sk_lock;
+
+ /* Limited number of keys cached */
+ lru_cache* nk_cache;
+
+ /* Need exclusive access for LRUs, since lookups make changes */
+ pthread_mutex_t nk_lock;
+
+ /* Needed to protect various talloc calls */
+ pthread_mutex_t mem_lock;
+
} REGFI_FILE;
+typedef struct _regfi_iter_position
+{
+ /* key offset */
+ uint32_t offset;
+
+ /* Index of the current subkey */
+ uint32_t cur_subkey;
+
+ /* Index of the current value */
+ uint32_t cur_value;
+
+ /* The number of subkeys of this key */
+ uint32_t num_subkeys;
+
+ /* The number of values of this key */
+ uint32_t num_values;
+
+} REGFI_ITER_POSITION;
+
+
/** Registry hive iterator
* @ingroup regfiIteratorLayer
*/
@@ -751,31 +846,10 @@ typedef struct _regfi_iterator
/** All current parent keys and associated iterator positions */
void_stack* key_positions;
- /** The current key */
- REGFI_NK_REC* cur_key;
-
- /** The encoding that all strings are converted to as set during iterator
- * creation.
- */
- REGFI_ENCODING string_encoding;
-
- /** Index of the current subkey */
- uint32_t cur_subkey;
-
- /** Index of the current value */
- uint32_t cur_value;
+ REGFI_ITER_POSITION* cur;
} REGFI_ITERATOR;
-typedef struct _regfi_iter_position
-{
- REGFI_NK_REC* nk;
- uint32_t cur_subkey;
- /* We could store a cur_value here as well, but didn't see
- * the use in it right now.
- */
-} REGFI_ITER_POSITION;
-
/** General purpose buffer with stored length
* @ingroup regfiBottomLayer
@@ -797,17 +871,16 @@ typedef struct _regfi_buffer
*/
/******************************************************************************/
-/** Attempts to open a registry hive and allocate related data structures.
- *
- * @param filename A string containing the relative or absolute path of the
- * registry hive to be opened.
+
+
+/** Returns the current regfi library version
*
- * @return A reference to a newly allocated REGFI_FILE structure,
- * if successful; NULL on error.
+ * @return A string indicating the version.
*
* @ingroup regfiBase
*/
-REGFI_FILE* regfi_open(const char* filename);
+_EXPORT()
+const char* regfi_version();
/** Parses file headers of an already open registry hive file and
@@ -815,24 +888,46 @@ REGFI_FILE* regfi_open(const char* filename);
*
* @param fd A file descriptor of an already open file. Must be seekable.
*
+ * @param output_encoding Character encoding that strings should be returned in.
+ * Only supply the REGFI_ENCODING_* constants, as others
+ * will be rejected.
+ * The following values are currently accepted:
+ * REGFI_ENCODING_DEFAULT (currently REGFI_ENCODING_ASCII)
+ * REGFI_ENCODING_ASCII
+ * REGFI_ENCODING_UTF8
+ *
* @return A reference to a newly allocated REGFI_FILE structure, if successful;
- * NULL on error.
+ * NULL on error. Use regfi_free to free the returned REGFI_FILE.
*
* @ingroup regfiBase
*/
-REGFI_FILE* regfi_alloc(int fd);
+_EXPORT()
+REGFI_FILE* regfi_alloc(int fd, REGFI_ENCODING output_encoding);
-/** Closes and frees an open registry hive.
+/** Parses file headers returned by supplied callback functions.
+ *
+ * This interface is useful if you have a registry hive in memory
+ * or have some other reason to emulate a real file.
+ *
+ * @param file_cb A structure defining the callback functions needed to access the file.
*
- * @param file The registry structure to close.
+ * @param output_encoding Character encoding that strings should be returned in.
+ * Only supply the REGFI_ENCODING_* constants, as others
+ * will be rejected.
+ * The following values are currently accepted:
+ * REGFI_ENCODING_DEFAULT (currently REGFI_ENCODING_ASCII)
+ * REGFI_ENCODING_ASCII
+ * REGFI_ENCODING_UTF8
*
- * @return 0 on success, -1 on failure with errno set.
- * errno codes are similar to those of close(2).
+ * @return A reference to a newly allocated REGFI_FILE structure, if successful;
+ * NULL on error. Use regfi_free to free the returned REGFI_FILE.
*
* @ingroup regfiBase
*/
-int regfi_close(REGFI_FILE* file);
+_EXPORT()
+REGFI_FILE* regfi_alloc_cb(REGFI_RAW_FILE* file_cb,
+ REGFI_ENCODING output_encoding);
/** Frees a hive's data structures without closing the underlying file.
@@ -841,67 +936,299 @@ int regfi_close(REGFI_FILE* file);
*
* @ingroup regfiBase
*/
-void regfi_free(REGFI_FILE* file);
+_EXPORT()
+void regfi_free(REGFI_FILE* file);
/** Get errors, warnings, and/or verbose information relating to processing of
* the given registry file.
*
- * @param file the structure for the registry file
- *
* @return A newly allocated char* which must be free()d by the caller.
*
* @ingroup regfiBase
*/
-char* regfi_get_messages(REGFI_FILE* file);
+_EXPORT()
+char* regfi_log_get_str();
-/** Set the verbosity level of errors and warnings generated by the library
- * (as accessible via regfi_get_messages).
- *
- * This may be called at any time and will take effect immediately.
- *
- * @param file the structure for the registry file
+/** Set the verbosity level of messages generated by the library for the
+ * current thread.
*
- * @param mask an integer representing the types of messages desired.
+ * @param mask An integer representing the types of messages desired.
* Acceptable values are created through bitwise ORs of
- * REGFI_MSG_* values. For instance, if only errors and
+ * REGFI_LOG_* values. For instance, if only errors and
* informational messages were desired (but not warnings),
- * then one would specify: REGFI_MSG_ERROR|REGFI_MSG_INFO
- * New REGFI_FILE structures are created with:
- * REGFI_MSG_ERROR|REGFI_MSG_WARN
- * Note that error and warning messages will continue to
- * accumulate in memory if they are not fetched using
- * regfi_get_messages and then freed by the caller.
- * To disable error messages entirely, supply 0, which
- * will prevent message accumulation.
+ * then one would specify: REGFI_LOG_ERROR|REGFI_LOG_INFO
+ * By default the message mask is: REGFI_LOG_ERROR|REGFI_LOG_WARN.
+ *
+ * @return true on success and false on failure. Failure occurs if
+ * underlying pthread functions fail. errno is set in this case.
+ *
+ * Message masks are set in a thread-specific way. If one were to set a message
+ * mask in one thread and then spawn a new thread, then the new thread will have
+ * it's message mask reset to the default. This function may be called at any
+ * time and will take effect immediately for the current thread.
+ *
+ * @note When a non-zero message mask is set, messages will
+ * accumulate in memory without limit if they are not fetched using
+ * @ref regfi_log_get_str and subsequently freed by the caller. It is
+ * recommended that messsages be fetched after each regfi API call in
+ * order to provide the most context.
*
* @ingroup regfiBase
*/
-void regfi_set_message_mask(REGFI_FILE* file, uint16_t mask);
+_EXPORT()
+bool regfi_log_set_mask(uint16_t mask);
-/* Dispose of previously parsed records */
+/** Fetches a hive's root key.
+ *
+ * @return Returns the root key or NULL on failure. Key must be freed using
+ * @ref regfi_free_record.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const REGFI_NK* regfi_get_rootkey(REGFI_FILE* file);
+
-/** Frees a key structure previously returned by one of the API functions
+/** Frees a record previously returned by one of the API functions.
*
- * XXX: finish documenting
+ * @param file The file from which the record originated.
+ * (This is needed for memory management reasons.)
+ *
+ * @param record Any of the following record types: REGFI_NK, REGFI_VK,
+ * REGFI_SK, REGFI_DATA, and REGFI_CLASSNAME records.
+ *
+ * @note The "const" in the record data type is a bit misleading and is there just for
+ * convenience. Since records returned previously must not be modified by users
+ * of the API due to internal caching, these are returned as const, so this
+ * function is const to make passing those records back easy.
*
* @ingroup regfiBase
*/
-void regfi_free_key(REGFI_NK_REC* nk);
+_EXPORT()
+void regfi_free_record(REGFI_FILE* file, const void* record);
-/** Frees a value structure previously returned by one of the API functions
+/** Increments reference count on record
*
- * XXX: finish documenting
+ * Adds an extra internal reference to specified record, making it necessary to
+ * call regfi_free_record on it an additional time before it is freed. This is
+ * useful in cases where multiple threads/structures need access to a shared record,
+ * without requiring them to be in sync with when it is freed.
+ *
+ * @param file The file from which the record originated.
+ * (This is needed for memory management reasons.)
+ *
+ * @param record Any of the following record types: REGFI_NK, REGFI_VK,
+ * REGFI_SK, REGFI_DATA, and REGFI_CLASSNAME records.
+ *
+ * @return Updated record pointer on success, NULL otherwise
+ *
+ * @note Be sure to use the returned record for further access to the structure
+ * instead of the previous version of the pointer. E.g.:
+ * @code
+ * myKey = (const REGFI_NK*)regfi_reference_record(myFile, myKey);
+ * @endcode
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const void* regfi_reference_record(REGFI_FILE* file, const void* record);
+
+
+/** Retrieves number of subkeys referenced by this key.
+ *
+ * Number of subkeyss in key structure and subkey list structure could differ,
+ * so this provides a standard/sane way of determining the number.
+ *
+ * @param key the key whose number of subkeys is desired
+ *
+ * @return Returns the number of subkeys referenced by this key.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+uint32_t regfi_fetch_num_subkeys(const REGFI_NK* key);
+
+
+/** Retrieves number of values referenced by this key.
+ *
+ * Number of values in key structure and value list structure could differ,
+ * so this provides a standard/sane way of determining the number.
+ *
+ * @param key the key whose number of values is desired
+ *
+ * @return Returns the number of values referenced by this key.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+uint32_t regfi_fetch_num_values(const REGFI_NK* key);
+
+
+/** Retrieves classname for a given key.
+ *
+ * @param file the file from which key is derived
+ * @param key the key whose classname is desired
+ *
+ * @return Returns a newly allocated classname structure, or NULL on failure.
+ * Classname structures must be freed with @ref regfi_free_record.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const REGFI_CLASSNAME* regfi_fetch_classname(REGFI_FILE* file,
+ const REGFI_NK* key);
+
+
+/** Returns the SK (security) record referenced by the supplied key.
+ *
+ * @param file the file from which key is derived
+ * @param key the key whose SK record is desired
+ *
+ * @return A read-only SK structure, or NULL on failure.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const REGFI_SK* regfi_fetch_sk(REGFI_FILE* file, const REGFI_NK* key);
+
+
+/** Returns the next SK (security) record referenced by the supplied SK record
+ *
+ * @param file the file from which sk is derived
+ * @param sk the SK record whose next sibling SK record is desired
+ *
+ * @return A read-only SK structure, or NULL on failure.
+ *
+ * @note
+ * SK records are included in a circular, doubly-linked list.
+ * To iterate over all SK records, be sure to check for the repetition of
+ * the SK record you started with to determine when all have been traversed.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const REGFI_SK* regfi_next_sk(REGFI_FILE* file, const REGFI_SK* sk);
+
+
+/** Returns the previous SK (security) record referenced by the supplied SK record
+ *
+ * @param file the file from which sk is derived
+ * @param sk the SK record whose previous sibling SK record is desired
+ *
+ * @return A read-only SK structure, or NULL on failure.
+ *
+ * @note
+ * SK records are included in a circular, doubly-linked list.
+ * To iterate over all SK records, be sure to check for the repetition of
+ * the SK record you started with to determine when all have been traversed.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const REGFI_SK* regfi_prev_sk(REGFI_FILE* file, const REGFI_SK* sk);
+
+
+/** Retrieves data for a given value.
+ *
+ * @param file the file from which value is derived
+ * @param value the value whose data is desired
+ *
+ * @return Returns a newly allocated data structure, or NULL on failure.
+ * Data structures must be freed with @ref regfi_free_record.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const REGFI_DATA* regfi_fetch_data(REGFI_FILE* file,
+ const REGFI_VK* value);
+
+
+/** Locates a specific subkey of a given key.
+ *
+ * @param file the file from which key is derived
+ * @param key the key whose subkey is desired
+ * @param name name of the desired subkey (case-insensitive)
+ * @param index a return value: the index of the desired subkey.
+ * undefined on error
+ *
+ * @return true if the subkey is found, false if an error occurred or if the
+ * specified name could not be found. If an error occurs, messages
+ * will be written explaining the issue. (See regfi_log_get_str.)
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+bool regfi_find_subkey(REGFI_FILE* file, const REGFI_NK* key,
+ const char* name, uint32_t* index);
+
+
+/** Locates a specific value of a given key.
+ *
+ * @param file the file from which key is derived
+ * @param key the key whose value is desired
+ * @param name name of the desired value (case-insensitive)
+ * @param index a return value: the index of the desired value.
+ * undefined on error
+ *
+ * @return true if the value is found, false if an error occurred or if the
+ * specified name could not be found. If an error occurs, messages
+ * will be written explaining the issue. (See regfi_log_get_str.)
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+bool regfi_find_value(REGFI_FILE* file, const REGFI_NK* key,
+ const char* name, uint32_t* index);
+
+
+/** Retrieves a specific subkey of a given key.
+ *
+ * @param file the file from which key is derived
+ * @param key the key whose subkey is desired
+ * @param index the index of the desired subkey
+ *
+ * @return the requested subkey or NULL on error.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const REGFI_NK* regfi_get_subkey(REGFI_FILE* file, const REGFI_NK* key,
+ uint32_t index);
+
+
+/** Retrieves a specific value of a given key.
+ *
+ * @param file the file from which key is derived
+ * @param key the key whose value is desired
+ * @param index the index of the desired value
+ *
+ * @return the requested value or NULL on error.
*
* @ingroup regfiBase
*/
-void regfi_free_value(REGFI_VK_REC* vk);
+_EXPORT()
+const REGFI_VK* regfi_get_value(REGFI_FILE* file, const REGFI_NK* key,
+ uint32_t index);
+/** Uses a key's parent_off reference to retrieve it's parent.
+ *
+ * @param file the file from which key is derived
+ * @param key the key whose parent is desired
+ *
+ * @return the requested subkey or NULL on error.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const REGFI_NK* regfi_get_parentkey(REGFI_FILE* file, const REGFI_NK* key);
+
+
/******************************************************************************/
/**
* @defgroup regfiIteratorLayer Iterator Layer: Primary regfi Library Interface
@@ -916,21 +1243,13 @@ void regfi_free_value(REGFI_VK_REC* vk);
*
* @param file The opened registry file the iterator should be created for.
*
- * @param output_encoding Character encoding that strings should be returned in.
- * Only supply the REGFI_ENCODING_* constants, as others
- * will be rejected.
- * The following values are currently accepted:
- * REGFI_ENCODING_DEFAULT (currently REGFI_ENCODING_ASCII)
- * REGFI_ENCODING_ASCII
- * REGFI_ENCODING_UTF8
- *
* @return A newly allocated REGFI_ITERATOR.
* Must be free()d with regfi_iterator_free.
*
* @ingroup regfiIteratorLayer
*/
-REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* file,
- REGFI_ENCODING output_encoding);
+_EXPORT()
+REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* file);
/** Frees a registry file iterator previously created by regfi_iterator_new.
@@ -941,7 +1260,8 @@ REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* file,
*
* @ingroup regfiIteratorLayer
*/
-void regfi_iterator_free(REGFI_ITERATOR* i);
+_EXPORT()
+void regfi_iterator_free(REGFI_ITERATOR* i);
/** Traverse deeper into the registry tree at the current subkey.
@@ -957,7 +1277,8 @@ void regfi_iterator_free(REGFI_ITERATOR* i);
*
* @ingroup regfiIteratorLayer
*/
-bool regfi_iterator_down(REGFI_ITERATOR* i);
+_EXPORT()
+bool regfi_iterator_down(REGFI_ITERATOR* i);
/** Traverse up to the current key's parent key.
@@ -969,7 +1290,8 @@ bool regfi_iterator_down(REGFI_ITERATOR* i);
*
* @ingroup regfiIteratorLayer
*/
-bool regfi_iterator_up(REGFI_ITERATOR* i);
+_EXPORT()
+bool regfi_iterator_up(REGFI_ITERATOR* i);
/** Traverse up to the root key of the hive.
@@ -980,7 +1302,8 @@ bool regfi_iterator_up(REGFI_ITERATOR* i);
*
* @ingroup regfiIteratorLayer
*/
-bool regfi_iterator_to_root(REGFI_ITERATOR* i);
+_EXPORT()
+bool regfi_iterator_to_root(REGFI_ITERATOR* i);
/** Traverse down multiple levels in the registry hive.
@@ -1001,7 +1324,8 @@ bool regfi_iterator_to_root(REGFI_ITERATOR* i);
*
* @ingroup regfiIteratorLayer
*/
-bool regfi_iterator_walk_path(REGFI_ITERATOR* i, const char** path);
+_EXPORT()
+bool regfi_iterator_descend(REGFI_ITERATOR* i, const char** path);
/** Returns the currently referenced key.
@@ -1009,36 +1333,25 @@ bool regfi_iterator_walk_path(REGFI_ITERATOR* i, const char** path);
* @param i the iterator
*
* @return A read-only key structure for the current key, or NULL on failure.
+ * Data structures must be freed with @ref regfi_free_record.
*
* @ingroup regfiIteratorLayer
*/
-const REGFI_NK_REC* regfi_iterator_cur_key(REGFI_ITERATOR* i);
-
-
-/** Returns the SK (security) record referenced by the current key.
- *
- * @param i the iterator
- *
- * @return A read-only SK structure, or NULL on failure.
- *
- * @ingroup regfiIteratorLayer
- */
-const REGFI_SK_REC* regfi_iterator_cur_sk(REGFI_ITERATOR* i);
+_EXPORT()
+const REGFI_NK* regfi_iterator_cur_key(REGFI_ITERATOR* i);
/** Sets the internal subkey index to the first subkey referenced by the current
- * key and returns that key.
+ * key.
*
* @param i the iterator
*
- * @return A newly allocated key structure for the newly referenced first
- * subkey, or NULL on failure. Failure may be due to a lack of any
- * subkeys or other errors. Newly allocated keys must be freed with
- * regfi_free_key.
+ * @return True if the current key has any subkeys, false otherwise.
*
* @ingroup regfiIteratorLayer
*/
-REGFI_NK_REC* regfi_iterator_first_subkey(REGFI_ITERATOR* i);
+_EXPORT()
+bool regfi_iterator_first_subkey(REGFI_ITERATOR* i);
/** Returns the currently indexed subkey.
@@ -1047,30 +1360,30 @@ REGFI_NK_REC* regfi_iterator_first_subkey(REGFI_ITERATOR* i);
*
* @return A newly allocated key structure for the currently referenced subkey,
* or NULL on failure. Newly allocated keys must be freed with
- * regfi_free_key.
+ * @ref regfi_free_record.
*
* @ingroup regfiIteratorLayer
*/
-REGFI_NK_REC* regfi_iterator_cur_subkey(REGFI_ITERATOR* i);
+_EXPORT()
+const REGFI_NK* regfi_iterator_cur_subkey(REGFI_ITERATOR* i);
-/** Increments the internal subkey index to the next key in the subkey-list and
- * returns the subkey for that index.
+/** Increments the internal subkey index to the next key in the subkey-list.
*
* @param i the iterator
*
- * @return A newly allocated key structure for the next subkey or NULL on
- * failure. Newly allocated keys must be freed with regfi_free_key.
+ * @return True if another subkey should exist, false otherwise.
*
* @ingroup regfiIteratorLayer
*/
-REGFI_NK_REC* regfi_iterator_next_subkey(REGFI_ITERATOR* i);
+_EXPORT()
+bool regfi_iterator_next_subkey(REGFI_ITERATOR* i);
/** Searches for a subkey with a given name under the current key.
*
- * @param i the iterator
- * @param subkey_name subkey name to search for
+ * @param i the iterator
+ * @param name subkey name to search for
*
* @return True if such a subkey was found, false otherwise. If a subkey is
* found, the current subkey index is set to that subkey. Otherwise,
@@ -1078,22 +1391,21 @@ REGFI_NK_REC* regfi_iterator_next_subkey(REGFI_ITERATOR* i);
*
* @ingroup regfiIteratorLayer
*/
-bool regfi_iterator_find_subkey(REGFI_ITERATOR* i,
- const char* subkey_name);
+_EXPORT()
+bool regfi_iterator_find_subkey(REGFI_ITERATOR* i, const char* name);
+
/** Sets the internal value index to the first value referenced by the current
- * key and returns that value.
+ * key.
*
* @param i the iterator
*
- * @return A newly allocated value structure for the newly referenced first
- * value, or NULL on failure. Failure may be due to a lack of any
- * values or other errors. Newly allocated keys must be freed with
- * regfi_free_value.
+ * @return True if the current key has any values, false otherwise.
*
* @ingroup regfiIteratorLayer
*/
-REGFI_VK_REC* regfi_iterator_first_value(REGFI_ITERATOR* i);
+_EXPORT()
+bool regfi_iterator_first_value(REGFI_ITERATOR* i);
/** Returns the currently indexed value.
@@ -1102,30 +1414,30 @@ REGFI_VK_REC* regfi_iterator_first_value(REGFI_ITERATOR* i);
*
* @return A newly allocated value structure for the currently referenced value,
* or NULL on failure. Newly allocated values must be freed with
- * regfi_free_value.
+ * @ref regfi_free_record.
*
* @ingroup regfiIteratorLayer
*/
-REGFI_VK_REC* regfi_iterator_cur_value(REGFI_ITERATOR* i);
+_EXPORT()
+const REGFI_VK* regfi_iterator_cur_value(REGFI_ITERATOR* i);
-/** Increments the internal value index to the next value in the value-list and
- * returns the value for that index.
+/** Increments the internal value index to the next value in the value-list.
*
* @param i the iterator
*
- * @return A newly allocated key structure for the next value or NULL on
- * failure. Newly allocated keys must be freed with regfi_free_value.
+ * @return True if another value should exist, false otherwise.
*
* @ingroup regfiIteratorLayer
*/
-REGFI_VK_REC* regfi_iterator_next_value(REGFI_ITERATOR* i);
+_EXPORT()
+bool regfi_iterator_next_value(REGFI_ITERATOR* i);
/** Searches for a value with a given name under the current key.
*
- * @param i the iterator
- * @param value_name value name to search for
+ * @param i the iterator
+ * @param name value name to search for
*
* @return True if such a value was found, false otherwise. If a value is
* found, the current value index is set to that value. Otherwise,
@@ -1133,36 +1445,27 @@ REGFI_VK_REC* regfi_iterator_next_value(REGFI_ITERATOR* i);
*
* @ingroup regfiIteratorLayer
*/
-bool regfi_iterator_find_value(REGFI_ITERATOR* i,
- const char* value_name);
-
-/** Retrieves classname for a given key.
- *
- * @param i the iterator
- * @param key the key whose classname is desired
- *
- * @return Returns a newly allocated classname structure, or NULL on failure.
- * Classname structures must be freed with regfi_free_classname.
- *
- * @ingroup regfiIteratorLayer
- */
-REGFI_CLASSNAME* regfi_iterator_fetch_classname(REGFI_ITERATOR* i,
- const REGFI_NK_REC* key);
+_EXPORT()
+bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* name);
-/** Retrieves data for a given value.
+/** Returns the current key and all parent keys as a list of NK records
*
* @param i the iterator
- * @param value the value whose data is desired
*
- * @return Returns a newly allocated data structure, or NULL on failure.
- * Data structures must be freed with regfi_free_data.
+ * @return An array of NK record pointers terminated by a NULL pointer.
+ * This array may be passed directly to regfi_free_record to free
+ * the entire array.
+ *
+ * @note In order to use an element of the array independently from the
+ * array (that is, to hold a pointer to an individual NK record while
+ * freeing the remaining array), callers must first use
+ * regfi_reference_record on the elements to be kept.
*
* @ingroup regfiIteratorLayer
*/
-REGFI_DATA* regfi_iterator_fetch_data(REGFI_ITERATOR* i,
- const REGFI_VK_REC* value);
-
+_EXPORT()
+const REGFI_NK** regfi_iterator_ancestry(REGFI_ITERATOR* i);
/******************************************************************************/
@@ -1171,15 +1474,15 @@ REGFI_DATA* regfi_iterator_fetch_data(REGFI_ITERATOR* i,
*/
/******************************************************************************/
-/** Loads a key at a given file offset along with associated data structures.
+/** Loads a key and associated data structures given a file offset.
*
* XXX: finish documenting
*
* @ingroup regfiGlueLayer
*/
-REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32_t offset,
- REGFI_ENCODING output_encoding,
- bool strict);
+_EXPORT()
+REGFI_NK* regfi_load_key(REGFI_FILE* file, uint32_t offset,
+ bool strict);
/** Loads a value at a given file offset alng with associated data structures.
@@ -1188,9 +1491,9 @@ REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32_t offset,
*
* @ingroup regfiGlueLayer
*/
-REGFI_VK_REC* regfi_load_value(REGFI_FILE* file, uint32_t offset,
- REGFI_ENCODING output_encoding,
- bool strict);
+_EXPORT()
+REGFI_VK* regfi_load_value(REGFI_FILE* file, uint32_t offset,
+ bool strict);
/** Loads a logical subkey list in its entirety which may span multiple records.
@@ -1199,9 +1502,10 @@ REGFI_VK_REC* regfi_load_value(REGFI_FILE* file, uint32_t offset,
*
* @ingroup regfiGlueLayer
*/
-REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32_t offset,
- uint32_t num_keys, uint32_t max_size,
- bool strict);
+_EXPORT()
+REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32_t offset,
+ uint32_t num_keys, uint32_t max_size,
+ bool strict);
/** Loads a valuelist.
@@ -1210,9 +1514,10 @@ REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32_t offset,
*
* @ingroup regfiGlueLayer
*/
-REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32_t offset,
- uint32_t num_values, uint32_t max_size,
- bool strict);
+_EXPORT()
+REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32_t offset,
+ uint32_t num_values, uint32_t max_size,
+ bool strict);
/** Loads a data record which may be contained in the virtual offset, in a
@@ -1222,9 +1527,10 @@ REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32_t offset,
*
* @ingroup regfiGlueLayer
*/
-REGFI_BUFFER regfi_load_data(REGFI_FILE* file, uint32_t voffset,
- uint32_t length, bool data_in_offset,
- bool strict);
+_EXPORT()
+REGFI_BUFFER regfi_load_data(REGFI_FILE* file, uint32_t voffset,
+ uint32_t length, bool data_in_offset,
+ bool strict);
/** Loads the data associated with a big data record at the specified offset.
@@ -1233,10 +1539,11 @@ REGFI_BUFFER regfi_load_data(REGFI_FILE* file, uint32_t voffset,
*
* @ingroup regfiGlueLayer
*/
-REGFI_BUFFER regfi_load_big_data(REGFI_FILE* file, uint32_t offset,
- uint32_t data_length,uint32_t cell_length,
- range_list* used_ranges,
- bool strict);
+_EXPORT()
+REGFI_BUFFER regfi_load_big_data(REGFI_FILE* file, uint32_t offset,
+ uint32_t data_length,uint32_t cell_length,
+ range_list* used_ranges,
+ bool strict);
/** Given raw data, attempts to interpret the data based on a specified registry
@@ -1246,28 +1553,11 @@ REGFI_BUFFER regfi_load_big_data(REGFI_FILE* file, uint32_t offset,
*
* @ingroup regfiGlueLayer
*/
-bool regfi_interpret_data(REGFI_FILE* file,
- REGFI_ENCODING string_encoding,
- uint32_t type, REGFI_DATA* data);
+_EXPORT()
+bool regfi_interpret_data(REGFI_FILE* file,
+ uint32_t type, REGFI_DATA* data);
-/** Frees the memory associated with a REGFI_CLASSNAME data structure.
- *
- * XXX: finish documenting
- *
- * @ingroup regfiGlueLayer
- */
-void regfi_free_classname(REGFI_CLASSNAME* classname);
-
-
-/** Frees the memory associated with a REGFI_DATA data structure.
- *
- * XXX: finish documenting
- *
- * @ingroup regfiGlueLayer
- */
-void regfi_free_data(REGFI_DATA* data);
-
/* These are cached so return values don't need to be freed. */
@@ -1277,8 +1567,11 @@ void regfi_free_data(REGFI_DATA* data);
*
* @ingroup regfiGlueLayer
*/
-const REGFI_SK_REC* regfi_load_sk(REGFI_FILE* file, uint32_t offset,
- bool strict);
+_EXPORT()
+const REGFI_SK* regfi_load_sk(REGFI_FILE* file, uint32_t offset,
+ bool strict);
+
+
/** Retrieves the HBIN data structure stored at the specified offset.
@@ -1287,7 +1580,8 @@ const REGFI_SK_REC* regfi_load_sk(REGFI_FILE* file, uint32_t offset,
*
* @ingroup regfiGlueLayer
*/
-const REGFI_HBIN* regfi_lookup_hbin(REGFI_FILE* file, uint32_t offset);
+_EXPORT()
+const REGFI_HBIN* regfi_lookup_hbin(REGFI_FILE* file, uint32_t offset);
@@ -1297,9 +1591,12 @@ const REGFI_HBIN* regfi_lookup_hbin(REGFI_FILE* file, uint32_t offset);
*/
/******************************************************************************/
-REGFI_FILE* regfi_parse_regf(int fd, bool strict);
-REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32_t offset,
- bool strict);
+_EXPORT()
+REGFI_FILE* regfi_parse_regf(REGFI_RAW_FILE* file_cb, bool strict);
+
+_EXPORT()
+REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32_t offset,
+ bool strict);
/** Parses an NK record at the specified offset
@@ -1314,8 +1611,9 @@ REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32_t offset,
*
* @ingroup regfiParseLayer
*/
-REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32_t offset,
- uint32_t max_size, bool strict);
+_EXPORT()
+REGFI_NK* regfi_parse_nk(REGFI_FILE* file, uint32_t offset,
+ uint32_t max_size, bool strict);
/** Parses a single cell containing a subkey-list record.
@@ -1324,8 +1622,9 @@ REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32_t offset,
*
* @ingroup regfiParseLayer
*/
-REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32_t offset,
- uint32_t max_size, bool strict);
+_EXPORT()
+REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32_t offset,
+ uint32_t max_size, bool strict);
/** Parses a VK (value) record at the specified offset
@@ -1334,8 +1633,9 @@ REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32_t offset,
*
* @ingroup regfiParseLayer
*/
-REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32_t offset,
- uint32_t max_size, bool strict);
+_EXPORT()
+REGFI_VK* regfi_parse_vk(REGFI_FILE* file, uint32_t offset,
+ uint32_t max_size, bool strict);
/** Parses an SK (security) record at the specified offset
@@ -1344,8 +1644,9 @@ REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32_t offset,
*
* @ingroup regfiParseLayer
*/
-REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32_t offset,
- uint32_t max_size, bool strict);
+_EXPORT()
+REGFI_SK* regfi_parse_sk(REGFI_FILE* file, uint32_t offset,
+ uint32_t max_size, bool strict);
/** Retrieves information on all cells in the registry hive which are
@@ -1357,7 +1658,8 @@ REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32_t offset,
*
* @ingroup regfiParseLayer
*/
-range_list* regfi_parse_unalloc_cells(REGFI_FILE* file);
+_EXPORT()
+range_list* regfi_parse_unalloc_cells(REGFI_FILE* file);
/** Helper function to parse a cell
@@ -1366,9 +1668,10 @@ range_list* regfi_parse_unalloc_cells(REGFI_FILE* file);
*
* @ingroup regfiParseLayer
*/
-bool regfi_parse_cell(int fd, uint32_t offset,
- uint8_t* hdr, uint32_t hdr_len,
- uint32_t* cell_length, bool* unalloc);
+_EXPORT()
+bool regfi_parse_cell(REGFI_RAW_FILE* file_cb, uint32_t offset,
+ uint8_t* hdr, uint32_t hdr_len,
+ uint32_t* cell_length, bool* unalloc);
/** Parses a classname cell
@@ -1377,9 +1680,10 @@ bool regfi_parse_cell(int fd, uint32_t offset,
*
* @ingroup regfiParseLayer
*/
-uint8_t* regfi_parse_classname(REGFI_FILE* file, uint32_t offset,
- uint16_t* name_length,
- uint32_t max_size, bool strict);
+_EXPORT()
+uint8_t* regfi_parse_classname(REGFI_FILE* file, uint32_t offset,
+ uint16_t* name_length,
+ uint32_t max_size, bool strict);
/** Parses a single-cell data record
@@ -1388,8 +1692,9 @@ uint8_t* regfi_parse_classname(REGFI_FILE* file, uint32_t offset,
*
* @ingroup regfiParseLayer
*/
-REGFI_BUFFER regfi_parse_data(REGFI_FILE* file, uint32_t offset,
- uint32_t length, bool strict);
+_EXPORT()
+REGFI_BUFFER regfi_parse_data(REGFI_FILE* file, uint32_t offset,
+ uint32_t length, bool strict);
/** Parses a "little data" record which is stored entirely within the
@@ -1399,24 +1704,37 @@ REGFI_BUFFER regfi_parse_data(REGFI_FILE* file, uint32_t offset,
*
* @ingroup regfiParseLayer
*/
-REGFI_BUFFER regfi_parse_little_data(REGFI_FILE* file, uint32_t voffset,
- uint32_t length, bool strict);
+_EXPORT()
+REGFI_BUFFER regfi_parse_little_data(REGFI_FILE* file, uint32_t voffset,
+ uint32_t length, bool strict);
/******************************************************************************/
-/* Private Functions */
+/* Private (and undocumented) Functions */
/******************************************************************************/
-REGFI_NK_REC* regfi_rootkey(REGFI_FILE* file,
- REGFI_ENCODING output_encoding);
-void regfi_subkeylist_free(REGFI_SUBKEY_LIST* list);
-uint32_t regfi_read(int fd, uint8_t* buf, uint32_t* length);
-
+int64_t regfi_raw_seek(REGFI_RAW_FILE* self,
+ uint64_t offset, int whence);
+ssize_t regfi_raw_read(REGFI_RAW_FILE* self,
+ void* buf, size_t count);
+_EXPORT()
+uint64_t regfi_seek(REGFI_RAW_FILE* file_cb,
+ uint64_t offset, int whence);
+_EXPORT()
+uint32_t regfi_read(REGFI_RAW_FILE* file_cb,
+ uint8_t* buf, uint32_t* length);
+
+_EXPORT()
const char* regfi_type_val2str(unsigned int val);
+_EXPORT()
int regfi_type_str2val(const char* str);
+_EXPORT()
char* regfi_get_sacl(WINSEC_DESC* sec_desc);
+_EXPORT()
char* regfi_get_dacl(WINSEC_DESC* sec_desc);
+_EXPORT()
char* regfi_get_owner(WINSEC_DESC* sec_desc);
+_EXPORT()
char* regfi_get_group(WINSEC_DESC* sec_desc);
REGFI_SUBKEY_LIST* regfi_merge_subkeylists(uint16_t num_lists,
@@ -1427,24 +1745,29 @@ REGFI_SUBKEY_LIST* regfi_load_subkeylist_aux(REGFI_FILE* file, uint32_t offse
uint8_t depth_left);
void regfi_add_message(REGFI_FILE* file, uint16_t msg_type,
const char* fmt, ...);
-REGFI_NK_REC* regfi_copy_nk(const REGFI_NK_REC* nk);
-REGFI_VK_REC* regfi_copy_vk(const REGFI_VK_REC* vk);
+REGFI_NK* regfi_copy_nk(const REGFI_NK* nk);
+REGFI_VK* regfi_copy_vk(const REGFI_VK* vk);
+_EXPORT()
int32_t regfi_calc_maxsize(REGFI_FILE* file, uint32_t offset);
-int32_t regfi_conv_charset(const char* input_charset,
- const char* output_charset,
- uint8_t* input, char* output,
- uint32_t input_len, uint32_t output_max);
+REGFI_BUFFER regfi_conv_charset(const char* input_charset, const char* output_charset,
+ uint8_t* input, uint32_t input_len);
+_EXPORT()
REGFI_DATA* regfi_buffer_to_data(REGFI_BUFFER raw_data);
/* XXX: move to base API and document */
-void regfi_unix2nt_time(REGFI_NTTIME* nt, time_t t);
-time_t regfi_nt2unix_time(const REGFI_NTTIME* nt);
+_EXPORT()
+REGFI_NTTIME regfi_unix2nt_time(time_t t);
+_EXPORT()
+double regfi_nt2unix_time(REGFI_NTTIME nt);
+
+_EXPORT()
+void regfi_interpret_keyname(REGFI_FILE* file, REGFI_NK* nk, bool strict);
+_EXPORT()
+void regfi_interpret_valuename(REGFI_FILE* file, REGFI_VK* vk, bool strict);
-void regfi_interpret_keyname(REGFI_FILE* file, REGFI_NK_REC* nk,
- REGFI_ENCODING output_encoding, bool strict);
-void regfi_interpret_valuename(REGFI_FILE* file, REGFI_VK_REC* vk,
- REGFI_ENCODING output_encoding, bool strict);
+_EXPORT()
+void regfi_init();
#endif /* _REGFI_H */
diff --git a/include/talloc.h b/include/talloc.h
deleted file mode 100644
index d7a08a6..0000000
--- a/include/talloc.h
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- Unix SMB/CIFS implementation.
- Samba temporary memory allocation functions
-
- Copyright (C) Andrew Tridgell 2004-2005
- Copyright (C) Stefan Metzmacher 2006
- Copyright (C) Timothy D. Morgan 2009
-
- ** NOTE! The following LGPL license applies to the talloc library.
- ** This does NOT imply that all of reglookup is released under the LGPL
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 3 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-/* $Id: talloc.h 168 2010-03-03 00:08:42Z tim $ */
-
-#ifndef _TALLOC_H_
-#define _TALLOC_H_
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <string.h>
-#include <stdbool.h>
-
-#ifndef MAX
-#define MAX(a,b) ((a)>(b)?(a):(b))
-#endif
-
-#ifndef MIN
-#define MIN(a,b) ((a)<(b)?(a):(b))
-#endif
-
-/*
- this uses a little trick to allow __LINE__ to be stringified
-*/
-#ifndef __location__
-#define __TALLOC_STRING_LINE1__(s) #s
-#define __TALLOC_STRING_LINE2__(s) __TALLOC_STRING_LINE1__(s)
-#define __TALLOC_STRING_LINE3__ __TALLOC_STRING_LINE2__(__LINE__)
-#define __location__ __FILE__ ":" __TALLOC_STRING_LINE3__
-#endif
-
-#ifndef TALLOC_DEPRECATED
-#define TALLOC_DEPRECATED 0
-#endif
-
-#ifndef PRINTF_ATTRIBUTE
-#if (__GNUC__ >= 3)
-/** Use gcc attribute to check printf fns. a1 is the 1-based index of
- * the parameter containing the format, and a2 the index of the first
- * argument. Note that some gcc 2.x versions don't handle this
- * properly **/
-#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2)))
-#else
-#define PRINTF_ATTRIBUTE(a1, a2)
-#endif
-#endif
-
-#define talloc_set_destructor(ptr, function) \
- _talloc_set_destructor((ptr), (int (*)(void *))(function))
-#define _TALLOC_TYPEOF(ptr) void *
-#define talloc_steal(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr))
-
-#define talloc_reference(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_reference((ctx),(ptr))
-#define talloc_move(ctx, ptr) (_TALLOC_TYPEOF(*(ptr)))_talloc_move((ctx),(void *)(ptr))
-
-/* useful macros for creating type checked pointers */
-#define talloc(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type)
-#define talloc_size(ctx, size) talloc_named_const(ctx, size, __location__)
-#define talloc_ptrtype(ctx, ptr) (_TALLOC_TYPEOF(ptr))talloc_size(ctx, sizeof(*(ptr)))
-
-#define talloc_new(ctx) talloc_named_const(ctx, 0, "talloc_new: " __location__)
-
-#define talloc_zero(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type)
-#define talloc_zero_size(ctx, size) _talloc_zero(ctx, size, __location__)
-
-#define talloc_zero_array(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type)
-#define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type)
-#define talloc_array_size(ctx, size, count) _talloc_array(ctx, size, count, __location__)
-#define talloc_array_ptrtype(ctx, ptr, count) (_TALLOC_TYPEOF(ptr))talloc_array_size(ctx, sizeof(*(ptr)), count)
-
-#define talloc_realloc(ctx, p, type, count) (type *)_talloc_realloc_array(ctx, p, sizeof(type), count, #type)
-#define talloc_realloc_size(ctx, ptr, size) _talloc_realloc(ctx, ptr, size, __location__)
-
-#define talloc_memdup(t, p, size) _talloc_memdup(t, p, size, __location__)
-
-#define talloc_set_type(ptr, type) talloc_set_name_const(ptr, #type)
-#define talloc_get_type(ptr, type) (type *)talloc_check_name(ptr, #type)
-
-#define talloc_find_parent_bytype(ptr, type) (type *)talloc_find_parent_byname(ptr, #type)
-
-/* The following definitions come from talloc.c */
-void *_talloc(const void *context, size_t size);
-void *talloc_pool(const void *context, size_t size);
-void _talloc_set_destructor(const void *ptr, int (*destructor)(void *));
-int talloc_increase_ref_count(const void *ptr);
-size_t talloc_reference_count(const void *ptr);
-void *_talloc_reference(const void *context, const void *ptr);
-int talloc_unlink(const void *context, void *ptr);
-const char *talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
-void talloc_set_name_const(const void *ptr, const char *name);
-void *talloc_named(const void *context, size_t size,
- const char *fmt, ...) PRINTF_ATTRIBUTE(3,4);
-void *talloc_named_const(const void *context, size_t size, const char *name);
-const char *talloc_get_name(const void *ptr);
-void *talloc_check_name(const void *ptr, const char *name);
-void *talloc_parent(const void *ptr);
-const char *talloc_parent_name(const void *ptr);
-void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2);
-int talloc_free(void *ptr);
-void talloc_free_children(void *ptr);
-void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name);
-void *_talloc_steal(const void *new_ctx, const void *ptr);
-void *_talloc_move(const void *new_ctx, const void *pptr);
-size_t talloc_total_size(const void *ptr);
-size_t talloc_total_blocks(const void *ptr);
-void talloc_report_depth_cb(const void *ptr, int depth, int max_depth,
- void (*callback)(const void *ptr,
- int depth, int max_depth,
- int is_ref,
- void *private_data),
- void *private_data);
-void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f);
-void talloc_report_full(const void *ptr, FILE *f);
-void talloc_report(const void *ptr, FILE *f);
-void talloc_enable_null_tracking(void);
-void talloc_disable_null_tracking(void);
-void talloc_enable_leak_report(void);
-void talloc_enable_leak_report_full(void);
-void *_talloc_zero(const void *ctx, size_t size, const char *name);
-void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name);
-void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name);
-void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name);
-void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name);
-void *talloc_realloc_fn(const void *context, void *ptr, size_t size);
-void *talloc_autofree_context(void);
-size_t talloc_get_size(const void *ctx);
-void *talloc_find_parent_byname(const void *ctx, const char *name);
-void talloc_show_parents(const void *context, FILE *file);
-int talloc_is_parent(const void *context, const void *ptr);
-
-char *talloc_strdup(const void *t, const char *p);
-char *talloc_strdup_append(char *s, const char *a);
-char *talloc_strdup_append_buffer(char *s, const char *a);
-
-char *talloc_strndup(const void *t, const char *p, size_t n);
-char *talloc_strndup_append(char *s, const char *a, size_t n);
-char *talloc_strndup_append_buffer(char *s, const char *a, size_t n);
-
-char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
-char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
-char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
-
-char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
-char *talloc_asprintf_append(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
-char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
-
-#endif
diff --git a/include/void_stack.h b/include/void_stack.h
index dc1c048..eb17766 100644
--- a/include/void_stack.h
+++ b/include/void_stack.h
@@ -14,7 +14,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: void_stack.h 169 2010-03-03 19:24:58Z tim $
+ * $Id: void_stack.h 253 2011-06-13 02:27:42Z tim $
*/
/**
@@ -31,7 +31,9 @@
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
-#include "talloc.h"
+#include <talloc.h>
+
+#include "compat.h"
/** XXX: document this. */
typedef struct _void_stack
@@ -58,6 +60,7 @@ typedef struct _void_stack_iterator
* @return a pointer to the newly allocated void_stack,
* or NULL if an error occurred.
*/
+_EXPORT()
void_stack* void_stack_new(unsigned short max_size);
@@ -67,6 +70,7 @@ void_stack* void_stack_new(unsigned short max_size);
*
* @return a pointer to the duplicate void_stack, or NULL if an error occurred.
*/
+_EXPORT()
void_stack* void_stack_copy(const void_stack* v);
@@ -77,6 +81,7 @@ void_stack* void_stack_copy(const void_stack* v);
* @return a pointer to the duplicate void_stack
* (which will be in reverse order), or NULL if an error occurred.
*/
+_EXPORT()
void_stack* void_stack_copy_reverse(const void_stack* v);
@@ -85,6 +90,7 @@ void_stack* void_stack_copy_reverse(const void_stack* v);
*
* @param stack the stack to be free()d.
*/
+_EXPORT()
void void_stack_free(void_stack* stack);
@@ -97,6 +103,7 @@ void void_stack_free(void_stack* stack);
*
* @param stack the stack to be free()d.
*/
+_EXPORT()
void void_stack_free_deep(void_stack* stack);
@@ -106,6 +113,7 @@ void void_stack_free_deep(void_stack* stack);
*
* @return the number of elements currently on the stack.
*/
+_EXPORT()
unsigned short void_stack_size(const void_stack* stack);
@@ -116,6 +124,7 @@ unsigned short void_stack_size(const void_stack* stack);
* @return a pointer to the popped stack element, or NULL if no elements exist
* on the stack.
*/
+_EXPORT()
void* void_stack_pop(void_stack* stack);
@@ -126,6 +135,7 @@ void* void_stack_pop(void_stack* stack);
*
* @return true if the element was successfully added, false otherwise.
*/
+_EXPORT()
bool void_stack_push(void_stack* stack, void* e);
@@ -136,6 +146,7 @@ bool void_stack_push(void_stack* stack, void* e);
* @return a pointer to the current element on the top of the stack, or NULL if
* no elements exist in the stack.
*/
+_EXPORT()
const void* void_stack_cur(const void_stack* stack);
@@ -145,6 +156,7 @@ const void* void_stack_cur(const void_stack* stack);
*
* @return a new void_stack_iterator, or NULL if an error occurred.
*/
+_EXPORT()
void_stack_iterator* void_stack_iterator_new(const void_stack* stack);
@@ -154,6 +166,7 @@ void_stack_iterator* void_stack_iterator_new(const void_stack* stack);
*
* @param iter the void_stack_iterator to be free()d.
*/
+_EXPORT()
void void_stack_iterator_free(void_stack_iterator* iter);
@@ -165,6 +178,7 @@ void void_stack_iterator_free(void_stack_iterator* iter);
*
* @return a pointer to the next element.
*/
+_EXPORT()
const void* void_stack_iterator_next(void_stack_iterator* iter);
diff --git a/include/winsec.h b/include/winsec.h
index 6c7de49..9df4a30 100644
--- a/include/winsec.h
+++ b/include/winsec.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005,2009-2010 Timothy D. Morgan
+ * Copyright (C) 2005,2009-2011 Timothy D. Morgan
* Copyright (C) 1992-2005 Samba development team
*
* This program is free software; you can redistribute it and/or modify
@@ -15,7 +15,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: winsec.h 169 2010-03-03 19:24:58Z tim $
+ * $Id: winsec.h 261 2011-06-17 00:55:49Z tim $
*/
/**
@@ -43,8 +43,9 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include <talloc.h>
-#include "talloc.h"
+#include "compat.h"
#include "byteorder.h"
@@ -216,6 +217,7 @@ typedef struct _winsec_desc
*
* XXX: finish documenting
*/
+_EXPORT()
WINSEC_DESC* winsec_parse_descriptor(const uint8_t* buf, uint32_t buf_len);
@@ -223,12 +225,14 @@ WINSEC_DESC* winsec_parse_descriptor(const uint8_t* buf, uint32_t buf_len);
*
* XXX: finish documenting
*/
+_EXPORT()
void winsec_free_descriptor(WINSEC_DESC* desc);
/**
*
* XXX: finish documenting
*/
+_EXPORT()
WINSEC_DESC* winsec_parse_desc(void* talloc_ctx,
const uint8_t* buf, uint32_t buf_len);
@@ -236,6 +240,7 @@ WINSEC_DESC* winsec_parse_desc(void* talloc_ctx,
*
* XXX: finish documenting
*/
+_EXPORT()
WINSEC_ACL* winsec_parse_acl(void* talloc_ctx,
const uint8_t* buf, uint32_t buf_len);
@@ -243,6 +248,7 @@ WINSEC_ACL* winsec_parse_acl(void* talloc_ctx,
*
* XXX: finish documenting
*/
+_EXPORT()
WINSEC_ACE* winsec_parse_ace(void* talloc_ctx,
const uint8_t* buf, uint32_t buf_len);
@@ -250,6 +256,7 @@ WINSEC_ACE* winsec_parse_ace(void* talloc_ctx,
*
* XXX: finish documenting
*/
+_EXPORT()
WINSEC_DOM_SID* winsec_parse_dom_sid(void* talloc_ctx,
const uint8_t* buf, uint32_t buf_len);
@@ -257,6 +264,7 @@ WINSEC_DOM_SID* winsec_parse_dom_sid(void* talloc_ctx,
*
* XXX: finish documenting
*/
+_EXPORT()
WINSEC_UUID* winsec_parse_uuid(void* talloc_ctx,
const uint8_t* buf, uint32_t buf_len);
@@ -265,48 +273,63 @@ WINSEC_UUID* winsec_parse_uuid(void* talloc_ctx,
*
* XXX: finish documenting
*/
+_EXPORT()
size_t winsec_sid_size(const WINSEC_DOM_SID* sid);
/**
*
* XXX: finish documenting
*/
+_EXPORT()
int winsec_sid_compare_auth(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2);
/**
*
* XXX: finish documenting
*/
+_EXPORT()
int winsec_sid_compare(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2);
/**
*
* XXX: finish documenting
*/
+_EXPORT()
bool winsec_sid_equal(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2);
/**
*
* XXX: finish documenting
*/
+_EXPORT()
+char* winsec_sid2str(const WINSEC_DOM_SID* sid);
+
+/**
+ *
+ * XXX: finish documenting
+ */
+_EXPORT()
bool winsec_desc_equal(WINSEC_DESC* s1, WINSEC_DESC* s2);
/**
*
* XXX: finish documenting
*/
+_EXPORT()
bool winsec_acl_equal(WINSEC_ACL* s1, WINSEC_ACL* s2);
/**
*
* XXX: finish documenting
*/
+_EXPORT()
bool winsec_ace_equal(WINSEC_ACE* s1, WINSEC_ACE* s2);
/**
*
* XXX: finish documenting
*/
+_EXPORT()
bool winsec_ace_object(uint8_t type);
#endif /* _WINSEC_H */
diff --git a/lib/lru_cache.c b/lib/lru_cache.c
index dfa9111..67c1fce 100644
--- a/lib/lru_cache.c
+++ b/lib/lru_cache.c
@@ -14,7 +14,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: lru_cache.c 169 2010-03-03 19:24:58Z tim $
+ * $Id: lru_cache.c 252 2011-05-08 17:33:49Z tim $
*/
/**
@@ -118,13 +118,15 @@ lru_cache* lru_cache_create_ctx(void* talloc_ctx, uint32_t max_keys,
if(max_keys == 0)
ret_val->num_buckets = 1024;
+ else if(max_keys == 1)
+ ret_val->num_buckets = 1;
else
{
ret_val->num_buckets = max_keys/lru_cache_floor_log2(max_keys);
if(ret_val->num_buckets < 1)
ret_val->num_buckets = 1;
}
-
+
ret_val->table = talloc_array(ret_val,
lru_cache_element*, ret_val->num_buckets);
if(ret_val->table == NULL)
@@ -148,7 +150,7 @@ lru_cache* lru_cache_create_ctx(void* talloc_ctx, uint32_t max_keys,
void lru_cache_destroy(lru_cache* ht)
{
ht->secret = 0;
- talloc_free(ht);
+ talloc_unlink(NULL, ht);
}
@@ -176,7 +178,7 @@ bool lru_cache_update(lru_cache* ht, const void* index,
* so remove it from the list for now.
*/
if(ht->talloc_data)
- talloc_free(e->data);
+ talloc_unlink(e, e->data);
if(e->newer == NULL)
ht->newest = e->older;
@@ -223,7 +225,7 @@ bool lru_cache_update(lru_cache* ht, const void* index,
e->next = NULL;
if(ht->talloc_data)
- talloc_free(e->data);
+ talloc_unlink(e, e->data);
tmp_index = talloc_realloc_size(e, e->index, index_len);
if(tmp_index == NULL)
@@ -254,13 +256,12 @@ bool lru_cache_update(lru_cache* ht, const void* index,
memcpy(e->index, index, index_len);
e->index_len = index_len;
- /* Insert at beginning of chain, in a vaguely LRU style */
e->next = ht->table[hash];
ht->table[hash] = e;
}
- e->data = data;
if(ht->talloc_data)
- talloc_steal(e, e->data);
+ data = talloc_reference(e, data);
+ e->data = data;
/* Finally, let's insert the element to the newest position in the LRU list.*/
if(ht->newest != NULL)
diff --git a/lib/regfi.c b/lib/regfi.c
index d292dfe..aced069 100644
--- a/lib/regfi.c
+++ b/lib/regfi.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005-2010 Timothy D. Morgan
+ * Copyright (C) 2005-2011 Timothy D. Morgan
* Copyright (C) 2005 Gerald (Jerry) Carter
*
* This program is free software; you can redistribute it and/or modify
@@ -15,7 +15,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: regfi.c 173 2010-03-08 03:39:09Z tim $
+ * $Id: regfi.c 274 2011-09-28 00:29:25Z tim $
*/
/**
@@ -33,6 +33,11 @@
#include "regfi.h"
+/* Library version can be overridden at build time */
+#ifndef REGFI_VERSION
+#define REGFI_VERSION "trunk"
+#endif
+
/* Registry types mapping */
const unsigned int regfi_num_reg_types = 12;
@@ -41,73 +46,161 @@ static const char* regfi_type_names[] =
"MULTI_SZ", "RSRC_LIST", "RSRC_DESC", "RSRC_REQ_LIST", "QWORD"};
const char* regfi_encoding_names[] =
- {"US-ASCII//TRANSLIT", "UTF-8//TRANSLIT", "UTF-16LE//TRANSLIT"};
+ {"US-ASCII", "UTF-8", "UTF-16LE"};
+
+
+/* Ensures regfi_init runs only once */
+static pthread_once_t regfi_init_once = PTHREAD_ONCE_INIT;
+
+
+/******************************************************************************
+ ******************************************************************************/
+const char* regfi_version()
+{
+ return REGFI_VERSION;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+void regfi_log_free(void* ptr)
+{
+ REGFI_LOG* log_info = (REGFI_LOG*)ptr;
+
+ if(log_info->messages != NULL)
+ free(log_info->messages);
+
+ talloc_free(log_info);
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+void regfi_init()
+{
+ int err;
+ if((err = pthread_key_create(®fi_log_key, regfi_log_free)) != 0)
+ fprintf(stderr, "ERROR: key_create: %s\n", strerror(err));
+ errno = err;
+}
/******************************************************************************
******************************************************************************/
-void regfi_add_message(REGFI_FILE* file, uint16_t msg_type, const char* fmt, ...)
+REGFI_LOG* regfi_log_new()
{
- /* XXX: This function is not particularly efficient,
- * but then it is mostly used during errors.
+ int err;
+ REGFI_LOG* log_info = talloc(NULL, REGFI_LOG);
+ if(log_info == NULL)
+ return NULL;
+
+ log_info->msg_mask = REGFI_DEFAULT_LOG_MASK;
+ log_info->messages = NULL;
+
+ pthread_once(®fi_init_once, regfi_init);
+
+ if((err = pthread_setspecific(regfi_log_key, log_info)) != 0)
+ {
+ fprintf(stderr, "ERROR: setspecific: %s\n", strerror(err));
+ goto fail;
+ }
+
+ return log_info;
+
+ fail:
+ talloc_free(log_info);
+ errno = err;
+ return NULL;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+void regfi_log_add(uint16_t msg_type, const char* fmt, ...)
+{
+ /* XXX: Switch internal storage over to a linked list or stack.
+ * Then add a regfi_log_get function that returns the list in some
+ * convenient, user-friendly data structure. regfi_log_get_str should
+ * stick around and will simply smush the list into a big string when
+ * it's called, rather than having messages smushed when they're first
+ * written to the log.
*/
uint32_t buf_size, buf_used;
char* new_msg;
+ REGFI_LOG* log_info;
va_list args;
- if((file->msg_mask & msg_type) != 0)
- {
- if(file->last_message == NULL)
- buf_used = 0;
- else
- buf_used = strlen(file->last_message);
-
- buf_size = buf_used+strlen(fmt)+160;
- new_msg = realloc(file->last_message, buf_size);
- if(new_msg == NULL)
- /* XXX: should we report this? */
- return;
+ log_info = (REGFI_LOG*)pthread_getspecific(regfi_log_key);
+ if(log_info == NULL && (log_info = regfi_log_new()) == NULL)
+ return;
- switch (msg_type)
- {
- case REGFI_MSG_INFO:
- strcpy(new_msg+buf_used, "INFO: ");
- buf_used += 6;
- break;
- case REGFI_MSG_WARN:
- strcpy(new_msg+buf_used, "WARN: ");
- buf_used += 6;
- break;
- case REGFI_MSG_ERROR:
- strcpy(new_msg+buf_used, "ERROR: ");
- buf_used += 7;
- break;
- }
+ if((log_info->msg_mask & msg_type) == 0)
+ return;
- va_start(args, fmt);
- vsnprintf(new_msg+buf_used, buf_size-buf_used, fmt, args);
- va_end(args);
- strncat(new_msg, "\n", buf_size-1);
-
- file->last_message = new_msg;
+ if(log_info->messages == NULL)
+ buf_used = 0;
+ else
+ buf_used = strlen(log_info->messages);
+
+ buf_size = buf_used+strlen(fmt)+160;
+ new_msg = realloc(log_info->messages, buf_size);
+ if(new_msg == NULL)
+ /* XXX: should we report this? */
+ return;
+
+ switch (msg_type)
+ {
+ case REGFI_LOG_INFO:
+ strcpy(new_msg+buf_used, "INFO: ");
+ buf_used += 6;
+ break;
+ case REGFI_LOG_WARN:
+ strcpy(new_msg+buf_used, "WARN: ");
+ buf_used += 6;
+ break;
+ case REGFI_LOG_ERROR:
+ strcpy(new_msg+buf_used, "ERROR: ");
+ buf_used += 7;
+ break;
}
+
+ va_start(args, fmt);
+ vsnprintf(new_msg+buf_used, buf_size-buf_used, fmt, args);
+ va_end(args);
+ strncat(new_msg, "\n", buf_size-1);
+
+ log_info->messages = new_msg;
}
/******************************************************************************
******************************************************************************/
-char* regfi_get_messages(REGFI_FILE* file)
+char* regfi_log_get_str()
{
- char* ret_val = file->last_message;
- file->last_message = NULL;
+ char* ret_val;
+ REGFI_LOG* log_info = (REGFI_LOG*)pthread_getspecific(regfi_log_key);
+ if(log_info == NULL && (log_info = regfi_log_new()) == NULL)
+ return NULL;
+
+ ret_val = log_info->messages;
+ log_info->messages = NULL;
return ret_val;
}
-void regfi_set_message_mask(REGFI_FILE* file, uint16_t mask)
+/******************************************************************************
+ ******************************************************************************/
+bool regfi_log_set_mask(uint16_t msg_mask)
{
- file->msg_mask = mask;
+ REGFI_LOG* log_info = (REGFI_LOG*)pthread_getspecific(regfi_log_key);
+ if(log_info == NULL && (log_info = regfi_log_new()) == NULL)
+ {
+ return false;
+ }
+
+ log_info->msg_mask = msg_mask;
+ return true;
}
@@ -309,28 +402,6 @@ char* regfi_ace_perms2str(uint32_t perms)
}
-char* regfi_sid2str(WINSEC_DOM_SID* sid)
-{
- uint32_t i, size = WINSEC_MAX_SUBAUTHS*11 + 24;
- uint32_t left = size;
- uint8_t comps = sid->num_auths;
- char* ret_val = malloc(size);
-
- if(ret_val == NULL)
- return NULL;
-
- if(comps > WINSEC_MAX_SUBAUTHS)
- comps = WINSEC_MAX_SUBAUTHS;
-
- left -= sprintf(ret_val, "S-%u-%u", sid->sid_rev_num, sid->id_auth[5]);
-
- for (i = 0; i < comps; i++)
- left -= snprintf(ret_val+(size-left), left, "-%u", sid->sub_auths[i]);
-
- return ret_val;
-}
-
-
char* regfi_get_acl(WINSEC_ACL* acl)
{
uint32_t i, extra, size = 0;
@@ -346,7 +417,7 @@ char* regfi_get_acl(WINSEC_ACL* acl)
for (i = 0; i < acl->num_aces && !failed; i++)
{
- sid_str = regfi_sid2str(acl->aces[i]->trustee);
+ sid_str = winsec_sid2str(acl->aces[i]->trustee);
type_str = regfi_ace_type2str(acl->aces[i]->type);
perms_str = regfi_ace_perms2str(acl->aces[i]->access_mask);
flags_str = regfi_ace_flags2str(acl->aces[i]->flags);
@@ -411,20 +482,116 @@ char* regfi_get_dacl(WINSEC_DESC *sec_desc)
char* regfi_get_owner(WINSEC_DESC *sec_desc)
{
- return regfi_sid2str(sec_desc->owner_sid);
+ return winsec_sid2str(sec_desc->owner_sid);
}
char* regfi_get_group(WINSEC_DESC *sec_desc)
{
- return regfi_sid2str(sec_desc->grp_sid);
+ return winsec_sid2str(sec_desc->grp_sid);
+}
+
+
+bool regfi_read_lock(REGFI_FILE* file, pthread_rwlock_t* lock, const char* context)
+{
+ int lock_ret = pthread_rwlock_rdlock(lock);
+ if(lock_ret != 0)
+ {
+ regfi_log_add(REGFI_LOG_ERROR, "Error obtaining read lock in"
+ "%s due to: %s\n", context, strerror(lock_ret));
+ return false;
+ }
+
+ return true;
+}
+
+
+bool regfi_write_lock(REGFI_FILE* file, pthread_rwlock_t* lock, const char* context)
+{
+ int lock_ret = pthread_rwlock_wrlock(lock);
+ if(lock_ret != 0)
+ {
+ regfi_log_add(REGFI_LOG_ERROR, "Error obtaining write lock in"
+ "%s due to: %s\n", context, strerror(lock_ret));
+ return false;
+ }
+
+ return true;
+}
+
+
+bool regfi_rw_unlock(REGFI_FILE* file, pthread_rwlock_t* lock, const char* context)
+{
+ int lock_ret = pthread_rwlock_unlock(lock);
+ if(lock_ret != 0)
+ {
+ regfi_log_add(REGFI_LOG_ERROR, "Error releasing lock in"
+ "%s due to: %s\n", context, strerror(lock_ret));
+ return false;
+ }
+
+ return true;
+}
+
+
+bool regfi_lock(REGFI_FILE* file, pthread_mutex_t* lock, const char* context)
+{
+ int lock_ret = pthread_mutex_lock(lock);
+ if(lock_ret != 0)
+ {
+ regfi_log_add(REGFI_LOG_ERROR, "Error obtaining mutex lock in"
+ "%s due to: %s\n", context, strerror(lock_ret));
+ return false;
+ }
+
+ return true;
+}
+
+
+bool regfi_unlock(REGFI_FILE* file, pthread_mutex_t* lock, const char* context)
+{
+ int lock_ret = pthread_mutex_unlock(lock);
+ if(lock_ret != 0)
+ {
+ regfi_log_add(REGFI_LOG_ERROR, "Error releasing mutex lock in"
+ "%s due to: %s\n", context, strerror(lock_ret));
+ return false;
+ }
+
+ return true;
+}
+
+
+int64_t regfi_raw_seek(REGFI_RAW_FILE* self, uint64_t offset, int whence)
+{
+ if(sizeof(off_t) == 4 && offset > 2147483647)
+ {
+ errno = EOVERFLOW;
+ return -1;
+ }
+ return lseek(*(int*)self->state, offset, whence);
+}
+
+ssize_t regfi_raw_read(REGFI_RAW_FILE* self, void* buf, size_t count)
+{
+ return read(*(int*)self->state, buf, count);
+}
+
+
+/*****************************************************************************
+ * Convenience function to wrap up the ugly callback stuff
+ *****************************************************************************/
+uint64_t regfi_seek(REGFI_RAW_FILE* file_cb, uint64_t offset, int whence)
+{
+ return file_cb->seek(file_cb, offset, whence);
}
/*****************************************************************************
* This function is just like read(2), except that it continues to
* re-try reading from the file descriptor if EINTR or EAGAIN is received.
- * regfi_read will attempt to read length bytes from fd and write them to buf.
+ * regfi_read will attempt to read length bytes from the file and write them to
+ * buf.
*
* On success, 0 is returned. Upon failure, an errno code is returned.
*
@@ -432,14 +599,16 @@ char* regfi_get_group(WINSEC_DESC *sec_desc)
* parameter by reference. If both the return value and length parameter are
* returned as 0, then EOF was encountered immediately
*****************************************************************************/
-uint32_t regfi_read(int fd, uint8_t* buf, uint32_t* length)
+uint32_t regfi_read(REGFI_RAW_FILE* file_cb, uint8_t* buf, uint32_t* length)
{
uint32_t rsize = 0;
uint32_t rret = 0;
do
{
- rret = read(fd, buf + rsize, *length - rsize);
+ rret = file_cb->read(file_cb,
+ buf + rsize,
+ *length - rsize);
if(rret > 0)
rsize += rret;
}while(*length - rsize > 0
@@ -456,18 +625,18 @@ uint32_t regfi_read(int fd, uint8_t* buf, uint32_t* length)
/*****************************************************************************
*
*****************************************************************************/
-bool regfi_parse_cell(int fd, uint32_t offset, uint8_t* hdr, uint32_t hdr_len,
- uint32_t* cell_length, bool* unalloc)
+bool regfi_parse_cell(REGFI_RAW_FILE* file_cb, uint32_t offset, uint8_t* hdr,
+ uint32_t hdr_len, uint32_t* cell_length, bool* unalloc)
{
uint32_t length;
int32_t raw_length;
uint8_t tmp[4];
- if(lseek(fd, offset, SEEK_SET) == -1)
+ if(regfi_seek(file_cb, offset, SEEK_SET) == -1)
return false;
length = 4;
- if((regfi_read(fd, tmp, &length) != 0) || length != 4)
+ if((regfi_read(file_cb, tmp, &length) != 0) || length != 4)
return false;
raw_length = IVALS(tmp, 0);
@@ -488,7 +657,7 @@ bool regfi_parse_cell(int fd, uint32_t offset, uint8_t* hdr, uint32_t hdr_len,
if(hdr_len > 0)
{
length = hdr_len;
- if((regfi_read(fd, hdr, &length) != 0) || length != hdr_len)
+ if((regfi_read(file_cb, hdr, &length) != 0) || length != hdr_len)
return false;
}
@@ -552,7 +721,7 @@ REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32_t offset,
REGFI_MAX_SUBKEY_DEPTH);
if(ret_val == NULL)
{
- regfi_add_message(file, REGFI_MSG_WARN, "Failed to load subkey list at"
+ regfi_log_add(REGFI_LOG_WARN, "Failed to load subkey list at"
" offset 0x%.8X.", offset);
return NULL;
}
@@ -563,7 +732,7 @@ REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32_t offset,
* NK record, or the number in the subkey list. Just emit a warning for
* now if they don't match.
*/
- regfi_add_message(file, REGFI_MSG_WARN, "Number of subkeys listed in parent"
+ regfi_log_add(REGFI_LOG_WARN, "Number of subkeys listed in parent"
" (%d) did not match number found in subkey list/tree (%d)"
" while parsing subkey list/tree at offset 0x%.8X.",
num_keys, ret_val->num_keys, offset);
@@ -586,7 +755,7 @@ REGFI_SUBKEY_LIST* regfi_load_subkeylist_aux(REGFI_FILE* file, uint32_t offset,
if(depth_left == 0)
{
- regfi_add_message(file, REGFI_MSG_WARN, "Maximum depth reached"
+ regfi_log_add(REGFI_LOG_WARN, "Maximum depth reached"
" while parsing subkey list/tree at offset 0x%.8X.",
offset);
return NULL;
@@ -626,27 +795,30 @@ REGFI_SUBKEY_LIST* regfi_load_subkeylist_aux(REGFI_FILE* file, uint32_t offset,
REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32_t offset,
uint32_t max_size, bool strict)
{
- REGFI_SUBKEY_LIST* ret_val;
+ REGFI_SUBKEY_LIST* ret_val = NULL;
uint32_t i, cell_length, length, elem_size, read_len;
uint8_t* elements = NULL;
uint8_t buf[REGFI_SUBKEY_LIST_MIN_LEN];
bool unalloc;
bool recursive_type;
- if(!regfi_parse_cell(file->fd, offset, buf, REGFI_SUBKEY_LIST_MIN_LEN,
+ if(!regfi_lock(file, &file->cb_lock, "regfi_parse_subkeylist"))
+ goto fail;
+
+ if(!regfi_parse_cell(file->cb, offset, buf, REGFI_SUBKEY_LIST_MIN_LEN,
&cell_length, &unalloc))
{
- regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while "
+ regfi_log_add(REGFI_LOG_WARN, "Could not parse cell while "
"parsing subkey-list at offset 0x%.8X.", offset);
- return NULL;
+ goto fail_locked;
}
if(cell_length > max_size)
{
- regfi_add_message(file, REGFI_MSG_WARN, "Cell size longer than max_size"
+ regfi_log_add(REGFI_LOG_WARN, "Cell size longer than max_size"
" while parsing subkey-list at offset 0x%.8X.", offset);
if(strict)
- return NULL;
+ goto fail_locked;
cell_length = max_size & 0xFFFFFFF8;
}
@@ -657,20 +829,22 @@ REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32_t offset,
elem_size = sizeof(uint32_t);
}
else if(buf[0] == 'l' && buf[1] == 'i')
+ {
elem_size = sizeof(uint32_t);
+ }
else if((buf[0] == 'l') && (buf[1] == 'f' || buf[1] == 'h'))
elem_size = sizeof(REGFI_SUBKEY_LIST_ELEM);
else
{
- regfi_add_message(file, REGFI_MSG_ERROR, "Unknown magic number"
+ regfi_log_add(REGFI_LOG_ERROR, "Unknown magic number"
" (0x%.2X, 0x%.2X) encountered while parsing"
" subkey-list at offset 0x%.8X.", buf[0], buf[1], offset);
- return NULL;
+ goto fail_locked;
}
ret_val = talloc(NULL, REGFI_SUBKEY_LIST);
if(ret_val == NULL)
- return NULL;
+ goto fail_locked;
ret_val->offset = offset;
ret_val->cell_size = cell_length;
@@ -685,26 +859,29 @@ REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32_t offset,
length = elem_size*ret_val->num_children;
if(cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32_t) < length)
{
- regfi_add_message(file, REGFI_MSG_WARN, "Number of elements too large for"
+ regfi_log_add(REGFI_LOG_WARN, "Number of elements too large for"
" cell while parsing subkey-list at offset 0x%.8X.",
offset);
if(strict)
- goto fail;
+ goto fail_locked;
length = cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32_t);
}
ret_val->elements = talloc_array(ret_val, REGFI_SUBKEY_LIST_ELEM,
ret_val->num_children);
if(ret_val->elements == NULL)
- goto fail;
+ goto fail_locked;
elements = (uint8_t*)malloc(length);
if(elements == NULL)
- goto fail;
+ goto fail_locked;
read_len = length;
- if(regfi_read(file->fd, elements, &read_len) != 0 || read_len != length)
- goto fail;
+ if(regfi_read(file->cb, elements, &read_len) != 0 || read_len!=length)
+ goto fail_locked;
+
+ if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_subkeylist"))
+ goto fail;
if(elem_size == sizeof(uint32_t))
{
@@ -726,6 +903,8 @@ REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32_t offset,
return ret_val;
+ fail_locked:
+ regfi_unlock(file, &file->cb_lock, "regfi_parse_subkeylist");
fail:
if(elements != NULL)
free(elements);
@@ -782,7 +961,7 @@ REGFI_SUBKEY_LIST* regfi_merge_subkeylists(uint16_t num_lists,
}
for(i=0; i < num_lists; i++)
- regfi_subkeylist_free(lists[i]);
+ talloc_free(lists[i]);
free(lists);
return ret_val;
@@ -792,33 +971,36 @@ REGFI_SUBKEY_LIST* regfi_merge_subkeylists(uint16_t num_lists,
/******************************************************************************
*
******************************************************************************/
-REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32_t offset, uint32_t max_size,
+REGFI_SK* regfi_parse_sk(REGFI_FILE* file, uint32_t offset, uint32_t max_size,
bool strict)
{
- REGFI_SK_REC* ret_val;
+ REGFI_SK* ret_val = NULL;
uint8_t* sec_desc_buf = NULL;
uint32_t cell_length, length;
uint8_t sk_header[REGFI_SK_MIN_LENGTH];
bool unalloc = false;
- if(!regfi_parse_cell(file->fd, offset, sk_header, REGFI_SK_MIN_LENGTH,
+ if(!regfi_lock(file, &file->cb_lock, "regfi_parse_sk"))
+ goto fail;
+
+ if(!regfi_parse_cell(file->cb, offset, sk_header, REGFI_SK_MIN_LENGTH,
&cell_length, &unalloc))
{
- regfi_add_message(file, REGFI_MSG_WARN, "Could not parse SK record cell"
+ regfi_log_add(REGFI_LOG_WARN, "Could not parse SK record cell"
" at offset 0x%.8X.", offset);
- return NULL;
+ goto fail_locked;
}
if(sk_header[0] != 's' || sk_header[1] != 'k')
{
- regfi_add_message(file, REGFI_MSG_WARN, "Magic number mismatch in parsing"
+ regfi_log_add(REGFI_LOG_WARN, "Magic number mismatch in parsing"
" SK record at offset 0x%.8X.", offset);
- return NULL;
+ goto fail_locked;
}
- ret_val = talloc(NULL, REGFI_SK_REC);
+ ret_val = talloc(NULL, REGFI_SK);
if(ret_val == NULL)
- return NULL;
+ goto fail_locked;
ret_val->offset = offset;
/* XXX: Is there a way to be more conservative (shorter) with
@@ -831,9 +1013,9 @@ REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32_t offset, uint32_t max_siz
if((ret_val->cell_size < REGFI_SK_MIN_LENGTH)
|| (strict && (ret_val->cell_size & 0x00000007) != 0))
{
- regfi_add_message(file, REGFI_MSG_WARN, "Invalid cell size found while"
+ regfi_log_add(REGFI_LOG_WARN, "Invalid cell size found while"
" parsing SK record at offset 0x%.8X.", offset);
- goto fail;
+ goto fail_locked;
}
ret_val->magic[0] = sk_header[0];
@@ -848,38 +1030,41 @@ REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32_t offset, uint32_t max_siz
if((ret_val->prev_sk_off & 0x00000007) != 0
|| (ret_val->next_sk_off & 0x00000007) != 0)
{
- regfi_add_message(file, REGFI_MSG_WARN, "SK record's next/previous offsets"
+ regfi_log_add(REGFI_LOG_WARN, "SK record's next/previous offsets"
" are not a multiple of 8 while parsing SK record at"
" offset 0x%.8X.", offset);
- goto fail;
+ goto fail_locked;
}
if(ret_val->desc_size + REGFI_SK_MIN_LENGTH > ret_val->cell_size)
{
- regfi_add_message(file, REGFI_MSG_WARN, "Security descriptor too large for"
+ regfi_log_add(REGFI_LOG_WARN, "Security descriptor too large for"
" cell while parsing SK record at offset 0x%.8X.",
offset);
- goto fail;
+ goto fail_locked;
}
sec_desc_buf = (uint8_t*)malloc(ret_val->desc_size);
if(sec_desc_buf == NULL)
- goto fail;
+ goto fail_locked;
length = ret_val->desc_size;
- if(regfi_read(file->fd, sec_desc_buf, &length) != 0
+ if(regfi_read(file->cb, sec_desc_buf, &length) != 0
|| length != ret_val->desc_size)
{
- regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read security"
+ regfi_log_add(REGFI_LOG_ERROR, "Failed to read security"
" descriptor while parsing SK record at offset 0x%.8X.",
offset);
- goto fail;
+ goto fail_locked;
}
+ if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_sk"))
+ goto fail;
+
if(!(ret_val->sec_desc = winsec_parse_desc(ret_val, sec_desc_buf,
ret_val->desc_size)))
{
- regfi_add_message(file, REGFI_MSG_ERROR, "Failed to parse security"
+ regfi_log_add(REGFI_LOG_ERROR, "Failed to parse security"
" descriptor while parsing SK record at offset 0x%.8X.",
offset);
goto fail;
@@ -888,6 +1073,8 @@ REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32_t offset, uint32_t max_siz
free(sec_desc_buf);
return ret_val;
+ fail_locked:
+ regfi_unlock(file, &file->cb_lock, "regfi_parse_sk");
fail:
if(sec_desc_buf != NULL)
free(sec_desc_buf);
@@ -899,58 +1086,63 @@ REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32_t offset, uint32_t max_siz
REGFI_VALUE_LIST* regfi_parse_valuelist(REGFI_FILE* file, uint32_t offset,
uint32_t num_values, bool strict)
{
- REGFI_VALUE_LIST* ret_val;
+ REGFI_VALUE_LIST* ret_val = NULL;
uint32_t i, cell_length, length, read_len;
bool unalloc;
- if(!regfi_parse_cell(file->fd, offset, NULL, 0, &cell_length, &unalloc))
+ if(!regfi_lock(file, &file->cb_lock, "regfi_parse_valuelist"))
+ goto fail;
+
+ if(!regfi_parse_cell(file->cb, offset, NULL, 0, &cell_length, &unalloc))
{
- regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read cell header"
+ regfi_log_add(REGFI_LOG_ERROR, "Failed to read cell header"
" while parsing value list at offset 0x%.8X.", offset);
- return NULL;
+ goto fail_locked;
}
if((cell_length & 0x00000007) != 0)
{
- regfi_add_message(file, REGFI_MSG_WARN, "Cell length not a multiple of 8"
+ regfi_log_add(REGFI_LOG_WARN, "Cell length not a multiple of 8"
" while parsing value list at offset 0x%.8X.", offset);
if(strict)
- return NULL;
+ goto fail_locked;
cell_length = cell_length & 0xFFFFFFF8;
}
if((num_values * sizeof(uint32_t)) > cell_length-sizeof(uint32_t))
{
- regfi_add_message(file, REGFI_MSG_WARN, "Too many values found"
+ regfi_log_add(REGFI_LOG_WARN, "Too many values found"
" while parsing value list at offset 0x%.8X.", offset);
if(strict)
- return NULL;
+ goto fail_locked;
num_values = cell_length/sizeof(uint32_t) - sizeof(uint32_t);
}
read_len = num_values*sizeof(uint32_t);
ret_val = talloc(NULL, REGFI_VALUE_LIST);
if(ret_val == NULL)
- return NULL;
+ goto fail_locked;
ret_val->elements = (REGFI_VALUE_LIST_ELEM*)talloc_size(ret_val, read_len);
if(ret_val->elements == NULL)
- {
- talloc_free(ret_val);
- return NULL;
- }
+ goto fail_locked;
+
+ ret_val->offset = offset;
+ ret_val->cell_size = cell_length;
ret_val->num_values = num_values;
length = read_len;
- if((regfi_read(file->fd, (uint8_t*)ret_val->elements, &length) != 0)
+ if((regfi_read(file->cb, (uint8_t*)ret_val->elements, &length) != 0)
|| length != read_len)
{
- regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read value pointers"
+ regfi_log_add(REGFI_LOG_ERROR, "Failed to read value pointers"
" while parsing value list at offset 0x%.8X.", offset);
- talloc_free(ret_val);
- return NULL;
+ goto fail_locked;
}
+ if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_valuelist"))
+ goto fail;
+
for(i=0; i < num_values; i++)
{
/* Fix endianness */
@@ -964,21 +1156,25 @@ REGFI_VALUE_LIST* regfi_parse_valuelist(REGFI_FILE* file, uint32_t offset,
if((ret_val->elements[i] + REGFI_REGF_SIZE > file->file_length)
|| ((ret_val->elements[i] & 0x00000007) != 0))
{
- regfi_add_message(file, REGFI_MSG_WARN, "Invalid value pointer"
+ regfi_log_add(REGFI_LOG_WARN, "Invalid value pointer"
" (0x%.8X) found while parsing value list at offset"
" 0x%.8X.", ret_val->elements[i], offset);
- talloc_free(ret_val);
- return NULL;
+ goto fail;
}
}
}
return ret_val;
-}
+ fail_locked:
+ regfi_unlock(file, &file->cb_lock, "regfi_parse_valuelist");
+ fail:
+ talloc_free(ret_val);
+ return NULL;
+}
-void regfi_interpret_valuename(REGFI_FILE* file, REGFI_VK_REC* vk,
- REGFI_ENCODING output_encoding, bool strict)
+/* XXX: should give this boolean return type to indicate errors */
+void regfi_interpret_valuename(REGFI_FILE* file, REGFI_VK* vk, bool strict)
{
/* XXX: Registry value names are supposedly limited to 16383 characters
* according to:
@@ -988,38 +1184,36 @@ void regfi_interpret_valuename(REGFI_FILE* file, REGFI_VK_REC* vk,
* Also, it may be useful to use this information to limit false positives
* when recovering deleted VK records.
*/
- int32_t tmp_size;
+ REGFI_BUFFER tmp_buf;
REGFI_ENCODING from_encoding = (vk->flags & REGFI_VK_FLAG_ASCIINAME)
? REGFI_ENCODING_ASCII : REGFI_ENCODING_UTF16LE;
- if(from_encoding == output_encoding)
+ if(vk->name_length == 0)
+ return;
+
+ if(from_encoding == file->string_encoding)
{
- vk->valuename_raw = talloc_realloc(vk, vk->valuename_raw,
- uint8_t, vk->name_length+1);
- vk->valuename_raw[vk->name_length] = '\0';
- vk->valuename = (char*)vk->valuename_raw;
+ vk->name_raw[vk->name_length] = '\0';
+ vk->name = (char*)vk->name_raw;
}
else
{
- vk->valuename = talloc_array(vk, char, vk->name_length+1);
- if(vk->valuename == NULL)
+ tmp_buf = regfi_conv_charset(regfi_encoding_int2str(from_encoding),
+ regfi_encoding_int2str(file->string_encoding),
+ vk->name_raw, vk->name_length);
+ if(tmp_buf.buf == NULL)
{
- regfi_free_value(vk);
- return;
+ regfi_log_add(REGFI_LOG_WARN, "Error occurred while converting"
+ " value name to encoding %s. VK offset: 0x%.8X."
+ " Error message: %s",
+ regfi_encoding_int2str(file->string_encoding),
+ vk->offset, strerror(errno));
+ vk->name = NULL;
}
-
- tmp_size = regfi_conv_charset(regfi_encoding_int2str(from_encoding),
- regfi_encoding_int2str(output_encoding),
- vk->valuename_raw, vk->valuename,
- vk->name_length, vk->name_length+1);
- if(tmp_size < 0)
+ else
{
- regfi_add_message(file, REGFI_MSG_WARN, "Error occurred while converting"
- " valuename to encoding %s. Error message: %s",
- regfi_encoding_int2str(output_encoding),
- strerror(-tmp_size));
- talloc_free(vk->valuename);
- vk->valuename = NULL;
+ vk->name = (char*)tmp_buf.buf;
+ talloc_reparent(NULL, vk, vk->name);
}
}
}
@@ -1027,10 +1221,9 @@ void regfi_interpret_valuename(REGFI_FILE* file, REGFI_VK_REC* vk,
/******************************************************************************
******************************************************************************/
-REGFI_VK_REC* regfi_load_value(REGFI_FILE* file, uint32_t offset,
- REGFI_ENCODING output_encoding, bool strict)
+REGFI_VK* regfi_load_value(REGFI_FILE* file, uint32_t offset, bool strict)
{
- REGFI_VK_REC* ret_val = NULL;
+ REGFI_VK* ret_val = NULL;
int32_t max_size;
max_size = regfi_calc_maxsize(file, offset);
@@ -1041,7 +1234,7 @@ REGFI_VK_REC* regfi_load_value(REGFI_FILE* file, uint32_t offset,
if(ret_val == NULL)
return NULL;
- regfi_interpret_valuename(file, ret_val, output_encoding, strict);
+ regfi_interpret_valuename(file, ret_val, strict);
return ret_val;
}
@@ -1058,7 +1251,7 @@ REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32_t offset,
if((num_values+1) * sizeof(uint32_t) > max_size)
{
- regfi_add_message(file, REGFI_MSG_WARN, "Number of values indicated by"
+ regfi_log_add(REGFI_LOG_WARN, "Number of values indicated by"
" parent key (%d) would cause cell to straddle HBIN"
" boundary while loading value list at offset"
" 0x%.8X.", num_values, offset);
@@ -1073,8 +1266,8 @@ REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32_t offset,
}
-void regfi_interpret_keyname(REGFI_FILE* file, REGFI_NK_REC* nk,
- REGFI_ENCODING output_encoding, bool strict)
+/* XXX: should give this boolean return type to indicate errors */
+void regfi_interpret_keyname(REGFI_FILE* file, REGFI_NK* nk, bool strict)
{
/* XXX: Registry key names are supposedly limited to 255 characters according to:
* http://msdn.microsoft.com/en-us/library/ms724872%28VS.85%29.aspx
@@ -1083,37 +1276,36 @@ void regfi_interpret_keyname(REGFI_FILE* file, REGFI_NK_REC* nk,
* Also, it may be useful to use this information to limit false positives
* when recovering deleted NK records.
*/
- int32_t tmp_size;
+ REGFI_BUFFER tmp_buf;
REGFI_ENCODING from_encoding = (nk->flags & REGFI_NK_FLAG_ASCIINAME)
? REGFI_ENCODING_ASCII : REGFI_ENCODING_UTF16LE;
-
- if(from_encoding == output_encoding)
+
+ if(nk->name_length == 0)
+ return;
+
+ if(from_encoding == file->string_encoding)
{
- nk->keyname_raw = talloc_realloc(nk, nk->keyname_raw, uint8_t, nk->name_length+1);
- nk->keyname_raw[nk->name_length] = '\0';
- nk->keyname = (char*)nk->keyname_raw;
+ nk->name_raw[nk->name_length] = '\0';
+ nk->name = (char*)nk->name_raw;
}
else
{
- nk->keyname = talloc_array(nk, char, nk->name_length+1);
- if(nk->keyname == NULL)
+ tmp_buf = regfi_conv_charset(regfi_encoding_int2str(from_encoding),
+ regfi_encoding_int2str(file->string_encoding),
+ nk->name_raw, nk->name_length);
+ if(tmp_buf.buf == NULL)
{
- regfi_free_key(nk);
- return;
+ regfi_log_add(REGFI_LOG_WARN, "Error occurred while converting"
+ " key name to encoding %s. NK offset: 0x%.8X."
+ " Error message: %s",
+ regfi_encoding_int2str(file->string_encoding),
+ nk->offset, strerror(errno));
+ nk->name = NULL;
}
-
- tmp_size = regfi_conv_charset(regfi_encoding_int2str(from_encoding),
- regfi_encoding_int2str(output_encoding),
- nk->keyname_raw, nk->keyname,
- nk->name_length, nk->name_length+1);
- if(tmp_size < 0)
+ else
{
- regfi_add_message(file, REGFI_MSG_WARN, "Error occurred while converting"
- " keyname to encoding %s. Error message: %s",
- regfi_encoding_int2str(output_encoding),
- strerror(-tmp_size));
- talloc_free(nk->keyname);
- nk->keyname = NULL;
+ nk->name = (char*)tmp_buf.buf;
+ talloc_reparent(NULL, nk, nk->name);
}
}
}
@@ -1122,13 +1314,30 @@ void regfi_interpret_keyname(REGFI_FILE* file, REGFI_NK_REC* nk,
/******************************************************************************
*
******************************************************************************/
-REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32_t offset,
- REGFI_ENCODING output_encoding, bool strict)
+REGFI_NK* regfi_load_key(REGFI_FILE* file, uint32_t offset, bool strict)
{
- REGFI_NK_REC* nk;
+ REGFI_NK* nk;
uint32_t off;
int32_t max_size;
+ if(file->nk_cache != NULL)
+ {
+ /* First, check to see if we have this key in our cache */
+ if(!regfi_lock(file, &file->mem_lock, "regfi_load_nk"))
+ return NULL;
+ regfi_lock(file, &file->nk_lock, "regfi_load_nk");
+
+ nk = (REGFI_NK*)lru_cache_find(file->nk_cache, &offset, 4);
+ if(nk != NULL)
+ nk = talloc_reference(NULL, nk);
+
+ regfi_unlock(file, &file->nk_lock, "regfi_load_nk");
+ regfi_unlock(file, &file->mem_lock, "regfi_load_nk");
+ if(nk != NULL)
+ return nk;
+ }
+
+ /* Not cached currently, proceed with loading it */
max_size = regfi_calc_maxsize(file, offset);
if (max_size < 0)
return NULL;
@@ -1136,12 +1345,12 @@ REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32_t offset,
/* get the initial nk record */
if((nk = regfi_parse_nk(file, offset, max_size, true)) == NULL)
{
- regfi_add_message(file, REGFI_MSG_ERROR, "Could not load NK record at"
- " offset 0x%.8X.", offset);
+ regfi_log_add(REGFI_LOG_ERROR, "Could not load NK record at"
+ " offset 0x%.8X.", offset);
return NULL;
}
- regfi_interpret_keyname(file, nk, output_encoding, strict);
+ regfi_interpret_keyname(file, nk, strict);
/* get value list */
if(nk->num_values && (nk->values_off!=REGFI_OFFSET_NONE))
@@ -1152,12 +1361,11 @@ REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32_t offset,
{
if(strict)
{
- regfi_free_key(nk);
+ talloc_free(nk);
return NULL;
}
else
nk->values = NULL;
-
}
else
{
@@ -1165,15 +1373,15 @@ REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32_t offset,
max_size, true);
if(nk->values == NULL)
{
- regfi_add_message(file, REGFI_MSG_WARN, "Could not load value list"
- " for NK record at offset 0x%.8X.", offset);
+ regfi_log_add(REGFI_LOG_WARN, "Could not load value list"
+ " for NK record at offset 0x%.8X.", offset);
if(strict)
{
- regfi_free_key(nk);
+ talloc_free(nk);
return NULL;
}
}
- talloc_steal(nk, nk->values);
+ talloc_reparent(NULL, nk, nk->values);
}
}
@@ -1186,7 +1394,7 @@ REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32_t offset,
{
if(strict)
{
- regfi_free_key(nk);
+ talloc_free(nk);
return NULL;
}
else
@@ -1199,52 +1407,82 @@ REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32_t offset,
if(nk->subkeys == NULL)
{
- regfi_add_message(file, REGFI_MSG_WARN, "Could not load subkey list"
- " while parsing NK record at offset 0x%.8X.", offset);
+ regfi_log_add(REGFI_LOG_WARN, "Could not load subkey list"
+ " while parsing NK record at offset 0x%.8X.", offset);
nk->num_subkeys = 0;
}
- talloc_steal(nk, nk->subkeys);
+ talloc_reparent(NULL, nk, nk->subkeys);
}
}
+ if(file->nk_cache != NULL)
+ {
+ /* All is well, so let us cache this key for later */
+ if(!regfi_lock(file, &file->mem_lock, "regfi_load_nk"))
+ return NULL;
+ regfi_lock(file, &file->nk_lock, "regfi_load_nk");
+
+ lru_cache_update(file->nk_cache, &offset, 4, nk);
+
+ regfi_unlock(file, &file->nk_lock, "regfi_load_nk");
+ regfi_unlock(file, &file->mem_lock, "regfi_load_nk");
+ }
+
return nk;
}
/******************************************************************************
******************************************************************************/
-const REGFI_SK_REC* regfi_load_sk(REGFI_FILE* file, uint32_t offset, bool strict)
+const REGFI_SK* regfi_load_sk(REGFI_FILE* file, uint32_t offset, bool strict)
{
- REGFI_SK_REC* ret_val = NULL;
+ REGFI_SK* ret_val = NULL;
int32_t max_size;
void* failure_ptr = NULL;
+ max_size = regfi_calc_maxsize(file, offset);
+ if(max_size < 0)
+ return NULL;
+
+ if(file->sk_cache == NULL)
+ return regfi_parse_sk(file, offset, max_size, strict);
+
+ if(!regfi_lock(file, &file->mem_lock, "regfi_load_sk"))
+ return NULL;
+ regfi_lock(file, &file->sk_lock, "regfi_load_sk");
+
/* First look if we have already parsed it */
- ret_val = (REGFI_SK_REC*)lru_cache_find(file->sk_cache, &offset, 4);
+ ret_val = (REGFI_SK*)lru_cache_find(file->sk_cache, &offset, 4);
/* Bail out if we have previously cached a parse failure at this offset. */
if(ret_val == (void*)REGFI_OFFSET_NONE)
- return NULL;
+ {
+ ret_val = NULL;
+ goto unlock;
+ }
if(ret_val == NULL)
{
- max_size = regfi_calc_maxsize(file, offset);
- if(max_size < 0)
- return NULL;
-
ret_val = regfi_parse_sk(file, offset, max_size, strict);
if(ret_val == NULL)
{ /* Cache the parse failure and bail out. */
failure_ptr = talloc(NULL, uint32_t);
if(failure_ptr == NULL)
- return NULL;
+ goto unlock;
+
*(uint32_t*)failure_ptr = REGFI_OFFSET_NONE;
lru_cache_update(file->sk_cache, &offset, 4, failure_ptr);
- return NULL;
- }
- lru_cache_update(file->sk_cache, &offset, 4, ret_val);
+ /* Let the cache be the only owner of this */
+ talloc_unlink(NULL, failure_ptr);
+ }
}
+ else
+ ret_val = talloc_reference(NULL, ret_val);
+
+ unlock:
+ regfi_unlock(file, &file->sk_lock, "regfi_load_sk");
+ regfi_unlock(file, &file->mem_lock, "regfi_load_sk");
return ret_val;
}
@@ -1253,10 +1491,9 @@ const REGFI_SK_REC* regfi_load_sk(REGFI_FILE* file, uint32_t offset, bool strict
/******************************************************************************
******************************************************************************/
-REGFI_NK_REC* regfi_find_root_nk(REGFI_FILE* file, const REGFI_HBIN* hbin,
- REGFI_ENCODING output_encoding)
+REGFI_NK* regfi_find_root_nk(REGFI_FILE* file, const REGFI_HBIN* hbin)
{
- REGFI_NK_REC* nk = NULL;
+ REGFI_NK* nk = NULL;
uint32_t cell_length;
uint32_t cur_offset = hbin->file_off+REGFI_HBIN_HEADER_SIZE;
uint32_t hbin_end = hbin->file_off+hbin->block_size;
@@ -1264,16 +1501,26 @@ REGFI_NK_REC* regfi_find_root_nk(REGFI_FILE* file, const REGFI_HBIN* hbin,
while(cur_offset < hbin_end)
{
- if(!regfi_parse_cell(file->fd, cur_offset, NULL, 0, &cell_length, &unalloc))
- {
- regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell at offset"
- " 0x%.8X while searching for root key.", cur_offset);
+
+ if(!regfi_lock(file, &file->cb_lock, "regfi_find_root_nk"))
return NULL;
+
+ if(!regfi_parse_cell(file->cb, cur_offset, NULL, 0, &cell_length, &unalloc))
+ {
+ regfi_log_add(REGFI_LOG_WARN, "Could not parse cell at offset"
+ " 0x%.8X while searching for root key.", cur_offset);
+ goto error_locked;
}
-
+
+ if(!regfi_unlock(file, &file->cb_lock, "regfi_find_root_nk"))
+ return NULL;
+
+ if(cell_length == 0)
+ break;
+
if(!unalloc)
{
- nk = regfi_load_key(file, cur_offset, output_encoding, true);
+ nk = regfi_load_key(file, cur_offset, true);
if(nk != NULL)
{
if(nk->flags & REGFI_NK_FLAG_ROOT)
@@ -1285,67 +1532,141 @@ REGFI_NK_REC* regfi_find_root_nk(REGFI_FILE* file, const REGFI_HBIN* hbin,
}
return NULL;
+
+ error_locked:
+ regfi_unlock(file, &file->cb_lock, "regfi_find_root_nk");
+ return NULL;
}
+
/******************************************************************************
******************************************************************************/
-REGFI_FILE* regfi_open(const char* filename)
+REGFI_FILE* regfi_alloc(int fd, REGFI_ENCODING output_encoding)
{
REGFI_FILE* ret_val;
- int fd;
-
- /* open an existing file */
- if ((fd = open(filename, REGFI_OPEN_FLAGS)) == -1)
- {
- /* fprintf(stderr, "regfi_open: failure to open %s (%s)\n", filename, strerror(errno));*/
+ REGFI_RAW_FILE* file_cb = talloc(NULL, REGFI_RAW_FILE);
+ if(file_cb == NULL)
return NULL;
- }
-
- ret_val = regfi_alloc(fd);
+ file_cb->state = (void*)talloc(file_cb, int);
+ if(file_cb->state == NULL)
+ goto fail;
+ *(int*)file_cb->state = fd;
+
+ file_cb->cur_off = 0;
+ file_cb->size = 0;
+ file_cb->read = ®fi_raw_read;
+ file_cb->seek = ®fi_raw_seek;
+
+ ret_val = regfi_alloc_cb(file_cb, output_encoding);
if(ret_val == NULL)
- close(fd);
+ goto fail;
+ /* In this case, we want file_cb to be freed when ret_val is */
+ talloc_reparent(NULL, ret_val, file_cb);
return ret_val;
-}
-
+
+ fail:
+ talloc_free(file_cb);
+ return NULL;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+static int regfi_free_cb(void* f)
+{
+ REGFI_FILE* file = (REGFI_FILE*)f;
+
+ pthread_mutex_destroy(&file->cb_lock);
+ pthread_rwlock_destroy(&file->hbins_lock);
+ pthread_mutex_destroy(&file->sk_lock);
+ pthread_mutex_destroy(&file->nk_lock);
+ pthread_mutex_destroy(&file->mem_lock);
+
+ return 0;
+}
+
/******************************************************************************
******************************************************************************/
-REGFI_FILE* regfi_alloc(int fd)
+REGFI_FILE* regfi_alloc_cb(REGFI_RAW_FILE* file_cb,
+ REGFI_ENCODING output_encoding)
{
- struct stat sbuf;
REGFI_FILE* rb;
REGFI_HBIN* hbin = NULL;
- uint32_t hbin_off, file_length, cache_secret;
+ uint32_t hbin_off, cache_secret;
+ int64_t file_length;
bool rla;
- /* Determine file length. Must be at least big enough
- * for the header and one hbin.
+ /* Determine file length. Must be at least big enough for the header
+ * and one hbin.
*/
- if (fstat(fd, &sbuf) == -1)
- return NULL;
- file_length = sbuf.st_size;
+ file_length = regfi_seek(file_cb, 0, SEEK_END);
if(file_length < REGFI_REGF_SIZE+REGFI_HBIN_ALLOC)
+ {
+ regfi_log_add(REGFI_LOG_ERROR, "File length (%d) too short to contain a"
+ " header and at least one HBIN.", file_length);
+ return NULL;
+ }
+ regfi_seek(file_cb, 0, SEEK_SET);
+
+ if(output_encoding != REGFI_ENCODING_UTF8
+ && output_encoding != REGFI_ENCODING_ASCII)
+ {
+ regfi_log_add(REGFI_LOG_ERROR, "Invalid output_encoding supplied"
+ " in creation of regfi iterator.");
return NULL;
+ }
/* Read file header */
- if ((rb = regfi_parse_regf(fd, true)) == NULL)
+ if ((rb = regfi_parse_regf(file_cb, false)) == NULL)
{
- /* fprintf(stderr, "regfi_alloc: Failed to read initial REGF block\n"); */
+ regfi_log_add(REGFI_LOG_ERROR, "Failed to read REGF block.");
return NULL;
}
- rb->file_length = file_length;
+ rb->file_length = file_length;
+ rb->cb = file_cb;
+ rb->string_encoding = output_encoding;
+
+ if(pthread_mutex_init(&rb->cb_lock, NULL) != 0)
+ {
+ regfi_log_add(REGFI_LOG_ERROR, "Failed to create cb_lock mutex.");
+ goto fail;
+ }
+
+ if(pthread_rwlock_init(&rb->hbins_lock, NULL) != 0)
+ {
+ regfi_log_add(REGFI_LOG_ERROR, "Failed to create hbins_lock rwlock.");
+ goto fail;
+ }
+
+ if(pthread_mutex_init(&rb->sk_lock, NULL) != 0)
+ {
+ regfi_log_add(REGFI_LOG_ERROR, "Failed to create sk_lock mutex.");
+ goto fail;
+ }
+
+ if(pthread_mutex_init(&rb->nk_lock, NULL) != 0)
+ {
+ regfi_log_add(REGFI_LOG_ERROR, "Failed to create nk_lock mutex.");
+ goto fail;
+ }
+
+ if(pthread_mutex_init(&rb->mem_lock, NULL) != 0)
+ {
+ regfi_log_add(REGFI_LOG_ERROR, "Failed to create mem_lock mutex.");
+ goto fail;
+ }
rb->hbins = range_list_new();
if(rb->hbins == NULL)
{
- /* fprintf(stderr, "regfi_alloc: Failed to create HBIN list.\n"); */
- talloc_free(rb);
- return NULL;
+ regfi_log_add(REGFI_LOG_ERROR, "Failed to create HBIN range_list.");
+ goto fail;
}
- talloc_steal(rb, rb->hbins);
+ talloc_reparent(NULL, rb, rb->hbins);
rla = true;
hbin_off = REGFI_REGF_SIZE;
@@ -1354,7 +1675,8 @@ REGFI_FILE* regfi_alloc(int fd)
{
rla = range_list_add(rb->hbins, hbin->file_off, hbin->block_size, hbin);
if(rla)
- talloc_steal(rb->hbins, hbin);
+ talloc_reparent(NULL, rb->hbins, hbin);
+
hbin_off = hbin->file_off + hbin->block_size;
hbin = regfi_parse_hbin(rb, hbin_off, true);
}
@@ -1365,43 +1687,38 @@ REGFI_FILE* regfi_alloc(int fd)
*/
cache_secret = 0x15DEAD05^time(NULL)^(getpid()<<16);
- /* Cache an unlimited number of SK records. Typically there are very few. */
- rb->sk_cache = lru_cache_create_ctx(rb, 0, cache_secret, true);
+ rb->sk_cache = NULL;
+ if(REGFI_CACHE_SK_MAX > 0)
+ rb->sk_cache = lru_cache_create_ctx(rb, REGFI_CACHE_SK_MAX,
+ cache_secret, true);
- /* Default message mask */
- rb->msg_mask = REGFI_MSG_ERROR|REGFI_MSG_WARN;
+ rb->nk_cache = NULL;
+ if(REGFI_CACHE_NK_MAX > 0)
+ rb->nk_cache = lru_cache_create_ctx(rb, REGFI_CACHE_NK_MAX,
+ cache_secret, true);
/* success */
+ talloc_set_destructor(rb, regfi_free_cb);
return rb;
-}
-
-
-/******************************************************************************
- ******************************************************************************/
-int regfi_close(REGFI_FILE* file)
-{
- int fd;
-
- /* nothing to do if there is no open file */
- if ((file == NULL) || (file->fd == -1))
- return 0;
- fd = file->fd;
- file->fd = -1;
-
- regfi_free(file);
-
- return close(fd);
+ fail:
+ pthread_mutex_destroy(&rb->cb_lock);
+ pthread_rwlock_destroy(&rb->hbins_lock);
+ pthread_mutex_destroy(&rb->sk_lock);
+ pthread_mutex_destroy(&rb->nk_lock);
+ pthread_mutex_destroy(&rb->mem_lock);
+
+ range_list_free(rb->hbins);
+ talloc_free(rb);
+ return NULL;
}
/******************************************************************************
******************************************************************************/
-void regfi_free(REGFI_FILE *file)
+void regfi_free(REGFI_FILE* file)
{
- if(file->last_message != NULL)
- free(file->last_message);
-
+ /* Callback handles cleanup side effects */
talloc_free(file);
}
@@ -1410,9 +1727,9 @@ void regfi_free(REGFI_FILE *file)
* First checks the offset given by the file header, then checks the
* rest of the file if that fails.
******************************************************************************/
-REGFI_NK_REC* regfi_rootkey(REGFI_FILE* file, REGFI_ENCODING output_encoding)
+const REGFI_NK* regfi_get_rootkey(REGFI_FILE* file)
{
- REGFI_NK_REC* nk = NULL;
+ REGFI_NK* nk = NULL;
REGFI_HBIN* hbin;
uint32_t root_offset, i, num_hbins;
@@ -1420,81 +1737,126 @@ REGFI_NK_REC* regfi_rootkey(REGFI_FILE* file, REGFI_ENCODING output_encoding)
return NULL;
root_offset = file->root_cell+REGFI_REGF_SIZE;
- nk = regfi_load_key(file, root_offset, output_encoding, true);
+ nk = regfi_load_key(file, root_offset, true);
if(nk != NULL)
{
if(nk->flags & REGFI_NK_FLAG_ROOT)
return nk;
}
- regfi_add_message(file, REGFI_MSG_WARN, "File header indicated root key at"
- " location 0x%.8X, but no root key found."
- " Searching rest of file...", root_offset);
+ regfi_log_add(REGFI_LOG_WARN, "File header indicated root key at"
+ " location 0x%.8X, but no root key found."
+ " Searching rest of file...", root_offset);
/* If the file header gives bad info, scan through the file one HBIN
* block at a time looking for an NK record with a root key type.
*/
+
+ if(!regfi_read_lock(file, &file->hbins_lock, "regfi_get_rootkey"))
+ return NULL;
+
num_hbins = range_list_size(file->hbins);
for(i=0; i < num_hbins && nk == NULL; i++)
{
hbin = (REGFI_HBIN*)range_list_get(file->hbins, i)->data;
- nk = regfi_find_root_nk(file, hbin, output_encoding);
+ nk = regfi_find_root_nk(file, hbin);
}
+ if(!regfi_rw_unlock(file, &file->hbins_lock, "regfi_get_rootkey"))
+ return NULL;
+
return nk;
}
/******************************************************************************
*****************************************************************************/
-void regfi_free_key(REGFI_NK_REC* nk)
+void regfi_free_record(REGFI_FILE* file, const void* record)
{
- regfi_subkeylist_free(nk->subkeys);
- talloc_free(nk);
+ if(!regfi_lock(file, &file->mem_lock, "regfi_free_record"))
+ return;
+
+ talloc_unlink(NULL, (void*)record);
+
+ regfi_unlock(file, &file->mem_lock, "regfi_free_record");
}
/******************************************************************************
*****************************************************************************/
-void regfi_free_value(REGFI_VK_REC* vk)
+const void* regfi_reference_record(REGFI_FILE* file, const void* record)
{
- talloc_free(vk);
+ const void* ret_val = NULL;
+
+ if(!regfi_lock(file, &file->mem_lock, "regfi_reference_record"))
+ return ret_val;
+
+ ret_val = talloc_reference(NULL, record);
+
+ regfi_unlock(file, &file->mem_lock, "regfi_reference_record");
+ return ret_val;
}
/******************************************************************************
*****************************************************************************/
-void regfi_subkeylist_free(REGFI_SUBKEY_LIST* list)
+uint32_t regfi_fetch_num_subkeys(const REGFI_NK* key)
{
- if(list != NULL)
+ uint32_t num_in_list = 0;
+ if(key == NULL)
+ return 0;
+
+ if(key->subkeys != NULL)
+ num_in_list = key->subkeys->num_keys;
+
+ if(num_in_list != key->num_subkeys)
{
- talloc_free(list);
+ regfi_log_add(REGFI_LOG_INFO, "Key at offset 0x%.8X contains %d keys in its"
+ " subkey list but reports %d should be available.",
+ key->offset, num_in_list, key->num_subkeys);
+ return (num_in_list < key->num_subkeys)?num_in_list:key->num_subkeys;
}
+
+ return num_in_list;
}
/******************************************************************************
*****************************************************************************/
-REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* file,
- REGFI_ENCODING output_encoding)
+uint32_t regfi_fetch_num_values(const REGFI_NK* key)
{
- REGFI_NK_REC* root;
- REGFI_ITERATOR* ret_val;
+ uint32_t num_in_list = 0;
+ if(key == NULL)
+ return 0;
- if(output_encoding != REGFI_ENCODING_UTF8
- && output_encoding != REGFI_ENCODING_ASCII)
- {
- regfi_add_message(file, REGFI_MSG_ERROR, "Invalid output_encoding supplied"
- " in creation of regfi iterator.");
- return NULL;
+ if(key->values != NULL)
+ num_in_list = key->values->num_values;
+
+ if(num_in_list != key->num_values)
+ {
+ regfi_log_add(REGFI_LOG_INFO, "Key at offset 0x%.8X contains %d values in"
+ " its value list but reports %d should be available.",
+ key->offset, num_in_list, key->num_values);
+ return (num_in_list < key->num_values)?num_in_list:key->num_values;
}
+
+ return num_in_list;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* file)
+{
+ REGFI_NK* root;
+ REGFI_ITERATOR* ret_val;
ret_val = talloc(NULL, REGFI_ITERATOR);
if(ret_val == NULL)
return NULL;
-
- root = regfi_rootkey(file, output_encoding);
- if(root == NULL)
+
+ ret_val->cur = talloc(ret_val, REGFI_ITER_POSITION);
+ if(ret_val->cur == NULL)
{
talloc_free(ret_val);
return NULL;
@@ -1506,14 +1868,31 @@ REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* file,
talloc_free(ret_val);
return NULL;
}
- talloc_steal(ret_val, ret_val->key_positions);
+ talloc_reparent(NULL, ret_val, ret_val->key_positions);
+ root = (REGFI_NK*)regfi_get_rootkey(file);
+ if(root == NULL)
+ {
+ talloc_free(ret_val);
+ return NULL;
+ }
+
+ ret_val->cur->offset = root->offset;
+ if(root->subkeys_off == REGFI_OFFSET_NONE)
+ ret_val->cur->num_subkeys = 0;
+ else
+ ret_val->cur->num_subkeys = regfi_fetch_num_subkeys(root);
+
+ if(root->values_off == REGFI_OFFSET_NONE)
+ ret_val->cur->num_values = 0;
+ else
+ ret_val->cur->num_values = regfi_fetch_num_values(root);
+
+ ret_val->cur->cur_subkey = 0;
+ ret_val->cur->cur_value = 0;
ret_val->f = file;
- ret_val->cur_key = root;
- ret_val->cur_subkey = 0;
- ret_val->cur_value = 0;
- ret_val->string_encoding = output_encoding;
-
+
+ regfi_free_record(ret_val->f, root);
return ret_val;
}
@@ -1522,44 +1901,50 @@ REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* file,
*****************************************************************************/
void regfi_iterator_free(REGFI_ITERATOR* i)
{
- talloc_free(i);
+ talloc_unlink(NULL, i);
}
-
/******************************************************************************
*****************************************************************************/
/* XXX: some way of indicating reason for failure should be added. */
bool regfi_iterator_down(REGFI_ITERATOR* i)
{
- REGFI_NK_REC* subkey;
- REGFI_ITER_POSITION* pos;
-
- pos = talloc(i->key_positions, REGFI_ITER_POSITION);
+ REGFI_NK* subkey;
+ REGFI_ITER_POSITION* pos = talloc(i, REGFI_ITER_POSITION);
if(pos == NULL)
return false;
- subkey = (REGFI_NK_REC*)regfi_iterator_cur_subkey(i);
+ subkey = (REGFI_NK*)regfi_iterator_cur_subkey(i);
if(subkey == NULL)
{
talloc_free(pos);
return false;
}
- pos->nk = i->cur_key;
- pos->cur_subkey = i->cur_subkey;
- if(!void_stack_push(i->key_positions, pos))
+ if(!void_stack_push(i->key_positions, i->cur))
{
talloc_free(pos);
- regfi_free_key(subkey);
+ regfi_free_record(i->f, subkey);
return false;
}
- talloc_steal(i, subkey);
- i->cur_key = subkey;
- i->cur_subkey = 0;
- i->cur_value = 0;
+ pos->offset = subkey->offset;
+ if(subkey->subkeys_off == REGFI_OFFSET_NONE)
+ pos->num_subkeys = 0;
+ else
+ pos->num_subkeys = regfi_fetch_num_subkeys(subkey);
+ if(subkey->values_off == REGFI_OFFSET_NONE)
+ pos->num_values = 0;
+ else
+ pos->num_values = regfi_fetch_num_values(subkey);
+
+ pos->cur_subkey = 0;
+ pos->cur_value = 0;
+ i->cur = pos;
+
+ regfi_free_record(i->f, subkey);
return true;
}
@@ -1574,12 +1959,14 @@ bool regfi_iterator_up(REGFI_ITERATOR* i)
if(pos == NULL)
return false;
- regfi_free_key(i->cur_key);
- i->cur_key = pos->nk;
- i->cur_subkey = pos->cur_subkey;
- i->cur_value = 0;
- talloc_free(pos);
+ if(!regfi_lock(i->f, &i->f->mem_lock, "regfi_iterator_up"))
+ return false;
+
+ talloc_unlink(i, i->cur);
+
+ regfi_unlock(i->f, &i->f->mem_lock, "regfi_iterator_up");
+ i->cur = pos;
return true;
}
@@ -1597,43 +1984,33 @@ bool regfi_iterator_to_root(REGFI_ITERATOR* i)
/******************************************************************************
*****************************************************************************/
-bool regfi_iterator_find_subkey(REGFI_ITERATOR* i, const char* subkey_name)
+bool regfi_iterator_find_subkey(REGFI_ITERATOR* i, const char* name)
{
- REGFI_NK_REC* subkey;
- bool found = false;
- uint32_t old_subkey = i->cur_subkey;
-
- if(subkey_name == NULL)
- return false;
+ const REGFI_NK* cur_key;
+ uint32_t new_index;
+ bool ret_val = false;
- /* XXX: this alloc/free of each sub key might be a bit excessive */
- subkey = (REGFI_NK_REC*)regfi_iterator_first_subkey(i);
- while((subkey != NULL) && (found == false))
+ cur_key = regfi_iterator_cur_key(i);
+ if(cur_key == NULL)
{
- if(subkey->keyname != NULL
- && strcasecmp(subkey->keyname, subkey_name) == 0)
- found = true;
- else
- {
- regfi_free_key(subkey);
- subkey = (REGFI_NK_REC*)regfi_iterator_next_subkey(i);
- }
+ regfi_log_add(REGFI_LOG_ERROR, "Current key invalid in find_subkey.");
+ return ret_val;
}
- if(found == false)
+ if(regfi_find_subkey(i->f, cur_key, name, &new_index))
{
- i->cur_subkey = old_subkey;
- return false;
+ i->cur->cur_subkey = new_index;
+ ret_val = true;
}
- regfi_free_key(subkey);
- return true;
+ regfi_free_record(i->f, cur_key);
+ return ret_val;
}
/******************************************************************************
*****************************************************************************/
-bool regfi_iterator_walk_path(REGFI_ITERATOR* i, const char** path)
+bool regfi_iterator_descend(REGFI_ITERATOR* i, const char** path)
{
uint32_t x;
if(path == NULL)
@@ -1646,8 +2023,10 @@ bool regfi_iterator_walk_path(REGFI_ITERATOR* i, const char** path)
{ continue; }
if(path[x] == NULL)
+ {
return true;
-
+ }
+
/* XXX: is this the right number of times? */
for(; x > 0; x--)
regfi_iterator_up(i);
@@ -1658,178 +2037,228 @@ bool regfi_iterator_walk_path(REGFI_ITERATOR* i, const char** path)
/******************************************************************************
*****************************************************************************/
-const REGFI_NK_REC* regfi_iterator_cur_key(REGFI_ITERATOR* i)
+const REGFI_NK* regfi_iterator_cur_key(REGFI_ITERATOR* i)
{
- return i->cur_key;
+ const REGFI_NK* ret_val = NULL;
+
+ ret_val = regfi_load_key(i->f, i->cur->offset, true);
+ return ret_val;
}
/******************************************************************************
*****************************************************************************/
-const REGFI_SK_REC* regfi_iterator_cur_sk(REGFI_ITERATOR* i)
+const REGFI_SK* regfi_fetch_sk(REGFI_FILE* file, const REGFI_NK* key)
{
- if(i->cur_key == NULL || i->cur_key->sk_off == REGFI_OFFSET_NONE)
+ if(key == NULL || key->sk_off == REGFI_OFFSET_NONE)
return NULL;
- return regfi_load_sk(i->f, i->cur_key->sk_off + REGFI_REGF_SIZE, true);
+ return regfi_load_sk(file, key->sk_off + REGFI_REGF_SIZE, true);
}
/******************************************************************************
*****************************************************************************/
-REGFI_NK_REC* regfi_iterator_first_subkey(REGFI_ITERATOR* i)
+const REGFI_SK* regfi_next_sk(REGFI_FILE* file, const REGFI_SK* sk)
{
- i->cur_subkey = 0;
- return regfi_iterator_cur_subkey(i);
+ if(sk == NULL || sk->next_sk_off == REGFI_OFFSET_NONE)
+ return NULL;
+
+ return regfi_load_sk(file, sk->next_sk_off + REGFI_REGF_SIZE, true);
}
/******************************************************************************
*****************************************************************************/
-REGFI_NK_REC* regfi_iterator_cur_subkey(REGFI_ITERATOR* i)
+const REGFI_SK* regfi_prev_sk(REGFI_FILE* file, const REGFI_SK* sk)
{
- uint32_t nk_offset;
-
- /* see if there is anything left to report */
- if (!(i->cur_key) || (i->cur_key->subkeys_off==REGFI_OFFSET_NONE)
- || (i->cur_subkey >= i->cur_key->num_subkeys))
+ if(sk == NULL || sk->prev_sk_off == REGFI_OFFSET_NONE)
return NULL;
- nk_offset = i->cur_key->subkeys->elements[i->cur_subkey].offset;
-
- return regfi_load_key(i->f, nk_offset+REGFI_REGF_SIZE, i->string_encoding,
- true);
+ return regfi_load_sk(file, sk->prev_sk_off + REGFI_REGF_SIZE, true);
}
/******************************************************************************
*****************************************************************************/
-/* XXX: some way of indicating reason for failure should be added. */
-REGFI_NK_REC* regfi_iterator_next_subkey(REGFI_ITERATOR* i)
+bool regfi_iterator_first_subkey(REGFI_ITERATOR* i)
{
- REGFI_NK_REC* subkey;
+ i->cur->cur_subkey = 0;
+ return (i->cur->cur_subkey < i->cur->num_subkeys);
+}
- i->cur_subkey++;
- subkey = regfi_iterator_cur_subkey(i);
- if(subkey == NULL)
- i->cur_subkey--;
+/******************************************************************************
+ *****************************************************************************/
+const REGFI_NK* regfi_iterator_cur_subkey(REGFI_ITERATOR* i)
+{
+ const REGFI_NK* cur_key;
+ const REGFI_NK* ret_val;
+
+ cur_key = regfi_iterator_cur_key(i);
+ if(cur_key == NULL)
+ {
+ regfi_log_add(REGFI_LOG_ERROR, "Current key invalid in cur_subkey.");
+ return NULL;
+ }
- return subkey;
+ ret_val = regfi_get_subkey(i->f, cur_key, i->cur->cur_subkey);
+
+ regfi_free_record(i->f, cur_key);
+ return ret_val;
}
/******************************************************************************
*****************************************************************************/
-bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* value_name)
+bool regfi_iterator_next_subkey(REGFI_ITERATOR* i)
{
- REGFI_VK_REC* cur;
- bool found = false;
- uint32_t old_value = i->cur_value;
+ i->cur->cur_subkey++;
+ return (i->cur->cur_subkey < i->cur->num_subkeys);
+}
- /* XXX: cur->valuename can be NULL in the registry.
- * Should we allow for a way to search for that?
- */
- if(value_name == NULL)
- return false;
- cur = regfi_iterator_first_value(i);
- while((cur != NULL) && (found == false))
+/******************************************************************************
+ *****************************************************************************/
+bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* name)
+{
+ const REGFI_NK* cur_key;
+ uint32_t new_index;
+ bool ret_val = false;
+
+ cur_key = regfi_iterator_cur_key(i);
+ if(cur_key == NULL)
{
- if((cur->valuename != NULL)
- && (strcasecmp(cur->valuename, value_name) == 0))
- found = true;
- else
- {
- regfi_free_value(cur);
- cur = regfi_iterator_next_value(i);
- }
+ regfi_log_add(REGFI_LOG_ERROR, "Current key invalid in find_value.");
+ return ret_val;
}
-
- if(found == false)
+
+ if(regfi_find_value(i->f, cur_key, name, &new_index))
{
- i->cur_value = old_value;
- return false;
+ i->cur->cur_value = new_index;
+ ret_val = true;
}
- regfi_free_value(cur);
- return true;
+ regfi_free_record(i->f, cur_key);
+ return ret_val;
}
/******************************************************************************
*****************************************************************************/
-REGFI_VK_REC* regfi_iterator_first_value(REGFI_ITERATOR* i)
+bool regfi_iterator_first_value(REGFI_ITERATOR* i)
{
- i->cur_value = 0;
- return regfi_iterator_cur_value(i);
+ i->cur->cur_value = 0;
+ return (i->cur->cur_value < i->cur->num_values);
}
/******************************************************************************
*****************************************************************************/
-REGFI_VK_REC* regfi_iterator_cur_value(REGFI_ITERATOR* i)
+const REGFI_VK* regfi_iterator_cur_value(REGFI_ITERATOR* i)
{
- REGFI_VK_REC* ret_val = NULL;
- uint32_t voffset;
+ const REGFI_NK* cur_key;
+ const REGFI_VK* ret_val = NULL;
- if(i->cur_key->values != NULL && i->cur_key->values->elements != NULL)
+ cur_key = regfi_iterator_cur_key(i);
+ if(cur_key == NULL)
{
- if(i->cur_value < i->cur_key->values->num_values)
- {
- voffset = i->cur_key->values->elements[i->cur_value];
- ret_val = regfi_load_value(i->f, voffset+REGFI_REGF_SIZE,
- i->string_encoding, true);
- }
+ regfi_log_add(REGFI_LOG_ERROR, "Current key invalid in cur_value.");
+ return ret_val;
}
+ ret_val = regfi_get_value(i->f, cur_key, i->cur->cur_value);
+
+ regfi_free_record(i->f, cur_key);
return ret_val;
}
/******************************************************************************
*****************************************************************************/
-REGFI_VK_REC* regfi_iterator_next_value(REGFI_ITERATOR* i)
+bool regfi_iterator_next_value(REGFI_ITERATOR* i)
+{
+ i->cur->cur_value++;
+ return (i->cur->cur_value < i->cur->num_values);
+}
+
+
+
+
+/******************************************************************************
+ *****************************************************************************/
+const REGFI_NK** regfi_iterator_ancestry(REGFI_ITERATOR* i)
{
- REGFI_VK_REC* ret_val;
+ REGFI_NK** ret_val;
+ void_stack_iterator* iter;
+ const REGFI_ITER_POSITION* cur;
+ uint16_t k, num_keys;
- i->cur_value++;
- ret_val = regfi_iterator_cur_value(i);
+ num_keys = void_stack_size(i->key_positions)+1;
+ ret_val = talloc_array(NULL, REGFI_NK*, num_keys+1);
if(ret_val == NULL)
- i->cur_value--;
+ return NULL;
- return ret_val;
+ iter = void_stack_iterator_new(i->key_positions);
+ if (iter == NULL)
+ {
+ talloc_free(ret_val);
+ return NULL;
+ }
+
+ k=0;
+ for(cur=void_stack_iterator_next(iter);
+ cur != NULL; cur=void_stack_iterator_next(iter))
+ {
+ ret_val[k++] = regfi_load_key(i->f, cur->offset, true);
+ }
+ ret_val[k] = regfi_load_key(i->f, i->cur->offset, true);
+ void_stack_iterator_free(iter);
+
+ if(!regfi_lock(i->f, &i->f->mem_lock, "regfi_iterator_ancestry"))
+ {
+ talloc_free(ret_val);
+ return NULL;
+ }
+
+ for(k=0; k<num_keys; k++)
+ talloc_reparent(NULL, ret_val, ret_val[k]);
+
+ regfi_unlock(i->f, &i->f->mem_lock, "regfi_iterator_ancestry");
+
+ ret_val[k] = NULL;
+ return (const REGFI_NK**)ret_val;
}
/******************************************************************************
*****************************************************************************/
-REGFI_CLASSNAME* regfi_iterator_fetch_classname(REGFI_ITERATOR* i,
- const REGFI_NK_REC* key)
+const REGFI_CLASSNAME* regfi_fetch_classname(REGFI_FILE* file,
+ const REGFI_NK* key)
{
REGFI_CLASSNAME* ret_val;
uint8_t* raw;
- char* interpreted;
+ REGFI_BUFFER tmp_buf;
uint32_t offset;
- int32_t conv_size, max_size;
+ int32_t max_size;
uint16_t parse_length;
if(key->classname_off == REGFI_OFFSET_NONE || key->classname_length == 0)
return NULL;
offset = key->classname_off + REGFI_REGF_SIZE;
- max_size = regfi_calc_maxsize(i->f, offset);
+ max_size = regfi_calc_maxsize(file, offset);
if(max_size <= 0)
return NULL;
parse_length = key->classname_length;
- raw = regfi_parse_classname(i->f, offset, &parse_length, max_size, true);
+ raw = regfi_parse_classname(file, offset, &parse_length, max_size, true);
if(raw == NULL)
{
- regfi_add_message(i->f, REGFI_MSG_WARN, "Could not parse class"
- " name at offset 0x%.8X for key record at offset 0x%.8X.",
- offset, key->offset);
+ regfi_log_add(REGFI_LOG_WARN, "Could not parse class"
+ " name at offset 0x%.8X for key record at offset 0x%.8X.",
+ offset, key->offset);
return NULL;
}
@@ -1837,29 +2266,25 @@ REGFI_CLASSNAME* regfi_iterator_fetch_classname(REGFI_ITERATOR* i,
if(ret_val == NULL)
return NULL;
+ ret_val->offset = offset;
ret_val->raw = raw;
ret_val->size = parse_length;
- talloc_steal(ret_val, raw);
-
- interpreted = talloc_array(NULL, char, parse_length);
+ talloc_reparent(NULL, ret_val, raw);
- conv_size = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE),
- regfi_encoding_int2str(i->string_encoding),
- raw, interpreted,
- parse_length, parse_length);
- if(conv_size < 0)
+ tmp_buf = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE),
+ regfi_encoding_int2str(file->string_encoding),
+ raw, parse_length);
+ if(tmp_buf.buf == NULL)
{
- regfi_add_message(i->f, REGFI_MSG_WARN, "Error occurred while"
- " converting classname to charset %s. Error message: %s",
- i->string_encoding, strerror(-conv_size));
- talloc_free(interpreted);
+ regfi_log_add(REGFI_LOG_WARN, "Error occurred while"
+ " converting classname to charset %s. Error message: %s",
+ file->string_encoding, strerror(errno));
ret_val->interpreted = NULL;
}
else
{
- interpreted = talloc_realloc(NULL, interpreted, char, conv_size);
- ret_val->interpreted = interpreted;
- talloc_steal(ret_val, interpreted);
+ ret_val->interpreted = (char*)tmp_buf.buf;
+ talloc_reparent(NULL, ret_val, tmp_buf.buf);
}
return ret_val;
@@ -1868,21 +2293,21 @@ REGFI_CLASSNAME* regfi_iterator_fetch_classname(REGFI_ITERATOR* i,
/******************************************************************************
*****************************************************************************/
-REGFI_DATA* regfi_iterator_fetch_data(REGFI_ITERATOR* i,
- const REGFI_VK_REC* value)
+const REGFI_DATA* regfi_fetch_data(REGFI_FILE* file,
+ const REGFI_VK* value)
{
REGFI_DATA* ret_val = NULL;
REGFI_BUFFER raw_data;
if(value->data_size != 0)
{
- raw_data = regfi_load_data(i->f, value->data_off, value->data_size,
- value->data_in_offset, true);
+ raw_data = regfi_load_data(file, value->data_off, value->data_size,
+ value->data_in_offset, true);
if(raw_data.buf == NULL)
{
- regfi_add_message(i->f, REGFI_MSG_WARN, "Could not parse data record"
- " while parsing VK record at offset 0x%.8X.",
- value->offset);
+ regfi_log_add(REGFI_LOG_WARN, "Could not parse data record"
+ " while parsing VK record at offset 0x%.8X.",
+ value->offset);
}
else
{
@@ -1890,19 +2315,19 @@ REGFI_DATA* regfi_iterator_fetch_data(REGFI_ITERATOR* i,
if(ret_val == NULL)
{
- regfi_add_message(i->f, REGFI_MSG_WARN, "Error occurred in converting"
- " data buffer to data structure while interpreting "
- "data for VK record at offset 0x%.8X.",
- value->offset);
+ regfi_log_add(REGFI_LOG_WARN, "Error occurred in converting"
+ " data buffer to data structure while interpreting "
+ "data for VK record at offset 0x%.8X.",
+ value->offset);
talloc_free(raw_data.buf);
return NULL;
}
- if(!regfi_interpret_data(i->f, i->string_encoding, value->type, ret_val))
+ if(!regfi_interpret_data(file, value->type, ret_val))
{
- regfi_add_message(i->f, REGFI_MSG_INFO, "Error occurred while"
- " interpreting data for VK record at offset 0x%.8X.",
- value->offset);
+ regfi_log_add(REGFI_LOG_INFO, "Error occurred while"
+ " interpreting data for VK record at offset 0x%.8X.",
+ value->offset);
}
}
}
@@ -1911,21 +2336,131 @@ REGFI_DATA* regfi_iterator_fetch_data(REGFI_ITERATOR* i,
}
+
+/******************************************************************************
+ *****************************************************************************/
+bool regfi_find_subkey(REGFI_FILE* file, const REGFI_NK* key,
+ const char* name, uint32_t* index)
+{
+ const REGFI_NK* cur;
+ uint32_t i;
+ uint32_t num_subkeys = regfi_fetch_num_subkeys(key);
+ bool found = false;
+
+ /* XXX: should we allow "(default)" subkey names?
+ * Do realistically they exist?
+ */
+ if(name == NULL)
+ return false;
+
+ /* XXX: Should lazily build a hash table in memory to index where keys are when
+ * there are a large number of subkeys. Attach this to cached keys to
+ * bound the extra amount of memory used.
+ */
+ for(i=0; (i < num_subkeys) && (found == false); i++)
+ {
+ cur = regfi_get_subkey(file, key, i);
+ if(cur == NULL)
+ return false;
+
+ /* A NULL name signifies the "(default)" value for a key */
+ if(cur->name != NULL && (strcasecmp(cur->name, name) == 0))
+ {
+ found = true;
+ *index = i;
+ }
+
+ regfi_free_record(file, cur);
+ }
+
+ return found;
+}
+
+
+
+/******************************************************************************
+ *****************************************************************************/
+bool regfi_find_value(REGFI_FILE* file, const REGFI_NK* key,
+ const char* name, uint32_t* index)
+{
+ const REGFI_VK* cur;
+ uint32_t i;
+ uint32_t num_values = regfi_fetch_num_values(key);
+ bool found = false;
+
+ /* XXX: Should lazily build a hash table in memory to index where values are when
+ * there are a large number of them. Attach this to cached keys to
+ * bound the extra amount of memory used.
+ */
+ for(i=0; (i < num_values) && (found == false); i++)
+ {
+ cur = regfi_get_value(file, key, i);
+ if(cur == NULL)
+ return false;
+
+ /* A NULL name signifies the "(default)" value for a key */
+ if(((name == NULL) && (cur->name == NULL))
+ || ((name != NULL) && (cur->name != NULL)
+ && (strcasecmp(cur->name, name) == 0)))
+ {
+ found = true;
+ *index = i;
+ }
+
+ regfi_free_record(file, cur);
+ }
+
+ return found;
+}
+
+
+
+/******************************************************************************
+ *****************************************************************************/
+const REGFI_NK* regfi_get_subkey(REGFI_FILE* file, const REGFI_NK* key,
+ uint32_t index)
+{
+ if(index < regfi_fetch_num_subkeys(key))
+ {
+ return regfi_load_key(file,
+ key->subkeys->elements[index].offset+REGFI_REGF_SIZE,
+ true);
+ }
+
+ return NULL;
+}
+
+
/******************************************************************************
*****************************************************************************/
-void regfi_free_classname(REGFI_CLASSNAME* classname)
+const REGFI_VK* regfi_get_value(REGFI_FILE* file, const REGFI_NK* key,
+ uint32_t index)
{
- talloc_free(classname);
+ if(index < regfi_fetch_num_values(key))
+ {
+ return regfi_load_value(file,
+ key->values->elements[index]+REGFI_REGF_SIZE,
+ true);
+ }
+
+ return NULL;
}
+
+
/******************************************************************************
*****************************************************************************/
-void regfi_free_data(REGFI_DATA* data)
+const REGFI_NK* regfi_get_parentkey(REGFI_FILE* file, const REGFI_NK* key)
{
- talloc_free(data);
+ if(key != NULL && key->parent_off != REGFI_OFFSET_NONE)
+ return regfi_load_key(file,
+ key->parent_off+REGFI_REGF_SIZE, true);
+
+ return NULL;
}
+
/******************************************************************************
*****************************************************************************/
REGFI_DATA* regfi_buffer_to_data(REGFI_BUFFER raw_data)
@@ -1939,7 +2474,7 @@ REGFI_DATA* regfi_buffer_to_data(REGFI_BUFFER raw_data)
if(ret_val == NULL)
return NULL;
- talloc_steal(ret_val, raw_data.buf);
+ talloc_reparent(NULL, ret_val, raw_data.buf);
ret_val->raw = raw_data.buf;
ret_val->size = raw_data.len;
ret_val->interpreted_size = 0;
@@ -1951,13 +2486,11 @@ REGFI_DATA* regfi_buffer_to_data(REGFI_BUFFER raw_data)
/******************************************************************************
*****************************************************************************/
-bool regfi_interpret_data(REGFI_FILE* file, REGFI_ENCODING string_encoding,
- uint32_t type, REGFI_DATA* data)
+bool regfi_interpret_data(REGFI_FILE* file, uint32_t type, REGFI_DATA* data)
{
+ REGFI_BUFFER tmp_buf;
uint8_t** tmp_array;
- uint8_t* tmp_str;
- int32_t tmp_size;
- uint32_t i, j, array_size;
+ uint32_t i, j;
if(data == NULL)
return false;
@@ -1968,33 +2501,23 @@ bool regfi_interpret_data(REGFI_FILE* file, REGFI_ENCODING string_encoding,
case REG_EXPAND_SZ:
/* REG_LINK is a symbolic link, stored as a unicode string. */
case REG_LINK:
- tmp_str = talloc_array(NULL, uint8_t, data->size);
- if(tmp_str == NULL)
+ tmp_buf = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE),
+ regfi_encoding_int2str(file->string_encoding),
+ data->raw, data->size);
+ if(tmp_buf.buf == NULL)
{
- data->interpreted.string = NULL;
- data->interpreted_size = 0;
- return false;
- }
-
- tmp_size = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE),
- regfi_encoding_int2str(string_encoding),
- data->raw, (char*)tmp_str,
- data->size, data->size);
- if(tmp_size < 0)
- {
- regfi_add_message(file, REGFI_MSG_INFO, "Error occurred while"
- " converting data of type %d to %s. Error message: %s",
- type, string_encoding, strerror(-tmp_size));
- talloc_free(tmp_str);
+ regfi_log_add(REGFI_LOG_INFO, "Error occurred while"
+ " converting data of type %d to string encoding %d."
+ " Error message: %s",
+ type, file->string_encoding, strerror(errno));
data->interpreted.string = NULL;
data->interpreted_size = 0;
return false;
}
- tmp_str = talloc_realloc(NULL, tmp_str, uint8_t, tmp_size);
- data->interpreted.string = tmp_str;
- data->interpreted_size = tmp_size;
- talloc_steal(data, tmp_str);
+ data->interpreted.string = tmp_buf.buf;
+ data->interpreted_size = tmp_buf.len;
+ talloc_reparent(NULL, data, tmp_buf.buf);
break;
case REG_DWORD:
@@ -2032,55 +2555,46 @@ bool regfi_interpret_data(REGFI_FILE* file, REGFI_ENCODING string_encoding,
break;
case REG_MULTI_SZ:
- tmp_str = talloc_array(NULL, uint8_t, data->size);
- if(tmp_str == NULL)
- {
- data->interpreted.multiple_string = NULL;
- data->interpreted_size = 0;
- return false;
- }
-
/* Attempt to convert entire string from UTF-16LE to output encoding,
* then parse and quote fields individually.
*/
- tmp_size = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE),
- regfi_encoding_int2str(string_encoding),
- data->raw, (char*)tmp_str,
- data->size, data->size);
- if(tmp_size < 0)
+ tmp_buf = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE),
+ regfi_encoding_int2str(file->string_encoding),
+ data->raw, data->size);
+ if(tmp_buf.buf == NULL)
{
- regfi_add_message(file, REGFI_MSG_INFO, "Error occurred while"
- " converting data of type %d to %s. Error message: %s",
- type, string_encoding, strerror(-tmp_size));
- talloc_free(tmp_str);
+ regfi_log_add(REGFI_LOG_INFO, "Error occurred while"
+ " converting data of type %d to string encoding %d."
+ " Error message: %s",
+ type, file->string_encoding, strerror(errno));
data->interpreted.multiple_string = NULL;
data->interpreted_size = 0;
return false;
}
- array_size = tmp_size+1;
- tmp_array = talloc_array(NULL, uint8_t*, array_size);
+ tmp_array = talloc_array(NULL, uint8_t*, tmp_buf.len+1);
if(tmp_array == NULL)
{
- talloc_free(tmp_str);
+ talloc_free(tmp_buf.buf);
data->interpreted.string = NULL;
data->interpreted_size = 0;
return false;
}
-
- tmp_array[0] = tmp_str;
- for(i=0,j=1; i < tmp_size && j < array_size-1; i++)
+
+ tmp_array[0] = tmp_buf.buf;
+ for(i=0,j=1; i < tmp_buf.len && j < tmp_buf.len; i++)
{
- if(tmp_str[i] == '\0' && (i+1 < tmp_size))
- tmp_array[j++] = tmp_str+i+1;
+ if(tmp_buf.buf[i] == '\0' && (i+1 < tmp_buf.len)
+ && tmp_buf.buf[i+1] != '\0')
+ tmp_array[j++] = tmp_buf.buf+i+1;
}
tmp_array[j] = NULL;
tmp_array = talloc_realloc(NULL, tmp_array, uint8_t*, j+1);
data->interpreted.multiple_string = tmp_array;
/* XXX: how meaningful is this? should we store number of strings instead? */
- data->interpreted_size = tmp_size;
- talloc_steal(tmp_array, tmp_str);
- talloc_steal(data, tmp_array);
+ data->interpreted_size = tmp_buf.len;
+ talloc_reparent(NULL, tmp_array, tmp_buf.buf);
+ talloc_reparent(NULL, data, tmp_array);
break;
/* XXX: Dont know how to interpret these yet, just treat as binary */
@@ -2121,40 +2635,82 @@ bool regfi_interpret_data(REGFI_FILE* file, REGFI_ENCODING string_encoding,
/******************************************************************************
- * Convert from UTF-16LE to specified character set.
- * On error, returns a negative errno code.
+ * Convert string from input_charset to output_charset.
+ * On error, returns a NULL buf attribute and sets the errno.
*****************************************************************************/
-int32_t regfi_conv_charset(const char* input_charset, const char* output_charset,
- uint8_t* input, char* output,
- uint32_t input_len, uint32_t output_max)
+REGFI_BUFFER regfi_conv_charset(const char* input_charset, const char* output_charset,
+ uint8_t* input, uint32_t input_len)
{
iconv_t conv_desc;
char* inbuf = (char*)input;
- char* outbuf = output;
- size_t in_len = (size_t)input_len;
- size_t out_len = (size_t)(output_max-1);
+ char* outbuf;
+ char* retbuf;
+ size_t allocated = (size_t)input_len;
+ size_t in_left = (size_t)input_len;
+ size_t out_left = (size_t)allocated-1;
+ REGFI_BUFFER ret_val;
int ret;
+ ret_val.buf = NULL;
+ ret_val.len = 0;
+ retbuf = talloc_array(NULL, char, allocated);
+ outbuf = retbuf;
+ if(outbuf == NULL)
+ {
+ errno = ENOMEM;
+ return ret_val;
+ }
+
+ /* Set up conversion descriptor. */
/* XXX: Consider creating a couple of conversion descriptors earlier,
* storing them on an iterator so they don't have to be recreated
* each time.
*/
-
- /* Set up conversion descriptor. */
conv_desc = iconv_open(output_charset, input_charset);
- ret = iconv(conv_desc, &inbuf, &in_len, &outbuf, &out_len);
+ ret = 0;
+ do
+ {
+ if(ret == -1)
+ {
+ retbuf = talloc_realloc(NULL, retbuf, char, allocated+(in_left*2));
+ if(retbuf == NULL)
+ {
+ errno = ENOMEM;
+ return ret_val;
+ }
+ outbuf = retbuf+(allocated-1-out_left);
+ out_left += in_left*2;
+ allocated += in_left*2;
+ }
+ ret = iconv(conv_desc, &inbuf, &in_left, &outbuf, &out_left);
+
+ } while(ret == -1 && errno == E2BIG);
+
if(ret == -1)
{
iconv_close(conv_desc);
- return -errno;
+ return ret_val;
}
- *outbuf = '\0';
- iconv_close(conv_desc);
- return output_max-out_len-1;
-}
+ /* Save memory */
+ if(out_left > 0)
+ {
+ retbuf = talloc_realloc(NULL, retbuf, char, allocated-out_left);
+ if(retbuf == NULL)
+ {
+ errno = ENOMEM;
+ return ret_val;
+ }
+ allocated -= out_left;
+ }
+ retbuf[allocated-1] = '\0';
+ iconv_close(conv_desc);
+ ret_val.buf = (uint8_t*)retbuf;
+ ret_val.len = allocated-1;
+ return ret_val;
+}
/*******************************************************************
@@ -2180,9 +2736,8 @@ static uint32_t regfi_compute_header_checksum(uint8_t* buffer)
/*******************************************************************
- * XXX: Add way to return more detailed error information.
*******************************************************************/
-REGFI_FILE* regfi_parse_regf(int fd, bool strict)
+REGFI_FILE* regfi_parse_regf(REGFI_RAW_FILE* file_cb, bool strict)
{
uint8_t file_header[REGFI_REGF_SIZE];
uint32_t length;
@@ -2192,50 +2747,66 @@ REGFI_FILE* regfi_parse_regf(int fd, bool strict)
if(ret_val == NULL)
return NULL;
- ret_val->fd = fd;
ret_val->sk_cache = NULL;
- ret_val->last_message = NULL;
ret_val->hbins = NULL;
-
+
length = REGFI_REGF_SIZE;
- if((regfi_read(fd, file_header, &length)) != 0 || length != REGFI_REGF_SIZE)
+ if((regfi_read(file_cb, file_header, &length)) != 0
+ || length != REGFI_REGF_SIZE)
+ {
+ regfi_log_add(REGFI_LOG_WARN, "Read failed while parsing REGF structure.");
goto fail;
-
+ }
+
ret_val->checksum = IVAL(file_header, 0x1FC);
ret_val->computed_checksum = regfi_compute_header_checksum(file_header);
if (strict && (ret_val->checksum != ret_val->computed_checksum))
- goto fail;
+ {
+ regfi_log_add(REGFI_LOG_WARN, "Stored header checksum (%.8X) did not equal"
+ " computed checksum (%.8X).",
+ ret_val->checksum, ret_val->computed_checksum);
+ if(strict)
+ goto fail;
+ }
memcpy(ret_val->magic, file_header, REGFI_REGF_MAGIC_SIZE);
if(memcmp(ret_val->magic, "regf", REGFI_REGF_MAGIC_SIZE) != 0)
{
- if(strict)
- goto fail;
- regfi_add_message(ret_val, REGFI_MSG_WARN, "Magic number mismatch "
- "(%.2X %.2X %.2X %.2X) while parsing hive header",
- ret_val->magic[0], ret_val->magic[1],
- ret_val->magic[2], ret_val->magic[3]);
+ regfi_log_add(REGFI_LOG_ERROR, "Magic number mismatch "
+ "(%.2X %.2X %.2X %.2X) while parsing hive header",
+ ret_val->magic[0], ret_val->magic[1],
+ ret_val->magic[2], ret_val->magic[3]);
+ goto fail;
}
+
ret_val->sequence1 = IVAL(file_header, 0x4);
ret_val->sequence2 = IVAL(file_header, 0x8);
- ret_val->mtime.low = IVAL(file_header, 0xC);
- ret_val->mtime.high = IVAL(file_header, 0x10);
+ ret_val->mtime = ((uint64_t)IVAL(file_header, 0x10)) << 32;
+ ret_val->mtime |= IVAL(file_header, 0xC);
ret_val->major_version = IVAL(file_header, 0x14);
ret_val->minor_version = IVAL(file_header, 0x18);
ret_val->type = IVAL(file_header, 0x1C);
ret_val->format = IVAL(file_header, 0x20);
ret_val->root_cell = IVAL(file_header, 0x24);
ret_val->last_block = IVAL(file_header, 0x28);
-
ret_val->cluster = IVAL(file_header, 0x2C);
memcpy(ret_val->file_name, file_header+0x30, REGFI_REGF_NAME_SIZE);
- /* XXX: Should we add a warning if these uuid parsers fail? Can they? */
ret_val->rm_id = winsec_parse_uuid(ret_val, file_header+0x70, 16);
+ if(ret_val->rm_id == NULL)
+ regfi_log_add(REGFI_LOG_WARN, "Hive header's rm_id failed to parse.");
+
ret_val->log_id = winsec_parse_uuid(ret_val, file_header+0x80, 16);
+ if(ret_val->log_id == NULL)
+ regfi_log_add(REGFI_LOG_WARN, "Hive header's log_id failed to parse.");
+
ret_val->flags = IVAL(file_header, 0x90);
+
ret_val->tm_id = winsec_parse_uuid(ret_val, file_header+0x94, 16);
+ if(ret_val->tm_id == NULL)
+ regfi_log_add(REGFI_LOG_WARN, "Hive header's tm_id failed to parse.");
+
ret_val->guid_signature = IVAL(file_header, 0xa4);
memcpy(ret_val->reserved1, file_header+0xa8, REGFI_REGF_RESERVED1_SIZE);
@@ -2262,51 +2833,56 @@ REGFI_FILE* regfi_parse_regf(int fd, bool strict)
******************************************************************************/
REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32_t offset, bool strict)
{
- REGFI_HBIN *hbin;
+ REGFI_HBIN* hbin = NULL;
uint8_t hbin_header[REGFI_HBIN_HEADER_SIZE];
uint32_t length;
if(offset >= file->file_length)
- return NULL;
+ goto fail;
+
+ if(!regfi_lock(file, &file->cb_lock, "regfi_parse_hbin"))
+ goto fail;
- if(lseek(file->fd, offset, SEEK_SET) == -1)
+ if(regfi_seek(file->cb, offset, SEEK_SET) == -1)
{
- regfi_add_message(file, REGFI_MSG_ERROR, "Seek failed"
- " while parsing hbin at offset 0x%.8X.", offset);
- return NULL;
+ regfi_log_add(REGFI_LOG_ERROR, "Seek failed"
+ " while parsing hbin at offset 0x%.8X.", offset);
+ goto fail_locked;
}
length = REGFI_HBIN_HEADER_SIZE;
- if((regfi_read(file->fd, hbin_header, &length) != 0)
+ if((regfi_read(file->cb, hbin_header, &length) != 0)
|| length != REGFI_HBIN_HEADER_SIZE)
- return NULL;
-
- if(lseek(file->fd, offset, SEEK_SET) == -1)
{
- regfi_add_message(file, REGFI_MSG_ERROR, "Seek failed"
- " while parsing hbin at offset 0x%.8X.", offset);
- return NULL;
+ regfi_log_add(REGFI_LOG_ERROR, "Read failed"
+ " while parsing hbin at offset 0x%.8X.", offset);
+ goto fail_locked;
}
+ if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_hbin"))
+ goto fail;
+
hbin = talloc(NULL, REGFI_HBIN);
if(hbin == NULL)
- return NULL;
+ goto fail;
hbin->file_off = offset;
memcpy(hbin->magic, hbin_header, 4);
if(strict && (memcmp(hbin->magic, "hbin", 4) != 0))
{
- regfi_add_message(file, REGFI_MSG_INFO, "Magic number mismatch "
- "(%.2X %.2X %.2X %.2X) while parsing hbin at offset"
- " 0x%.8X.", hbin->magic[0], hbin->magic[1],
- hbin->magic[2], hbin->magic[3], offset);
- talloc_free(hbin);
- return NULL;
+ /* This always seems to happen at the end of a file, so we make it an INFO
+ * message, rather than something more serious.
+ */
+ regfi_log_add(REGFI_LOG_INFO, "Magic number mismatch "
+ "(%.2X %.2X %.2X %.2X) while parsing hbin at offset"
+ " 0x%.8X.", hbin->magic[0], hbin->magic[1],
+ hbin->magic[2], hbin->magic[3], offset);
+ goto fail;
}
hbin->first_hbin_off = IVAL(hbin_header, 0x4);
hbin->block_size = IVAL(hbin_header, 0x8);
- /* this should be the same thing as hbin->block_size but just in case */
+ /* this should be the same thing as hbin->block_size, but just in case */
hbin->next_block = IVAL(hbin_header, 0x1C);
@@ -2319,49 +2895,56 @@ REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32_t offset, bool strict)
if((offset + hbin->block_size > file->file_length)
|| (hbin->block_size & 0xFFFFF000) != hbin->block_size)
{
- regfi_add_message(file, REGFI_MSG_ERROR, "The hbin offset is not aligned"
- " or runs off the end of the file"
- " while parsing hbin at offset 0x%.8X.", offset);
- talloc_free(hbin);
- return NULL;
+ regfi_log_add(REGFI_LOG_ERROR, "The hbin offset is not aligned"
+ " or runs off the end of the file"
+ " while parsing hbin at offset 0x%.8X.", offset);
+ goto fail;
}
return hbin;
+
+ fail_locked:
+ regfi_unlock(file, &file->cb_lock, "regfi_parse_hbin");
+ fail:
+ talloc_free(hbin);
+ return NULL;
}
/*******************************************************************
*******************************************************************/
-REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32_t offset,
- uint32_t max_size, bool strict)
+REGFI_NK* regfi_parse_nk(REGFI_FILE* file, uint32_t offset,
+ uint32_t max_size, bool strict)
{
uint8_t nk_header[REGFI_NK_MIN_LENGTH];
- REGFI_NK_REC* ret_val;
+ REGFI_NK* ret_val;
uint32_t length,cell_length;
bool unalloc = false;
- if(!regfi_parse_cell(file->fd, offset, nk_header, REGFI_NK_MIN_LENGTH,
- &cell_length, &unalloc))
+ ret_val = talloc(NULL, REGFI_NK);
+ if(ret_val == NULL)
{
- regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell header"
- " while parsing NK record at offset 0x%.8X.", offset);
- return NULL;
+ regfi_log_add(REGFI_LOG_ERROR, "Failed to allocate memory while"
+ " parsing NK record at offset 0x%.8X.", offset);
+ goto fail;
}
- /* A bit of validation before bothering to allocate memory */
- if((nk_header[0x0] != 'n') || (nk_header[0x1] != 'k'))
+ if(!regfi_lock(file, &file->cb_lock, "regfi_parse_nk"))
+ goto fail;
+
+ if(!regfi_parse_cell(file->cb, offset, nk_header, REGFI_NK_MIN_LENGTH,
+ &cell_length, &unalloc))
{
- regfi_add_message(file, REGFI_MSG_WARN, "Magic number mismatch in parsing"
- " NK record at offset 0x%.8X.", offset);
- return NULL;
+ regfi_log_add(REGFI_LOG_WARN, "Could not parse cell header"
+ " while parsing NK record at offset 0x%.8X.", offset);
+ goto fail_locked;
}
- ret_val = talloc(NULL, REGFI_NK_REC);
- if(ret_val == NULL)
+ if((nk_header[0x0] != 'n') || (nk_header[0x1] != 'k'))
{
- regfi_add_message(file, REGFI_MSG_ERROR, "Failed to allocate memory while"
- " parsing NK record at offset 0x%.8X.", offset);
- return NULL;
+ regfi_log_add(REGFI_LOG_WARN, "Magic number mismatch in parsing"
+ " NK record at offset 0x%.8X.", offset);
+ goto fail_locked;
}
ret_val->values = NULL;
@@ -2374,10 +2957,9 @@ REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32_t offset,
if((ret_val->cell_size < REGFI_NK_MIN_LENGTH)
|| (strict && (ret_val->cell_size & 0x00000007) != 0))
{
- regfi_add_message(file, REGFI_MSG_WARN, "A length check failed while"
- " parsing NK record at offset 0x%.8X.", offset);
- talloc_free(ret_val);
- return NULL;
+ regfi_log_add(REGFI_LOG_WARN, "A length check failed while"
+ " parsing NK record at offset 0x%.8X.", offset);
+ goto fail_locked;
}
ret_val->magic[0] = nk_header[0x0];
@@ -2386,23 +2968,21 @@ REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32_t offset,
if((ret_val->flags & ~REGFI_NK_KNOWN_FLAGS) != 0)
{
- regfi_add_message(file, REGFI_MSG_WARN, "Unknown key flags (0x%.4X) while"
- " parsing NK record at offset 0x%.8X.",
- (ret_val->flags & ~REGFI_NK_KNOWN_FLAGS), offset);
+ regfi_log_add(REGFI_LOG_WARN, "Unknown key flags (0x%.4X) while"
+ " parsing NK record at offset 0x%.8X.",
+ (ret_val->flags & ~REGFI_NK_KNOWN_FLAGS), offset);
}
- ret_val->mtime.low = IVAL(nk_header, 0x4);
- ret_val->mtime.high = IVAL(nk_header, 0x8);
+ ret_val->mtime = ((uint64_t)IVAL(nk_header, 0x8)) << 32;
+ ret_val->mtime |= IVAL(nk_header, 0x4);
/* If the key is unallocated and the MTIME is earlier than Jan 1, 1990
* or later than Jan 1, 2290, we consider this a bad key. This helps
* weed out some false positives during deleted data recovery.
*/
if(unalloc
- && ((ret_val->mtime.high < REGFI_MTIME_MIN_HIGH
- && ret_val->mtime.low < REGFI_MTIME_MIN_LOW)
- || (ret_val->mtime.high > REGFI_MTIME_MAX_HIGH
- && ret_val->mtime.low > REGFI_MTIME_MAX_LOW)))
- return NULL;
+ && (ret_val->mtime < REGFI_MTIME_MIN
+ || ret_val->mtime > REGFI_MTIME_MAX))
+ { goto fail_locked; }
ret_val->unknown1 = IVAL(nk_header, 0xC);
ret_val->parent_off = IVAL(nk_header, 0x10);
@@ -2423,16 +3003,15 @@ REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32_t offset,
ret_val->name_length = SVAL(nk_header, 0x48);
ret_val->classname_length = SVAL(nk_header, 0x4A);
- ret_val->keyname = NULL;
+ ret_val->name = NULL;
if(ret_val->name_length + REGFI_NK_MIN_LENGTH > ret_val->cell_size)
{
if(strict)
{
- regfi_add_message(file, REGFI_MSG_ERROR, "Contents too large for cell"
- " while parsing NK record at offset 0x%.8X.", offset);
- talloc_free(ret_val);
- return NULL;
+ regfi_log_add(REGFI_LOG_ERROR, "Contents too large for cell"
+ " while parsing NK record at offset 0x%.8X.", offset);
+ goto fail_locked;
}
else
ret_val->name_length = ret_val->cell_size - REGFI_NK_MIN_LENGTH;
@@ -2449,128 +3028,147 @@ REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32_t offset,
ret_val->cell_size = length;
}
- ret_val->keyname_raw = talloc_array(ret_val, uint8_t, ret_val->name_length);
- if(ret_val->keyname_raw == NULL)
- {
- talloc_free(ret_val);
- return NULL;
- }
+ /* +1 to length in case we decided to use this directly as a string later */
+ ret_val->name_raw = talloc_array(ret_val, uint8_t, ret_val->name_length+1);
+ if(ret_val->name_raw == NULL)
+ goto fail_locked;
/* Don't need to seek, should be at the right offset */
length = ret_val->name_length;
- if((regfi_read(file->fd, (uint8_t*)ret_val->keyname_raw, &length) != 0)
+ if((regfi_read(file->cb, (uint8_t*)ret_val->name_raw, &length) != 0)
|| length != ret_val->name_length)
{
- regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read key name"
- " while parsing NK record at offset 0x%.8X.", offset);
- talloc_free(ret_val);
- return NULL;
+ regfi_log_add(REGFI_LOG_ERROR, "Failed to read key name"
+ " while parsing NK record at offset 0x%.8X.", offset);
+ goto fail_locked;
}
+ if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_nk"))
+ goto fail;
+
return ret_val;
+
+ fail_locked:
+ regfi_unlock(file, &file->cb_lock, "regfi_parse_nk");
+ fail:
+ talloc_free(ret_val);
+ return NULL;
}
uint8_t* regfi_parse_classname(REGFI_FILE* file, uint32_t offset,
- uint16_t* name_length, uint32_t max_size, bool strict)
+ uint16_t* name_length, uint32_t max_size, bool strict)
{
uint8_t* ret_val = NULL;
uint32_t length;
uint32_t cell_length;
bool unalloc = false;
- if(*name_length > 0 && offset != REGFI_OFFSET_NONE
- && (offset & 0x00000007) == 0)
- {
- if(!regfi_parse_cell(file->fd, offset, NULL, 0, &cell_length, &unalloc))
- {
- regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell header"
- " while parsing class name at offset 0x%.8X.", offset);
- return NULL;
- }
-
- if((cell_length & 0x0000007) != 0)
- {
- regfi_add_message(file, REGFI_MSG_ERROR, "Cell length not a multiple of 8"
- " while parsing class name at offset 0x%.8X.", offset);
- return NULL;
- }
+ if(*name_length <= 0 || offset == REGFI_OFFSET_NONE
+ || (offset & 0x00000007) != 0)
+ { goto fail; }
- if(cell_length > max_size)
- {
- regfi_add_message(file, REGFI_MSG_WARN, "Cell stretches past hbin "
- "boundary while parsing class name at offset 0x%.8X.",
- offset);
- if(strict)
- return NULL;
- cell_length = max_size;
- }
+ if(!regfi_lock(file, &file->cb_lock, "regfi_parse_classname"))
+ goto fail;
- if((cell_length - 4) < *name_length)
- {
- regfi_add_message(file, REGFI_MSG_WARN, "Class name is larger than"
- " cell_length while parsing class name at offset"
- " 0x%.8X.", offset);
- if(strict)
- return NULL;
- *name_length = cell_length - 4;
- }
-
- ret_val = talloc_array(NULL, uint8_t, *name_length);
- if(ret_val != NULL)
+ if(!regfi_parse_cell(file->cb, offset, NULL, 0, &cell_length, &unalloc))
+ {
+ regfi_log_add(REGFI_LOG_WARN, "Could not parse cell header"
+ " while parsing class name at offset 0x%.8X.", offset);
+ goto fail_locked;
+ }
+
+ if((cell_length & 0x0000007) != 0)
+ {
+ regfi_log_add(REGFI_LOG_ERROR, "Cell length not a multiple of 8"
+ " while parsing class name at offset 0x%.8X.", offset);
+ goto fail_locked;
+ }
+
+ if(cell_length > max_size)
+ {
+ regfi_log_add(REGFI_LOG_WARN, "Cell stretches past hbin "
+ "boundary while parsing class name at offset 0x%.8X.",
+ offset);
+ if(strict)
+ goto fail_locked;
+ cell_length = max_size;
+ }
+
+ if((cell_length - 4) < *name_length)
+ {
+ regfi_log_add(REGFI_LOG_WARN, "Class name is larger than"
+ " cell_length while parsing class name at offset"
+ " 0x%.8X.", offset);
+ if(strict)
+ goto fail_locked;
+ *name_length = cell_length - 4;
+ }
+
+ ret_val = talloc_array(NULL, uint8_t, *name_length);
+ if(ret_val != NULL)
+ {
+ length = *name_length;
+ if((regfi_read(file->cb, ret_val, &length) != 0)
+ || length != *name_length)
{
- length = *name_length;
- if((regfi_read(file->fd, ret_val, &length) != 0)
- || length != *name_length)
- {
- regfi_add_message(file, REGFI_MSG_ERROR, "Could not read class name"
- " while parsing class name at offset 0x%.8X.", offset);
- talloc_free(ret_val);
- return NULL;
- }
+ regfi_log_add(REGFI_LOG_ERROR, "Could not read class name"
+ " while parsing class name at offset 0x%.8X.", offset);
+ goto fail_locked;
}
}
+ if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_classname"))
+ goto fail;
+
return ret_val;
+
+ fail_locked:
+ regfi_unlock(file, &file->cb_lock, "regfi_parse_classname");
+ fail:
+ talloc_free(ret_val);
+ return NULL;
}
/******************************************************************************
*******************************************************************************/
-REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32_t offset,
+REGFI_VK* regfi_parse_vk(REGFI_FILE* file, uint32_t offset,
uint32_t max_size, bool strict)
{
- REGFI_VK_REC* ret_val;
+ REGFI_VK* ret_val;
uint8_t vk_header[REGFI_VK_MIN_LENGTH];
uint32_t raw_data_size, length, cell_length;
bool unalloc = false;
- if(!regfi_parse_cell(file->fd, offset, vk_header, REGFI_VK_MIN_LENGTH,
+ ret_val = talloc(NULL, REGFI_VK);
+ if(ret_val == NULL)
+ goto fail;
+
+ if(!regfi_lock(file, &file->cb_lock, "regfi_parse_nk"))
+ goto fail;
+
+ if(!regfi_parse_cell(file->cb, offset, vk_header, REGFI_VK_MIN_LENGTH,
&cell_length, &unalloc))
{
- regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell header"
- " while parsing VK record at offset 0x%.8X.", offset);
- return NULL;
+ regfi_log_add(REGFI_LOG_WARN, "Could not parse cell header"
+ " while parsing VK record at offset 0x%.8X.", offset);
+ goto fail_locked;
}
- ret_val = talloc(NULL, REGFI_VK_REC);
- if(ret_val == NULL)
- return NULL;
-
ret_val->offset = offset;
ret_val->cell_size = cell_length;
- ret_val->valuename = NULL;
- ret_val->valuename_raw = NULL;
+ ret_val->name = NULL;
+ ret_val->name_raw = NULL;
if(ret_val->cell_size > max_size)
ret_val->cell_size = max_size & 0xFFFFFFF8;
if((ret_val->cell_size < REGFI_VK_MIN_LENGTH)
|| (ret_val->cell_size & 0x00000007) != 0)
{
- regfi_add_message(file, REGFI_MSG_WARN, "Invalid cell size encountered"
- " while parsing VK record at offset 0x%.8X.", offset);
- talloc_free(ret_val);
- return NULL;
+ regfi_log_add(REGFI_LOG_WARN, "Invalid cell size encountered"
+ " while parsing VK record at offset 0x%.8X.", offset);
+ goto fail_locked;
}
ret_val->magic[0] = vk_header[0x0];
@@ -2581,10 +3179,9 @@ REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32_t offset,
* often have this (and the name length) overwritten with
* 0xFFFF.
*/
- regfi_add_message(file, REGFI_MSG_WARN, "Magic number mismatch"
- " while parsing VK record at offset 0x%.8X.", offset);
- talloc_free(ret_val);
- return NULL;
+ regfi_log_add(REGFI_LOG_WARN, "Magic number mismatch"
+ " while parsing VK record at offset 0x%.8X.", offset);
+ goto fail_locked;
}
ret_val->name_length = SVAL(vk_header, 0x2);
@@ -2603,14 +3200,11 @@ REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32_t offset,
{
if(ret_val->name_length + REGFI_VK_MIN_LENGTH + 4 > ret_val->cell_size)
{
- regfi_add_message(file, REGFI_MSG_WARN, "Name too long for remaining cell"
- " space while parsing VK record at offset 0x%.8X.",
- offset);
+ regfi_log_add(REGFI_LOG_WARN, "Name too long for remaining cell"
+ " space while parsing VK record at offset 0x%.8X.",
+ offset);
if(strict)
- {
- talloc_free(ret_val);
- return NULL;
- }
+ goto fail_locked;
else
ret_val->name_length = ret_val->cell_size - REGFI_VK_MIN_LENGTH - 4;
}
@@ -2620,26 +3214,26 @@ REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32_t offset,
if(cell_length < ret_val->name_length + REGFI_VK_MIN_LENGTH + 4)
cell_length+=8;
- ret_val->valuename_raw = talloc_array(ret_val, uint8_t, ret_val->name_length);
- if(ret_val->valuename_raw == NULL)
- {
- talloc_free(ret_val);
- return NULL;
- }
+ /* +1 to length in case we decided to use this directly as a string later */
+ ret_val->name_raw = talloc_array(ret_val, uint8_t, ret_val->name_length+1);
+ if(ret_val->name_raw == NULL)
+ goto fail_locked;
length = ret_val->name_length;
- if((regfi_read(file->fd, (uint8_t*)ret_val->valuename_raw, &length) != 0)
+ if((regfi_read(file->cb, (uint8_t*)ret_val->name_raw, &length) != 0)
|| length != ret_val->name_length)
{
- regfi_add_message(file, REGFI_MSG_ERROR, "Could not read value name"
- " while parsing VK record at offset 0x%.8X.", offset);
- talloc_free(ret_val);
- return NULL;
+ regfi_log_add(REGFI_LOG_ERROR, "Could not read value name"
+ " while parsing VK record at offset 0x%.8X.", offset);
+ goto fail_locked;
}
}
else
cell_length = REGFI_VK_MIN_LENGTH + 4;
+ if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_nk"))
+ goto fail;
+
if(unalloc)
{
/* If cell_size is still greater, truncate. */
@@ -2648,6 +3242,12 @@ REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32_t offset,
}
return ret_val;
+
+ fail_locked:
+ regfi_unlock(file, &file->cb_lock, "regfi_parse_vk");
+ fail:
+ talloc_free(ret_val);
+ return NULL;
}
@@ -2678,8 +3278,8 @@ REGFI_BUFFER regfi_load_data(REGFI_FILE* file, uint32_t voffset,
/* XXX: add way to skip this check at user discression. */
if(length > REGFI_VK_MAX_DATA_LENGTH)
{
- regfi_add_message(file, REGFI_MSG_WARN, "Value data size %d larger than "
- "%d, truncating...", length, REGFI_VK_MAX_DATA_LENGTH);
+ regfi_log_add(REGFI_LOG_WARN, "Value data size %d larger than "
+ "%d, truncating...", length, REGFI_VK_MAX_DATA_LENGTH);
length = REGFI_VK_MAX_DATA_LENGTH;
}
@@ -2691,32 +3291,38 @@ REGFI_BUFFER regfi_load_data(REGFI_FILE* file, uint32_t voffset,
max_size = regfi_calc_maxsize(file, offset);
if(max_size < 0)
{
- regfi_add_message(file, REGFI_MSG_WARN, "Could not find HBIN for data"
- " at offset 0x%.8X.", offset);
+ regfi_log_add(REGFI_LOG_WARN, "Could not find HBIN for data"
+ " at offset 0x%.8X.", offset);
goto fail;
}
- if(!regfi_parse_cell(file->fd, offset, NULL, 0,
+ if(!regfi_lock(file, &file->cb_lock, "regfi_load_data"))
+ goto fail;
+
+ if(!regfi_parse_cell(file->cb, offset, NULL, 0,
&cell_length, &unalloc))
{
- regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
- " parsing data record at offset 0x%.8X.", offset);
- goto fail;
+ regfi_log_add(REGFI_LOG_WARN, "Could not parse cell while"
+ " parsing data record at offset 0x%.8X.", offset);
+ goto fail_locked;
}
+ if(!regfi_unlock(file, &file->cb_lock, "regfi_load_data"))
+ goto fail;
+
if((cell_length & 0x00000007) != 0)
{
- regfi_add_message(file, REGFI_MSG_WARN, "Cell length not multiple of 8"
- " while parsing data record at offset 0x%.8X.",
- offset);
+ regfi_log_add(REGFI_LOG_WARN, "Cell length not multiple of 8"
+ " while parsing data record at offset 0x%.8X.",
+ offset);
goto fail;
}
if(cell_length > max_size)
{
- regfi_add_message(file, REGFI_MSG_WARN, "Cell extends past HBIN boundary"
- " while parsing data record at offset 0x%.8X.",
- offset);
+ regfi_log_add(REGFI_LOG_WARN, "Cell extends past HBIN boundary"
+ " while parsing data record at offset 0x%.8X.",
+ offset);
goto fail;
}
@@ -2734,10 +3340,10 @@ REGFI_BUFFER regfi_load_data(REGFI_FILE* file, uint32_t voffset,
}
else
{
- regfi_add_message(file, REGFI_MSG_WARN, "Data length (0x%.8X) larger than"
- " remaining cell length (0x%.8X)"
- " while parsing data record at offset 0x%.8X.",
- length, cell_length - 4, offset);
+ regfi_log_add(REGFI_LOG_WARN, "Data length (0x%.8X) larger than"
+ " remaining cell length (0x%.8X)"
+ " while parsing data record at offset 0x%.8X.",
+ length, cell_length - 4, offset);
if(strict)
goto fail;
else
@@ -2750,6 +3356,8 @@ REGFI_BUFFER regfi_load_data(REGFI_FILE* file, uint32_t voffset,
return ret_val;
+ fail_locked:
+ regfi_unlock(file, &file->cb_lock, "regfi_load_data");
fail:
ret_val.buf = NULL;
ret_val.len = 0;
@@ -2769,28 +3377,40 @@ REGFI_BUFFER regfi_parse_data(REGFI_FILE* file, uint32_t offset,
ret_val.buf = NULL;
ret_val.len = 0;
- if(lseek(file->fd, offset+4, SEEK_SET) == -1)
- {
- regfi_add_message(file, REGFI_MSG_WARN, "Could not seek while "
- "reading data at offset 0x%.8X.", offset);
- return ret_val;
- }
-
if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL)
- return ret_val;
+ goto fail;
ret_val.len = length;
+
+ if(!regfi_lock(file, &file->cb_lock, "regfi_parse_data"))
+ goto fail;
+
+ if(regfi_seek(file->cb, offset+4, SEEK_SET) == -1)
+ {
+ regfi_log_add(REGFI_LOG_WARN, "Could not seek while "
+ "reading data at offset 0x%.8X.", offset);
+ goto fail_locked;
+ }
read_length = length;
- if((regfi_read(file->fd, ret_val.buf, &read_length) != 0)
+ if((regfi_read(file->cb, ret_val.buf, &read_length) != 0)
|| read_length != length)
{
- regfi_add_message(file, REGFI_MSG_ERROR, "Could not read data block while"
- " parsing data record at offset 0x%.8X.", offset);
- talloc_free(ret_val.buf);
- ret_val.buf = NULL;
- ret_val.buf = 0;
+ regfi_log_add(REGFI_LOG_ERROR, "Could not read data block while"
+ " parsing data record at offset 0x%.8X.", offset);
+ goto fail_locked;
}
+ if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_data"))
+ goto fail;
+
+ return ret_val;
+
+ fail_locked:
+ regfi_unlock(file, &file->cb_lock, "regfi_parse_data");
+ fail:
+ talloc_free(ret_val.buf);
+ ret_val.buf = NULL;
+ ret_val.buf = 0;
return ret_val;
}
@@ -2810,9 +3430,9 @@ REGFI_BUFFER regfi_parse_little_data(REGFI_FILE* file, uint32_t voffset,
if(length > 4)
{
- regfi_add_message(file, REGFI_MSG_ERROR, "Data in offset but length > 4"
- " while parsing data record. (voffset=0x%.8X, length=%d)",
- voffset, length);
+ regfi_log_add(REGFI_LOG_ERROR, "Data in offset but length > 4"
+ " while parsing data record. (voffset=0x%.8X, length=%d)",
+ voffset, length);
return ret_val;
}
@@ -2842,37 +3462,43 @@ REGFI_BUFFER regfi_parse_big_data_header(REGFI_FILE* file, uint32_t offset,
if(REGFI_BIG_DATA_MIN_LENGTH > max_size)
{
- regfi_add_message(file, REGFI_MSG_WARN, "Big data header exceeded max_size "
- "while parsing big data header at offset 0x%.8X.",offset);
+ regfi_log_add(REGFI_LOG_WARN, "Big data header exceeded max_size "
+ "while parsing big data header at offset 0x%.8X.",offset);
goto fail;
}
- if(!regfi_parse_cell(file->fd, offset, ret_val.buf, REGFI_BIG_DATA_MIN_LENGTH,
+ if(!regfi_lock(file, &file->cb_lock, "regfi_parse_big_data_header"))
+ goto fail;
+
+
+ if(!regfi_parse_cell(file->cb, offset, ret_val.buf, REGFI_BIG_DATA_MIN_LENGTH,
&cell_length, &unalloc))
{
- regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
- " parsing big data header at offset 0x%.8X.", offset);
- goto fail;
+ regfi_log_add(REGFI_LOG_WARN, "Could not parse cell while"
+ " parsing big data header at offset 0x%.8X.", offset);
+ goto fail_locked;
}
+ if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_header"))
+ goto fail;
+
if((ret_val.buf[0] != 'd') || (ret_val.buf[1] != 'b'))
{
- regfi_add_message(file, REGFI_MSG_WARN, "Unknown magic number"
- " (0x%.2X, 0x%.2X) encountered while parsing"
- " big data header at offset 0x%.8X.",
- ret_val.buf[0], ret_val.buf[1], offset);
+ regfi_log_add(REGFI_LOG_WARN, "Unknown magic number"
+ " (0x%.2X, 0x%.2X) encountered while parsing"
+ " big data header at offset 0x%.8X.",
+ ret_val.buf[0], ret_val.buf[1], offset);
goto fail;
}
ret_val.len = REGFI_BIG_DATA_MIN_LENGTH;
return ret_val;
+ fail_locked:
+ regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_header");
fail:
- if(ret_val.buf != NULL)
- {
- talloc_free(ret_val.buf);
- ret_val.buf = NULL;
- }
+ talloc_free(ret_val.buf);
+ ret_val.buf = NULL;
ret_val.len = 0;
return ret_val;
}
@@ -2892,7 +3518,6 @@ uint32_t* regfi_parse_big_data_indirect(REGFI_FILE* file, uint32_t offset,
bool unalloc;
/* XXX: do something with unalloc? */
-
max_size = regfi_calc_maxsize(file, offset);
if((max_size < 0) || (num_chunks*sizeof(uint32_t) + 4 > max_size))
return NULL;
@@ -2901,16 +3526,22 @@ uint32_t* regfi_parse_big_data_indirect(REGFI_FILE* file, uint32_t offset,
if(ret_val == NULL)
goto fail;
- if(!regfi_parse_cell(file->fd, offset, (uint8_t*)ret_val,
+ if(!regfi_lock(file, &file->cb_lock, "regfi_parse_big_data_indirect"))
+ goto fail;
+
+ if(!regfi_parse_cell(file->cb, offset, (uint8_t*)ret_val,
num_chunks*sizeof(uint32_t),
&indirect_length, &unalloc))
{
- regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
- " parsing big data indirect record at offset 0x%.8X.",
- offset);
- goto fail;
+ regfi_log_add(REGFI_LOG_WARN, "Could not parse cell while"
+ " parsing big data indirect record at offset 0x%.8X.",
+ offset);
+ goto fail_locked;
}
+ if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_indirect"))
+ goto fail;
+
/* Convert pointers to proper endianess, verify they are aligned. */
for(i=0; i<num_chunks; i++)
{
@@ -2921,9 +3552,10 @@ uint32_t* regfi_parse_big_data_indirect(REGFI_FILE* file, uint32_t offset,
return ret_val;
+ fail_locked:
+ regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_indirect");
fail:
- if(ret_val != NULL)
- talloc_free(ret_val);
+ talloc_free(ret_val);
return NULL;
}
@@ -2955,22 +3587,30 @@ range_list* regfi_parse_big_data_cells(REGFI_FILE* file, uint32_t* offsets,
for(i=0; i<num_chunks; i++)
{
+ if(!regfi_lock(file, &file->cb_lock, "regfi_parse_big_data_cells"))
+ goto fail;
+
chunk_offset = offsets[i]+REGFI_REGF_SIZE;
- if(!regfi_parse_cell(file->fd, chunk_offset, NULL, 0,
+ if(!regfi_parse_cell(file->cb, chunk_offset, NULL, 0,
&cell_length, &unalloc))
{
- regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
- " parsing big data chunk at offset 0x%.8X.",
- chunk_offset);
- goto fail;
+ regfi_log_add(REGFI_LOG_WARN, "Could not parse cell while"
+ " parsing big data chunk at offset 0x%.8X.",
+ chunk_offset);
+ goto fail_locked;
}
+ if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_cells"))
+ goto fail;
+
if(!range_list_add(ret_val, chunk_offset, cell_length, NULL))
goto fail;
}
return ret_val;
+ fail_locked:
+ regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_cells");
fail:
if(ret_val != NULL)
range_list_free(ret_val);
@@ -3046,9 +3686,9 @@ REGFI_BUFFER regfi_load_big_data(REGFI_FILE* file,
{
if(i+1 != num_chunks)
{
- regfi_add_message(file, REGFI_MSG_WARN, "Left over chunks detected "
- "while constructing big data at offset 0x%.8X "
- "(chunk offset 0x%.8X).", offset, cell_info->offset);
+ regfi_log_add(REGFI_LOG_WARN, "Left over chunks detected "
+ "while constructing big data at offset 0x%.8X "
+ "(chunk offset 0x%.8X).", offset, cell_info->offset);
}
read_length = data_left;
}
@@ -3058,30 +3698,36 @@ REGFI_BUFFER regfi_load_big_data(REGFI_FILE* file,
if(read_length > regfi_calc_maxsize(file, cell_info->offset))
{
- regfi_add_message(file, REGFI_MSG_WARN, "A chunk exceeded the maxsize "
- "while constructing big data at offset 0x%.8X "
- "(chunk offset 0x%.8X).", offset, cell_info->offset);
+ regfi_log_add(REGFI_LOG_WARN, "A chunk exceeded the maxsize "
+ "while constructing big data at offset 0x%.8X "
+ "(chunk offset 0x%.8X).", offset, cell_info->offset);
goto fail;
}
- if(lseek(file->fd, cell_info->offset+sizeof(uint32_t), SEEK_SET) == -1)
- {
- regfi_add_message(file, REGFI_MSG_WARN, "Could not seek to chunk while "
- "constructing big data at offset 0x%.8X "
- "(chunk offset 0x%.8X).", offset, cell_info->offset);
+ if(!regfi_lock(file, &file->cb_lock, "regfi_load_big_data"))
goto fail;
+
+ if(regfi_seek(file->cb, cell_info->offset+sizeof(uint32_t), SEEK_SET) == -1)
+ {
+ regfi_log_add(REGFI_LOG_WARN, "Could not seek to chunk while "
+ "constructing big data at offset 0x%.8X "
+ "(chunk offset 0x%.8X).", offset, cell_info->offset);
+ goto fail_locked;
}
tmp_len = read_length;
- if(regfi_read(file->fd, ret_val.buf+(data_length-data_left),
+ if(regfi_read(file->cb, ret_val.buf+(data_length-data_left),
&read_length) != 0 || (read_length != tmp_len))
{
- regfi_add_message(file, REGFI_MSG_WARN, "Could not read data chunk while"
- " constructing big data at offset 0x%.8X"
- " (chunk offset 0x%.8X).", offset, cell_info->offset);
- goto fail;
+ regfi_log_add(REGFI_LOG_WARN, "Could not read data chunk while"
+ " constructing big data at offset 0x%.8X"
+ " (chunk offset 0x%.8X).", offset, cell_info->offset);
+ goto fail_locked;
}
+ if(!regfi_unlock(file, &file->cb_lock, "regfi_load_big_data"))
+ goto fail;
+
if(used_ranges != NULL)
if(!range_list_add(used_ranges, cell_info->offset,cell_info->length,NULL))
goto fail;
@@ -3093,11 +3739,11 @@ REGFI_BUFFER regfi_load_big_data(REGFI_FILE* file,
ret_val.len = data_length-data_left;
return ret_val;
+ fail_locked:
+ regfi_unlock(file, &file->cb_lock, "regfi_load_big_data");
fail:
- if(ret_val.buf != NULL)
- talloc_free(ret_val.buf);
- if(indirect_ptrs != NULL)
- talloc_free(indirect_ptrs);
+ talloc_free(ret_val.buf);
+ talloc_free(indirect_ptrs);
if(bd_cells != NULL)
range_list_free(bd_cells);
ret_val.buf = NULL;
@@ -3118,6 +3764,12 @@ range_list* regfi_parse_unalloc_cells(REGFI_FILE* file)
if(ret_val == NULL)
return NULL;
+ if(!regfi_read_lock(file, &file->hbins_lock, "regfi_parse_unalloc_cells"))
+ {
+ range_list_free(ret_val);
+ return NULL;
+ }
+
num_hbins = range_list_size(file->hbins);
for(i=0; i<num_hbins; i++)
{
@@ -3129,15 +3781,24 @@ range_list* regfi_parse_unalloc_cells(REGFI_FILE* file)
curr_off = REGFI_HBIN_HEADER_SIZE;
while(curr_off < hbin->block_size)
{
- if(!regfi_parse_cell(file->fd, hbin->file_off+curr_off, NULL, 0,
+ if(!regfi_lock(file, &file->cb_lock, "regfi_parse_unalloc_cells"))
+ break;
+
+ if(!regfi_parse_cell(file->cb, hbin->file_off+curr_off, NULL, 0,
&cell_len, &is_unalloc))
+ {
+ regfi_unlock(file, &file->cb_lock, "regfi_parse_unalloc_cells");
break;
-
+ }
+
+ if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_unalloc_cells"))
+ break;
+
if((cell_len == 0) || ((cell_len & 0x00000007) != 0))
{
- regfi_add_message(file, REGFI_MSG_ERROR, "Bad cell length encountered"
- " while parsing unallocated cells at offset 0x%.8X.",
- hbin->file_off+curr_off);
+ regfi_log_add(REGFI_LOG_ERROR, "Bad cell length encountered"
+ " while parsing unallocated cells at offset 0x%.8X.",
+ hbin->file_off+curr_off);
break;
}
@@ -3156,6 +3817,12 @@ range_list* regfi_parse_unalloc_cells(REGFI_FILE* file)
}
}
+ if(!regfi_rw_unlock(file, &file->hbins_lock, "regfi_parse_unalloc_cells"))
+ {
+ range_list_free(ret_val);
+ return NULL;
+ }
+
return ret_val;
}
@@ -3163,33 +3830,21 @@ range_list* regfi_parse_unalloc_cells(REGFI_FILE* file)
/* From lib/time.c */
/****************************************************************************
- Put a 8 byte filetime from a time_t
+ Returns an 8 byte filetime from a time_t
This takes real GMT as input and converts to kludge-GMT
****************************************************************************/
-void regfi_unix2nt_time(REGFI_NTTIME *nt, time_t t)
+REGFI_NTTIME regfi_unix2nt_time(time_t t)
{
double d;
-
- if (t==0)
- {
- nt->low = 0;
- nt->high = 0;
- return;
- }
+
+ if (t==0)
+ return 0L;
if (t == TIME_T_MAX)
- {
- nt->low = 0xffffffff;
- nt->high = 0x7fffffff;
- return;
- }
+ return 0x7fffffffffffffffL;
if (t == -1)
- {
- nt->low = 0xffffffff;
- nt->high = 0xffffffff;
- return;
- }
+ return 0xffffffffffffffffL;
/* this converts GMT to kludge-GMT */
/* XXX: This was removed due to difficult dependency requirements.
@@ -3198,12 +3853,14 @@ void regfi_unix2nt_time(REGFI_NTTIME *nt, time_t t)
*/
/* t -= TimeDiff(t) - get_serverzone(); */
- d = (double)(t);
- d += TIME_FIXUP_CONSTANT;
+ d = (double)(t) + REGFI_TIME_FIXUP;
d *= 1.0e7;
-
- nt->high = (uint32_t)(d * (1.0/(4.0*(double)(1<<30))));
- nt->low = (uint32_t)(d - ((double)nt->high)*4.0*(double)(1<<30));
+ /*
+ nt->high = (uint32_t)(d * (1.0/c));
+ nt->low = (uint32_t)(d - ((double)nt->high) * c);
+ */
+
+ return (REGFI_NTTIME) d;
}
@@ -3218,32 +3875,17 @@ void regfi_unix2nt_time(REGFI_NTTIME *nt, time_t t)
serverzone. This is NOT the same as GMT in some cases. This routine
converts this to real GMT.
****************************************************************************/
-time_t regfi_nt2unix_time(const REGFI_NTTIME* nt)
+double regfi_nt2unix_time(REGFI_NTTIME nt)
{
- double d;
- time_t ret;
- /* The next two lines are a fix needed for the
- broken SCO compiler. JRA. */
- time_t l_time_min = TIME_T_MIN;
- time_t l_time_max = TIME_T_MAX;
+ double ret_val;
- if (nt->high == 0 || (nt->high == 0xffffffff && nt->low == 0xffffffff))
- return(0);
+ if (nt == 0 || nt == 0xffffffffffffffffL)
+ return 0;
- d = ((double)nt->high)*4.0*(double)(1<<30);
- d += (nt->low&0xFFF00000);
- d *= 1.0e-7;
+ ret_val = (double)(nt) * 1.0e-7;
/* now adjust by 369 years to make the secs since 1970 */
- d -= TIME_FIXUP_CONSTANT;
-
- if (d <= l_time_min)
- return (l_time_min);
-
- if (d >= l_time_max)
- return (l_time_max);
-
- ret = (time_t)(d+0.5);
+ ret_val -= REGFI_TIME_FIXUP;
/* this takes us from kludge-GMT to real GMT */
/* XXX: This was removed due to difficult dependency requirements.
@@ -3255,7 +3897,7 @@ time_t regfi_nt2unix_time(const REGFI_NTTIME* nt)
ret += LocTimeDiff(ret);
*/
- return(ret);
+ return ret_val;
}
/* End of stuff from lib/time.c */
diff --git a/lib/talloc.c b/lib/talloc.c
deleted file mode 100644
index faa4378..0000000
--- a/lib/talloc.c
+++ /dev/null
@@ -1,1696 +0,0 @@
-/*
- Samba Unix SMB/CIFS implementation.
-
- Samba trivial allocation library - new interface
- inspired by http://swapped.cc/halloc/
-
- NOTE: Please read talloc_guide.txt for full documentation
-
- Copyright (C) Andrew Tridgell 2004
- Copyright (C) Stefan Metzmacher 2006
- Copyright (C) Timothy D. Morgan 2009
-
- ** NOTE! The following LGPL license applies to the talloc library.
- ** This does NOT imply that all of reglookup is released under the LGPL
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 3 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-/* $Id: talloc.c 147 2009-02-22 19:31:52Z tim $ */
-
-#include "talloc.h"
-
-/* use this to force every realloc to change the pointer, to stress test
- code that might not cope */
-#define ALWAYS_REALLOC 0
-
-
-#define MAX_TALLOC_SIZE 0x10000000
-#define TALLOC_MAGIC 0xe814ec70
-#define TALLOC_FLAG_FREE 0x01
-#define TALLOC_FLAG_LOOP 0x02
-#define TALLOC_FLAG_POOL 0x04 /* This is a talloc pool */
-#define TALLOC_FLAG_POOLMEM 0x08 /* This is allocated in a pool */
-#define TALLOC_MAGIC_REFERENCE ((const char *)1)
-
-/* by default we abort when given a bad pointer (such as when talloc_free() is called
- on a pointer that came from malloc() */
-#ifndef TALLOC_ABORT
-#define TALLOC_ABORT(reason) abort()
-#endif
-
-#ifndef discard_const_p
-#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
-# define discard_const_p(type, ptr) ((type *)((intptr_t)(ptr)))
-#else
-# define discard_const_p(type, ptr) ((type *)(ptr))
-#endif
-#endif
-
-/* these macros gain us a few percent of speed on gcc */
-#if (__GNUC__ >= 3)
-/* the strange !! is to ensure that __builtin_expect() takes either 0 or 1
- as its first argument */
-#define likely(x) __builtin_expect(!!(x), 1)
-#define unlikely(x) __builtin_expect(!!(x), 0)
-#else
-#define likely(x) x
-#define unlikely(x) x
-#endif
-
-/* this null_context is only used if talloc_enable_leak_report() or
- talloc_enable_leak_report_full() is called, otherwise it remains
- NULL
-*/
-static void *null_context;
-static void *autofree_context;
-
-struct talloc_reference_handle {
- struct talloc_reference_handle *next, *prev;
- void *ptr;
-};
-
-typedef int (*talloc_destructor_t)(void *);
-
-struct talloc_chunk {
- struct talloc_chunk *next, *prev;
- struct talloc_chunk *parent, *child;
- struct talloc_reference_handle *refs;
- talloc_destructor_t destructor;
- const char *name;
- size_t size;
- unsigned flags;
-
- /*
- * "pool" has dual use:
- *
- * For the talloc pool itself (i.e. TALLOC_FLAG_POOL is set), "pool"
- * marks the end of the currently allocated area.
- *
- * For members of the pool (i.e. TALLOC_FLAG_POOLMEM is set), "pool"
- * is a pointer to the struct talloc_chunk of the pool that it was
- * allocated from. This way children can quickly find the pool to chew
- * from.
- */
- void *pool;
-};
-
-/* 16 byte alignment seems to keep everyone happy */
-#define TC_HDR_SIZE ((sizeof(struct talloc_chunk)+15)&~15)
-#define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc))
-
-static void talloc_abort_double_free(void)
-{
- TALLOC_ABORT("Bad talloc magic value - double free");
-}
-
-static void talloc_abort_unknown_value(void)
-{
- TALLOC_ABORT("Bad talloc magic value - unknown value");
-}
-
-/* panic if we get a bad magic value */
-static inline struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr)
-{
- const char *pp = (const char *)ptr;
- struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, pp - TC_HDR_SIZE);
- if (unlikely((tc->flags & (TALLOC_FLAG_FREE | ~0xF)) != TALLOC_MAGIC)) {
- if (tc->flags & TALLOC_FLAG_FREE) {
- talloc_abort_double_free();
- } else {
- talloc_abort_unknown_value();
- }
- }
- return tc;
-}
-
-/* hook into the front of the list */
-#define _TLIST_ADD(list, p) \
-do { \
- if (!(list)) { \
- (list) = (p); \
- (p)->next = (p)->prev = NULL; \
- } else { \
- (list)->prev = (p); \
- (p)->next = (list); \
- (p)->prev = NULL; \
- (list) = (p); \
- }\
-} while (0)
-
-/* remove an element from a list - element doesn't have to be in list. */
-#define _TLIST_REMOVE(list, p) \
-do { \
- if ((p) == (list)) { \
- (list) = (p)->next; \
- if (list) (list)->prev = NULL; \
- } else { \
- if ((p)->prev) (p)->prev->next = (p)->next; \
- if ((p)->next) (p)->next->prev = (p)->prev; \
- } \
- if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \
-} while (0)
-
-
-/*
- return the parent chunk of a pointer
-*/
-static inline struct talloc_chunk *talloc_parent_chunk(const void *ptr)
-{
- struct talloc_chunk *tc;
-
- if (unlikely(ptr == NULL)) {
- return NULL;
- }
-
- tc = talloc_chunk_from_ptr(ptr);
- while (tc->prev) tc=tc->prev;
-
- return tc->parent;
-}
-
-void *talloc_parent(const void *ptr)
-{
- struct talloc_chunk *tc = talloc_parent_chunk(ptr);
- return tc? TC_PTR_FROM_CHUNK(tc) : NULL;
-}
-
-/*
- find parents name
-*/
-const char *talloc_parent_name(const void *ptr)
-{
- struct talloc_chunk *tc = talloc_parent_chunk(ptr);
- return tc? tc->name : NULL;
-}
-
-/*
- A pool carries an in-pool object count count in the first 16 bytes.
- bytes. This is done to support talloc_steal() to a parent outside of the
- pool. The count includes the pool itself, so a talloc_free() on a pool will
- only destroy the pool if the count has dropped to zero. A talloc_free() of a
- pool member will reduce the count, and eventually also call free(3) on the
- pool memory.
-
- The object count is not put into "struct talloc_chunk" because it is only
- relevant for talloc pools and the alignment to 16 bytes would increase the
- memory footprint of each talloc chunk by those 16 bytes.
-*/
-
-#define TALLOC_POOL_HDR_SIZE 16
-
-static unsigned int *talloc_pool_objectcount(struct talloc_chunk *tc)
-{
- return (unsigned int *)((char *)tc + sizeof(struct talloc_chunk));
-}
-
-/*
- Allocate from a pool
-*/
-
-static struct talloc_chunk *talloc_alloc_pool(struct talloc_chunk *parent,
- size_t size)
-{
- struct talloc_chunk *pool_ctx = NULL;
- size_t space_left;
- struct talloc_chunk *result;
- size_t chunk_size;
-
- if (parent == NULL) {
- return NULL;
- }
-
- if (parent->flags & TALLOC_FLAG_POOL) {
- pool_ctx = parent;
- }
- else if (parent->flags & TALLOC_FLAG_POOLMEM) {
- pool_ctx = (struct talloc_chunk *)parent->pool;
- }
-
- if (pool_ctx == NULL) {
- return NULL;
- }
-
- space_left = ((char *)pool_ctx + TC_HDR_SIZE + pool_ctx->size)
- - ((char *)pool_ctx->pool);
-
- /*
- * Align size to 16 bytes
- */
- chunk_size = ((size + 15) & ~15);
-
- if (space_left < chunk_size) {
- return NULL;
- }
-
- result = (struct talloc_chunk *)pool_ctx->pool;
-
-#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED)
- VALGRIND_MAKE_MEM_UNDEFINED(result, size);
-#endif
-
- pool_ctx->pool = (void *)((char *)result + chunk_size);
-
- result->flags = TALLOC_MAGIC | TALLOC_FLAG_POOLMEM;
- result->pool = pool_ctx;
-
- *talloc_pool_objectcount(pool_ctx) += 1;
-
- return result;
-}
-
-/*
- Allocate a bit of memory as a child of an existing pointer
-*/
-static inline void *__talloc(const void *context, size_t size)
-{
- struct talloc_chunk *tc = NULL;
-
- if (unlikely(context == NULL)) {
- context = null_context;
- }
-
- if (unlikely(size >= MAX_TALLOC_SIZE)) {
- return NULL;
- }
-
- if (context != NULL) {
- tc = talloc_alloc_pool(talloc_chunk_from_ptr(context),
- TC_HDR_SIZE+size);
- }
-
- if (tc == NULL) {
- tc = (struct talloc_chunk *)malloc(TC_HDR_SIZE+size);
- if (unlikely(tc == NULL)) return NULL;
- tc->flags = TALLOC_MAGIC;
- tc->pool = NULL;
- }
-
- tc->size = size;
- tc->destructor = NULL;
- tc->child = NULL;
- tc->name = NULL;
- tc->refs = NULL;
-
- if (likely(context)) {
- struct talloc_chunk *parent = talloc_chunk_from_ptr(context);
-
- if (parent->child) {
- parent->child->parent = NULL;
- tc->next = parent->child;
- tc->next->prev = tc;
- } else {
- tc->next = NULL;
- }
- tc->parent = parent;
- tc->prev = NULL;
- parent->child = tc;
- } else {
- tc->next = tc->prev = tc->parent = NULL;
- }
-
- return TC_PTR_FROM_CHUNK(tc);
-}
-
-/*
- * Create a talloc pool
- */
-
-void *talloc_pool(const void *context, size_t size)
-{
- void *result = __talloc(context, size + TALLOC_POOL_HDR_SIZE);
- struct talloc_chunk *tc;
-
- if (unlikely(result == NULL)) {
- return NULL;
- }
-
- tc = talloc_chunk_from_ptr(result);
-
- tc->flags |= TALLOC_FLAG_POOL;
- tc->pool = (char *)result + TALLOC_POOL_HDR_SIZE;
-
- *talloc_pool_objectcount(tc) = 1;
-
-#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS)
- VALGRIND_MAKE_MEM_NOACCESS(tc->pool, size);
-#endif
-
- return result;
-}
-
-/*
- setup a destructor to be called on free of a pointer
- the destructor should return 0 on success, or -1 on failure.
- if the destructor fails then the free is failed, and the memory can
- be continued to be used
-*/
-void _talloc_set_destructor(const void *ptr, int (*destructor)(void *))
-{
- struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
- tc->destructor = destructor;
-}
-
-/*
- increase the reference count on a piece of memory.
-*/
-int talloc_increase_ref_count(const void *ptr)
-{
- if (unlikely(!talloc_reference(null_context, ptr))) {
- return -1;
- }
- return 0;
-}
-
-/*
- helper for talloc_reference()
-
- this is referenced by a function pointer and should not be inline
-*/
-static int talloc_reference_destructor(struct talloc_reference_handle *handle)
-{
- struct talloc_chunk *ptr_tc = talloc_chunk_from_ptr(handle->ptr);
- _TLIST_REMOVE(ptr_tc->refs, handle);
- return 0;
-}
-
-/*
- more efficient way to add a name to a pointer - the name must point to a
- true string constant
-*/
-static inline void _talloc_set_name_const(const void *ptr, const char *name)
-{
- struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
- tc->name = name;
-}
-
-/*
- internal talloc_named_const()
-*/
-static inline void *_talloc_named_const(const void *context, size_t size, const char *name)
-{
- void *ptr;
-
- ptr = __talloc(context, size);
- if (unlikely(ptr == NULL)) {
- return NULL;
- }
-
- _talloc_set_name_const(ptr, name);
-
- return ptr;
-}
-
-/*
- make a secondary reference to a pointer, hanging off the given context.
- the pointer remains valid until both the original caller and this given
- context are freed.
-
- the major use for this is when two different structures need to reference the
- same underlying data, and you want to be able to free the two instances separately,
- and in either order
-*/
-void *_talloc_reference(const void *context, const void *ptr)
-{
- struct talloc_chunk *tc;
- struct talloc_reference_handle *handle;
- if (unlikely(ptr == NULL)) return NULL;
-
- tc = talloc_chunk_from_ptr(ptr);
- handle = (struct talloc_reference_handle *)_talloc_named_const(context,
- sizeof(struct talloc_reference_handle),
- TALLOC_MAGIC_REFERENCE);
- if (unlikely(handle == NULL)) return NULL;
-
- /* note that we hang the destructor off the handle, not the
- main context as that allows the caller to still setup their
- own destructor on the context if they want to */
- talloc_set_destructor(handle, talloc_reference_destructor);
- handle->ptr = discard_const_p(void, ptr);
- _TLIST_ADD(tc->refs, handle);
- return handle->ptr;
-}
-
-
-/*
- internal talloc_free call
-*/
-static inline int _talloc_free(void *ptr)
-{
- struct talloc_chunk *tc;
-
- if (unlikely(ptr == NULL)) {
- return -1;
- }
-
- tc = talloc_chunk_from_ptr(ptr);
-
- if (unlikely(tc->refs)) {
- int is_child;
- /* check this is a reference from a child or grantchild
- * back to it's parent or grantparent
- *
- * in that case we need to remove the reference and
- * call another instance of talloc_free() on the current
- * pointer.
- */
- is_child = talloc_is_parent(tc->refs, ptr);
- _talloc_free(tc->refs);
- if (is_child) {
- return _talloc_free(ptr);
- }
- return -1;
- }
-
- if (unlikely(tc->flags & TALLOC_FLAG_LOOP)) {
- /* we have a free loop - stop looping */
- return 0;
- }
-
- if (unlikely(tc->destructor)) {
- talloc_destructor_t d = tc->destructor;
- if (d == (talloc_destructor_t)-1) {
- return -1;
- }
- tc->destructor = (talloc_destructor_t)-1;
- if (d(ptr) == -1) {
- tc->destructor = d;
- return -1;
- }
- tc->destructor = NULL;
- }
-
- if (tc->parent) {
- _TLIST_REMOVE(tc->parent->child, tc);
- if (tc->parent->child) {
- tc->parent->child->parent = tc->parent;
- }
- } else {
- if (tc->prev) tc->prev->next = tc->next;
- if (tc->next) tc->next->prev = tc->prev;
- }
-
- tc->flags |= TALLOC_FLAG_LOOP;
-
- while (tc->child) {
- /* we need to work out who will own an abandoned child
- if it cannot be freed. In priority order, the first
- choice is owner of any remaining reference to this
- pointer, the second choice is our parent, and the
- final choice is the null context. */
- void *child = TC_PTR_FROM_CHUNK(tc->child);
- const void *new_parent = null_context;
- if (unlikely(tc->child->refs)) {
- struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
- if (p) new_parent = TC_PTR_FROM_CHUNK(p);
- }
- if (unlikely(_talloc_free(child) == -1)) {
- if (new_parent == null_context) {
- struct talloc_chunk *p = talloc_parent_chunk(ptr);
- if (p) new_parent = TC_PTR_FROM_CHUNK(p);
- }
- talloc_steal(new_parent, child);
- }
- }
-
- tc->flags |= TALLOC_FLAG_FREE;
-
- if (tc->flags & (TALLOC_FLAG_POOL|TALLOC_FLAG_POOLMEM)) {
- struct talloc_chunk *pool;
- unsigned int *pool_object_count;
-
- pool = (tc->flags & TALLOC_FLAG_POOL)
- ? tc : (struct talloc_chunk *)tc->pool;
-
- pool_object_count = talloc_pool_objectcount(pool);
-
- if (*pool_object_count == 0) {
- TALLOC_ABORT("Pool object count zero!");
- }
-
- *pool_object_count -= 1;
-
- if (*pool_object_count == 0) {
- free(pool);
- }
- }
- else {
- free(tc);
- }
- return 0;
-}
-
-/*
- move a lump of memory from one talloc context to another return the
- ptr on success, or NULL if it could not be transferred.
- passing NULL as ptr will always return NULL with no side effects.
-*/
-void *_talloc_steal(const void *new_ctx, const void *ptr)
-{
- struct talloc_chunk *tc, *new_tc;
-
- if (unlikely(!ptr)) {
- return NULL;
- }
-
- if (unlikely(new_ctx == NULL)) {
- new_ctx = null_context;
- }
-
- tc = talloc_chunk_from_ptr(ptr);
-
- if (unlikely(new_ctx == NULL)) {
- if (tc->parent) {
- _TLIST_REMOVE(tc->parent->child, tc);
- if (tc->parent->child) {
- tc->parent->child->parent = tc->parent;
- }
- } else {
- if (tc->prev) tc->prev->next = tc->next;
- if (tc->next) tc->next->prev = tc->prev;
- }
-
- tc->parent = tc->next = tc->prev = NULL;
- return discard_const_p(void, ptr);
- }
-
- new_tc = talloc_chunk_from_ptr(new_ctx);
-
- if (unlikely(tc == new_tc || tc->parent == new_tc)) {
- return discard_const_p(void, ptr);
- }
-
- if (tc->parent) {
- _TLIST_REMOVE(tc->parent->child, tc);
- if (tc->parent->child) {
- tc->parent->child->parent = tc->parent;
- }
- } else {
- if (tc->prev) tc->prev->next = tc->next;
- if (tc->next) tc->next->prev = tc->prev;
- }
-
- tc->parent = new_tc;
- if (new_tc->child) new_tc->child->parent = NULL;
- _TLIST_ADD(new_tc->child, tc);
-
- return discard_const_p(void, ptr);
-}
-
-
-
-/*
- remove a secondary reference to a pointer. This undo's what
- talloc_reference() has done. The context and pointer arguments
- must match those given to a talloc_reference()
-*/
-static inline int talloc_unreference(const void *context, const void *ptr)
-{
- struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
- struct talloc_reference_handle *h;
-
- if (unlikely(context == NULL)) {
- context = null_context;
- }
-
- for (h=tc->refs;h;h=h->next) {
- struct talloc_chunk *p = talloc_parent_chunk(h);
- if (p == NULL) {
- if (context == NULL) break;
- } else if (TC_PTR_FROM_CHUNK(p) == context) {
- break;
- }
- }
- if (h == NULL) {
- return -1;
- }
-
- return _talloc_free(h);
-}
-
-/*
- remove a specific parent context from a pointer. This is a more
- controlled varient of talloc_free()
-*/
-int talloc_unlink(const void *context, void *ptr)
-{
- struct talloc_chunk *tc_p, *new_p;
- void *new_parent;
-
- if (ptr == NULL) {
- return -1;
- }
-
- if (context == NULL) {
- context = null_context;
- }
-
- if (talloc_unreference(context, ptr) == 0) {
- return 0;
- }
-
- if (context == NULL) {
- if (talloc_parent_chunk(ptr) != NULL) {
- return -1;
- }
- } else {
- if (talloc_chunk_from_ptr(context) != talloc_parent_chunk(ptr)) {
- return -1;
- }
- }
-
- tc_p = talloc_chunk_from_ptr(ptr);
-
- if (tc_p->refs == NULL) {
- return _talloc_free(ptr);
- }
-
- new_p = talloc_parent_chunk(tc_p->refs);
- if (new_p) {
- new_parent = TC_PTR_FROM_CHUNK(new_p);
- } else {
- new_parent = NULL;
- }
-
- if (talloc_unreference(new_parent, ptr) != 0) {
- return -1;
- }
-
- talloc_steal(new_parent, ptr);
-
- return 0;
-}
-
-/*
- add a name to an existing pointer - va_list version
-*/
-static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
-
-static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap)
-{
- struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
- tc->name = talloc_vasprintf(ptr, fmt, ap);
- if (likely(tc->name)) {
- _talloc_set_name_const(tc->name, ".name");
- }
- return tc->name;
-}
-
-/*
- add a name to an existing pointer
-*/
-const char *talloc_set_name(const void *ptr, const char *fmt, ...)
-{
- const char *name;
- va_list ap;
- va_start(ap, fmt);
- name = talloc_set_name_v(ptr, fmt, ap);
- va_end(ap);
- return name;
-}
-
-
-/*
- create a named talloc pointer. Any talloc pointer can be named, and
- talloc_named() operates just like talloc() except that it allows you
- to name the pointer.
-*/
-void *talloc_named(const void *context, size_t size, const char *fmt, ...)
-{
- va_list ap;
- void *ptr;
- const char *name;
-
- ptr = __talloc(context, size);
- if (unlikely(ptr == NULL)) return NULL;
-
- va_start(ap, fmt);
- name = talloc_set_name_v(ptr, fmt, ap);
- va_end(ap);
-
- if (unlikely(name == NULL)) {
- _talloc_free(ptr);
- return NULL;
- }
-
- return ptr;
-}
-
-/*
- return the name of a talloc ptr, or "UNNAMED"
-*/
-const char *talloc_get_name(const void *ptr)
-{
- struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
- if (unlikely(tc->name == TALLOC_MAGIC_REFERENCE)) {
- return ".reference";
- }
- if (likely(tc->name)) {
- return tc->name;
- }
- return "UNNAMED";
-}
-
-
-/*
- check if a pointer has the given name. If it does, return the pointer,
- otherwise return NULL
-*/
-void *talloc_check_name(const void *ptr, const char *name)
-{
- const char *pname;
- if (unlikely(ptr == NULL)) return NULL;
- pname = talloc_get_name(ptr);
- if (likely(pname == name || strcmp(pname, name) == 0)) {
- return discard_const_p(void, ptr);
- }
- return NULL;
-}
-
-
-/*
- this is for compatibility with older versions of talloc
-*/
-void *talloc_init(const char *fmt, ...)
-{
- va_list ap;
- void *ptr;
- const char *name;
-
- /*
- * samba3 expects talloc_report_depth_cb(NULL, ...)
- * reports all talloc'ed memory, so we need to enable
- * null_tracking
- */
- talloc_enable_null_tracking();
-
- ptr = __talloc(NULL, 0);
- if (unlikely(ptr == NULL)) return NULL;
-
- va_start(ap, fmt);
- name = talloc_set_name_v(ptr, fmt, ap);
- va_end(ap);
-
- if (unlikely(name == NULL)) {
- _talloc_free(ptr);
- return NULL;
- }
-
- return ptr;
-}
-
-/*
- this is a replacement for the Samba3 talloc_destroy_pool functionality. It
- should probably not be used in new code. It's in here to keep the talloc
- code consistent across Samba 3 and 4.
-*/
-void talloc_free_children(void *ptr)
-{
- struct talloc_chunk *tc;
-
- if (unlikely(ptr == NULL)) {
- return;
- }
-
- tc = talloc_chunk_from_ptr(ptr);
-
- while (tc->child) {
- /* we need to work out who will own an abandoned child
- if it cannot be freed. In priority order, the first
- choice is owner of any remaining reference to this
- pointer, the second choice is our parent, and the
- final choice is the null context. */
- void *child = TC_PTR_FROM_CHUNK(tc->child);
- const void *new_parent = null_context;
- if (unlikely(tc->child->refs)) {
- struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
- if (p) new_parent = TC_PTR_FROM_CHUNK(p);
- }
- if (unlikely(_talloc_free(child) == -1)) {
- if (new_parent == null_context) {
- struct talloc_chunk *p = talloc_parent_chunk(ptr);
- if (p) new_parent = TC_PTR_FROM_CHUNK(p);
- }
- talloc_steal(new_parent, child);
- }
- }
-
- if ((tc->flags & TALLOC_FLAG_POOL)
- && (*talloc_pool_objectcount(tc) == 1)) {
- tc->pool = ((char *)tc + TC_HDR_SIZE + TALLOC_POOL_HDR_SIZE);
-#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS)
- VALGRIND_MAKE_MEM_NOACCESS(
- tc->pool, tc->size - TALLOC_POOL_HDR_SIZE);
-#endif
- }
-}
-
-/*
- Allocate a bit of memory as a child of an existing pointer
-*/
-void *_talloc(const void *context, size_t size)
-{
- return __talloc(context, size);
-}
-
-/*
- externally callable talloc_set_name_const()
-*/
-void talloc_set_name_const(const void *ptr, const char *name)
-{
- _talloc_set_name_const(ptr, name);
-}
-
-/*
- create a named talloc pointer. Any talloc pointer can be named, and
- talloc_named() operates just like talloc() except that it allows you
- to name the pointer.
-*/
-void *talloc_named_const(const void *context, size_t size, const char *name)
-{
- return _talloc_named_const(context, size, name);
-}
-
-/*
- free a talloc pointer. This also frees all child pointers of this
- pointer recursively
-
- return 0 if the memory is actually freed, otherwise -1. The memory
- will not be freed if the ref_count is > 1 or the destructor (if
- any) returns non-zero
-*/
-int talloc_free(void *ptr)
-{
- return _talloc_free(ptr);
-}
-
-
-
-/*
- A talloc version of realloc. The context argument is only used if
- ptr is NULL
-*/
-void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name)
-{
- struct talloc_chunk *tc;
- void *new_ptr;
- bool malloced = false;
-
- /* size zero is equivalent to free() */
- if (unlikely(size == 0)) {
- _talloc_free(ptr);
- return NULL;
- }
-
- if (unlikely(size >= MAX_TALLOC_SIZE)) {
- return NULL;
- }
-
- /* realloc(NULL) is equivalent to malloc() */
- if (ptr == NULL) {
- return _talloc_named_const(context, size, name);
- }
-
- tc = talloc_chunk_from_ptr(ptr);
-
- /* don't allow realloc on referenced pointers */
- if (unlikely(tc->refs)) {
- return NULL;
- }
-
- /* don't shrink if we have less than 1k to gain */
- if ((size < tc->size) && ((tc->size - size) < 1024)) {
- tc->size = size;
- return ptr;
- }
-
- /* by resetting magic we catch users of the old memory */
- tc->flags |= TALLOC_FLAG_FREE;
-
-#if ALWAYS_REALLOC
- new_ptr = malloc(size + TC_HDR_SIZE);
- if (new_ptr) {
- memcpy(new_ptr, tc, tc->size + TC_HDR_SIZE);
- free(tc);
- }
-#else
- if (tc->flags & TALLOC_FLAG_POOLMEM) {
-
- new_ptr = talloc_alloc_pool(tc, size + TC_HDR_SIZE);
- *talloc_pool_objectcount((struct talloc_chunk *)
- (tc->pool)) -= 1;
-
- if (new_ptr == NULL) {
- new_ptr = malloc(TC_HDR_SIZE+size);
- malloced = true;
- }
-
- if (new_ptr) {
- memcpy(new_ptr, tc, MIN(tc->size,size) + TC_HDR_SIZE);
- }
- }
- else {
- new_ptr = realloc(tc, size + TC_HDR_SIZE);
- }
-#endif
- if (unlikely(!new_ptr)) {
- tc->flags &= ~TALLOC_FLAG_FREE;
- return NULL;
- }
-
- tc = (struct talloc_chunk *)new_ptr;
- tc->flags &= ~TALLOC_FLAG_FREE;
- if (malloced) {
- tc->flags &= ~TALLOC_FLAG_POOLMEM;
- }
- if (tc->parent) {
- tc->parent->child = tc;
- }
- if (tc->child) {
- tc->child->parent = tc;
- }
-
- if (tc->prev) {
- tc->prev->next = tc;
- }
- if (tc->next) {
- tc->next->prev = tc;
- }
-
- tc->size = size;
- _talloc_set_name_const(TC_PTR_FROM_CHUNK(tc), name);
-
- return TC_PTR_FROM_CHUNK(tc);
-}
-
-/*
- a wrapper around talloc_steal() for situations where you are moving a pointer
- between two structures, and want the old pointer to be set to NULL
-*/
-void *_talloc_move(const void *new_ctx, const void *_pptr)
-{
- const void **pptr = discard_const_p(const void *,_pptr);
- void *ret = _talloc_steal(new_ctx, *pptr);
- (*pptr) = NULL;
- return ret;
-}
-
-/*
- return the total size of a talloc pool (subtree)
-*/
-size_t talloc_total_size(const void *ptr)
-{
- size_t total = 0;
- struct talloc_chunk *c, *tc;
-
- if (ptr == NULL) {
- ptr = null_context;
- }
- if (ptr == NULL) {
- return 0;
- }
-
- tc = talloc_chunk_from_ptr(ptr);
-
- if (tc->flags & TALLOC_FLAG_LOOP) {
- return 0;
- }
-
- tc->flags |= TALLOC_FLAG_LOOP;
-
- total = tc->size;
- for (c=tc->child;c;c=c->next) {
- total += talloc_total_size(TC_PTR_FROM_CHUNK(c));
- }
-
- tc->flags &= ~TALLOC_FLAG_LOOP;
-
- return total;
-}
-
-/*
- return the total number of blocks in a talloc pool (subtree)
-*/
-size_t talloc_total_blocks(const void *ptr)
-{
- size_t total = 0;
- struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr);
-
- if (tc->flags & TALLOC_FLAG_LOOP) {
- return 0;
- }
-
- tc->flags |= TALLOC_FLAG_LOOP;
-
- total++;
- for (c=tc->child;c;c=c->next) {
- total += talloc_total_blocks(TC_PTR_FROM_CHUNK(c));
- }
-
- tc->flags &= ~TALLOC_FLAG_LOOP;
-
- return total;
-}
-
-/*
- return the number of external references to a pointer
-*/
-size_t talloc_reference_count(const void *ptr)
-{
- struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
- struct talloc_reference_handle *h;
- size_t ret = 0;
-
- for (h=tc->refs;h;h=h->next) {
- ret++;
- }
- return ret;
-}
-
-/*
- report on memory usage by all children of a pointer, giving a full tree view
-*/
-void talloc_report_depth_cb(const void *ptr, int depth, int max_depth,
- void (*callback)(const void *ptr,
- int depth, int max_depth,
- int is_ref,
- void *private_data),
- void *private_data)
-{
- struct talloc_chunk *c, *tc;
-
- if (ptr == NULL) {
- ptr = null_context;
- }
- if (ptr == NULL) return;
-
- tc = talloc_chunk_from_ptr(ptr);
-
- if (tc->flags & TALLOC_FLAG_LOOP) {
- return;
- }
-
- callback(ptr, depth, max_depth, 0, private_data);
-
- if (max_depth >= 0 && depth >= max_depth) {
- return;
- }
-
- tc->flags |= TALLOC_FLAG_LOOP;
- for (c=tc->child;c;c=c->next) {
- if (c->name == TALLOC_MAGIC_REFERENCE) {
- struct talloc_reference_handle *h = (struct talloc_reference_handle *)TC_PTR_FROM_CHUNK(c);
- callback(h->ptr, depth + 1, max_depth, 1, private_data);
- } else {
- talloc_report_depth_cb(TC_PTR_FROM_CHUNK(c), depth + 1, max_depth, callback, private_data);
- }
- }
- tc->flags &= ~TALLOC_FLAG_LOOP;
-}
-
-static void talloc_report_depth_FILE_helper(const void *ptr, int depth, int max_depth, int is_ref, void *_f)
-{
- const char *name = talloc_get_name(ptr);
- FILE *f = (FILE *)_f;
-
- if (is_ref) {
- fprintf(f, "%*sreference to: %s\n", depth*4, "", name);
- return;
- }
-
- if (depth == 0) {
- fprintf(f,"%stalloc report on '%s' (total %6lu bytes in %3lu blocks)\n",
- (max_depth < 0 ? "full " :""), name,
- (unsigned long)talloc_total_size(ptr),
- (unsigned long)talloc_total_blocks(ptr));
- return;
- }
-
- fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d) %p\n",
- depth*4, "",
- name,
- (unsigned long)talloc_total_size(ptr),
- (unsigned long)talloc_total_blocks(ptr),
- (int)talloc_reference_count(ptr), ptr);
-
-#if 0
- fprintf(f, "content: ");
- if (talloc_total_size(ptr)) {
- int tot = talloc_total_size(ptr);
- int i;
-
- for (i = 0; i < tot; i++) {
- if ((((char *)ptr)[i] > 31) && (((char *)ptr)[i] < 126)) {
- fprintf(f, "%c", ((char *)ptr)[i]);
- } else {
- fprintf(f, "~%02x", ((char *)ptr)[i]);
- }
- }
- }
- fprintf(f, "\n");
-#endif
-}
-
-/*
- report on memory usage by all children of a pointer, giving a full tree view
-*/
-void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f)
-{
- talloc_report_depth_cb(ptr, depth, max_depth, talloc_report_depth_FILE_helper, f);
- fflush(f);
-}
-
-/*
- report on memory usage by all children of a pointer, giving a full tree view
-*/
-void talloc_report_full(const void *ptr, FILE *f)
-{
- talloc_report_depth_file(ptr, 0, -1, f);
-}
-
-/*
- report on memory usage by all children of a pointer
-*/
-void talloc_report(const void *ptr, FILE *f)
-{
- talloc_report_depth_file(ptr, 0, 1, f);
-}
-
-/*
- report on any memory hanging off the null context
-*/
-static void talloc_report_null(void)
-{
- if (talloc_total_size(null_context) != 0) {
- talloc_report(null_context, stderr);
- }
-}
-
-/*
- report on any memory hanging off the null context
-*/
-static void talloc_report_null_full(void)
-{
- if (talloc_total_size(null_context) != 0) {
- talloc_report_full(null_context, stderr);
- }
-}
-
-/*
- enable tracking of the NULL context
-*/
-void talloc_enable_null_tracking(void)
-{
- if (null_context == NULL) {
- null_context = _talloc_named_const(NULL, 0, "null_context");
- }
-}
-
-/*
- disable tracking of the NULL context
-*/
-void talloc_disable_null_tracking(void)
-{
- _talloc_free(null_context);
- null_context = NULL;
-}
-
-/*
- enable leak reporting on exit
-*/
-void talloc_enable_leak_report(void)
-{
- talloc_enable_null_tracking();
- atexit(talloc_report_null);
-}
-
-/*
- enable full leak reporting on exit
-*/
-void talloc_enable_leak_report_full(void)
-{
- talloc_enable_null_tracking();
- atexit(talloc_report_null_full);
-}
-
-/*
- talloc and zero memory.
-*/
-void *_talloc_zero(const void *ctx, size_t size, const char *name)
-{
- void *p = _talloc_named_const(ctx, size, name);
-
- if (p) {
- memset(p, '\0', size);
- }
-
- return p;
-}
-
-/*
- memdup with a talloc.
-*/
-void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name)
-{
- void *newp = _talloc_named_const(t, size, name);
-
- if (likely(newp)) {
- memcpy(newp, p, size);
- }
-
- return newp;
-}
-
-static inline char *__talloc_strlendup(const void *t, const char *p, size_t len)
-{
- char *ret;
-
- ret = (char *)__talloc(t, len + 1);
- if (unlikely(!ret)) return NULL;
-
- memcpy(ret, p, len);
- ret[len] = 0;
-
- _talloc_set_name_const(ret, ret);
- return ret;
-}
-
-/*
- strdup with a talloc
-*/
-char *talloc_strdup(const void *t, const char *p)
-{
- if (unlikely(!p)) return NULL;
- return __talloc_strlendup(t, p, strlen(p));
-}
-
-/*
- strndup with a talloc
-*/
-char *talloc_strndup(const void *t, const char *p, size_t n)
-{
- if (unlikely(!p)) return NULL;
- return __talloc_strlendup(t, p, n);
-}
-
-static inline char *__talloc_strlendup_append(char *s, size_t slen,
- const char *a, size_t alen)
-{
- char *ret;
-
- ret = talloc_realloc(NULL, s, char, slen + alen + 1);
- if (unlikely(!ret)) return NULL;
-
- /* append the string and the trailing \0 */
- memcpy(&ret[slen], a, alen);
- ret[slen+alen] = 0;
-
- _talloc_set_name_const(ret, ret);
- return ret;
-}
-
-/*
- * Appends at the end of the string.
- */
-char *talloc_strdup_append(char *s, const char *a)
-{
- if (unlikely(!s)) {
- return talloc_strdup(NULL, a);
- }
-
- if (unlikely(!a)) {
- return s;
- }
-
- return __talloc_strlendup_append(s, strlen(s), a, strlen(a));
-}
-
-/*
- * Appends at the end of the talloc'ed buffer,
- * not the end of the string.
- */
-char *talloc_strdup_append_buffer(char *s, const char *a)
-{
- size_t slen;
-
- if (unlikely(!s)) {
- return talloc_strdup(NULL, a);
- }
-
- if (unlikely(!a)) {
- return s;
- }
-
- slen = talloc_get_size(s);
- if (likely(slen > 0)) {
- slen--;
- }
-
- return __talloc_strlendup_append(s, slen, a, strlen(a));
-}
-
-/*
- * Appends at the end of the string.
- */
-char *talloc_strndup_append(char *s, const char *a, size_t n)
-{
- if (unlikely(!s)) {
- return talloc_strdup(NULL, a);
- }
-
- if (unlikely(!a)) {
- return s;
- }
-
- return __talloc_strlendup_append(s, strlen(s), a, n);
-}
-
-/*
- * Appends at the end of the talloc'ed buffer,
- * not the end of the string.
- */
-char *talloc_strndup_append_buffer(char *s, const char *a, size_t n)
-{
- size_t slen;
-
- if (unlikely(!s)) {
- return talloc_strdup(NULL, a);
- }
-
- if (unlikely(!a)) {
- return s;
- }
-
- slen = talloc_get_size(s);
- if (likely(slen > 0)) {
- slen--;
- }
-
- return __talloc_strlendup_append(s, slen, a, n);
-}
-
-
-char *talloc_vasprintf(const void *t, const char *fmt, va_list ap)
-{
- int len;
- char *ret;
- va_list ap2;
- char c;
-
- /* this call looks strange, but it makes it work on older solaris boxes */
- va_copy(ap2, ap);
- len = vsnprintf(&c, 1, fmt, ap2);
- va_end(ap2);
- if (unlikely(len < 0)) {
- return NULL;
- }
-
- ret = (char *)__talloc(t, len+1);
- if (unlikely(!ret)) return NULL;
-
- va_copy(ap2, ap);
- vsnprintf(ret, len+1, fmt, ap2);
- va_end(ap2);
-
- _talloc_set_name_const(ret, ret);
- return ret;
-}
-
-
-/*
- Perform string formatting, and return a pointer to newly allocated
- memory holding the result, inside a memory pool.
- */
-char *talloc_asprintf(const void *t, const char *fmt, ...)
-{
- va_list ap;
- char *ret;
-
- va_start(ap, fmt);
- ret = talloc_vasprintf(t, fmt, ap);
- va_end(ap);
- return ret;
-}
-
-static inline char *__talloc_vaslenprintf_append(char *s, size_t slen,
- const char *fmt, va_list ap)
- PRINTF_ATTRIBUTE(3,0);
-
-static inline char *__talloc_vaslenprintf_append(char *s, size_t slen,
- const char *fmt, va_list ap)
-{
- ssize_t alen;
- va_list ap2;
- char c;
-
- va_copy(ap2, ap);
- alen = vsnprintf(&c, 1, fmt, ap2);
- va_end(ap2);
-
- if (alen <= 0) {
- /* Either the vsnprintf failed or the format resulted in
- * no characters being formatted. In the former case, we
- * ought to return NULL, in the latter we ought to return
- * the original string. Most current callers of this
- * function expect it to never return NULL.
- */
- return s;
- }
-
- s = talloc_realloc(NULL, s, char, slen + alen + 1);
- if (!s) return NULL;
-
- va_copy(ap2, ap);
- vsnprintf(s + slen, alen + 1, fmt, ap2);
- va_end(ap2);
-
- _talloc_set_name_const(s, s);
- return s;
-}
-
-/**
- * Realloc @p s to append the formatted result of @p fmt and @p ap,
- * and return @p s, which may have moved. Good for gradually
- * accumulating output into a string buffer. Appends at the end
- * of the string.
- **/
-char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap)
-{
- if (unlikely(!s)) {
- return talloc_vasprintf(NULL, fmt, ap);
- }
-
- return __talloc_vaslenprintf_append(s, strlen(s), fmt, ap);
-}
-
-/**
- * Realloc @p s to append the formatted result of @p fmt and @p ap,
- * and return @p s, which may have moved. Always appends at the
- * end of the talloc'ed buffer, not the end of the string.
- **/
-char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap)
-{
- size_t slen;
-
- if (unlikely(!s)) {
- return talloc_vasprintf(NULL, fmt, ap);
- }
-
- slen = talloc_get_size(s);
- if (likely(slen > 0)) {
- slen--;
- }
-
- return __talloc_vaslenprintf_append(s, slen, fmt, ap);
-}
-
-/*
- Realloc @p s to append the formatted result of @p fmt and return @p
- s, which may have moved. Good for gradually accumulating output
- into a string buffer.
- */
-char *talloc_asprintf_append(char *s, const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- s = talloc_vasprintf_append(s, fmt, ap);
- va_end(ap);
- return s;
-}
-
-/*
- Realloc @p s to append the formatted result of @p fmt and return @p
- s, which may have moved. Good for gradually accumulating output
- into a buffer.
- */
-char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- s = talloc_vasprintf_append_buffer(s, fmt, ap);
- va_end(ap);
- return s;
-}
-
-/*
- alloc an array, checking for integer overflow in the array size
-*/
-void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name)
-{
- if (count >= MAX_TALLOC_SIZE/el_size) {
- return NULL;
- }
- return _talloc_named_const(ctx, el_size * count, name);
-}
-
-/*
- alloc an zero array, checking for integer overflow in the array size
-*/
-void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name)
-{
- if (count >= MAX_TALLOC_SIZE/el_size) {
- return NULL;
- }
- return _talloc_zero(ctx, el_size * count, name);
-}
-
-/*
- realloc an array, checking for integer overflow in the array size
-*/
-void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name)
-{
- if (count >= MAX_TALLOC_SIZE/el_size) {
- return NULL;
- }
- return _talloc_realloc(ctx, ptr, el_size * count, name);
-}
-
-/*
- a function version of talloc_realloc(), so it can be passed as a function pointer
- to libraries that want a realloc function (a realloc function encapsulates
- all the basic capabilities of an allocation library, which is why this is useful)
-*/
-void *talloc_realloc_fn(const void *context, void *ptr, size_t size)
-{
- return _talloc_realloc(context, ptr, size, NULL);
-}
-
-
-static int talloc_autofree_destructor(void *ptr)
-{
- autofree_context = NULL;
- return 0;
-}
-
-static void talloc_autofree(void)
-{
- _talloc_free(autofree_context);
-}
-
-/*
- return a context which will be auto-freed on exit
- this is useful for reducing the noise in leak reports
-*/
-void *talloc_autofree_context(void)
-{
- if (autofree_context == NULL) {
- autofree_context = _talloc_named_const(NULL, 0, "autofree_context");
- talloc_set_destructor(autofree_context, talloc_autofree_destructor);
- atexit(talloc_autofree);
- }
- return autofree_context;
-}
-
-size_t talloc_get_size(const void *context)
-{
- struct talloc_chunk *tc;
-
- if (context == NULL)
- return 0;
-
- tc = talloc_chunk_from_ptr(context);
-
- return tc->size;
-}
-
-/*
- find a parent of this context that has the given name, if any
-*/
-void *talloc_find_parent_byname(const void *context, const char *name)
-{
- struct talloc_chunk *tc;
-
- if (context == NULL) {
- return NULL;
- }
-
- tc = talloc_chunk_from_ptr(context);
- while (tc) {
- if (tc->name && strcmp(tc->name, name) == 0) {
- return TC_PTR_FROM_CHUNK(tc);
- }
- while (tc && tc->prev) tc = tc->prev;
- if (tc) {
- tc = tc->parent;
- }
- }
- return NULL;
-}
-
-/*
- show the parentage of a context
-*/
-void talloc_show_parents(const void *context, FILE *file)
-{
- struct talloc_chunk *tc;
-
- if (context == NULL) {
- fprintf(file, "talloc no parents for NULL\n");
- return;
- }
-
- tc = talloc_chunk_from_ptr(context);
- fprintf(file, "talloc parents of '%s'\n", talloc_get_name(context));
- while (tc) {
- fprintf(file, "\t'%s'\n", talloc_get_name(TC_PTR_FROM_CHUNK(tc)));
- while (tc && tc->prev) tc = tc->prev;
- if (tc) {
- tc = tc->parent;
- }
- }
- fflush(file);
-}
-
-/*
- return 1 if ptr is a parent of context
-*/
-int talloc_is_parent(const void *context, const void *ptr)
-{
- struct talloc_chunk *tc;
-
- if (context == NULL) {
- return 0;
- }
-
- tc = talloc_chunk_from_ptr(context);
- while (tc) {
- if (TC_PTR_FROM_CHUNK(tc) == ptr) return 1;
- while (tc && tc->prev) tc = tc->prev;
- if (tc) {
- tc = tc->parent;
- }
- }
- return 0;
-}
diff --git a/lib/winsec.c b/lib/winsec.c
index 2aea7ba..ce2c3a6 100644
--- a/lib/winsec.c
+++ b/lib/winsec.c
@@ -1,6 +1,5 @@
/*
- *
- * Copyright (C) 2005-2006,2009-2010 Timothy D. Morgan
+ * Copyright (C) 2005-2006,2009-2011 Timothy D. Morgan
* Copyright (C) 1992-2005 Samba development team
* (see individual files under Subversion for details.)
*
@@ -17,7 +16,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: winsec.c 169 2010-03-03 19:24:58Z tim $
+ * $Id: winsec.c 261 2011-06-17 00:55:49Z tim $
*/
/** @file */
@@ -61,6 +60,7 @@ WINSEC_DESC* winsec_parse_desc(void* talloc_ctx,
ret_val->sbz1 = buf[1];
ret_val->control = SVAL(buf, 0x2);
+ /* XXX: should probably reject any non-self relative */
if(!(ret_val->control & WINSEC_DESC_SELF_RELATIVE))
fprintf(stderr, "DEBUG: NOT self-relative!\n");
@@ -225,7 +225,9 @@ WINSEC_ACE* winsec_parse_ace(void* talloc_ctx,
ret_val->flags = buf[1];
ret_val->size = SVAL(buf, 0x2);
ret_val->access_mask = IVAL(buf, 0x4);
-
+ ret_val->obj_guid = NULL;
+ ret_val->inh_guid = NULL;
+
offset = 0x8;
/* check whether object access is present */
@@ -245,8 +247,6 @@ WINSEC_ACE* winsec_parse_ace(void* talloc_ctx,
}
offset += sizeof(WINSEC_UUID);
}
- else
- ret_val->obj_guid = NULL;
if(ret_val->obj_flags & WINSEC_ACE_OBJECT_INHERITED_PRESENT)
{
@@ -259,8 +259,6 @@ WINSEC_ACE* winsec_parse_ace(void* talloc_ctx,
}
offset += sizeof(WINSEC_UUID);
}
- else
- ret_val->inh_guid = NULL;
}
ret_val->trustee = winsec_parse_dom_sid(ret_val, buf+offset, buf_len-offset);
@@ -409,6 +407,30 @@ bool winsec_sid_equal(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2)
/******************************************************************************
+ ******************************************************************************/
+char* winsec_sid2str(const WINSEC_DOM_SID* sid)
+{
+ uint32_t i, size = WINSEC_MAX_SUBAUTHS*11 + 24;
+ uint32_t left = size;
+ uint8_t comps = sid->num_auths;
+ char* ret_val = malloc(size);
+
+ if(ret_val == NULL)
+ return NULL;
+
+ if(comps > WINSEC_MAX_SUBAUTHS)
+ comps = WINSEC_MAX_SUBAUTHS;
+
+ left -= sprintf(ret_val, "S-%u-%u", sid->sid_rev_num, sid->id_auth[5]);
+
+ for (i = 0; i < comps; i++)
+ left -= snprintf(ret_val+(size-left), left, "-%u", sid->sub_auths[i]);
+
+ return ret_val;
+}
+
+
+/******************************************************************************
* Compares two WINSEC_DESC structures.
******************************************************************************/
bool winsec_desc_equal(WINSEC_DESC* s1, WINSEC_DESC* s2)
diff --git a/pyregfi-distutils.py b/pyregfi-distutils.py
new file mode 100644
index 0000000..41c11df
--- /dev/null
+++ b/pyregfi-distutils.py
@@ -0,0 +1,4 @@
+# Called from scons with appropriate python version
+
+from distutils.core import setup
+setup(name='pyregfi', version='1.0', package_dir={'':'python'}, packages=['pyregfi'])
diff --git a/python/experimental/SConstruct b/python/experimental/SConstruct
new file mode 100644
index 0000000..e4cae85
--- /dev/null
+++ b/python/experimental/SConstruct
@@ -0,0 +1,46 @@
+import os, os.path, pdb
+from functools import partial
+import utils
+import config
+import class_parser
+
+def build_python_bindings(target, source, env, initialization=''):
+ """ A command to generate python bindings """
+ module_name = os.path.splitext(os.path.basename(target[0].name))[0]
+ utils.warn("Generating automatic python bindings for module %s" % module_name)
+
+ p = class_parser.HeaderParser(module_name, verbose=config.V)
+ p.module.init_string = initialization
+ p.parse_filenames([s.get_abspath() for s in source])
+
+ fd = open(target[0].get_abspath(), 'w')
+ p.write(fd)
+ fd.close()
+
+
+
+nenv = utils.ExtendedEnvironment()
+
+BOUND_FILES = Split("""
+ ../include/regfi.h
+ regfi/pyregfi.h
+ """)
+
+nenv.config = config
+if config.DEBUG:
+ nenv.Append(CFLAGS = "-std=gnu99 -pedantic -Wall -fPIC -ggdb -O0")
+ nenv.Append(CPPPATH=['../include', 'include'])
+ nenv.python_cppflags = '-I/usr/include/python2.5_d'
+else:
+ nenv.Append(CFLAGS = "-std=gnu99 -pedantic -Wall -fPIC")
+ nenv.Append(CPPPATH=['../include', 'include'])
+
+nenv.Append(LIBPATH="../lib")
+nenv.Append(LINKFLAGS="")
+# XXX: why should I need to call regfi_init() when it should be called only once automatically?
+nenv.Command('regfi/pyregfi.c', BOUND_FILES, partial(build_python_bindings,
+ initialization='pyregfi_init();regfi_init();'))
+nenv.Depends('regfi/pyregfi.c', 'class_parser.py')
+
+nenv.PythonModule("pyregfi", ['regfi/pyregfi.c', 'regfi/regfi.c', 'regfi/class.c', 'regfi/error.c'],
+ LIBS=['regfi', 'talloc'])
diff --git a/python/experimental/class_parser.py b/python/experimental/class_parser.py
new file mode 100644
index 0000000..ebd5a58
--- /dev/null
+++ b/python/experimental/class_parser.py
@@ -0,0 +1,2973 @@
+import sys, os, re, pdb, StringIO
+
+DEBUG = 0
+
+def log(msg):
+ if DEBUG>0:
+ sys.stderr.write(msg+"\n")
+
+def escape_for_string(string):
+ result = string
+ result = result.encode("string-escape")
+ result = result.replace('"',r'\"')
+
+ return result
+
+class Module:
+ def __init__(self, name):
+ self.name = name
+ self.constants = set()
+ self.classes = {}
+ self.headers = '#include <Python.h>\n'
+
+ self.files = []
+
+ def __str__(self):
+ result = "Module %s\n" % (self.name)
+ l = self.classes.values()
+ l.sort()
+ for attr in l:
+ if attr.is_active():
+ result += " %s\n" % attr
+
+ l = list(self.constants)
+ l.sort()
+ result += 'Constants:\n'
+ for attr, type in l:
+ result += " %s\n" % attr
+
+ return result
+
+ init_string = ''
+ def initialization(self):
+ result = self.init_string + """
+talloc_set_log_fn((void(*)(const char*))printf);
+//talloc_enable_leak_report();
+"""
+ for cls in self.classes.values():
+ if cls.is_active():
+ result += cls.initialise()
+
+ return result
+
+ def add_constant(self, constant, type="numeric"):
+ """ This will be called to add #define constant macros """
+ self.constants.add((constant, type))
+
+ def add_class(self, cls, handler):
+ self.classes[cls.class_name] = cls
+
+ ## Make a wrapper in the type dispatcher so we can handle
+ ## passing this class from/to python
+ type_dispatcher[cls.class_name] = handler
+
+ def private_functions(self):
+ """ Emits hard coded private functions for doing various things """
+ return """
+/* The following is a static array mapping CLASS() pointers to their
+python wrappers. This is used to allow the correct wrapper to be
+chosen depending on the object type found - regardless of the
+prototype.
+
+This is basically a safer way for us to cast the correct python type
+depending on context rather than assuming a type based on the .h
+definition. For example consider the function
+
+AFFObject Resolver.open(uri, mode)
+
+The .h file implies that an AFFObject object is returned, but this is
+not true as most of the time an object of a derived class will be
+returned. In C we cast the returned value to the correct type. In the
+python wrapper we just instantiate the correct python object wrapper
+at runtime depending on the actual returned type. We use this lookup
+table to do so.
+*/
+static int TOTAL_CLASSES=0;
+
+/* This is a global reference to this module so classes can call each
+ other.
+*/
+static PyObject *g_module = NULL;
+
+static struct python_wrapper_map_t {
+ Object class_ref;
+ PyTypeObject *python_type;
+} python_wrappers[%s];
+
+/** This is a generic wrapper type */
+typedef struct {
+ PyObject_HEAD
+ void *base;
+ void *ctx;
+} Gen_wrapper;
+
+/* Create the relevant wrapper from the item based on the lookup
+table.
+*/
+Gen_wrapper *new_class_wrapper(Object item) {
+ int i;
+ Gen_wrapper *result;
+ Object cls;
+
+ /* Hack to get the current null_context */
+ void* null_context;
+ int* tmp = talloc(NULL, int);
+ null_context = talloc_parent(tmp);
+ talloc_free(tmp);
+
+ // Return None for a NULL pointer
+ if(!item) {
+ Py_INCREF(Py_None);
+ return (Gen_wrapper *)Py_None;
+ }
+
+ // Search for subclasses
+ for(cls=(Object)item->__class__; cls != cls->__super__; cls=cls->__super__) {
+ for(i=0; i<TOTAL_CLASSES; i++) {
+ if(python_wrappers[i].class_ref == cls) {
+ PyErr_Clear();
+
+ result = (Gen_wrapper *)_PyObject_New(python_wrappers[i].python_type);
+ result->ctx = talloc_asprintf(NULL, "new_class_wrapper %%s@%%p",
+ NAMEOF(item), (void*)item);
+ result->base = (void *)item;
+
+ /* If its owned by the null_context it means that the function does
+ not want to own the memory - we therefore steal it so it gets freed
+ with the python object. */
+ if(talloc_parent(result->base) == null_context)
+ talloc_steal(result->ctx, result->base);
+
+ return result;
+ }
+ }
+ }
+
+ PyErr_Format(PyExc_RuntimeError, "Unable to find a wrapper for object %%s", NAMEOF(item));
+ return NULL;
+}
+
+static PyObject *resolve_exception(char **error_buff) {
+ enum _error_type *type = aff4_get_current_error(error_buff);
+ switch(*type) {
+case EProgrammingError:
+ return PyExc_SystemError;
+case EKeyError:
+ return PyExc_KeyError;
+case ERuntimeError:
+ return PyExc_RuntimeError;
+case EWarning:
+ return PyExc_AssertionError;
+default:
+ return PyExc_RuntimeError;
+};
+}
+
+static int type_check(PyObject *obj, PyTypeObject *type) {
+ PyTypeObject *tmp;
+
+ // Recurse through the inheritance tree and check if the types are expected
+ if(obj)
+ for(tmp = obj->ob_type; tmp && tmp != &PyBaseObject_Type; tmp = tmp->tp_base) {
+ if(tmp == type) return 1;
+ }
+
+ return 0;
+}
+
+static int check_error() {
+ if(!CheckError(EZero)) {
+ char *buffer;
+ PyObject *exception = resolve_exception(&buffer);
+
+ PyErr_Format(exception, "%%s", buffer);
+ ClearError();
+ return 1;
+ }
+ return 0;
+}
+
+#define CHECK_ERROR if(check_error()) goto error;
+
+""" % (len(self.classes)+1)
+
+ def initialise_class(self, class_name, out, done = None):
+ if done and class_name in done: return
+
+ done.add(class_name)
+
+ cls = self.classes[class_name]
+ """ Write out class initialisation code into the main init function. """
+ if cls.is_active():
+ base_class = self.classes.get(cls.base_class_name)
+
+ if base_class and base_class.is_active():
+ ## We have a base class - ensure it gets written out
+ ## first:
+ self.initialise_class(cls.base_class_name, out, done)
+
+ ## Now assign ourselves as derived from them
+ out.write(" %s_Type.tp_base = &%s_Type;" % (
+ cls.class_name, cls.base_class_name))
+
+ out.write("""
+ %(name)s_Type.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&%(name)s_Type) < 0)
+ return;
+
+ Py_INCREF((PyObject *)&%(name)s_Type);
+ PyModule_AddObject(m, "%(name)s", (PyObject *)&%(name)s_Type);
+""" % {'name': cls.class_name})
+
+ def write(self, out):
+ ## Prepare all classes
+ for cls in self.classes.values():
+ cls.prepare()
+
+ out.write("""
+/**********************************************************************
+ Autogenerated module %s
+
+This module was autogenerated from the following files:
+""" % self.name)
+ for file in self.files:
+ out.write("%s\n" % file)
+
+ out.write("\nThis module implements the following classes:\n")
+ out.write(self.__str__())
+ out.write("""***********************************************************************/
+""")
+ out.write(self.headers)
+ out.write(self.private_functions())
+
+ for cls in self.classes.values():
+ if cls.is_active():
+ out.write("/******************** %s ***********************/" % cls.class_name)
+ cls.struct(out)
+ cls.prototypes(out)
+
+ out.write("/*****************************************************\n Implementation\n******************************************************/\n\n")
+ for cls in self.classes.values():
+ if cls.is_active():
+ cls.PyMethodDef(out)
+ cls.code(out)
+ cls.PyTypeObject(out)
+
+ ## Write the module initializer
+ out.write("""
+static PyMethodDef %(module)s_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+PyMODINIT_FUNC init%(module)s(void) {
+ /* Make sure threads are enabled */
+ PyEval_InitThreads();
+
+ /* create module */
+ PyObject *m = Py_InitModule3("%(module)s", %(module)s_methods,
+ "%(module)s module.");
+ PyObject *d = PyModule_GetDict(m);
+ PyObject *tmp;
+
+ g_module = m;
+""" % {'module': self.name})
+
+ ## The trick is to initialise the classes in order of their
+ ## inheritance. The following code will order initializations
+ ## according to their inheritance tree
+ done = set()
+ for class_name in self.classes.keys():
+ self.initialise_class(class_name, out, done)
+
+ ## Add the constants in here
+ for constant, type in self.constants:
+ if type == 'integer':
+ out.write(""" tmp = PyLong_FromUnsignedLongLong((int64_t)%s); \n""" % constant)
+ elif type == 'string':
+ out.write(" tmp = PyString_FromString((char *)%s); \n" % constant)
+ else:
+ out.write(" // I dont know how to convert %s type %s\n" % (constant, type))
+ continue
+
+ out.write("""
+ PyDict_SetItemString(d, "%s", tmp);
+ Py_DECREF(tmp);\n""" % (constant))
+
+ out.write(self.initialization())
+ out.write("}\n\n")
+
+class Type:
+ interface = None
+ buildstr = 'O'
+ sense = 'IN'
+ error_value = "return 0;"
+ active = True
+
+ def __init__(self, name, type):
+ self.name = name
+ self.type = type
+ self.attributes = set()
+
+ def comment(self):
+ return "%s %s " % (self.type, self.name)
+
+ def python_name(self):
+ return self.name
+
+ def python_proxy_post_call(self):
+ """ This is called after a proxy call """
+ return ''
+
+ def returned_python_definition(self, *arg, **kw):
+ return self.definition(*arg, **kw)
+
+ def definition(self, default=None, **kw):
+ if default:
+ return "%s %s=%s;\n" % (self.type, self.name, default)
+ else:
+ return "%s __attribute__((unused)) %s;\n" % (self.type, self.name)
+
+ def byref(self):
+ return "&%s" % self.name
+
+ def call_arg(self):
+ return self.name
+
+ def pre_call(self, method):
+ return ''
+
+ def assign(self, call, method, target=None):
+ return "Py_BEGIN_ALLOW_THREADS\n%s = %s;\nPy_END_ALLOW_THREADS\n" % (target or self.name, call)
+
+ def post_call(self, method):
+ if "DESTRUCTOR" in self.attributes:
+ return "talloc_free(self->ctx); self->base = NULL;\n"
+
+ return ''
+
+ def from_python_object(self, source, destination, method, **kw):
+ return ''
+
+ def return_value(self, value):
+ return "return %s;" % value
+
+ def __str__(self):
+ if self.name == 'func_return':
+ return self.type
+ if 'void' in self.type:
+ return ''
+
+ result = "%s : %s" % (self.type, self.name)
+
+ return result
+
+class String(Type):
+ interface = 'string'
+ buildstr = 's'
+ error_value = "return NULL;"
+
+ def __init__(self, name, type):
+ Type.__init__(self, name, type)
+ self.length = "strlen(%s)" % name
+
+ def byref(self):
+ return "&%s" % self.name
+
+ def to_python_object(self, name=None, result='py_result',**kw):
+ name = name or self.name
+
+ result = """PyErr_Clear();
+ if(!%(name)s) { PyErr_Format(PyExc_RuntimeError, "%(name)s is NULL"); goto error; }
+ %(result)s = PyString_FromStringAndSize((char *)%(name)s, %(length)s);\nif(!%(result)s) goto error;
+""" % dict(name=name, result=result,length=self.length)
+
+ if "BORROWED" not in self.attributes and 'BORROWED' not in kw:
+ result += "talloc_unlink(NULL, %s);\n" % name
+
+ return result
+
+ def from_python_object(self, source, destination, method, context='NULL'):
+ method.error_set = True
+ return """
+{
+ char *buff; Py_ssize_t length;
+
+ PyErr_Clear();
+ if(-1==PyString_AsStringAndSize(%(source)s, &buff, &length))
+ goto error;
+
+ %(destination)s = talloc_size(%(context)s, length + 1);
+ memcpy(%(destination)s, buff, length);
+ %(destination)s[length]=0;
+}
+""" % dict(source = source, destination = destination, context =context)
+
+class ZString(String):
+ interface = 'null_terminated_string'
+
+class BorrowedString(String):
+ def to_python_object(self, name=None, result='py_result', **kw):
+ name = name or self.name
+ return "PyErr_Clear();\n" +\
+ "%s = PyString_FromStringAndSize((char *)%(name)s, %(length)s);\n" % dict(
+ name=name, length=self.length, result=result)
+
+class Char_and_Length(Type):
+ interface = 'char_and_length'
+ buildstr = 's#'
+ error_value = "return NULL;"
+
+ def __init__(self, data, data_type, length, length_type):
+ Type.__init__(self, data, data_type)
+
+ self.name = data
+ self.data_type=data_type
+ self.length = length
+ self.length_type = length_type
+
+ def comment(self):
+ return "%s %s, %s %s" % (self.data_type, self.name,
+ self.length_type, self.length)
+
+ def definition(self, default = '""', **kw):
+ return "char *%s=%s; Py_ssize_t %s=strlen(%s);\n" % (
+ self.name, default,
+ self.length, default)
+
+ def byref(self):
+ return "&%s, &%s" % (self.name, self.length)
+
+ def call_arg(self):
+ return "(%s)%s, (%s)%s" % (self.data_type, self.name, self.length_type,
+ self.length)
+
+ def to_python_object(self, name=None, result='py_result', **kw):
+ return "PyErr_Clear();\n"\
+ "%s = PyString_FromStringAndSize(%s, %s);\nif(!%s) goto error;" % (
+ result, self.name, self.length, result);
+
+class Integer(Type):
+ interface = 'integer'
+ buildstr = 'K'
+
+ def __init__(self, name,type):
+ Type.__init__(self,name,type)
+ self.type = 'uint64_t'
+ self.original_type = type
+
+ def definition(self, default = 0, **kw):
+ return Type.definition(self, default)
+
+ def to_python_object(self, name=None, result='py_result', **kw):
+ name = name or self.name
+ return "PyErr_Clear();\n%s = PyLong_FromLongLong(%s);\n" % (result, name)
+
+ def from_python_object(self, source, destination, method, **kw):
+ return "PyErr_Clear();\n"\
+ "%(destination)s = PyLong_AsUnsignedLong(%(source)s);\n" % dict(
+ source = source, destination= destination)
+
+ def comment(self):
+ return "%s %s " % (self.original_type, self.name)
+
+class Integer32(Integer):
+ buildstr = 'I'
+
+ def __init__(self, name,type):
+ Type.__init__(self,name,type)
+ self.type = 'unsigned int '
+ self.original_type = type
+
+ def to_python_object(self, name=None, result='py_result', **kw):
+ name = name or self.name
+ return "PyErr_Clear();\n%s = PyLong_FromLong(%s);\n" % (result, name)
+
+class Integer64(Integer):
+ buildstr = 'K'
+ type = 'unsigned int'
+
+class Char(Integer):
+ buildstr = "s"
+ interface = 'small_integer'
+
+ def to_python_object(self, name = None, result = 'py_result', **kw):
+ ## We really want to return a string here
+ return """{ char *str_%(name)s = &%(name)s;
+ PyErr_Clear();
+ %(result)s = PyString_FromStringAndSize(str_%(name)s, 1);
+if(!%(result)s) goto error;
+}
+""" % dict(result=result, name = name or self.name)
+
+ def definition(self, default = '"\\x0"', **kw):
+ ## Shut up unused warnings
+ return "char %s __attribute__((unused))=0;\nchar *str_%s __attribute__((unused)) = %s;\n" % (
+ self.name,self.name, default)
+
+ def byref(self):
+ return "&str_%s" % self.name
+
+ def pre_call(self, method):
+ method.error_set = True
+ return """
+if(strlen(str_%(name)s)!=1) {
+ PyErr_Format(PyExc_RuntimeError,
+ "You must only provide a single character for arg %(name)r");
+ goto error;
+}
+
+%(name)s = str_%(name)s[0];
+""" % dict(name = self.name)
+
+class StringOut(String):
+ sense = 'OUT'
+
+class IntegerOut(Integer):
+ sense = 'OUT_DONE'
+ buildstr = ''
+
+ def python_name(self):
+ return None
+
+ def byref(self):
+ return ''
+
+ def call_arg(self):
+ return "&%s" % self.name
+
+class Integer32Out(Integer32):
+ sense = 'OUT_DONE'
+ buildstr = ''
+
+ def python_name(self):
+ return None
+
+ def byref(self):
+ return ''
+
+ def call_arg(self):
+ return "&%s" % self.name
+
+class Char_and_Length_OUT(Char_and_Length):
+ sense = 'OUT_DONE'
+ buildstr = 'l'
+
+ def definition(self, default = 0, **kw):
+ return "char *%s=NULL; Py_ssize_t %s=%s;\n" % (
+ self.name,
+ self.length, default) + "PyObject *tmp_%s;\n" % self.name
+
+ def python_name(self):
+ return self.length
+
+ def byref(self):
+ return "&%s" % self.length
+
+ def pre_call(self, method):
+ return """PyErr_Clear();
+tmp_%s = PyString_FromStringAndSize(NULL, %s);
+if(!tmp_%s) goto error;
+PyString_AsStringAndSize(tmp_%s, &%s, (Py_ssize_t *)&%s);
+""" % (self.name, self.length, self.name, self.name, self.name, self.length)
+
+ def to_python_object(self, name=None, result='py_result', sense='in', **kw):
+ name = name or self.name
+ if 'results' in kw:
+ kw['results'].pop(0)
+
+ if sense=='proxied':
+ return "py_%s = PyLong_FromLong(%s);\n" % (self.name, self.length)
+
+ return """if(func_return > %(length)s) {
+ func_return = 0;
+}
+
+_PyString_Resize(&tmp_%(name)s, func_return); \n%(result)s = tmp_%(name)s;\n""" % \
+ dict(name= name, result= result, length=self.length)
+
+
+ def python_proxy_post_call(self, result='py_result'):
+ return """
+{
+ char *tmp_buff; int tmp_len;
+ if(-1==PyString_AsStringAndSize(%(result)s, &tmp_buff, &tmp_len)) goto error;
+
+ memcpy(%(name)s,tmp_buff, tmp_len);
+ Py_DECREF(%(result)s);
+ %(result)s = PyLong_FromLong(tmp_len);
+}
+""" % dict(result = result, name=self.name)
+
+class TDB_DATA_P(Char_and_Length_OUT):
+ bare_type = "TDB_DATA"
+
+ def __init__(self, name, type):
+ Type.__init__(self, name, type)
+
+ def definition(self, default=None, **kw):
+ return Type.definition(self)
+
+ def byref(self):
+ return "%s.dptr, &%s.dsize" % (self.name, self.name)
+
+ def pre_call(self, method):
+ return ''
+
+ def call_arg(self):
+ return Type.call_arg(self)
+
+ def to_python_object(self, name=None,result='py_result', **kw):
+ name = name or self.name
+ return "PyErr_Clear();"\
+ "%s = PyString_FromStringAndSize((char *)%s->dptr, %s->dsize);"\
+ "\ntalloc_free(%s);" % (result,
+ name, name, name)
+
+ def from_python_object(self, source, destination, method, **kw):
+ method.error_set = True
+ return """
+%(destination)s = talloc(self, %(bare_type)s);
+{ Py_ssize_t tmp; char *buf;
+
+ PyErr_Clear();
+ if(-1==PyString_AsStringAndSize(%(source)s, &buf, &tmp)) {
+ goto error;
+}
+
+ // Take a copy of the python string
+ %(destination)s->dptr = talloc_memdup(%(destination)s, buf, tmp);
+ %(destination)s->dsize = tmp;
+}
+// We no longer need the python object
+Py_DECREF(%(source)s);
+""" % dict(source = source, destination = destination,
+ bare_type = self.bare_type)
+
+class TDB_DATA(TDB_DATA_P):
+ def to_python_object(self, name = None, result='py_result', **kw):
+ name = name or self.name
+
+ return "PyErr_Clear();\n"\
+ "%s = PyString_FromStringAndSize((char *)%s.dptr, %s.dsize);\n" % (
+ result,
+ name, name)
+
+class Void(Type):
+ buildstr = ''
+ error_value = "return;"
+ original_type = ''
+
+ def __init__(self, *args):
+ Type.__init__(self, None, 'void *')
+
+ def definition(self, default = None, **kw):
+ return ''
+
+ def to_python_object(self, name=None, result = 'py_result', **kw):
+ return "Py_INCREF(Py_None); py_result = Py_None;\n"
+
+ def call_arg(self):
+ return "NULL"
+
+ def byref(self):
+ return None
+
+ def assign(self, call, method, target=None):
+ ## We dont assign the result to anything
+ return "Py_BEGIN_ALLOW_THREADS\n%s;\nPy_END_ALLOW_THREADS\n" % call
+
+ def return_value(self, value):
+ return "return;"
+
+class StringArray(String):
+ interface = 'array'
+ buildstr = 'O'
+
+ def definition(self, default = '""', **kw):
+ return "char **%s=NULL; PyObject *py_%s=NULL;\n" % (
+ self.name, self.name)
+
+ def byref(self):
+ return "&py_%s" % (self.name)
+
+ def from_python_object(self, source, destination, method, context='NULL'):
+ method.error_set = True
+ return """{
+Py_ssize_t i,size=0;
+
+if(%(source)s) {
+ if(!PySequence_Check(%(source)s)) {
+ PyErr_Format(PyExc_ValueError, "%(destination)s must be a sequence");
+ goto error;
+ }
+
+ size = PySequence_Size(%(source)s);
+}
+
+%(destination)s = talloc_zero_array(NULL, char *, size + 1);
+
+for(i=0; i<size;i++) {
+ PyObject *tmp = PySequence_GetItem(%(source)s, i);
+ if(!tmp) goto error;
+ %(destination)s[i] = PyString_AsString(tmp);
+ if(!%(destination)s[i]) {
+ Py_DECREF(tmp);
+ goto error;
+ }
+ Py_DECREF(tmp);
+}
+
+}""" % dict(source = source, destination = destination, context = context)
+
+ def pre_call(self, method):
+ return self.from_python_object("py_%s" % self.name, self.name, method)
+
+ def error_condition(self):
+ return """if(%s) talloc_free(%s);\n""" % (self.name, self.name)
+
+class Wrapper(Type):
+ """ This class represents a wrapped C type """
+ sense = 'IN'
+ error_value = "return NULL;"
+
+ def from_python_object(self, source, destination, method, **kw):
+ return """
+/* First check that the returned value is in fact a Wrapper */
+if(!type_check(%(source)s, &%(type)s_Type)) {
+ PyErr_Format(PyExc_RuntimeError, "function must return an %(type)s instance");
+ goto error;
+}
+
+%(destination)s = ((Gen_wrapper *)%(source)s)->base;
+""" % dict(source = source, destination = destination, type = self.type)
+
+ def to_python_object(self, **kw):
+ return ''
+
+ def returned_python_definition(self, default = 'NULL', sense='in', **kw):
+ return "%s %s;\n" % (self.type, self.name)
+
+ def definition(self, default = 'NULL', sense='in', **kw):
+ result = "Gen_wrapper *%s __attribute__((unused)) = %s;\n" % (self.name, default)
+ if sense == 'in' and not 'OUT' in self.attributes:
+ result += " %s __attribute__((unused)) call_%s;\n" % (self.type, self.name)
+
+ return result
+
+ def call_arg(self):
+ return "call_%s" % self.name
+
+ def pre_call(self, method):
+ if 'OUT' in self.attributes or self.sense == 'OUT':
+ return ''
+
+ return """
+if(!%(name)s || (PyObject *)%(name)s==Py_None) {
+ call_%(name)s = NULL;
+} else if(!type_check((PyObject *)%(name)s,&%(type)s_Type)) {
+ PyErr_Format(PyExc_RuntimeError, "%(name)s must be derived from type %(type)s");
+ goto error;
+} else {
+ call_%(name)s = %(name)s->base;
+}\n""" % self.__dict__
+
+ def assign(self, call, method, target=None):
+ method.error_set = True;
+ args = dict(name = target or self.name, call = call, type = self.type)
+
+ result = """{
+ Object returned_object;
+
+ ClearError();
+
+ Py_BEGIN_ALLOW_THREADS
+ returned_object = (Object)%(call)s;
+ Py_END_ALLOW_THREADS
+
+ CHECK_ERROR;
+""" % args
+
+ ## Is NULL an acceptable return type? In some python code NULL
+ ## can be returned (e.g. in iterators) but usually it should
+ ## be converted to None.
+ if "NULL_OK" in self.attributes:
+ result += """if(returned_object == NULL) {
+ %(name)s = NULL; """ % args
+ else:
+ result += """
+ // A NULL return without errors means we return None
+ if(!returned_object) {
+ %(name)s = (Gen_wrapper *)Py_None;
+ Py_INCREF(Py_None);
+""" % args
+
+ result += """
+ } else {
+ //printf("%%s: Wrapping %%s@%%p\\n", __FUNCTION__, NAMEOF(returned_object), returned_object);
+ %(name)s = new_class_wrapper(returned_object);
+ if(!%(name)s) goto error;
+""" % args
+
+ if "BORROWED" in self.attributes:
+ result += " talloc_reference(%(name)s->ctx, %(name)s->base);\n" % args
+
+ result += """ }
+ }
+"""
+ return result
+
+ def to_python_object(self, name=None, result = 'py_result', sense='in', **kw):
+ name = name or self.name
+ args = dict(result=result,
+ name = name)
+
+ if sense=='proxied':
+ return "%(result)s = (PyObject *)new_class_wrapper((Object)%(name)s);\n" % args
+
+ return "%(result)s = (PyObject *)%(name)s;\n" % args
+
+class PointerWrapper(Wrapper):
+ """ A pointer to a wrapped class """
+ def __init__(self, name, type):
+ type = type.split()[0]
+ Wrapper.__init__(self,name, type)
+
+ def definition(self, default = 'NULL', sense='in', **kw):
+ result = "Gen_wrapper *%s = %s;" % (self.name, default)
+ if sense == 'in' and not 'OUT' in self.attributes:
+ result += " %s *call_%s;\n" % (self.type, self.name)
+
+ return result
+
+ def pre_call(self, method):
+ if 'OUT' in self.attributes or self.sense == 'OUT':
+ return ''
+
+ return """
+if(!%(name)s || (PyObject *)%(name)s==Py_None) {
+ call_%(name)s = NULL;
+} else if(!type_check((PyObject *)%(name)s,&%(type)s_Type)) {
+ PyErr_Format(PyExc_RuntimeError, "%(name)s must be derived from type %(type)s");
+ goto error;
+} else {
+ call_%(name)s = (%(type)s *)&%(name)s->base;
+}\n""" % self.__dict__
+
+class StructWrapper(Wrapper):
+ """ A wrapper for struct classes """
+ def assign(self, call, method, target = None):
+ args = dict(name = target or self.name, call = call, type = self.type)
+ result = """
+PyErr_Clear();
+%(name)s = (Gen_wrapper *)PyObject_New(py%(type)s, &%(type)s_Type);
+%(name)s->ctx = talloc_size(NULL, 1);
+%(name)s->base = %(call)s;
+""" % args
+
+ if "NULL_OK" in self.attributes:
+ result += "if(!%(name)s->base) { Py_DECREF(%(name)s); return NULL; }" % args
+
+ result += """
+// A NULL object gets translated to a None
+ if(!%(name)s->base) {
+ Py_DECREF(%(name)s);
+ Py_INCREF(Py_None);
+ %(name)s = (Gen_wrapper *)Py_None;
+ } else {
+""" % args
+
+ if "FOREIGN" in self.attributes:
+ result += '// Not taking references to foreign memory\n'
+ elif "BORROWED" in self.attributes:
+ result += "talloc_reference(%(name)s->ctx, %(name)s->base);\n" % args
+ else:
+ result += "talloc_steal(%(name)s->ctx, %(name)s->base);\n" % args
+
+ result += "}\n"
+
+ return result
+
+ def byref(self):
+ return "&%s" % self.name
+
+ def definition(self, default = 'NULL', sense='in', **kw):
+ result = "Gen_wrapper *%s = %s;" % (self.name, default)
+ if sense == 'in' and not 'OUT' in self.attributes:
+ result += " %s *call_%s;\n" % (self.type, self.name)
+
+ return result;
+
+class PointerStructWrapper(StructWrapper):
+ def __init__(self, name, type):
+ type = type.split()[0]
+ Wrapper.__init__(self,name, type)
+
+class Timeval(Type):
+ """ handle struct timeval values """
+ interface = 'numeric'
+ buildstr = 'f'
+
+ def definition(self, default = None, **kw):
+ return "float %(name)s_flt; struct timeval %(name)s;\n" % self.__dict__
+
+ def byref(self):
+ return "&%s_flt" % self.name
+
+ def pre_call(self, method):
+ return "%(name)s.tv_sec = (int)%(name)s_flt; %(name)s.tv_usec = (%(name)s_flt - %(name)s.tv_sec) * 1e6;\n" % self.__dict__
+
+ def to_python_object(self, name=None, result = 'py_result', **kw):
+ name = name or self.name
+ return """%(name)s_flt = (double)(%(name)s.tv_sec) + %(name)s.tv_usec;
+%(result)s = PyFloat_FromDouble(%(name)s_flt);
+""" % dict(name = name, result=result)
+
+class PyObject(Type):
+ """ Accept an opaque python object """
+ interface = 'opaque'
+ buildstr = 'O'
+ def definition(self, default = 'NULL', **kw):
+ self.default = default
+ return 'PyObject *%(name)s = %(default)s;\n' % self.__dict__
+
+ def byref(self):
+ return "&%s" % self.name
+
+type_dispatcher = {
+ "IN char *": String,
+ "IN unsigned char *": String,
+ "unsigned char *": String,
+ "char *": String,
+ "ZString": ZString,
+
+ "OUT char *": StringOut,
+ "OUT unsigned char *": StringOut,
+ "unsigned int": Integer,
+ 'int': Integer,
+ 'OUT uint64_t *': IntegerOut,
+ 'OUT uint32_t *': Integer32Out,
+ 'char': Char,
+ 'void': Void,
+ 'void *': Void,
+
+ 'TDB_DATA *': TDB_DATA_P,
+ 'TDB_DATA': TDB_DATA,
+ 'uint64_t': Integer,
+ 'uint32_t': Integer32,
+ 'time_t': Integer32,
+ 'uint16_t': Integer,
+ 'uint8_t': Integer,
+ 'int64_t': Integer,
+ 'ssize_t': Integer,
+ 'size_t': Integer,
+ 'unsigned long int': Integer,
+ 'struct timeval': Timeval,
+ 'char **': StringArray,
+
+ 'PyObject *': PyObject,
+ }
+
+method_attributes = ['BORROWED', 'DESTRUCTOR','IGNORE']
+
+def dispatch(name, type):
+ if not type: return Void()
+
+ m = re.match("struct ([a-zA-Z0-9]+)_t *", type)
+ if m:
+ type = m.group(1)
+
+ type_components = type.split()
+ attributes = set()
+
+ if type_components[0] in method_attributes:
+ attributes.add(type_components.pop(0))
+
+ type = " ".join(type_components)
+ result = type_dispatcher[type](name, type)
+
+ result.attributes = attributes
+
+ return result
+
+class ResultException:
+ value = 0
+ exception = "PyExc_IOError"
+
+ def __init__(self, check, exception, message):
+ self.check = check
+ self.exception = exception
+ self.message = message
+
+ def write(self, out):
+ out.write("\n//Handle exceptions\n")
+ out.write("if(%s) {\n PyErr_Format(PyExc_%s, %s);\n goto error; \n}\n\n" % (
+ self.check, self.exception, self.message))
+
+class Method:
+ default_re = re.compile("DEFAULT\(([A-Z_a-z0-9]+)\) =(.+)")
+ exception_re = re.compile("RAISES\(([^,]+),\s*([^\)]+)\) =(.+)")
+ typedefed_re = re.compile(r"struct (.+)_t \*")
+
+ def __init__(self, class_name, base_class_name, method_name, args, return_type,
+ myclass = None):
+ self.name = method_name
+ ## myclass needs to be a class generator
+ if not isinstance(myclass, ClassGenerator): raise RuntimeError("myclass must be a class generator")
+
+ self.myclass = myclass
+ self.docstring = ''
+ self.defaults = {}
+ self.exception = None
+ self.error_set = False
+ self.class_name = class_name
+ self.base_class_name = base_class_name
+ self.args = []
+ self.definition_class_name = class_name
+ for type,name in args:
+ self.add_arg(type, name)
+
+ try:
+ self.return_type = dispatch('func_return', return_type)
+ self.return_type.attributes.add("OUT")
+ self.return_type.original_type = return_type
+ except KeyError:
+ ## Is it a wrapped type?
+ if return_type:
+ log("Unable to handle return type %s.%s %s" % (self.class_name, self.name, return_type))
+ #pdb.set_trace()
+ self.return_type = Void()
+
+ def clone(self, new_class_name):
+ self.find_optional_vars()
+
+ result = self.__class__(new_class_name, self.base_class_name, self.name,
+ [], 'void *',
+ myclass = self.myclass)
+ result.args = self.args
+ result.return_type = self.return_type
+ result.definition_class_name = self.definition_class_name
+ result.defaults = self.defaults
+ result.exception = self.exception
+
+ return result
+
+ def find_optional_vars(self):
+ for line in self.docstring.splitlines():
+ m =self.default_re.search(line)
+ if m:
+ name = m.group(1)
+ value = m.group(2)
+ log("Setting default value for %s of %s" % (m.group(1),
+ m.group(2)))
+ self.defaults[name] = value
+
+ m =self.exception_re.search(line)
+ if m:
+ self.exception = ResultException(m.group(1), m.group(2), m.group(3))
+
+ def write_local_vars(self, out):
+ self.find_optional_vars()
+
+ ## We do it in two passes - first mandatory then optional
+ kwlist = """static char *kwlist[] = {"""
+ ## Mandatory
+ for type in self.args:
+ python_name = type.python_name()
+ if python_name and python_name not in self.defaults:
+ kwlist += '"%s",' % python_name
+
+ for type in self.args:
+ python_name = type.python_name()
+ if python_name and python_name in self.defaults:
+ kwlist += '"%s",' % python_name
+
+ kwlist += ' NULL};\n'
+
+ for type in self.args:
+ python_name = type.python_name()
+ try:
+ out.write(type.definition(default = self.defaults[python_name]))
+ except KeyError:
+ out.write(type.definition())
+
+ ## Make up the format string for the parse args in two pases
+ parse_line = ''
+ for type in self.args:
+ python_name = type.python_name()
+ if type.buildstr and python_name not in self.defaults:
+ parse_line += type.buildstr
+
+ parse_line += '|'
+ for type in self.args:
+ python_name = type.python_name()
+ if type.buildstr and python_name in self.defaults:
+ parse_line += type.buildstr
+
+ if parse_line != '|':
+ ## Now parse the args from python objects
+ out.write(kwlist)
+ out.write("\nif(!PyArg_ParseTupleAndKeywords(args, kwds, \"%s\", kwlist, " % parse_line)
+ tmp = []
+ for type in self.args:
+ ref = type.byref()
+ if ref:
+ tmp.append(ref)
+
+ out.write(",".join(tmp))
+ self.error_set = True
+ out.write("))\n goto error;\n\n")
+
+ def error_condition(self):
+ result = ""
+ if "DESTRUCTOR" in self.return_type.attributes:
+ result += "talloc_free(self->ctx); self->base = NULL;\n"
+
+ return result +"return NULL;\n";
+
+ def write_definition(self, out):
+ args = dict(method = self.name, class_name = self.class_name)
+ out.write("\n/********************************************************\nAutogenerated wrapper for function:\n")
+ out.write(self.comment())
+ out.write("********************************************************/\n")
+
+ self._prototype(out)
+ out.write("""{
+ PyObject *returned_result, *py_result;
+""" % args)
+
+ out.write(self.return_type.definition())
+
+ self.write_local_vars( out);
+
+ out.write("""// Make sure that we have something valid to wrap
+if(!self->base) return PyErr_Format(PyExc_RuntimeError, "%(class_name)s object no longer valid");
+""" % args)
+
+ ## Precall preparations
+ out.write("// Precall preparations\n")
+ out.write(self.return_type.pre_call(self))
+ for type in self.args:
+ out.write(type.pre_call(self))
+
+ out.write("""// Check the function is implemented
+ { void *method = ((%(def_class_name)s)self->base)->%(method)s;
+ if(!method || (void *)unimplemented == (void *)method) {
+ PyErr_Format(PyExc_RuntimeError, "%(class_name)s.%(method)s is not implemented");
+ goto error;
+ }
+ }
+""" % dict(def_class_name = self.definition_class_name, method=self.name,
+ class_name = self.class_name))
+
+ out.write("\n// Make the call\n ClearError();")
+ call = "((%s)self->base)->%s(((%s)self->base)" % (self.definition_class_name, self.name, self.definition_class_name)
+ tmp = ''
+ for type in self.args:
+ tmp += ", " + type.call_arg()
+
+ call += "%s)" % tmp
+
+ ## Now call the wrapped function
+ out.write(self.return_type.assign(call, self))
+ if self.exception:
+ self.exception.write(out)
+
+ self.error_set = True
+
+ out.write("\n// Postcall preparations\n")
+ ## Postcall preparations
+ out.write(self.return_type.post_call(self))
+ for type in self.args:
+ out.write(type.post_call(self))
+
+ ## Now assemble the results
+ results = [self.return_type.to_python_object()]
+ for type in self.args:
+ if type.sense == 'OUT_DONE':
+ results.append(type.to_python_object(results = results))
+
+ ## If all the results are returned by reference we dont need
+ ## to prepend the void return value at all.
+ if isinstance(self.return_type, Void) and len(results)>1:
+ results.pop(0)
+
+ out.write("\n// prepare results\n")
+ ## Make a tuple of results and pass them back
+ if len(results)>1:
+ out.write("returned_result = PyList_New(0);\n")
+ for result in results:
+ out.write(result)
+ out.write("PyList_Append(returned_result, py_result); Py_DECREF(py_result);\n");
+ out.write("return returned_result;\n")
+ else:
+ out.write(results[0])
+ ## This useless code removes compiler warnings
+ out.write("returned_result = py_result;\nreturn returned_result;\n");
+
+ ## Write the error part of the function
+ if self.error_set:
+ out.write("\n// error conditions:\n")
+ out.write("error:\n " + self.error_condition());
+
+ out.write("\n}\n\n")
+
+ def add_arg(self, type, name):
+ try:
+ t = type_dispatcher[type](name, type)
+ except KeyError:
+ ## Sometimes types must be typedefed in advance
+ try:
+ m = self.typedefed_re.match(type)
+ type = m.group(1)
+ log( "Trying %s for %s" % (type, m.group(0)))
+ t = type_dispatcher[type](name, type)
+ except (KeyError, AttributeError):
+ log( "Unable to handle type %s.%s %s" % (self.class_name, self.name, type))
+ return
+
+ ## Here we collapse char * + int type interfaces into a
+ ## coherent string like interface.
+ try:
+ previous = self.args[-1]
+ if t.interface == 'integer' and \
+ previous.interface == 'string':
+
+ ## We make a distinction between IN variables and OUT
+ ## variables
+ if previous.sense == 'OUT':
+ cls = Char_and_Length_OUT
+ else:
+ cls = Char_and_Length
+
+
+ cls = cls(
+ previous.name,
+ previous.type,
+ name, type)
+
+ self.args[-1] = cls
+
+ return
+ except IndexError:
+ pass
+
+ self.args.append(t)
+
+ def comment(self):
+ result = self.return_type.original_type+" "+self.class_name+"."+self.name+"("
+ args = []
+ for type in self.args:
+ args.append( type.comment())
+
+ result += ",".join(args) + ");\n"
+
+ return result
+
+ def prototype(self, out):
+ self._prototype(out)
+ out.write(";\n")
+
+ def _prototype(self, out):
+ out.write("""static PyObject *py%(class_name)s_%(method)s(py%(class_name)s *self, PyObject *args, PyObject *kwds) """ % dict(method = self.name, class_name = self.class_name))
+
+ def __str__(self):
+ result = "def %s %s(%s):" % (
+ self.return_type,
+ self.name, ' , '.join([a.__str__() for a in self.args]))
+ return result
+
+ def PyMethodDef(self, out):
+ docstring = self.comment() + "\n\n" + self.docstring
+ out.write(' {"%s",(PyCFunction)py%s_%s, METH_VARARGS|METH_KEYWORDS, "%s"},\n' % (
+ self.name,
+ self.class_name,
+ self.name, escape_for_string(docstring)))
+
+class IteratorMethod(Method):
+ """ A Method which implements an iterator """
+ def __init__(self, *args, **kw):
+ Method.__init__(self, *args, **kw)
+
+ ## Tell the return type that a NULL python return is ok
+ self.return_type.attributes.add("NULL_OK")
+
+ def _prototype(self, out):
+ out.write("""static PyObject *py%(class_name)s_%(method)s(py%(class_name)s *self)""" % dict(method = self.name, class_name = self.class_name))
+
+ def __str__(self):
+ result = "Iterator returning %s." % (
+ self.return_type)
+ return result
+
+ def PyMethodDef(self, out):
+ ## This method should not go in the method table as its linked
+ ## in directly
+ pass
+
+class SelfIteratorMethod(IteratorMethod):
+ def write_definition(self, out):
+ args = dict(method = self.name, class_name = self.class_name)
+ out.write("\n/********************************************************\nAutogenerated wrapper for function:\n")
+ out.write(self.comment())
+ out.write("********************************************************/\n")
+
+ self._prototype(out)
+ out.write("""{
+ ((%(class_name)s)self->base)->%(method)s((%(class_name)s)self->base);
+ return PyObject_SelfIter((PyObject *)self);
+}
+""" % args)
+
+class ConstructorMethod(Method):
+ ## Python constructors are a bit different than regular methods
+ def _prototype(self, out):
+ out.write("""
+static int py%(class_name)s_init(py%(class_name)s *self, PyObject *args, PyObject *kwds)
+""" % dict(method = self.name, class_name = self.class_name))
+
+ def write_destructor(self, out):
+ ## We make sure that we unlink exactly the reference we need
+ ## (the object will persist if it has some other
+ ## references). Note that if we just used talloc_free here it
+ ## will remove some random reference which may not actually be
+ ## the reference we own (which is NULL).
+ free = """
+ if(self->base) {
+ //printf("Unlinking %s@%p\\n", NAMEOF(self->base), self->base);
+ talloc_free(self->ctx);
+ self->base=NULL;
+ }
+"""
+ out.write("""static void
+%(class_name)s_dealloc(py%(class_name)s *self) {
+%(free)s
+ PyObject_Del(self);
+}\n
+""" % dict(class_name = self.class_name, free=free))
+
+ def error_condition(self):
+ return "return -1;";
+
+ def write_definition(self, out):
+ self._prototype(out)
+ out.write("""{\n""")
+ self.write_local_vars(out)
+
+ ## Precall preparations
+ for type in self.args:
+ out.write(type.pre_call(self))
+
+ ## Now call the wrapped function
+ out.write("\nself->ctx = talloc_strdup(NULL, \"%s\");" % self.class_name)
+ out.write("\n ClearError();\nPy_BEGIN_ALLOW_THREADS\nself->base = CONSTRUCT(%s, %s, %s, self->ctx" % (
+ self.class_name,
+ self.definition_class_name,
+ self.name))
+ tmp = ''
+ for type in self.args:
+ tmp += ", " + type.call_arg()
+
+ self.error_set = True
+ out.write("""%s);\nPy_END_ALLOW_THREADS\n
+ if(!CheckError(EZero)) {
+ char *buffer;
+ PyObject *exception = resolve_exception(&buffer);
+
+ PyErr_Format(exception,
+ "%%s", buffer);
+ ClearError();
+ goto error;
+ } else if(!self->base) {
+ PyErr_Format(PyExc_IOError, "Unable to construct class %s");
+ goto error;
+ }
+""" % (tmp, self.class_name))
+
+ out.write(" return 0;\n");
+
+ ## Write the error part of the function
+ if self.error_set:
+ out.write("error:\n " + self.error_condition());
+
+ out.write("\n}\n\n")
+
+class GetattrMethod(Method):
+ def __init__(self, class_name, base_class_name, myclass):
+ self.base_class_name = base_class_name
+ self._attributes = []
+ self.error_set = True
+ self.return_type = Void()
+ self.myclass = myclass
+ self.rename_class_name(class_name)
+
+ def add_attribute(self, attr):
+ if attr.name:
+ self._attributes.append([self.class_name, attr])
+
+ def rename_class_name(self, new_name):
+ """ This allows us to rename the class_name at a later stage.
+ Required for late initialization of Structs whose name is not
+ know until much later on.
+ """
+ self.class_name = new_name
+ self.name = "py%s_getattr" % new_name
+ for x in self._attributes:
+ x[0] = new_name
+
+ def get_attributes(self):
+ for class_name, attr in self._attributes:
+ try:
+ if not self.myclass.module.classes[attr.type].active:
+ continue
+ except KeyError: pass
+
+ yield class_name, attr
+
+ def __str__(self):
+ result = ""
+ for class_name, attr in self.get_attributes():
+ result += " %s\n" % attr.__str__()
+
+ return result
+
+
+ def clone(self, class_name):
+ result = self.__class__(class_name, self.base_class_name, self.myclass)
+ result._attributes = self._attributes[:]
+
+ return result
+
+ def prototype(self, out):
+ if self.name:
+ out.write("""
+static PyObject *%(name)s(py%(class_name)s *self, PyObject *name);
+""" % self.__dict__)
+
+ def built_ins(self, out):
+ """ check for some built in attributes we need to support """
+ out.write(""" if(!strcmp(name, "__members__")) {
+ PyObject *result = PyList_New(0);
+ PyObject *tmp;
+ PyMethodDef *i;
+
+ if(!result) goto error;
+""")
+ ## Add attributes
+ for class_name, attr in self.get_attributes():
+ out.write(""" tmp = PyString_FromString("%(name)s");
+ PyList_Append(result, tmp); Py_DECREF(tmp);
+""" % dict(name = attr.name))
+
+ ## Add methods
+ out.write("""
+
+ for(i=%s_methods; i->ml_name; i++) {
+ tmp = PyString_FromString(i->ml_name);
+ PyList_Append(result, tmp); Py_DECREF(tmp);
+ }""" % self.class_name)
+
+ out.write("""
+ return result;
+ }\n""")
+
+ def write_definition(self, out):
+ if not self.name: return
+ out.write("""
+static PyObject *py%(class_name)s_getattr(py%(class_name)s *self, PyObject *pyname) {
+ char *name;
+ // Try to hand it off to the python native handler first
+ PyObject *result = PyObject_GenericGetAttr((PyObject*)self, pyname);
+
+ if(result) return result;
+
+ PyErr_Clear();
+ // No - nothing interesting was found by python
+ name = PyString_AsString(pyname);
+
+ if(!self->base) return PyErr_Format(PyExc_RuntimeError, "Wrapped object (%(class_name)s.%(name)s) no longer valid");
+ if(!name) return NULL;
+""" % self.__dict__)
+
+ self.built_ins(out)
+
+ for class_name, attr in self.get_attributes():
+ ## what we want to assign
+ if self.base_class_name:
+ call = "(((%s)self->base)->%s)" % (class_name, attr.name)
+ else:
+ call = "(self->base->%s)" % (attr.name)
+
+ out.write("""
+if(!strcmp(name, "%(name)s")) {
+ PyObject *py_result;
+ %(python_def)s
+
+ %(python_assign)s
+ %(python_obj)s
+ return py_result;
+}""" % dict(name = attr.name, python_obj = attr.to_python_object(),
+ python_assign = attr.assign(call, self),
+ python_def = attr.definition(sense='out')))
+
+ out.write("""
+
+ return PyObject_GenericGetAttr((PyObject *)self, pyname);
+""" % self.__dict__)
+
+ ## Write the error part of the function
+ if self.error_set:
+ out.write("error:\n" + self.error_condition());
+
+ out.write("}\n\n")
+
+class ProxiedGetattr(GetattrMethod):
+ def built_ins(self,out):
+ out.write(""" if(!strcmp(name, "__members__")) {
+ PyObject *result;
+ PyObject *tmp;
+ PyMethodDef *i;
+
+ PyErr_Clear();
+ // Get the list of members from our proxied object
+ result = PyObject_GetAttrString(self->base->proxied, name);
+ if(!result) goto error;
+""")
+ ## Add attributes
+ for class_name, attr in self.get_attributes():
+ out.write(""" tmp = PyString_FromString("%(name)s");
+ PyList_Append(result, tmp); Py_DECREF(tmp);
+""" % dict(name = attr.name))
+
+ ## Add methods
+ out.write("""
+
+ for(i=%s_methods; i->ml_name; i++) {
+ tmp = PyString_FromString(i->ml_name);
+ PyList_Append(result, tmp); Py_DECREF(tmp);
+ } """ % self.class_name)
+
+ out.write("""
+ return result;
+ }\n""")
+
+ out.write(""" /** Just try to get the attribute from our proxied object */ {
+ PyObject *result = PyObject_GetAttrString(self->base->proxied, name);
+ if(result) return result;
+} """)
+
+class ProxiedMethod(Method):
+ def __init__(self, method, myclass):
+ self.name = method.name
+ self.method = method
+ self.myclass = myclass
+ self.class_name = method.class_name
+ self.base_class_name = method.base_class_name
+ self.args = method.args
+ self.definition_class_name = method.definition_class_name
+ self.return_type = method.return_type
+ self.docstring = "Proxy for %s" % self.name
+ self.defaults = {}
+ self.exception = None
+ self.error_set = False
+
+ def get_name(self):
+ return "py%(class_name)s_%(name)s" % dict(class_name =self.myclass.class_name,
+ name = self.name)
+
+ def _prototype(self, out):
+ out.write("""
+static %(return_type)s %(name)s(%(definition_class_name)s self""" % dict(
+ return_type = self.return_type.original_type,
+ class_name = self.myclass.class_name,
+ method = self.name,
+ name = self.get_name(),
+ definition_class_name = self.definition_class_name))
+
+ for arg in self.args:
+ out.write(", %s" % (arg.comment()))
+
+ out.write(")")
+
+ def prototype(self, out):
+ self._prototype(out)
+ out.write(";\n")
+
+ def write_definition(self, out):
+ self._prototype(out)
+ self._write_definition(out)
+
+ def _write_definition(self, out):
+ ## We need to grab the GIL before we do anything
+ out.write("""{
+ //Grab the GIL so we can do python stuff
+ PyGILState_STATE gstate;
+ gstate = PyGILState_Ensure();
+ """)
+
+ out.write("{\nPyObject *py_result=NULL;\n")
+ out.write('PyObject *method_name = PyString_FromString("%s");\n' % self.name)
+ out.write(self.return_type.returned_python_definition())
+
+ for arg in self.args:
+ out.write("PyObject *py_%s=NULL;\n" % arg.name)
+
+ out.write("\n//Obtain python objects for all the args:\n")
+ for arg in self.args:
+ out.write(arg.to_python_object(result = "py_%s" % arg.name,
+ sense='proxied', BORROWED=True))
+
+ out.write('if(!((%s)self)->proxied) {\n RaiseError(ERuntimeError, "No proxied object in %s"); goto error;\n}\n' % (self.myclass.class_name, self.myclass.class_name))
+
+ out.write("\n//Now call the method\n")
+ out.write("""PyErr_Clear();
+py_result = PyObject_CallMethodObjArgs(((%s)self)->proxied,method_name,""" % self.myclass.class_name)
+ for arg in self.args:
+ out.write("py_%s," % arg.name)
+
+ ## Sentinal
+ self.error_set = True
+ out.write("""NULL);
+
+/** Check for python errors */
+if(PyErr_Occurred()) {
+ PyObject *exception_t, *exception, *tb;
+ PyObject *str;
+ char *str_c;
+ char *error_str;
+ enum _error_type *error_type = aff4_get_current_error(&error_str);
+
+ // Fetch the exception state and convert it to a string:
+ PyErr_Fetch(&exception_t, &exception, &tb);
+
+ str = PyObject_Repr(exception);
+ str_c = PyString_AsString(str);
+
+ if(str_c) {
+ strncpy(error_str, str_c, BUFF_SIZE-1);
+ error_str[BUFF_SIZE-1]=0;
+ *error_type = ERuntimeError;
+ }
+ Py_DECREF(str);
+ goto error;
+}
+
+""");
+
+ for arg in self.args:
+ out.write(arg.python_proxy_post_call())
+
+ ## Now convert the python value back to a value
+ out.write(self.return_type.from_python_object('py_result',self.return_type.name, self, context = "self"))
+
+ out.write("if(py_result) { Py_DECREF(py_result);}\nPy_DECREF(method_name);\n\n");
+ out.write("PyGILState_Release(gstate);\n")
+
+ ## Decref all our python objects:
+ for arg in self.args:
+ out.write("if(py_%s) { Py_DECREF(py_%s);}\n" %( arg.name, arg.name))
+
+ out.write(self.return_type.return_value('func_return'))
+ if self.error_set:
+ out.write("\nerror:\n")
+ out.write("if(py_result) { Py_DECREF(py_result);}\nPy_DECREF(method_name);\n\n");
+ ## Decref all our python objects:
+ for arg in self.args:
+ out.write("if(py_%s) { Py_DECREF(py_%s);}\n" % (arg.name, arg.name))
+
+ out.write("PyGILState_Release(gstate);\n %s;\n" % self.error_condition())
+
+ out.write(" }\n}\n")
+
+ def error_condition(self):
+ return self.return_type.error_value
+
+class StructConstructor(ConstructorMethod):
+ """ A constructor for struct wrappers - basically just allocate
+ memory for the struct.
+ """
+ def write_definition(self, out):
+ out.write("""static int py%(class_name)s_init(py%(class_name)s *self, PyObject *args, PyObject *kwds) {\n""" % dict(method = self.name, class_name = self.class_name))
+ out.write("\nself->ctx = talloc_strdup(NULL, \"%s\");" % self.class_name)
+ out.write("\nself->base = talloc(self->ctx, %s);\n" % self.class_name)
+ out.write(" return 0;\n}\n\n")
+
+ def write_destructor(self, out):
+ out.write("""static void
+%(class_name)s_dealloc(py%(class_name)s *self) {
+ talloc_free(self->ctx);
+}\n
+""" % dict(class_name = self.class_name))
+
+class ProxyConstructor(ConstructorMethod):
+ def write_destructor(self, out):
+ out.write("""static void
+%(class_name)s_dealloc(py%(class_name)s *self) {
+ if(self->base) {
+ // Release the proxied object
+ //Py_DECREF(self->base->proxied);
+ talloc_free(self->ctx);
+ self->base = NULL;
+ }
+}\n
+
+static int %(class_name)s_destructor(void *this) {
+ py%(class_name)s *self = (py%(class_name)s *)this;
+ Py_DECREF(self->base->proxied);
+ return 0;
+}
+""" % dict(class_name = self.class_name))
+
+ def initialise_attributes(self, out):
+ attributes = self.myclass.module.classes[self.base_class_name].attributes.get_attributes()
+ for definition_class_name, attribute in attributes:
+ out.write("""
+{
+ // Converting from %(attribute_name)s
+ PyErr_Clear();
+ PyObject *py_result = PyObject_GetAttrString(self->base->proxied, "%(name)s");
+
+ if(py_result) {
+ %(type)s tmp;
+ %(from_python_object)s;
+ ((%(definition_class_name)s)self->base)->%(name)s = tmp;
+ Py_DECREF(py_result);
+ }
+ PyErr_Clear();
+}""" % dict(definition = attribute.definition(), name=attribute.name,
+ attribute_name = attribute.__class__.__name__,
+ type = attribute.type,
+ definition_class_name = definition_class_name,
+ from_python_object = attribute.from_python_object(
+ 'py_result',"tmp", method=self,
+ context = 'self->base')))
+
+
+ def write_constructor_proxy(self, out):
+ ## Get the base_class constructor
+ self.base_cons_method = ProxiedMethod(self.myclass.module.classes[self.base_class_name].constructor, self.myclass)
+
+ self.base_cons_method._prototype(out)
+ out.write("{\nPyObject *py_result;\n")
+ out.write('PyObject *method_name;')
+ out.write("%(class_name)s this = (%(class_name)s)self;\n" % self.__dict__)
+ out.write("PyGILState_STATE gstate;\ngstate = PyGILState_Ensure();\n")
+ out.write('method_name = PyString_FromString("__class__");\n')
+ for arg in self.base_cons_method.args:
+ out.write("PyObject *py_%s;\n" % arg.name)
+
+ out.write("\n//Obtain python objects for all the args:\n")
+ for arg in self.base_cons_method.args:
+ out.write(arg.to_python_object(result = "py_%s" % arg.name,
+ sense = 'proxied',
+ BORROWED=True))
+
+ out.write('if(!((%s)self)->proxied) {\n RaiseError(ERuntimeError, "No proxied object in %s"); goto error;\n}\n' % (self.myclass.class_name, self.myclass.class_name))
+
+ out.write("""
+// Enlarge the object size to accomodate the extended class
+self = talloc_realloc_size(self, self, sizeof(struct %(base_class_name)s_t));
+""" % self.__dict__)
+ out.write("\n//Now call the method\n")
+ out.write("PyErr_Clear();\npy_result = PyObject_CallMethodObjArgs(((%s)self)->proxied,method_name," % self.myclass.class_name)
+
+ call = ''
+ for arg in self.base_cons_method.args:
+ call += "py_%s," % arg.name
+
+ ## Sentinal
+ self.error_set = True
+ call += """NULL"""
+
+ out.write(call + ");\n");
+ out.write("""
+if(!py_result && PyCallable_Check(this->proxied)) {
+ PyErr_Clear();
+ py_result = PyObject_CallFunctionObjArgs(((%(name)s)self)->proxied, %(call)s);
+}
+
+/** Check for python errors */
+if(PyErr_Occurred()) {
+ PyObject *exception_t, *exception, *tb;
+ PyObject *str;
+ char *str_c;
+ char *error_str;
+ enum _error_type *error_type = aff4_get_current_error(&error_str);
+
+ // Fetch the exception state and convert it to a string:
+ PyErr_Fetch(&exception_t, &exception, &tb);
+
+ str = PyObject_Repr(exception);
+ str_c = PyString_AsString(str);
+
+ if(str_c) {
+ strncpy(error_str, str_c, BUFF_SIZE-1);
+ error_str[BUFF_SIZE-1]=0;
+ *error_type = ERuntimeError;
+ }
+ Py_DECREF(str);
+ goto error;
+}
+
+// Take over the proxied object now
+this->proxied = py_result;
+""" % dict(name=self.myclass.class_name, call = call));
+
+ ## Now we try to populate the C struct slots with proxies of
+ ## the python objects
+ for class_name, attr in self.myclass.module.classes[\
+ self.base_class_name].attributes.get_attributes():
+ out.write("""
+// Converting %(name)s from proxy:
+{
+ PyObject *py_result = PyObject_GetAttrString(this->proxied, "%(name)s");
+ if(py_result) {
+""" % dict(name = attr.name))
+ out.write("{ %s " % attr.definition())
+ out.write(attr.from_python_object("py_result",
+ "((%s)self)->%s" % (class_name, attr.name),
+ self))
+ out.write("}\nPy_DECREF(py_result);\n}\n}\n")
+
+ out.write("PyGILState_Release(gstate);\n")
+ out.write("\n\nreturn self;\n")
+ if self.error_set:
+ out.write("error:\n PyGILState_Release(gstate);\ntalloc_free(self); return NULL;\n")
+
+ out.write("}\n\n")
+
+ def write_definition(self, out):
+ self.write_constructor_proxy(out)
+ out.write("""static int py%(class_name)s_init(py%(class_name)s *self, PyObject *args, PyObject *kwds) {
+ PyGILState_STATE gstate = PyGILState_Ensure();
+""" % dict(method = self.name, class_name = self.class_name))
+
+ self.write_local_vars(out)
+
+ ## Precall preparations
+ for type in self.args:
+ out.write(type.pre_call(self))
+
+ ## Make up the call
+ self.call = "talloc_memdup(NULL, &__%(class_name)s, sizeof(__%(class_name)s));\n" % self.__dict__
+
+ ## Now call the wrapped function
+ out.write("""
+ self->base = %(call)s
+
+// Take over a copy of the proxied object
+ self->base->proxied = proxied;
+
+ /* We take a reference to the proxied object, and the proxied base
+ class takes a reference. This way we (the python object) and the
+ proxied C class can be freed independantly and only when both are
+ freed the proxied object is freed. */
+
+ //Py_INCREF(proxied);
+ Py_INCREF(proxied);
+ talloc_set_destructor((void*)self->base, %(class_name)s_destructor);
+""" % self.__dict__)
+
+ ## Install the handler for the constructor. FIXME - implement
+ ## shortcut bypassing here so if the proxied class is itself a
+ ## python binding to a C class and it does not override
+ ## anything, we call directly into the original C class method
+ ## instead of converting to python, and back.
+ out.write("((%(definition_class_name)s)self->base)->%(name)s = %(func)s;\n" % dict(
+ definition_class_name = self.base_cons_method.definition_class_name,
+ name = self.base_cons_method.name,
+ func = self.base_cons_method.get_name()))
+
+ ## Now iterate over all our methods and install handlers:
+ for method in self.myclass.methods:
+ out.write("""
+ if(1 || PyDict_GetItemString(proxied->ob_type->tp_dict, "%(name)s")) {
+ ((%(definition_class_name)s)self->base)->%(name)s = py%(class_name)s_%(name)s;
+ }
+""" % dict(definition_class_name = method.definition_class_name,
+ name = method.name,
+ class_name = self.myclass.class_name))
+
+ ## Now fill in all attributes from the proxied object. Since
+ ## the C struct attribute access is just memory access its
+ ## difficult to trap it and refill attributes dynamically from
+ ## the python object. Therefore for now we just read all
+ ## attributes initially and populate the C struct with them.
+ self.initialise_attributes(out)
+
+ out.write("\n PyGILState_Release(gstate);\n return 0;\n");
+
+ ## Write the error part of the function
+ if self.error_set:
+ out.write("error:\n PyGILState_Release(gstate);\n " + self.error_condition());
+
+ out.write("\n}\n\n")
+
+class EmptyConstructor(ConstructorMethod):
+ def write_definition(self, out):
+ out.write("""static int py%(class_name)s_init(py%(class_name)s *self, PyObject *args, PyObject *kwds) {\n""" % dict(method = self.name, class_name = self.class_name))
+ out.write("""return 0;}\n\n""")
+
+class ClassGenerator:
+ docstring = ''
+ def __init__(self, class_name, base_class_name, module):
+ self.class_name = class_name
+ self.methods = []
+ self.module = module
+ self.constructor = EmptyConstructor(class_name, base_class_name,
+ "Con", [], '', myclass = self)
+
+ self.base_class_name = base_class_name
+ self.attributes = GetattrMethod(self.class_name, self.base_class_name, self)
+ self.modifier = set()
+ self.active = True
+ self.iterator = None
+
+ def prepare(self):
+ """ This method is called just before we need to write the
+ output and allows us to do any last minute fixups.
+ """
+ pass
+
+ def __str__(self):
+ result = "#%s\n" % self.docstring
+
+ result += "Class %s(%s):\n" % (self.class_name, self.base_class_name)
+ result += " Constructor:%s\n" % self.constructor
+ result += " Attributes:\n%s\n" % self.attributes
+ result += " Methods:\n"
+ for a in self.methods:
+ result += " %s\n" % a.__str__()
+
+ return result
+
+ def is_active(self):
+ """ Returns true if this class is active and should be generated """
+ if not self.active or self.modifier and \
+ ('PRIVATE' in self.modifier or 'ABSTRACT' in self.modifier):
+ log("%s is not active %s" % (self.class_name, self.modifier))
+ return False
+
+ return True
+
+ def clone(self, new_class_name):
+ """ Creates a clone of this class - usefull when implementing
+ class extensions
+ """
+ result = ClassGenerator(new_class_name, self.class_name, self.module)
+ result.constructor = self.constructor.clone(new_class_name)
+ result.methods = [ x.clone(new_class_name) for x in self.methods ]
+ result.attributes = self.attributes.clone(new_class_name)
+
+ return result
+
+ def add_attribute(self, attr_name, attr_type, modifier):
+ try:
+ if not self.module.classes[attr_type].is_active(): return
+ except KeyError: pass
+
+ try:
+ ## All attribute references are always borrowed - that
+ ## means we dont want to free them after accessing them
+ type_class = dispatch(attr_name, "BORROWED "+attr_type)
+ except KeyError:
+ log("Unknown attribute type %s for %s.%s" % (attr_type,
+ self.class_name,
+ attr_name))
+ return
+
+ type_class.attributes.add(modifier)
+ self.attributes.add_attribute(type_class)
+
+ def add_constructor(self, method_name, args, return_type, docstring):
+ if method_name.startswith("Con"):
+ self.constructor = ConstructorMethod(self.class_name, self.base_class_name,
+ method_name, args, return_type,
+ myclass = self)
+ self.constructor.docstring = docstring
+
+ def struct(self,out):
+ out.write("""\ntypedef struct {
+ PyObject_HEAD
+ %(class_name)s base;
+ void *ctx;
+} py%(class_name)s;\n
+""" % dict(class_name=self.class_name))
+
+ def code(self, out):
+ if not self.constructor:
+ raise RuntimeError("No constructor found for class %s" % self.class_name)
+
+ self.constructor.write_destructor(out)
+ self.constructor.write_definition(out)
+ if self.attributes:
+ self.attributes.write_definition(out)
+
+ for m in self.methods:
+ m.write_definition(out)
+
+ def initialise(self):
+ return "python_wrappers[TOTAL_CLASSES].class_ref = (Object)&__%s;\n" \
+ "python_wrappers[TOTAL_CLASSES++].python_type = &%s_Type;\n" % (
+ self.class_name, self.class_name)
+
+ def PyMethodDef(self, out):
+ out.write("static PyMethodDef %s_methods[] = {\n" % self.class_name)
+ for method in self.methods:
+ method.PyMethodDef(out)
+
+ out.write(" {NULL} /* Sentinel */\n};\n")
+
+ def prototypes(self, out):
+ """ Write prototype suitable for .h file """
+ out.write("""staticforward PyTypeObject %s_Type;\n""" % self.class_name)
+ self.constructor.prototype(out)
+
+ if self.attributes:
+ self.attributes.prototype(out)
+ for method in self.methods:
+ method.prototype(out)
+
+ def numeric_protocol_int(self):
+ pass
+
+ def numeric_protocol_nonzero(self):
+ return """
+static int
+%(class_name)s_nonzero(py%(class_name)s *v)
+{
+ return v->base != 0;
+}
+""" % self.__dict__
+
+ def numeric_protocol(self, out):
+ args = {'class':self.class_name}
+ for type, func in [ ('nonzero', self.numeric_protocol_nonzero),
+ ('int', self.numeric_protocol_int) ]:
+ definition = func()
+ if definition:
+ out.write(definition)
+ args[type] = "%s_%s" % (self.class_name,type)
+ else:
+ args[type] = '0'
+
+ out.write("""
+static PyNumberMethods %(class)s_as_number = {
+ (binaryfunc) 0, /*nb_add*/
+ (binaryfunc) 0, /*nb_subtract*/
+ (binaryfunc) 0, /*nb_multiply*/
+ 0, /*nb_divide*/
+ 0, /*nb_remainder*/
+ 0, /*nb_divmod*/
+ 0, /*nb_power*/
+ (unaryfunc) 0, /*nb_negative*/
+ (unaryfunc) 0, /*tp_positive*/
+ (unaryfunc) 0, /*tp_absolute*/
+ (inquiry) %(nonzero)s, /*tp_nonzero*/
+ (unaryfunc) 0, /*nb_invert*/
+ 0, /*nb_lshift*/
+ (binaryfunc) 0, /*nb_rshift*/
+ 0, /*nb_and*/
+ 0, /*nb_xor*/
+ 0, /*nb_or*/
+ 0, /*nb_coerce*/
+ (unaryfunc) %(int)s, /*nb_int*/
+ 0, /*nb_long*/
+ 0, /*nb_float*/
+ 0, /*nb_oct*/
+ 0, /*nb_hex*/
+ 0, /* nb_inplace_add */
+ 0, /* nb_inplace_subtract */
+ 0, /* nb_inplace_multiply */
+ 0, /* nb_inplace_divide */
+ 0, /* nb_inplace_remainder */
+ 0, /* nb_inplace_power */
+ 0, /* nb_inplace_lshift */
+ 0, /* nb_inplace_rshift */
+ 0, /* nb_inplace_and */
+ 0, /* nb_inplace_xor */
+ 0, /* nb_inplace_or */
+ 0, /* nb_floor_divide */
+ 0, /* nb_true_divide */
+ 0, /* nb_inplace_floor_divide */
+ 0, /* nb_inplace_true_divide */
+ 0, /* nb_index */
+};
+""" % args)
+ return "&%(class)s_as_number" % args
+
+ def PyTypeObject(self, out):
+ args = {'class':self.class_name, 'module': self.module.name,
+ 'iterator': 0,
+ 'iternext': 0,
+ 'tp_str': 0,
+ 'getattr_func': 0,
+ 'docstring': "%s: %s" % (self.class_name,
+ escape_for_string(self.docstring))}
+
+ if self.attributes:
+ args['getattr_func'] = self.attributes.name
+
+ args['numeric_protocol'] = self.numeric_protocol(out)
+ if "ITERATOR" in self.modifier:
+ args['iterator'] = "PyObject_SelfIter"
+ args['iternext'] = "py%s_iternext" % self.class_name
+
+ if "SELF_ITER" in self.modifier:
+ args['iterator'] = 'py%s___iter__' % self.class_name
+
+ if "TP_STR" in self.modifier:
+ args['tp_str'] = 'py%s___str__' % self.class_name
+
+ out.write("""
+static PyTypeObject %(class)s_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "%(module)s.%(class)s", /* tp_name */
+ sizeof(py%(class)s), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)%(class)s_dealloc,/* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ %(numeric_protocol)s, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ (reprfunc)%(tp_str)s, /* tp_str */
+ (getattrofunc)%(getattr_func)s, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ "%(docstring)s", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)%(iterator)s, /* tp_iter */
+ (iternextfunc)%(iternext)s,/* tp_iternext */
+ %(class)s_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)py%(class)s_init, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+""" % args )
+
+class StructGenerator(ClassGenerator):
+ """ A wrapper generator for structs """
+ def __init__(self, class_name, module):
+ self.class_name = class_name
+ self.methods = []
+ self.module = module
+ self.base_class_name = None
+ self.active = False
+ self.modifier = set()
+ self.constructor = None
+ self.attributes = GetattrMethod(self.class_name, self.base_class_name, self)
+
+ def prepare(self):
+ ## This is needed for late stage initialization - sometimes
+ ## our class_name is not know until now.
+ if not self.constructor:
+ self.constructor = StructConstructor(self.class_name, self.base_class_name,
+ 'Con', [], "void", myclass = self)
+
+ self.attributes.rename_class_name(self.class_name)
+ for x in self.attributes._attributes:
+ x[1].attributes.add('FOREIGN')
+
+ def __str__(self):
+ result = "#%s\n" % self.docstring
+
+ result += "Struct %s:\n" % (self.class_name)
+ result += "%s\n" % self.attributes
+
+ return result
+
+ def struct(self, out):
+ out.write("""\ntypedef struct {
+ PyObject_HEAD
+ %(class_name)s *base;
+ void *ctx;
+} py%(class_name)s;\n
+""" % dict(class_name=self.class_name))
+
+ def initialise(self):
+ return ''
+
+class ProxyClassGenerator(ClassGenerator):
+ def __init__(self, class_name, base_class_name, module, *args, **kwargs):
+ ClassGenerator.__init__(self, class_name, base_class_name, module, *args, **kwargs)
+ self.constructor = ProxyConstructor(self.class_name,
+ self.base_class_name, '__init__',
+ [('PyObject *', 'proxied')],
+ 'void', myclass = self)
+ self.module = module
+ self.attributes = ProxiedGetattr(self.class_name, self.base_class_name, self)
+
+
+ def initialise(self):
+ return "INIT_CLASS(%(class_name)s);\n" % self.__dict__ + ClassGenerator.initialise(self)
+
+ def struct(self, out):
+ out.write("""
+// The proxied type is an extension of the wrapped type with a pointer
+// to the proxied PyObject.
+CLASS(%(class_name)s, %(base_class_name)s)
+ uint32_t magic;
+ PyObject *proxied;
+END_CLASS
+
+VIRTUAL(%(class_name)s, %(base_class_name)s) {
+} END_VIRTUAL
+
+typedef struct {
+ PyObject_HEAD
+ %(class_name)s base;
+ void *ctx;
+} py%(class_name)s;\n
+""" % self.__dict__)
+
+ def PyMethodDef(self, out):
+ out.write("static PyMethodDef %s_methods[] = {\n" % self.class_name)
+ ## For now no methods
+ out.write(" {NULL} /* Sentinel */\n};\n")
+
+class parser:
+ class_re = re.compile(r"^([A-Z]+)?\s*CLASS\(([A-Z_a-z0-9]+)\s*,\s*([A-Z_a-z0-9]+)\)")
+ method_re = re.compile(r"^\s*([0-9A-Z_a-z ]+\s+\*?)METHOD\(([A-Z_a-z0-9]+),\s*([A-Z_a-z0-9]+),?")
+ enum_start_re = re.compile(r'enum ([0-9A-Z_a-z]+) {')
+ enum_re = re.compile(r'([0-9A-Z_a-z]+) = [^,]+,')
+ enum_end_re = re.compile('}')
+ arg_re = re.compile(r"\s*([0-9A-Z a-z_]+\s+\*?)([0-9A-Za-z_]+),?")
+ constant_re = re.compile(r"#define\s+([A-Z_0-9]+)\s+[^\s]+")
+ struct_re = re.compile(r"([A-Z]+)?\s+(typedef )?struct\s+([A-Z_a-z0-9]+)\s+{")
+ proxy_class_re = re.compile(r"^([A-Z]+)?\s*PROXY_CLASS\(([A-Za-z0-9]+)\)")
+ end_class_re = re.compile("END_CLASS")
+ attribute_re = re.compile(r"^\s*([0-9A-Z_a-z ]+\s+\*?)\s*([A-Z_a-z]+)\s*;")
+ comment_re = re.compile(r"^\s*//")
+ comment_start_re = re.compile(r"/\*+")
+ comment_end_re = re.compile(r"\*+/")
+ blank_line_re = re.compile("\s+")
+ typedef_re = re.compile("typedef ([A-Za-z_0-9]+) +([A-Za-z_0-9]+) *;")
+ bind_re = re.compile("BIND\(([a-zA-Z_0-9]+)\);")
+ current_class = None
+
+ def __init__(self, module, verbosity=0):
+ self.module = module
+ self.current_comment = ''
+ self.verbosity = verbosity
+ self.state = 'DEFAULT'
+
+ ## this is a list of objects which are to be bound
+ self.to_bind = []
+
+ global DEBUG
+
+ DEBUG = verbosity
+
+ io = StringIO.StringIO("""
+// Base object
+CLASS(Object, Obj)
+END_CLASS
+""")
+ self.parse_fd(io, '')
+
+ def add_class(self, class_name, base_class_name, class_type, handler, docstring, modifier):
+ try:
+ self.current_class = self.module.classes[base_class_name].clone(class_name)
+ except (KeyError, AttributeError):
+ log("Base class %s is not defined !!!!" % base_class_name)
+ self.current_class = class_type(class_name, base_class_name, self.module)
+
+ ## Now add the new class to the module object
+ self.current_class.docstring = docstring
+ self.current_class.modifier = modifier
+ self.module.add_class(self.current_class, handler)
+
+ def parse_filenames(self, filenames):
+ ## Be quiet for the first pass as many problems will be
+ ## resolved on the second pass anyway.
+ global DEBUG
+ DEBUG = 0
+ for f in filenames:
+ self._parse(f)
+
+ DEBUG = self.verbosity
+ log("Second pass: Consolidating definitions")
+ for f in filenames:
+ self._parse(f)
+
+ def _parse(self, filename):
+ fd = open(filename)
+ self.parse_fd(fd, filename)
+ fd.close()
+
+ if filename not in self.module.files:
+ self.module.headers += '#include "%s"\n' % filename
+ self.module.files.append(filename)
+
+ def parse_fd(self, fd, filename):
+ self.current_class = None
+ self.current_comment = ''
+
+ while 1:
+ line = fd.readline()
+ if not line: break
+
+ ## Handle binds
+ m= self.bind_re.search(line)
+ if m:
+ print "Will bind %s" % m.group(1)
+ self.to_bind.append(m.group(1))
+ continue
+
+ ## Handle enums
+ if self.state == 'DEFAULT':
+ m = self.enum_start_re.search(line)
+ if m:
+ self.state = 'enum'
+ type_dispatcher[m.group(1)] = type_dispatcher['int']
+ continue
+
+ elif self.state == 'enum':
+ m = self.enum_re.search(line)
+ if m:
+ self.module.add_constant(m.group(1), 'integer')
+
+ if '}' in line:
+ self.state = 'DEFAULT'
+ continue
+
+ ## Handle c++ style comments //
+ m = self.comment_re.match(line)
+ if m:
+ self.current_comment = line[m.end():]
+ while 1:
+ line = fd.readline()
+
+ m = self.comment_re.match(line)
+ if not m:
+ break
+
+ self.current_comment += line[m.end():]
+
+ ## Multiline C style comments
+ m = self.comment_start_re.search(line)
+ if m:
+ line = line[m.end():]
+ while 1:
+ m = self.comment_end_re.search(line)
+ if m:
+ self.current_comment += line[:m.start()]
+ line = fd.readline()
+ break
+ else:
+ self.current_comment += line
+
+ line = fd.readline()
+ if not line: break
+
+ ## Handle simple typdefs (as if typedef uint64_t my_fancy_type;)
+ m = self.typedef_re.search(line)
+ if m:
+ ## We basically add a new type as a copy of the old
+ ## type
+ old, new = m.group(1), m.group(2)
+ if old in type_dispatcher:
+ type_dispatcher[new] = type_dispatcher[old]
+
+ ## Handle constant #define
+ m = self.constant_re.search(line)
+ if m:
+ ## We need to determine if it is a string or integer
+ if re.search('"', line):
+ ## Its a string
+ self.module.add_constant(m.group(1), 'string')
+ else:
+ self.module.add_constant(m.group(1), 'integer')
+
+ ## Wrap structs
+ m = self.struct_re.search(line)
+ if m:
+ modifier = m.group(1)
+ class_name = m.group(3)
+ base_class_name = None
+ ## Structs may be refered to as a pointer or absolute
+ ## - its the same thing ultimatley.
+
+ ## We only wrap structures which are explicitely bound
+ if (modifier and 'BOUND' in modifier) or \
+ class_name in self.to_bind:
+ self.add_class(class_name, base_class_name, StructGenerator, StructWrapper,
+ self.current_comment, modifier)
+ type_dispatcher["%s *" % class_name] = PointerStructWrapper
+
+ continue
+
+ m = self.class_re.search(line)
+ if m:
+ ## We need to make a new class now... We basically
+ ## need to build on top of previously declared base
+ ## classes - so we try to find base classes, clone
+ ## them if possible:
+ modifier = m.group(1)
+ class_name = m.group(2)
+ base_class_name = m.group(3)
+ self.add_class(class_name, base_class_name, ClassGenerator, Wrapper,
+ self.current_comment, modifier)
+ type_dispatcher["%s *" % class_name] = PointerWrapper
+
+ continue
+
+ ## Make a proxy class for python callbacks
+ m = self.proxy_class_re.search(line)
+ if m:
+ modifier = m.group(1)
+ base_class_name = m.group(2)
+ class_name = "Proxied%s" % base_class_name
+ try:
+ proxied_class = self.module.classes[base_class_name]
+ except KeyError:
+ raise RuntimeError("Need to create a proxy for %s but it has not been defined (yet). You must place the PROXIED_CLASS() instruction after the class definition" % base_class_name)
+ self.current_class = ProxyClassGenerator(class_name,
+ base_class_name, self.module)
+ #self.current_class.constructor.args += proxied_class.constructor.args
+ self.current_class.docstring = self.current_comment
+
+ ## Create proxies for all these methods
+ for method in proxied_class.methods:
+ self.current_class.methods.append(ProxiedMethod(method, self.current_class))
+
+ self.module.add_class(self.current_class, Wrapper)
+
+ ## Make sure that further lines are not interpreted as part of this class.
+ self.current_class = None
+
+ m = self.method_re.search(line)
+ if self.current_class and m:
+ args = []
+ method_name = m.group(3)
+ return_type = m.group(1).strip()
+ ## Ignore private methods
+ if return_type.startswith("PRIVATE"): continue
+
+ ## Now parse the args
+ offset = m.end()
+ while 1:
+ m = self.arg_re.match(line[offset:])
+ if not m:
+ ## Allow multiline definitions if there is \\
+ ## at the end of the line
+ if line.strip().endswith("\\"):
+ line = fd.readline()
+ offset = 0
+ if line:
+ continue
+
+ break
+
+ offset += m.end()
+ args.append([m.group(1).strip(), m.group(2).strip()])
+
+ if return_type == self.current_class.class_name and \
+ method_name.startswith("Con"):
+ self.current_class.add_constructor(method_name, args, return_type,
+ self.current_comment)
+ else:
+ self.current_class.add_method(method_name, args, return_type,
+ self.current_comment)
+
+ m = self.attribute_re.search(line)
+ if self.current_class and m:
+ type = m.group(1)
+ name = m.group(2)
+ self.current_class.add_attribute(name, type)
+
+ m = self.end_class_re.search(line)
+ if m:
+ ## Just clear the current class context
+ self.current_class = None
+
+ ## If this is a shadow file we do not include it from the
+ ## main module. A shadow file is a way for us to redefine
+ ## other C constructs which will be imported from an
+ ## external header file so we can bind them slightly
+ ## differently.
+ if "this is a shadow file" in self.current_comment \
+ and filename not in self.module.files:
+ self.module.files.append(filename)
+
+ ## We only care about comment immediately above methods
+ ## etc as we take them to be documentation. If we get here
+ ## the comment is not above anything we care about - so we
+ ## clear it:
+ self.current_comment = ''
+
+ def write(self, out):
+ self.module.write(out)
+
+
+import lexer
+
+class EnumConstructor(ConstructorMethod):
+ def write_destructor(self, out):
+ out.write("""static void
+%(class_name)s_dealloc(py%(class_name)s *self) {
+ Py_DECREF(self->value);
+ PyObject_Del(self);
+}\n
+""" % dict(class_name = self.class_name))
+ def write_definition(self, out):
+ self.myclass.modifier.add("TP_STR")
+ self._prototype(out)
+ out.write("""{
+static char *kwlist[] = {"value", NULL};
+
+if(!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &self->value))
+ goto error;
+
+Py_INCREF(self->value);
+
+ return 0;
+error:
+ return -1;
+}
+
+static PyObject *py%(class_name)s___str__(py%(class_name)s *self) {
+ PyObject *result = PyDict_GetItem(%(class_name)s_rev_lookup, self->value);
+
+ if(result) {
+ Py_INCREF(result);
+ } else {
+ result = PyObject_Str(self->value);
+ }
+
+ return result;
+}
+
+""" % self.__dict__)
+
+class Enum(StructGenerator):
+ def __init__(self, name, module):
+ StructGenerator.__init__(self, name, module)
+ self.values = []
+ self.name = name
+ self.attributes = None
+ self.active = True
+
+ def prepare(self):
+ self.constructor = EnumConstructor(self.class_name, self.base_class_name,
+ 'Con', [], "void", myclass = self)
+ StructGenerator.prepare(self)
+
+ def __str__(self):
+ result = "Enum %s:\n" % (self.name)
+ for attr in self.values:
+ result += " %s\n" % attr.__str__()
+
+ return result
+
+ def struct(self,out):
+ out.write("""\ntypedef struct {
+ PyObject_HEAD
+ PyObject *value;
+} py%(class_name)s;\n
+
+
+static PyObject *%(class_name)s_Dict_lookup;
+static PyObject *%(class_name)s_rev_lookup;
+""" % dict(class_name=self.class_name))
+
+ def PyMethodDef(self, out):
+ out.write("static PyMethodDef %s_methods[] = {\n" % self.class_name)
+ out.write(" {NULL} /* Sentinel */\n};\n")
+
+ def numeric_protocol_nonzero(self):
+ pass
+
+ def numeric_protocol_int(self):
+ return """
+static PyObject *%(class_name)s_int(py%(class_name)s *self) {
+ Py_INCREF(self->value);
+ return self->value;
+}
+""" % self.__dict__
+
+ def initialise(self):
+ result = """
+%(class_name)s_Dict_lookup = PyDict_New();
+%(class_name)s_rev_lookup = PyDict_New();
+""" % self.__dict__
+
+ if self.values:
+ result += "{ PyObject *tmp, *tmp2;\n"
+ for attr in self.values:
+ result += ''' tmp = PyLong_FromLong(%(value)s);
+ tmp2 = PyString_FromString("%(value)s");
+ PyDict_SetItem(%(class_name)s_Dict_lookup, tmp2, tmp);
+ PyDict_SetItem(%(class_name)s_rev_lookup, tmp, tmp2);
+ Py_DECREF(tmp);
+ Py_DECREF(tmp2);
+
+''' % dict(value = attr, class_name=self.class_name)
+ result += "};\n"
+
+ return result
+
+class EnumType(Integer):
+ buildstr = 'i'
+
+ def __init__(self, name, type):
+ Integer.__init__(self, name, type)
+ self.type = type
+
+ def to_python_object(self, name=None, result='py_result', **kw):
+ name = name or self.name
+ return """PyErr_Clear();
+%s = PyObject_CallMethod(g_module, "%s", "K", (uint64_t)%s);
+""" % (result, self.type, name)
+
+ def pre_call(self, method):
+ method.error_set = True
+ return """
+// Check if the integer passed is actually a valid member of the enum
+// Enum value of 0 is always allowed
+if(%(name)s) { PyObject *py_%(name)s = PyLong_FromLong(%(name)s);
+ PyObject *tmp = PyDict_GetItem(%(type)s_rev_lookup, py_%(name)s);
+
+ Py_DECREF(py_%(name)s);
+ if(!tmp) {
+ PyErr_Format(PyExc_RuntimeError, "value %%lu is not valid for Enum %(type)s of arg '%(name)s'", (unsigned long)%(name)s);
+ goto error;
+ }
+ Py_DECREF(tmp);
+}
+""" % self.__dict__
+
+class HeaderParser(lexer.SelfFeederMixIn):
+ tokens = [
+ [ 'INITIAL', r'#define\s+', 'PUSH_STATE', 'DEFINE' ],
+ [ 'DEFINE', r'([A-Za-z_0-9]+)\s+[^\n]+', 'DEFINE,POP_STATE', None ],
+ [ 'DEFINE', r'\n', 'POP_STATE', None],
+
+ ## Recognize ansi c comments
+ [ '.', r'/\*(.)', 'PUSH_STATE', 'COMMENT' ],
+ [ 'COMMENT', r'(.+?)\*/\s+', 'COMMENT_END,POP_STATE', None],
+ [ 'COMMENT', r'(.+)', 'COMMENT', None],
+
+ ## And c++ comments
+ [ '.', r'//([^\n]+)', 'COMMENT', None],
+
+ ## An empty line clears the current comment
+ [ '.', r'\r?\n\r?\n', 'CLEAR_COMMENT', None],
+
+ ## Ignore whitespace
+ [ '.', r'\s+', 'SPACE', None ],
+ [ '.', r'\\\n', 'SPACE', None ],
+
+ ## Recognize CLASS() definitions
+ [ 'INITIAL', r"^([A-Z]+)?\s*CLASS\(([A-Z_a-z0-9]+)\s*,\s*([A-Z_a-z0-9]+)\)",
+ 'PUSH_STATE,CLASS_START', 'CLASS'],
+
+ [ 'CLASS', r"^\s*(FOREIGN|ABSTRACT|PRIVATE)?([0-9A-Z_a-z ]+( |\*))METHOD\(([A-Z_a-z0-9]+),\s*([A-Z_a-z0-9]+),?",
+ "PUSH_STATE,METHOD_START", "METHOD"],
+ [ 'METHOD', r"\s*([0-9A-Z a-z_]+\s+\*?\*?)([0-9A-Za-z_]+),?", "METHOD_ARG", None ],
+ [ 'METHOD', r'\);', 'POP_STATE,METHOD_END', None],
+
+ [ 'CLASS', r"^\s*(FOREIGN|ABSTRACT)?([0-9A-Z_a-z ]+\s+\*?)\s*([A-Z_a-z0-9]+)\s*;",
+ 'CLASS_ATTRIBUTE', None],
+ [ 'CLASS', "END_CLASS", 'END_CLASS,POP_STATE', None],
+
+ ## Recognize struct definitions (With name)
+ [ 'INITIAL', "([A-Z_a-z0-9 ]+)?struct\s+([A-Z_a-z0-9]+)\s+{",
+ 'PUSH_STATE,STRUCT_START', 'STRUCT'],
+
+ ## Without name (using typedef)
+ [ 'INITIAL', "typedef\s+struct\s+{",
+ 'PUSH_STATE,TYPEDEF_STRUCT_START', 'STRUCT'],
+
+ [ 'STRUCT', r"^\s*([0-9A-Z_a-z ]+\s+\*?)\s*([A-Z_a-z0-9]+)\s*;",
+ 'STRUCT_ATTRIBUTE', None],
+
+ [ 'STRUCT', r"^\s*([0-9A-Z_a-z ]+)\*\s+([A-Z_a-z0-9]+)\s*;",
+ 'STRUCT_ATTRIBUTE_PTR', None],
+
+ ## Struct ended with typedef
+ [ 'STRUCT', '}\s+([0-9A-Za-z_]+);', 'POP_STATE,TYPEDEF_STRUCT_END', None],
+ [ 'STRUCT', '}', 'POP_STATE,STRUCT_END', None],
+
+ ## Handle recursive struct or union definition (At the moment
+ ## we cant handle them at all)
+ [ '(RECURSIVE_)?STRUCT', '(struct|union)\s+([_A-Za-z0-9]+)?\s*{', 'PUSH_STATE', 'RECURSIVE_STRUCT'],
+ [ 'RECURSIVE_STRUCT', '}\s+[0-9A-Za-z]+', 'POP_STATE', None],
+
+ ## Process enums (2 forms - named and typedefed)
+ [ 'INITIAL', r'enum\s+([0-9A-Za-z_]+)\s+{', 'PUSH_STATE,ENUM_START', 'ENUM' ],
+ ## Unnamed
+ [ 'INITIAL', r'typedef\s+enum\s+{', 'PUSH_STATE,TYPEDEF_ENUM_START', 'ENUM' ],
+ [ 'ENUM', r'([0-9A-Za-z_]+)\s+=[^\n]+', 'ENUM_VALUE', None],
+
+ ## Typedefed ending
+ [ 'ENUM', r'}\s+([0-9A-Za-z_]+);', 'POP_STATE,TYPEDEFED_ENUM_END', None],
+ [ 'ENUM', r'}', 'POP_STATE,ENUM_END', None],
+
+ [ 'INITIAL', r'BIND_STRUCT\(([0-9A-Za-z_ \*]+)\)', 'BIND_STRUCT', None],
+
+ ## A simple typedef of one type for another type:
+ [ 'INITIAL', r"typedef ([A-Za-z_0-9]+) +([^;]+);", 'SIMPLE_TYPEDEF', None],
+
+ ## Handle proxied directives
+ [ 'INITIAL', r"PROXY_CLASS\(([A-Za-z0-9]+)\);", 'PROXY_CLASS', None],
+
+ ]
+
+ def __init__(self, name, verbose = 1):
+ self.module = Module(name)
+ lexer.SelfFeederMixIn.__init__(self, verbose = 0)
+
+ io = StringIO.StringIO("""
+// Base object
+CLASS(Object, Obj)
+END_CLASS
+""")
+ self.parse_fd(io)
+
+ current_comment = ''
+ def COMMENT(self, t, m):
+ self.current_comment += m.group(1) + "\n"
+
+ def COMMENT_END(self, t, m):
+ self.current_comment += m.group(1)
+
+ def CLEAR_COMMENT(self, t, m):
+ self.current_comment = ''
+
+ def DEFINE(self, t, m):
+ line = m.group(0)
+ line = line.split('/*')[0]
+ if '"' in line:
+ type = 'string'
+ else:
+ type = 'integer'
+
+ name = m.group(1).strip()
+ if len(name)>3 and name[0]!='_' and name==name.upper():
+ self.module.add_constant(name, type)
+
+ current_class = None
+ def CLASS_START(self, t, m):
+ class_name = m.group(2).strip()
+ base_class_name = m.group(3).strip()
+
+ try:
+ self.current_class = self.module.classes[base_class_name].clone(class_name)
+ except (KeyError, AttributeError):
+ log("Base class %s is not defined !!!!" % base_class_name)
+ self.current_class = ClassGenerator(class_name, base_class_name, self.module)
+
+ self.current_class.docstring = self.current_comment
+ self.current_class.modifier.add(m.group(1))
+ self.module.add_class(self.current_class, Wrapper)
+ type_dispatcher["%s *" % class_name] = PointerWrapper
+
+ current_method = None
+ def METHOD_START(self, t, m):
+ return_type = m.group(2).strip()
+ method_name = m.group(5).strip()
+ modifier = m.group(1) or ''
+
+ if 'PRIVATE' in modifier: return
+
+ ## Is it a regular method or a constructor?
+ self.current_method = Method
+ if return_type == self.current_class.class_name and \
+ method_name.startswith("Con"):
+ self.current_method = ConstructorMethod
+ elif method_name == 'iternext':
+ self.current_method = IteratorMethod
+ self.current_class.modifier.add("ITERATOR")
+ elif method_name == '__iter__':
+ self.current_method = SelfIteratorMethod
+ self.current_class.modifier.add("SELF_ITER")
+ elif method_name == '__str__':
+ self.current_class.modifier.add("TP_STR")
+
+ self.current_method = self.current_method(self.current_class.class_name,
+ self.current_class.base_class_name,
+ method_name, [], return_type,
+ myclass = self.current_class)
+ self.current_method.docstring = self.current_comment
+ self.current_method.modifier = modifier
+
+ def METHOD_ARG(self, t, m):
+ name = m.group(2).strip()
+ type = m.group(1).strip()
+ if self.current_method:
+ self.current_method.add_arg(type, name)
+
+ def METHOD_END(self, t, m):
+ if not self.current_method: return
+
+ if isinstance(self.current_method, ConstructorMethod):
+ self.current_class.constructor = self.current_method
+ else:
+ found = False
+ for i in range(len(self.current_class.methods)):
+ ## Try to replace existing methods with this new method
+ method = self.current_class.methods[i]
+ if method.name == self.current_method.name:
+ self.current_class.methods[i] = self.current_method
+ self.current_method = None
+ return
+
+ ## Method does not exist, just add to the end
+ self.current_class.methods.append(self.current_method)
+
+ self.current_method = None
+
+ def CLASS_ATTRIBUTE(self, t, m):
+ modifier = m.group(1) or ''
+ type = m.group(2).strip()
+ name = m.group(3).strip()
+ self.current_class.add_attribute(name, type, modifier)
+
+ def END_CLASS(self, t, m):
+ self.current_class = None
+
+ current_struct = None
+ def STRUCT_START(self, t, m):
+ self.current_struct = StructGenerator(m.group(2).strip(), self.module)
+ self.current_struct.docstring = self.current_comment
+ self.current_struct.modifier.add(m.group(1))
+
+ def TYPEDEF_STRUCT_START(self, t, m):
+ self.current_struct = StructGenerator(None, self.module)
+ self.current_struct.docstring = self.current_comment
+
+ def STRUCT_ATTRIBUTE(self, t, m):
+ name = m.group(2).strip()
+ type = m.group(1).strip()
+ self.current_struct.add_attribute(name, type, '')
+
+ def STRUCT_ATTRIBUTE_PTR(self, t, m):
+ type = "%s *" % m.group(1).strip()
+ name = m.group(2).strip()
+ self.current_struct.add_attribute(name, type, '')
+
+ def STRUCT_END(self, t, m):
+ self.module.add_class(self.current_struct, StructWrapper)
+ type_dispatcher["%s *" % self.current_struct.class_name] = PointerStructWrapper
+ self.current_struct = None
+
+ def TYPEDEF_STRUCT_END(self, t, m):
+ self.current_struct.class_name = m.group(1).strip()
+
+ self.STRUCT_END(t, m)
+
+ current_enum = None
+ def ENUM_START(self, t, m):
+ self.current_enum = Enum(m.group(1).strip(), self.module)
+
+ def TYPEDEF_ENUM_START(self, t, m):
+ self.current_enum = Enum(None, self.module)
+
+ def ENUM_VALUE(self, t, m):
+ self.current_enum.values.append(m.group(1).strip())
+
+ def ENUM_END(self, t, m):
+ self.module.classes[self.current_enum.name] = self.current_enum
+
+ ## For now we just treat enums as an integer, and also add
+ ## them to the constant table. In future it would be nice to
+ ## have them as a proper python object so we can override
+ ## __str__ and __int__.
+ for attr in self.current_enum.values:
+ self.module.add_constant(attr, 'integer')
+
+ #type_dispatcher[self.current_enum.name] = Integer
+ type_dispatcher[self.current_enum.name] = EnumType
+ self.current_enum = None
+
+ def TYPEDEFED_ENUM_END(self, t, m):
+ self.current_enum.name = self.current_enum.class_name = m.group(1)
+ self.ENUM_END(t, m)
+
+ def BIND_STRUCT(self, t, m):
+ self.module.classes[m.group(1)].active = True
+
+ def SIMPLE_TYPEDEF(self, t, m):
+ ## We basically add a new type as a copy of the old
+ ## type
+ old, new = m.group(1).strip(), m.group(2).strip()
+ if old in type_dispatcher:
+ type_dispatcher[new] = type_dispatcher[old]
+
+ def PROXY_CLASS(self, t, m):
+ base_class_name = m.group(1).strip()
+ class_name = "Proxied%s" % base_class_name
+ try:
+ proxied_class = self.module.classes[base_class_name]
+ except KeyError:
+ raise RuntimeError("Need to create a proxy for %s but it has not been defined (yet). You must place the PROXIED_CLASS() instruction after the class definition" % base_class_name)
+ current_class = ProxyClassGenerator(class_name,
+ base_class_name, self.module)
+ #self.current_class.constructor.args += proxied_class.constructor.args
+ current_class.docstring = self.current_comment
+
+ ## Create proxies for all these methods
+ for method in proxied_class.methods:
+ current_class.methods.append(ProxiedMethod(method, current_class))
+
+ self.module.add_class(current_class, Wrapper)
+
+ def parse_filenames(self, filenames):
+ for f in filenames:
+ self._parse(f)
+
+ ## Second pass
+ for f in filenames:
+ self._parse(f)
+
+ def _parse(self, filename):
+ fd = open(filename)
+ self.parse_fd(fd)
+ fd.close()
+
+ if filename not in self.module.files:
+ self.module.headers += '#include "%s"\n' % filename
+ self.module.files.append(filename)
+
+ def write(self, out):
+ self.module.write(out)
+
+
+if __name__ == '__main__':
+ p = HeaderParser('pyaff4', verbose = 1)
+ for arg in sys.argv[1:]:
+ p.parse_fd(open(arg))
+
+ log("second parse")
+ for arg in sys.argv[1:]:
+ p.parse_fd(open(arg))
+
+ pdb.set_trace()
+ p.write(sys.stdout)
+
+# p = parser(Module("pyaff4"))
+# for arg in sys.argv[1:]:
+# p.parse(arg)
+# log("second parse")
+# p.parse(arg)
+
+# p.write(sys.stdout)
+
diff --git a/python/experimental/config.py b/python/experimental/config.py
new file mode 100644
index 0000000..81d3e6b
--- /dev/null
+++ b/python/experimental/config.py
@@ -0,0 +1,4 @@
+MINGW_XCOMPILE = False
+V = True
+
+DEBUG = True
diff --git a/python/experimental/include/aff4_errors.h b/python/experimental/include/aff4_errors.h
new file mode 100644
index 0000000..a14dc1f
--- /dev/null
+++ b/python/experimental/include/aff4_errors.h
@@ -0,0 +1,67 @@
+/*
+** aff4_errors.h
+**
+** Made by mic
+** Login <mic at laptop>
+**
+** Started on Sat Mar 6 20:54:25 2010 mic
+** Last update Sat Mar 6 20:54:25 2010 mic
+*/
+
+#ifndef AFF4_ERRORS_H_
+# define AFF4_ERRORS_H_
+
+// Some helpful little things
+#define ERROR_BUFFER_SIZE 1024
+
+/** This is used for error reporting. This is similar to the way
+ python does it, i.e. we set the error flag and return NULL.
+*/
+enum _error_type {
+ EZero,EGeneric,EOverflow,EWarning,
+ EUnderflow,EIOError, ENoMemory, EInvalidParameter, ERuntimeError, EKeyError,
+
+ // Reserved for impossible conditions
+ EProgrammingError
+};
+
+void *raise_errors(enum _error_type t, char *string, ...);
+
+/** We only set the error state if its not already set */
+#define RaiseError(t, message, ...) \
+ if(*aff4_get_current_error(NULL) == EZero) { \
+ raise_errors(t, "%s: (%s:%d) " message, __FUNCTION__, __FILE__, __LINE__, ## __VA_ARGS__); \
+ };
+
+#define LogWarnings(format, ...) \
+ do { \
+ RaiseError(EWarning, format, ## __VA_ARGS__); \
+ PrintError(); \
+ } while(0);
+
+#define ClearError() \
+ do {*aff4_get_current_error(NULL) = EZero;} while(0);
+
+#define PrintError() \
+ do {char *error_str; if(*aff4_get_current_error(&error_str)) fprintf(stdout, "%s", error_str); fflush(stdout); ClearError(); }while(0);
+
+#define CheckError(error) \
+ (*aff4_get_current_error(NULL) == error)
+
+/** The current error state is returned by this function.
+
+ This is done in a thread safe manner.
+ */
+enum _error_type *aff4_get_current_error(char **error_str);
+
+
+// These macros are used when we need to do something which might
+// change the error state on the error path of a function.
+#define PUSH_ERROR_STATE { enum _error_type *tmp_error_p = aff4_get_current_error(NULL); enum _error_type tmp_error = *tmp_error_p; enum _error_type exception __attribute__((unused));
+
+#define POP_ERROR_STATE *tmp_error_p = tmp_error;};
+
+
+void error_init();
+
+#endif /* !AFF4_ERRORS_H_ */
diff --git a/python/experimental/include/class.h b/python/experimental/include/class.h
new file mode 100644
index 0000000..89a804c
--- /dev/null
+++ b/python/experimental/include/class.h
@@ -0,0 +1,447 @@
+/***************************************************
+ Classes and objects in C
+
+ This file makes it easy to implement classes and objects in C. To
+ define a class we need to perform three steps:
+
+ Define the class prototype. This is suitable to go in a .h file for
+ general use by other code.
+
+ Note all classes extend Object.
+
+ Example::
+
+CLASS(Foo, Object)
+ int x;
+ int y;
+
+ //This declares a method of a class Foo, called Con returning a
+ //Foo object. In other words it is a constructor.
+ Foo METHOD(Foo, Con, int x, int y);
+ int METHOD(Foo, add);
+
+END_CLASS
+
+Now we need to define some functions for the constructor and
+methods. Note that the constuctor is using ALLOCATE_CLASS to allocate
+space for the class structures. Callers may call with self==NULL to
+force allocation of a new class. Note that we do not call the
+constructor of our superclass implicitly here. (Calling the sperclass
+constructor is optional, but ALLOCATE_CLASS is not.).
+
+Foo Foo_Con(Foo self,int x,int y) {
+ self->x = x;
+ self->y = y;
+
+ return self;
+};
+
+int Foo_add(Foo this) {
+ return (this->x + this->y);
+};
+
+Now we need to define the Virtual function table - These are those
+functions and attributes which are defined in this class (over its
+superclass). Basically these are all those things in the class
+definition above, with real function names binding them. (Note that by
+convention we preceed the name of the method with the name of the
+class):
+
+VIRTUAL(Foo,Object)
+ VMETHOD(Con) = Foo_Con;
+ VMETHOD(add) = Foo_add;
+END_VIRTUAL
+
+We can use inheritance too:
+
+CLASS(Bar, Foo)
+ Bar METHOD(Bar, Con, char *something)
+END_CLASS
+
+Here Bar extends Foo and defines a new constructor with a different prototype:
+
+VIRTUAL(Bar,Foo)
+ VMETHOD(Con) = Bar_Con
+END_VIRTUAL
+
+If there is a function which expects a Foo, we will need to over ride
+the Foo constructor in the Bar, so the function will not see the
+difference between the Foo and Bar:
+
+CLASS(Bar,Foo)
+ int bar_attr;
+END_CLASS
+
+Foo Bar_Con(Foo self, int x, int y) {
+...
+}
+
+VIRTUAL(Bar, Foo)
+ VMETHOD(super.Con) = Bar_Con
+END_VIRTUAL
+
+Note that in this case we are over riding the Con method defined in
+Foo while creating derived Bar classes. The notation in the VIRTUAL
+table is to use super.Con, because Foo's Con method (the one we are
+over riding), can be located by using super.Con inside a Bar object.
+
+Imagine now that in Bar_Con we wish to use methods and attributes
+defined in Bar. Since Bar_Con over rides Bar's base class (Foo) it
+must have the prototype described above. Since self is of type Foo its
+impossible to use self->bar_attr (There is no bar_attr in Foo - its in
+Bar).
+
+In this case, we need to make a type cast to convice C that self is
+actually a Bar not a Foo:
+
+Foo Bar_Con(Foo self, int x, int y) {
+ Bar this = (Bar)self;
+
+ this->bar_attr=1
+};
+
+This allows us to access bars attributes.
+
+This is a general oddity with C style classes, which C++ and Java
+hide. In C we must always know which class defines which method and
+attribute and reference the right class's method. So for example if we
+want to call a Bar's add method:
+
+Bar a;
+
+a->super.add()
+
+because add is defined in Bar's super class (Foo). Constract this with
+C++ or Java which hide where methods are defined and simply make all
+methods appear like they were defined inside the derived class. This
+takes a while to get used to but the compiler will ensure that the
+references are correct - otherwise things will generally not compile
+properly.
+
+This difference can be used for good and bad. It is possible in C to
+call the base class's version of the method at any time (despite the
+fact it was over ridden).
+
+For example:
+
+CLASS(Derived, Foo)
+ int METHOD(Derived, add);
+END_CLASS
+
+VIRTUAL(Derived, Foo)
+ VMETHOD(add) = Derived_add
+END_VIRTUAL
+
+If d is a Derived object, we can call Foo's version like this:
+d->super.add()
+
+But Derived's version is accessed by:
+d->add()
+
+Sometimes a derived class may want to over ride the base class's
+methods as well, in this case the VIRTUAL section should over ride
+super.add as well.
+
+*/
+/******************************************************
+# Copyright 2004: Commonwealth of Australia.
+#
+# Developed by the Computer Network Vulnerability Team,
+# Information Security Group.
+# Department of Defence.
+#
+# Michael Cohen <scudette at users.sourceforge.net>
+#
+# ******************************************************
+# Version: FLAG $Version: 0.87-pre1 Date: Thu Jun 12 00:48:38 EST 2008$
+# ******************************************************
+#
+# * 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 2
+# * 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, write to the Free Software
+# * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# ******************************************************/
+#ifndef __CLASS_H__
+#define __CLASS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifdef min
+#undef min
+#endif
+#define min(X, Y) ((X) < (Y) ? (X) : (Y))
+
+#ifdef max
+#undef max
+#endif
+#define max(X, Y) ((X) > (Y) ? (X) : (Y))
+
+
+#include <talloc.h>
+
+#define CLASS(class,super_class) \
+ typedef struct class ## _t *class; \
+ int class ## _init(Object self); \
+ extern struct class ## _t __ ## class; \
+ struct class ## _t { struct super_class ## _t super; \
+ class __class__; \
+ super_class __super__;
+
+
+#define METHOD(cls, name, ... ) \
+ (* name)(cls self, ## __VA_ARGS__ )
+
+ // Class methods are attached to the class but are not called with
+ // an instance. This is similar to the python class method or java
+ // static methods.
+#define CLASS_METHOD(name, ... ) \
+ (*name)(__VA_ARGS__)
+
+/***************************************************
+ This is a convenience macro which may be used if x if really large
+
+***************************************************/
+#define CALL(x, method, ... ) \
+ (x)->method((x), ## __VA_ARGS__)
+
+#define END_CLASS };
+
+/***************************************************
+ This is used to set the classes up for use:
+
+ class_init = checks the class template (__class) to see if it has
+ been allocated. otherwise allocates it in the global context.
+
+ class_Alloc = Allocates new memory for an instance of the
+ class. This is a recursive function calling each super class in
+ turn and setting the currently over ridden defaults. So for eample
+ suppose this class (foo) derives from bar, we first fill the
+ template with bars methods, and attributes. Then we over write
+ those with foos methods and attributes.
+
+**********************************************************/
+#define VIRTUAL(class,superclass) \
+ struct class ## _t __ ## class; \
+ \
+ int class ## _init(Object this) { \
+ class self = (class)this; \
+ if(self->__super__) return 1; \
+ superclass ##_init(this); \
+ this->__class__ = (Object)&__ ## class; \
+ self->__class__ = (class)&__ ## class; \
+ this->__super__ = (Object)&__ ## superclass; \
+ self->__super__ = (superclass)&__ ## superclass; \
+ this->__size = sizeof(struct class ## _t); \
+ this->__name__ = #class;
+
+#define SET_DOCSTRING(string) \
+ ((Object)self)->__doc__ = string
+
+#define END_VIRTUAL return 1; }
+
+#define VMETHOD(method) \
+ (self)->method
+
+#define VMETHOD_BASE(base, method) \
+ (((base)self)->method)
+
+#define CLASS_ATTR(self, base, method) \
+ (((base)self)->method)
+
+#define VATTR(attribute) \
+ (self)->attribute
+
+#define NAMEOF(obj) \
+ ((Object)obj)->__name__
+
+#define SIZEOF(obj) \
+ ((Object)obj)->__size
+
+#define DOCSTRING(obj) \
+ ((Object)obj)->__doc__
+
+#define INIT_CLASS(class) \
+ class ## _init((Object)&__ ## class)
+
+/*************************************************************
+ This MACRO is used to construct a new Class using a constructor.
+
+ This is done to try and hide the bare (unbound) method names in
+ order to prevent name space pollution. (Bare methods may be
+ defined as static within the implementation file). This macro
+ ensures that class structures are initialised properly before
+ calling their constructors.
+
+ We require the following args:
+ class - the type of class to make
+ virt_class - The class where the method was defined
+ constructors - The constructor method to use
+ context - a talloc context to use.
+
+
+ Note that the class and virt_class do not have to be the same if
+ the method was not defined in the current class. For example
+ suppose Foo extends Bar, but method is defined in Bar but
+ inherited in Foo:
+
+ CONSTRUCT(Foo, Bar, super.method, context)
+
+ virt_class is Bar because thats where method was defined.
+*************************************************************/
+
+// The following only initialises the class if the __super__ element
+// is NULL. This is fast as it wont call the initaliser unnecessaily
+#define CONSTRUCT(class, virt_class, constructor, context, ... ) \
+ (class)( __## class.__super__ == NULL ? \
+ class ## _init((Object)&__ ## class) : 0, \
+ __## virt_class.__super__ == NULL ? \
+ virt_class ## _init((Object)&__ ## virt_class): 0, \
+ ((virt_class)(&__ ## class))->constructor( \
+ (virt_class)_talloc_memdup(context, &__ ## class, sizeof(struct class ## _t), __location__ "(" #class ")"), \
+ ## __VA_ARGS__) )
+
+/** This variant is useful when all we have is a class reference
+ (GETCLASS(Foo)) or &__Foo
+*/
+#define CONSTRUCT_FROM_REFERENCE(class, constructor, context, ... ) \
+ ( (class)->constructor( \
+ (void *)_talloc_memdup(context, ((Object)class), ((Object)class)->__size, __location__ "(" #class "." #constructor ")"), \
+ ## __VA_ARGS__) )
+
+/** Finds the size of the class in x */
+#define CLASS_SIZE(class) \
+ ((Object)class)->__size
+
+typedef struct Object_t *Object;
+
+struct Object_t {
+ //A reference to a class instance - this is useful to be able to
+ //tell which class an object really belongs to:
+ Object __class__;
+
+ //And its super class:
+ Object __super__;
+
+ char *__name__;
+
+ /** Objects may have a doc string associated with them. */
+ char *__doc__;
+
+ //How large the class is:
+ int __size;
+};
+
+#define SUPER(base, imp, method, ...) \
+ ((base)&__ ## imp)->method((base)self, ## __VA_ARGS__)
+
+#define GETCLASS(class) \
+ (Object)&__ ## class
+
+// Returns true if the obj belongs to the class
+#define ISINSTANCE(obj,class) \
+ (((Object)obj)->__class__ == GETCLASS(class))
+
+// This is a string comparison version of ISINSTANCE which works
+// across different shared objects.
+#define ISNAMEINSTANCE(obj, class) \
+ (obj && !strcmp(class, NAMEOF(obj)))
+
+// We need to ensure that class was properly initialised:
+#define ISSUBCLASS(obj,class) \
+ issubclass((Object)obj, (Object)&__ ## class)
+
+#define CLASSOF(obj) \
+ ((Object)obj)->__class__
+
+void Object_init(Object);
+
+extern struct Object_t __Object;
+
+int issubclass(Object obj, Object class);
+
+extern void unimplemented(Object self);
+
+#define UNIMPLEMENTED(class, method) \
+ ((class)self)->method = (void *)unimplemented;
+
+#define ZSTRING_NO_NULL(str) str , (strlen(str))
+#define ZSTRING(str) str , (strlen(str)+1)
+
+ // These dont do anything but are useful to indicate when a function
+ // parameter is used purely to return a value. They are now used to
+ // assist the python binding generator in generating the right sort
+ // of code
+#define OUT
+#define IN
+
+ // This modifier before a class means that the class is abstract and
+ // does not have an implementation - we do not generate bindings for
+ // that class then.
+#define ABSTRACT
+
+ // This modifier indicates that the following pointer is pointing to
+ // a borrowed reference - callers must not free the memory after use.
+#define BORROWED
+
+ // This tells the autobinder to generated bindings to this struct
+#define BOUND
+
+ // This tells the autobinder to ignore this class as it should be
+ // private to the implementation - external callers should not
+ // access this.
+#define PRIVATE
+
+ // This attribute of a method means that this method is a
+ // desctructor - the object is no longer valid after this method is
+ // run
+#define DESTRUCTOR
+
+ // including this after an argument definition will cause the
+ // autogenerator to assign default values to that parameter and make
+ // it optional
+#define DEFAULT(x)
+
+ // This explicitely denote that the type is a null terminated char
+ // ptr as opposed to a pointer to char and length.
+#define ZString char *
+
+ /* The following is a direction for the autogenerator to proxy the
+ given class. This is done in the following way:
+
+1) a new python type is created called Proxy_class_name() with a
+constructor which takes a surrogate object.
+
+2) The proxy class contains a member "base" of the type of the proxied
+C class.
+
+3) The returned python object may be passed to any C functions which
+expect the proxied class, and internal C calls will be converted to
+python method calls on the proxied object.
+ */
+#define PROXY_CLASS(name)
+
+ /* This signals the autogenerator to bind the named struct */
+#define BIND_STRUCT(name)
+
+ // This means that the memory owned by this pointer is managed
+ // externally (not using talloc). It is dangerous to use this
+ // keyword too much because we are unable to manage its memory
+ // appropriately and it can be free'd from under us.
+#define FOREIGN
+
+#endif
+#ifdef __cplusplus
+} /* closing brace for extern "C" */
+#endif
diff --git a/python/experimental/inspect.py b/python/experimental/inspect.py
new file mode 100644
index 0000000..b2d0684
--- /dev/null
+++ b/python/experimental/inspect.py
@@ -0,0 +1,278 @@
+#!/usr/bin/python
+""" Module to inspect the contents of an AFF4 volume """
+import sys, pdb, os
+import re, cmd, readline
+import optparse
+import pyaff4
+import shlex
+import fnmatch, time
+import subprocess
+import readline
+
+time.sleep(1)
+
+## Set more sane completer delimiters
+readline.set_completer_delims(' \t\n`!@#$^&*()=+[{]}\\|;:\'",<>?')
+
+## Configure colors
+colors = {}
+colors['cyan'] = '\033[96m'
+colors['purple'] = '\033[95m'
+colors['blue'] = '\033[94m'
+colors['green'] = '\033[92m'
+colors['yellow'] = '\033[93m'
+colors['red'] = '\033[91m'
+colors['end'] = '\033[0m'
+
+#If the output is not a terminal, remove the colors
+if not sys.stdout.isatty():
+ for key, value in colors.iteritems():
+ colors[key] = ''
+
+parser = optparse.OptionParser()
+(options, args) = parser.parse_args()
+
+## Load the specified args as volumes
+oracle = pyaff4.Resolver()
+VOLUMES = []
+STREAMS = {}
+
+for arg in args:
+ volume = pyaff4.RDFURN()
+ volume.set(arg)
+ if oracle.load(volume):
+ VOLUMES.append(volume)
+ urn = pyaff4.RDFURN()
+ iter = oracle.get_iter(volume, pyaff4.AFF4_CONTAINS)
+ while oracle.iter_next(iter, urn):
+ print urn.value
+ if not urn: break
+
+ ## We store is as a simplified path
+ path = "/%s%s" % (urn.parser.netloc, urn.parser.query)
+ STREAMS[path] = urn
+
+class Inspector(cmd.Cmd):
+ prompt = "/:>"
+ intro = "Inspecting the volumes %s. Type 'help' for help." % args
+ CWD = "/"
+
+ def cmdloop(self, *args):
+ while 1:
+ try:
+ if not cmd.Cmd.cmdloop(self, *args):
+ break
+ except KeyboardInterrupt,e:
+ print "Type %sexit%s to exit, %shelp%s for help" % (
+ colors['yellow'],
+ colors['end'],
+ colors['cyan'],
+ colors['end'])
+
+ def do_EOF(self, args):
+ """ Exit this program """
+ print "%sGoodbye%s" % (colors['yellow'],
+ colors['end'])
+ return True
+
+ def do_cd(self, line):
+ """ Change directory """
+ args = shlex.split(line)
+ if not args[0].startswith("/"):
+ args[0] = self.CWD + args[0]
+
+ try:
+ potential_cd = os.path.normpath(args[0])
+ if not potential_cd.endswith('/'):
+ potential_cd += "/"
+
+ for s in STREAMS:
+ if s.startswith(potential_cd):
+ self.CWD = potential_cd
+ self.prompt = "%s:>" % self.CWD
+ return
+
+ raise IOError("No such directory %s" % potential_cd)
+ except IndexError:
+ print "CWD: %s" % self.CWD
+
+ def complete_cd(self, *args):
+ return self.complete_stream(*args)
+
+ def do_help(self, line):
+ """ Provide help for commands """
+ args = shlex.split(line)
+ if not args:
+ ## List all commands
+ for k in dir(self):
+ if k.startswith("do_"):
+ method = getattr(self, k)
+ print "%s%s%s: %s" % (colors['red'],
+ k[3:],
+ colors['end'],
+ method.__doc__)
+ return
+
+ for arg in args:
+ try:
+ method = getattr(self, "do_%s" % arg)
+ except AttributeError:
+ print "%sError:%s Command %s%s%s not found" % (colors['red'],
+ colors['end'],
+ colors['yellow'],
+ arg,
+ colors['end'])
+
+ print "%s: %s" % (arg, method.__doc__)
+
+ def do_ls(self, line):
+ """ List streams matching a glob expression """
+ globs = shlex.split(line)
+ if not globs: globs=[self.CWD]
+
+ result = []
+
+ for s in STREAMS:
+ for g in globs:
+ if fnmatch.fnmatch(s, g + "*"):
+ if s.startswith(self.CWD):
+ s = s[len(self.CWD):]
+
+ path = s.split("/")[0]
+ if path == s:
+ decoration = colors['blue']
+ else:
+ decoration = colors['end']
+
+ if path not in result:
+ print "%s%s%s" % (decoration, path, colors['end'])
+ result.append(path)
+
+ def do_less(self, line):
+ """ Read the streams specified and pipe them through the pager """
+ globs = shlex.split(line)
+ for s in STREAMS:
+ for g in globs:
+ if not g.startswith("/"):
+ g = self.CWD + g
+
+ g = os.path.normpath(g)
+ if fnmatch.fnmatch(s, g):
+ ## Try to open the stream
+ try:
+ fd = oracle.open(STREAMS[s], 'r')
+ except IOError, e:
+ raise
+
+ pager = os.environ.get("PAGER","less")
+ pipe=os.popen(pager,"w")
+
+ while 1:
+ data = fd.read(1024 * 1024)
+ if not data: break
+
+ pipe.write(data)
+
+ pipe.close()
+
+ def do_cp(self, line):
+ """ Copy a stream from a source to a destination. """
+ globs = shlex.split(line)
+ src = globs[0]
+ dest = globs[1]
+ if(len(globs) > 2):
+ print "usage: cp src dest"
+ return
+
+ if not src.startswith("/"):
+ src = self.CWD + src
+
+ src = os.path.normpath(src)
+ src_urn = pyaff4.RDFURN()
+ src_urn.set("aff4:/" + src)
+ ## Try to open the stream
+ try:
+ fd = oracle.open(src_urn, 'r')
+ except IOError, e:
+ raise
+
+ dest_fd = open(dest, "w")
+ while 1:
+ data = fd.read(1024 * 1024)
+ if not data: break
+
+ dest_fd.write(data)
+
+ dest_fd.close()
+
+ def complete_cp(self, *args):
+ return self.complete_stream(*args)
+
+ def complete_less(self, *args):
+ return self.complete_stream(*args)
+
+ def _display_attribute(self, iter):
+ while 1:
+ obj = oracle.alloc_from_iter(iter)
+ if not obj: break
+
+ print " -> type (%s) " % (obj.dataType)
+ print " -> data (%s) " % (obj.serialise(iter.urn))
+
+
+ def do_resolve(self, line):
+ globs = shlex.split(line)
+ attribute = pyaff4.XSDString()
+ subject = pyaff4.RDFURN()
+ iter = pyaff4.RESOLVER_ITER()
+
+ subject.set(globs[0])
+ try:
+ attribute.set(globs[1])
+ print attribute.value
+ self._display_attribute(iter)
+
+ except IndexError:
+ ## Just display all the attributes
+ while oracle.attributes_iter(subject, attribute, iter):
+ print attribute.value
+ self._display_attribute(iter)
+
+ def complete_stream(self, text, line, begidx, endidx):
+ if not text.startswith("/"):
+ text = self.CWD + text
+
+ if not text:
+ completions = STREAMS.keys()
+
+ completions = [ text + f[len(text):].split("/")[0]
+ for f in STREAMS.keys()
+ if f.startswith(text)
+ ]
+
+ return completions
+
+ def do_pwd(self, line):
+ print "CWD: %s" % self.CWD
+
+ def do_exit(self, *args):
+ """ Exits the program """
+ return self.do_EOF(*args)
+
+ def emptyline(self):
+ return
+
+ def onecmd(self, line):
+ try:
+ return cmd.Cmd.onecmd(self, line)
+ except Exception,e:
+ print "%sError:%s %s%s%s %s" % (colors['red'],
+ colors['end'],
+ colors['yellow'],
+ e.__class__.__name__,
+ colors['end'],
+ e)
+
+ return None
+
+Inspector().cmdloop()
diff --git a/python/experimental/lexer.py b/python/experimental/lexer.py
new file mode 100644
index 0000000..3def511
--- /dev/null
+++ b/python/experimental/lexer.py
@@ -0,0 +1,195 @@
+#!/usr/bin/env python
+# ******************************************************
+# Michael Cohen <scudette at users.sourceforge.net>
+#
+# ******************************************************
+# Version: FLAG $Version: 0.87-pre1 Date: Thu Jun 12 00:48:38 EST 2008$
+# ******************************************************
+#
+# * 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 2
+# * 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, write to the Free Software
+# * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# ******************************************************
+""" A simple feed lexer.
+"""
+
+import re,sys
+
+class Lexer:
+ """ A generic feed lexer """
+ ## The following is a description of the states we have and the
+ ## way we move through them: format is an array of
+ ## [ state_re, re, token/action, next state ]
+ tokens = []
+ state = "INITIAL"
+ buffer = ''
+ error = 0
+ verbose = 0
+ state_stack = []
+ processed = 0
+ processed_buffer = ''
+ saved_state = None
+ flags = 0
+
+ def __init__(self, verbose=0, fd=None):
+ if not self.verbose:
+ self.verbose = verbose
+
+ if len(self.tokens[0])==4:
+ for row in self.tokens:
+ row.append(re.compile(row[0], re.DOTALL))
+ row.append(re.compile(row[1], re.DOTALL | re.M | re.S | self.flags ))
+
+ self.fd = fd
+
+ def save_state(self, t=None, m=None):
+ """ Returns a dict which represents the current state of the lexer.
+
+ When provided to restore_state, the lexer is guaranteed to be
+ in the same state as when the save_state was called.
+
+ Note that derived classes may need to extend this.
+ """
+ ## Cant save our state if we have errors. We need to guarantee
+ ## that we rewind to a good part of the file.
+ if self.error: return
+ try:
+ end = m.end()
+ except: end = 0
+
+ self.saved_state = dict(state_stack = self.state_stack[:],
+ processed = self.processed - end,
+ processed_buffer = self.processed_buffer,
+ readptr = self.fd.tell() - len(self.buffer) - end,
+ state = self.state,
+ objects = self.objects[:],
+ error = self.error,
+ )
+
+ if self.verbose>1:
+ print "Saving state %s" % self.processed
+
+ def restore_state(self):
+ state = self.saved_state
+ if not state: return
+
+ self.state_stack = state['state_stack']
+ self.processed = state['processed']
+ self.processed_buffer = state['processed_buffer']
+ self.buffer = ''
+ self.fd.seek(state['readptr'])
+ self.state = state['state']
+ self.objects = state['objects']
+ self.error = state['error']
+ if self.verbose>1:
+ print "Restoring state to offset %s" % self.processed
+
+ def next_token(self, end = True):
+ ## Now try to match any of the regexes in order:
+ current_state = self.state
+ for state_re, re_str, token, next, state, regex in self.tokens:
+ ## Does the rule apply for us now?
+ if state.match(current_state):
+ if self.verbose > 2:
+ print "%s: Trying to match %r with %r" % (self.state, self.buffer[:10], re_str)
+ m = regex.match(self.buffer)
+ if m:
+ if self.verbose > 3:
+ print "%s matched %s" % (re_str, m.group(0).encode("utf8"))
+ ## The match consumes the data off the buffer (the
+ ## handler can put it back if it likes)
+ self.processed_buffer += self.buffer[:m.end()]
+ self.buffer = self.buffer[m.end():]
+ self.processed += m.end()
+
+ ## Try to iterate over all the callbacks specified:
+ for t in token.split(','):
+ try:
+ if self.verbose > 0:
+ print "0x%X: Calling %s %r" % (self.processed, t, m.group(0))
+ cb = getattr(self, t, self.default_handler)
+ except AttributeError:
+ continue
+
+ ## Is there a callback to handle this action?
+ next_state = cb(t, m)
+ if next_state == "CONTINUE":
+ continue
+
+ elif next_state:
+ next = next_state
+ self.state = next
+
+
+ if next:
+ self.state = next
+
+ return token
+
+ ## Check that we are making progress - if we are too full, we
+ ## assume we are stuck:
+ if end and len(self.buffer)>0 or len(self.buffer)>1024:
+ self.processed_buffer += self.buffer[:1]
+ self.buffer = self.buffer[1:]
+ self.ERROR("Lexer Stuck, discarding 1 byte (%r) - state %s" % (self.buffer[:10], self.state))
+ return "ERROR"
+
+ ## No token were found
+ return None
+
+ def feed(self, data):
+ self.buffer += data
+
+ def empty(self):
+ return not len(self.buffer)
+
+ def default_handler(self, token, match):
+ if self.verbose > 2:
+ print "Default handler: %s with %r" % (token,match.group(0))
+
+ def ERROR(self, message = None, weight =1):
+ if self.verbose > 0 and message:
+ print "Error(%s): %s" % (weight,message)
+
+ self.error += weight
+
+ def PUSH_STATE(self, token = None, match = None):
+ if self.verbose > 1:
+ print "Storing state %s" % self.state
+ self.state_stack.append(self.state)
+
+ def POP_STATE(self, token = None, match = None):
+ try:
+ state = self.state_stack.pop()
+ if self.verbose > 1:
+ print "Returned state to %s" % state
+
+ return state
+ except IndexError:
+ print "Tried to pop the state but failed - possible recursion error"
+ return None
+
+ def close(self):
+ """ Just a conveniece function to force us to parse all the data """
+ while self.next_token(): pass
+
+class SelfFeederMixIn(Lexer):
+ """ This mixin is used to make a lexer which feeds itself one
+ sector at the time.
+
+ Note that self.fd must be the fd we read from.
+ """
+ def parse_fd(self, fd):
+ self.feed(fd.read())
+ while self.next_token(): pass
+
diff --git a/python/experimental/regfi/class.c b/python/experimental/regfi/class.c
new file mode 100644
index 0000000..bcd3b74
--- /dev/null
+++ b/python/experimental/regfi/class.c
@@ -0,0 +1,63 @@
+/******************************************************
+# Copyright 2004: Commonwealth of Australia.
+#
+# Developed by the Computer Network Vulnerability Team,
+# Information Security Group.
+# Department of Defence.
+#
+# Michael Cohen <scudette at users.sourceforge.net>
+#
+# ******************************************************
+# Version: FLAG $Version: 0.87-pre1 Date: Thu Jun 12 00:48:38 EST 2008$
+# ******************************************************
+#
+# * 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 2
+# * 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, write to the Free Software
+# * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# ******************************************************/
+#include "class.h"
+
+#define BUFF_SIZE 1024
+
+// Noone should instantiate Object directly. this should be already
+// allocated therefore:
+
+void Object_init(Object this) {
+ this->__class__ = &__Object;
+ this->__super__ = NULL;
+}
+
+struct Object_t __Object = {
+ .__class__ = &__Object,
+ .__super__ = &__Object,
+ .__size = sizeof(struct Object_t),
+ .__name__ = "Object"
+};
+
+int issubclass(Object obj, Object class) {
+ obj = obj->__class__;
+ while(1) {
+ if(obj == class->__class__)
+ return 1;
+
+ obj=obj->__super__;
+
+ if(obj == &__Object || obj==NULL)
+ return 0;
+ };
+}
+
+void unimplemented(Object self) {
+ printf("%s contains unimplemented functions.. is it an abstract class?\n", NAMEOF(self));
+ abort();
+}
diff --git a/python/experimental/regfi/error.c b/python/experimental/regfi/error.c
new file mode 100644
index 0000000..6c2a181
--- /dev/null
+++ b/python/experimental/regfi/error.c
@@ -0,0 +1,84 @@
+/*
+** error.c
+**
+** Made by (mic)
+** Login <mic at laptop>
+**
+** Started on Mon Mar 15 20:45:09 2010 mic
+** Last update Sun May 12 01:17:25 2002 Speed Blue
+*/
+
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <talloc.h>
+#include "aff4_errors.h"
+
+/** These slots carry the TLS error keys */
+static pthread_key_t error_str_slot;
+static pthread_key_t error_value_slot;
+
+void error_dest(void *slot) {
+ if(slot) talloc_free(slot);
+}
+
+void *raise_errors(enum _error_type t, char *reason, ...) {
+ char *error_buffer;
+ // This has to succeed:
+ enum _error_type *type = aff4_get_current_error(&error_buffer);
+
+ if(reason) {
+ va_list ap;
+ va_start(ap, reason);
+
+ vsnprintf(error_buffer, ERROR_BUFFER_SIZE-1, reason,ap);
+ error_buffer[ERROR_BUFFER_SIZE-1]=0;
+ va_end(ap);
+ };
+
+ //update the error type
+ *type = t;
+
+ return NULL;
+}
+
+static int error_subsystem_initialised=0;
+
+enum _error_type *aff4_get_current_error(char **error_buffer) {
+ enum _error_type *type;
+
+ if(!error_subsystem_initialised) error_init();
+
+ type = pthread_getspecific(error_value_slot);
+
+ // This is optional
+ if(error_buffer) {
+ *error_buffer = pthread_getspecific(error_str_slot);
+
+ // If TLS buffers are not set we need to create them
+ if(!*error_buffer) {
+ *error_buffer =talloc_size(NULL, ERROR_BUFFER_SIZE);
+ pthread_setspecific(error_str_slot, *error_buffer);
+ };
+ };
+
+ if(!type) {
+ type = talloc(NULL, enum _error_type);
+ pthread_setspecific(error_value_slot, type);
+ };
+
+ return type;
+}
+
+/** Initialise the error subsystem */
+void error_init() {
+ error_subsystem_initialised = 1;
+
+ // We create the error buffer slots
+ if(pthread_key_create(&error_str_slot, error_dest) ||
+ pthread_key_create(&error_value_slot, error_dest)) {
+ printf("Unable to set up TLS variables\n");
+ abort();
+ };
+}
diff --git a/python/experimental/regfi/pyregfi.h b/python/experimental/regfi/pyregfi.h
new file mode 100644
index 0000000..c7923b8
--- /dev/null
+++ b/python/experimental/regfi/pyregfi.h
@@ -0,0 +1,182 @@
+/*
+ * Top-level definitions for pyregfi to be processed by Michael Cohen's
+ * automated Python bindings generator.
+ *
+ * Copyright (C) 2010 Michael I. Cohen
+ * Copyright (C) 2010 Timothy D. Morgan
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: $
+ *
+ */
+
+#ifndef PYREGFI_H_
+# define PYREGFI_H_
+
+#include "class.h"
+#include "aff4_errors.h"
+#include "regfi.h"
+
+/** Forward declarations */
+struct RegistryFile_t;
+struct RegistryKey_t;
+struct SubkeyIterator_t;
+struct ValueIterator_t;
+struct TreeIterator_t;
+
+BIND_STRUCT(REGFI_NK)
+BIND_STRUCT(REGFI_VK)
+BIND_STRUCT(REGFI_DATA)
+
+/** This is the base class for data objects */
+CLASS(RawData, Object)
+ const REGFI_DATA *data;
+ const REGFI_VK *rec;
+
+ RawData METHOD(RawData, Con, REGFI_DATA *data, REGFI_VK *record);
+
+ /** Return the raw buffer as a string. By default we only return
+ this much data - specify a required length to return more.
+
+ DEFAULT(len) = 4096;
+ */
+ int METHOD(RawData, get_value, OUT char *buffer, int len);
+END_CLASS
+
+CLASS(DataString, RawData)
+ BORROWED char *METHOD(DataString, get_value);
+END_CLASS
+
+CLASS(DWORDData, RawData)
+ uint64_t METHOD(DWORDData, get_value);
+END_CLASS
+
+/** This is an iterator for traversing an entire registry hive */
+CLASS(TreeIterator, Object)
+ PRIVATE REGFI_ITERATOR *iter;
+ PRIVATE struct RegistryFile_t *file;
+ PRIVATE bool root_traversed;
+
+ struct TreeIterator_t *METHOD(TreeIterator, Con, struct RegistryFile_t *file,
+ char **path, REGFI_ENCODING encoding);
+
+ void METHOD(TreeIterator, __iter__);
+ struct RegistryKey_t *METHOD(TreeIterator, iternext);
+
+
+ int METHOD(TreeIterator, down);
+ int METHOD(TreeIterator, up);
+
+ struct RegistryKey_t *METHOD(TreeIterator, current);
+ int METHOD(TreeIterator, to_root);
+
+END_CLASS
+
+
+/** XXX */
+CLASS(RegistryKey, Object)
+ struct RegistryFile_t *file;
+ const REGFI_NK *key;
+
+ struct RegistryKey_t *METHOD(RegistryKey, Con,
+ struct RegistryFile_t *file, REGFI_NK *base_key);
+
+ struct SubkeyIterator_t *METHOD(RegistryKey, subkeys);
+ struct ValueIterator_t *METHOD(RegistryKey, values);
+
+END_CLASS
+
+
+/** This is an iterator for reading keys from the registry */
+CLASS(SubkeyIterator, Object)
+ struct RegistryFile_t *file;
+ PRIVATE const REGFI_SUBKEY_LIST *list;
+ PRIVATE uint32_t cur;
+
+ SubkeyIterator METHOD(SubkeyIterator, Con,
+ struct RegistryFile_t *file, REGFI_NK *key);
+
+ void METHOD(SubkeyIterator, __iter__);
+ RegistryKey METHOD(SubkeyIterator, iternext);
+END_CLASS
+
+
+
+/** This is an iterator for reading values from the registry */
+CLASS(ValueIterator, Object)
+ struct RegistryFile_t *file;
+ PRIVATE const REGFI_VALUE_LIST *list;
+ PRIVATE uint32_t cur;
+
+ ValueIterator METHOD(ValueIterator, Con,
+ struct RegistryFile_t *file, REGFI_NK *key);
+
+ void METHOD(ValueIterator, __iter__);
+ REGFI_VK *METHOD(ValueIterator, iternext);
+END_CLASS
+
+
+
+CLASS(RegistryFile, Object)
+ REGFI_FILE *reg;
+ int fd;
+
+ RegistryFile METHOD(RegistryFile, Con, char *filename);
+
+ /* Get an iterator for a specific path in the register if path is
+ specified.
+
+ XXX: can we switch to UTF-8 and have Python properly import that?
+
+ DEFAULT(path) == NULL;
+ DEFAULT(encoding) = REGFI_ENCODING_ASCII;
+ */
+ TreeIterator METHOD(RegistryFile, TreeIterator, char **path, REGFI_ENCODING encoding);
+
+ /** Set the verbosity level of messages generated by the library for the
+ * current thread.
+ *
+ * @param mask An integer representing the types of messages desired.
+ * Acceptable values are created through bitwise ORs of
+ * REGFI_LOG_* values. For instance, if only errors and
+ * informational messages were desired (but not warnings),
+ * then one would specify: REGFI_LOG_ERROR|REGFI_LOG_INFO
+ * By default the message mask is: REGFI_LOG_ERROR|REGFI_LOG_WARN.
+ *
+ * @return true on success and false on failure. Failure occurs if
+ * underlying pthread functions fail. errno is set in this case.
+ *
+ * Message masks are set in a thread-specific way. If one were to set a message
+ * mask in one thread and then spawn a new thread, then the new thread will have
+ * it's message mask reset to the default. This function may be called at any
+ * time and will take effect immediately for the current thread.
+ *
+ * @note When a non-zero message mask is set, messages will
+ * accumulate in memory without limit if they are not fetched using
+ * @ref regfi_get_log_str and subsequently freed by the caller. It is
+ * recommended that messsages be fetched after each regfi API call in
+ * order to provide the most context.
+ *
+ * @ingroup regfiBase
+ */
+ int METHOD(RegistryFile, set_log_mask, uint16_t mask);
+
+
+END_CLASS
+
+
+void pyregfi_init();
+
+#endif /* !PYREGFI_H_ */
diff --git a/python/experimental/regfi/regfi.c b/python/experimental/regfi/regfi.c
new file mode 100644
index 0000000..13a058c
--- /dev/null
+++ b/python/experimental/regfi/regfi.c
@@ -0,0 +1,443 @@
+/*
+ * pyregfi/libregfi glue code
+ *
+ * Copyright (C) 2010 Michael I. Cohen
+ * Copyright (C) 2010 Timothy D. Morgan
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: $
+ */
+
+#include "pyregfi.h"
+
+static int RegistryFile_dest(void *self)
+{
+ RegistryFile this = (RegistryFile)self;
+
+ regfi_free(this->reg);
+ close(this->fd);
+
+ return 0;
+}
+
+static RegistryFile RegistryFile_Con(RegistryFile self, char *filename)
+{
+ self->fd = open(filename, O_RDONLY);
+ if(self->fd < 0)
+ {
+ RaiseError(EIOError, "Unable to open %s", filename);
+ goto error;
+ }
+
+ self->reg = regfi_alloc(self->fd);
+
+ if(!self->reg)
+ {
+ RaiseError(ERuntimeError, "REGFI Error: %s", regfi_log_get_str());
+ /*char* e = regfi_log_get_str();*/
+ /*fprintf(stderr, "%p\n", e);*/
+ goto error;
+ }
+
+ talloc_set_destructor((void *)self, RegistryFile_dest);
+ return self;
+
+ error:
+ talloc_free(self);
+ return NULL;
+}
+
+static TreeIterator RegistryFile_TreeIterator(RegistryFile self, char **path, REGFI_ENCODING encoding)
+{
+ return CONSTRUCT(TreeIterator, TreeIterator, Con, NULL, self, path, encoding);
+}
+
+
+VIRTUAL(RegistryFile, Object) {
+ VMETHOD(Con) = RegistryFile_Con;
+ VMETHOD(TreeIterator) = RegistryFile_TreeIterator;
+} END_VIRTUAL
+
+
+static int RegistryKey_dest(void *self)
+{
+ RegistryKey this = (RegistryKey)self;
+
+ talloc_unlink(this, (void*)this->key);
+
+ return 0;
+}
+
+static RegistryKey RegistryKey_Con(RegistryKey self,
+ RegistryFile file, REGFI_NK* base_key)
+{
+ if(base_key == NULL)
+ goto error;
+
+ self->key = base_key;
+ self->file = file;
+ talloc_reference(self, self->key);
+ talloc_set_destructor((void *)self, RegistryKey_dest);
+
+ return self;
+
+ error:
+ talloc_free(self);
+ return NULL;
+}
+
+/* XXX: would be nice to change this into a property, rather than a function,
+ * but that would require a custom __getattr__. Can that be done? */
+static SubkeyIterator RegistryKey_subkeys(RegistryKey self)
+{
+ return CONSTRUCT(SubkeyIterator, SubkeyIterator, Con, NULL, self->file, self->key);
+}
+
+/* XXX: would be nice to change this into a property, rather than a function,
+ * but that would require a custom __getattr__. Can that be done? */
+static SubkeyIterator RegistryKey_values(RegistryKey self)
+{
+ return CONSTRUCT(ValueIterator, ValueIterator, Con, NULL, self->file, self->key);
+}
+
+
+VIRTUAL(RegistryKey, Object) {
+ VMETHOD(Con) = RegistryKey_Con;
+ VMETHOD(subkeys) = RegistryKey_subkeys;
+ VMETHOD(values) = RegistryKey_values;
+} END_VIRTUAL
+
+
+static int TreeIterator_dest(void *self)
+{
+ TreeIterator this = (TreeIterator)self;
+
+ regfi_iterator_free(this->iter);
+ return 0;
+}
+
+static TreeIterator TreeIterator_Con(TreeIterator self,
+ RegistryFile file,
+ char **path,
+ REGFI_ENCODING encoding)
+{
+ self->iter = regfi_iterator_new(file->reg, encoding);
+ self->file = file;
+
+ if(!self->iter)
+ {
+ RaiseError(ERuntimeError, "Error: %s", regfi_log_get_str());
+ goto error;
+ }
+
+ talloc_set_destructor((void*)self, TreeIterator_dest);
+
+ /* Traverse to the path */
+ if(path[0])
+ {
+ if(!regfi_iterator_walk_path(self->iter, (const char **)path))
+ {
+ RaiseError(ERuntimeError, "Unable to walk down key path");
+ goto error;
+ }
+ }
+
+ self->root_traversed = false;
+
+ return self;
+ error:
+ return NULL;
+}
+
+static void TreeIterator__iter__(TreeIterator self)
+{
+ return;
+}
+
+
+static RegistryKey TreeIterator_next(TreeIterator self)
+{
+ if(!self->root_traversed)
+ self->root_traversed = true;
+ else if(!regfi_iterator_down(self->iter))
+ {
+ do
+ {
+ if(!regfi_iterator_up(self->iter))
+ return NULL;
+ } while(!regfi_iterator_next_subkey(self->iter));
+
+ /* XXX: This is an error condition.
+ * Probably should throw an exception or something. */
+ if(!regfi_iterator_down(self->iter))
+ return NULL;
+ }
+
+ regfi_iterator_first_subkey(self->iter);
+ return CONSTRUCT(RegistryKey, RegistryKey, Con, NULL, self->file,
+ regfi_iterator_cur_key(self->iter));
+}
+
+static int TreeIterator_down(TreeIterator self)
+{
+ int result = regfi_iterator_down(self->iter);
+ regfi_iterator_first_subkey(self->iter);
+ regfi_iterator_first_value(self->iter);
+ return result;
+}
+
+static int TreeIterator_up(TreeIterator self)
+{
+ return regfi_iterator_up(self->iter);
+}
+
+static RegistryKey TreeIterator_current(TreeIterator self)
+{
+ return CONSTRUCT(RegistryKey, RegistryKey, Con, NULL, self->file,
+ regfi_iterator_cur_key(self->iter));
+}
+
+static int TreeIterator_to_root(TreeIterator self)
+{
+ return regfi_iterator_to_root(self->iter);
+}
+
+
+VIRTUAL(TreeIterator, Object) {
+ VMETHOD(Con) = TreeIterator_Con;
+ VMETHOD(iternext) = TreeIterator_next;
+ VMETHOD(down) = TreeIterator_down;
+ VMETHOD(up) = TreeIterator_up;
+ VMETHOD(current) = TreeIterator_current;
+ VMETHOD(to_root) = TreeIterator_to_root;
+ VMETHOD(__iter__) = TreeIterator__iter__;
+ /* VMETHOD(list_values) = TreeIterator_list_values;*/
+} END_VIRTUAL
+
+
+
+static int SubkeyIterator_dest(void *self)
+{
+ SubkeyIterator this = (SubkeyIterator)self;
+
+ talloc_unlink(this, (void*)this->list);
+
+ return 0;
+}
+
+static SubkeyIterator SubkeyIterator_Con(SubkeyIterator self,
+ struct RegistryFile_t* file,
+ REGFI_NK* key)
+{
+ /* XXX: add a talloc reference? */
+ self->file = file;
+
+ /* Add a reference to the list */
+ self->list = key->subkeys;
+ talloc_reference(self, self->list);
+
+ self->cur = 0;
+ talloc_set_destructor((void *)self, SubkeyIterator_dest);
+
+ return self;
+}
+
+static void SubkeyIterator__iter__(SubkeyIterator self)
+{
+ return;
+}
+
+static RegistryKey SubkeyIterator_iternext(SubkeyIterator self)
+{
+ const REGFI_NK* nk;
+
+ if(self->list != NULL && self->cur < self->list->num_keys)
+ {
+ /* XXX: can we switch to UTF-8 and have Python properly import that? */
+ nk = regfi_load_key(self->file->reg,
+ self->list->elements[self->cur].offset+REGFI_REGF_SIZE,
+ REGFI_ENCODING_ASCII, true);
+ self->cur++;
+ return CONSTRUCT(RegistryKey, RegistryKey, Con, NULL, self->file, nk);
+ }
+
+ return NULL;
+}
+
+VIRTUAL(SubkeyIterator, Object) {
+ VMETHOD(Con) = SubkeyIterator_Con;
+ VMETHOD(__iter__) = SubkeyIterator__iter__;
+ VMETHOD(iternext) = SubkeyIterator_iternext;
+} END_VIRTUAL
+
+
+
+static int ValueIterator_dest(void *self)
+{
+ ValueIterator this = (ValueIterator)self;
+
+ if(this->list != NULL)
+ talloc_unlink(this, (void*)this->list);
+
+ return 0;
+}
+
+static ValueIterator ValueIterator_Con(ValueIterator self,
+ struct RegistryFile_t* file,
+ REGFI_NK* key)
+{
+ /* XXX: add a talloc reference? */
+ self->file = file;
+ self->cur = 0;
+
+ /* Add a reference to the list */
+ self->list = key->values;
+ if(self->list != NULL)
+ talloc_reference(self, self->list);
+
+ talloc_set_destructor((void *)self, ValueIterator_dest);
+
+ return self;
+}
+
+static void ValueIterator__iter__(ValueIterator self)
+{
+ return;
+}
+
+static REGFI_VK* ValueIterator_iternext(ValueIterator self)
+{
+ const REGFI_VK* vk;
+
+ if(self->list != NULL && self->cur < self->list->num_values)
+ {
+ /* XXX: can we switch to UTF-8 and have Python properly import that? */
+ vk = regfi_load_value(self->file->reg,
+ self->list->elements[self->cur]+REGFI_REGF_SIZE,
+ REGFI_ENCODING_ASCII, true);
+ self->cur++;
+ /*return CONSTRUCT(RegistryKey, RegistryKey, Con, NULL, vk); */
+ return vk;
+ }
+
+ return NULL;
+
+
+ /* XXX: shouldn't parse data here every time we walk over a value.
+ * Instead, make data fetching a method and parse it then.
+ */
+ /*
+ data = (REGFI_DATA *)regfi_iterator_fetch_data(self->iter, rec);
+ if(!data) {
+ RaiseError(ERuntimeError, "Unable to fetch data: %s", regfi_log_get_str());
+ goto error;
+ }
+
+ switch(rec->type) {
+ case REG_EXPAND_SZ:
+ case REG_SZ:
+ result = (RawData)CONSTRUCT(DataString, RawData, Con, NULL, data, rec);
+ break;
+
+ case REG_DWORD:
+ result = (RawData)CONSTRUCT(DWORDData, RawData, Con, NULL, data, rec);
+ break;
+
+ case REG_BINARY:
+ default:
+ result = (RawData)CONSTRUCT(RawData, RawData, Con, NULL, data, rec);
+ break;
+ }
+
+ return result;
+ error:
+ talloc_free(self);
+ return NULL;
+ */
+}
+
+VIRTUAL(ValueIterator, Object) {
+ VMETHOD(Con) = ValueIterator_Con;
+ VMETHOD(__iter__) = ValueIterator__iter__;
+ VMETHOD(iternext) = ValueIterator_iternext;
+} END_VIRTUAL
+
+
+
+static int RawData_dest(void *self)
+{
+ RawData this = (RawData)self;
+
+ if(this->data) {
+ regfi_free_record(this->data);
+ };
+
+ if(this->rec) {
+ regfi_free_record(this->rec);
+ };
+
+ return 0;
+}
+
+static RawData RawData_Con(RawData self, REGFI_DATA *data, REGFI_VK *record)
+{
+ self->rec = record;
+ self->data = data;
+
+ talloc_set_destructor((void *)self, RawData_dest);
+
+ return self;
+}
+
+static int RawData_get_value(RawData self, char *buff, int len)
+{
+ int available_to_read = min(len, self->data->interpreted_size);
+
+ memcpy(buff, self->data->raw, available_to_read);
+
+ return available_to_read;
+}
+
+VIRTUAL(RawData, Object) {
+ VMETHOD(Con) = RawData_Con;
+ VMETHOD(get_value) = RawData_get_value;
+} END_VIRTUAL
+
+static char* DataString_get_value(DataString self)
+{
+ RawData this = (RawData)self;
+
+ return (char*)this->data->interpreted.string;
+}
+
+VIRTUAL(DataString, RawData) {
+ VMETHOD(get_value) = DataString_get_value;
+} END_VIRTUAL
+
+static uint64_t DWORDData_get_value(DWORDData self)
+{
+ RawData this = (RawData)self;
+
+ return this->data->interpreted.dword;
+}
+
+VIRTUAL(DWORDData, RawData) {
+ VMETHOD(get_value) = DWORDData_get_value;
+} END_VIRTUAL
+
+void pyregfi_init()
+{
+ INIT_CLASS(RegistryFile);
+}
diff --git a/python/experimental/utils.py b/python/experimental/utils.py
new file mode 100644
index 0000000..84ab10d
--- /dev/null
+++ b/python/experimental/utils.py
@@ -0,0 +1,511 @@
+import os, sys, re, pdb
+import distutils.sysconfig as sysconfig
+import distutils.util
+import platform
+import SCons.SConf as SConf
+import config
+
+# taken from scons wiki
+def CheckPKGConfig(context, version):
+ context.Message( 'Checking for pkg-config version > %s... ' % version)
+ ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0]
+ context.Result( ret )
+ return ret
+
+def CheckFramework(context, name):
+ ret = 0
+ if (platform.system().lower() == 'darwin'):
+ context.Message( '\nLooking for framework %s... ' % name )
+ lastFRAMEWORKS = context.env['FRAMEWORKS']
+ context.env.Append(FRAMEWORKS = [name])
+ ret = context.TryLink("""
+ int main(int argc, char **argv) {
+ return 0;
+ }
+ """, '.c')
+ if not ret:
+ context.env.Replace(FRAMEWORKS = lastFRAMEWORKS
+)
+
+ return ret
+
+def CheckFink(context):
+ context.Message( 'Looking for fink... ')
+ prog = context.env.WhereIs('fink')
+ if prog:
+ ret = 1
+ prefix = prog.rsplit(os.sep, 2)[0]
+ context.env.Append(LIBPATH = [prefix + os.sep +'lib'],
+ CPPPATH = [prefix + os.sep +'include'])
+ context.Message( 'Adding fink lib and include path')
+ else:
+ ret = 0
+
+ context.Result(ret)
+ return int(ret)
+
+def CheckMacports(context):
+ context.Message( 'Looking for macports... ')
+ prog = context.env.WhereIs('port')
+ if prog:
+ ret = 1
+ prefix = prog.rsplit(os.sep, 2)[0]
+ context.env.Append(LIBPATH = [prefix + os.sep + 'lib'],
+ CPPPATH = [prefix + os.sep + 'include'])
+ context.Message( 'Adding port lib and include path')
+ else:
+ ret = 0
+
+ context.Result(ret)
+ return int(ret)
+
+# TODO: We should use the scons one instead
+def CheckLib(context, name):
+ context.Message( 'Looking for lib %s... ' % name )
+ lastLIBS = context.env['LIBS']
+ context.env.Append(LIBS = [name])
+ ret = context.TryLink("""
+ int main(int argc, char **argv) {
+ return 0;
+ }
+ """,'.c')
+ if not ret:
+ context.env.Replace(LIBS = lastLIBS)
+
+ return ret
+
+def ConfigPKG(context, name):
+ context.Message( '\nUsing pkg-config for %s... ' % name )
+ ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0]
+ context.Result( ret )
+ if ret:
+ context.env.ParseConfig('pkg-config --cflags --libs \'%s\'' % name)
+ return int(ret)
+
+def CheckPKG(context, name):
+ context.Message( 'Checking for %s... ' % name )
+ if platform.system().lower() == 'windows':
+ return 0
+ ret = 1
+ if not CheckFramework(context, name):
+ if not ConfigPKG(context, name.lower()):
+ ret = CheckLib(context, name)
+
+ context.Result(ret)
+ return int(ret)
+
+
+## Configure colors for pretty builds
+colors = {}
+colors['cyan'] = '\033[96m'
+colors['purple'] = '\033[95m'
+colors['blue'] = '\033[94m'
+colors['green'] = '\033[92m'
+colors['yellow'] = '\033[93m'
+colors['red'] = '\033[91m'
+colors['end'] = '\033[0m'
+
+#If the output is not a terminal, remove the colors
+if not sys.stdout.isatty():
+ for key, value in colors.iteritems():
+ colors[key] = ''
+
+def error(msg):
+ print "%s%s%s" % (colors['red'], msg, colors['end'])
+ sys.exit(1)
+
+def warn(msg):
+ print "%s%s%s" % (colors['yellow'], msg, colors['end'])
+
+compile_source_message = '%sCompiling %s==> %s$SOURCE%s' % \
+ (colors['blue'], colors['purple'], colors['yellow'], colors['end'])
+
+compile_shared_source_message = '%sCompiling shared %s==> %s$SOURCE%s' % \
+ (colors['blue'], colors['purple'], colors['yellow'], colors['end'])
+
+compile_python_source_message = '%sCompiling python module %s==> %s$SOURCE%s' % \
+ (colors['blue'], colors['purple'], colors['yellow'], colors['end'])
+
+link_program_message = '%sLinking Program %s==> %s$TARGET%s' % \
+ (colors['red'], colors['purple'], colors['yellow'], colors['end'])
+
+link_library_message = '%sLinking Static Library %s==> %s$TARGET%s' % \
+ (colors['red'], colors['purple'], colors['yellow'], colors['end'])
+
+ranlib_library_message = '%sRanlib Library %s==> %s$TARGET%s' % \
+ (colors['red'], colors['purple'], colors['yellow'], colors['end'])
+
+link_shared_library_message = '%sLinking Shared Library %s==> %s$TARGET%s' % \
+ (colors['red'], colors['purple'], colors['yellow'], colors['end'])
+
+link_python_module_message = '%sLinking Native Python module %s==> %s${TARGET}%s' % \
+ (colors['red'], colors['purple'], colors['yellow'], colors['end'])
+
+java_library_message = '%sCreating Java Archive %s==> %s$TARGET%s' % \
+ (colors['red'], colors['purple'], colors['yellow'], colors['end'])
+
+def install_colors(args):
+ """ Installs colors into an environment """
+ args.update(dict( CXXCOMSTR = compile_source_message,
+ CCCOMSTR = compile_source_message,
+ SHCCCOMSTR = compile_shared_source_message,
+ SHCXXCOMSTR = compile_shared_source_message,
+ ARCOMSTR = link_library_message,
+ RANLIBCOMSTR = ranlib_library_message,
+ SHLINKCOMSTR = link_shared_library_message,
+ LINKCOMSTR = link_program_message,
+ JARCOMSTR = java_library_message,
+ JAVACCOMSTR = compile_source_message,))
+
+import optparse
+
+### This workaround is because scons does not provide access to the
+### parser, and by setting Help() we are unable to generate the option
+### listing from AddOption
+my_parser = optparse.OptionParser()
+
+import SCons.Script.Main as Main
+import SCons.Script as Script
+
+def add_option(arg, option, *args, **kwargs):
+ opt = "--%s" % option
+ Main.AddOption(opt, *args, **kwargs)
+ my_parser.add_option(opt, *args, **kwargs)
+
+ arg[option] = Main.GetOption(option)
+
+def generate_help(vars, env):
+ Script.Help("AFF4 build system configuration.\n\nFollowing are compile time options:\n")
+ Script.Help(my_parser.format_help())
+ Script.Help("\nThe following variables can be used on the command line:\n")
+ Script.Help(vars.GenerateHelpText(env))
+
+HEADERS = {}
+
+def check_size(conf, types):
+ global _DEFAULTS
+
+ for t in types:
+ name = "SIZEOF_" + t.replace(" ","_").upper()
+ HEADERS[name] = conf.CheckTypeSize(
+ t, size = _DEFAULTS[t][0])
+
+def check_type(conf, types):
+ header = None
+ for t in types:
+ if ':' in t:
+ t, header = t.split(':')
+ define = "HAVE_" + t.upper().replace(".","_")
+
+ result = 0
+ if conf.CheckType(t, includes="#include <%s>\n" % header):
+ result = 1
+
+ HEADERS[define] = result
+
+def check_build(conf, message, define, prog):
+ """ Build and links prog and adds define if that succeeds """
+ context = SConf.CheckContext(conf)
+ context.Message("Checking for %s ..." % message)
+ if context.TryLink(prog, ".c"):
+ HEADERS[define] = 1
+ context.Message("yes\n")
+ else:
+ context.Message("no\n")
+
+def check(type, conf, headers, extra_include =''):
+ for header in headers:
+ if ":" in header:
+ define, header = header.split(':')
+ else:
+ if "/" in header:
+ tmp = header.split("/")[-1]
+ else:
+ tmp = header
+
+ define = "HAVE_" + tmp.upper().replace(".","_")
+
+ global HEADERS
+ result = 0
+ if type == 'header':
+ #pdb.set_trace()
+ if conf.CheckCHeader(header): result = 1
+ HEADERS[define] = result
+ elif type == 'func':
+ if conf.CheckFunc(header, header=extra_include): result = 1
+ HEADERS[define] = result
+ elif type == 'lib':
+ if conf.CheckLib(header): result =1
+ HEADERS[define] = result
+
+## Build the config.h file
+def config_h_build(target, source, env):
+ config_h_defines = env.Dictionary()
+ config_h_defines.update(env.config.__dict__)
+ warn("Generating %s" % (target[0].path))
+
+ for a_target, a_source in zip(target, source):
+ config_h = file(str(a_target), "w")
+ config_h_in = file(str(a_source), "r")
+ config_h.write(config_h_in.read() % config_h_defines)
+ config_h_in.close()
+
+ keys = HEADERS.keys()
+ keys.sort()
+
+ for k in keys:
+ if HEADERS[k]:
+ config_h.write("#define %s %s\n" % (k,HEADERS[k]))
+ else:
+ config_h.write("/** %s unset */\n" % k)
+
+ config_h.close()
+
+import SCons.Environment
+
+class ExtendedEnvironment(SCons.Environment.Environment):
+ """ Implementation from Richard Levitte email to
+ org.tigris.scons.dev dated Jan 26, 2006 7:05:10 am."""
+ python_cppflags = "-I"+sysconfig.get_python_inc()
+
+ def PythonModule(self, libname, lib_objs=[], **kwargs):
+ """ This builds a python module which is almost a library but
+ is sometimes named differently.
+
+ We have two modes - a cross compile mode where we do our best
+ to guess the flags. In the native mode we can get the required
+ flags directly from distutils.
+ """
+ if config.MINGW_XCOMPILE:
+ shlib_suffix = ".pyd"
+ cppflags = "-I%s" % config.XCOMPILE_PYTHON_PATH
+ shlink_flags = ['']
+
+ else:
+ platform = self.subst('$PLATFORM')
+ shlib_pre_action = None
+ shlib_suffix = distutils.util.split_quoted(
+ sysconfig.get_config_var('SO'))
+ shlib_post_action = None
+ cppflags = distutils.util.split_quoted(self.python_cppflags)
+ shlink_flags = str(self['LINKFLAGS']).split()
+
+ install_dest = distutils.util.split_quoted(
+ os.path.join(
+ sysconfig.get_config_var('BINLIBDEST'),os.path.dirname(libname)))
+
+ flags = distutils.util.split_quoted(
+ sysconfig.get_config_var('LDSHARED'))
+
+ ## For some stupid reason they include the compiler in LDSHARED
+ shlink_flags.extend([x for x in flags if 'gcc' not in x])
+
+ shlink_flags.append(sysconfig.get_config_var('LOCALMODLIBS'))
+
+ ## TODO cross compile mode
+ kwargs['LIBPREFIX'] = ''
+ kwargs['CPPFLAGS'] = cppflags
+ kwargs['SHLIBSUFFIX'] = shlib_suffix
+ kwargs['SHLINKFLAGS'] = shlink_flags
+
+ if not self.config.V:
+ kwargs['SHCCCOMSTR'] = compile_python_source_message
+ kwargs['SHLINKCOMSTR'] = link_python_module_message
+
+ lib = self.SharedLibrary(libname,lib_objs,
+ **kwargs)
+
+ ## Install it to the right spot
+ self.Install(install_dest, lib)
+ self.Alias('install', install_dest)
+
+ return lib
+
+ def VersionedSharedLibrary(self, libname, libversion, lib_objs=[]):
+ """ This creates a version library similar to libtool.
+
+ We name the library with the appropriate soname.
+ """
+ platform = self.subst('$PLATFORM')
+ shlib_pre_action = None
+ shlib_suffix = self.subst('$SHLIBSUFFIX')
+ shlib_post_action = None
+ shlink_flags = SCons.Util.CLVar(self.subst('$SHLINKFLAGS'))
+
+ if platform == 'posix':
+ shlib_post_action = [ 'rm -f $TARGET', 'ln -s ${SOURCE.file} $TARGET' ]
+ shlib_post_action_output_re = [ '%s\\.[0-9\\.]*$' % re.escape(shlib_suffix), shlib_suffix ]
+ shlib_suffix += '.' + libversion
+ shlink_flags += [ '-Wl,-Bsymbolic', '-Wl,-soname=${LIBPREFIX}%s%s' % (
+ libname, shlib_suffix) ]
+ elif platform == 'aix':
+ shlib_pre_action = [ "nm -Pg $SOURCES > ${TARGET}.tmp1", "grep ' [BDT] ' < ${TARGET}.tmp1 > ${TARGET}.tmp2", "cut -f1 -d' ' < ${TARGET}.tmp2 > ${TARGET}", "rm -f ${TARGET}.tmp[12]" ]
+ shlib_pre_action_output_re = [ '$', '.exp' ]
+ shlib_post_action = [ 'rm -f $TARGET', 'ln -s $SOURCE $TARGET' ]
+ shlib_post_action_output_re = [ '%s\\.[0-9\\.]*' % re.escape(shlib_suffix), shlib_suffix ]
+ shlib_suffix += '.' + libversion
+ shlink_flags += ['-G', '-bE:${TARGET}.exp', '-bM:SRE']
+ elif platform == 'cygwin':
+ shlink_flags += [ '-Wl,-Bsymbolic', '-Wl,--out-implib,${TARGET.base}.a' ]
+ elif platform == 'darwin':
+ shlib_suffix = '.' + libversion + shlib_suffix
+ shlink_flags += [ '-dynamiclib', '-current-version %s' % libversion ]
+
+ lib = self.SharedLibrary(libname,lib_objs,
+ SHLIBSUFFIX=shlib_suffix,
+ SHLINKFLAGS=shlink_flags)
+
+ if shlib_pre_action:
+ shlib_pre_action_output = re.sub(shlib_pre_action_output_re[0], shlib_pre_action_output_re[1], str(lib[0]))
+ self.Command(shlib_pre_action_output, [ lib_objs ], shlib_pre_action)
+ self.Depends(lib, shlib_pre_action_output)
+
+ if shlib_post_action:
+ shlib_post_action_output = re.sub(shlib_post_action_output_re[0], shlib_post_action_output_re[1], str(lib[0]))
+ self.Command(shlib_post_action_output, lib, shlib_post_action)
+
+ return lib
+
+ def InstallVersionedSharedLibrary(self, destination, lib):
+ platform = self.subst('$PLATFORM')
+ shlib_suffix = self.subst('$SHLIBSUFFIX')
+ shlib_install_pre_action = None
+ shlib_install_post_action = None
+
+ if platform == 'posix':
+ shlib_post_action = [ 'rm -f $TARGET', 'ln -s ${SOURCE.file} $TARGET' ]
+ shlib_post_action_output_re = [ '%s\\.[0-9\\.]*$' % re.escape(shlib_suffix), shlib_suffix ]
+ shlib_install_post_action = shlib_post_action
+ shlib_install_post_action_output_re = shlib_post_action_output_re
+
+ ilib = self.Install(destination,lib)
+
+ if shlib_install_pre_action:
+ shlib_install_pre_action_output = re.sub(shlib_install_pre_action_output_re[0], shlib_install_pre_action_output_re[1], str(ilib[0]))
+ self.Command(shlib_install_pre_action_output, ilib, shlib_install_pre_action)
+ self.Depends(shlib_install_pre_action_output, ilib)
+
+ if shlib_install_post_action:
+ shlib_install_post_action_output = re.sub(shlib_install_post_action_output_re[0], shlib_install_post_action_output_re[1], str(ilib[0]))
+ self.Command(shlib_install_post_action_output, ilib, shlib_install_post_action)
+
+
+import subprocess
+
+def pkg_config(pkg, type):
+ try:
+ result = subprocess.Popen(["%s-config" % pkg, "--%s" % type],
+ stdout=subprocess.PIPE).communicate()[0]
+ except:
+ error("Unable to run %s-config - do you have the dev package installed?" % pkg)
+
+ return result.strip()
+
+
+# Sensible default for common types on common platforms.
+_DEFAULTS = {
+ 'char': [1,],
+ 'short' : [2,],
+ 'int' : [4, 2],
+ 'long' : [4, 8],
+ 'long long' : [8, 4],
+ # Normally, there is no need to check unsigned types, because they are
+ # guaranteed to be of the same size than their signed counterpart.
+ 'unsigned char': [1,],
+ 'unsigned short' : [2,],
+ 'unsigned int' : [4, 2],
+ 'unsigned long' : [4, 8],
+ 'unsigned long long' : [8, 4],
+ 'float' : [4,],
+ 'double' : [8,],
+ 'long double' : [12,],
+ 'size_t' : [4,],
+}
+
+def CheckTypeSize(context, type, includes = None, language = 'C', size = None):
+ """This check can be used to get the size of a given type, or to check whether
+ the type is of expected size.
+
+ Arguments:
+ - type : str
+ the type to check
+ - includes : sequence
+ list of headers to include in the test code before testing the type
+ - language : str
+ 'C' or 'C++'
+ - size : int
+ if given, will test wether the type has the given number of bytes.
+ If not given, will test against a list of sizes (all sizes between
+ 0 and 16 bytes are tested).
+
+ Returns:
+ status : int
+ 0 if the check failed, or the found size of the type if the check succeeded."""
+ minsz = 0
+ maxsz = 16
+
+ if includes:
+ src = "\n".join([r"#include <%s>\n" % i for i in includes])
+ else:
+ src = ""
+
+ if language == 'C':
+ ext = '.c'
+ elif language == 'C++':
+ ext = '.cpp'
+ else:
+ raise NotImplementedError("%s is not a recognized language" % language)
+
+ # test code taken from autoconf: this is a pretty clever hack to find that
+ # a type is of a given size using only compilation. This speeds things up
+ # quite a bit compared to straightforward code using TryRun
+ src += r"""
+typedef %s scons_check_type;
+
+int main()
+{
+ static int test_array[1 - 2 * !(((long int) (sizeof(scons_check_type))) <= %d)];
+ test_array[0] = 0;
+
+ return 0;
+}
+"""
+
+ if size:
+ # Only check if the given size is the right one
+ context.Message('Checking %s is %d bytes... ' % (type, size))
+ st = context.TryCompile(src % (type, size), ext)
+ context.Result(st)
+
+ if st:
+ return size
+ else:
+ return 0
+ else:
+ # Only check if the given size is the right one
+ context.Message('Checking size of %s ... ' % type)
+
+ # Try sensible defaults first
+ try:
+ szrange = _DEFAULTS[type]
+ except KeyError:
+ szrange = []
+ szrange.extend(xrange(minsz, maxsz))
+ st = 0
+
+ # Actual test
+ for sz in szrange:
+ st = context.TryCompile(src % (type, sz), ext)
+ if st:
+ break
+
+ if st:
+ context.Result('%d' % sz)
+ return sz
+ else:
+ context.Result('Failed !')
+ return 0
+
+#For example, to check wether long is 4 bytes on your platform, you can do:
+#config.CheckTypeSize('long', size = 4).
+## Now check the sizes
diff --git a/python/pyregfi/__init__.py b/python/pyregfi/__init__.py
new file mode 100644
index 0000000..84d06b0
--- /dev/null
+++ b/python/pyregfi/__init__.py
@@ -0,0 +1,1143 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2010-2011 Timothy D. Morgan
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 3 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# $Id: $
+
+## @package pyregfi
+# Python interface to the regfi library.
+#
+
+## @mainpage API Documentation
+#
+# The pyregfi module provides a Python interface to the @ref regfi Windows
+# registry library.
+#
+# The library operates on registry hives, each of which is contained within a
+# single file. The quickest way to get started, is to use the @ref openHive()
+# function to obtain a Hive object. For example:
+# @code
+# >>> import pyregfi
+# >>> myHive = pyregfi.openHive('/mnt/win/c/WINDOWS/system32/config/system')
+# @endcode
+#
+# Using this Hive object, one can begin investigating what top-level keys
+# exist by starting with the root Key attribute:
+# @code
+# >>> for key in myHive.root.subkeys:
+# ... print(key.name)
+# ControlSet001
+# ControlSet003
+# LastKnownGoodRecovery
+# MountedDevices
+# Select
+# Setup
+# WPA
+# @endcode
+#
+# From there, accessing subkeys and values by name is a simple matter of:
+# @code
+# >>> myKey = myHive.root.subkeys['Select']
+# >>> myValue = myKey.values['Current']
+# @endcode
+#
+# The data associated with a Value can be obtained through the fetch_data()
+# method:
+# @code
+# >>> print(myValue.fetch_data())
+# 1
+# @endcode
+#
+# While useful for simple exercises, using the subkeys object for deeply nested
+# paths is not efficient and doesn't make for particularly attractive code.
+# Instead, a special-purpose HiveIterator class is provided for simplicity of
+# use and fast access to specific known paths:
+# @code
+# >>> myIter = pyregfi.HiveIterator(myHive)
+# >>> myIter.descend(['ControlSet001','Control','NetworkProvider','HwOrder'])
+# >>> myKey = myIter.current_key()
+# >>> print(myKey.values['ProviderOrder'].fetch_data())
+# RDPNP,LanmanWorkstation,WebClient
+# @endcode
+#
+# The first two lines above can be simplified in some "syntactic sugar" provided
+# by the Hive.subtree() method. Also, as one might expect, the HiveIterator
+# also acts as an iterator, producing keys in a depth-first order.
+# For instance, to traverse all keys under the ControlSet003\\Services key,
+# printing their names as we go, we could do:
+# @code
+# >>> for key in Hive.subtree(['ControlSet003','Services']):
+# >>> print(key.name)
+# Services
+# Abiosdsk
+# abp480n5
+# Parameters
+# PnpInterface
+# ACPI
+# [...]
+# @endcode
+#
+# Note that "Services" was printed first, since the subtree is traversed as a
+# "preordering depth-first" search starting with the HiveIterator's current_key().
+# As one might expect, traversals of subtrees stops when all elements in a
+# specific subtree (and none outside of it) have been traversed.
+#
+# For more information, peruse the various attributes and methods available on
+# the Hive, HiveIterator, Key, Value, and Security classes.
+#
+# @note @ref regfi is a read-only library by design and there
+# are no plans to implement write support.
+#
+# @note At present, pyregfi has been tested with Python versions 2.6 and 3.1
+#
+# @note Developers strive to make pyregfi thread-safe.
+#
+import sys
+import time
+import ctypes
+import ctypes.util
+import threading
+from pyregfi.structures import *
+
+
+## An enumeration of registry Value data types
+#
+# @note This is a static class, there is no need to instantiate it.
+# Just access its attributes directly as DATA_TYPES.SZ, etc
+class DATA_TYPES(object):
+ # XXX: add dictionary lookup attributes to convert both directions between
+ # the integers and typenames
+
+ ## None / Unknown
+ NONE = 0
+ ## String
+ SZ = 1
+ ## String with %...% expansions
+ EXPAND_SZ = 2
+ ## Binary buffer
+ BINARY = 3
+ ## 32 bit integer (little endian)
+ DWORD = 4 # DWORD, little endian
+ ## 32 bit integer (little endian)
+ DWORD_LE = 4
+ ## 32 bit integer (big endian)
+ DWORD_BE = 5 # DWORD, big endian
+ ## Symbolic link
+ LINK = 6
+ ## List of strings
+ MULTI_SZ = 7
+ ## Unknown structure
+ RESOURCE_LIST = 8
+ ## Unknown structure
+ FULL_RESOURCE_DESCRIPTOR = 9
+ ## Unknown structure
+ RESOURCE_REQUIREMENTS_LIST = 10
+ ## 64 bit integer
+ QWORD = 11 # 64-bit little endian
+
+
+## An enumeration of log message types
+#
+# @note This is a static class, there is no need to instantiate it.
+# Just access its attributes directly as LOG_TYPES.INFO, etc
+class LOG_TYPES(object):
+ ## Informational messages, useful in debugging
+ INFO = 0x01
+ ## Non-critical problems in structure parsing or intepretation
+ WARN = 0x04
+ ## Major failures
+ ERROR = 0x10
+
+
+def _buffer2bytearray(char_pointer, length):
+ if length == 0 or char_pointer == None:
+ return None
+
+ ret_val = bytearray(length)
+ for i in range(0,length):
+ ret_val[i] = char_pointer[i][0]
+
+ return ret_val
+
+
+def _strlist2charss(str_list):
+ ret_val = []
+ for s in str_list:
+ ret_val.append(s.encode('utf-8', 'replace'))
+
+ ret_val = (ctypes.c_char_p*(len(str_list)+1))(*ret_val)
+ # Terminate the char** with a NULL pointer
+ ret_val[-1] = 0
+
+ return ret_val
+
+
+def _charss2strlist(chars_pointer):
+ ret_val = []
+ i = 0
+ s = chars_pointer[i]
+ while s:
+ ret_val.append(s.decode('utf-8', 'replace'))
+ i += 1
+ s = chars_pointer[i]
+
+ return ret_val
+
+
+
+## Returns the (py)regfi library version
+#
+# @return A string indicating the version
+def getVersion():
+ return regfi.regfi_version()
+
+
+## Retrieves messages produced by regfi during parsing and interpretation
+#
+# The regfi C library may generate log messages stored in a special thread-safe
+# global data structure. These messages should be retrieved periodically or
+# after each major operation by callers to determine if any errors or warnings
+# should be reported to the user. Failure to retrieve these could result in
+# excessive memory consumption.
+def getLogMessages():
+ msgs = regfi.regfi_log_get_str()
+ if not msgs:
+ return ''
+ return msgs.decode('utf-8')
+
+
+## Sets the types of log messages to record
+#
+# @param log_types A sequence of message types that regfi should generate.
+# Message types can be found in the LOG_TYPES enumeration.
+#
+# @return True on success, False on failure. Failures are rare, but could
+# indicate that global logging is not operating as expected.
+#
+# Example:
+# @code
+# setLogMask((LOG_TYPES.ERROR, LOG_TYPES.WARN, LOG_TYPES.INFO))
+# @endcode
+#
+# The message mask is a global (all hives, iterators), thread-specific value.
+# For more information, see @ref regfi_log_set_mask.
+#
+def setLogMask(log_types):
+ mask = 0
+ for m in log_types:
+ mask |= m
+ return regfi.regfi_log_set_mask(mask)
+
+
+## Opens a file as a registry hive
+#
+# @param path The file path of a hive, as one would provide to the
+# open() built-in
+#
+# @return A new Hive instance
+def openHive(path):
+ fh = open(path, 'rb')
+ return Hive(fh)
+
+
+## Abstract class for most objects returned by the library
+class _StructureWrapper(object):
+ _hive = None
+ _base = None
+
+ def __init__(self, hive, base):
+ if not hive:
+ raise Exception("Could not create _StructureWrapper,"
+ + " hive is NULL. Current log:\n"
+ + getLogMessages())
+ if not base:
+ raise Exception("Could not create _StructureWrapper,"
+ + " base is NULL. Current log:\n"
+ + getLogMessages())
+ self._hive = hive
+ self._base = base
+
+
+ # Memory management for most regfi structures is taken care of here
+ def __del__(self):
+ if self._base:
+ regfi.regfi_free_record(self._hive.file, self._base)
+
+
+ # Any attribute requests not explicitly defined in subclasses gets passed
+ # to the equivalent REGFI_* structure defined in structures.py
+ def __getattr__(self, name):
+ return getattr(self._base.contents, name)
+
+
+ ## Test for equality
+ #
+ # Records returned by pyregfi may be compared with one another. For example:
+ # @code
+ # >>> key2 = key1.subkeys['child']
+ # >>> key1 == key2
+ # False
+ # >>> key1 != key2
+ # True
+ # >>> key1 == key2.get_parent()
+ # True
+ # @endcode
+ def __eq__(self, other):
+ return (type(self) == type(other)) and (self.offset == other.offset)
+
+
+ def __ne__(self, other):
+ return (not self.__eq__(other))
+
+
+class Key():
+ pass
+
+
+class Value():
+ pass
+
+
+
+## Represents a registry SK record which contains a security descriptor
+#
+class Security(_StructureWrapper):
+ ## Number of registry Keys referencing this SK record
+ ref_count = 1
+
+ ## The absolute file offset of the SK record's cell in the Hive file
+ offset = 0xCAFEBABE
+
+ ## The @ref winsec.SecurityDescriptor for this SK record
+ descriptor = object()
+
+ def __init__(self, hive, base):
+ super(Security, self).__init__(hive, base)
+ # XXX: add checks for NULL pointers
+ self.descriptor = winsec.SecurityDescriptor(base.contents.sec_desc.contents)
+
+ ## Loads the "next" Security record in the hive
+ #
+ # @note
+ # SK records are included in a circular, doubly-linked list.
+ # To iterate over all SK records, be sure to check for the repetition of
+ # the SK record you started with to determine when all have been traversed.
+ def next_security(self):
+ return Security(self._hive,
+ regfi.regfi_next_sk(self._hive.file, self._base))
+
+ ## Loads the "previous" Security record in the hive
+ #
+ # @note
+ # SK records are included in a circular, doubly-linked list.
+ # To iterate over all SK records, be sure to check for the repetition of
+ # the SK record you started with to determine when all have been traversed.
+ def prev_security(self):
+ return Security(self._hive,
+ regfi.regfi_prev_sk(self._hive.file, self._base))
+
+
+## Abstract class for ValueList and SubkeyList
+class _GenericList(object):
+ # XXX: consider implementing keys(), values(), items() and other dictionary methods
+ _hive = None
+ _key_base = None
+ _length = None
+ _current = None
+
+ # implementation-specific functions for SubkeyList and ValueList
+ _fetch_num = None
+ _find_element = None
+ _get_element = None
+ _constructor = None
+
+ def __init__(self, key):
+ if not key:
+ raise Exception("Could not create _GenericList; key is NULL."
+ + "Current log:\n" + getLogMessages())
+
+ base = regfi.regfi_reference_record(key._hive.file, key._base)
+ if not base:
+ raise Exception("Could not create _GenericList; memory error."
+ + "Current log:\n" + getLogMessages())
+ self._key_base = cast(base, type(key._base))
+ self._length = self._fetch_num(self._key_base)
+ self._hive = key._hive
+
+
+ def __del__(self):
+ regfi.regfi_free_record(self._hive.file, self._key_base)
+
+
+ ## Length of list
+ def __len__(self):
+ return self._length
+
+
+ ## Retrieves a list element by name
+ #
+ # @param name The name of the subkey or value desired.
+ # This is case-insensitive.
+ #
+ # @note The registry format does not inherently prevent multiple
+ # subkeys or values from having the same name, having a key
+ # and a value with the same name, or having the same name in
+ # different cases that could both match.
+ # This interface simply returns the first match in the list.
+ # Lookups using this method could also fail due to incorrectly
+ # encoded strings stored as names.
+ # To identify any duplicates or elements with malformed names,
+ # use the iterator interface to check every list element.
+ #
+ # @return the first element whose name matches, or None if the element
+ # could not be found
+ def __getitem__(self, name):
+ # XXX: Consider interpreting integer names as offsets in the underlying list
+ index = ctypes.c_uint32()
+ if isinstance(name, str):
+ name = name.encode('utf-8')
+
+ if name != None:
+ name = create_string_buffer(bytes(name))
+
+ if self._find_element(self._hive.file, self._key_base,
+ name, byref(index)):
+ return self._constructor(self._hive,
+ self._get_element(self._hive.file,
+ self._key_base,
+ index))
+ raise KeyError('')
+
+
+ ## Fetches the requested element by name, or the default value if the lookup
+ # fails.
+ #
+ def get(self, name, default):
+ try:
+ return self[name]
+ except KeyError:
+ return default
+
+ def __iter__(self):
+ self._current = 0
+ return self
+
+ def __next__(self):
+ if self._current >= self._length:
+ raise StopIteration('')
+
+ elem = self._get_element(self._hive.file, self._key_base,
+ ctypes.c_uint32(self._current))
+ self._current += 1
+ return self._constructor(self._hive, elem)
+
+ # For Python 2.x
+ next = __next__
+
+
+## The list of subkeys associated with a Key
+#
+# This attribute is both iterable:
+# @code
+# for k in myKey.subkeys:
+# ...
+# @endcode
+# and accessible as a dictionary:
+# @code
+# mySubkey = myKey.subkeys["keyName"]
+# @endcode
+#
+# You may also request the len() of a subkeys list.
+# However keys(), values(), items() and similar methods are not currently
+# implemented.
+class SubkeyList(_GenericList):
+ _fetch_num = regfi.regfi_fetch_num_subkeys
+ _find_element = regfi.regfi_find_subkey
+ _get_element = regfi.regfi_get_subkey
+
+
+## The list of values associated with a Key
+#
+# This attribute is both iterable:
+# @code
+# for v in myKey.values:
+# ...
+# @endcode
+# and accessible as a dictionary:
+# @code
+# myValue = myKey.values["valueName"]
+# @endcode
+#
+# You may also request the len() of a values list.
+# However keys(), values(), items() and similar methods are not currently
+# implemented.
+class ValueList(_GenericList):
+ _fetch_num = regfi.regfi_fetch_num_values
+ _find_element = regfi.regfi_find_value
+ _get_element = regfi.regfi_get_value
+
+
+## Registry key
+# These represent registry keys (@ref REGFI_NK records) and provide
+# access to their subkeys, values, and other metadata.
+#
+# @note Key instances may provide access to more attributes than are
+# documented here. However, undocumented attributes may change over time
+# and are not officially supported. If you need access to an attribute
+# not shown here, see @ref pyregfi.structures.
+class Key(_StructureWrapper):
+ ## A @ref ValueList object representing the list of Values
+ # stored on this Key
+ values = None
+
+ ## A @ref SubkeyList object representing the list of subkeys
+ # stored on this Key
+ subkeys = None
+
+ ## The raw Key name as an uninterpreted bytearray
+ name_raw = (b"...")
+
+ ## The name of the Key as a (unicode) string
+ name = "..."
+
+ ## The string encoding used to store the Key's name ("ascii" or "utf-16-le")
+ name_encoding = "ascii"
+
+ ## The absolute file offset of the Key record's cell in the Hive file
+ offset = 0xCAFEBABE
+
+ ## This Key's last modified time represented as the number of seconds
+ # since the UNIX epoch in UTC; similar to what time.time() returns
+ modified = 1300000000.123456
+
+ ## The NK record's flags field
+ flags = 0x10110001
+
+ def __init__(self, hive, base):
+ super(Key, self).__init__(hive, base)
+ self.values = ValueList(self)
+ self.subkeys = SubkeyList(self)
+
+ def __getattr__(self, name):
+ if name == "name":
+ ret_val = super(Key, self).__getattr__(name)
+
+ if not ret_val:
+ ret_val = self.name_raw
+ if ret_val != None:
+ ret_val = ret_val.decode(self.name_encoding, 'replace')
+ else:
+ ret_val = ret_val.decode('utf-8', 'replace')
+
+ elif name == "name_encoding":
+ flags = super(Key, self).__getattr__("flags")
+ if (flags & structures.REGFI_NK_FLAG_ASCIINAME) > 0:
+ ret_val = "ascii"
+ ret_val = "utf-16-le"
+
+ elif name == "name_raw":
+ ret_val = super(Key, self).__getattr__(name)
+ length = super(Key, self).__getattr__('name_length')
+ ret_val = _buffer2bytearray(ret_val, length)
+
+ elif name == "modified":
+ ret_val = regfi.regfi_nt2unix_time(self._base.contents.mtime)
+
+ else:
+ ret_val = super(Key, self).__getattr__(name)
+
+ return ret_val
+
+
+ ## Retrieves the Security properties for this key
+ def fetch_security(self):
+ return Security(self._hive,
+ regfi.regfi_fetch_sk(self._hive.file, self._base))
+
+
+ ## Retrieves the class name for this key
+ #
+ # Class names are typically stored as UTF-16LE strings, so these are decoded
+ # into proper python (unicode) strings. However, if this fails, a bytearray
+ # is instead returned containing the raw buffer stored for the class name.
+ #
+ # @return The class name as a string or bytearray. None if a class name
+ # doesn't exist or an unrecoverable error occurred during retrieval.
+ def fetch_classname(self):
+ ret_val = None
+ cn_p = regfi.regfi_fetch_classname(self._hive.file, self._base)
+ if cn_p:
+ cn_struct = cn_p.contents
+ if cn_struct.interpreted:
+ ret_val = cn_struct.interpreted.decode('utf-8', 'replace')
+ else:
+ ret_val = _buffer2bytearray(cn_struct.raw,
+ cn_struct.size)
+ regfi.regfi_free_record(self._hive.file, cn_p)
+
+ return ret_val
+
+
+ ## Retrieves this key's parent key
+ #
+ # @return The parent's Key instance or None if current key is root
+ # (or an error occured)
+ def get_parent(self):
+ if self.is_root():
+ return None
+ parent_base = regfi.regfi_get_parentkey(self._hive.file, self._base)
+ if parent_base:
+ return Key(self._hive, parent_base)
+ return None
+
+
+ ## Checks to see if this Key is the root of its Hive
+ #
+ # @return True if it is, False otherwise
+ def is_root(self):
+ return (self._hive.root == self)
+
+
+## Registry value (metadata)
+#
+# These represent registry values (@ref REGFI_VK records) and provide
+# access to their associated data.
+#
+# @note Value instances may provide access to more attributes than are
+# documented here. However, undocumented attributes may change over time
+# and are not officially supported. If you need access to an attribute
+# not shown here, see @ref pyregfi.structures.
+class Value(_StructureWrapper):
+ ## The raw Value name as an uninterpreted bytearray
+ name_raw = (b"...")
+
+ ## The name of the Value as a (unicode) string
+ name = "..."
+
+ ## The string encoding used to store the Value's name ("ascii" or "utf-16-le")
+ name_encoding = "ascii"
+
+ ## The absolute file offset of the Value record's cell in the Hive file
+ offset = 0xCAFEBABE
+
+ ## The length of data advertised in the VK record
+ data_size = 0xCAFEBABE
+
+ ## An integer which represents the data type for this Value's data
+ # Typically this value is one of 12 types defined in @ref DATA_TYPES,
+ # but in some cases (the SAM hive) it may be used for other purposes
+ type = DATA_TYPES.NONE
+
+ ## The VK record's flags field
+ flags = 0x10110001
+
+ ## Retrieves the Value's data according to advertised type
+ #
+ # Data is loaded from its cell(s) and then interpreted based on the data
+ # type recorded in the Value. It is not uncommon for data to be stored with
+ # the wrong type or even with invalid types. If you have difficulty
+ # obtaining desired data here, use @ref fetch_raw_data().
+ #
+ # @return The interpreted representation of the data as one of several
+ # possible Python types, as listed below. None if any failure
+ # occurred during extraction or conversion.
+ #
+ # @retval string for SZ, EXPAND_SZ, and LINK
+ # @retval int for DWORD, DWORD_BE, and QWORD
+ # @retval list(string) for MULTI_SZ
+ # @retval bytearray for NONE, BINARY, RESOURCE_LIST,
+ # FULL_RESOURCE_DESCRIPTOR, and RESOURCE_REQUIREMENTS_LIST
+ #
+ def fetch_data(self):
+ ret_val = None
+ data_p = regfi.regfi_fetch_data(self._hive.file, self._base)
+ if not data_p:
+ return None
+ data_struct = data_p.contents
+
+ if data_struct.interpreted_size == 0:
+ ret_val = None
+ elif data_struct.type in (DATA_TYPES.SZ, DATA_TYPES.EXPAND_SZ, DATA_TYPES.LINK):
+ # Unicode strings
+ ret_val = data_struct.interpreted.string.decode('utf-8', 'replace')
+ elif data_struct.type in (DATA_TYPES.DWORD, DATA_TYPES.DWORD_BE):
+ # 32 bit integers
+ ret_val = data_struct.interpreted.dword
+ elif data_struct.type == DATA_TYPES.QWORD:
+ # 64 bit integers
+ ret_val = data_struct.interpreted.qword
+ elif data_struct.type == DATA_TYPES.MULTI_SZ:
+ ret_val = _charss2strlist(data_struct.interpreted.multiple_string)
+ elif data_struct.type in (DATA_TYPES.NONE, DATA_TYPES.RESOURCE_LIST,
+ DATA_TYPES.FULL_RESOURCE_DESCRIPTOR,
+ DATA_TYPES.RESOURCE_REQUIREMENTS_LIST,
+ DATA_TYPES.BINARY):
+ ret_val = _buffer2bytearray(data_struct.interpreted.none,
+ data_struct.interpreted_size)
+
+ regfi.regfi_free_record(self._hive.file, data_p)
+ return ret_val
+
+
+ ## Retrieves raw representation of Value's data
+ #
+ # @return A bytearray containing the data
+ #
+ def fetch_raw_data(self):
+ ret_val = None
+ # XXX: should we load the data without interpretation instead?
+ data_p = regfi.regfi_fetch_data(self._hive.file, self._base)
+ if not data_p:
+ return None
+
+ data_struct = data_p.contents
+ ret_val = _buffer2bytearray(data_struct.raw,
+ data_struct.size)
+ regfi.regfi_free_record(self._hive.file, data_p)
+ return ret_val
+
+
+ def __getattr__(self, name):
+ ret_val = None
+ if name == "name":
+ ret_val = super(Value, self).__getattr__(name)
+ if not ret_val:
+ ret_val = self.name_raw
+ if ret_val != None:
+ ret_val = ret_val.decode(self.name_encoding, 'replace')
+ else:
+ ret_val = ret_val.decode('utf-8', 'replace')
+
+ elif name == "name_encoding":
+ flags = super(Value, self).__getattr__("flags")
+ if (flags & structures.REGFI_VK_FLAG_ASCIINAME) > 0:
+ ret_val = "ascii"
+ else:
+ ret_val = "utf-16-le"
+
+ elif name == "name_raw":
+ ret_val = super(Value, self).__getattr__(name)
+ length = super(Value, self).__getattr__('name_length')
+ ret_val = _buffer2bytearray(ret_val, length)
+
+ else:
+ ret_val = super(Value, self).__getattr__(name)
+
+ return ret_val
+
+
+# Avoids chicken/egg class definitions.
+# Also makes for convenient code reuse in these lists' parent classes.
+SubkeyList._constructor = Key
+ValueList._constructor = Value
+
+
+
+## Represents a single registry hive (file)
+class Hive():
+ file = None
+ raw_file = None
+ _fh = None
+ #_root = None
+
+
+ ## The root Key of this Hive
+ root = None
+
+ ## This Hives's last modified time represented as the number of seconds
+ # since the UNIX epoch in UTC; similar to what time.time() returns
+ modified = 1300000000.123456
+
+ ## First sequence number
+ sequence1 = 12345678
+
+ ## Second sequence number
+ sequence2 = 12345678
+
+ ## Major version
+ major_version = 1
+
+ ## Minor version
+ minor_version = 5
+
+ ## Constructor
+ #
+ # Initialize a new Hive based on a Python file object. To open a file by
+ # path, see @ref openHive.
+ #
+ # @param fh A Python file object. The constructor first looks for a valid
+ # fileno attribute on this object and uses it if possible.
+ # Otherwise, the seek and read methods are used for file
+ # access.
+ #
+ # @note Supplied file must be seekable. Do not perform any operation on
+ # the provided file object while a Hive is using it. Do not
+ # construct multiple Hive instances from the same file object.
+ # If a file must be accessed by separate code and pyregfi
+ # simultaneously, use a separate file descriptor. Hives are
+ # thread-safe, so multiple threads may use a single Hive object.
+ def __init__(self, fh):
+ # The fileno method may not exist, or it may throw an exception
+ # when called if the file isn't backed with a descriptor.
+ self._fh = fh
+ fn = None
+ try:
+ # XXX: Native calls to Windows filenos don't seem to work.
+ # Need to investigate why.
+ if not is_win32 and hasattr(fh, 'fileno'):
+ fn = fh.fileno()
+ except:
+ pass
+
+ if fn != None:
+ self.file = regfi.regfi_alloc(fn, REGFI_ENCODING_UTF8)
+ if not self.file:
+ # XXX: switch to non-generic exception
+ raise Exception("Could not open registry file. Current log:\n"
+ + getLogMessages())
+ else:
+ fh.seek(0)
+ self.raw_file = structures.REGFI_RAW_FILE()
+ self.raw_file.fh = fh
+ self.raw_file.seek = seek_cb_type(self.raw_file.cb_seek)
+ self.raw_file.read = read_cb_type(self.raw_file.cb_read)
+ self.file = regfi.regfi_alloc_cb(pointer(self.raw_file), REGFI_ENCODING_UTF8)
+ if not self.file:
+ # XXX: switch to non-generic exception
+ raise Exception("Could not open registry file. Current log:\n"
+ + getLogMessages())
+
+
+ def __getattr__(self, name):
+ if name == "root":
+ # XXX: This creates reference loops. Need to cache better inside regfi
+ #if self._root == None:
+ # self._root = Key(self, regfi.regfi_get_rootkey(self.file))
+ #return self._root
+ return Key(self, regfi.regfi_get_rootkey(self.file))
+
+ elif name == "modified":
+ return regfi.regfi_nt2unix_time(self._base.contents.mtime)
+
+ return getattr(self.file.contents, name)
+
+
+ def __del__(self):
+ if self.file:
+ regfi.regfi_free(self.file)
+
+ def __iter__(self):
+ return HiveIterator(self)
+
+
+ ## Creates a @ref HiveIterator initialized at the specified path in
+ # the hive.
+ #
+ # @param path A list of Key names which represent an absolute path within
+ # the Hive
+ #
+ # @return A @ref HiveIterator which is positioned at the specified path.
+ #
+ # @exception Exception If the path could not be found/traversed
+ def subtree(self, path):
+ hi = HiveIterator(self)
+ hi.descend(path)
+ return hi
+
+
+## A special purpose iterator for registry hives
+#
+# Iterating over an object of this type causes all keys in a specific
+# hive subtree to be returned in a depth-first manner. These iterators
+# are typically created using the @ref Hive.subtree() function on a @ref Hive
+# object.
+#
+# HiveIterators can also be used to manually traverse up and down a
+# registry hive as they retain information about the current position in
+# the hive, along with which iteration state for subkeys and values for
+# every parent key. See the @ref up and @ref down methods for more
+# information.
+class HiveIterator():
+ _hive = None
+ _iter = None
+ _iteration_root = None
+ _lock = None
+
+ def __init__(self, hive):
+ self._iter = regfi.regfi_iterator_new(hive.file)
+ if not self._iter:
+ raise Exception("Could not create iterator. Current log:\n"
+ + getLogMessages())
+ self._hive = hive
+ self._lock = threading.RLock()
+
+ def __getattr__(self, name):
+ self._lock.acquire()
+ ret_val = getattr(self._iter.contents, name)
+ self._lock.release()
+ return ret_val
+
+ def __del__(self):
+ self._lock.acquire()
+ regfi.regfi_iterator_free(self._iter)
+ self._lock.release()
+
+ def __iter__(self):
+ self._lock.acquire()
+ self._iteration_root = None
+ self._lock.release()
+ return self
+
+ def __next__(self):
+ self._lock.acquire()
+ if self._iteration_root == None:
+ self._iteration_root = self.current_key().offset
+ elif not regfi.regfi_iterator_down(self._iter):
+ up_ret = regfi.regfi_iterator_up(self._iter)
+ while (up_ret and
+ not regfi.regfi_iterator_next_subkey(self._iter)):
+ if self._iteration_root == self.current_key().offset:
+ self._iteration_root = None
+ self._lock.release()
+ raise StopIteration('')
+ up_ret = regfi.regfi_iterator_up(self._iter)
+
+ if not up_ret:
+ self._iteration_root = None
+ self._lock.release()
+ raise StopIteration('')
+
+ # XXX: Use non-generic exception
+ if not regfi.regfi_iterator_down(self._iter):
+ self._lock.release()
+ raise Exception('Error traversing iterator downward.'+
+ ' Current log:\n'+ getLogMessages())
+
+ regfi.regfi_iterator_first_subkey(self._iter)
+ ret_val = self.current_key()
+ self._lock.release()
+
+ return ret_val
+
+
+ # For Python 2.x
+ next = __next__
+
+ # XXX: Should add sanity checks on some of these traversal functions
+ # to throw exceptions if a traversal/retrieval *should* have worked
+ # but failed for some reason.
+
+ ## Descends the iterator to a subkey
+ #
+ # Descends the iterator one level to the current subkey, or a subkey
+ # specified by name.
+ #
+ # @param subkey_name If specified, locates specified subkey by name
+ # (via find_subkey()) and descends to it.
+ #
+ # @return True if successful, False otherwise
+ def down(self, subkey_name=None):
+ ret_val = None
+ if subkey_name == None:
+ self._lock.acquire()
+ ret_val = regfi.regfi_iterator_down(self._iter)
+ else:
+ if name != None:
+ name = name.encode('utf-8')
+ self._lock.acquire()
+ ret_val = (regfi.regfi_iterator_find_subkey(self._iter, name)
+ and regfi.regfi_iterator_down(self._iter))
+
+ self._lock.release()
+ return ret_val
+
+
+ ## Causes the iterator to ascend to the current Key's parent
+ #
+ # @return True if successful, False otherwise
+ #
+ # @note The state of current subkeys and values at this level in the tree
+ # is lost as a side effect. That is, if you go up() and then back
+ # down() again, current_subkey() and current_value() will return
+ # default selections.
+ def up(self):
+ self._lock.acquire()
+ ret_val = regfi.regfi_iterator_up(self._iter)
+ self._lock.release()
+ return ret_val
+
+
+ ## Selects first subkey of current key
+ #
+ # @return A Key instance for the first subkey.
+ # None on error or if the current key has no subkeys.
+ def first_subkey(self):
+ ret_val = None
+ self._lock.acquire()
+ if regfi.regfi_iterator_first_subkey(self._iter):
+ ret_val = self.current_subkey()
+ self._lock.release()
+ return ret_val
+
+
+ ## Selects first value of current Key
+ #
+ # @return A Value instance for the first value.
+ # None on error or if the current key has no values.
+ def first_value(self):
+ ret_val = None
+ self._lock.acquire()
+ if regfi.regfi_iterator_first_value(self._iter):
+ ret_val = self.current_value()
+ self._lock.release()
+ return ret_val
+
+
+ ## Selects the next subkey in the current Key's list
+ #
+ # @return A Key instance for the next subkey.
+ # None if there are no remaining subkeys or an error occurred.
+ def next_subkey(self):
+ ret_val = None
+ self._lock.acquire()
+ if regfi.regfi_iterator_next_subkey(self._iter):
+ ret_val = self.current_subkey()
+ self._lock.release()
+ return ret_val
+
+
+ ## Selects the next value in the current Key's list
+ #
+ # @return A Value instance for the next value.
+ # None if there are no remaining values or an error occurred.
+ def next_value(self):
+ ret_val = None
+ self._lock.acquire()
+ if regfi.regfi_iterator_next_value(self._iter):
+ ret_val = self.current_value()
+ self._lock.release()
+ return ret_val
+
+
+ ## Selects the first subkey which has the specified name
+ #
+ # @return A Key instance for the selected key.
+ # None if it could not be located or an error occurred.
+ def find_subkey(self, name):
+ if name != None:
+ name = name.encode('utf-8')
+ ret_val = None
+ self._lock.acquire()
+ if regfi.regfi_iterator_find_subkey(self._iter, name):
+ ret_val = self.current_subkey()
+ self._lock.release()
+ return ret_val
+
+
+ ## Selects the first value which has the specified name
+ #
+ # @return A Value instance for the selected value.
+ # None if it could not be located or an error occurred.
+ def find_value(self, name):
+ if name != None:
+ name = name.encode('utf-8')
+ ret_val = None
+ self._lock.acquire()
+ if regfi.regfi_iterator_find_value(self._iter, name):
+ ret_val = self.current_value()
+ self._lock.release()
+ return ret_val
+
+ ## Retrieves the currently selected subkey
+ #
+ # @return A Key instance of the current subkey
+ def current_subkey(self):
+ self._lock.acquire()
+ ret_val = Key(self._hive, regfi.regfi_iterator_cur_subkey(self._iter))
+ self._lock.release()
+ return ret_val
+
+ ## Retrieves the currently selected value
+ #
+ # @return A Value instance of the current value
+ def current_value(self):
+ self._lock.acquire()
+ ret_val = Value(self._hive, regfi.regfi_iterator_cur_value(self._iter))
+ self._lock.release()
+ return ret_val
+
+ ## Retrieves the current key
+ #
+ # @return A Key instance of the current position of the iterator
+ def current_key(self):
+ self._lock.acquire()
+ ret_val = Key(self._hive, regfi.regfi_iterator_cur_key(self._iter))
+ self._lock.release()
+ return ret_val
+
+ ## Traverse downward multiple levels
+ #
+ # This is more efficient than calling down() multiple times
+ #
+ # @param path A list of Key names which represent the path to descend
+ #
+ # @exception Exception If path could not be located
+ def descend(self, path):
+ cpath = _strlist2charss(path)
+
+ self._lock.acquire()
+ result = regfi.regfi_iterator_descend(self._iter, cpath)
+ self._lock.release()
+ if not result:
+ # XXX: Use non-generic exception
+ raise Exception('Could not locate path.\n'+getLogMessages())
+
+ ## Obtains a list of the current key's ancestry
+ #
+ # @return A list of all parent keys starting with the root Key and ending
+ # with the current Key
+ def ancestry(self):
+ self._lock.acquire()
+ result = regfi.regfi_iterator_ancestry(self._iter)
+ self._lock.release()
+
+ ret_val = []
+ i = 0
+ k = result[i]
+ while k:
+ k = cast(regfi.regfi_reference_record(self._hive.file, k), POINTER(REGFI_NK))
+ ret_val.append(Key(self._hive, k))
+ i += 1
+ k = result[i]
+
+ regfi.regfi_free_record(self._hive.file, result)
+ return ret_val
+
+ ## Obtains the current path of the iterator
+ #
+ # @return A list of key names starting with the root up to and
+ # including the current key
+ #
+ def current_path(self):
+ ancestry = self.ancestry()
+ return [a.name for a in ancestry]
+
+
+# Freeing symbols defined for the sake of documentation
+del Value.name,Value.name_encoding,Value.name_raw,Value.offset,Value.data_size,Value.type,Value.flags
+del Key.name,Key.name_encoding,Key.name_raw,Key.offset,Key.modified,Key.flags
+del Hive.root,Hive.modified,Hive.sequence1,Hive.sequence2,Hive.major_version,Hive.minor_version
+del Security.ref_count,Security.offset,Security.descriptor
diff --git a/python/pyregfi/structures.py b/python/pyregfi/structures.py
new file mode 100644
index 0000000..7dec55a
--- /dev/null
+++ b/python/pyregfi/structures.py
@@ -0,0 +1,368 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2010-2011 Timothy D. Morgan
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 3 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# $Id: $
+
+## @package pyregfi.structures
+# Low-level data structures and C API mappings.
+#
+# Most users need not venture here. For more information, see the source.
+
+import sys
+import os
+import traceback
+import ctypes
+import ctypes.util
+from ctypes import *
+
+is_win32 = hasattr(ctypes, 'windll')
+
+# XXX: can we always be sure enums are this size?
+REGFI_ENCODING = c_uint32
+REGFI_ENCODING_UTF8 = REGFI_ENCODING(1)
+
+REGFI_NK_FLAG_ASCIINAME = 0x0020
+REGFI_VK_FLAG_ASCIINAME = 0x0001
+
+REGFI_DATA_TYPE = c_uint32
+REGFI_NTTIME = c_uint64
+
+REGFI_REGF_SIZE = 0x1000
+
+# Prototype everything first so we don't have to worry about reference order
+class REGFI_VK(Structure):
+ pass
+
+class REGFI_SK(Structure):
+ pass
+
+class REGFI_SUBKEY_LIST(Structure):
+ pass
+
+class REGFI_VALUE_LIST(Structure):
+ pass
+
+class REGFI_CLASSNAME(Structure):
+ pass
+
+class REGFI_DATA(Structure):
+ pass
+
+class REGFI_NK(Structure):
+ pass
+
+class REGFI_ITERATOR(Structure):
+ pass
+
+class REGFI_FILE(Structure):
+ pass
+
+class REGFI_RAW_FILE(Structure):
+ fh = None
+
+ def cb_seek(self, raw_file, offset, whence):
+ try:
+ self.fh.seek(offset, whence)
+ except Exception:
+ traceback.print_exc()
+ set_errno(74) # os.EX_IOERR
+ return -1
+
+ return self.fh.tell()
+
+
+ def cb_read(self, raw_file, buf, count):
+ try:
+ # XXX: anyway to do a readinto() here?
+ tmp = self.fh.read(count)
+ memmove(buf,tmp,len(tmp))
+
+ except Exception:
+ traceback.print_exc()
+ set_errno(74) # os.EX_IOERR
+ return -1
+ return len(tmp)
+
+
+# Load libregfi according to platform
+regfi = None
+if is_win32:
+ # XXX: Using C calling conventions on cross-compiled DLLs seems to work fine
+ # on Windows, but I'm not sure if stdcall symbols are supported
+ # correctly for native Windows binaries...
+ #regfi = ctypes.windll.libregfi
+ #CB_FACTORY = ctypes.WINFUNCTYPE
+ regfi = ctypes.CDLL('libregfi.dll', use_errno=True)
+ CB_FACTORY = ctypes.CFUNCTYPE
+else:
+ regfi = ctypes.CDLL(ctypes.util.find_library('regfi'), use_errno=True)
+ CB_FACTORY = ctypes.CFUNCTYPE
+
+seek_cb_type = CB_FACTORY(c_int64, POINTER(REGFI_RAW_FILE), c_uint64, c_int, use_errno=True)
+read_cb_type = CB_FACTORY(c_int64, POINTER(REGFI_RAW_FILE), POINTER(c_char), c_size_t, use_errno=True)
+
+
+from .winsec import *
+
+REGFI_VK._fields_ = [('offset', c_uint32),
+ ('cell_size', c_uint32),
+ ('name', c_char_p),
+ ('name_raw', POINTER(c_char)),
+ ('name_length', c_uint16),
+ ('hbin_off', c_uint32),
+ ('data_size', c_uint32),
+ ('data_off', c_uint32),
+ ('type', REGFI_DATA_TYPE),
+ ('magic', c_char * 2),
+ ('flags', c_uint16),
+ ('unknown1', c_uint16),
+ ('data_in_offset', c_bool),
+ ]
+
+
+REGFI_SK._fields_ = [('offset', c_uint32),
+ ('cell_size', c_uint32),
+ ('sec_desc', POINTER(WINSEC_DESC)),
+ ('hbin_off', c_uint32),
+ ('prev_sk_off', c_uint32),
+ ('next_sk_off', c_uint32),
+ ('ref_count', c_uint32),
+ ('desc_size', c_uint32),
+ ('unknown_tag', c_uint16),
+ ('magic', c_char * 2),
+ ]
+
+
+REGFI_NK._fields_ = [('offset', c_uint32),
+ ('cell_size', c_uint32),
+ ('values', POINTER(REGFI_VALUE_LIST)),
+ ('subkeys', POINTER(REGFI_SUBKEY_LIST)),
+ ('flags', c_uint16),
+ ('magic', c_char * 2),
+ ('mtime', REGFI_NTTIME),
+ ('name_length', c_uint16),
+ ('classname_length', c_uint16),
+ ('name', c_char_p),
+ ('name_raw', POINTER(c_char)),
+ ('parent_off', c_uint32),
+ ('classname_off', c_uint32),
+ ('max_bytes_subkeyname', c_uint32),
+ ('max_bytes_subkeyclassname', c_uint32),
+ ('max_bytes_valuename', c_uint32),
+ ('max_bytes_value', c_uint32),
+ ('unknown1', c_uint32),
+ ('unknown2', c_uint32),
+ ('unknown3', c_uint32),
+ ('unk_index', c_uint32),
+ ('num_subkeys', c_uint32),
+ ('subkeys_off', c_uint32),
+ ('num_values', c_uint32),
+ ('values_off', c_uint32),
+ ('sk_off', c_uint32),
+ ]
+
+
+REGFI_SUBKEY_LIST._fields_ = [('offset', c_uint32),
+ ('cell_size', c_uint32),
+ ('num_children', c_uint32),
+ ('num_keys', c_uint32),
+ ('elements', c_void_p),
+ ('magic', c_char * 2),
+ ('recursive_type', c_bool),
+ ]
+
+
+REGFI_VALUE_LIST._fields_ = [('offset', c_uint32),
+ ('cell_size', c_uint32),
+ ('num_values', c_uint32),
+ ('elements', c_void_p),
+ ]
+
+REGFI_CLASSNAME._fields_ = [('offset', c_uint32),
+ ('interpreted', c_char_p),
+ ('raw', POINTER(c_char)),
+ ('size', c_uint16),
+ ]
+
+
+class REGFI_DATA__interpreted(Union):
+ _fields_ = [('none',POINTER(c_char)),
+ ('string', c_char_p),
+ ('expand_string', c_char_p),
+ ('binary',POINTER(c_char)),
+ ('dword', c_uint32),
+ ('dword_be', c_uint32),
+ ('link', c_char_p),
+ ('multiple_string', POINTER(c_char_p)),
+ ('qword', c_uint64),
+ ('resource_list',POINTER(c_char)),
+ ('full_resource_descriptor',POINTER(c_char)),
+ ('resource_requirements_list',POINTER(c_char)),
+ ]
+REGFI_DATA._fields_ = [('offset', c_uint32),
+ ('type', REGFI_DATA_TYPE),
+ ('size', c_uint32),
+ ('raw', POINTER(c_char)),
+ ('interpreted_size', c_uint32),
+ ('interpreted', REGFI_DATA__interpreted),
+ ]
+
+
+REGFI_FILE._fields_ = [('magic', c_char * 4),
+ ('sequence1', c_uint32),
+ ('sequence2', c_uint32),
+ ('mtime', REGFI_NTTIME),
+ ('major_version', c_uint32),
+ ('minor_version', c_uint32),
+ ('type', c_uint32),
+ ('format', c_uint32),
+ ('root_cell', c_uint32),
+ ('last_block', c_uint32),
+ ('cluster', c_uint32),
+ ]
+
+
+REGFI_RAW_FILE._fields_ = [('seek', seek_cb_type),
+ ('read', read_cb_type),
+ ('cur_off', c_uint64),
+ ('size', c_uint64),
+ ('state', c_void_p),
+ ]
+
+
+# Define function prototypes
+regfi.regfi_version.argtypes = []
+regfi.regfi_version.restype = c_char_p
+
+regfi.regfi_alloc.argtypes = [c_int, REGFI_ENCODING]
+regfi.regfi_alloc.restype = POINTER(REGFI_FILE)
+
+regfi.regfi_alloc_cb.argtypes = [POINTER(REGFI_RAW_FILE), REGFI_ENCODING]
+regfi.regfi_alloc_cb.restype = POINTER(REGFI_FILE)
+
+regfi.regfi_free.argtypes = [POINTER(REGFI_FILE)]
+regfi.regfi_free.restype = None
+
+regfi.regfi_log_get_str.argtypes = []
+regfi.regfi_log_get_str.restype = c_char_p
+
+regfi.regfi_log_set_mask.argtypes = [c_uint16]
+regfi.regfi_log_set_mask.restype = c_bool
+
+regfi.regfi_get_rootkey.argtypes = [POINTER(REGFI_FILE)]
+regfi.regfi_get_rootkey.restype = POINTER(REGFI_NK)
+
+regfi.regfi_free_record.argtypes = [POINTER(REGFI_FILE), c_void_p]
+regfi.regfi_free_record.restype = None
+
+regfi.regfi_reference_record.argtypes = [POINTER(REGFI_FILE), c_void_p]
+regfi.regfi_reference_record.restype = c_void_p
+
+regfi.regfi_fetch_num_subkeys.argtypes = [POINTER(REGFI_NK)]
+regfi.regfi_fetch_num_subkeys.restype = c_uint32
+
+regfi.regfi_fetch_num_values.argtypes = [POINTER(REGFI_NK)]
+regfi.regfi_fetch_num_values.restype = c_uint32
+
+regfi.regfi_fetch_classname.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK)]
+regfi.regfi_fetch_classname.restype = POINTER(REGFI_CLASSNAME)
+
+regfi.regfi_fetch_sk.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK)]
+regfi.regfi_fetch_sk.restype = POINTER(REGFI_SK)
+
+regfi.regfi_next_sk.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_SK)]
+regfi.regfi_next_sk.restype = POINTER(REGFI_SK)
+
+regfi.regfi_prev_sk.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_SK)]
+regfi.regfi_prev_sk.restype = POINTER(REGFI_SK)
+
+regfi.regfi_fetch_data.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_VK)]
+regfi.regfi_fetch_data.restype = POINTER(REGFI_DATA)
+
+regfi.regfi_find_subkey.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK),
+ c_char_p, POINTER(c_uint32)]
+regfi.regfi_find_subkey.restype = c_bool
+
+regfi.regfi_find_value.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK),
+ c_char_p, POINTER(c_uint32)]
+regfi.regfi_find_value.restype = c_bool
+
+regfi.regfi_get_subkey.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK),
+ c_uint32]
+regfi.regfi_get_subkey.restype = POINTER(REGFI_NK)
+
+regfi.regfi_get_value.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK),
+ c_uint32]
+regfi.regfi_get_value.restype = POINTER(REGFI_VK)
+
+regfi.regfi_get_parentkey.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK)]
+regfi.regfi_get_parentkey.restype = POINTER(REGFI_NK)
+
+regfi.regfi_nt2unix_time.argtypes = [REGFI_NTTIME]
+regfi.regfi_nt2unix_time.restype = c_double
+
+regfi.regfi_iterator_new.argtypes = [POINTER(REGFI_FILE)]
+regfi.regfi_iterator_new.restype = POINTER(REGFI_ITERATOR)
+
+regfi.regfi_iterator_free.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_free.restype = None
+
+regfi.regfi_iterator_down.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_down.restype = c_bool
+
+regfi.regfi_iterator_up.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_up.restype = c_bool
+
+regfi.regfi_iterator_to_root.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_to_root.restype = c_bool
+
+regfi.regfi_iterator_descend.argtypes = [POINTER(REGFI_ITERATOR), POINTER(c_char_p)]
+regfi.regfi_iterator_descend.restype = c_bool
+
+regfi.regfi_iterator_cur_key.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_cur_key.restype = POINTER(REGFI_NK)
+
+regfi.regfi_iterator_first_subkey.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_first_subkey.restype = c_bool
+
+regfi.regfi_iterator_cur_subkey.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_cur_subkey.restype = POINTER(REGFI_NK)
+
+regfi.regfi_iterator_next_subkey.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_next_subkey.restype = c_bool
+
+regfi.regfi_iterator_find_subkey.argtypes = [POINTER(REGFI_ITERATOR), c_char_p]
+regfi.regfi_iterator_find_subkey.restype = c_bool
+
+regfi.regfi_iterator_first_value.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_first_value.restype = c_bool
+
+regfi.regfi_iterator_cur_value.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_cur_value.restype = POINTER(REGFI_VK)
+
+regfi.regfi_iterator_next_value.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_next_value.restype = c_bool
+
+regfi.regfi_iterator_find_value.argtypes = [POINTER(REGFI_ITERATOR), c_char_p]
+regfi.regfi_iterator_find_value.restype = c_bool
+
+regfi.regfi_iterator_ancestry.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_ancestry.restype = POINTER(POINTER(REGFI_NK))
+
+regfi.regfi_init.argtypes = []
+regfi.regfi_init.restype = None
+regfi.regfi_init()
diff --git a/python/pyregfi/winsec.py b/python/pyregfi/winsec.py
new file mode 100644
index 0000000..bdce699
--- /dev/null
+++ b/python/pyregfi/winsec.py
@@ -0,0 +1,201 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2011 Timothy D. Morgan
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 3 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# $Id: $
+
+## @package pyregfi.winsec
+# Low-level data structures for winsec library
+#
+
+import sys
+import os
+import uuid
+import ctypes
+import ctypes.util
+from ctypes import *
+from .structures import regfi
+
+is_win32 = hasattr(ctypes, 'windll')
+WINSEC_MAX_SUBAUTHS = 15
+
+if is_win32:
+ libc = cdll.msvcrt
+else:
+ libc = cdll.LoadLibrary("libc.so.6")
+
+class WINSEC_UUID(Structure):
+ pass
+
+class WINSEC_DOM_SID(Structure):
+ pass
+
+class WINSEC_ACE(Structure):
+ pass
+
+class WINSEC_ACL(Structure):
+ pass
+
+class WINSEC_DESC(Structure):
+ pass
+
+WINSEC_UUID._fields_ = [('time_low', c_uint32),
+ ('time_mid', c_uint16),
+ ('time_hi_and_version', c_uint16),
+ ('clock_seq', c_uint8*2),
+ ('node', c_uint8*6),
+ ]
+
+WINSEC_DOM_SID._fields_ = [('sid_rev_num', c_uint8),
+ ('num_auths', c_uint8),
+ ('id_auths', c_uint8*6),
+ ('sub_auths', c_uint32*WINSEC_MAX_SUBAUTHS),
+ ]
+
+WINSEC_ACE._fields_ = [('type', c_uint8),
+ ('flags', c_uint8),
+ ('size', c_uint16),
+ ('access_mask', c_uint32),
+ ('obj_flags', c_uint32),
+ ('obj_guid', POINTER(WINSEC_UUID)),
+ ('inh_guid', POINTER(WINSEC_UUID)),
+ ('trustee', POINTER(WINSEC_DOM_SID)),
+ ]
+
+WINSEC_ACL._fields_ = [('revision', c_uint16),
+ ('size', c_uint16),
+ ('num_aces', c_uint32),
+ ('aces', POINTER(POINTER(WINSEC_ACE))),
+ ]
+
+WINSEC_DESC._fields_ = [('revision', c_uint8),
+ ('sbz1', c_uint8),
+ ('control', c_uint16),
+ ('off_owner_sid', c_uint32),
+ ('off_grp_sid', c_uint32),
+ ('off_sacl', c_uint32),
+ ('off_dacl', c_uint32),
+ ('owner_sid', POINTER(WINSEC_DOM_SID)),
+ ('grp_sid', POINTER(WINSEC_DOM_SID)),
+ ('sacl', POINTER(WINSEC_ACL)),
+ ('dacl', POINTER(WINSEC_ACL)),
+ ]
+regfi.winsec_sid2str.argtypes = [POINTER(WINSEC_DOM_SID)]
+regfi.winsec_sid2str.restype = POINTER(c_char)
+
+
+def _guid2uuid(guid):
+ if not guid:
+ return None
+ return uuid.UUID(fields=(guid.contents.time_low,
+ guid.contents.time_mid,
+ guid.contents.time_hi_and_version,
+ guid.contents.clock_seq[0],
+ guid.contents.clock_seq[1],
+ guid.contents.node[0]<<40
+ ^ guid.contents.node[1]<<32
+ ^ guid.contents.node[2]<<24
+ ^ guid.contents.node[3]<<16
+ ^ guid.contents.node[4]<<8
+ ^ guid.contents.node[5]))
+
+## Represents a Microsoft access control entry, which are elements of access
+# control lists. For more information, see:
+# http://msdn.microsoft.com/en-us/library/aa374868%28v=vs.85%29.aspx
+#
+# @note
+# This interface is subject to change
+class ACE(object):
+ ## The type of entry as an integer
+ type = 1234
+
+ ## The flags as an integer
+ flags = 0x1234
+
+ ## The access mask/permissions as an integer
+ access_mask = 0x1234
+
+ ## The trustee's SID as a string
+ trustee = "S-1-2..."
+
+ ## The object GUID as a Python UUID
+ # May be None
+ object = uuid.UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678))
+
+ ## The inherited object GUID as a Python UUID
+ # May be None
+ inherited_object = uuid.UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678))
+
+ def __init__(self, ace):
+ # Just copy all of the values out so we don't need to manage memory
+ self.object = _guid2uuid(ace.obj_guid)
+ self.inherited_object = _guid2uuid(ace.inh_guid)
+
+ c_str = regfi.winsec_sid2str(ace.trustee)
+ self.trustee = ctypes.cast(c_str, c_char_p).value.decode('utf-8', 'replace')
+ libc.free(c_str)
+
+ self.type = int(ace.type)
+ self.flags = int(ace.flags)
+ self.access_mask = int(ace.access_mask)
+
+
+## A Microsoft security descriptor
+# For more information, see:
+# http://msdn.microsoft.com/en-us/library/aa379563%28v=vs.85%29.aspx
+#
+class SecurityDescriptor(object):
+ ## The security descriptor's owner SID, as a string
+ owner = "S-1-2-..."
+
+ ## The security descriptor's group SID, as a string
+ group = "S-1-2-..."
+
+ ## The system access control list represented as a list of @ref ACE objects.
+ #
+ # Is set to None if a sacl isn't defined
+ sacl = []
+
+ ## The discretionary access control list represented as a list of @ref ACE objects
+ #
+ # Is set to None if a dacl isn't defined
+ dacl = []
+
+ def __init__(self, sec_desc):
+ c_str = regfi.winsec_sid2str(sec_desc.owner_sid)
+ self.owner = ctypes.cast(c_str, c_char_p).value.decode('utf-8', 'replace')
+ libc.free(c_str)
+
+ c_str = regfi.winsec_sid2str(sec_desc.grp_sid)
+ self.group = ctypes.cast(c_str, c_char_p).value.decode('utf-8', 'replace')
+ libc.free(c_str)
+
+ self.sacl = None
+ if sec_desc.sacl:
+ self.sacl = []
+ for i in range(0,sec_desc.sacl.contents.num_aces):
+ self.sacl.append(ACE(sec_desc.sacl.contents.aces[i].contents))
+
+ self.dacl = None
+ if sec_desc.dacl:
+ self.dacl = []
+ for i in range(0,sec_desc.dacl.contents.num_aces):
+ self.dacl.append(ACE(sec_desc.dacl.contents.aces[i].contents))
+
+
+# Free class objects used for documentation
+del ACE.type,ACE.flags,ACE.access_mask,ACE.object,ACE.inherited_object
+del SecurityDescriptor.owner,SecurityDescriptor.group,SecurityDescriptor.sacl,SecurityDescriptor.dacl
diff --git a/regfi_version.py b/regfi_version.py
new file mode 100644
index 0000000..30e9180
--- /dev/null
+++ b/regfi_version.py
@@ -0,0 +1 @@
+REGFI_VERSION="1.0.1"
diff --git a/src/Makefile b/src/Makefile
deleted file mode 100644
index 880485d..0000000
--- a/src/Makefile
+++ /dev/null
@@ -1,28 +0,0 @@
-# $Id: Makefile 143 2009-02-13 03:24:27Z tim $
-
-################################################################################
-
-REGLOOKUP=$(BUILD_BIN)/reglookup$(BIN_EXT)
-REGLOOKUP_RECOVER=$(BUILD_BIN)/reglookup-recover$(BIN_EXT)
-OBJ=$(wildcard ../lib/*.o)
-FILES=$(REGLOOKUP) $(REGLOOKUP_RECOVER)
-
-all: $(FILES)
-
-$(REGLOOKUP): reglookup.o $(OBJ)
- $(CC) $(CFLAGS) $(OPTS) $(LIB) -o $@ reglookup.o $(OBJ) $(EXTRA_OBJ)
-
-$(REGLOOKUP_RECOVER): reglookup-recover.o $(OBJ)
- $(CC) $(CFLAGS) $(OPTS) $(LIB) -o $@ reglookup-recover.o $(OBJ) $(EXTRA_OBJ)
-
-reglookup.o: reglookup.c
- $(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ reglookup.c
-
-reglookup-recover.o: reglookup-recover.c
- $(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ reglookup-recover.c
-
-install:
- install -m 0755 $(FILES) $(BIN_PREFIX)
-
-clean:
- rm -f *.o
diff --git a/src/common.c b/src/common.c
index a0e40e5..99f58aa 100644
--- a/src/common.c
+++ b/src/common.c
@@ -2,7 +2,7 @@
* This file stores code common to the command line tools.
* XXX: This should be converted to a proper library.
*
- * Copyright (C) 2005-2008 Timothy D. Morgan
+ * Copyright (C) 2005-2008,2011 Timothy D. Morgan
* Copyright (C) 2002 Richard Sharpe, rsharpe at richardsharpe.com
*
* This program is free software; you can redistribute it and/or modify
@@ -18,17 +18,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: common.c 172 2010-03-08 03:04:34Z tim $
+ * $Id: common.c 256 2011-06-15 22:05:37Z tim $
*/
#include <iconv.h>
iconv_t conv_desc;
-const char* key_special_chars = ",\"\\/";
-const char* subfield_special_chars = ",\"\\|";
-const char* common_special_chars = ",\"\\";
-
-#define REGLOOKUP_VERSION "0.12.0"
+const char* key_special_chars = ",\"/";
+const char* subfield_special_chars = ",\"|";
+const char* common_special_chars = ",\"";
#define REGLOOKUP_EXIT_OK 0
#define REGLOOKUP_EXIT_OSERR 71
@@ -37,15 +35,23 @@ const char* common_special_chars = ",\"\\";
#define REGLOOKUP_EXIT_NOINPUT 66
+/* Windows is lame */
+#ifdef O_BINARY
+#define REGLOOKUP_OPEN_FLAGS O_RDONLY|O_BINARY
+#else
+#define REGLOOKUP_OPEN_FLAGS O_RDONLY
+#endif
+
+
void bailOut(int code, char* message)
{
- fprintf(stderr, message);
+ fprintf(stderr, "%s", message);
exit(code);
}
-void printMsgs(REGFI_FILE* f)
+void printMsgs()
{
- char* msgs = regfi_get_messages(f);
+ char* msgs = regfi_log_get_str();
if(msgs != NULL)
{
fprintf(stderr, "%s", msgs);
@@ -53,9 +59,9 @@ void printMsgs(REGFI_FILE* f)
}
}
-void clearMsgs(REGFI_FILE* f)
+void clearMsgs()
{
- char* msgs = regfi_get_messages(f);
+ char* msgs = regfi_log_get_str();
if(msgs != NULL)
free(msgs);
}
@@ -63,7 +69,7 @@ void clearMsgs(REGFI_FILE* f)
/* Returns a newly malloc()ed string which contains original buffer,
* except for non-printable or special characters are quoted in hex
- * with the syntax '\xQQ' where QQ is the hex ascii value of the quoted
+ * with the syntax '%QQ' where QQ is the hex ascii value of the quoted
* character. A null terminator is added, since only ascii, not binary,
* is returned.
*/
@@ -112,10 +118,11 @@ static char* quote_buffer(const unsigned char* str,
ret_val = tmp_buf;
}
- if(str[i] < 32 || str[i] > 126 || strchr(special, str[i]) != NULL)
+ if(str[i] < 32 || str[i] > 126
+ || str[i] == '%' || strchr(special, str[i]) != NULL)
{
num_written += snprintf(ret_val + num_written, buf_len - num_written,
- "\\x%.2X", str[i]);
+ "%%%.2X", str[i]);
}
else
ret_val[num_written++] = str[i];
@@ -128,7 +135,7 @@ static char* quote_buffer(const unsigned char* str,
/* Returns a newly malloc()ed string which contains original string,
* except for non-printable or special characters are quoted in hex
- * with the syntax '\xQQ' where QQ is the hex ascii value of the quoted
+ * with the syntax '%QQ' where QQ is the hex ascii value of the quoted
* character.
*/
static char* quote_string(const char* str, const char* special)
@@ -153,7 +160,7 @@ static char* quote_string(const char* str, const char* special)
* is the responsibility of the caller to free both a non-NULL return
* value, and a non-NULL (*error_msg).
*/
-static char* data_to_ascii(REGFI_DATA* data, char** error_msg)
+static char* data_to_ascii(const REGFI_DATA* data, char** error_msg)
{
char* ret_val;
char* cur_quoted;
@@ -250,7 +257,7 @@ static char* data_to_ascii(REGFI_DATA* data, char** error_msg)
{
cur_quoted = quote_string((char*)data->interpreted.multiple_string[i],
subfield_special_chars);
- if(cur_quoted != NULL && cur_quoted[0] != '\0')
+ if(cur_quoted != NULL)
{
tmp_len = snprintf(tmp_ptr, ret_val_left, "%s%s",delim, cur_quoted);
tmp_ptr += tmp_len;
@@ -311,28 +318,58 @@ static char* data_to_ascii(REGFI_DATA* data, char** error_msg)
}
-static char* get_quoted_keyname(const REGFI_NK_REC* nk)
+static char* get_quoted_keyname(const REGFI_NK* nk)
{
char* ret_val;
- if(nk->keyname == NULL)
- ret_val = quote_buffer(nk->keyname_raw, nk->name_length, key_special_chars);
+ if(nk->name == NULL)
+ ret_val = quote_buffer(nk->name_raw, nk->name_length, key_special_chars);
else
- ret_val = quote_string(nk->keyname, key_special_chars);
+ ret_val = quote_string(nk->name, key_special_chars);
return ret_val;
}
-static char* get_quoted_valuename(const REGFI_VK_REC* vk)
+static char* get_quoted_valuename(const REGFI_VK* vk)
{
char* ret_val;
- if(vk->valuename == NULL)
- ret_val = quote_buffer(vk->valuename_raw, vk->name_length,
+ if(vk->name == NULL)
+ ret_val = quote_buffer(vk->name_raw, vk->name_length,
key_special_chars);
else
- ret_val = quote_string(vk->valuename, key_special_chars);
+ ret_val = quote_string(vk->name, key_special_chars);
+
+ return ret_val;
+}
+
+
+int openHive(const char* filename)
+{
+ int ret_val;
+
+ /* open an existing file */
+ if ((ret_val = open(filename, REGLOOKUP_OPEN_FLAGS)) == -1)
+ {
+ fprintf(stderr, "ERROR: Failed to open hive. Error returned: %s\n",
+ strerror(errno));
+ return -1;
+ }
return ret_val;
}
+
+
+void formatTime(REGFI_NTTIME nttime, char* output)
+{
+ time_t tmp_time[1];
+ struct tm* tmp_time_s = NULL;
+
+ *tmp_time = (time_t)regfi_nt2unix_time(nttime);
+ tmp_time_s = gmtime(tmp_time);
+ strftime(output,
+ (4+1+2+1+2)+1+(2+1+2+1+2)+1,
+ "%Y-%m-%d %H:%M:%S",
+ tmp_time_s);
+}
diff --git a/src/reglookup-recover.c b/src/reglookup-recover.c
index 0cd06f2..6e17895 100644
--- a/src/reglookup-recover.c
+++ b/src/reglookup-recover.c
@@ -16,7 +16,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: reglookup-recover.c 173 2010-03-08 03:39:09Z tim $
+ * $Id: reglookup-recover.c 273 2011-07-22 02:05:25Z tim $
*/
#include <stdio.h>
@@ -34,18 +34,18 @@ bool print_security = false;
bool print_header = true;
bool print_leftover = false;
bool print_parsedraw = false;
-char* registry_file = NULL;
+const char* registry_file = NULL;
#include "common.c"
-char* getQuotedData(int fd, uint32_t offset, uint32_t length)
+char* getQuotedData(REGFI_RAW_FILE* file_cb, uint32_t offset, uint32_t length)
{
uint8_t* buf;
char* quoted_buf;
uint32_t len;
- if((lseek(fd, offset, SEEK_SET)) == -1)
+ if((regfi_seek(file_cb, offset, SEEK_SET)) == -1)
return NULL;
buf = (uint8_t*)malloc(length);
@@ -53,7 +53,7 @@ char* getQuotedData(int fd, uint32_t offset, uint32_t length)
return NULL;
len = length;
- if((regfi_read(fd, buf, &length) != 0) || length != len)
+ if((regfi_read(file_cb, buf, &length) != 0) || length != len)
{
free(buf);
return NULL;
@@ -66,20 +66,16 @@ char* getQuotedData(int fd, uint32_t offset, uint32_t length)
}
/* XXX: Somewhere in here, need to start looking for and handling classnames */
-void printKey(REGFI_FILE* f, REGFI_NK_REC* nk, const char* prefix)
+void printKey(REGFI_FILE* f, REGFI_NK* nk, const char* prefix)
{
- char mtime[20];
- time_t tmp_time[1];
- struct tm* tmp_time_s = NULL;
+ char mtime[24];
char* quoted_name = NULL;
char* quoted_raw = "";
- *tmp_time = regfi_nt2unix_time(&nk->mtime);
- tmp_time_s = gmtime(tmp_time);
- strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s);
-
+ formatTime(nk->mtime, mtime);
+
/* XXX: Add command line option to choose output encoding */
- regfi_interpret_keyname(f, nk, REGFI_ENCODING_ASCII, true);
+ regfi_interpret_keyname(f, nk, true);
quoted_name = get_quoted_keyname(nk);
if (quoted_name == NULL)
@@ -95,7 +91,7 @@ void printKey(REGFI_FILE* f, REGFI_NK_REC* nk, const char* prefix)
}
if(print_parsedraw)
- quoted_raw = getQuotedData(f->fd, nk->offset, nk->cell_size);
+ quoted_raw = getQuotedData(f->cb, nk->offset, nk->cell_size);
printf("%.8X,%.8X,KEY,%s,%s,%s,%d,,,,,,,,%s\n", nk->offset, nk->cell_size,
prefix, quoted_name, mtime, nk->num_values, quoted_raw);
@@ -106,7 +102,7 @@ void printKey(REGFI_FILE* f, REGFI_NK_REC* nk, const char* prefix)
}
-void printValue(REGFI_FILE* f, REGFI_VK_REC* vk, const char* prefix)
+void printValue(REGFI_FILE* f, REGFI_VK* vk, const char* prefix)
{
char* quoted_value = NULL;
char* quoted_name = NULL;
@@ -115,7 +111,7 @@ void printValue(REGFI_FILE* f, REGFI_VK_REC* vk, const char* prefix)
const char* str_type = NULL;
/* XXX: Add command line option to choose output encoding */
- regfi_interpret_valuename(f, vk, REGFI_ENCODING_ASCII, true);
+ regfi_interpret_valuename(f, vk, true);
quoted_name = get_quoted_valuename(vk);
if (quoted_name == NULL)
@@ -132,7 +128,7 @@ void printValue(REGFI_FILE* f, REGFI_VK_REC* vk, const char* prefix)
/* XXX: Add command line option to choose output encoding */
if(vk->data != NULL
- && !regfi_interpret_data(f, REGFI_ENCODING_ASCII, vk->type, vk->data))
+ && !regfi_interpret_data(f, vk->type, vk->data))
{
fprintf(stderr, "WARN: Error occurred while interpreting data for VK record"
" at offset 0x%.8X.\n", vk->offset);
@@ -161,7 +157,7 @@ void printValue(REGFI_FILE* f, REGFI_VK_REC* vk, const char* prefix)
if(print_parsedraw)
- quoted_raw = getQuotedData(f->fd, vk->offset, vk->cell_size);
+ quoted_raw = getQuotedData(f->cb, vk->offset, vk->cell_size);
str_type = regfi_type_val2str(vk->type);
if(str_type == NULL)
@@ -184,7 +180,7 @@ void printValue(REGFI_FILE* f, REGFI_VK_REC* vk, const char* prefix)
}
-void printSK(REGFI_FILE* f, REGFI_SK_REC* sk)
+void printSK(REGFI_FILE* f, REGFI_SK* sk)
{
char* quoted_raw = NULL;
char* empty_str = "";
@@ -194,7 +190,7 @@ void printSK(REGFI_FILE* f, REGFI_SK_REC* sk)
char* dacl = regfi_get_dacl(sk->sec_desc);
if(print_parsedraw)
- quoted_raw = getQuotedData(f->fd, sk->offset, sk->cell_size);
+ quoted_raw = getQuotedData(f->cb, sk->offset, sk->cell_size);
if(owner == NULL)
owner = empty_str;
@@ -228,10 +224,10 @@ int printCell(REGFI_FILE* f, uint32_t offset)
uint32_t cell_length;
bool unalloc;
- if(!regfi_parse_cell(f->fd, offset, NULL, 0, &cell_length, &unalloc))
+ if(!regfi_parse_cell(f->cb, offset, NULL, 0, &cell_length, &unalloc))
return 1;
- quoted_buf = getQuotedData(f->fd, offset, cell_length);
+ quoted_buf = getQuotedData(f->cb, offset, cell_length);
if(quoted_buf == NULL)
return 2;
@@ -249,10 +245,10 @@ int printCell(REGFI_FILE* f, uint32_t offset)
/* XXX: This is not terribly efficient, as it may reparse many keys
* repeatedly. Should try to add caching.
*/
-char* getParentPath(REGFI_FILE* f, REGFI_NK_REC* nk)
+char* getParentPath(REGFI_FILE* f, REGFI_NK* nk)
{
void_stack* path_stack = void_stack_new(REGFI_MAX_DEPTH);
- REGFI_NK_REC* cur_ancestor;
+ REGFI_NK* cur_ancestor;
char* ret_val;
uint32_t virt_offset, i, stack_size, ret_val_size, ret_val_used, offset;
int32_t max_size;
@@ -285,7 +281,7 @@ char* getParentPath(REGFI_FILE* f, REGFI_NK_REC* nk)
if(path_element != NULL)
{
/* XXX: Add command line option to choose output encoding */
- regfi_interpret_keyname(f, cur_ancestor, REGFI_ENCODING_ASCII, true);
+ regfi_interpret_keyname(f, cur_ancestor, true);
path_element->buf = (uint8_t*)get_quoted_keyname(cur_ancestor);
}
@@ -294,7 +290,7 @@ char* getParentPath(REGFI_FILE* f, REGFI_NK_REC* nk)
|| !void_stack_push(path_stack, path_element))
{
/* XXX: Need to add a warning here */
- regfi_free_key(cur_ancestor);
+ regfi_free_record(f, cur_ancestor);
void_stack_free(path_stack);
return NULL;
}
@@ -306,7 +302,7 @@ char* getParentPath(REGFI_FILE* f, REGFI_NK_REC* nk)
path_element->len = strlen((char*)path_element->buf);
ret_val_size += path_element->len + 1;
- regfi_free_key(cur_ancestor);
+ regfi_free_record(f, cur_ancestor);
}
}
}
@@ -339,7 +335,7 @@ char* getParentPath(REGFI_FILE* f, REGFI_NK_REC* nk)
static void usage(void)
{
fprintf(stderr, "Usage: reglookup-recover [options] <REGISTRY_FILE>\n");
- fprintf(stderr, "Version: %s\n", REGLOOKUP_VERSION);
+ fprintf(stderr, "Version: %s\n", regfi_version());
fprintf(stderr, "Options:\n");
fprintf(stderr, "\t-v\t sets verbose mode.\n");
fprintf(stderr, "\t-h\t enables header row. (default)\n");
@@ -413,7 +409,7 @@ int extractVKs(REGFI_FILE* f,
range_list* unalloc_values)
{
const range_list_element* cur_elem;
- REGFI_VK_REC* vk;
+ REGFI_VK* vk;
uint32_t i, j;
for(i=0; i < range_list_size(unalloc_cells); i++)
@@ -456,7 +452,7 @@ int extractDataCells(REGFI_FILE* file,
range_list* unalloc_values)
{
const range_list_element* cur_elem;
- REGFI_VK_REC* vk;
+ REGFI_VK* vk;
range_list* bd_cells;
REGFI_BUFFER data;
uint32_t i, j, offset, cell_length, length;
@@ -472,7 +468,7 @@ int extractDataCells(REGFI_FILE* file,
for(i=0; i<range_list_size(unalloc_values); i++)
{
cur_elem = range_list_get(unalloc_values, i);
- vk = (REGFI_VK_REC*)cur_elem->data;
+ vk = (REGFI_VK*)cur_elem->data;
if(vk == NULL)
return 11;
@@ -489,7 +485,7 @@ int extractDataCells(REGFI_FILE* file,
{
max_size = regfi_calc_maxsize(file, offset);
if(max_size >= 0
- && regfi_parse_cell(file->fd, offset, NULL, 0,
+ && regfi_parse_cell(file->cb, offset, NULL, 0,
&cell_length, &unalloc)
&& (cell_length & 0x00000007) == 0
&& cell_length <= max_size)
@@ -576,7 +572,7 @@ int extractDataCells(REGFI_FILE* file,
* vk->data item can be removed from the structure.
*/
vk->data = regfi_buffer_to_data(data);
- talloc_steal(vk, vk->data);
+ talloc_reparent(NULL, vk, vk->data);
}
}
@@ -591,7 +587,7 @@ int extractKeys(REGFI_FILE* f,
range_list* unalloc_keys)
{
const range_list_element* cur_elem;
- REGFI_NK_REC* key;
+ REGFI_NK* key = NULL;
uint32_t i, j;
int error_code = 0;
@@ -615,7 +611,7 @@ int extractKeys(REGFI_FILE* f,
error_code = 20;
goto fail;
}
- talloc_steal(unalloc_keys, key);
+ talloc_reparent(NULL, unalloc_keys, key);
j+=key->cell_size-8;
}
}
@@ -634,7 +630,7 @@ int extractKeys(REGFI_FILE* f,
return 0;
fail:
- regfi_free_key(key);
+ regfi_free_record(f, key);
return error_code;
}
@@ -643,8 +639,8 @@ int extractValueLists(REGFI_FILE* f,
range_list* unalloc_keys,
range_list* unalloc_linked_values)
{
- REGFI_NK_REC* nk;
- REGFI_VK_REC* vk;
+ REGFI_NK* nk;
+ REGFI_VK* vk;
const range_list_element* cur_elem;
uint32_t i, j, num_keys, off, values_length;
int32_t max_size;
@@ -735,7 +731,7 @@ int extractSKs(REGFI_FILE* f,
range_list* unalloc_sks)
{
const range_list_element* cur_elem;
- REGFI_SK_REC* sk;
+ REGFI_SK* sk;
uint32_t i, j;
for(i=0; i < range_list_size(unalloc_cells); i++)
@@ -756,7 +752,7 @@ int extractSKs(REGFI_FILE* f,
fprintf(stderr, "ERROR: Couldn't add sk to unalloc_sks.\n");
return 20;
}
- talloc_steal(unalloc_sks, sk);
+ talloc_reparent(NULL, unalloc_sks, sk);
j+=sk->cell_size-8;
}
}
@@ -785,10 +781,11 @@ int main(int argc, char** argv)
char** parent_paths;
char* tmp_name;
char* tmp_path;
- REGFI_NK_REC* tmp_key;
- REGFI_VK_REC* tmp_value;
+ REGFI_NK* tmp_key;
+ REGFI_VK* tmp_value;
uint32_t argi, arge, i, j, ret, num_unalloc_keys;
-
+ int fd;
+
/* Process command line arguments */
if(argc < 2)
{
@@ -820,21 +817,27 @@ int main(int argc, char** argv)
bailOut(REGLOOKUP_EXIT_USAGE, "");
}
}
- /*test_offset = strtol(argv[argi++], NULL, 16);*/
-
- if((registry_file = strdup(argv[argi])) == NULL)
- bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Memory allocation problem.\n");
+ registry_file = argv[argi];
- f = regfi_open(registry_file);
- if(f == NULL)
+ fd = openHive(registry_file);
+ if(fd < 0)
{
fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file);
bailOut(REGLOOKUP_EXIT_NOINPUT, "");
}
+
if(print_verbose)
- regfi_set_message_mask(f, REGFI_MSG_ERROR|REGFI_MSG_WARN|REGFI_MSG_INFO);
+ regfi_log_set_mask(REGFI_LOG_ERROR|REGFI_LOG_WARN|REGFI_LOG_INFO);
else
- regfi_set_message_mask(f, REGFI_MSG_ERROR);
+ regfi_log_set_mask(REGFI_LOG_ERROR);
+
+ /* XXX: add command line option to choose output encoding */
+ f = regfi_alloc(fd, REGFI_ENCODING_ASCII);
+ if(f == NULL)
+ {
+ close(fd);
+ bailOut(REGLOOKUP_EXIT_NOINPUT, "ERROR: Failed to create REGFI_FILE structure.\n");
+ }
if(print_header)
printf("OFFSET,REC_LENGTH,REC_TYPE,PATH,NAME,"
@@ -919,7 +922,7 @@ int main(int argc, char** argv)
for(i=0; i < num_unalloc_keys; i++)
{
cur_elem = range_list_get(unalloc_keys, i);
- tmp_key = (REGFI_NK_REC*)cur_elem->data;
+ tmp_key = (REGFI_NK*)cur_elem->data;
if(tmp_key == NULL)
return 20;
@@ -933,13 +936,13 @@ int main(int argc, char** argv)
for(i=0; i < num_unalloc_keys; i++)
{
cur_elem = range_list_get(unalloc_keys, i);
- tmp_key = (REGFI_NK_REC*)cur_elem->data;
+ tmp_key = (REGFI_NK*)cur_elem->data;
printKey(f, tmp_key, parent_paths[i]);
if(tmp_key->num_values > 0 && tmp_key->values != NULL)
{
/* XXX: Add command line option to choose output encoding */
- regfi_interpret_keyname(f, tmp_key, REGFI_ENCODING_ASCII, true);
+ regfi_interpret_keyname(f, tmp_key, true);
tmp_name = get_quoted_keyname(tmp_key);
tmp_path = (char*)malloc(strlen(parent_paths[i])+strlen(tmp_name)+2);
@@ -953,9 +956,9 @@ int main(int argc, char** argv)
for(j=0; j < tmp_key->values->num_values; j++)
{
tmp_value =
- (REGFI_VK_REC*)range_list_find_data(unalloc_linked_values,
- tmp_key->values->elements[j]
- + REGFI_REGF_SIZE);
+ (REGFI_VK*)range_list_find_data(unalloc_linked_values,
+ tmp_key->values->elements[j]
+ + REGFI_REGF_SIZE);
if(tmp_value != NULL)
printValue(f, tmp_value, tmp_path);
}
@@ -970,7 +973,7 @@ int main(int argc, char** argv)
for(i=0; i < range_list_size(unalloc_values); i++)
{
cur_elem = range_list_get(unalloc_values, i);
- tmp_value = (REGFI_VK_REC*)cur_elem->data;
+ tmp_value = (REGFI_VK*)cur_elem->data;
printValue(f, tmp_value, "");
}
@@ -990,5 +993,8 @@ int main(int argc, char** argv)
range_list_free(unalloc_values);
range_list_free(unalloc_sks);
+ regfi_free(f);
+ close(fd);
+
return 0;
}
diff --git a/src/reglookup.c b/src/reglookup.c
index 9969065..810e55c 100644
--- a/src/reglookup.c
+++ b/src/reglookup.c
@@ -1,7 +1,7 @@
/*
* A utility to read a Windows NT and later registry files.
*
- * Copyright (C) 2005-2010 Timothy D. Morgan
+ * Copyright (C) 2005-2011 Timothy D. Morgan
* Copyright (C) 2010 Tobias Mueller (portions of '-i' code)
* Copyright (C) 2002 Richard Sharpe, rsharpe at richardsharpe.com
*
@@ -18,7 +18,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: reglookup.c 172 2010-03-08 03:04:34Z tim $
+ * $Id: reglookup.c 261 2011-06-17 00:55:49Z tim $
*/
@@ -39,7 +39,7 @@ bool path_filter_enabled = false;
bool type_filter_enabled = false;
char* path_filter = NULL;
int type_filter;
-char* registry_file = NULL;
+const char* registry_file = NULL;
/* Other globals */
REGFI_FILE* f;
@@ -51,9 +51,15 @@ REGFI_FILE* f;
#include "common.c"
-void printValue(REGFI_ITERATOR* iter, const REGFI_VK_REC* vk, char* prefix)
+static bool keysEqual(const REGFI_NK* x, const REGFI_NK* y)
{
- REGFI_DATA* data;
+ return (x != NULL && y != NULL && x->offset == y->offset);
+}
+
+void printValue(REGFI_ITERATOR* iter, const REGFI_VK* vk, char* prefix)
+{
+ const REGFI_NK* cur_key;
+ const REGFI_DATA* data;
char* quoted_value = NULL;
char* quoted_name = NULL;
char* conv_error = NULL;
@@ -76,7 +82,7 @@ void printValue(REGFI_ITERATOR* iter, const REGFI_VK_REC* vk, char* prefix)
quoted_name[0] = '\0';
}
- data = regfi_iterator_fetch_data(iter, vk);
+ data = regfi_fetch_data(iter->f, vk);
printMsgs(iter->f);
if(data != NULL)
@@ -94,14 +100,16 @@ void printValue(REGFI_ITERATOR* iter, const REGFI_VK_REC* vk, char* prefix)
else if(conv_error != NULL)
fprintf(stderr, "WARN: While quoting value for '%s/%s', "
"warning returned: %s\n", prefix, quoted_name, conv_error);
- regfi_free_data(data);
+ regfi_free_record(iter->f, data);
}
if(print_value_mtime)
{
- *tmp_time = regfi_nt2unix_time(&iter->cur_key->mtime);
+ cur_key = regfi_iterator_cur_key(iter);
+ *tmp_time = regfi_nt2unix_time(cur_key->mtime);
tmp_time_s = gmtime(tmp_time);
strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s);
+ regfi_free_record(iter->f, cur_key);
}
else
mtime[0] = '\0';
@@ -200,11 +208,11 @@ void freePath(char** path)
}
-/* Returns a quoted path from an iterator's stack */
+/* Returns a quoted path of the current iterator's position */
char* iter2Path(REGFI_ITERATOR* i)
{
- const REGFI_ITER_POSITION* cur;
- const REGFI_NK_REC* tmp_key;
+ const REGFI_NK** path;
+ uint32_t k;
uint32_t buf_left = 127;
uint32_t buf_len = buf_left+1;
uint32_t name_len = 0;
@@ -212,77 +220,71 @@ char* iter2Path(REGFI_ITERATOR* i)
char* buf;
char* new_buf;
char* name;
- void_stack_iterator* iter;
buf = (char*)malloc((buf_len)*sizeof(char));
if (buf == NULL)
return NULL;
buf[0] = '\0';
- iter = void_stack_iterator_new(i->key_positions);
- if (iter == NULL)
+ path = regfi_iterator_ancestry(i);
+ if(path == NULL)
{
free(buf);
return NULL;
}
- /* skip root element */
- if(void_stack_size(i->key_positions) < 1)
- {
- buf[0] = '/';
- buf[1] = '\0';
- return buf;
- }
- cur = void_stack_iterator_next(iter);
-
- do
+ for(k=0; path[k] != NULL; k++)
{
- cur = void_stack_iterator_next(iter);
- if (cur == NULL)
- tmp_key = i->cur_key;
+ /* skip root element's name */
+ if(k == 0)
+ {
+ buf[0] = '/';
+ buf[1] = '\0';
+ }
else
- tmp_key = cur->nk;
-
- name = get_quoted_keyname(tmp_key);
-
- buf[buf_len-buf_left-1] = '/';
- buf_left -= 1;
- name_len = strlen(name);
- if(name_len+1 > buf_left)
{
- grow_amt = (uint32_t)(buf_len/2);
- buf_len += name_len+1+grow_amt-buf_left;
- if((new_buf = realloc(buf, buf_len)) == NULL)
+ name = get_quoted_keyname(path[k]);
+
+ buf[buf_len-buf_left-1] = '/';
+ buf_left -= 1;
+ name_len = strlen(name);
+ if(name_len+1 > buf_left)
{
- free(name);
- free(buf);
- free(iter);
- return NULL;
+ grow_amt = (uint32_t)(buf_len/2);
+ buf_len += name_len+1+grow_amt-buf_left;
+ if((new_buf = realloc(buf, buf_len)) == NULL)
+ {
+ regfi_free_record(i->f, path);
+ free(name);
+ free(buf);
+ return NULL;
+ }
+ buf = new_buf;
+ buf_left = grow_amt + name_len + 1;
}
- buf = new_buf;
- buf_left = grow_amt + name_len + 1;
+ strncpy(buf+(buf_len-buf_left-1), name, name_len);
+ buf_left -= name_len;
+ buf[buf_len-buf_left-1] = '\0';
+ free(name);
}
- strncpy(buf+(buf_len-buf_left-1), name, name_len);
- buf_left -= name_len;
- buf[buf_len-buf_left-1] = '\0';
- free(name);
- } while(cur != NULL);
+ }
+ regfi_free_record(i->f, path);
return buf;
}
void printValueList(REGFI_ITERATOR* iter, char* prefix)
{
- REGFI_VK_REC* value;
+ const REGFI_VK* value;
- value = regfi_iterator_first_value(iter);
- while(value != NULL)
+ regfi_iterator_first_value(iter);
+ while((value = regfi_iterator_cur_value(iter)) != NULL)
{
if(!type_filter_enabled || (value->type == type_filter))
printValue(iter, value, prefix);
- regfi_free_value(value);
- value = regfi_iterator_next_value(iter);
+ regfi_free_record(iter->f, value);
+ regfi_iterator_next_value(iter);
printMsgs(iter->f);
}
}
@@ -295,24 +297,22 @@ void printKey(REGFI_ITERATOR* iter, char* full_path)
char* group = NULL;
char* sacl = NULL;
char* dacl = NULL;
+ char mtime[24];
char* quoted_classname;
- char mtime[20];
- time_t tmp_time[1];
- struct tm* tmp_time_s = NULL;
- const REGFI_SK_REC* sk;
- const REGFI_NK_REC* k = regfi_iterator_cur_key(iter);
- REGFI_CLASSNAME* classname;
+ const REGFI_SK* sk;
+ const REGFI_NK* key = regfi_iterator_cur_key(iter);
+ const REGFI_CLASSNAME* classname;
- *tmp_time = regfi_nt2unix_time(&k->mtime);
- tmp_time_s = gmtime(tmp_time);
- strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s);
+ formatTime(key->mtime, mtime);
- if(print_security && (sk=regfi_iterator_cur_sk(iter)))
+ if(print_security && (sk=regfi_fetch_sk(iter->f, key)))
{
owner = regfi_get_owner(sk->sec_desc);
group = regfi_get_group(sk->sec_desc);
sacl = regfi_get_sacl(sk->sec_desc);
dacl = regfi_get_dacl(sk->sec_desc);
+ regfi_free_record(iter->f, sk);
+
if(owner == NULL)
owner = empty_str;
if(group == NULL)
@@ -322,7 +322,7 @@ void printKey(REGFI_ITERATOR* iter, char* full_path)
if(dacl == NULL)
dacl = empty_str;
- classname = regfi_iterator_fetch_classname(iter, k);
+ classname = regfi_fetch_classname(iter->f, key);
printMsgs(iter->f);
if(classname != NULL)
{
@@ -346,7 +346,7 @@ void printKey(REGFI_ITERATOR* iter, char* full_path)
}
else
quoted_classname = empty_str;
- regfi_free_classname(classname);
+ regfi_free_record(iter->f, classname);
printMsgs(iter->f);
printf("%s,KEY,,%s,%s,%s,%s,%s,%s\n", full_path, mtime,
@@ -365,20 +365,24 @@ void printKey(REGFI_ITERATOR* iter, char* full_path)
}
else
printf("%s,KEY,,%s\n", full_path, mtime);
+
+ regfi_free_record(iter->f, key);
}
void printKeyTree(REGFI_ITERATOR* iter)
{
- const REGFI_NK_REC* root = NULL;
- const REGFI_NK_REC* cur = NULL;
- REGFI_NK_REC* sub = NULL;
+ const REGFI_NK* root = NULL;
+ const REGFI_NK* cur = NULL;
+ const REGFI_NK* sub = NULL;
char* path = NULL;
int key_type = regfi_type_str2val("KEY");
bool print_this = true;
- root = cur = regfi_iterator_cur_key(iter);
- sub = regfi_iterator_first_subkey(iter);
+ root = regfi_iterator_cur_key(iter);
+ cur = root = regfi_reference_record(iter->f, root);
+ regfi_iterator_first_subkey(iter);
+ sub = regfi_iterator_cur_subkey(iter);
printMsgs(iter->f);
if(root == NULL)
@@ -402,8 +406,10 @@ void printKeyTree(REGFI_ITERATOR* iter)
if(sub == NULL)
{
- if(cur != root)
+ if(!keysEqual(cur, root))
{
+ regfi_free_record(iter->f, cur);
+ cur = NULL;
/* We're done with this sub-tree, going up and hitting other branches. */
if(!regfi_iterator_up(iter))
{
@@ -418,7 +424,8 @@ void printKeyTree(REGFI_ITERATOR* iter)
bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: unexpected NULL for key.\n");
}
- sub = regfi_iterator_next_subkey(iter);
+ regfi_iterator_next_subkey(iter);
+ sub = regfi_iterator_cur_subkey(iter);
}
print_this = false;
}
@@ -426,6 +433,8 @@ void printKeyTree(REGFI_ITERATOR* iter)
{ /* We have unexplored sub-keys.
* Let's move down and print this first sub-tree out.
*/
+ regfi_free_record(iter->f, cur);
+ cur = NULL;
if(!regfi_iterator_down(iter))
{
printMsgs(iter->f);
@@ -433,12 +442,16 @@ void printKeyTree(REGFI_ITERATOR* iter)
}
cur = regfi_iterator_cur_key(iter);
- regfi_free_key(sub);
- sub = regfi_iterator_first_subkey(iter);
+ regfi_free_record(iter->f, sub);
+ regfi_iterator_first_subkey(iter);
+ sub = regfi_iterator_cur_subkey(iter);
print_this = true;
}
printMsgs(iter->f);
- } while(!((cur == root) && (sub == NULL)));
+ } while(!(keysEqual(cur, root) && (sub == NULL)));
+ if(cur != NULL)
+ regfi_free_record(iter->f, cur);
+ regfi_free_record(iter->f, root);
if(print_verbose)
fprintf(stderr, "INFO: Finished printing key tree.\n");
@@ -456,7 +469,7 @@ void printKeyTree(REGFI_ITERATOR* iter)
*/
int retrievePath(REGFI_ITERATOR* iter, char** path)
{
- REGFI_VK_REC* value;
+ const REGFI_VK* value;
char* tmp_path_joined;
const char** tmp_path;
uint32_t i;
@@ -489,7 +502,7 @@ int retrievePath(REGFI_ITERATOR* iter, char** path)
return 2;
}
- if(!regfi_iterator_walk_path(iter, tmp_path))
+ if(!regfi_iterator_descend(iter, tmp_path))
{
printMsgs(iter->f);
free(tmp_path);
@@ -511,7 +524,7 @@ int retrievePath(REGFI_ITERATOR* iter, char** path)
if(!type_filter_enabled || (value->type == type_filter))
printValue(iter, value, tmp_path_joined);
- regfi_free_value(value);
+ regfi_free_record(iter->f, value);
free(tmp_path);
free(tmp_path_joined);
return 1;
@@ -544,7 +557,7 @@ static void usage(void)
fprintf(stderr, "Usage: reglookup [-v] [-s]"
" [-p <PATH_FILTER>] [-t <TYPE_FILTER>]"
" <REGISTRY_FILE>\n");
- fprintf(stderr, "Version: %s\n", REGLOOKUP_VERSION);
+ fprintf(stderr, "Version: %s\n", regfi_version());
fprintf(stderr, "Options:\n");
fprintf(stderr, "\t-v\t sets verbose mode.\n");
fprintf(stderr, "\t-h\t enables header row. (default)\n");
@@ -562,7 +575,7 @@ int main(int argc, char** argv)
{
char** path = NULL;
REGFI_ITERATOR* iter;
- int retr_path_ret;
+ int retr_path_ret, fd;
uint32_t argi, arge;
/* Process command line arguments */
@@ -620,21 +633,27 @@ int main(int argc, char** argv)
bailOut(REGLOOKUP_EXIT_USAGE, "");
}
}
- if((registry_file = strdup(argv[argi])) == NULL)
- bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Memory allocation problem.\n");
+ registry_file = argv[argi];
- f = regfi_open(registry_file);
- if(f == NULL)
+ if(print_verbose)
+ regfi_log_set_mask(REGFI_LOG_INFO|REGFI_LOG_WARN|REGFI_LOG_ERROR);
+
+ fd = openHive(registry_file);
+ if(fd < 0)
{
fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file);
bailOut(REGLOOKUP_EXIT_NOINPUT, "");
}
-
- if(print_verbose)
- regfi_set_message_mask(f, REGFI_MSG_INFO|REGFI_MSG_WARN|REGFI_MSG_ERROR);
-
+
/* XXX: add command line option to choose output encoding */
- iter = regfi_iterator_new(f, REGFI_ENCODING_ASCII);
+ f = regfi_alloc(fd, REGFI_ENCODING_ASCII);
+ if(f == NULL)
+ {
+ close(fd);
+ bailOut(REGLOOKUP_EXIT_NOINPUT, "ERROR: Failed to create REGFI_FILE structure.\n");
+ }
+
+ iter = regfi_iterator_new(f);
if(iter == NULL)
{
printMsgs(f);
@@ -674,7 +693,8 @@ int main(int argc, char** argv)
printKeyTree(iter);
regfi_iterator_free(iter);
- regfi_close(f);
+ regfi_free(f);
+ close(fd);
return 0;
}
--
debian-forensics/reglookup
More information about the forensics-changes
mailing list