#include "wimlib.h"
#include "wimlib/blob_table.h"
#include "wimlib/dentry.h"
+#include "wimlib/encoding.h"
#include "wimlib/endianness.h"
#include "wimlib/error.h"
+#include "wimlib/metadata.h"
#include "wimlib/registry.h"
#include "wimlib/wim.h"
#include "wimlib/xml_windows.h"
if (ctx->debug_enabled) \
WARNING(format, ##__VA_ARGS__)
-/* Path to the SOFTWARE registry hive */
-static const tchar * const software_hive_path =
- WIMLIB_WIM_PATH_SEPARATOR_STRING T("Windows")
- WIMLIB_WIM_PATH_SEPARATOR_STRING T("System32")
- WIMLIB_WIM_PATH_SEPARATOR_STRING T("config")
- WIMLIB_WIM_PATH_SEPARATOR_STRING T("SOFTWARE");
-
-/* Path to the SYSTEM registry hive */
-static const tchar * const system_hive_path =
- WIMLIB_WIM_PATH_SEPARATOR_STRING T("Windows")
- WIMLIB_WIM_PATH_SEPARATOR_STRING T("System32")
- WIMLIB_WIM_PATH_SEPARATOR_STRING T("config")
- WIMLIB_WIM_PATH_SEPARATOR_STRING T("SYSTEM");
-
-/* Path to kernel32.dll */
-static const tchar * const kernel32_dll_path =
- WIMLIB_WIM_PATH_SEPARATOR_STRING T("Windows")
- WIMLIB_WIM_PATH_SEPARATOR_STRING T("System32")
- WIMLIB_WIM_PATH_SEPARATOR_STRING T("kernel32.dll");
-
/* Set a property in the XML document, with error checking. */
static void
set_string_property(struct windows_info_ctx *ctx,
s64 major_version = -1;
s64 minor_version = -1;
tchar *version_string;
+ tchar *build_string;
/* Image flags */
copy_registry_string(ctx, regf, version_key, T("EditionID"),
}
/* Build number */
- copy_registry_string(ctx, regf, version_key, T("CurrentBuild"),
- T("WINDOWS/VERSION/BUILD"));
+
+ /* Note: "CurrentBuild" is marked as obsolete in Windows XP registries
+ * (example value: "1.511.1 () (Obsolete data - do not use)"), and
+ * "CurrentBuildNumber" contains the correct value. But oddly enough,
+ * it is "CurrentBuild" that contains the correct value on *later*
+ * versions of Windows. */
+ if (get_string_from_registry(ctx, regf, version_key, T("CurrentBuild"),
+ &build_string))
+ {
+ if (tstrchr(build_string, T('.'))) {
+ FREE(build_string);
+ build_string = NULL;
+ get_string_from_registry(ctx, regf, version_key,
+ T("CurrentBuildNumber"),
+ &build_string);
+ }
+ if (build_string) {
+ set_string_property(ctx, T("WINDOWS/VERSION/BUILD"),
+ build_string);
+ FREE(build_string);
+ }
+ }
}
/* Gather the default language from the SYSTEM registry hive. */
*/
static void *
load_file_contents(struct windows_info_ctx *ctx,
- const tchar *path, size_t *size_ret)
+ const struct wim_dentry *dentry, const char *filename,
+ size_t *size_ret)
{
- const struct wim_dentry *dentry;
const struct blob_descriptor *blob;
void *contents;
int ret;
- dentry = get_dentry(ctx->wim, path, WIMLIB_CASE_INSENSITIVE);
if (!dentry) {
- XML_WARN("File \"%"TS"\" not found", path);
+ XML_WARN("%s does not exist", filename);
return NULL;
}
blob = inode_get_blob_for_unnamed_data_stream(dentry->d_inode,
ctx->wim->blob_table);
if (!blob) {
- XML_WARN("File \"%"TS"\" has no contents", path);
+ XML_WARN("%s has no contents", filename);
return NULL;
}
ret = read_blob_into_alloc_buf(blob, &contents);
if (ret) {
- XML_WARN("Error loading file \"%"TS"\" (size=%"PRIu64"): %"TS,
- path, blob->size, wimlib_get_error_string(ret));
+ XML_WARN("Error loading %s (size=%"PRIu64"): %"TS,
+ filename, blob->size, wimlib_get_error_string(ret));
ctx->oom_encountered |= (ret == WIMLIB_ERR_NOMEM &&
blob->size < 100000000);
return NULL;
/* Load and validate a registry hive file. */
static void *
-load_hive(struct windows_info_ctx *ctx, const tchar *path)
+load_hive(struct windows_info_ctx *ctx, const struct wim_dentry *dentry,
+ const char *filename)
{
void *hive_mem;
size_t hive_size;
- hive_mem = load_file_contents(ctx, path, &hive_size);
+ hive_mem = load_file_contents(ctx, dentry, filename, &hive_size);
if (hive_mem && !is_registry_valid(ctx, hive_mem, hive_size)) {
- XML_WARN("\"%"TS"\" is not a valid registry hive!", path);
+ XML_WARN("%s is not a valid registry hive!", filename);
FREE(hive_mem);
hive_mem = NULL;
}
return hive_mem;
}
-/*
- * Set Windows-specific XML information for the currently selected WIM image.
- *
- * This process is heavily based on heuristics and hard-coded logic related to
- * where Windows stores certain types of information. Therefore, it simply
- * tries to set as much information as possible. If there's a problem, it skips
- * the affected information and proceeds to the next part. It only returns an
- * error code if there was a severe problem such as out-of-memory.
- */
-int
-set_windows_specific_info(WIMStruct *wim)
+/* Set the WINDOWS/SYSTEMROOT property to the name of the directory specified by
+ * 'systemroot'. */
+static void
+set_systemroot_property(struct windows_info_ctx *ctx,
+ const struct wim_dentry *systemroot)
+{
+ utf16lechar *uname;
+ const tchar *name;
+ size_t name_nbytes;
+ int ret;
+
+ /* to uppercase ... */
+ uname = utf16le_dupz(systemroot->d_name, systemroot->d_name_nbytes);
+ if (!uname) {
+ ctx->oom_encountered = true;
+ goto out;
+ }
+ for (size_t i = 0; i < systemroot->d_name_nbytes / 2; i++)
+ uname[i] = cpu_to_le16(upcase[le16_to_cpu(uname[i])]);
+
+ /* to tstring ... */
+ ret = utf16le_get_tstr(uname, systemroot->d_name_nbytes,
+ &name, &name_nbytes);
+ if (ret) {
+ ctx->oom_encountered |= (ret == WIMLIB_ERR_NOMEM);
+ XML_WARN("Failed to get systemroot name: %"TS,
+ wimlib_get_error_string(ret));
+ goto out;
+ }
+ set_string_property(ctx, T("WINDOWS/SYSTEMROOT"), name);
+ utf16le_put_tstr(name);
+out:
+ FREE(uname);
+}
+
+static int
+do_set_windows_specific_info(WIMStruct *wim,
+ const struct wim_dentry *systemroot,
+ const struct wim_dentry *kernel32,
+ const struct wim_dentry *software,
+ const struct wim_dentry *system)
{
void *contents;
size_t size;
.debug_enabled = (tgetenv(T("WIMLIB_DEBUG_XML_INFO")) != NULL),
}, *ctx = &_ctx;
- if ((contents = load_file_contents(ctx, kernel32_dll_path, &size))) {
- set_string_property(ctx, T("WINDOWS/SYSTEMROOT"), T("WINDOWS"));
+ set_systemroot_property(ctx, systemroot);
+
+ if ((contents = load_file_contents(ctx, kernel32, "kernel32.dll", &size))) {
set_info_from_kernel32(ctx, contents, size);
FREE(contents);
}
- if ((contents = load_hive(ctx, software_hive_path))) {
+ if ((contents = load_hive(ctx, software, "SOFTWARE"))) {
set_info_from_software_hive(ctx, contents);
FREE(contents);
}
- if ((contents = load_hive(ctx, system_hive_path))) {
+ if ((contents = load_hive(ctx, system, "SYSTEM"))) {
set_info_from_system_hive(ctx, contents);
FREE(contents);
}
return 0;
}
+
+/* Windows */
+static const utf16lechar windows_name[] = {
+ cpu_to_le16('W'), cpu_to_le16('i'), cpu_to_le16('n'),
+ cpu_to_le16('d'), cpu_to_le16('o'), cpu_to_le16('w'),
+ cpu_to_le16('s'),
+};
+
+/* System32 */
+static const utf16lechar system32_name[] = {
+ cpu_to_le16('S'), cpu_to_le16('y'), cpu_to_le16('s'),
+ cpu_to_le16('t'), cpu_to_le16('e'), cpu_to_le16('m'),
+ cpu_to_le16('3'), cpu_to_le16('2'),
+};
+
+/* kernel32.dll */
+static const utf16lechar kernel32_name[] = {
+ cpu_to_le16('k'), cpu_to_le16('e'), cpu_to_le16('r'),
+ cpu_to_le16('n'), cpu_to_le16('e'), cpu_to_le16('l'),
+ cpu_to_le16('3'), cpu_to_le16('2'), cpu_to_le16('.'),
+ cpu_to_le16('d'), cpu_to_le16('l'), cpu_to_le16('l'),
+};
+
+/* config */
+static const utf16lechar config_name[] = {
+ cpu_to_le16('c'), cpu_to_le16('o'), cpu_to_le16('n'),
+ cpu_to_le16('f'), cpu_to_le16('i'), cpu_to_le16('g'),
+};
+
+/* SOFTWARE */
+static const utf16lechar software_name[] = {
+ cpu_to_le16('S'), cpu_to_le16('O'), cpu_to_le16('F'),
+ cpu_to_le16('T'), cpu_to_le16('W'), cpu_to_le16('A'),
+ cpu_to_le16('R'), cpu_to_le16('E'),
+};
+
+/* SYSTEM */
+static const utf16lechar system_name[] = {
+ cpu_to_le16('S'), cpu_to_le16('Y'), cpu_to_le16('S'),
+ cpu_to_le16('T'), cpu_to_le16('E'), cpu_to_le16('M'),
+};
+
+#define GET_CHILD(parent, child_name) \
+ get_dentry_child_with_utf16le_name(parent, \
+ child_name, \
+ sizeof(child_name), \
+ WIMLIB_CASE_INSENSITIVE)
+
+static bool
+is_default_systemroot(const struct wim_dentry *potential_systemroot)
+{
+ return !cmp_utf16le_strings(potential_systemroot->d_name,
+ potential_systemroot->d_name_nbytes / 2,
+ windows_name,
+ ARRAY_LEN(windows_name),
+ true);
+}
+
+/*
+ * Set Windows-specific XML information for the currently selected WIM image.
+ *
+ * This process is heavily based on heuristics and hard-coded logic related to
+ * where Windows stores certain types of information. Therefore, it simply
+ * tries to set as much information as possible. If there's a problem, it skips
+ * the affected information and proceeds to the next part. It only returns an
+ * error code if there was a severe problem such as out-of-memory.
+ */
+int
+set_windows_specific_info(WIMStruct *wim)
+{
+ const struct wim_dentry *root, *potential_systemroot,
+ *best_systemroot = NULL,
+ *best_kernel32 = NULL,
+ *best_software = NULL,
+ *best_system = NULL;
+ int best_score = 0;
+
+ root = wim_get_current_root_dentry(wim);
+ if (!root)
+ return 0;
+
+ /* Find the system root. This is usually the toplevel directory
+ * "Windows", but it might be a different toplevel directory. Choose
+ * the directory that contains the greatest number of the files we want:
+ * System32/kernel32.dll, System32/config/SOFTWARE, and
+ * System32/config/SYSTEM. Compare all names case insensitively. */
+ for_dentry_child(potential_systemroot, root) {
+ const struct wim_dentry *system32, *kernel32, *config,
+ *software = NULL, *system = NULL;
+ int score;
+
+ if (!dentry_is_directory(potential_systemroot))
+ continue;
+ system32 = GET_CHILD(potential_systemroot, system32_name);
+ if (!system32)
+ continue;
+ kernel32 = GET_CHILD(system32, kernel32_name);
+ config = GET_CHILD(system32, config_name);
+ if (config) {
+ software = GET_CHILD(config, software_name);
+ system = GET_CHILD(config, system_name);
+ }
+
+ score = !!kernel32 + !!software + !!system;
+ if (score >= best_score) {
+ /* If there's a tie, prefer the "Windows" directory. */
+ if (score > best_score ||
+ is_default_systemroot(potential_systemroot))
+ {
+ best_score = score;
+ best_systemroot = potential_systemroot;
+ best_kernel32 = kernel32;
+ best_software = software;
+ best_system = system;
+ }
+ }
+ }
+
+ if (likely(best_score == 0))
+ return 0; /* No Windows system root found. */
+
+ /* Found the Windows system root. */
+ return do_set_windows_specific_info(wim, best_systemroot, best_kernel32,
+ best_software, best_system);
+}