[PATCH] hivexml: Add root attribute to the root node
by Alex Nelson
New feature: If the root node of the XML root is the hive root node,
denote with attribute/value root="1".
Signed-off-by: Alex Nelson <ajnelson(a)cs.ucsc.edu>
---
xml/hivexml.c | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/xml/hivexml.c b/xml/hivexml.c
index 2967ac9..f29c80c 100644
--- a/xml/hivexml.c
+++ b/xml/hivexml.c
@@ -204,6 +204,10 @@ node_start (hive_h *h, void *writer_v, hive_node_h node, const char *name)
XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "node"));
XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "name", BAD_CAST name));
+ if (node == hivex_root (h)) {
+ XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "root", BAD_CAST "1"));
+ }
+
last_modified = hivex_node_timestamp (h, node);
if (last_modified >= 0) {
timebuf = filetime_to_8601 (last_modified);
--
1.7.6
13 years, 4 months
[Hivex] [PATCH v3] Report last-modified time of hive root and nodes
by Alex Nelson
The infrastructure for modified-time reporting has been essentially
unused. These changes report the registry time by treating the
time fields as Windows filetime fields stored in little-Endian
(which means they can be treated as a single 64-bit little-Endian
integer).
This patch adds to the hivex ABI:
* int64_t hivex_last_modified (hive_h *)
* int64_t hivex_node_timestamp (hive_h *, hive_node_h)
These two functions return the hive's last-modified time and
a particular node's last-modified time, respectively. Credit
to Richard Jones for the ABI suggestion, and for the tip on
Microsoft's filetime time span.
hivexml employs these two functions to produce mtime elements
for a hive and all of its nodes, producing ISO-8601 formatted
time.
Signed-off-by: Alex Nelson <ajnelson(a)cs.ucsc.edu>
---
generator/generator.ml | 20 +++++++++++++
lib/hivex.c | 46 +++++++++++++++++++++++++++++--
xml/hivexml.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 133 insertions(+), 4 deletions(-)
diff --git a/generator/generator.ml b/generator/generator.ml
index de911f1..370472f 100755
--- a/generator/generator.ml
+++ b/generator/generator.ml
@@ -158,6 +158,13 @@ but instead are lost. See L<hivex(3)/WRITING TO HIVE FILES>.";
"\
Return root node of the hive. All valid hives must contain a root node.";
+ "last_modified", (RInt64, [AHive]),
+ "return the modification time of the root node of the hive",
+ "\
+Return the modification time of the root node of the hive. The
+returned value is equivalent in type and caveats as
+hivex_node_timestamp.";
+
"node_name", (RString, [AHive; ANode "node"]),
"return the name of the node",
"\
@@ -170,6 +177,16 @@ only know the \"real\" name of the root node by knowing which registry
file this hive originally comes from, which is knowledge that is
outside the scope of this library.";
+ "node_timestamp", (RInt64, [AHive; ANode "node"]),
+ "return the modification time of the node",
+ "\
+Return the modification time of the node. Output is an Endian-correct
+64-bit signed number.
+
+Though Windows' filetime struct looks like it should be an unsigned
+64-bit int, Microsoft only guarantees FILETIME through 9999-12-31, due
+to conversion limitations with .Net's DateTime.";
+
"node_children", (RNodeList, [AHive; ANode "node"]),
"return children of node",
"\
@@ -708,6 +725,9 @@ typedef size_t hive_value_h;
# define HIVEX_NO_KEY ENOENT
#endif
+#include <time.h>
+#define TIMESTAMP_BUF_LEN 32
+
/* Pre-defined types. */
enum hive_type {
";
diff --git a/lib/hivex.c b/lib/hivex.c
index fedbb6c..1e77831 100644
--- a/lib/hivex.c
+++ b/lib/hivex.c
@@ -93,6 +93,7 @@ struct hive_h {
/* Fields from the header, extracted from little-endianness hell. */
size_t rootoffs; /* Root key offset (always an nk-block). */
size_t endpages; /* Offset of end of pages. */
+ int64_t last_modified; /* mtime of base block. */
/* For writing. */
size_t endblocks; /* Offset to next block allocation (0
@@ -104,7 +105,7 @@ struct ntreg_header {
char magic[4]; /* "regf" */
uint32_t sequence1;
uint32_t sequence2;
- char last_modified[8];
+ int64_t last_modified;
uint32_t major_ver; /* 1 */
uint32_t minor_ver; /* 3 */
uint32_t unknown5; /* 0 */
@@ -173,7 +174,7 @@ struct ntreg_nk_record {
int32_t seg_len; /* length (always -ve because used) */
char id[2]; /* "nk" */
uint16_t flags;
- char timestamp[8];
+ int64_t timestamp;
uint32_t unknown1;
uint32_t parent; /* offset of owner/parent */
uint32_t nr_subkeys; /* number of subkeys */
@@ -359,6 +360,9 @@ hivex_open (const char *filename, int flags)
goto error;
}
+ /* Last-modified time. */
+ h->last_modified = le64toh ((int64_t) h->hdr->last_modified);
+
if (h->msglvl >= 2) {
char *name = windows_utf16_to_utf8 (h->hdr->name, 64);
@@ -367,6 +371,8 @@ hivex_open (const char *filename, int flags)
" file version %" PRIu32 ".%" PRIu32 "\n"
" sequence nos %" PRIu32 " %" PRIu32 "\n"
" (sequences nos should match if hive was synched at shutdown)\n"
+ " last modified \n"
+ " (decimal, 100 ns) %" PRIu64 "\n"
" original file name %s\n"
" (only 32 chars are stored, name is probably truncated)\n"
" root offset 0x%x + 0x1000\n"
@@ -374,6 +380,7 @@ hivex_open (const char *filename, int flags)
" checksum 0x%x (calculated 0x%x)\n",
major_ver, le32toh (h->hdr->minor_ver),
le32toh (h->hdr->sequence1), le32toh (h->hdr->sequence2),
+ h->last_modified,
name ? name : "(conversion failed)",
le32toh (h->hdr->offset),
le32toh (h->hdr->blocks), h->size,
@@ -608,6 +615,39 @@ hivex_node_name (hive_h *h, hive_node_h node)
return ret;
}
+int64_t
+hivex_timestamp_check (hive_h *h, hive_node_h node, int64_t timestamp)
+{
+ if (timestamp < 0) {
+ fprintf (stderr, "Negative time reported at %z: %" PRIi64 "\n", node, timestamp);
+ errno = EINVAL;
+ return -1;
+ }
+ return timestamp;
+}
+
+int64_t
+hivex_last_modified (hive_h *h)
+{
+ return hivex_timestamp_check (h, 0, h->last_modified);
+}
+
+int64_t
+hivex_node_timestamp (hive_h *h, hive_node_h node)
+{
+ int64_t ret;
+
+ if (!IS_VALID_BLOCK (h, node) || !BLOCK_ID_EQ (h, node, "nk")) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ struct ntreg_nk_record *nk = (struct ntreg_nk_record *) (h->addr + node);
+
+ ret = le64toh (nk->timestamp);
+ return hivex_timestamp_check (h, node, ret);
+}
+
#if 0
/* I think the documentation for the sk and classname fields in the nk
* record is wrong, or else the offset field is in the wrong place.
@@ -2264,7 +2304,7 @@ hivex_node_add_child (hive_h *h, hive_node_h parent, const char *name)
nk->sk = htole32 (parent_sk_offset - 0x1000);
/* Inherit parent timestamp. */
- memcpy (nk->timestamp, parent_nk->timestamp, sizeof (parent_nk->timestamp));
+ nk->timestamp = parent_nk->timestamp;
/* What I found out the hard way (not documented anywhere): the
* subkeys in lh-records must be kept sorted. If you just add a
diff --git a/xml/hivexml.c b/xml/hivexml.c
index 90cb22b..c68a3d2 100644
--- a/xml/hivexml.c
+++ b/xml/hivexml.c
@@ -64,6 +64,8 @@ static struct hivex_visitor visitor = {
.value_other = value_other
};
+char * filetime_to_8601 (int64_t windows_ticks);
+
#define XML_CHECK(proc, args) \
do { \
if ((proc args) == -1) { \
@@ -124,6 +126,20 @@ main (int argc, char *argv[])
XML_CHECK (xmlTextWriterStartDocument, (writer, NULL, "utf-8", NULL));
XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "hive"));
+ int64_t hive_mtime = hivex_last_modified (h);
+ if (hive_mtime < 0)
+ goto skip_mtime;
+ char *timebuf = filetime_to_8601 (hive_mtime);
+ if (timebuf == NULL)
+ goto skip_mtime;
+ XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "mtime"));
+ XML_CHECK (xmlTextWriterWriteString, (writer, BAD_CAST timebuf));
+ XML_CHECK (xmlTextWriterEndElement, (writer));
+ free (timebuf);
+ timebuf = NULL;
+
+skip_mtime:
+
if (hivex_visit (h, &visitor, sizeof visitor, writer, visit_flags) == -1) {
perror (argv[optind]);
exit (EXIT_FAILURE);
@@ -141,13 +157,66 @@ main (int argc, char *argv[])
exit (EXIT_SUCCESS);
}
+#define WINDOWS_TICK 10000000LL
+#define SEC_TO_UNIX_EPOCH 11644473600LL
+/**
+ * Convert Windows filetime to ISO 8601 format.
+ * Source for filetime->time_t conversion: http://stackoverflow.com/questions/6161776/convert-windows-filetime-to-se...
+ * Source for time_t->char* conversion: Fiwalk version 0.6.14's fiwalk.cpp.
+ * @param windows_ticks Expected to not have any remaining Endian issues.
+ *
+ * Caller is responsible for freeing non-null returned buffer.
+ */
+char *
+filetime_to_8601 (int64_t windows_ticks)
+{
+ char *ret = calloc (1 + TIMESTAMP_BUF_LEN, sizeof (char));
+ if (ret == NULL) {
+ goto error_other;
+ }
+ uint64_t nanos = windows_ticks % WINDOWS_TICK;
+ time_t tt = (windows_ticks / WINDOWS_TICK - SEC_TO_UNIX_EPOCH);
+ struct tm time_tm;
+ if (gmtime_r (&tt, &time_tm) == NULL) {
+ fprintf (stderr, "filetime_to_8601: Error running gmtime_r on timestamp (decimal hundreds of ns: %" PRIu64 ").\n", windows_ticks);
+ goto error_cleanup;
+ }
+ if (strftime (ret, TIMESTAMP_BUF_LEN, "%FT%TZ", &time_tm) == 0)
+ goto error_cleanup;
+ return ret;
+
+error_cleanup:
+ free (ret);
+ ret = NULL;
+error_other:
+ return NULL;
+}
+
static int
node_start (hive_h *h, void *writer_v, hive_node_h node, const char *name)
{
+ int ret = 0;
+ int64_t last_modified;
+ char *timebuf;
+
xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "node"));
XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "name", BAD_CAST name));
- return 0;
+
+ last_modified = hivex_node_timestamp (h, node);
+ if (last_modified < 0)
+ goto skip_mtime;
+ timebuf = filetime_to_8601 (last_modified);
+ if (!timebuf)
+ goto skip_mtime;
+ XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "mtime"));
+ XML_CHECK (xmlTextWriterWriteString, (writer, BAD_CAST timebuf));
+ XML_CHECK (xmlTextWriterEndElement, (writer));
+ free (timebuf);
+ timebuf = NULL;
+
+skip_mtime:
+ return ret;
}
static int
--
1.7.6
13 years, 4 months
libguestfs 1.12
by Hilko Bengen
Hi!
Last night, I uploaded version 1.12.3-2 of the libguestfs package to
Debian/unstable. I consider most of the initial work done. Apart from
fixing bugs that users will reportt, a few issues remain:
- Building bindings for multiple Python versions. I have just done this
for hivex, it is probably going to be an issue being able build with
$(builddir) != $(srcdir).
- Improving febootstrap so that downloading of packages may not be
needed in the future.
- Bindings for other languages: The configure script lists Ruby, Java,
Haskell, and PHP. I am not feeling like doing anything on those in the
short term, but I'm happy to accept patches.
- Backports for the current stable Debian version (squeeze)?
Cheers,
-Hilko
13 years, 4 months
[Hivex] [PATCH] Correct 32-bit to 64-bit call
by Alex Nelson
---
generator/generator.ml | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/generator/generator.ml b/generator/generator.ml
index 31478cd..de911f1 100755
--- a/generator/generator.ml
+++ b/generator/generator.ml
@@ -1771,7 +1771,7 @@ static void raise_closed (const char *) Noreturn;
pr " rv = copy_type_value (r, len, t);\n";
pr " free (r);\n"
| RInt32 -> pr " rv = caml_copy_int32 (r);\n"
- | RInt64 -> pr " rv = caml_copy_int32 (r);\n"
+ | RInt64 -> pr " rv = caml_copy_int64 (r);\n"
);
pr " CAMLreturn (rv);\n";
--
1.7.3.1
13 years, 4 months
[PATCH] hivex: A few tweaks to enable building in a separate directory
by Hilko Bengen
---
configure.ac | 2 +-
images/Makefile.am | 5 +++--
lib/Makefile.am | 14 +++++++-------
ocaml/Makefile.am | 3 ++-
ocaml/t/hivex_300_fold.ml | 3 +--
python/run-python-tests | 25 -------------------------
python/run-python-tests.in | 25 +++++++++++++++++++++++++
7 files changed, 39 insertions(+), 38 deletions(-)
delete mode 100755 python/run-python-tests
create mode 100755 python/run-python-tests.in
diff --git a/configure.ac b/configure.ac
index 22b806f..31d20ff 100644
--- a/configure.ac
+++ b/configure.ac
@@ -434,7 +434,7 @@ AC_CONFIG_FILES([Makefile
lib/tools/Makefile
ocaml/Makefile ocaml/META
perl/Makefile perl/Makefile.PL
- python/Makefile
+ python/Makefile python/run-python-tests
po/Makefile.in
regedit/Makefile
sh/Makefile
diff --git a/images/Makefile.am b/images/Makefile.am
index 2adaed3..e176d3c 100644
--- a/images/Makefile.am
+++ b/images/Makefile.am
@@ -29,7 +29,8 @@ mklarge_LDADD = ../lib/libhivex.la
noinst_DATA = large
-large: mklarge
- ./mklarge $(srcdir)/minimal large
+large: minimal mklarge
+ cp -u $(srcdir)/minimal $(builddir)/minimal
+ ./mklarge $(builddir)/minimal $(builddir)/large
CLEANFILES = $(noinst_DATA)
diff --git a/lib/Makefile.am b/lib/Makefile.am
index d54aaee..7e5b92b 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -33,11 +33,11 @@ libhivex_la_SOURCES = \
libhivex_la_LIBADD = ../gnulib/lib/libgnu.la
libhivex_la_LDFLAGS = \
-version-info 0:0:0 \
- $(VERSION_SCRIPT_FLAGS)hivex.syms \
+ $(VERSION_SCRIPT_FLAGS)$(srcdir)/hivex.syms \
$(LTLIBINTL) \
$(LTLIBTHREAD)
libhivex_la_CFLAGS = $(WARN_CFLAGS) $(WERROR_CFLAGS)
-libhivex_la_CPPFLAGS = -I$(top_srcdir)/gnulib/lib
+libhivex_la_CPPFLAGS = -I$(top_srcdir)/gnulib/lib -I$(srcdir)
include_HEADERS = hivex.h
@@ -56,10 +56,10 @@ noinst_DATA = \
$(top_builddir)/html/hivex.3.html: hivex.pod
mkdir -p $(top_builddir)/html
- cd $(top_builddir) && pod2html \
- --css 'pod.css' \
- --htmldir html \
- --outfile html/hivex.3.html \
- lib/hivex.pod
+ pod2html \
+ --css $(top_srcdir)/css/pod.css \
+ --htmldir $(top_builddir)/html \
+ --outfile $(top_builddir)/html/hivex.3.html \
+ $<
CLEANFILES = $(man_MANS)
diff --git a/ocaml/Makefile.am b/ocaml/Makefile.am
index c688df0..89a59cd 100644
--- a/ocaml/Makefile.am
+++ b/ocaml/Makefile.am
@@ -64,7 +64,8 @@ TESTS = \
noinst_DATA += $(TESTS)
# https://www.redhat.com/archives/libguestfs/2011-May/thread.html#00015
-t/%: t/%.cmo mlhivex.cma
+t/%: $(srcdir)/t/%.cmo mlhivex.cma
+ mkdir -p t
$(LIBTOOL) --mode=execute -dlopen $(top_builddir)/lib/libhivex.la \
$(OCAMLFIND) ocamlc -dllpath $(abs_builddir) -package unix \
-linkpkg mlhivex.cma $< -o $@
diff --git a/ocaml/t/hivex_300_fold.ml b/ocaml/t/hivex_300_fold.ml
index 0c7bc4f..c8de6e8 100644
--- a/ocaml/t/hivex_300_fold.ml
+++ b/ocaml/t/hivex_300_fold.ml
@@ -21,7 +21,6 @@
open Unix
open Printf
let (//) = Filename.concat
-let srcdir = try Sys.getenv "srcdir" with Not_found -> "."
(* This is a generic function to fold over hives.
* fn : 'a -> node -> 'a is called for each node
@@ -36,7 +35,7 @@ let hive_fold h fn fv a root =
fold a root
let () =
- let h = Hivex.open_file (srcdir // "../images/large") [] in
+ let h = Hivex.open_file ("../images/large") [] in
(* Count the number of nodes and values in the hive. *)
let count_node (nodes, values) _ = (nodes+1, values) in
diff --git a/python/run-python-tests b/python/run-python-tests
deleted file mode 100755
index e8c8c14..0000000
--- a/python/run-python-tests
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash -
-# hivex Python bindings
-# Copyright (C) 2009-2011 Red Hat Inc.
-#
-# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-set -e
-shopt -s nullglob
-
-for f in t/*.py; do
- basename "$f"
- python "$f"
-done
diff --git a/python/run-python-tests.in b/python/run-python-tests.in
new file mode 100755
index 0000000..af51080
--- /dev/null
+++ b/python/run-python-tests.in
@@ -0,0 +1,25 @@
+#!/bin/bash -
+# hivex Python bindings
+# Copyright (C) 2009-2011 Red Hat Inc.
+#
+# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+set -e
+shopt -s nullglob
+
+for f in $(srcdir)t/*.py; do
+ basename "$f"
+ @PYTHON@ "$f"
+done
--
1.7.5.4
13 years, 4 months
[Hivex][PATCH v2] Report last-modified time of hive root and nodes
by Alex Nelson
The infrastructure for modified-time reporting has been essentially
unused. These changes report the registry time by treating the
time fields as Windows filetime fields stored in little-Endian
(which means they can be treated as a single 64-bit little-Endian
integer).
This patch adds the node_mtime function to the visitor API.
Signed-off-by: Alex Nelson <ajnelson(a)cs.ucsc.edu>
---
generator/generator.ml | 3 ++
lib/hivex.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++--
xml/hivexml.c | 18 +++++++++-
3 files changed, 112 insertions(+), 4 deletions(-)
diff --git a/generator/generator.ml b/generator/generator.ml
index 31478cd..36615f7 100755
--- a/generator/generator.ml
+++ b/generator/generator.ml
@@ -772,6 +772,7 @@ struct hivex_visitor {
int (*value_none) (hive_h *, void *opaque, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value);
int (*value_other) (hive_h *, void *opaque, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value);
int (*value_any) (hive_h *, void *opaque, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value);
+ int (*node_mtime) (hive_h *h, void *writer_v, hive_node_h node, const char *last_modified);
};
#define HIVEX_VISIT_SKIP_BAD 1
@@ -1134,6 +1135,8 @@ all, set the function pointer to NULL.
*/
int (*value_any) (hive_h *, void *opaque, hive_node_h, hive_value_h,
hive_type t, size_t len, const char *key, const char *value);
+ int (*node_mtime) (hive_h *h, void *writer_v, hive_node_h node,
+ const char *last_modified)
};
=over 4
diff --git a/lib/hivex.c b/lib/hivex.c
index fedbb6c..4f3dcb1 100644
--- a/lib/hivex.c
+++ b/lib/hivex.c
@@ -33,6 +33,7 @@
#include <sys/mman.h>
#include <sys/stat.h>
#include <assert.h>
+#include <time.h>
#include "c-ctype.h"
#include "full-read.h"
@@ -93,6 +94,7 @@ struct hive_h {
/* Fields from the header, extracted from little-endianness hell. */
size_t rootoffs; /* Root key offset (always an nk-block). */
size_t endpages; /* Offset of end of pages. */
+ char *last_modified; /* mtime of base block. */
/* For writing. */
size_t endblocks; /* Offset to next block allocation (0
@@ -104,7 +106,7 @@ struct ntreg_header {
char magic[4]; /* "regf" */
uint32_t sequence1;
uint32_t sequence2;
- char last_modified[8];
+ uint64_t last_modified;
uint32_t major_ver; /* 1 */
uint32_t minor_ver; /* 3 */
uint32_t unknown5; /* 0 */
@@ -173,7 +175,7 @@ struct ntreg_nk_record {
int32_t seg_len; /* length (always -ve because used) */
char id[2]; /* "nk" */
uint16_t flags;
- char timestamp[8];
+ uint64_t timestamp;
uint32_t unknown1;
uint32_t parent; /* offset of owner/parent */
uint32_t nr_subkeys; /* number of subkeys */
@@ -265,7 +267,34 @@ header_checksum (const hive_h *h)
return sum;
}
+#define WINDOWS_TICK 10000000LL
+#define SEC_TO_UNIX_EPOCH 11644473600LL
+/**
+ * Convert Windows filetime to ISO 8601 format.
+ * Source for filetime->time_t conversion: http://stackoverflow.com/questions/6161776/convert-windows-filetime-to-se...
+ * Source for time_t->char* conversion: Fiwalk version 0.6.14's fiwalk.cpp.
+ * @param windows_ticks Expected to not have any remaining Endian issues.
+ */
+int
+filetime_to_8601 (char *buf, int bufsize, uint64_t windows_ticks)
+{
+ if (buf == NULL) {
+ fprintf (stderr, "filetime_to_8601: Received null output buffer, unable to proceed.\n");
+ return -1;
+ }
+ uint64_t nanos = windows_ticks % WINDOWS_TICK;
+ time_t tt = (windows_ticks / WINDOWS_TICK - SEC_TO_UNIX_EPOCH);
+ struct tm time_tm;
+ if (gmtime_r (&tt, &time_tm) == NULL) {
+ fprintf (stderr, "filetime_to_8601: Error running gmtime_r on timestamp (decimal hundreds of ns: %" PRIu64 ").\n", windows_ticks);
+ return -1;
+ }
+ strftime(buf, bufsize, "%FT%TZ", &time_tm);
+ return 0;
+}
+
#define HIVEX_OPEN_MSGLVL_MASK (HIVEX_OPEN_VERBOSE|HIVEX_OPEN_DEBUG)
+#define TIMESTAMP_BUF_LEN 32
hive_h *
hivex_open (const char *filename, int flags)
@@ -359,6 +388,15 @@ hivex_open (const char *filename, int flags)
goto error;
}
+ /* Last-modified time. */
+ h->last_modified = (char *) calloc(1 + TIMESTAMP_BUF_LEN, sizeof(char));
+ int ft_rc = filetime_to_8601(h->last_modified, TIMESTAMP_BUF_LEN, le64toh ((uint64_t) h->hdr->last_modified));
+ if (ft_rc) {
+ fprintf (stderr, "hivex: failed to parse time value\n");
+ free(h->last_modified);
+ h->last_modified = NULL;
+ }
+
if (h->msglvl >= 2) {
char *name = windows_utf16_to_utf8 (h->hdr->name, 64);
@@ -367,6 +405,8 @@ hivex_open (const char *filename, int flags)
" file version %" PRIu32 ".%" PRIu32 "\n"
" sequence nos %" PRIu32 " %" PRIu32 "\n"
" (sequences nos should match if hive was synched at shutdown)\n"
+ " last modified %s\n"
+ " (decimal, 100 ns) %" PRIu64 "\n"
" original file name %s\n"
" (only 32 chars are stored, name is probably truncated)\n"
" root offset 0x%x + 0x1000\n"
@@ -374,6 +414,8 @@ hivex_open (const char *filename, int flags)
" checksum 0x%x (calculated 0x%x)\n",
major_ver, le32toh (h->hdr->minor_ver),
le32toh (h->hdr->sequence1), le32toh (h->hdr->sequence2),
+ h->last_modified,
+ le64toh (h->hdr->last_modified),
name ? name : "(conversion failed)",
le32toh (h->hdr->offset),
le32toh (h->hdr->blocks), h->size,
@@ -541,6 +583,10 @@ hivex_close (hive_h *h)
if (h->msglvl >= 1)
fprintf (stderr, "hivex_close\n");
+ if (h->last_modified) {
+ free (h->last_modified);
+ h->last_modified = NULL;
+ }
free (h->bitmap);
if (!h->writable)
munmap (h->addr, h->size);
@@ -608,6 +654,33 @@ hivex_node_name (hive_h *h, hive_node_h node)
return ret;
}
+/* Caller responsible for freeing returned char* if non-null. */
+char *
+hivex_node_mtime (hive_h *h, hive_node_h node)
+{
+ int ft_rc;
+ if (!IS_VALID_BLOCK (h, node) || !BLOCK_ID_EQ (h, node, "nk")) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ struct ntreg_nk_record *nk = (struct ntreg_nk_record *) (h->addr + node);
+
+ char *ret = calloc (32 + 1, sizeof(char));
+ if (ret == NULL)
+ return ret;
+ ft_rc = filetime_to_8601 (ret, 32, le64toh (nk->timestamp));
+ if (h->msglvl >= 2) {
+ fprintf(stderr, "hivex_node_mtime: nk->timestamp: %" PRIu64 "\n", nk->timestamp);
+ fprintf(stderr, "hivex_node_mtime: ret: %s\n", ret);
+ }
+ if (ft_rc) {
+ free(ret);
+ ret = NULL;
+ }
+ return ret;
+}
+
#if 0
/* I think the documentation for the sk and classname fields in the nk
* record is wrong, or else the offset field is in the wrong place.
@@ -1560,6 +1633,7 @@ hivex__visit_node (hive_h *h, hive_node_h node,
{
int skip_bad = flags & HIVEX_VISIT_SKIP_BAD;
char *name = NULL;
+ char *last_modified = NULL;
hive_value_h *values = NULL;
hive_node_h *children = NULL;
char *key = NULL;
@@ -1584,10 +1658,24 @@ hivex__visit_node (hive_h *h, hive_node_h node,
BITMAP_CLR (unvisited, node);
name = hivex_node_name (h, node);
+ last_modified = hivex_node_mtime (h, node);
+ if (h->msglvl >= 2)
+ fprintf(stderr, "hivex__visit_node: last_modified: %s\n", last_modified ? last_modified : "NULL" );
if (!name) return skip_bad ? 0 : -1;
+
+ /* Report extra for hive's root node. */
+ if (node == hivex_root (h)) {
+ //Produce mtime for hive, not present node
+ if (vtor->node_mtime && vtor->node_mtime (h, opaque, node, h->last_modified) == -1)
+ goto error;
+ }
+
if (vtor->node_start && vtor->node_start (h, opaque, node, name) == -1)
goto error;
+ if (vtor->node_mtime && vtor->node_mtime (h, opaque, node, last_modified) == -1)
+ goto error;
+
values = hivex_node_values (h, node);
if (!values) {
ret = skip_bad ? 0 : -1;
@@ -1764,6 +1852,7 @@ hivex__visit_node (hive_h *h, hive_node_h node,
error:
free (name);
+ free (last_modified);
free (values);
free (children);
free (key);
@@ -2264,7 +2353,7 @@ hivex_node_add_child (hive_h *h, hive_node_h parent, const char *name)
nk->sk = htole32 (parent_sk_offset - 0x1000);
/* Inherit parent timestamp. */
- memcpy (nk->timestamp, parent_nk->timestamp, sizeof (parent_nk->timestamp));
+ nk->timestamp = parent_nk->timestamp;
/* What I found out the hard way (not documented anywhere): the
* subkeys in lh-records must be kept sorted. If you just add a
diff --git a/xml/hivexml.c b/xml/hivexml.c
index 90cb22b..25e8cd9 100644
--- a/xml/hivexml.c
+++ b/xml/hivexml.c
@@ -50,6 +50,7 @@ static int value_qword (hive_h *, void *, hive_node_h, hive_value_h, hive_type t
static int value_binary (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value);
static int value_none (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value);
static int value_other (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value);
+static int node_mtime (hive_h *h, void *writer_v, hive_node_h node, const char *last_modified);
static struct hivex_visitor visitor = {
.node_start = node_start,
@@ -61,7 +62,8 @@ static struct hivex_visitor visitor = {
.value_qword = value_qword,
.value_binary = value_binary,
.value_none = value_none,
- .value_other = value_other
+ .value_other = value_other,
+ .node_mtime = node_mtime
};
#define XML_CHECK(proc, args) \
@@ -142,6 +144,20 @@ main (int argc, char *argv[])
}
static int
+node_mtime (hive_h *h, void *writer_v, hive_node_h node, const char *last_modified)
+{
+ if (!last_modified) {
+ fprintf(stderr, "node_mtime: last_modified came across NULL.\n");
+ return -1;
+ }
+ xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
+ XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "mtime"));
+ XML_CHECK (xmlTextWriterWriteString, (writer, BAD_CAST last_modified));
+ XML_CHECK (xmlTextWriterEndElement, (writer));
+ return 0;
+}
+
+static int
node_start (hive_h *h, void *writer_v, hive_node_h node, const char *name)
{
xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
--
1.7.6
13 years, 4 months
libguestfs-test-tool on ubuntu: kernel panic
by David Konerding
Hi,
I am trying to get libguestfs-test-tool working on ubuntu. The first
problem I had was that HiveX won't compile on ubuntu to due to some missing
symbols (rpl-getopt, I see this has already been reported). Second problem
is that if HiveX is not available, guestfish doesn't link because code
refers to symbols which aren't compiled into libguestfs (I was able to work
around this by #ifdeffing references to those functions).
I'm not reporting on either of those problems (yet). While trying to get
guestfish running, I noticed it failed to boot the appliance. I ran the
suggested libguestfs-test-tool and am providing the output. I think the
problem is that it's trying to use the kernel from my (customized) Ubuntu
workstation to build a Fedora machine, but I'm not certain what the specific
problem is (my experience is these two distributions differ so much in the
built-in kernel options there are a host of problems that can occur). I was
wondering if there is a way to get a Fedora kernel and boot with that
instead of my host kernel.
Anyway, including the diagnostic output in case it helps.
libguestfs: new guestfs handle 0x12da3c0
libguestfs: [00000ms] febootstrap-supermin-helper --verbose -f checksum
'/usr/local/home/dek/sw/libguestfs-1.12.3/lib/guestfs/supermin.d' x86_64
supermin helper [00000ms] whitelist = (not specified), host_cpu = x86_64,
kernel = (null), initrd = (null), appliance = (null)
supermin helper [00000ms] inputs[0] =
/usr/local/home/dek/sw/libguestfs-1.12.3/lib/guestfs/supermin.d
checking modpath /lib/modules/2.6.32-redacted-generic is a directory
picked vmlinuz-2.6.32-redacted-generic because modpath
/lib/modules/2.6.32-redacted-generic exists
checking modpath /lib/modules/2.6.32-redacted-generic is a directory
picked vmlinuz-2.6.32-redacted-generic because modpath
/lib/modules/2.6.32-redacted-generic exists
supermin helper [00000ms] finished creating kernel
supermin helper [00000ms] visiting
/usr/local/home/dek/sw/libguestfs-1.12.3/lib/guestfs/supermin.d
supermin helper [00000ms] visiting
/usr/local/home/dek/sw/libguestfs-1.12.3/lib/guestfs/supermin.d/base.img
supermin helper [00000ms] visiting
/usr/local/home/dek/sw/libguestfs-1.12.3/lib/guestfs/supermin.d/daemon.img
supermin helper [00000ms] visiting
/usr/local/home/dek/sw/libguestfs-1.12.3/lib/guestfs/supermin.d/hostfiles
supermin helper [00002ms] visiting
/usr/local/home/dek/sw/libguestfs-1.12.3/lib/guestfs/supermin.d/init.img
supermin helper [00033ms] finished creating appliance
libguestfs: [00037ms] begin testing qemu features
libguestfs: [00051ms] finished testing qemu features
libguestfs: accept_from_daemon: 0x12da3c0 g->state = 1
libguestfs: [00051ms] /home/dek/sw/qemu-kvm-0.14.1/bin//qemu-system-x86_64 \
-drive
file=/tmp/libguestfs-test-tool-sda-mte9oJ,cache=off,format=raw,if=virtio \
-nodefconfig \
-enable-kvm \
-nodefaults \
-nographic \
-m 500 \
-no-reboot \
-no-hpet \
-device virtio-serial \
-serial stdio \
-chardev socket,path=/tmp/libguestfsj5iN3r/guestfsd.sock,id=channel0 \
-device virtserialport,chardev=channel0,name=org.libguestfs.channel.0 \
-kernel /var/tmp/.guestfs-88619/kernel.32581 \
-initrd /var/tmp/.guestfs-88619/initrd.32581 \
-append 'panic=1 console=ttyS0 udevtimeout=300 noapic acpi=off
printk.time=1 cgroup_disable=memory selinux=0 guestfs_verbose=1 TERM=rxvt '
\
-drive
file=/var/tmp/.guestfs-88619/root.32581,snapshot=on,if=virtio,cache=unsafe
[ 0.000000] Initializing cgroup subsys cpuset
[ 0.000000] Initializing cgroup subsys cpu
[ 0.000000] Linux version 2.6.32-redacted-generic (redacated) (gcc
version 4.4.3 (Ubuntu 4.4.3-4ubuntu5) ) #redacted-Ubuntu SMP Tue May 24
05:09:12 PDT 2011 (Ubuntu 2.6.32-redacted-generic 2.6.32.38+drm33.16)
[ 0.000000] Command line: panic=1 console=ttyS0 udevtimeout=300 noapic
acpi=off printk.time=1 cgroup_disable=memory selinux=0 guestfs_verbose=1
TERM=rxvt
[ 0.000000] KERNEL supported cpus:
[ 0.000000] Intel GenuineIntel
[ 0.000000] AMD AuthenticAMD
[ 0.000000] Centaur CentaurHauls
[ 0.000000] BIOS-provided physical RAM map:
[ 0.000000] BIOS-e820: 0000000000000000 - 000000000009bc00 (usable)
[ 0.000000] BIOS-e820: 000000000009bc00 - 00000000000a0000 (reserved)
[ 0.000000] BIOS-e820: 00000000000f0000 - 0000000000100000 (reserved)
[ 0.000000] BIOS-e820: 0000000000100000 - 000000001f3fd000 (usable)
[ 0.000000] BIOS-e820: 000000001f3fd000 - 000000001f400000 (reserved)
[ 0.000000] BIOS-e820: 00000000feffc000 - 00000000ff000000 (reserved)
[ 0.000000] BIOS-e820: 00000000fffc0000 - 0000000100000000 (reserved)
[ 0.000000] DMI 2.4 present.
[ 0.000000] last_pfn = 0x1f3fd max_arch_pfn = 0x400000000
[ 0.000000] PAT not supported by CPU.
[ 0.000000] Scanning 1 areas for low memory corruption
[ 0.000000] modified physical RAM map:
[ 0.000000] modified: 0000000000000000 - 0000000000001000 (usable)
[ 0.000000] modified: 0000000000001000 - 0000000000006000 (reserved)
[ 0.000000] modified: 0000000000006000 - 000000000009bc00 (usable)
[ 0.000000] modified: 000000000009bc00 - 00000000000a0000 (reserved)
[ 0.000000] modified: 00000000000f0000 - 0000000000100000 (reserved)
[ 0.000000] modified: 0000000000100000 - 000000001f3fd000 (usable)
[ 0.000000] modified: 000000001f3fd000 - 000000001f400000 (reserved)
[ 0.000000] modified: 00000000feffc000 - 00000000ff000000 (reserved)
[ 0.000000] modified: 00000000fffc0000 - 0000000100000000 (reserved)
[ 0.000000] init_memory_mapping: 0000000000000000-000000001f3fd000
[ 0.000000] NX (Execute Disable) protection: active
[ 0.000000] RAMDISK: 1f2f8000 - 1f3efa00
[ 0.000000] No NUMA configuration found
[ 0.000000] Faking a node at 0000000000000000-000000001f3fd000
[ 0.000000] Bootmem setup node 0 0000000000000000-000000001f3fd000
[ 0.000000] NODE_DATA [0000000000009000 - 000000000000dfff]
[ 0.000000] bootmap [000000000000e000 - 0000000000011e7f] pages 4
[ 0.000000] (7 early reservations) ==> bootmem [0000000000 - 001f3fd000]
[ 0.000000] #0 [0000000000 - 0000001000] BIOS data page ==>
[0000000000 - 0000001000]
[ 0.000000] #1 [0000006000 - 0000008000] TRAMPOLINE ==>
[0000006000 - 0000008000]
[ 0.000000] #2 [0001000000 - 0001a423a4] TEXT DATA BSS ==>
[0001000000 - 0001a423a4]
[ 0.000000] #3 [001f2f8000 - 001f3efa00] RAMDISK ==>
[001f2f8000 - 001f3efa00]
[ 0.000000] #4 [000009bc00 - 0000100000] BIOS reserved ==>
[000009bc00 - 0000100000]
[ 0.000000] #5 [0001a43000 - 0001a43049] BRK ==>
[0001a43000 - 0001a43049]
[ 0.000000] #6 [0000008000 - 0000009000] PGTABLE ==>
[0000008000 - 0000009000]
[ 0.000000] found SMP MP-table at [ffff8800000fdbf0] fdbf0
[ 0.000000] kvm-clock: cpu 0, msr 0:1859d01, boot clock
[ 0.000000] Zone PFN ranges:
[ 0.000000] DMA 0x00000000 -> 0x00001000
[ 0.000000] DMA32 0x00001000 -> 0x00100000
[ 0.000000] Normal 0x00100000 -> 0x00100000
[ 0.000000] Movable zone start PFN for each node
[ 0.000000] early_node_map[3] active PFN ranges
[ 0.000000] 0: 0x00000000 -> 0x00000001
[ 0.000000] 0: 0x00000006 -> 0x0000009b
[ 0.000000] 0: 0x00000100 -> 0x0001f3fd
[ 0.000000] SFI: Simple Firmware Interface v0.7 http://simplefirmware.org
[ 0.000000] Intel MultiProcessor Specification v1.4
[ 0.000000] MPTABLE: OEM ID: BOCHSCPU
[ 0.000000] MPTABLE: Product ID: 0.1
[ 0.000000] MPTABLE: APIC at: 0xFEE00000
[ 0.000000] Processor #0 (Bootup-CPU)
[ 0.000000] I/O APIC #1 Version 17 at 0xFEC00000.
[ 0.000000] Processors: 1
[ 0.000000] SMP: Allowing 1 CPUs, 0 hotplug CPUs
[ 0.000000] PM: Registered nosave memory: 0000000000001000 -
0000000000006000
[ 0.000000] PM: Registered nosave memory: 000000000009b000 -
000000000009c000
[ 0.000000] PM: Registered nosave memory: 000000000009c000 -
00000000000a0000
[ 0.000000] PM: Registered nosave memory: 00000000000a0000 -
00000000000f0000
[ 0.000000] PM: Registered nosave memory: 00000000000f0000 -
0000000000100000
[ 0.000000] Allocating PCI resources starting at 1f400000 (gap:
1f400000:dfbfc000)
[ 0.000000] Booting paravirtualized kernel on KVM
[ 0.000000] NR_CPUS:64 nr_cpumask_bits:64 nr_cpu_ids:1 nr_node_ids:1
[ 0.000000] PERCPU: Embedded 30 pages/cpu @ffff880001c00000 s91992 r8192
d22696 u2097152
[ 0.000000] pcpu-alloc: s91992 r8192 d22696 u2097152 alloc=1*2097152
[ 0.000000] pcpu-alloc: [0] 0
[ 0.000000] kvm-clock: cpu 0, msr 0:1c15d01, primary cpu clock
[ 0.000000] Built 1 zonelists in Node order, mobility grouping on. Total
pages: 126037
[ 0.000000] Policy zone: DMA32
[ 0.000000] Kernel command line: panic=1 console=ttyS0 udevtimeout=300
noapic acpi=off printk.time=1 cgroup_disable=memory selinux=0
guestfs_verbose=1 TERM=rxvt
[ 0.000000] Disabling memory control group subsystem
[ 0.000000] PID hash table entries: 2048 (order: 2, 16384 bytes)
[ 0.000000] Initializing CPU#0
[ 0.000000] Checking aperture...
[ 0.000000] No AGP bridge found
[ 0.000000] Memory: 491628k/511988k available (5418k kernel code, 424k
absent, 19936k reserved, 3040k data, 888k init)
[ 0.000000] SLUB: Genslabs=14, HWalign=64, Order=0-3, MinObjects=0,
CPUs=1, Nodes=1
[ 0.000000] Hierarchical RCU implementation.
[ 0.000000] NR_IRQS:4352 nr_irqs:256
[ 0.000000] Console: colour dummy device 80x25
[ 0.000000] console [ttyS0] enabled
[ 0.000000] Detected 2666.884 MHz processor.
[ 0.000000] TSC resynchronization disabled on non-AMD processors
[ 0.020000] Calibrating delay loop (skipped) preset value.. 5333.76
BogoMIPS (lpj=26668840)
[ 0.020000] Security Framework initialized
[ 0.020000] AppArmor: AppArmor initialized
[ 0.020000] Dentry cache hash table entries: 65536 (order: 7, 524288
bytes)
[ 0.020000] Inode-cache hash table entries: 32768 (order: 6, 262144
bytes)
[ 0.020000] Mount-cache hash table entries: 256
[ 0.020000] Initializing cgroup subsys ns
[ 0.020000] Initializing cgroup subsys cpuacct
[ 0.020000] Initializing cgroup subsys memory
[ 0.020000] Initializing cgroup subsys devices
[ 0.020000] Initializing cgroup subsys freezer
[ 0.020000] Initializing cgroup subsys net_cls
[ 0.020000] CPU: L1 I cache: 32K, L1 D cache: 32K
[ 0.020000] CPU: L2 cache: 4096K
[ 0.020000] CPU 0/0x0 -> Node 0
[ 0.020000] mce: CPU supports 10 MCE banks
[ 0.020000] Performance Events: unsupported p6 CPU model 2 no PMU driver,
software events only.
[ 0.020000] SMP alternatives: switching to UP code
[ 0.036487] Freeing SMP alternatives: 40k freed
[ 0.036868] ftrace: converting mcount calls to 0f 1f 44 00 00
[ 0.037294] ftrace: allocating 22510 entries in 89 pages
[ 0.060084] Setting APIC routing to flat
[ 0.060509] CPU0: Intel QEMU Virtual CPU version 0.14.1 stepping 03
[ 0.180142] Brought up 1 CPUs
[ 0.180420] Total of 1 processors activated (5333.76 BogoMIPS).
[ 0.181077] devtmpfs: initialized
[ 0.181589] regulator: core version 0.5
[ 0.181962] Time: 22:24:15 Date: 08/08/11
[ 0.182346] NET: Registered protocol family 16
[ 0.182875] PCI: Using configuration type 1 for base access
[ 0.183873] bio: create slab <bio-0> at 0
[ 0.184298] ACPI: Interpreter disabled.
[ 0.184657] vgaarb: loaded
[ 0.184970] SCSI subsystem initialized
[ 0.185422] usbcore: registered new interface driver usbfs
[ 0.185892] usbcore: registered new interface driver hub
[ 0.186427] usbcore: registered new device driver usb
[ 0.186962] PCI: Probing PCI hardware
[ 0.188091] pci 0000:00:01.3: quirk: region b000-b03f claimed by PIIX4
ACPI
[ 0.188699] pci 0000:00:01.3: quirk: region b100-b10f claimed by PIIX4
SMB
[ 0.190467] pci 0000:00:01.0: PIIX/ICH IRQ router [8086:7000]
[ 0.191162] NetLabel: Initializing
[ 0.191455] NetLabel: domain hash size = 128
[ 0.191820] NetLabel: protocols = UNLABELED CIPSOv4
[ 0.192240] NetLabel: unlabeled traffic allowed by default
[ 0.192734] Switching to clocksource kvm-clock
[ 0.194632] AppArmor: AppArmor Filesystem Enabled
[ 0.195042] pnp: PnP ACPI: disabled
[ 0.195493] NET: Registered protocol family 2
[ 0.195910] IP route cache hash table entries: 4096 (order: 3, 32768
bytes)
[ 0.196685] TCP established hash table entries: 16384 (order: 6, 262144
bytes)
[ 0.197463] TCP bind hash table entries: 16384 (order: 6, 262144 bytes)
[ 0.198174] TCP: Hash tables configured (established 16384 bind 16384)
[ 0.198743] TCP reno registered
[ 0.199067] NET: Registered protocol family 1
[ 0.199446] pci 0000:00:00.0: Limiting direct PCI/PCI transfers
[ 0.199948] pci 0000:00:01.0: PIIX3: Enabling Passive Release
[ 0.200000] pci 0000:00:01.0: Activating ISA DMA hang workarounds
[ 0.200000] platform rtc_cmos: registered platform RTC device (no PNP
device found)
[ 0.200000] Scanning for low memory corruption every 60 seconds
[ 0.200000] audit: initializing netlink socket (disabled)
[ 0.200000] type=2000 audit(1312842256.197:1): initialized
[ 0.206962] Trying to unpack rootfs image as initramfs...
[ 0.208967] Freeing initrd memory: 990k freed
[ 0.209521] HugeTLB registered 2 MB page size, pre-allocated 0 pages
[ 0.211015] VFS: Disk quotas dquot_6.5.2
[ 0.211416] Dquot-cache hash table entries: 512 (order 0, 4096 bytes)
[ 0.212498] fuse init (API version 7.13)
[ 0.212959] msgmni has been set to 962
[ 0.213485] alg: No test for stdrng (krng)
[ 0.213872] Block layer SCSI generic (bsg) driver version 0.4 loaded
(major 253)
[ 0.214489] io scheduler noop registered
[ 0.214820] io scheduler anticipatory registered
[ 0.215206] io scheduler deadline registered
[ 0.215605] io scheduler cfq registered (default)
[ 0.216045] pci_hotplug: PCI Hot Plug PCI Core version: 0.5
[ 0.216533] pciehp: PCI Express Hot Plug Controller Driver version: 0.4
[ 0.217996] Linux agpgart interface v0.103
[ 0.218381] Serial: 8250/16550 driver, 4 ports, IRQ sharing enabled
[ 0.219080] serial8250: ttyS0 at I/O 0x3f8 (irq = 4) is a 16550A
[ 0.220456] brd: module loaded
[ 0.221081] loop: module loaded
[ 0.221412] input: Macintosh mouse button emulation as
/devices/virtual/input/input0
[ 0.222300] scsi0 : ata_piix
[ 0.222642] scsi1 : ata_piix
[ 0.222927] ata1: PATA max MWDMA2 cmd 0x1f0 ctl 0x3f6 bmdma 0xc000 irq 14
[ 0.223507] ata2: PATA max MWDMA2 cmd 0x170 ctl 0x376 bmdma 0xc008 irq 15
[ 0.224454] Fixed MDIO Bus: probed
[ 0.224772] PPP generic driver version 2.4.2
[ 0.225170] tun: Universal TUN/TAP device driver, 1.6
[ 0.225606] tun: (C) 1999-2004 Max Krasnyansky <maxk(a)qualcomm.com>
[ 0.226187] ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
[ 0.226748] ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
[ 0.227271] uhci_hcd: USB Universal Host Controller Interface driver
[ 0.227843] PNP: No PS/2 controller found. Probing ports directly.
[ 0.228779] serio: i8042 KBD port at 0x60,0x64 irq 1
[ 0.229202] serio: i8042 AUX port at 0x60,0x64 irq 12
[ 0.229908] mice: PS/2 mouse device common for all mice
[ 0.230592] input: AT Translated Set 2 keyboard as
/devices/platform/i8042/serio0/input/input1
[ 0.231505] rtc_cmos rtc_cmos: rtc core: registered rtc_cmos as rtc0
[ 0.232078] rtc0: alarms up to one day, 114 bytes nvram
[ 0.232592] device-mapper: uevent: version 1.0.3
[ 0.233076] device-mapper: ioctl: 4.15.0-ioctl (2009-04-01) initialised:
dm-devel(a)redhat.com
[ 0.233851] device-mapper: multipath: version 1.1.0 loaded
[ 0.234315] device-mapper: multipath round-robin: version 1.0.0 loaded
[ 0.234971] cpuidle: using governor ladder
[ 0.235325] cpuidle: using governor menu
[ 0.235868] TCP cubic registered
[ 0.236282] NET: Registered protocol family 10
[ 0.236981] lo: Disabled Privacy Extensions
[ 0.237508] NET: Registered protocol family 17
[ 0.237992] registered taskstats version 1
[ 0.238536] Magic number: 7:322:451
[ 0.238902] rtc_cmos rtc_cmos: setting system clock to 2011-08-08
22:24:15 UTC (1312842255)
[ 0.239598] BIOS EDD facility v0.16 2004-Jun-25, 0 devices found
[ 0.240131] EDD information not available.
[ 0.380353] Freeing unused kernel memory: 888k freed
[ 0.381017] Write protecting the kernel read-only data: 7752k
febootstrap: mounting /proc
febootstrap: uptime: 0.38 0.18
febootstrap: ext2 mini initrd starting up
febootstrap: mounting /sys
febootstrap: internal insmod libcrc32c.ko
insmod: init_module: libcrc32c.ko: Unknown symbol in module
febootstrap: internal insmod crc32c-intel.ko
insmod: init_module: crc32c-intel.ko: No such device
febootstrap: internal insmod crc-itu-t.ko
febootstrap: internal insmod crc32c.ko
febootstrap: internal insmod crc-ccitt.ko
febootstrap: internal insmod crc7.ko
febootstrap: internal insmod scsi_transport_spi.ko
febootstrap: internal insmod sym53c8xx.ko
febootstrap: internal insmod virtio.ko
febootstrap: internal insmod virtio-rng.ko
febootstrap: internal insmod virtio_console.ko
febootstrap: internal insmod virtio_blk.ko
febootstrap: internal insmod virtio_net.ko
febootstrap: internal insmod virtio_ring.ko
febootstrap: internal insmod virtio_pci.ko
[ 0.401786] virtio-pci 0000:00:02.0: found PCI INT A -> IRQ 10
[ 0.403044] virtio-pci 0000:00:03.0: found PCI INT A -> IRQ 11
[ 0.404030] vda: unknown partition table
[ 0.405451] virtio-pci 0000:00:04.0: found PCI INT A -> IRQ 11
[ 0.406442] vdb: unknown partition table
febootstrap: internal insmod virtio_balloon.ko
febootstrap: picked /sys/block/vdb/dev as root device
febootstrap: creating /dev/root as block special 251:16
febootstrap: mounting new root on /root
febootstrap: chroot
/proc/uptime: No such file or directory
execl: /init: No such file or directory
febootstrap: debug: listing directory /
2 d . 040755 4096 88619:5000
2 d .. 040755 4096 88619:5000
11 d lost+found 040700 16384 0:0
12 d bin 040750 4096 23083:5000
13 d boot 040750 4096 23083:5000
14 d etc 040750 4096 23083:5000
109 d lib 040750 4096 23083:5000
156 d sbin 040750 4096 23083:5000
157 d usr 040750 4096 23083:5000
534 d var 040750 4096 23083:5000
690 - init 100750 3264 23083:5000
febootstrap: debug: listing directory /bin
12 d . 040750 4096 23083:5000
2 d .. 040755 4096 88619:5000
540 l rbash 120777 4 0:0 -> bash
541 l sh 120777 9 0:0 -> /bin/bash
febootstrap: debug: listing directory /lib
109 d . 040750 4096 23083:5000
2 d .. 040755 4096 88619:5000
110 d cryptsetup 040750 4096 23083:5000
113 d firmware 040750 4096 23083:5000
145 d init 040750 4096 23083:5000
146 d modules 040750 4096 23083:5000
147 d security 040750 4096 23083:5000
148 d udev 040750 4096 23083:5000
551 l libaio.so.1 120777 15 0:0 -> libaio.so.1.0.1
552 l libbsd.so.0 120777 15 0:0 -> libbsd.so.0.2.0
553 l libdbus-1.so.3 120777 18 0:0 -> libdbus-1.so.3.4.0
554 l libdrm_intel.so.1 120777 21 0:0 -> libdrm_intel.so.1.0.0
555 l libdrm_nouveau.so.1 120777 23 0:0 -> libdrm_nouveau.so.1.0.0
556 l libdrm_radeon.so.1 120777 22 0:0 -> libdrm_radeon.so.1.0.0
557 l libfuse.so.2 120777 16 0:0 -> libfuse.so.2.8.1
558 l libgcrypt.so.11 120777 19 0:0 -> libgcrypt.so.11.5.2
559 l libglib-2.0.so.0 120777 23 0:0 -> libglib-2.0.so.0.2400.1
560 l libnih-dbus.so.1 120777 20 0:0 -> libnih-dbus.so.1.0.0
561 l libnih.so.1 120777 15 0:0 -> libnih.so.1.0.0
562 - libntfs-3g.so.75.0.0 100644 266616 0:0
563 l libntfs-3g.so.75 120777 20 0:0 -> libntfs-3g.so.75.0.0
564 l libparted.so.0 120777 18 0:0 -> libparted.so.0.0.1
565 l libpcre.so.3 120777 17 0:0 -> libpcre.so.3.12.1
566 l libply-boot-client.so.2 120777 27 0:0 -> libply-boot-client.so.2.0.0
567 l libply-splash-core.so.2 120777 27 0:0 -> libply-splash-core.so.2.0.0
568 l libply-splash-graphics.so.2 120777 31 0:0 ->
libply-splash-graphics.so.2.0.0
569 l libply.so.2 120777 15 0:0 -> libply.so.2.0.0
570 l libpng12.so.0 120777 18 0:0 -> libpng12.so.0.42.0
571 l libpopt.so.0 120777 16 0:0 -> libpopt.so.0.0.0
572 l libslang.so.2 120777 17 0:0 -> libslang.so.2.2.2
573 l libss.so.2 120777 12 0:0 -> libss.so.2.0
574 l libudev.so.0 120777 16 0:0 -> libudev.so.0.6.1
575 l libulockmgr.so.1 120777 20 0:0 -> libulockmgr.so.1.0.1
576 l libusb-0.1.so.4 120777 19 0:0 -> libusb-0.1.so.4.4.4
febootstrap: debug: listing directory /lib64
/lib64: No such file or directory
[ 0.437284] Kernel panic - not syncing: Attempted to kill init!
[ 0.437873] Pid: 1, comm: init Not tainted 2.6.32-redacted-generic
#redacted-Ubuntu
[ 0.438573] Call Trace:
[ 0.438826] [<ffffffff81540282>] panic+0x78/0x14b
[ 0.439306] [<ffffffff8106988d>] forget_original_parent+0x30d/0x320
[ 0.440101] [<ffffffff81068cb4>] ? put_files_struct+0xc4/0xf0
[ 0.440686] [<ffffffff810698bb>] exit_notify+0x1b/0x1b0
[ 0.441208] [<ffffffff8106b300>] do_exit+0x1c0/0x390
[ 0.441715] [<ffffffff8106b525>] do_group_exit+0x55/0xd0
[ 0.442245] [<ffffffff8106b5b7>] sys_exit_group+0x17/0x20
[ 0.442795] [<ffffffff810121b2>] system_call_fastpath+0x16/0x1b
[ 0.443434] Rebooting in 1 seconds..libguestfs: error: unexpected end of
file when reading from daemon.
See earlier debug messages.
Or you can run 'libguestfs-test-tool' and post the complete output into
a bug report or message to the libguestfs mailing list.
libguestfs: child_cleanup: 0x12da3c0: child process died
libguestfs-test-tool: failed to launch appliance
libguestfs: closing guestfs handle 0x12da3c0 (state 0)
13 years, 4 months
[PATCH] Report last-modified time of hive root and nodes
by Alex Nelson
The infrastructure for modified-time reporting has been essentially
unused. These changes report the registry time by treating the
time fields as Windows filetime fields stored in little-Endian
(which means they can be treated as a single 64-bit little-Endian
integer). Some of the code changes necessary include:
* Exposing the hive_h structure in the hivex header file (via
generator.ml)
* Adding an additional argument to the node_start function, which
should cause no complications since the change is specific to the
C API.
Signed-off-by: Alex Nelson <ajnelson(a)cs.ucsc.edu>
---
generator/generator.ml | 44 ++++++++++++++++-
lib/hivex.c | 124 +++++++++++++++++++++++++++++++-----------------
xml/hivexml.c | 11 ++++-
3 files changed, 132 insertions(+), 47 deletions(-)
diff --git a/generator/generator.ml b/generator/generator.ml
index 31478cd..f1aa799 100755
--- a/generator/generator.ml
+++ b/generator/generator.ml
@@ -695,6 +695,46 @@ extern \"C\" {
/* NOTE: This API is documented in the man page hivex(3). */
/* Hive handle. */
+struct hive_h {
+ char *filename;
+ int fd;
+ size_t size;
+ int msglvl;
+ int writable;
+
+ /* Registry file, memory mapped if read-only, or malloc'd if writing. */
+ union {
+ char *addr;
+ struct ntreg_header *hdr;
+ };
+
+ /* Use a bitmap to store which file offsets are valid (point to a
+ * used block). We only need to store 1 bit per 32 bits of the file
+ * (because blocks are 4-byte aligned). We found that the average
+ * block size in a registry file is ~50 bytes. So roughly 1 in 12
+ * bits in the bitmap will be set, making it likely a more efficient
+ * structure than a hash table.
+ */
+ char *bitmap;
+#define BITMAP_SET(bitmap,off) (bitmap[(off)>>5] |= 1 << (((off)>>2)&7))
+#define BITMAP_CLR(bitmap,off) (bitmap[(off)>>5] &= ~ (1 << (((off)>>2)&7)))
+#define BITMAP_TST(bitmap,off) (bitmap[(off)>>5] & (1 << (((off)>>2)&7)))
+#define IS_VALID_BLOCK(h,off) \
+ (((off) & 3) == 0 && \
+ (off) >= 0x1000 && \
+ (off) < (h)->size && \
+ BITMAP_TST((h)->bitmap,(off)))
+
+ /* Fields from the header, extracted from little-endianness hell. */
+ size_t rootoffs; /* Root key offset (always an nk-block). */
+ size_t endpages; /* Offset of end of pages. */
+ char *last_modified; /* mtime of base block. */
+
+ /* For writing. */
+ size_t endblocks; /* Offset to next block allocation (0
+ if not allocated anything yet). */
+};
+
typedef struct hive_h hive_h;
/* Nodes and values. */
@@ -761,7 +801,7 @@ typedef struct hive_set_value hive_set_value;
* languages make it much simpler to iterate over a tree.
*/
struct hivex_visitor {
- int (*node_start) (hive_h *, void *opaque, hive_node_h, const char *name);
+ int (*node_start) (hive_h *, void *opaque, hive_node_h, const char *name, const char *last_modified);
int (*node_end) (hive_h *, void *opaque, hive_node_h, const char *name);
int (*value_string) (hive_h *, void *opaque, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *str);
int (*value_multiple_strings) (hive_h *, void *opaque, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, char **argv);
@@ -1110,7 +1150,7 @@ terminates immediately. If you don't need a callback function at
all, set the function pointer to NULL.
struct hivex_visitor {
- int (*node_start) (hive_h *, void *opaque, hive_node_h, const char *name);
+ int (*node_start) (hive_h *, void *opaque, hive_node_h, const char *name, const char *last_modified);
int (*node_end) (hive_h *, void *opaque, hive_node_h, const char *name);
int (*value_string) (hive_h *, void *opaque, hive_node_h, hive_value_h,
hive_type t, size_t len, const char *key, const char *str);
diff --git a/lib/hivex.c b/lib/hivex.c
index fedbb6c..474249f 100644
--- a/lib/hivex.c
+++ b/lib/hivex.c
@@ -33,6 +33,7 @@
#include <sys/mman.h>
#include <sys/stat.h>
#include <assert.h>
+#include <time.h>
#include "c-ctype.h"
#include "full-read.h"
@@ -60,51 +61,12 @@
static char *windows_utf16_to_utf8 (/* const */ char *input, size_t len);
static size_t utf16_string_len_in_bytes_max (const char *str, size_t len);
-struct hive_h {
- char *filename;
- int fd;
- size_t size;
- int msglvl;
- int writable;
-
- /* Registry file, memory mapped if read-only, or malloc'd if writing. */
- union {
- char *addr;
- struct ntreg_header *hdr;
- };
-
- /* Use a bitmap to store which file offsets are valid (point to a
- * used block). We only need to store 1 bit per 32 bits of the file
- * (because blocks are 4-byte aligned). We found that the average
- * block size in a registry file is ~50 bytes. So roughly 1 in 12
- * bits in the bitmap will be set, making it likely a more efficient
- * structure than a hash table.
- */
- char *bitmap;
-#define BITMAP_SET(bitmap,off) (bitmap[(off)>>5] |= 1 << (((off)>>2)&7))
-#define BITMAP_CLR(bitmap,off) (bitmap[(off)>>5] &= ~ (1 << (((off)>>2)&7)))
-#define BITMAP_TST(bitmap,off) (bitmap[(off)>>5] & (1 << (((off)>>2)&7)))
-#define IS_VALID_BLOCK(h,off) \
- (((off) & 3) == 0 && \
- (off) >= 0x1000 && \
- (off) < (h)->size && \
- BITMAP_TST((h)->bitmap,(off)))
-
- /* Fields from the header, extracted from little-endianness hell. */
- size_t rootoffs; /* Root key offset (always an nk-block). */
- size_t endpages; /* Offset of end of pages. */
-
- /* For writing. */
- size_t endblocks; /* Offset to next block allocation (0
- if not allocated anything yet). */
-};
-
/* NB. All fields are little endian. */
struct ntreg_header {
char magic[4]; /* "regf" */
uint32_t sequence1;
uint32_t sequence2;
- char last_modified[8];
+ uint64_t last_modified;
uint32_t major_ver; /* 1 */
uint32_t minor_ver; /* 3 */
uint32_t unknown5; /* 0 */
@@ -173,7 +135,7 @@ struct ntreg_nk_record {
int32_t seg_len; /* length (always -ve because used) */
char id[2]; /* "nk" */
uint16_t flags;
- char timestamp[8];
+ uint64_t timestamp;
uint32_t unknown1;
uint32_t parent; /* offset of owner/parent */
uint32_t nr_subkeys; /* number of subkeys */
@@ -265,7 +227,34 @@ header_checksum (const hive_h *h)
return sum;
}
+#define WINDOWS_TICK 10000000LL
+#define SEC_TO_UNIX_EPOCH 11644473600LL
+/**
+ * Convert Windows filetime to ISO 8601 format.
+ * Source for filetime->time_t conversion: http://stackoverflow.com/questions/6161776/convert-windows-filetime-to-se...
+ * Source for time_t->char* conversion: Fiwalk version 0.6.14's fiwalk.cpp.
+ * @param windows_ticks Expected to not have any remaining Endian issues.
+ */
+int
+filetime_to_8601 (char *buf, int bufsize, uint64_t windows_ticks)
+{
+ if (buf == NULL) {
+ fprintf (stderr, "filetime_to_8601: Received null output buffer, unable to proceed.\n");
+ return -1;
+ }
+ uint64_t nanos = windows_ticks % WINDOWS_TICK;
+ time_t tt = (windows_ticks / WINDOWS_TICK - SEC_TO_UNIX_EPOCH);
+ struct tm time_tm;
+ if (gmtime_r (&tt, &time_tm) == NULL) {
+ fprintf (stderr, "filetime_to_8601: Error running gmtime_r on timestamp (decimal hundreds of ns: %" PRIu64 ").\n", windows_ticks);
+ return -1;
+ }
+ strftime(buf, bufsize, "%FT%TZ", &time_tm);
+ return 0;
+}
+
#define HIVEX_OPEN_MSGLVL_MASK (HIVEX_OPEN_VERBOSE|HIVEX_OPEN_DEBUG)
+#define TIMESTAMP_BUF_LEN 32
hive_h *
hivex_open (const char *filename, int flags)
@@ -359,6 +348,15 @@ hivex_open (const char *filename, int flags)
goto error;
}
+ /* Last-modified time. */
+ h->last_modified = (char *) calloc(1 + TIMESTAMP_BUF_LEN, sizeof(char));
+ int ft_rc = filetime_to_8601(h->last_modified, TIMESTAMP_BUF_LEN, le64toh ((uint64_t) h->hdr->last_modified));
+ if (ft_rc) {
+ fprintf (stderr, "hivex: failed to parse time value\n");
+ free(h->last_modified);
+ h->last_modified = NULL;
+ }
+
if (h->msglvl >= 2) {
char *name = windows_utf16_to_utf8 (h->hdr->name, 64);
@@ -367,6 +365,8 @@ hivex_open (const char *filename, int flags)
" file version %" PRIu32 ".%" PRIu32 "\n"
" sequence nos %" PRIu32 " %" PRIu32 "\n"
" (sequences nos should match if hive was synched at shutdown)\n"
+ " last modified %s\n"
+ " (decimal, 100 ns) %" PRIu64 "\n"
" original file name %s\n"
" (only 32 chars are stored, name is probably truncated)\n"
" root offset 0x%x + 0x1000\n"
@@ -374,6 +374,8 @@ hivex_open (const char *filename, int flags)
" checksum 0x%x (calculated 0x%x)\n",
major_ver, le32toh (h->hdr->minor_ver),
le32toh (h->hdr->sequence1), le32toh (h->hdr->sequence2),
+ h->last_modified,
+ le64toh (h->hdr->last_modified),
name ? name : "(conversion failed)",
le32toh (h->hdr->offset),
le32toh (h->hdr->blocks), h->size,
@@ -541,6 +543,10 @@ hivex_close (hive_h *h)
if (h->msglvl >= 1)
fprintf (stderr, "hivex_close\n");
+ if (h->last_modified) {
+ free (h->last_modified);
+ h->last_modified = NULL;
+ }
free (h->bitmap);
if (!h->writable)
munmap (h->addr, h->size);
@@ -608,6 +614,33 @@ hivex_node_name (hive_h *h, hive_node_h node)
return ret;
}
+/* Caller responsible for freeing returned char* if non-null. */
+char *
+hivex_node_mtime (hive_h *h, hive_node_h node)
+{
+ int ft_rc;
+ if (!IS_VALID_BLOCK (h, node) || !BLOCK_ID_EQ (h, node, "nk")) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ struct ntreg_nk_record *nk = (struct ntreg_nk_record *) (h->addr + node);
+
+ char *ret = calloc (32 + 1, sizeof(char));
+ if (ret == NULL)
+ return ret;
+ ft_rc = filetime_to_8601 (ret, 32, le64toh (nk->timestamp));
+ if (h->msglvl >= 2) {
+ fprintf(stderr, "hivex_node_mtime: nk->timestamp: %" PRIu64 "\n", nk->timestamp);
+ fprintf(stderr, "hivex_node_mtime: ret: %s\n", ret);
+ }
+ if (ft_rc) {
+ free(ret);
+ ret = NULL;
+ }
+ return ret;
+}
+
#if 0
/* I think the documentation for the sk and classname fields in the nk
* record is wrong, or else the offset field is in the wrong place.
@@ -1560,6 +1593,7 @@ hivex__visit_node (hive_h *h, hive_node_h node,
{
int skip_bad = flags & HIVEX_VISIT_SKIP_BAD;
char *name = NULL;
+ char *last_modified = NULL;
hive_value_h *values = NULL;
hive_node_h *children = NULL;
char *key = NULL;
@@ -1584,8 +1618,11 @@ hivex__visit_node (hive_h *h, hive_node_h node,
BITMAP_CLR (unvisited, node);
name = hivex_node_name (h, node);
+ last_modified = hivex_node_mtime (h, node);
+ if (h->msglvl >= 2)
+ fprintf(stderr, "hivex__visit_node: last_modified: %s\n", last_modified ? last_modified : "NULL" );
if (!name) return skip_bad ? 0 : -1;
- if (vtor->node_start && vtor->node_start (h, opaque, node, name) == -1)
+ if (vtor->node_start && vtor->node_start (h, opaque, node, name, last_modified) == -1)
goto error;
values = hivex_node_values (h, node);
@@ -1764,6 +1801,7 @@ hivex__visit_node (hive_h *h, hive_node_h node,
error:
free (name);
+ free (last_modified);
free (values);
free (children);
free (key);
@@ -2264,7 +2302,7 @@ hivex_node_add_child (hive_h *h, hive_node_h parent, const char *name)
nk->sk = htole32 (parent_sk_offset - 0x1000);
/* Inherit parent timestamp. */
- memcpy (nk->timestamp, parent_nk->timestamp, sizeof (parent_nk->timestamp));
+ nk->timestamp = parent_nk->timestamp;
/* What I found out the hard way (not documented anywhere): the
* subkeys in lh-records must be kept sorted. If you just add a
diff --git a/xml/hivexml.c b/xml/hivexml.c
index 90cb22b..b32eaa2 100644
--- a/xml/hivexml.c
+++ b/xml/hivexml.c
@@ -40,7 +40,7 @@
#endif
/* Callback functions. */
-static int node_start (hive_h *, void *, hive_node_h, const char *name);
+static int node_start (hive_h *, void *, hive_node_h, const char *name, const char *last_modified);
static int node_end (hive_h *, void *, hive_node_h, const char *name);
static int value_string (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *str);
static int value_multiple_strings (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, char **argv);
@@ -123,6 +123,8 @@ main (int argc, char *argv[])
XML_CHECK (xmlTextWriterStartDocument, (writer, NULL, "utf-8", NULL));
XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "hive"));
+ if (h->last_modified)
+ XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "mtime", BAD_CAST (h->last_modified)));
if (hivex_visit (h, &visitor, sizeof visitor, writer, visit_flags) == -1) {
perror (argv[optind]);
@@ -142,11 +144,16 @@ main (int argc, char *argv[])
}
static int
-node_start (hive_h *h, void *writer_v, hive_node_h node, const char *name)
+node_start (hive_h *h, void *writer_v, hive_node_h node, const char *name, const char *last_modified)
{
xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "node"));
XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "name", BAD_CAST name));
+ if (last_modified) {
+ XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "mtime", BAD_CAST last_modified));
+ } else {
+ fprintf(stderr, "node_start: last_modified came across NULL.\n");
+ }
return 0;
}
--
1.7.6
13 years, 4 months