Richard W.M. Jones
2012-Aug-29 15:15 UTC
[Libguestfs] [PATCH 0/4] Add hivex APIs into the libguestfs API (RHBZ#852394)
This adds most of the hivex APIs directly to the libguestfs API, so that you can read and write Windows Registry hive files from libguestfs without needing to download and upload hive files from the guest. This is analogous to how Augeas APIs are exposed already (guestfs_aug_*) Also, inspection is now done using the new APIs, which fixes the following bug: https://bugzilla.redhat.com/show_bug.cgi?id=852394 "libguestfs inspection limits registries to 100 MiB" I tested this by: (a) Running virt-inspector before and after over a set of test Windows guests. There was no difference in the output. (b) Running 'make check' and 'make extra-tests'. These tests are still running. If they succeed (particularly extra-tests which uses valgrind) then that should give us high confidence there are no memory errors. Rich.
Richard W.M. Jones
2012-Aug-29 15:15 UTC
[Libguestfs] [PATCH 1/4] lib: Remove AUGEAS_CFLAGS, AUGEAS_LIBS.
From: "Richard W.M. Jones" <rjones at redhat.com> The library doesn't actually use libaugeas, except indirectly via the libguestfs API. The libguestfs API implements this in the daemon, so there's no need for the library to link to augeas at all. --- src/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 4d85747..5d3639a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -148,7 +148,7 @@ libguestfs_la_SOURCES = \ libguestfs.syms libguestfs_la_LIBADD = \ - $(HIVEX_LIBS) $(AUGEAS_LIBS) $(PCRE_LIBS) $(MAGIC_LIBS) \ + $(HIVEX_LIBS) $(PCRE_LIBS) $(MAGIC_LIBS) \ $(LIBVIRT_LIBS) $(LIBXML2_LIBS) \ ../gnulib/lib/libgnu.la \ $(GETADDRINFO_LIB) \ @@ -167,7 +167,7 @@ libguestfs_la_LIBADD += liberrnostring.la libprotocol.la libguestfs_la_CFLAGS = \ -DGUESTFS_DEFAULT_PATH='"$(libdir)/guestfs"' \ -DGUESTFS_WARN_DEPRECATED=1 \ - $(HIVEX_CFLAGS) $(AUGEAS_CFLAGS) $(PCRE_CFLAGS) \ + $(HIVEX_CFLAGS) $(PCRE_CFLAGS) \ $(LIBVIRT_CFLAGS) $(LIBXML2_CFLAGS) \ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ $(GCC_VISIBILITY_HIDDEN) -- 1.7.10.4
From: "Richard W.M. Jones" <rjones at redhat.com> Transscribe many hivex(3) APIs into the libguestfs API. There is one hive handle per libguestfs handle, as with Augeas. Note that hivex uses iconv_open for some APIs (eg. hivex_value_string). But since we delete all the i18n files from the appliance, this doesn't work -- iconv_open returns EINVAL. Therefore hivex APIs which require iconv cannot be bound in the daemon. --- appliance/packagelist.in | 3 + daemon/Makefile.am | 7 +- daemon/hivex.c | 509 ++++++++++++++++++++++++++++++ generator/generator_actions.ml | 187 +++++++++++ generator/generator_structs.ml | 14 + gobject/Makefile.inc | 10 +- java/Makefile.inc | 2 + java/com/redhat/et/libguestfs/.gitignore | 2 + po/POTFILES | 4 + src/MAX_PROC_NR | 2 +- src/guestfs.pod | 15 +- 11 files changed, 745 insertions(+), 10 deletions(-) create mode 100644 daemon/hivex.c diff --git a/appliance/packagelist.in b/appliance/packagelist.in index 4830962..6d412cb 100644 --- a/appliance/packagelist.in +++ b/appliance/packagelist.in @@ -30,6 +30,7 @@ gfs2-utils grub hfsplus-tools + hivex iputils kernel MAKEDEV @@ -57,6 +58,7 @@ hfsplus iproute libaugeas0 + libhivex0 linux-image nilfs-tools ntfs-3g @@ -76,6 +78,7 @@ btrfs-progs-unstable cryptsetup augeas + hivex zfs-fuse e2fsprogs grub diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 9ea6ce3..ffd8b81 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -121,6 +121,7 @@ guestfsd_SOURCES = \ guestfsd.c \ headtail.c \ hexdump.c \ + hivex.c \ htonl.c \ initrd.c \ inotify.c \ @@ -176,6 +177,7 @@ guestfsd_LDADD = \ libprotocol.a \ $(SELINUX_LIB) \ $(AUGEAS_LIBS) \ + $(HIVEX_LIBS) \ $(top_builddir)/gnulib/lib/.libs/libgnu.a \ $(GETADDRINFO_LIB) \ $(HOSTENT_LIB) \ @@ -186,6 +188,9 @@ guestfsd_LDADD = \ $(SERVENT_LIB) guestfsd_CPPFLAGS = -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib -guestfsd_CFLAGS = $(WARN_CFLAGS) $(WERROR_CFLAGS) $(AUGEAS_CFLAGS) +guestfsd_CFLAGS = \ + $(WARN_CFLAGS) $(WERROR_CFLAGS) \ + $(AUGEAS_CFLAGS) \ + $(HIVEX_CFLAGS) .PHONY: force diff --git a/daemon/hivex.c b/daemon/hivex.c new file mode 100644 index 0000000..f13d3d7 --- /dev/null +++ b/daemon/hivex.c @@ -0,0 +1,509 @@ +/* libguestfs - the guestfsd daemon + * Copyright (C) 2012 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> + +#include "guestfs_protocol.h" +#include "daemon.h" +#include "actions.h" +#include "optgroups.h" + +#ifdef HAVE_HIVEX + +#include <hivex.h> + +int +optgroup_hivex_available (void) +{ + return 1; +} + +/* The hivex handle. As with Augeas, there is one per guestfs handle / + * daemon. + */ +static hive_h *h = NULL; + +/* Clean up the hivex handle on daemon exit. */ +static void hivex_finalize (void) __attribute__((destructor)); +static void +hivex_finalize (void) +{ + if (h) { + hivex_close (h); + h = NULL; + } +} + +#define NEED_HANDLE(errcode) \ + do { \ + if (!h) { \ + reply_with_error ("%s: you must call 'hivex-open' first to initialize the hivex handle", __func__); \ + return (errcode); \ + } \ + } \ + while (0) + +/* Takes optional arguments, consult optargs_bitmask. */ +int +do_hivex_open (const char *filename, int verbose, int debug, int write) +{ + char *buf; + int flags = 0; + + if (h) { + hivex_close (h); + h = NULL; + } + + buf = sysroot_path (filename); + if (!buf) { + reply_with_perror ("malloc"); + return -1; + } + + if (optargs_bitmask & GUESTFS_HIVEX_OPEN_VERBOSE_BITMASK) { + if (verbose) + flags |= HIVEX_OPEN_VERBOSE; + } + if (optargs_bitmask & GUESTFS_HIVEX_OPEN_DEBUG_BITMASK) { + if (debug) + flags |= HIVEX_OPEN_DEBUG; + } + if (optargs_bitmask & GUESTFS_HIVEX_OPEN_WRITE_BITMASK) { + if (write) + flags |= HIVEX_OPEN_WRITE; + } + + h = hivex_open (buf, flags); + if (!h) { + reply_with_perror ("hivex failed to open %s", filename); + free (buf); + return -1; + } + + free (buf); + return 0; +} + +int +do_hivex_close (void) +{ + NEED_HANDLE (-1); + + hivex_close (h); + h = NULL; + + return 0; +} + +int64_t +do_hivex_root (void) +{ + int64_t r; + + NEED_HANDLE (-1); + + r = hivex_root (h); + if (r == 0) { + reply_with_perror ("failed"); + return -1; + } + + return r; +} + +char * +do_hivex_node_name (int64_t nodeh) +{ + char *r; + + NEED_HANDLE (NULL); + + r = hivex_node_name (h, nodeh); + if (r == NULL) { + reply_with_perror ("failed"); + return NULL; + } + + return r; +} + +guestfs_int_hivex_node_list * +do_hivex_node_children (int64_t nodeh) +{ + guestfs_int_hivex_node_list *ret; + hive_node_h *r; + size_t i, len; + + NEED_HANDLE (NULL); + + r = hivex_node_children (h, nodeh); + if (r == NULL) { + reply_with_perror ("failed"); + return NULL; + } + + len = 0; + for (i = 0; r[i] != 0; ++i) + len++; + + ret = malloc (sizeof *ret); + if (!ret) { + reply_with_perror ("malloc"); + free (r); + return NULL; + } + + ret->guestfs_int_hivex_node_list_len = len; + ret->guestfs_int_hivex_node_list_val + malloc (len * sizeof (guestfs_int_hivex_node)); + if (ret->guestfs_int_hivex_node_list_val == NULL) { + reply_with_perror ("malloc"); + free (ret); + free (r); + return NULL; + } + + for (i = 0; i < len; ++i) + ret->guestfs_int_hivex_node_list_val[i].hivex_node_h = r[i]; + + free (r); + + return ret; +} + +int64_t +do_hivex_node_get_child (int64_t nodeh, const char *name) +{ + int64_t r; + + NEED_HANDLE (-1); + + errno = 0; + r = hivex_node_get_child (h, nodeh, name); + if (r == 0 && errno != 0) { + reply_with_perror ("failed"); + return -1; + } + + return r; +} + +int64_t +do_hivex_node_parent (int64_t nodeh) +{ + int64_t r; + + NEED_HANDLE (-1); + + r = hivex_node_parent (h, nodeh); + if (r == 0) { + reply_with_perror ("failed"); + return -1; + } + + return r; +} + +guestfs_int_hivex_value_list * +do_hivex_node_values (int64_t nodeh) +{ + guestfs_int_hivex_value_list *ret; + hive_value_h *r; + size_t i, len; + + NEED_HANDLE (NULL); + + r = hivex_node_values (h, nodeh); + if (r == NULL) { + reply_with_perror ("failed"); + return NULL; + } + + len = 0; + for (i = 0; r[i] != 0; ++i) + len++; + + ret = malloc (sizeof *ret); + if (!ret) { + reply_with_perror ("malloc"); + free (r); + return NULL; + } + + ret->guestfs_int_hivex_value_list_len = len; + ret->guestfs_int_hivex_value_list_val + malloc (len * sizeof (guestfs_int_hivex_value)); + if (ret->guestfs_int_hivex_value_list_val == NULL) { + reply_with_perror ("malloc"); + free (ret); + free (r); + return NULL; + } + + for (i = 0; i < len; ++i) + ret->guestfs_int_hivex_value_list_val[i].hivex_value_h = (int64_t) r[i]; + + free (r); + + return ret; +} + +int64_t +do_hivex_node_get_value (int64_t nodeh, const char *key) +{ + int64_t r; + + NEED_HANDLE (-1); + + errno = 0; + r = hivex_node_get_value (h, nodeh, key); + if (r == 0 && errno != 0) { + reply_with_perror ("failed"); + return -1; + } + + return r; +} + +char * +do_hivex_value_key (int64_t valueh) +{ + char *r; + + NEED_HANDLE (NULL); + + r = hivex_value_key (h, valueh); + if (r == NULL) { + reply_with_perror ("failed"); + return NULL; + } + + return r; +} + +int64_t +do_hivex_value_type (int64_t valueh) +{ + hive_type r; + + NEED_HANDLE (-1); + + if (hivex_value_type (h, valueh, &r, NULL) == -1) { + reply_with_perror ("failed"); + return -1; + } + + return r; +} + +char * +do_hivex_value_value (int64_t valueh, size_t *size_r) +{ + char *r; + size_t size; + + NEED_HANDLE (NULL); + + r = hivex_value_value (h, valueh, NULL, &size); + if (r == NULL) { + reply_with_perror ("failed"); + return NULL; + } + + *size_r = size; + return r; +} + +int +do_hivex_commit (const char *filename) +{ + NEED_HANDLE (-1); + + if (hivex_commit (h, filename, 0) == -1) { + reply_with_perror ("failed"); + return -1; + } + + return 0; +} + +int64_t +do_hivex_node_add_child (int64_t parent, const char *name) +{ + int64_t r; + + NEED_HANDLE (-1); + + r = hivex_node_add_child (h, parent, name); + if (r == 0) { + reply_with_perror ("failed"); + return -1; + } + + return r; +} + +int +do_hivex_node_delete_child (int64_t nodeh) +{ + NEED_HANDLE (-1); + + if (hivex_node_delete_child (h, nodeh) == -1) { + reply_with_perror ("failed"); + return -1; + } + + return 0; +} + +int +do_hivex_node_set_value (int64_t nodeh, + const char *key, int64_t t, + const char *val, size_t val_size) +{ + const hive_set_value v + { .key = (char *) key, .t = t, .len = val_size, .value = (char *) val }; + + NEED_HANDLE (-1); + + if (hivex_node_set_value (h, nodeh, &v, 0) == -1) { + reply_with_perror ("failed"); + return -1; + } + + return 0; +} + +#else /* !HAVE_HIVEX */ + +/* Note that the wrapper code (daemon/stubs.c) ensures that the + * functions below are never called because optgroup_hivex_available + * returns false. + */ +int +optgroup_hivex_available (void) +{ + return 0; +} + +int __attribute__((noreturn)) +do_hivex_open (const char *filename, int verbose, int debug, int write) +{ + abort (); +} + +int __attribute__((noreturn)) +do_hivex_close (void) +{ + abort (); +} + +int64_t __attribute__((noreturn)) +do_hivex_root (void) +{ + abort (); +} + +char * __attribute__((noreturn)) +do_hivex_node_name (int64_t nodeh) +{ + abort (); +} + +guestfs_int_hivex_node_list * __attribute__((noreturn)) +do_hivex_node_children (int64_t nodeh) +{ + abort (); +} + +int64_t __attribute__((noreturn)) +do_hivex_node_get_child (int64_t nodeh, const char *name) +{ + abort (); +} + +int64_t __attribute__((noreturn)) +do_hivex_node_parent (int64_t nodeh) +{ + abort (); +} + +guestfs_int_hivex_value_list * __attribute__((noreturn)) +do_hivex_node_values (int64_t nodeh) +{ + abort (); +} + +int64_t __attribute__((noreturn)) +do_hivex_node_get_value (int64_t nodeh, const char *key) +{ + abort (); +} + +char * __attribute__((noreturn)) +do_hivex_value_key (int64_t valueh) +{ + abort (); +} + +int64_t __attribute__((noreturn)) +do_hivex_value_type (int64_t valueh) +{ + abort (); +} + +char * __attribute__((noreturn)) +do_hivex_value_value (int64_t valueh, size_t *size_r) +{ + abort (); +} + +int __attribute__((noreturn)) +do_hivex_commit (const char *filename) +{ + abort (); +} + +int64_t __attribute__((noreturn)) +do_hivex_node_add_child (int64_t parent, const char *name) +{ + abort (); +} + +int __attribute__((noreturn)) +do_hivex_node_delete_child (int64_t nodeh) +{ + abort (); +} + +int __attribute__((noreturn)) +do_hivex_node_set_value (int64_t nodeh, const char *key, int64_t t, const char *val, size_t val_size) +{ + abort (); +} + +#endif /* !HAVE_HIVEX */ diff --git a/generator/generator_actions.ml b/generator/generator_actions.ml index dbf5d00..6bcd053 100644 --- a/generator/generator_actions.ml +++ b/generator/generator_actions.ml @@ -9477,6 +9477,193 @@ Some of the parameters of a mounted filesystem can be examined and modified using the C<guestfs_xfs_info> and C<guestfs_xfs_growfs> calls." }; + { defaults with + name = "hivex_open"; + style = RErr, [Pathname "filename"], [OBool "verbose"; OBool "debug"; OBool "write"]; + proc_nr = Some 350; + optional = Some "hivex"; + shortdesc = "open a Windows Registry hive file"; + longdesc = "\ +Open the Windows Registry hive file named C<filename>. +If there was any previous hivex handle associated with this +guestfs session, then it is closed. + +This is a wrapper around the L<hivex(3)> call of the same name." }; + + { defaults with + name = "hivex_close"; + style = RErr, [], []; + proc_nr = Some 351; + optional = Some "hivex"; + shortdesc = "close the current hivex handle"; + longdesc = "\ +Close the current hivex handle. + +This is a wrapper around the L<hivex(3)> call of the same name." }; + + { defaults with + name = "hivex_root"; + style = RInt64 "nodeh", [], []; + proc_nr = Some 352; + optional = Some "hivex"; + shortdesc = "return the root node of the hive"; + longdesc = "\ +Return the root node of the hive. + +This is a wrapper around the L<hivex(3)> call of the same name." }; + + { defaults with + name = "hivex_node_name"; + style = RString "name", [Int64 "nodeh"], []; + proc_nr = Some 353; + optional = Some "hivex"; + shortdesc = "return the name of the node"; + longdesc = "\ +Return the name of C<nodeh>. + +This is a wrapper around the L<hivex(3)> call of the same name." }; + + { defaults with + name = "hivex_node_children"; + style = RStructList ("nodehs", "hivex_node"), [Int64 "nodeh"], []; + proc_nr = Some 354; + optional = Some "hivex"; + shortdesc = "return list of nodes which are subkeys of node"; + longdesc = "\ +Return the list of nodes which are subkeys of C<nodeh>. + +This is a wrapper around the L<hivex(3)> call of the same name." }; + + { defaults with + name = "hivex_node_get_child"; + style = RInt64 "child", [Int64 "nodeh"; String "name"], []; + proc_nr = Some 355; + optional = Some "hivex"; + shortdesc = "return the named child of node"; + longdesc = "\ +Return the child of C<nodeh> with the name C<name>, if it exists. +This can return C<0> meaning the name was not found. + +This is a wrapper around the L<hivex(3)> call of the same name." }; + + { defaults with + name = "hivex_node_parent"; + style = RInt64 "parent", [Int64 "nodeh"], []; + proc_nr = Some 356; + optional = Some "hivex"; + shortdesc = "return the parent of node"; + longdesc = "\ +Return the parent node of C<nodeh>. + +This is a wrapper around the L<hivex(3)> call of the same name." }; + + { defaults with + name = "hivex_node_values"; + style = RStructList ("valuehs", "hivex_value"), [Int64 "nodeh"], []; + proc_nr = Some 357; + optional = Some "hivex"; + shortdesc = "return list of values attached to node"; + longdesc = "\ +Return the array of (key, datatype, data) tuples attached to C<nodeh>. + +This is a wrapper around the L<hivex(3)> call of the same name." }; + + { defaults with + name = "hivex_node_get_value"; + style = RInt64 "valueh", [Int64 "nodeh"; String "key"], []; + proc_nr = Some 358; + optional = Some "hivex"; + shortdesc = "return the named value"; + longdesc = "\ +Return the value attached to C<nodeh> which has the +name C<key>, if it exists. This can return C<0> meaning +the key was not found. + +This is a wrapper around the L<hivex(3)> call of the same name." }; + + { defaults with + name = "hivex_value_key"; + style = RString "key", [Int64 "valueh"], []; + proc_nr = Some 359; + optional = Some "hivex"; + shortdesc = "return the key field from the (key, datatype, data) tuple"; + longdesc = "\ +Return the key (name) field of a (key, datatype, data) tuple. + +This is a wrapper around the L<hivex(3)> call of the same name." }; + + { defaults with + name = "hivex_value_type"; + style = RInt64 "datatype", [Int64 "valueh"], []; + proc_nr = Some 360; + optional = Some "hivex"; + shortdesc = "return the data type from the (key, datatype, data) tuple"; + longdesc = "\ +Return the data type field from a (key, datatype, data) tuple. + +This is a wrapper around the L<hivex(3)> call of the same name." }; + + { defaults with + name = "hivex_value_value"; + style = RBufferOut "databuf", [Int64 "valueh"], []; + proc_nr = Some 361; + optional = Some "hivex"; + shortdesc = "return the data field from the (key, datatype, data) tuple"; + longdesc = "\ +Return the data field of a (key, datatype, data) tuple. + +This is a wrapper around the L<hivex(3)> call of the same name." }; + + { defaults with + name = "hivex_commit"; + style = RErr, [OptString "filename"], []; + proc_nr = Some 362; + optional = Some "hivex"; + shortdesc = "commit (write) changes back to the hive"; + longdesc = "\ +Commit (write) changes to the hive. + +If the optional C<filename> parameter is null, then the changes +are written back to the same hive that was opened. If this is +not null then they are written to the alternate filename given +and the original hive is left untouched. + +This is a wrapper around the L<hivex(3)> call of the same name." }; + + { defaults with + name = "hivex_node_add_child"; + style = RInt64 "nodeh", [Int64 "parent"; String "name"], []; + proc_nr = Some 363; + optional = Some "hivex"; + shortdesc = "add a child node"; + longdesc = "\ +Add a child node to C<parent> named C<name>. + +This is a wrapper around the L<hivex(3)> call of the same name." }; + + { defaults with + name = "hivex_node_delete_child"; + style = RErr, [Int64 "nodeh"], []; + proc_nr = Some 364; + optional = Some "hivex"; + shortdesc = "delete a node (recursively)"; + longdesc = "\ +Delete C<nodeh>, recursively if necessary. + +This is a wrapper around the L<hivex(3)> call of the same name." }; + + { defaults with + name = "hivex_node_set_value"; + style = RErr, [Int64 "nodeh"; String "key"; Int64 "t"; BufferIn "val"], []; + proc_nr = Some 365; + optional = Some "hivex"; + shortdesc = "set or replace a single value in a node"; + longdesc = "\ +Set or replace a single value under the node C<nodeh>. The +C<key> is the name, C<t> is the type, and C<val> is the data. + +This is a wrapper around the L<hivex(3)> call of the same name." }; + ] (* Non-API meta-commands available only in guestfish. diff --git a/generator/generator_structs.ml b/generator/generator_structs.ml index d4fc1ce..2fa373f 100644 --- a/generator/generator_structs.ml +++ b/generator/generator_structs.ml @@ -263,6 +263,18 @@ let structs = [ "uts_version", FString; "uts_machine", FString; ]; + + (* Used by hivex_* APIs to return a list of int64 handles (node + * handles and value handles). Note that we can't add a putative + * 'RInt64List' type to the generator because we need to return + * length and size, and RStructList does this already. + *) + "hivex_node", [ + "hivex_node_h", FInt64; + ]; + "hivex_value", [ + "hivex_value_h", FInt64; + ]; ] (* end of structs *) (* For bindings which want camel case *) @@ -284,6 +296,8 @@ let camel_structs = [ "mdstat", "MDStat"; "btrfssubvolume", "BTRFSSubvolume"; "utsname", "UTSName"; + "hivex_node", "HivexNode"; + "hivex_value", "HivexValue"; ] let camel_structs = List.sort (fun (_,a) (_,b) -> compare a b) camel_structs diff --git a/gobject/Makefile.inc b/gobject/Makefile.inc index 13dc3e3..26d1c3c 100644 --- a/gobject/Makefile.inc +++ b/gobject/Makefile.inc @@ -40,6 +40,8 @@ guestfs_gobject_headers= \ include/guestfs-gobject/struct-btrfssubvolume.h \ include/guestfs-gobject/struct-xfsinfo.h \ include/guestfs-gobject/struct-utsname.h \ + include/guestfs-gobject/struct-hivex_node.h \ + include/guestfs-gobject/struct-hivex_value.h \ include/guestfs-gobject/optargs-internal_test.h \ include/guestfs-gobject/optargs-add_drive.h \ include/guestfs-gobject/optargs-add_domain.h \ @@ -74,7 +76,8 @@ guestfs_gobject_headers= \ include/guestfs-gobject/optargs-rsync.h \ include/guestfs-gobject/optargs-rsync_in.h \ include/guestfs-gobject/optargs-rsync_out.h \ - include/guestfs-gobject/optargs-xfs_admin.h + include/guestfs-gobject/optargs-xfs_admin.h \ + include/guestfs-gobject/optargs-hivex_open.h guestfs_gobject_sources= \ src/session.c \ @@ -96,6 +99,8 @@ guestfs_gobject_sources= \ src/struct-btrfssubvolume.c \ src/struct-xfsinfo.c \ src/struct-utsname.c \ + src/struct-hivex_node.c \ + src/struct-hivex_value.c \ src/optargs-internal_test.c \ src/optargs-add_drive.c \ src/optargs-add_domain.c \ @@ -130,4 +135,5 @@ guestfs_gobject_sources= \ src/optargs-rsync.c \ src/optargs-rsync_in.c \ src/optargs-rsync_out.c \ - src/optargs-xfs_admin.c + src/optargs-xfs_admin.c \ + src/optargs-hivex_open.c diff --git a/java/Makefile.inc b/java/Makefile.inc index 2b4b35e..73b45cc 100644 --- a/java/Makefile.inc +++ b/java/Makefile.inc @@ -23,6 +23,8 @@ java_built_sources = \ com/redhat/et/libguestfs/Application.java \ com/redhat/et/libguestfs/BTRFSSubvolume.java \ com/redhat/et/libguestfs/Dirent.java \ + com/redhat/et/libguestfs/HivexNode.java \ + com/redhat/et/libguestfs/HivexValue.java \ com/redhat/et/libguestfs/INotifyEvent.java \ com/redhat/et/libguestfs/ISOInfo.java \ com/redhat/et/libguestfs/IntBool.java \ diff --git a/java/com/redhat/et/libguestfs/.gitignore b/java/com/redhat/et/libguestfs/.gitignore index 80b245d..72a1144 100644 --- a/java/com/redhat/et/libguestfs/.gitignore +++ b/java/com/redhat/et/libguestfs/.gitignore @@ -1,6 +1,8 @@ Application.java BTRFSSubvolume.java Dirent.java +HivexNode.java +HivexValue.java INotifyEvent.java ISOInfo.java IntBool.java diff --git a/po/POTFILES b/po/POTFILES index 65dea19..13f20c1 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -41,6 +41,7 @@ daemon/grub.c daemon/guestfsd.c daemon/headtail.c daemon/hexdump.c +daemon/hivex.c daemon/htonl.c daemon/initrd.c daemon/inotify.c @@ -148,6 +149,7 @@ gobject/src/optargs-copy_file_to_file.c gobject/src/optargs-e2fsck.c gobject/src/optargs-fstrim.c gobject/src/optargs-grep.c +gobject/src/optargs-hivex_open.c gobject/src/optargs-inspect_get_icon.c gobject/src/optargs-internal_test.c gobject/src/optargs-md_create.c @@ -174,6 +176,8 @@ gobject/src/session.c gobject/src/struct-application.c gobject/src/struct-btrfssubvolume.c gobject/src/struct-dirent.c +gobject/src/struct-hivex_node.c +gobject/src/struct-hivex_value.c gobject/src/struct-inotify_event.c gobject/src/struct-int_bool.c gobject/src/struct-isoinfo.c diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR index aef2e27..4753102 100644 --- a/src/MAX_PROC_NR +++ b/src/MAX_PROC_NR @@ -1 +1 @@ -349 +365 diff --git a/src/guestfs.pod b/src/guestfs.pod index cd4f5c5..7361e0a 100644 --- a/src/guestfs.pod +++ b/src/guestfs.pod @@ -723,12 +723,15 @@ length filenames, keep the files in a tarball. =head3 ACCESSING THE WINDOWS REGISTRY Libguestfs also provides some help for decoding Windows Registry -"hive" files, through the library C<hivex> which is part of the -libguestfs project although ships as a separate tarball. You have to -locate and download the hive file(s) yourself, and then pass them to -C<hivex> functions. See also the programs L<hivexml(1)>, -L<hivexsh(1)>, L<hivexregedit(1)> and L<virt-win-reg(1)> for more help -on this issue. +"hive" files, through a separate C library called L<hivex(3)>. + +Before libguestfs 1.19.35 you had to download the hive file, operate +on it locally using hivex, and upload it again. Since this version, +we have included the major hivex APIs directly in the libguestfs API +(see L</guestfs_hivex_open>). This means that if you have opened a +Windows guest, you can read and write the registry directly. + +See also L<virt-win-reg(1)>. =head3 SYMLINKS ON NTFS-3G FILESYSTEMS -- 1.7.10.4
Richard W.M. Jones
2012-Aug-29 15:15 UTC
[Libguestfs] [PATCH 3/4] New API: guestfs_hivex_value_utf8
From: "Richard W.M. Jones" <rjones at redhat.com> A convenience function that reads a value from the registry and returns it as UTF-8. --- generator/generator_actions.ml | 20 +++++++++- src/inspect-fs-windows.c | 85 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 1 deletion(-) diff --git a/generator/generator_actions.ml b/generator/generator_actions.ml index 6bcd053..6290333 100644 --- a/generator/generator_actions.ml +++ b/generator/generator_actions.ml @@ -2224,6 +2224,22 @@ List the files in C<directory> (relative to the root directory, there is no cwd). The '.' and '..' entries are not returned, but hidden files are shown." }; + { defaults with + name = "hivex_value_utf8"; + style = RString "databuf", [Int64 "valueh"], []; + optional = Some "hivex"; + shortdesc = "return the data field from the (key, datatype, data) tuple"; + longdesc = "\ +This calls C<guestfs_hivex_value_value> (which returns the +data field from a hivex value tuple). It then assumes that +the field is a UTF-16LE string and converts the result to +UTF-8 (or if this is not possible, it returns an error). + +This is useful for reading strings out of the Windows registry. +However it is not foolproof because the registry is not +strongly-typed and fields can contain arbitrary or unexpected +data." }; + ] (* daemon_functions are any functions which cause some action @@ -9612,7 +9628,9 @@ This is a wrapper around the L<hivex(3)> call of the same name." }; longdesc = "\ Return the data field of a (key, datatype, data) tuple. -This is a wrapper around the L<hivex(3)> call of the same name." }; +This is a wrapper around the L<hivex(3)> call of the same name. + +See also: C<guestfs_hivex_value_utf8>." }; { defaults with name = "hivex_commit"; diff --git a/src/inspect-fs-windows.c b/src/inspect-fs-windows.c index d04024b..cd2bd6c 100644 --- a/src/inspect-fs-windows.c +++ b/src/inspect-fs-windows.c @@ -27,6 +27,7 @@ #include <string.h> #include <sys/stat.h> #include <errno.h> +#include <iconv.h> #ifdef HAVE_ENDIAN_H #include <endian.h> @@ -587,3 +588,87 @@ guestfs___case_sensitive_path_silently (guestfs_h *g, const char *path) g->error_cb = old_error_cb; return ret; } + +/* Read the data from 'valueh', assume it is UTF16LE and convert it to + * UTF8. This is copied from hivex_value_string which doesn't work in + * the appliance because it uses iconv_open which doesn't work because + * we delete all the i18n databases. + */ +static char *utf16_to_utf8 (/* const */ char *input, size_t len); + +char * +guestfs__hivex_value_utf8 (guestfs_h *g, int64_t valueh) +{ + char *buf, *ret; + size_t buflen; + + buf = guestfs_hivex_value_value (g, valueh, &buflen); + if (buf == NULL) + return NULL; + + ret = utf16_to_utf8 (buf, buflen); + if (ret == NULL) { + perrorf (g, "hivex: conversion of registry value to UTF8 failed"); + free (buf); + return NULL; + } + free (buf); + + return ret; +} + +static char * +utf16_to_utf8 (/* const */ char *input, size_t len) +{ + iconv_t ic = iconv_open ("UTF-8", "UTF-16"); + if (ic == (iconv_t) -1) + return NULL; + + /* iconv(3) has an insane interface ... */ + + /* Mostly UTF-8 will be smaller, so this is a good initial guess. */ + size_t outalloc = len; + + again:; + size_t inlen = len; + size_t outlen = outalloc; + char *out = malloc (outlen + 1); + if (out == NULL) { + int err = errno; + iconv_close (ic); + errno = err; + return NULL; + } + char *inp = input; + char *outp = out; + + size_t r = iconv (ic, &inp, &inlen, &outp, &outlen); + if (r == (size_t) -1) { + if (errno == E2BIG) { + int err = errno; + size_t prev = outalloc; + /* Try again with a larger output buffer. */ + free (out); + outalloc *= 2; + if (outalloc < prev) { + iconv_close (ic); + errno = err; + return NULL; + } + goto again; + } + else { + /* Else some conversion failure, eg. EILSEQ, EINVAL. */ + int err = errno; + iconv_close (ic); + free (out); + errno = err; + return NULL; + } + } + + *outp = '\0'; + iconv_close (ic); + + return out; +} -- 1.7.10.4
Richard W.M. Jones
2012-Aug-29 15:15 UTC
[Libguestfs] [PATCH 4/4] Update inspection and example programs to use new hivex* APIs (RHBZ#852394).
From: "Richard W.M. Jones" <rjones at redhat.com> I tested this by comparing the output of virt-inspector over Windows guests before and after the change, which was identical: $ md5sum `ls -1 /tmp/*.before /tmp/*.after` c292d6629b5a761eccb4a279754399b4 /tmp/Win2003.after c292d6629b5a761eccb4a279754399b4 /tmp/Win2003.before eb1e1ff29208a9ee46e9c100dfec26b2 /tmp/Win2012.after eb1e1ff29208a9ee46e9c100dfec26b2 /tmp/Win2012.before d060a95d7ffe5dce6c4e66feb80c2837 /tmp/Win7x32.after d060a95d7ffe5dce6c4e66feb80c2837 /tmp/Win7x32.before 8914eee70ac4f8a0317659e09e00dcdc /tmp/Win7x32Dynamic.after 8914eee70ac4f8a0317659e09e00dcdc /tmp/Win7x32Dynamic.before a2dcdfc0f9d64054640875aa791889e0 /tmp/Win7x32TwoDisks.after a2dcdfc0f9d64054640875aa791889e0 /tmp/Win7x32TwoDisks.before 5ed49568a5147dce7517c99de41ebf2e /tmp/Win8previewx64.after 5ed49568a5147dce7517c99de41ebf2e /tmp/Win8previewx64.before fdfc7d272b79a665ae3313ae1ae30660 /tmp/WinXP.after fdfc7d272b79a665ae3313ae1ae30660 /tmp/WinXP.before 3c705444be664f1316b21c5d8d3cb0be /tmp/WinXPRecConsole.after 3c705444be664f1316b21c5d8d3cb0be /tmp/WinXPRecConsole.before --- TODO | 8 ++ examples/Makefile.am | 4 +- examples/virt-dhcp-address.c | 97 ++++++++------------ src/Makefile.am | 4 +- src/dbdump.c | 4 - src/guestfs-internal.h | 8 -- src/inspect-apps.c | 92 ++++++------------- src/inspect-fs-cd.c | 8 -- src/inspect-fs-unix.c | 8 -- src/inspect-fs-windows.c | 206 ++++++++++++++++++------------------------ src/inspect-fs.c | 8 -- src/inspect.c | 143 ----------------------------- 12 files changed, 163 insertions(+), 427 deletions(-) diff --git a/TODO b/TODO index 7561c3d..026c94a 100644 --- a/TODO +++ b/TODO @@ -570,3 +570,11 @@ mke2fs Add a mke2fs API call allowing full configuration of filesystems. Then fix tests/bigdirs/test-big-dirs.pl to use it. + +hivex +----- + +Add more of hivex to the API, especially for writing. + +Reimplement virt-win-reg to use this API. (This is difficult because +the Perl libraries underneath access the hivex API directly). diff --git a/examples/Makefile.am b/examples/Makefile.am index 35bf765..c79a1db 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -95,10 +95,8 @@ virt_dhcp_address_SOURCES = virt-dhcp-address.c virt_dhcp_address_CFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -I$(top_srcdir)/src -I$(top_builddir)/src \ - $(WARN_CFLAGS) $(WERROR_CFLAGS) \ - $(HIVEX_CFLAGS) + $(WARN_CFLAGS) $(WERROR_CFLAGS) virt_dhcp_address_LDADD = \ - $(HIVEX_LIBS) \ $(top_builddir)/src/libguestfs.la endif diff --git a/examples/virt-dhcp-address.c b/examples/virt-dhcp-address.c index c6f25c6..c4e3647 100644 --- a/examples/virt-dhcp-address.c +++ b/examples/virt-dhcp-address.c @@ -17,7 +17,6 @@ #include <assert.h> #include <guestfs.h> -#include <hivex.h> static int compare_keys_len (const void *p1, const void *p2); static size_t count_strings (char *const *argv); @@ -198,11 +197,8 @@ static void print_dhcp_address_windows (guestfs_h *g, char *root_fs) { char *system_path; - char tmpfile[] = "/tmp/systemXXXXXX"; - int fd, err; - hive_h *h; - hive_node_h root, node, *nodes; - hive_value_h value; + int64_t root, node, value; + struct guestfs_hivex_node_list *nodes; char *controlset; size_t i; char *p; @@ -215,71 +211,52 @@ print_dhcp_address_windows (guestfs_h *g, char *root_fs) exit (EXIT_FAILURE); } - fd = mkstemp (tmpfile); - if (fd == -1) { - perror ("mkstemp"); - exit (EXIT_FAILURE); - } - - /* Download the SYSTEM hive. */ - if (guestfs_download (g, system_path, tmpfile) == -1) + /* Open the hive to parse it. Note that before libguestfs 1.19.35 + * you had to download the file and parse it using hivex(3). Since + * libguestfs 1.19.35, parts of the hivex(3) API are now exposed + * through libguestfs, and that is what we'll use here because it is + * more convenient and avoids having to download the hive. + */ + if (guestfs_hivex_open (g, system_path, -1) == -1) exit (EXIT_FAILURE); free (system_path); - controlset = guestfs_inspect_get_windows_current_control_set (g, root_fs); - if (controlset == NULL) - exit (EXIT_FAILURE); - - /* Open the hive to parse it. */ - h = hivex_open (tmpfile, 0); - err = errno; - close (fd); - unlink (tmpfile); - - if (h == NULL) { - errno = err; - perror ("hivex_open"); + root = guestfs_hivex_root (g); + if (root == -1) exit (EXIT_FAILURE); - } - - root = hivex_root (h); - if (root == 0) { - perror ("hivex_root"); - exit (EXIT_FAILURE); - } /* Get ControlSetXXX\Services\Tcpip\Parameters\Interfaces. */ + controlset = guestfs_inspect_get_windows_current_control_set (g, root_fs); + if (controlset == NULL) + exit (EXIT_FAILURE); const char *path[] = { controlset, "Services", "Tcpip", "Parameters", "Interfaces" }; node = root; - errno = 0; - for (i = 0; node != 0 && i < sizeof path / sizeof path[0]; ++i) - node = hivex_node_get_child (h, node, path[i]); + for (i = 0; node > 0 && i < sizeof path / sizeof path[0]; ++i) + node = guestfs_hivex_node_get_child (g, node, path[i]); + + if (node == -1) + exit (EXIT_FAILURE); if (node == 0) { - if (errno != 0) - perror ("hivex_node_get_child"); - else - fprintf (stderr, "virt-dhcp-address: HKLM\\System\\%s\\Services\\Tcpip\\Parameters\\Interfaces not found.", controlset); + fprintf (stderr, "virt-dhcp-address: HKLM\\System\\%s\\Services\\Tcpip\\Parameters\\Interfaces not found.", controlset); exit (EXIT_FAILURE); } + free (controlset); + /* Look for a node under here which has a "DhcpIPAddress" entry in it. */ - nodes = hivex_node_children (h, node); - if (nodes == NULL) { - perror ("hivex_node_children"); + nodes = guestfs_hivex_node_children (g, node); + if (nodes == NULL) exit (EXIT_FAILURE); - } value = 0; - for (i = 0; value == 0 && nodes[i] != 0; ++i) { - errno = 0; - value = hivex_node_get_value (h, nodes[i], "DhcpIPAddress"); - if (value == 0 && errno != 0) { - perror ("hivex_node_get_value"); + for (i = 0; value == 0 && i < nodes->len; ++i) { + value = guestfs_hivex_node_get_value (g, nodes->val[i].hivex_node_h, + "DhcpIPAddress"); + if (value == -1) exit (EXIT_FAILURE); - } } if (value == 0) { @@ -287,21 +264,21 @@ print_dhcp_address_windows (guestfs_h *g, char *root_fs) exit (EXIT_FAILURE); } - /* Get the string and use hivex's auto-conversion to convert it to UTF-8 - * for output. + guestfs_free_hivex_node_list (nodes); + + /* Get the string and use libguestfs's auto-conversion to convert it + * to UTF-8 for output. */ - p = hivex_value_string (h, value); - if (!p) { - perror ("hivex_value_string"); + p = guestfs_hivex_value_utf8 (g, value); + if (!p) exit (EXIT_FAILURE); - } printf ("%s\n", p); - /* Close the hive handle. */ - hivex_close (h); + free (p); - free (controlset); + /* Close the hive handle. */ + guestfs_hivex_close (g); } static int diff --git a/src/Makefile.am b/src/Makefile.am index 5d3639a..5e79662 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -148,7 +148,7 @@ libguestfs_la_SOURCES = \ libguestfs.syms libguestfs_la_LIBADD = \ - $(HIVEX_LIBS) $(PCRE_LIBS) $(MAGIC_LIBS) \ + $(PCRE_LIBS) $(MAGIC_LIBS) \ $(LIBVIRT_LIBS) $(LIBXML2_LIBS) \ ../gnulib/lib/libgnu.la \ $(GETADDRINFO_LIB) \ @@ -167,7 +167,7 @@ libguestfs_la_LIBADD += liberrnostring.la libprotocol.la libguestfs_la_CFLAGS = \ -DGUESTFS_DEFAULT_PATH='"$(libdir)/guestfs"' \ -DGUESTFS_WARN_DEPRECATED=1 \ - $(HIVEX_CFLAGS) $(PCRE_CFLAGS) \ + $(PCRE_CFLAGS) \ $(LIBVIRT_CFLAGS) $(LIBXML2_CFLAGS) \ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ $(GCC_VISIBILITY_HIDDEN) diff --git a/src/dbdump.c b/src/dbdump.c index ac20b67..0bdd9f4 100644 --- a/src/dbdump.c +++ b/src/dbdump.c @@ -34,10 +34,6 @@ #include <pcre.h> -#ifdef HAVE_HIVEX -#include <hivex.h> -#endif - #include "c-ctype.h" #include "ignore-value.h" diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h index 2355f13..b2f7518 100644 --- a/src/guestfs-internal.h +++ b/src/guestfs-internal.h @@ -107,11 +107,6 @@ #define MAX_SMALL_FILE_SIZE (2 * 1000 * 1000) #define MAX_AUGEAS_FILE_SIZE (100 * 1000) -/* Maximum Windows Registry hive that we will download to /tmp. Some - * registries can be legitimately very large. - */ -#define MAX_REGISTRY_SIZE (100 * 1000 * 1000) - /* Maximum RPM or dpkg database we will download to /tmp. RPM * 'Packages' database can get very large: 70 MB is roughly the * standard size for a new Fedora install, and after lots of package @@ -474,8 +469,6 @@ extern char *guestfs___download_to_tmp (guestfs_h *g, struct inspect_fs *fs, con extern char *guestfs___case_sensitive_path_silently (guestfs_h *g, const char *); extern struct inspect_fs *guestfs___search_for_root (guestfs_h *g, const char *root); extern char *guestfs___drive_name (size_t index, char *ret); - -#if defined(HAVE_HIVEX) extern int guestfs___check_for_filesystem_on (guestfs_h *g, const char *device, int is_block, int is_partnum); extern int guestfs___parse_unsigned_int (guestfs_h *g, const char *str); extern int guestfs___parse_unsigned_int_ignore_trailing (guestfs_h *g, const char *str); @@ -491,7 +484,6 @@ extern int guestfs___check_netbsd_root (guestfs_h *g, struct inspect_fs *fs); extern int guestfs___check_hurd_root (guestfs_h *g, struct inspect_fs *fs); extern int guestfs___has_windows_systemroot (guestfs_h *g); extern int guestfs___check_windows_root (guestfs_h *g, struct inspect_fs *fs); -#endif #define error(g,...) guestfs_error_errno((g),0,__VA_ARGS__) #define perrorf guestfs_perrorf diff --git a/src/inspect-apps.c b/src/inspect-apps.c index 1b0b5a5..06a02e2 100644 --- a/src/inspect-apps.c +++ b/src/inspect-apps.c @@ -34,10 +34,6 @@ #include <pcre.h> -#ifdef HAVE_HIVEX -#include <hivex.h> -#endif - #include "c-ctype.h" #include "ignore-value.h" #include "xstrtol.h" @@ -47,8 +43,6 @@ #include "guestfs-internal-actions.h" #include "guestfs_protocol.h" -#if defined(HAVE_HIVEX) - #ifdef DB_DUMP static struct guestfs_application_list *list_applications_rpm (guestfs_h *g, struct inspect_fs *fs); #endif @@ -414,7 +408,7 @@ list_applications_deb (guestfs_h *g, struct inspect_fs *fs) return ret; } -static void list_applications_windows_from_path (guestfs_h *g, hive_h *h, struct guestfs_application_list *apps, const char **path, size_t path_len); +static void list_applications_windows_from_path (guestfs_h *g, struct guestfs_application_list *apps, const char **path, size_t path_len); static struct guestfs_application_list * list_applications_windows (guestfs_h *g, struct inspect_fs *fs) @@ -431,24 +425,12 @@ list_applications_windows (guestfs_h *g, struct inspect_fs *fs) return NULL; } - char *software_hive = NULL; struct guestfs_application_list *ret = NULL; - hive_h *h = NULL; - software_hive = guestfs___download_to_tmp (g, fs, software_path, "software", - MAX_REGISTRY_SIZE); - if (software_hive == NULL) + if (guestfs_hivex_open (g, software_path, + GUESTFS_HIVEX_OPEN_VERBOSE, g->verbose, -1) == -1) goto out; - free (software_path); - software_path = NULL; - - h = hivex_open (software_hive, g->verbose ? HIVEX_OPEN_VERBOSE : 0); - if (h == NULL) { - perrorf (g, "hivex_open"); - goto out; - } - /* Allocate apps list. */ ret = safe_malloc (g, sizeof *ret); ret->len = 0; @@ -457,7 +439,7 @@ list_applications_windows (guestfs_h *g, struct inspect_fs *fs) /* Ordinary native applications. */ const char *hivepath[] { "Microsoft", "Windows", "CurrentVersion", "Uninstall" }; - list_applications_windows_from_path (g, h, ret, hivepath, + list_applications_windows_from_path (g, ret, hivepath, sizeof hivepath / sizeof hivepath[0]); /* 32-bit emulated Windows apps running on the WOW64 emulator. @@ -465,35 +447,34 @@ list_applications_windows (guestfs_h *g, struct inspect_fs *fs) */ const char *hivepath2[] { "WOW6432node", "Microsoft", "Windows", "CurrentVersion", "Uninstall" }; - list_applications_windows_from_path (g, h, ret, hivepath2, + list_applications_windows_from_path (g, ret, hivepath2, sizeof hivepath2 / sizeof hivepath2[0]); out: - if (h) hivex_close (h); + guestfs_hivex_close (g); free (software_path); - free (software_hive); return ret; } static void -list_applications_windows_from_path (guestfs_h *g, hive_h *h, +list_applications_windows_from_path (guestfs_h *g, struct guestfs_application_list *apps, const char **path, size_t path_len) { - hive_node_h *children = NULL; - hive_node_h node; + struct guestfs_hivex_node_list *children = NULL; + int64_t node; size_t i; - node = hivex_root (h); + node = guestfs_hivex_root (g); for (i = 0; node != 0 && i < path_len; ++i) - node = hivex_node_get_child (h, node, path[i]); + node = guestfs_hivex_node_get_child (g, node, path[i]); if (node == 0) return; - children = hivex_node_children (h, node); + children = guestfs_hivex_node_children (g, node); if (children == NULL) return; @@ -501,8 +482,9 @@ list_applications_windows_from_path (guestfs_h *g, hive_h *h, * See also: * http://nsis.sourceforge.net/Add_uninstall_information_to_Add/Remove_Programs#Optional_values */ - for (i = 0; children[i] != 0; ++i) { - hive_value_h value; + for (i = 0; i < children->len; ++i) { + int64_t child = children->val[i].hivex_node_h; + int64_t value; char *name = NULL; char *display_name = NULL; char *version = NULL; @@ -514,29 +496,29 @@ list_applications_windows_from_path (guestfs_h *g, hive_h *h, /* Use the node name as a proxy for the package name in Linux. The * display name is not language-independent, so it cannot be used. */ - name = hivex_node_name (h, children[i]); + name = guestfs_hivex_node_name (g, child); if (name == NULL) continue; - value = hivex_node_get_value (h, children[i], "DisplayName"); + value = guestfs_hivex_node_get_value (g, child, "DisplayName"); if (value) { - display_name = hivex_value_string (h, value); + display_name = guestfs_hivex_value_utf8 (g, value); if (display_name) { - value = hivex_node_get_value (h, children[i], "DisplayVersion"); + value = guestfs_hivex_node_get_value (g, child, "DisplayVersion"); if (value) - version = hivex_value_string (h, value); - value = hivex_node_get_value (h, children[i], "InstallLocation"); + version = guestfs_hivex_value_utf8 (g, value); + value = guestfs_hivex_node_get_value (g, child, "InstallLocation"); if (value) - install_path = hivex_value_string (h, value); - value = hivex_node_get_value (h, children[i], "Publisher"); + install_path = guestfs_hivex_value_utf8 (g, value); + value = guestfs_hivex_node_get_value (g, child, "Publisher"); if (value) - publisher = hivex_value_string (h, value); - value = hivex_node_get_value (h, children[i], "URLInfoAbout"); + publisher = guestfs_hivex_value_utf8 (g, value); + value = guestfs_hivex_node_get_value (g, child, "URLInfoAbout"); if (value) - url = hivex_value_string (h, value); - value = hivex_node_get_value (h, children[i], "Comments"); + url = guestfs_hivex_value_utf8 (g, value); + value = guestfs_hivex_node_get_value (g, child, "Comments"); if (value) - comments = hivex_value_string (h, value); + comments = guestfs_hivex_value_utf8 (g, value); add_application (g, apps, name, display_name, 0, version ? : "", @@ -557,7 +539,7 @@ list_applications_windows_from_path (guestfs_h *g, hive_h *h, free (comments); } - free (children); + guestfs_free_hivex_node_list (children); } static void @@ -606,19 +588,3 @@ sort_applications (struct guestfs_application_list *apps) qsort (apps->val, apps->len, sizeof (struct guestfs_application), compare_applications); } - -#else /* no hivex at compile time */ - -/* XXX These functions should be in an optgroup. */ - -#define NOT_IMPL(r) \ - error (g, _("inspection API not available since this version of libguestfs was compiled without the hivex library")); \ - return r - -struct guestfs_application_list * -guestfs__inspect_list_applications (guestfs_h *g, const char *root) -{ - NOT_IMPL(NULL); -} - -#endif /* no hivex at compile time */ diff --git a/src/inspect-fs-cd.c b/src/inspect-fs-cd.c index 6fec8a5..614f9a4 100644 --- a/src/inspect-fs-cd.c +++ b/src/inspect-fs-cd.c @@ -34,10 +34,6 @@ #include <pcre.h> -#ifdef HAVE_HIVEX -#include <hivex.h> -#endif - #include "c-ctype.h" #include "ignore-value.h" #include "xstrtol.h" @@ -47,8 +43,6 @@ #include "guestfs-internal-actions.h" #include "guestfs_protocol.h" -#if defined(HAVE_HIVEX) - /* Debian/Ubuntu install disks are easy ... * * These files are added by the debian-cd program, and it is worth @@ -484,5 +478,3 @@ guestfs___check_installer_root (guestfs_h *g, struct inspect_fs *fs) return 0; } - -#endif /* defined(HAVE_HIVEX) */ diff --git a/src/inspect-fs-unix.c b/src/inspect-fs-unix.c index 433ef5c..c00e69b 100644 --- a/src/inspect-fs-unix.c +++ b/src/inspect-fs-unix.c @@ -34,10 +34,6 @@ #include <pcre.h> -#ifdef HAVE_HIVEX -#include <hivex.h> -#endif - #include "c-ctype.h" #include "ignore-value.h" #include "xstrtol.h" @@ -49,8 +45,6 @@ #include "guestfs-internal-actions.h" #include "guestfs_protocol.h" -#if defined(HAVE_HIVEX) - /* Compile all the regular expressions once when the shared library is * loaded. PCRE is thread safe so we're supposedly OK here if * multiple threads call into the libguestfs API functions below @@ -1536,5 +1530,3 @@ is_partition (guestfs_h *g, const char *partition) return 1; } - -#endif /* defined(HAVE_HIVEX) */ diff --git a/src/inspect-fs-windows.c b/src/inspect-fs-windows.c index cd2bd6c..e972e97 100644 --- a/src/inspect-fs-windows.c +++ b/src/inspect-fs-windows.c @@ -35,10 +35,6 @@ #include <pcre.h> -#ifdef HAVE_HIVEX -#include <hivex.h> -#endif - #include "c-ctype.h" #include "ignore-value.h" #include "xstrtol.h" @@ -48,8 +44,6 @@ #include "guestfs-internal-actions.h" #include "guestfs_protocol.h" -#if defined(HAVE_HIVEX) - /* Compile all the regular expressions once when the shared library is * loaded. PCRE is thread safe so we're supposedly OK here if * multiple threads call into the libguestfs API functions below @@ -215,6 +209,8 @@ check_windows_arch (guestfs_h *g, struct inspect_fs *fs) static int check_windows_software_registry (guestfs_h *g, struct inspect_fs *fs) { + int ret = -1; + size_t len = strlen (fs->windows_systemroot) + 64; char software[len]; snprintf (software, len, "%s/system32/config/software", @@ -227,60 +223,46 @@ check_windows_software_registry (guestfs_h *g, struct inspect_fs *fs) */ return 0; - char *software_hive = NULL; - int ret = -1; - hive_h *h = NULL; - hive_value_h *values = NULL; - - software_hive = guestfs___download_to_tmp (g, fs, software_path, "software", - MAX_REGISTRY_SIZE); - if (software_hive == NULL) + if (guestfs_hivex_open (g, software_path, + GUESTFS_HIVEX_OPEN_VERBOSE, g->verbose, -1) == -1) goto out; - h = hivex_open (software_hive, g->verbose ? HIVEX_OPEN_VERBOSE : 0); - if (h == NULL) { - perrorf (g, "hivex_open"); - goto out; - } - - hive_node_h node = hivex_root (h); + int64_t node = guestfs_hivex_root (g); const char *hivepath[] { "Microsoft", "Windows NT", "CurrentVersion" }; size_t i; - for (i = 0; - node != 0 && i < sizeof hivepath / sizeof hivepath[0]; - ++i) { - node = hivex_node_get_child (h, node, hivepath[i]); - } + for (i = 0; node > 0 && i < sizeof hivepath / sizeof hivepath[0]; ++i) + node = guestfs_hivex_node_get_child (g, node, hivepath[i]); + + if (node == -1) + goto out; if (node == 0) { perrorf (g, "hivex: cannot locate HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"); goto out; } - values = hivex_node_values (h, node); + struct guestfs_hivex_value_list *values = NULL; + values = guestfs_hivex_node_values (g, node); - for (i = 0; values[i] != 0; ++i) { - char *key = hivex_value_key (h, values[i]); - if (key == NULL) { - perrorf (g, "hivex_value_key"); - goto out; - } + for (i = 0; i < values->len; ++i) { + int64_t value = values->val[i].hivex_value_h; + char *key = guestfs_hivex_value_key (g, value); + if (key == NULL) + goto out2; if (STRCASEEQ (key, "ProductName")) { - fs->product_name = hivex_value_string (h, values[i]); + fs->product_name = guestfs_hivex_value_utf8 (g, value); if (!fs->product_name) { - perrorf (g, "hivex_value_string"); free (key); - goto out; + goto out2; } } else if (STRCASEEQ (key, "CurrentVersion")) { - char *version = hivex_value_string (h, values[i]); + char *version = guestfs_hivex_value_utf8 (g, value); if (!version) { - perrorf (g, "hivex_value_string"); free (key); - goto out; + goto out2; } char *major, *minor; if (match2 (g, version, re_windows_version, &major, &minor)) { @@ -290,25 +272,24 @@ check_windows_software_registry (guestfs_h *g, struct inspect_fs *fs) free (minor); free (key); free (version); - goto out; + goto out2; } fs->minor_version = guestfs___parse_unsigned_int (g, minor); free (minor); if (fs->minor_version == -1) { free (key); free (version); - goto out; + goto out2; } } free (version); } else if (STRCASEEQ (key, "InstallationType")) { - fs->product_variant = hivex_value_string (h, values[i]); + fs->product_variant = guestfs_hivex_value_utf8 (g, value); if (!fs->product_variant) { - perrorf (g, "hivex_value_string"); free (key); - goto out; + goto out2; } } @@ -317,11 +298,11 @@ check_windows_software_registry (guestfs_h *g, struct inspect_fs *fs) ret = 0; + out2: + guestfs_free_hivex_value_list (values); out: - if (h) hivex_close (h); - free (values); + guestfs_hivex_close (g); free (software_path); - free (software_hive); return ret; } @@ -341,110 +322,94 @@ check_windows_system_registry (guestfs_h *g, struct inspect_fs *fs) */ return 0; - char *system_hive = NULL; int ret = -1; - hive_h *h = NULL; - hive_node_h root, node; - hive_value_h value, *values = NULL; + int64_t root, node, value; + struct guestfs_hivex_value_list *values = NULL; int32_t dword; size_t i, count; + char *buf = NULL; + size_t buflen; - system_hive - guestfs___download_to_tmp (g, fs, system_path, "system", - MAX_REGISTRY_SIZE); - if (system_hive == NULL) + if (guestfs_hivex_open (g, system_path, + GUESTFS_HIVEX_OPEN_VERBOSE, g->verbose, -1) == -1) goto out; - h = hivex_open (system_hive, g->verbose ? HIVEX_OPEN_VERBOSE : 0); - if (h == NULL) { - perrorf (g, "hivex_open"); + root = guestfs_hivex_root (g); + if (root == 0) goto out; - } - root = hivex_root (h); - if (root == 0) { - perrorf (g, "hivex_root"); + /* Get the CurrentControlSet. */ + node = guestfs_hivex_node_get_child (g, root, "Select"); + if (node == -1) goto out; - } - /* Get the CurrentControlSet. */ - errno = 0; - node = hivex_node_get_child (h, root, "Select"); if (node == 0) { - if (errno != 0) - perrorf (g, "hivex_node_get_child"); - else - error (g, "hivex: could not locate HKLM\\SYSTEM\\Select"); + error (g, "hivex: could not locate HKLM\\SYSTEM\\Select"); goto out; } - errno = 0; - value = hivex_node_get_value (h, node, "Current"); + value = guestfs_hivex_node_get_value (g, node, "Current"); + if (value == -1) + goto out; + if (value == 0) { - if (errno != 0) - perrorf (g, "hivex_node_get_value"); - else - error (g, "hivex: HKLM\\System\\Select Default entry not found."); + error (g, "hivex: HKLM\\System\\Select Default entry not found"); goto out; } /* XXX Should check the type. */ - dword = hivex_value_dword (h, value); + buf = guestfs_hivex_value_value (g, value, &buflen); + if (buflen != 4) { + error (g, "hivex: HKLM\\System\\Select\\Current expected to be DWORD"); + goto out; + } + dword = le32toh (*(int32_t *)buf); fs->windows_current_control_set = safe_asprintf (g, "ControlSet%03d", dword); /* Get the drive mappings. * This page explains the contents of HKLM\System\MountedDevices: * http://www.goodells.net/multiboot/partsigs.shtml */ - errno = 0; - node = hivex_node_get_child (h, root, "MountedDevices"); - if (node == 0) { - if (errno == 0) - /* Not found: skip getting drive letter mappings (RHBZ#803664). */ - goto skip_drive_letter_mappings; - /* errno != 0, so it's some other error from hivex */ - perrorf (g, "hivex_node_get_child"); + node = guestfs_hivex_node_get_child (g, root, "MountedDevices"); + if (node == -1) goto out; - } - values = hivex_node_values (h, node); + if (node == 0) + /* Not found: skip getting drive letter mappings (RHBZ#803664). */ + goto skip_drive_letter_mappings; + + values = guestfs_hivex_node_values (g, node); /* Count how many DOS drive letter mappings there are. This doesn't * ignore removable devices, so it overestimates, but that doesn't * matter because it just means we'll allocate a few bytes extra. */ - for (i = count = 0; values[i] != 0; ++i) { - char *key = hivex_value_key (h, values[i]); - if (key == NULL) { - perrorf (g, "hivex_value_key"); + for (i = count = 0; i < values->len; ++i) { + char *key = guestfs_hivex_value_key (g, values->val[i].hivex_value_h); + if (key == NULL) goto out; - } if (STRCASEEQLEN (key, "\\DosDevices\\", 12) && c_isalpha (key[12]) && key[13] == ':') count++; free (key); } - fs->drive_mappings = calloc (2*count + 1, sizeof (char *)); - if (fs->drive_mappings == NULL) { - perrorf (g, "calloc"); - goto out; - } + fs->drive_mappings = safe_calloc (g, 2*count + 1, sizeof (char *)); - for (i = count = 0; values[i] != 0; ++i) { - char *key = hivex_value_key (h, values[i]); - if (key == NULL) { - perrorf (g, "hivex_value_key"); + for (i = count = 0; i < values->len; ++i) { + int64_t v = values->val[i].hivex_value_h; + char *key = guestfs_hivex_value_key (g, v); + if (key == NULL) goto out; - } if (STRCASEEQLEN (key, "\\DosDevices\\", 12) && c_isalpha (key[12]) && key[13] == ':') { /* Get the binary value. Is it a fixed disk? */ char *blob, *device; size_t len; - hive_type type; + int64_t type; - blob = hivex_value_value (h, values[i], &type, &len); + type = guestfs_hivex_value_type (g, v); + blob = guestfs_hivex_value_value (g, v, &len); if (blob != NULL && type == 3 && len == 12) { /* Try to map the blob to a known disk and partition. */ device = map_registry_disk_blob (g, blob); @@ -463,31 +428,34 @@ check_windows_system_registry (guestfs_h *g, struct inspect_fs *fs) const char *hivepath[] { fs->windows_current_control_set, "Services", "Tcpip", "Parameters" }; for (node = root, i = 0; - node != 0 && i < sizeof hivepath / sizeof hivepath[0]; + node > 0 && i < sizeof hivepath / sizeof hivepath[0]; ++i) { - node = hivex_node_get_child (h, node, hivepath[i]); + node = guestfs_hivex_node_get_child (g, node, hivepath[i]); } + if (node == -1) + goto out; + if (node == 0) { perrorf (g, "hivex: cannot locate HKLM\\SYSTEM\\%s\\Services\\Tcpip\\Parameters", fs->windows_current_control_set); goto out; } - free (values); - values = hivex_node_values (h, node); + guestfs_free_hivex_value_list (values); + values = guestfs_hivex_node_values (g, node); + if (values == NULL) + goto out; - for (i = 0; values[i] != 0; ++i) { - char *key = hivex_value_key (h, values[i]); - if (key == NULL) { - perrorf (g, "hivex_value_key"); + for (i = 0; i < values->len; ++i) { + int64_t v = values->val[i].hivex_value_h; + char *key = guestfs_hivex_value_key (g, v); + if (key == NULL) goto out; - } if (STRCASEEQ (key, "Hostname")) { - fs->hostname = hivex_value_string (h, values[i]); + fs->hostname = guestfs_hivex_value_utf8 (g, v); if (!fs->hostname) { - perrorf (g, "hivex_value_string"); free (key); goto out; } @@ -500,10 +468,10 @@ check_windows_system_registry (guestfs_h *g, struct inspect_fs *fs) ret = 0; out: - if (h) hivex_close (h); - free (values); + guestfs_hivex_close (g); + if (values) guestfs_free_hivex_value_list (values); free (system_path); - free (system_hive); + free (buf); return ret; } @@ -577,8 +545,6 @@ map_registry_disk_blob (guestfs_h *g, const char *blob) return ret; } -#endif /* defined(HAVE_HIVEX) */ - char * guestfs___case_sensitive_path_silently (guestfs_h *g, const char *path) { diff --git a/src/inspect-fs.c b/src/inspect-fs.c index 33aff35..456a546 100644 --- a/src/inspect-fs.c +++ b/src/inspect-fs.c @@ -34,10 +34,6 @@ #include <pcre.h> -#ifdef HAVE_HIVEX -#include <hivex.h> -#endif - #include "c-ctype.h" #include "ignore-value.h" #include "xstrtol.h" @@ -47,8 +43,6 @@ #include "guestfs-internal-actions.h" #include "guestfs_protocol.h" -#if defined(HAVE_HIVEX) - /* Compile all the regular expressions once when the shared library is * loaded. PCRE is thread safe so we're supposedly OK here if * multiple threads call into the libguestfs API functions below @@ -603,5 +597,3 @@ guestfs___first_egrep_of_file (guestfs_h *g, const char *filename, return 1; } - -#endif /* defined(HAVE_HIVEX) */ diff --git a/src/inspect.c b/src/inspect.c index 44826c3..8cba6b1 100644 --- a/src/inspect.c +++ b/src/inspect.c @@ -34,10 +34,6 @@ #include <pcre.h> -#ifdef HAVE_HIVEX -#include <hivex.h> -#endif - #include "c-ctype.h" #include "ignore-value.h" #include "xstrtol.h" @@ -47,8 +43,6 @@ #include "guestfs-internal-actions.h" #include "guestfs_protocol.h" -#if defined(HAVE_HIVEX) - /* The main inspection code. */ char ** guestfs__inspect_os (guestfs_h *g) @@ -540,143 +534,6 @@ guestfs__inspect_get_hostname (guestfs_h *g, const char *root) return safe_strdup (g, fs->hostname ? : "unknown"); } -#else /* no hivex at compile time */ - -/* XXX These functions should be in an optgroup. */ - -#define NOT_IMPL(r) \ - guestfs_error_errno (g, ENOTSUP, _("inspection API not available since this version of libguestfs was compiled without the hivex library")); \ - return r - -char ** -guestfs__inspect_os (guestfs_h *g) -{ - NOT_IMPL(NULL); -} - -char ** -guestfs__inspect_get_roots (guestfs_h *g) -{ - NOT_IMPL(NULL); -} - -char * -guestfs__inspect_get_type (guestfs_h *g, const char *root) -{ - NOT_IMPL(NULL); -} - -char * -guestfs__inspect_get_arch (guestfs_h *g, const char *root) -{ - NOT_IMPL(NULL); -} - -char * -guestfs__inspect_get_distro (guestfs_h *g, const char *root) -{ - NOT_IMPL(NULL); -} - -int -guestfs__inspect_get_major_version (guestfs_h *g, const char *root) -{ - NOT_IMPL(-1); -} - -int -guestfs__inspect_get_minor_version (guestfs_h *g, const char *root) -{ - NOT_IMPL(-1); -} - -char * -guestfs__inspect_get_product_name (guestfs_h *g, const char *root) -{ - NOT_IMPL(NULL); -} - -char * -guestfs__inspect_get_product_variant (guestfs_h *g, const char *root) -{ - NOT_IMPL(NULL); -} - -char * -guestfs__inspect_get_windows_systemroot (guestfs_h *g, const char *root) -{ - NOT_IMPL(NULL); -} - -char * -guestfs__inspect_get_windows_current_control_set (guestfs_h *g, - const char *root) -{ - NOT_IMPL(NULL); -} - -char ** -guestfs__inspect_get_mountpoints (guestfs_h *g, const char *root) -{ - NOT_IMPL(NULL); -} - -char ** -guestfs__inspect_get_filesystems (guestfs_h *g, const char *root) -{ - NOT_IMPL(NULL); -} - -char ** -guestfs__inspect_get_drive_mappings (guestfs_h *g, const char *root) -{ - NOT_IMPL(NULL); -} - -char * -guestfs__inspect_get_package_format (guestfs_h *g, const char *root) -{ - NOT_IMPL(NULL); -} - -char * -guestfs__inspect_get_package_management (guestfs_h *g, const char *root) -{ - NOT_IMPL(NULL); -} - -char * -guestfs__inspect_get_hostname (guestfs_h *g, const char *root) -{ - NOT_IMPL(NULL); -} - -char * -guestfs__inspect_get_format (guestfs_h *g, const char *root) -{ - NOT_IMPL(NULL); -} - -int -guestfs__inspect_is_live (guestfs_h *g, const char *root) -{ - NOT_IMPL(-1); -} - -int -guestfs__inspect_is_netinst (guestfs_h *g, const char *root) -{ - NOT_IMPL(-1); -} - -int -guestfs__inspect_is_multipart (guestfs_h *g, const char *root) -{ - NOT_IMPL(-1); -} - -#endif /* no hivex at compile time */ - void guestfs___free_inspect_info (guestfs_h *g) { -- 1.7.10.4
Richard W.M. Jones
2012-Aug-29 15:28 UTC
[Libguestfs] [PATCH 0/4] Add hivex APIs into the libguestfs API (RHBZ#852394)
On Wed, Aug 29, 2012 at 04:15:31PM +0100, Richard W.M. Jones wrote:> https://bugzilla.redhat.com/show_bug.cgi?id=852394 > "libguestfs inspection limits registries to 100 MiB"On the subject of this limit: Before the change, we had this arbitrary 100 MiB limit so that we didn't download arbitrary amounts of data from the guest into /tmp. This is obviously a good thing, but a customer of ours ran into a guest which had a large number of software packages installed and had a > 100 MiB registry hive. After the change, things are a little bit more complex: * If you open a hive for reading, hivex mmaps it into the process map of the daemon. For a normal-sized hive, this effectively demand-loads the hive from disk as required, and since the majority of inspection operations hardly touch the hive, this is potentially more efficient. For a large (or corrupt/malicious) hive, a 32 bit processor will naturally limit the amount that can be mmapped to ~ 1 GB and fail gracefully if it is larger. A 64 bit processor will map just about any hive, but it's only demand-loaded so that doesn't especially matter. * If you open a hive for writing, hivex tries to malloc enough memory to store a complete copy of the hive, and will fail if there is not enough memory. It's likely that it doesn't fail gracefully (because of the implementation of malloc itself) if the hive is huge. Probably this will crash the daemon or appliance. Therefore when opening a hive for writing, it's a good idea to check the size of the hive beforehand to ensure it's reasonable. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones virt-top is 'top' for virtual machines. Tiny program with many powerful monitoring features, net stats, disk stats, logging, etc. http://et.redhat.com/~rjones/virt-top