From 62458ef728064652932d49fbab1116b2f2462fa8 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 3 Oct 2015 11:59:49 -0500 Subject: [PATCH] xml.c: refactor to use document tree --- include/wimlib/wim.h | 4 +- include/wimlib/xml.h | 69 +- src/add_image.c | 16 +- src/delete_image.c | 2 +- src/export_image.c | 18 +- src/extract.c | 16 +- src/mount_image.c | 2 +- src/wim.c | 17 +- src/win32_apply.c | 8 +- src/xml.c | 2331 ++++++++++++++++++---------------------- tools/windeps/Makefile | 1 + 11 files changed, 1106 insertions(+), 1378 deletions(-) diff --git a/include/wimlib/wim.h b/include/wimlib/wim.h index 011ecc34..860a3236 100644 --- a/include/wimlib/wim.h +++ b/include/wimlib/wim.h @@ -11,7 +11,7 @@ #include "wimlib/list.h" struct wim_image_metadata; -struct wim_info; +struct wim_xml_info; struct blob_table; /* @@ -59,7 +59,7 @@ struct WIMStruct { /* Information from the XML data of the WIM file. This information is * also maintained for a WIMStruct not backed by a file. */ - struct wim_info *wim_info; + struct wim_xml_info *xml_info; /* The blob table for this WIMStruct. If this WIMStruct has a backing * file, then this table will index the blobs contained in that file. diff --git a/include/wimlib/xml.h b/include/wimlib/xml.h index 209bb8c1..9ce52104 100644 --- a/include/wimlib/xml.h +++ b/include/wimlib/xml.h @@ -3,56 +3,65 @@ #include "wimlib/types.h" -struct wim_info; -struct wim_reshdr; +/*****************************************************************************/ -extern u64 -wim_info_get_total_bytes(const struct wim_info *info); +struct wim_xml_info; -extern u64 -wim_info_get_image_hard_link_bytes(const struct wim_info *info, int image); +extern struct wim_xml_info * +xml_new_info_struct(void); + +extern void +xml_free_info_struct(struct wim_xml_info *info); + +/*****************************************************************************/ + +extern int +xml_get_image_count(const struct wim_xml_info *info); extern u64 -wim_info_get_image_total_bytes(const struct wim_info *info, int image); +xml_get_total_bytes(const struct wim_xml_info *info); -extern unsigned -wim_info_get_num_images(const struct wim_info *info); +extern u64 +xml_get_image_total_bytes(const struct wim_xml_info *info, int image); -extern void -wim_info_set_wimboot(struct wim_info *info, int image, bool value); +extern u64 +xml_get_image_hard_link_bytes(const struct wim_xml_info *info, int image); extern bool -wim_info_get_wimboot(const struct wim_info *info, int image); +xml_get_wimboot(const struct wim_xml_info *info, int image); extern u64 -wim_info_get_windows_build_number(const struct wim_info *info, int image); +xml_get_windows_build_number(const struct wim_xml_info *info, int image); extern int -xml_export_image(const struct wim_info *old_wim_info, int image, - struct wim_info **new_wim_info_p, - const tchar *dest_image_name, - const tchar *dest_image_description); +xml_set_wimboot(struct wim_xml_info *info, int image); -extern size_t -xml_get_max_image_name_len(const WIMStruct *wim); +/*****************************************************************************/ -extern void +extern int xml_update_image_info(WIMStruct *wim, int image); -extern void -xml_delete_image(struct wim_info **wim_info_p, int image); +extern int +xml_add_image(struct wim_xml_info *info, const tchar *name); extern int -xml_add_image(WIMStruct *wim, const tchar *name); +xml_export_image(const struct wim_xml_info *src_info, int src_image, + struct wim_xml_info *dest_info, const tchar *dest_image_name, + const tchar *dest_image_description, bool wimboot); extern void -free_wim_info(struct wim_info *info); +xml_delete_image(struct wim_xml_info *info, int image); + extern void -print_image_info(const struct wim_info *wim_info, int image); +xml_print_image_info(struct wim_xml_info *info, int image); + +/*****************************************************************************/ -#define WIM_TOTALBYTES_USE_EXISTING ((u64)0 - 1) -#define WIM_TOTALBYTES_OMIT ((u64)0 - 2) +struct wim_reshdr; + +#define WIM_TOTALBYTES_USE_EXISTING ((u64)(-1)) +#define WIM_TOTALBYTES_OMIT ((u64)(-2)) extern int read_wim_xml_data(WIMStruct *wim); @@ -62,11 +71,13 @@ write_wim_xml_data(WIMStruct *wim, int image, u64 total_bytes, struct wim_reshdr *out_reshdr, int write_resource_flags); +/*****************************************************************************/ + extern void -libxml_global_init(void); +xml_global_init(void); extern void -libxml_global_cleanup(void); +xml_global_cleanup(void); extern void xml_set_memory_allocator(void *(*malloc_func)(size_t), diff --git a/src/add_image.c b/src/add_image.c index e2106c9b..2a4a6f09 100644 --- a/src/add_image.c +++ b/src/add_image.c @@ -88,9 +88,6 @@ wimlib_add_empty_image(WIMStruct *wim, const tchar *name, int *new_idx_ret) { int ret; - if (!name) - name = T(""); - if (wimlib_image_name_in_use(wim, name)) { ERROR("There is already an image named \"%"TS"\" in the WIM!", name); @@ -101,7 +98,7 @@ wimlib_add_empty_image(WIMStruct *wim, const tchar *name, int *new_idx_ret) if (ret) return ret; - ret = xml_add_image(wim, name); + ret = xml_add_image(wim->xml_info, name); if (ret) { put_image_metadata(wim->image_metadata[--wim->hdr.image_count], NULL); @@ -180,14 +177,17 @@ wimlib_add_image_multisource(WIMStruct *wim, if (ret) goto out_delete_image; + /* If requested, mark the new image as WIMBoot-compatible. */ + if (add_flags & WIMLIB_ADD_FLAG_WIMBOOT) { + ret = xml_set_wimboot(wim->xml_info, wim->hdr.image_count); + if (ret) + goto out_delete_image; + } + /* If requested, set this image as the WIM's bootable image. */ if (add_flags & WIMLIB_ADD_FLAG_BOOT) wim->hdr.boot_idx = wim->hdr.image_count; - /* If requested, mark new image as WIMBoot-compatible. */ - if (add_flags & WIMLIB_ADD_FLAG_WIMBOOT) - wim_info_set_wimboot(wim->wim_info, wim->hdr.image_count, true); - return 0; out_delete_image: diff --git a/src/delete_image.c b/src/delete_image.c index 8844d6b3..42e1ae4e 100644 --- a/src/delete_image.c +++ b/src/delete_image.c @@ -57,7 +57,7 @@ delete_wim_image(WIMStruct *wim, int image) --wim->hdr.image_count; /* Remove the image from the XML information. */ - xml_delete_image(&wim->wim_info, image); + xml_delete_image(wim->xml_info, image); /* Fix the boot index. */ if (wim->hdr.boot_idx == image) diff --git a/src/export_image.c b/src/export_image.c index 8ad2d533..b7f4511e 100644 --- a/src/export_image.c +++ b/src/export_image.c @@ -176,14 +176,14 @@ wimlib_export_image(WIMStruct *src_wim, /* Determine destination image name and description. */ if (export_flags & WIMLIB_EXPORT_FLAG_NO_NAMES) - next_dest_name = T(""); + next_dest_name = NULL; else if (dest_name) next_dest_name = dest_name; else next_dest_name = wimlib_get_image_name(src_wim, src_image); if (export_flags & WIMLIB_EXPORT_FLAG_NO_DESCRIPTIONS) - next_dest_description = T(""); + next_dest_description = NULL; else if (dest_description) next_dest_description = dest_description; else @@ -216,9 +216,10 @@ wimlib_export_image(WIMStruct *src_wim, } /* Export XML information into the destination WIM. */ - ret = xml_export_image(src_wim->wim_info, src_image, - &dest_wim->wim_info, next_dest_name, - next_dest_description); + ret = xml_export_image(src_wim->xml_info, src_image, + dest_wim->xml_info, next_dest_name, + next_dest_description, + export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT); if (ret) goto out_rollback; @@ -248,9 +249,6 @@ wimlib_export_image(WIMStruct *src_wim, int dst_image = orig_dest_image_count + 1 + (src_image - start_src_image); - if (export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT) - wim_info_set_wimboot(dest_wim->wim_info, dst_image, true); - if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) && (!all_images || src_image == src_wim->hdr.boot_idx)) dest_wim->hdr.boot_idx = dst_image; @@ -263,10 +261,10 @@ wimlib_export_image(WIMStruct *src_wim, return 0; out_rollback: - while ((image = wim_info_get_num_images(dest_wim->wim_info)) + while ((image = xml_get_image_count(dest_wim->xml_info)) > orig_dest_image_count) { - xml_delete_image(&dest_wim->wim_info, image); + xml_delete_image(dest_wim->xml_info, image); } while (dest_wim->hdr.image_count > orig_dest_image_count) { diff --git a/src/extract.c b/src/extract.c index e80bbe94..04d484f4 100644 --- a/src/extract.c +++ b/src/extract.c @@ -1386,12 +1386,12 @@ extract_trees(WIMStruct *wim, struct wim_dentry **trees, size_t num_trees, * subtract from this if hard links are * supported by the extraction mode.) */ ctx->progress.extract.total_bytes = - wim_info_get_image_total_bytes(wim->wim_info, - wim->current_image); + xml_get_image_total_bytes(wim->xml_info, + wim->current_image); if (ctx->supported_features.hard_links) { ctx->progress.extract.total_bytes -= - wim_info_get_image_hard_link_bytes(wim->wim_info, - wim->current_image); + xml_get_image_hard_link_bytes(wim->xml_info, + wim->current_image); } } @@ -1699,7 +1699,8 @@ image_name_ok_as_dir(const tchar *image_name) return image_name && *image_name && !tstrpbrk(image_name, filename_forbidden_chars) && tstrcmp(image_name, T(".")) && - tstrcmp(image_name, T("..")); + tstrcmp(image_name, T("..")) && + tstrlen(image_name) <= 128; } /* Extracts all images from the WIM to the directory @target, with the images @@ -1707,9 +1708,8 @@ image_name_ok_as_dir(const tchar *image_name) static int extract_all_images(WIMStruct *wim, const tchar *target, int extract_flags) { - size_t image_name_max_len = max(xml_get_max_image_name_len(wim), 20); size_t output_path_len = tstrlen(target); - tchar buf[output_path_len + 1 + image_name_max_len + 1]; + tchar buf[output_path_len + 1 + 128 + 1]; int ret; int image; const tchar *image_name; @@ -1865,7 +1865,7 @@ wimlib_extract_image_from_pipe_with_progress(int pipe_fd, if (ret) goto out_wimlib_free; - if (wim_info_get_num_images(pwm->wim_info) != pwm->hdr.image_count) { + if (xml_get_image_count(pwm->xml_info) != pwm->hdr.image_count) { ERROR("Image count in XML data is not the same as in WIM header."); ret = WIMLIB_ERR_IMAGE_COUNT; goto out_wimlib_free; diff --git a/src/mount_image.c b/src/mount_image.c index aaad08c3..83f176a1 100644 --- a/src/mount_image.c +++ b/src/mount_image.c @@ -1061,7 +1061,7 @@ renew_current_image(struct wimfs_context *ctx) if (ret) goto err_free_new_blob; - ret = xml_add_image(wim, ""); + ret = xml_add_image(wim->xml_info, NULL); if (ret) goto err_undo_append; diff --git a/src/wim.c b/src/wim.c index df867890..9b5e4b24 100644 --- a/src/wim.c +++ b/src/wim.c @@ -176,8 +176,9 @@ wimlib_create_new_wim(enum wimlib_compression_type ctype, WIMStruct **wim_ret) if (!wim) return WIMLIB_ERR_NOMEM; + wim->xml_info = xml_new_info_struct(); wim->blob_table = new_blob_table(9001); - if (!wim->blob_table) { + if (!wim->xml_info || !wim->blob_table) { wimlib_free(wim); return WIMLIB_ERR_NOMEM; } @@ -440,7 +441,7 @@ wimlib_print_available_images(const WIMStruct *wim, int image) tputchar(T('-')); tputchar(T('\n')); for (i = first; i <= last; i++) - print_image_info(wim->wim_info, i); + xml_print_image_info(wim->xml_info, i); } /* API function documented in wimlib.h */ @@ -456,7 +457,7 @@ wimlib_get_wim_info(WIMStruct *wim, struct wimlib_wim_info *info) info->part_number = wim->hdr.part_number; info->total_parts = wim->hdr.total_parts; info->compression_type = wim->compression_type; - info->total_bytes = wim_info_get_total_bytes(wim->wim_info); + info->total_bytes = xml_get_total_bytes(wim->xml_info); info->has_integrity_table = wim_has_integrity_table(wim); info->opened_from_file = (wim->filename != NULL); info->is_readonly = (wim->hdr.flags & WIM_HDR_FLAG_READONLY) || @@ -619,7 +620,6 @@ static int begin_read(WIMStruct *wim, const void *wim_filename_or_fd, int open_flags) { int ret; - int xml_num_images; const tchar *wimfile; if (open_flags & WIMLIB_OPEN_FLAG_FROM_PIPE) { @@ -738,8 +738,7 @@ begin_read(WIMStruct *wim, const void *wim_filename_or_fd, int open_flags) if (ret) return ret; - xml_num_images = wim_info_get_num_images(wim->wim_info); - if (xml_num_images != wim->hdr.image_count) { + if (xml_get_image_count(wim->xml_info) != wim->hdr.image_count) { ERROR("The WIM's header is inconsistent with its XML data.\n" " Please submit a bug report if you believe this " "WIM file should be considered valid."); @@ -890,7 +889,7 @@ wimlib_free(WIMStruct *wim) wimlib_free_decompressor(wim->decompressor); FREE(wim->filename); - free_wim_info(wim->wim_info); + xml_free_info_struct(wim->xml_info); if (wim->image_metadata) { for (unsigned i = 0; i < wim->hdr.image_count; i++) put_image_metadata(wim->image_metadata[i], NULL); @@ -961,7 +960,7 @@ wimlib_global_init(int init_flags) WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE)) goto out_unlock; - libxml_global_init(); + xml_global_init(); if (!(init_flags & WIMLIB_INIT_FLAG_ASSUME_UTF8)) { wimlib_mbs_is_utf8 = test_locale_ctype_utf8(); #ifdef WITH_NTFS_3G @@ -1000,7 +999,7 @@ wimlib_global_cleanup(void) if (!lib_initialized) goto out_unlock; - libxml_global_cleanup(); + xml_global_cleanup(); iconv_global_cleanup(); #ifdef __WIN32__ win32_global_cleanup(); diff --git a/src/win32_apply.c b/src/win32_apply.c index c35fbec2..a19d138f 100644 --- a/src/win32_apply.c +++ b/src/win32_apply.c @@ -686,8 +686,8 @@ start_wimboot_extraction(struct list_head *dentry_list, struct win32_apply_ctx * int ret; struct wim_dentry *dentry; - if (!wim_info_get_wimboot(ctx->common.wim->wim_info, - ctx->common.wim->current_image)) + if (!xml_get_wimboot(ctx->common.wim->xml_info, + ctx->common.wim->current_image)) WARNING("The WIM image is not marked as WIMBoot compatible. This usually\n" " means it is not intended to be used to back a Windows operating\n" " system. Proceeding anyway."); @@ -2938,8 +2938,8 @@ win32_extract(struct list_head *dentry_list, struct apply_ctx *_ctx) goto out; } - ctx->windows_build_number = wim_info_get_windows_build_number(ctx->common.wim->wim_info, - ctx->common.wim->current_image); + ctx->windows_build_number = xml_get_windows_build_number(ctx->common.wim->xml_info, + ctx->common.wim->current_image); dentry_count = count_dentries(dentry_list); diff --git a/src/xml.c b/src/xml.c index c71debbf..60643756 100644 --- a/src/xml.c +++ b/src/xml.c @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2012, 2013 Eric Biggers + * Copyright (C) 2012, 2013, 2015 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free @@ -25,13 +25,11 @@ # include "config.h" #endif -#include #include #include -#include +#include #include -#include "wimlib/assert.h" #include "wimlib/blob_table.h" #include "wimlib/dentry.h" #include "wimlib/encoding.h" @@ -43,1493 +41,1231 @@ #include "wimlib/xml.h" #include "wimlib/write.h" -/* Structures used to form an in-memory representation of the XML data (other - * than the raw parse tree from libxml). */ +/* + * A wrapper around a WIM file's XML document. The XML document contains + * metadata about each image in the WIM file as well as metadata about the WIM + * file itself. + */ +struct wim_xml_info { -struct windows_version { - u64 major; - u64 minor; - u64 build; - u64 sp_build; - u64 sp_level; -}; + /* The parsed XML document as a libxml2 document tree */ + xmlDocPtr doc; -struct windows_info { - u64 arch; - tchar *product_name; - tchar *edition_id; - tchar *installation_type; - tchar *pkeyconfigversion; - tchar *hal; - tchar *product_type; - tchar *product_suite; - tchar **languages; - tchar *default_language; - size_t num_languages; - tchar *system_root; - bool windows_version_exists; - struct windows_version windows_version; -}; + /* The root element of the document. This is a cached value, equal to + * xmlDocGetRootElement(doc). */ + xmlNode *root; -struct image_info { - int index; - bool windows_info_exists; - u64 dir_count; - u64 file_count; - u64 total_bytes; - u64 hard_link_bytes; - u64 creation_time; - u64 last_modification_time; - struct windows_info windows_info; - tchar *name; - tchar *description; - tchar *display_name; - tchar *display_description; - tchar *flags; - bool wimboot; - - /* Note: must update clone_image_info() if adding new fields here */ + /* A malloc()ed array containing a pointer to the IMAGE element for each + * WIM image. The image with 1-based index 'i' is at index 'i - 1' in + * this array. Note: these pointers are cached values, since they could + * also be found by searching the document. */ + xmlNode **images; + + /* The number of WIM images (the length of 'images') */ + int image_count; + + /* Temporary memory for UTF-8 => 'tchar' string translations. When an + * API function needs to return a 'tchar' string, it uses one of these + * array slots to hold the string and returns a pointer to it. */ + tchar *strings[128]; + size_t next_string_idx; + size_t num_strings; }; -/* A struct wim_info structure corresponds to the entire XML data for a WIM file. */ -struct wim_info { - u64 total_bytes; - int num_images; - /* Array of `struct image_info's, one for each image in the WIM that is - * mentioned in the XML data. */ - struct image_info *images; -}; +/*----------------------------------------------------------------------------* + * Internal functions * + *----------------------------------------------------------------------------*/ -struct xml_string_spec { - const char *name; - size_t offset; -}; +/* Iterate through the children of an xmlNode. */ +#define node_for_each_child(parent, child) \ + for (child = (parent)->children; child != NULL; child = child->next) -#define ELEM(STRING_NAME, MEMBER_NAME) \ - {STRING_NAME, offsetof(struct image_info, MEMBER_NAME)} -static const struct xml_string_spec -image_info_xml_string_specs[] = { - ELEM("NAME", name), - ELEM("DESCRIPTION", description), - ELEM("DISPLAYNAME", display_name), - ELEM("DISPLAYDESCRIPTION", display_description), - ELEM("FLAGS", flags), -}; -#undef ELEM - -#define ELEM(STRING_NAME, MEMBER_NAME) \ - {STRING_NAME, offsetof(struct windows_info, MEMBER_NAME)} -static const struct xml_string_spec -windows_info_xml_string_specs[] = { - ELEM("PRODUCTNAME", product_name), - ELEM("EDITIONID", edition_id), - ELEM("INSTALLATIONTYPE", installation_type), - ELEM("HAL", hal), - ELEM("PRODUCTTYPE", product_type), - ELEM("PRODUCTSUITE", product_suite), -}; -#undef ELEM - -u64 -wim_info_get_total_bytes(const struct wim_info *info) +/* Is the specified node an element of the specified name? */ +static bool +node_is_element(const xmlNode *node, const xmlChar *name) { - if (info) - return info->total_bytes; - else - return 0; + return node->type == XML_ELEMENT_NODE && xmlStrEqual(node->name, name); } -u64 -wim_info_get_image_hard_link_bytes(const struct wim_info *info, int image) +/* Retrieve a pointer to the UTF-8 text contents of the specified node, or NULL + * if the node has no text contents. This assumes the simple case where the + * node has a single TEXT child node. */ +static const xmlChar * +node_get_text(const xmlNode *node) { - if (info) - return info->images[image - 1].hard_link_bytes; - else - return 0; + const xmlNode *child; + + if (!node) + return NULL; + node_for_each_child(node, child) + if (child->type == XML_TEXT_NODE && child->content) + return child->content; + return NULL; } -u64 -wim_info_get_image_total_bytes(const struct wim_info *info, int image) +/* Retrieve an unsigned integer from the contents of the specified node, + * decoding it using the specified base. If the node has no contents or does + * not contain a valid number, returns 0. */ +static u64 +node_get_number(const xmlNode *node, int base) { - if (info) - return info->images[image - 1].total_bytes; - else + const xmlChar *str = node_get_text(node); + char *end; + unsigned long long v; + + if (!str) return 0; + v = strtoull(str, &end, base); + if ((xmlChar *)end == str || *end || v >= UINT64_MAX) + return 0; + return v; } -unsigned -wim_info_get_num_images(const struct wim_info *info) +/* Retrieve the timestamp from a time node. This node should have child + * elements HIGHPART and LOWPART; these elements will be used to construct a + * Windows-style timestamp. */ +static u64 +node_get_timestamp(const xmlNode *node) { - if (info) - return info->num_images; - else + u64 timestamp = 0; + xmlNode *child; + + if (!node) return 0; + node_for_each_child(node, child) { + if (node_is_element(child, "HIGHPART")) + timestamp |= node_get_number(child, 16) << 32; + else if (node_is_element(child, "LOWPART")) + timestamp |= node_get_number(child, 16); + } + return timestamp; } -void -wim_info_set_wimboot(struct wim_info *info, int image, bool value) +static int +tstr_get_utf8(const tchar *tstr, const xmlChar **utf8_ret) { - info->images[image - 1].wimboot = value; + if (wimlib_mbs_is_utf8) { + *utf8_ret = (xmlChar *)tstr; + return 0; + } + return tstr_to_utf8_simple(tstr, (char **)utf8_ret); } -bool -wim_info_get_wimboot(const struct wim_info *info, int image) +static void +tstr_put_utf8(const xmlChar *utf8) { - return info->images[image - 1].wimboot; + if (!wimlib_mbs_is_utf8) + FREE((void *)utf8); } -u64 -wim_info_get_windows_build_number(const struct wim_info *info, int image) +/* Retrieve the text contents of an XML element as a 'tchar' string. If not + * found or if the text could not be translated, returns NULL. */ +static const tchar * +node_get_ttext(struct wim_xml_info *info, xmlNode *node) { - return info->images[image - 1].windows_info.windows_version.build; -} + const xmlChar *text; + tchar **ttext_p; -/* Architecture constants are from w64 mingw winnt.h */ -#define PROCESSOR_ARCHITECTURE_INTEL 0 -#define PROCESSOR_ARCHITECTURE_MIPS 1 -#define PROCESSOR_ARCHITECTURE_ALPHA 2 -#define PROCESSOR_ARCHITECTURE_PPC 3 -#define PROCESSOR_ARCHITECTURE_SHX 4 -#define PROCESSOR_ARCHITECTURE_ARM 5 -#define PROCESSOR_ARCHITECTURE_IA64 6 -#define PROCESSOR_ARCHITECTURE_ALPHA64 7 -#define PROCESSOR_ARCHITECTURE_MSIL 8 -#define PROCESSOR_ARCHITECTURE_AMD64 9 -#define PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 10 - -/* Returns a statically allocated string that is a string representation of the - * architecture number. */ -static const tchar * -get_arch(int arch) -{ - switch (arch) { - case PROCESSOR_ARCHITECTURE_INTEL: - return T("x86"); - case PROCESSOR_ARCHITECTURE_MIPS: - return T("MIPS"); - case PROCESSOR_ARCHITECTURE_ARM: - return T("ARM"); - case PROCESSOR_ARCHITECTURE_IA64: - return T("ia64"); - case PROCESSOR_ARCHITECTURE_AMD64: - return T("x86_64"); - default: - return T("unknown"); - } -} + text = node_get_text(node); + if (!text || wimlib_mbs_is_utf8) + return (const tchar *)text; -/* Iterate through the children of an xmlNode. */ -#define for_node_child(parent, child) \ - for (child = parent->children; child != NULL; child = child->next) + ttext_p = &info->strings[info->next_string_idx]; + if (info->num_strings >= ARRAY_LEN(info->strings)) { + FREE(*ttext_p); + *ttext_p = NULL; + } + if (utf8_to_tstr_simple(text, ttext_p)) + return NULL; + if (info->num_strings < ARRAY_LEN(info->strings)) + info->num_strings++; + info->next_string_idx++; + info->next_string_idx %= ARRAY_LEN(info->strings); + return *ttext_p; +} -/* Utility functions for xmlNodes */ -static inline bool -node_is_element(xmlNode *node) +/* Unlink the specified node from its parent, then free it (recursively). */ +static void +unlink_and_free_tree(xmlNode *node) { - return node->type == XML_ELEMENT_NODE; + xmlUnlinkNode(node); + xmlFreeNode(node); } -static inline bool -node_is_text(xmlNode *node) +/* Unlink and free (recursively) all children of the specified node. */ +static void +unlink_and_free_children(xmlNode *node) { - return node->type == XML_TEXT_NODE; + xmlNode *child; + + while ((child = node->last) != NULL) + unlink_and_free_tree(child); } -static inline bool -node_name_is(xmlNode *node, const char *name) +/* Add the new child element 'replacement' to 'parent', replacing any same-named + * element that may already exist. */ +static void +node_replace_child_element(xmlNode *parent, xmlNode *replacement) { - /* For now, both upper case and lower case element names are accepted. */ - return strcasecmp((const char *)node->name, name) == 0; + xmlNode *child; + + node_for_each_child(parent, child) { + if (node_is_element(child, replacement->name)) { + xmlReplaceNode(child, replacement); + xmlFreeNode(child); + return; + } + } + + xmlAddChild(parent, replacement); } -static u64 -node_get_number(const xmlNode *u64_node, int base) +/* Set the text contents of the specified element to the specified string, + * replacing the existing contents (if any). The string is "raw" and is + * permitted to contain characters that have special meaning in XML. */ +static int +node_set_text(xmlNode *node, const xmlChar *text) { - xmlNode *child; - for_node_child(u64_node, child) - if (node_is_text(child)) - return strtoull(child->content, NULL, base); + xmlNode *text_node = xmlNewText(text); + if (!text_node) + return WIMLIB_ERR_NOMEM; + unlink_and_free_children(node); + xmlAddChild(node, text_node); return 0; } -/* Finds the text node that is a child of an element node and returns its - * content converted to a 64-bit unsigned integer. Returns 0 if no text node is - * found. */ -static u64 -node_get_u64(const xmlNode *u64_node) +/* Like 'node_set_text()', but takes in a 'tchar' string. */ +static int +node_set_ttext(xmlNode *node, const tchar *ttext) { - return node_get_number(u64_node, 10); + const xmlChar *text; + int ret; + + ret = tstr_get_utf8(ttext, &text); + if (ret) + return ret; + ret = node_set_text(node, text); + tstr_put_utf8(text); + return ret; } -/* Like node_get_u64(), but expects a number in base 16. */ -static u64 -node_get_hex_u64(const xmlNode *u64_node) +/* Create a new element containing text and optionally link it into a tree. */ +static xmlNode * +new_element_with_text(xmlNode *parent, const xmlChar *name, const xmlChar *text) { - return node_get_number(u64_node, 16); + xmlNode *node; + + node = xmlNewNode(NULL, name); + if (!node) + return NULL; + + if (node_set_text(node, text)) { + xmlFreeNode(node); + return NULL; + } + + if (parent) + xmlAddChild(parent, node); + return node; } +/* Create a new element containing text and optionally link it into a tree. */ static int -node_get_string(const xmlNode *string_node, tchar **tstr_ret) +new_element_with_ttext(xmlNode *parent, const xmlChar *name, const tchar *ttext, + xmlNode **node_ret) { - xmlNode *child; - - if (*tstr_ret) - return 0; + const xmlChar *text; + int ret; + xmlNode *node; - for_node_child(string_node, child) - if (node_is_text(child) && child->content) - return utf8_to_tstr_simple(child->content, tstr_ret); + ret = tstr_get_utf8(ttext, &text); + if (ret) + return ret; + node = new_element_with_text(parent, name, text); + tstr_put_utf8(text); + if (!node) + return WIMLIB_ERR_NOMEM; + if (node_ret) + *node_ret = node; return 0; } -/* Returns the timestamp from a time node. It has child elements and - * that are then used to construct a 64-bit timestamp. */ -static u64 -node_get_timestamp(const xmlNode *time_node) +/* Create a new timestamp element and optionally link it into a tree. */ +static xmlNode * +new_element_with_timestamp(xmlNode *parent, const xmlChar *name, u64 timestamp) { - u32 high_part = 0; - u32 low_part = 0; - xmlNode *child; - for_node_child(time_node, child) { - if (!node_is_element(child)) - continue; - if (node_name_is(child, "HIGHPART")) - high_part = node_get_hex_u64(child); - else if (node_name_is(child, "LOWPART")) - low_part = node_get_hex_u64(child); - } - return (u64)low_part | ((u64)high_part << 32); -} + xmlNode *node; + char buf[32]; -/* Used to sort an array of struct image_infos by their image indices. */ -static int -sort_by_index(const void *p1, const void *p2) -{ - int index_1 = ((const struct image_info*)p1)->index; - int index_2 = ((const struct image_info*)p2)->index; - if (index_1 < index_2) - return -1; - else if (index_1 > index_2) - return 1; - else - return 0; -} + node = xmlNewNode(NULL, name); + if (!node) + goto err; + sprintf(buf, "0x%08"PRIX32, (u32)(timestamp >> 32)); + if (!new_element_with_text(node, "HIGHPART", buf)) + goto err; -/* Frees memory allocated inside a struct windows_info structure. */ -static void -destroy_windows_info(struct windows_info *windows_info) -{ - FREE(windows_info->product_name); - FREE(windows_info->edition_id); - FREE(windows_info->installation_type); - FREE(windows_info->hal); - FREE(windows_info->product_type); - FREE(windows_info->product_suite); - FREE(windows_info->pkeyconfigversion); - for (size_t i = 0; i < windows_info->num_languages; i++) - FREE(windows_info->languages[i]); - FREE(windows_info->languages); - FREE(windows_info->default_language); - FREE(windows_info->system_root); -} - -/* Frees memory allocated inside a struct image_info structure. */ -static void -destroy_image_info(struct image_info *image_info) -{ - FREE(image_info->name); - FREE(image_info->description); - FREE(image_info->flags); - FREE(image_info->display_name); - FREE(image_info->display_description); - destroy_windows_info(&image_info->windows_info); - memset(image_info, 0, sizeof(struct image_info)); + sprintf(buf, "0x%08"PRIX32, (u32)timestamp); + if (!new_element_with_text(node, "LOWPART", buf)) + goto err; + + if (parent) + xmlAddChild(parent, node); + return node; + +err: + xmlFreeNode(node); + return NULL; } -void -free_wim_info(struct wim_info *info) +/* Create a new number element and optionally link it into a tree. */ +static xmlNode * +new_element_with_u64(xmlNode *parent, const xmlChar *name, u64 value) { - if (info) { - if (info->images) { - for (int i = 0; i < info->num_images; i++) - destroy_image_info(&info->images[i]); - FREE(info->images); - } - FREE(info); - } + char buf[32]; + + sprintf(buf, "%"PRIu64, value); + return new_element_with_text(parent, name, buf); } -/* Reads the information from a element inside the element. - * */ -static void -xml_read_windows_version(const xmlNode *version_node, - struct windows_version* windows_version) +/* Allocate a 'struct wim_xml_info'. The caller is responsible for initializing + * the document and the images array. */ +static struct wim_xml_info * +alloc_wim_xml_info(void) { - xmlNode *child; - for_node_child(version_node, child) { - if (!node_is_element(child)) - continue; - if (node_name_is(child, "MAJOR")) - windows_version->major = node_get_u64(child); - else if (node_name_is(child, "MINOR")) - windows_version->minor = node_get_u64(child); - else if (node_name_is(child, "BUILD")) - windows_version->build = node_get_u64(child); - else if (node_name_is(child, "SPBUILD")) - windows_version->sp_build = node_get_u64(child); - else if (node_name_is(child, "SPLEVEL")) - windows_version->sp_level = node_get_u64(child); + struct wim_xml_info *info = MALLOC(sizeof(*info)); + if (info) { + info->next_string_idx = 0; + info->num_strings = 0; } + return info; } -/* Reads the information from a element inside a element. - * */ static int -xml_read_languages(const xmlNode *languages_node, - tchar ***languages_ret, - size_t *num_languages_ret, - tchar **default_language_ret) +do_xml_path_walk(xmlNode *node, const xmlChar *path, bool create, + xmlNode **result_ret) { - xmlNode *child; - size_t num_languages = 0; - tchar **languages; - int ret; + size_t n = strlen(path) + 1; + xmlChar buf[n]; + xmlChar *p; + xmlChar c; - for_node_child(languages_node, child) - if (node_is_element(child) && node_name_is(child, "LANGUAGE")) - num_languages++; - - languages = CALLOC(num_languages, sizeof(languages[0])); - if (!languages) - return WIMLIB_ERR_NOMEM; + *result_ret = NULL; - *languages_ret = languages; - *num_languages_ret = num_languages; + if (!node) + return 0; - ret = 0; - for_node_child(languages_node, child) { - if (!node_is_element(child)) - continue; - if (node_name_is(child, "LANGUAGE")) - ret = node_get_string(child, languages++); - else if (node_name_is(child, "DEFAULT")) - ret = node_get_string(child, default_language_ret); - if (ret != 0) - break; + /* Copy the path to a temporary buffer. */ + memcpy(buf, path, n); + p = buf; + + if (*p == '/') + goto bad_syntax; + if (strchr(p, '[')) /* reserved for future use */ + goto bad_syntax; + c = *p; + + while (c != '\0') { + const xmlChar *name; + xmlNode *child; + + /* We have another path component. */ + + /* Parse the element name. */ + name = p; + while (*p != '/' && *p != '\0') + p++; + if (p == name) /* empty name? */ + goto bad_syntax; + c = *p; + *p = '\0'; + + /* Look for a matching child. */ + node_for_each_child(node, child) + if (node_is_element(child, name)) + goto next_step; + + /* No child matched the path. If create=false, the lookup + * failed. If create=true, create the needed element. */ + if (!create) + return 0; + child = xmlNewChild(node, NULL, name, NULL); + if (!child) + return WIMLIB_ERR_NOMEM; + next_step: + /* Continue to the next path component, if there is one. */ + node = child; + p++; } - return ret; + + *result_ret = node; + return 0; + +bad_syntax: + ERROR("The XML path \"%s\" has invalid syntax.", path); + return WIMLIB_ERR_INVALID_PARAM; } -/* Reads the information from a element inside an element. */ -static int -xml_read_windows_info(const xmlNode *windows_node, - struct windows_info *windows_info) +/* Retrieve the XML element, if any, at the specified 'path'. This supports a + * simple filesystem-like syntax. If the element was found, returns a pointer + * to it; otherwise returns NULL. */ +static xmlNode * +xml_get_node_by_path(xmlNode *root, const xmlChar *path) { - xmlNode *child; - int ret = 0; + xmlNode *node; + do_xml_path_walk(root, path, false, &node); + return node; +} - for_node_child(windows_node, child) { - if (!node_is_element(child)) - continue; - if (node_name_is(child, "ARCH")) { - windows_info->arch = node_get_u64(child); - } else if (node_name_is(child, "PRODUCTNAME")) { - ret = node_get_string(child, - &windows_info->product_name); - } else if (node_name_is(child, "EDITIONID")) { - ret = node_get_string(child, - &windows_info->edition_id); - } else if (node_name_is(child, "INSTALLATIONTYPE")) { - ret = node_get_string(child, - &windows_info->installation_type); - } else if (node_name_is(child, "PRODUCTTYPE")) { - ret = node_get_string(child, - &windows_info->product_type); - } else if (node_name_is(child, "PRODUCTSUITE")) { - ret = node_get_string(child, - &windows_info->product_suite); - } else if (node_name_is(child, "LANGUAGES")) { - ret = xml_read_languages(child, - &windows_info->languages, - &windows_info->num_languages, - &windows_info->default_language); - } else if (node_name_is(child, "VERSION")) { - xml_read_windows_version(child, - &windows_info->windows_version); - windows_info->windows_version_exists = true; - } else if (node_name_is(child, "SYSTEMROOT")) { - ret = node_get_string(child, &windows_info->system_root); - } else if (node_name_is(child, "HAL")) { - ret = node_get_string(child, &windows_info->hal); - } else if (node_name_is(child, "SERVICINGDATA")) { - xmlNode *grandchild; - - for_node_child(child, grandchild) { - if (node_is_element(grandchild) && - node_name_is(grandchild, "PKEYCONFIGVERSION")) - { - ret = node_get_string(grandchild, - &windows_info->pkeyconfigversion); - } - } - } +/* Similar to xml_get_node_by_path(), but creates the element and any requisite + * ancestor elements as needed. If successful, 0 is returned and *node_ret is + * set to a pointer to the resulting element. If unsuccessful, an error code is + * returned and *node_ret is set to NULL. */ +static int +xml_ensure_node_by_path(xmlNode *root, const xmlChar *path, xmlNode **node_ret) +{ + return do_xml_path_walk(root, path, true, node_ret); +} - if (ret != 0) - return ret; - } - return ret; +static u64 +xml_get_number_by_path(xmlNode *root, const xmlChar *path) +{ + return node_get_number(xml_get_node_by_path(root, path), 10); } -/* Reads the information from an element. */ -static int -xml_read_image_info(xmlNode *image_node, struct image_info *image_info) +static u64 +xml_get_timestamp_by_path(xmlNode *root, const xmlChar *path) { - xmlNode *child; - xmlChar *index_prop; - int ret; + return node_get_timestamp(xml_get_node_by_path(root, path)); +} - index_prop = xmlGetProp(image_node, "INDEX"); - if (index_prop) { - image_info->index = atoi(index_prop); - xmlFree(index_prop); - } else { - image_info->index = 1; - } +static const xmlChar * +xml_get_text_by_path(xmlNode *root, const xmlChar *path) +{ + return node_get_text(xml_get_node_by_path(root, path)); +} - ret = 0; - for_node_child(image_node, child) { - if (!node_is_element(child)) - continue; - if (node_name_is(child, "DIRCOUNT")) - image_info->dir_count = node_get_u64(child); - else if (node_name_is(child, "FILECOUNT")) - image_info->file_count = node_get_u64(child); - else if (node_name_is(child, "TOTALBYTES")) - image_info->total_bytes = node_get_u64(child); - else if (node_name_is(child, "HARDLINKBYTES")) - image_info->hard_link_bytes = node_get_u64(child); - else if (node_name_is(child, "CREATIONTIME")) - image_info->creation_time = node_get_timestamp(child); - else if (node_name_is(child, "LASTMODIFICATIONTIME")) - image_info->last_modification_time = node_get_timestamp(child); - else if (node_name_is(child, "WINDOWS")) { - ret = xml_read_windows_info(child, - &image_info->windows_info); - image_info->windows_info_exists = true; - } else if (node_name_is(child, "NAME")) { - ret = node_get_string(child, &image_info->name); - } else if (node_name_is(child, "DESCRIPTION")) { - ret = node_get_string(child, &image_info->description); - } else if (node_name_is(child, "FLAGS")) { - ret = node_get_string(child, &image_info->flags); - } else if (node_name_is(child, "DISPLAYNAME")) { - ret = node_get_string(child, &image_info->display_name); - } else if (node_name_is(child, "DISPLAYDESCRIPTION")) { - ret = node_get_string(child, &image_info->display_description); - } else if (node_name_is(child, "WIMBOOT")) { - if (node_get_u64(child) == 1) { - image_info->wimboot = true; - } - } - if (ret != 0) - return ret; - } - if (!image_info->name) { - tchar *empty_name; - empty_name = MALLOC(sizeof(tchar)); - if (!empty_name) - return WIMLIB_ERR_NOMEM; - *empty_name = T('\0'); - image_info->name = empty_name; - } - return ret; +static const tchar * +xml_get_ttext_by_path(struct wim_xml_info *info, xmlNode *root, + const xmlChar *path) +{ + return node_get_ttext(info, xml_get_node_by_path(root, path)); } -/* Reads the information from a element, which should be the root element - * of the XML tree. */ +/* Creates/replaces (if ttext is not NULL and not empty) or removes (if ttext is + * NULL or empty) an element containing text. */ static int -xml_read_wim_info(const xmlNode *wim_node, struct wim_info **wim_info_ret) +xml_set_ttext_by_path(xmlNode *root, const xmlChar *path, const tchar *ttext) { - struct wim_info *wim_info; - xmlNode *child; int ret; - int num_images; - int i; - - wim_info = CALLOC(1, sizeof(struct wim_info)); - if (!wim_info) - return WIMLIB_ERR_NOMEM; - - /* Count how many images there are. */ - num_images = 0; - for_node_child(wim_node, child) { - if (node_is_element(child) && node_name_is(child, "IMAGE")) { - if (unlikely(num_images == MAX_IMAGES)) { - ret = WIMLIB_ERR_IMAGE_COUNT; - goto err; - } - num_images++; - } - } - - if (num_images > 0) { - /* Allocate the array of struct image_infos and fill them in. */ - wim_info->images = CALLOC(num_images, sizeof(wim_info->images[0])); - if (!wim_info->images) { - ret = WIMLIB_ERR_NOMEM; - goto err; - } - wim_info->num_images = num_images; - i = 0; - for_node_child(wim_node, child) { - if (!node_is_element(child)) - continue; - if (node_name_is(child, "IMAGE")) { - ret = xml_read_image_info(child, - &wim_info->images[i]); - if (ret != 0) - goto err; - i++; - } else if (node_name_is(child, "TOTALBYTES")) { - wim_info->total_bytes = node_get_u64(child); - } else if (node_name_is(child, "ESD")) { - xmlNode *esdchild; - for_node_child(child, esdchild) { - if (node_is_element(esdchild) && - node_name_is(esdchild, "ENCRYPTED")) - { - ret = WIMLIB_ERR_WIM_IS_ENCRYPTED; - goto err; - } - } - } - } - - /* Sort the array of image info by image index. */ - qsort(wim_info->images, num_images, - sizeof(struct image_info), sort_by_index); - - /* Make sure the image indices make sense */ - for (i = 0; i < num_images; i++) { - if (wim_info->images[i].index != i + 1) { - ERROR("WIM images are not indexed [1...%d] " - "in XML data as expected", - num_images); - ret = WIMLIB_ERR_IMAGE_COUNT; - goto err; - } - } + xmlNode *node; + if (ttext && *ttext) { + /* Create or replace */ + ret = xml_ensure_node_by_path(root, path, &node); + if (ret) + return ret; + return node_set_ttext(node, ttext); + } else { + /* Remove */ + node = xml_get_node_by_path(root, path); + if (node) + unlink_and_free_tree(node); + return 0; } - *wim_info_ret = wim_info; - return 0; -err: - free_wim_info(wim_info); - return ret; } -/* Prints the information contained in a `struct windows_info'. */ -static void -print_windows_info(const struct windows_info *windows_info) +/* Sets a string property for the specified WIM image. */ +static int +set_image_property(WIMStruct *wim, int image, const xmlChar *name, + const tchar *value) { - const struct windows_version *windows_version; - - tprintf(T("Architecture: %"TS"\n"), - get_arch(windows_info->arch)); + struct wim_xml_info *info = wim->xml_info; - if (windows_info->product_name) { - tprintf(T("Product Name: %"TS"\n"), - windows_info->product_name); - } + if (image < 1 || image > info->image_count) + return WIMLIB_ERR_INVALID_IMAGE; - if (windows_info->edition_id) { - tprintf(T("Edition ID: %"TS"\n"), - windows_info->edition_id); - } + return xml_set_ttext_by_path(info->images[image - 1], name, value); +} - if (windows_info->installation_type) { - tprintf(T("Installation Type: %"TS"\n"), - windows_info->installation_type); - } +/* Gets a string property for the specified WIM image as a 'tchar' string. + * Returns a pointer to the property value if found; NULL if the image doesn't + * exist; or 'default_value' if the property doesn't exist in the image or if + * the property value could not be translated to a 'tchar' string. */ +static const tchar * +get_image_property(const WIMStruct *wim, int image, const xmlChar *name, + const tchar *default_value) +{ + struct wim_xml_info *info = wim->xml_info; + const tchar *value; - if (windows_info->hal) { - tprintf(T("HAL: %"TS"\n"), - windows_info->hal); - } + if (image < 1 || image > info->image_count) + return NULL; - if (windows_info->product_type) { - tprintf(T("Product Type: %"TS"\n"), - windows_info->product_type); - } + value = xml_get_ttext_by_path(info, info->images[image - 1], name); + return value ? value : default_value; +} - if (windows_info->product_suite) { - tprintf(T("Product Suite: %"TS"\n"), - windows_info->product_suite); - } +/* Unlink and return the node which represents the INDEX attribute of the + * specified IMAGE element. */ +static xmlAttr * +unlink_index_attribute(xmlNode *image_node) +{ + xmlAttr *attr = xmlHasProp(image_node, "INDEX"); + xmlUnlinkNode((xmlNode *)attr); + return attr; +} - tprintf(T("Languages: ")); - for (size_t i = 0; i < windows_info->num_languages; i++) { +/* Compute the total uncompressed size of the streams of the specified inode. */ +static u64 +inode_sum_stream_sizes(const struct wim_inode *inode, + const struct blob_table *blob_table) +{ + u64 total_size = 0; - tfputs(windows_info->languages[i], stdout); - tputchar(T(' ')); - } - tputchar(T('\n')); - if (windows_info->default_language) { - tprintf(T("Default Language: %"TS"\n"), - windows_info->default_language); - } - if (windows_info->system_root) { - tprintf(T("System Root: %"TS"\n"), - windows_info->system_root); - } + for (unsigned i = 0; i < inode->i_num_streams; i++) { + const struct blob_descriptor *blob; - if (windows_info->windows_version_exists) { - windows_version = &windows_info->windows_version; - tprintf(T("Major Version: %"PRIu64"\n"), - windows_version->major); - tprintf(T("Minor Version: %"PRIu64"\n"), - windows_version->minor); - tprintf(T("Build: %"PRIu64"\n"), - windows_version->build); - tprintf(T("Service Pack Build: %"PRIu64"\n"), - windows_version->sp_build); - tprintf(T("Service Pack Level: %"PRIu64"\n"), - windows_version->sp_level); + blob = stream_blob(&inode->i_streams[i], blob_table); + if (blob) + total_size += blob->size; } + return total_size; } static int -xml_write_string(xmlTextWriter *writer, const char *name, - const tchar *tstr) -{ - if (tstr) { - char *utf8_str; - int rc = tstr_to_utf8_simple(tstr, &utf8_str); - if (rc) - return rc; - rc = xmlTextWriterWriteElement(writer, name, utf8_str); - FREE(utf8_str); - if (rc < 0) - return rc; - } - return 0; -} +append_image_node(struct wim_xml_info *info, xmlNode *image_node) +{ + char buf[32]; + xmlNode **images; -static int -xml_write_strings_from_specs(xmlTextWriter *writer, - const void *struct_with_strings, - const struct xml_string_spec specs[], - size_t num_specs) -{ - for (size_t i = 0; i < num_specs; i++) { - int rc = xml_write_string(writer, specs[i].name, - *(const tchar * const *) - (struct_with_strings + specs[i].offset)); - if (rc) - return rc; - } - return 0; -} + /* Limit exceeded? */ + if (unlikely(info->image_count >= MAX_IMAGES)) + return WIMLIB_ERR_IMAGE_COUNT; -static int -dup_strings_from_specs(const void *old_struct_with_strings, - void *new_struct_with_strings, - const struct xml_string_spec specs[], - size_t num_specs) -{ - for (size_t i = 0; i < num_specs; i++) { - const tchar *old_str = *(const tchar * const *) - ((const void*)old_struct_with_strings + specs[i].offset); - tchar **new_str_p = (tchar **)((void*)new_struct_with_strings + specs[i].offset); - if (old_str) { - *new_str_p = TSTRDUP(old_str); - if (!*new_str_p) - return WIMLIB_ERR_NOMEM; - } - } - return 0; -} + /* Add the INDEX attribute. */ + sprintf(buf, "%d", info->image_count + 1); + if (!xmlNewProp(image_node, "INDEX", buf)) + return WIMLIB_ERR_NOMEM; -/* Writes the information contained in a `struct windows_version' to the XML - * document being written. This is the element inside the - * element. */ -static int -xml_write_windows_version(xmlTextWriter *writer, - const struct windows_version *version) -{ - int rc; - rc = xmlTextWriterStartElement(writer, "VERSION"); - if (rc < 0) - return rc; - - rc = xmlTextWriterWriteFormatElement(writer, "MAJOR", "%"PRIu64, - version->major); - if (rc < 0) - return rc; - - rc = xmlTextWriterWriteFormatElement(writer, "MINOR", "%"PRIu64, - version->minor); - if (rc < 0) - return rc; - - rc = xmlTextWriterWriteFormatElement(writer, "BUILD", "%"PRIu64, - version->build); - if (rc < 0) - return rc; - - rc = xmlTextWriterWriteFormatElement(writer, "SPBUILD", "%"PRIu64, - version->sp_build); - if (rc < 0) - return rc; - - rc = xmlTextWriterWriteFormatElement(writer, "SPLEVEL", "%"PRIu64, - version->sp_level); - if (rc < 0) - return rc; - - rc = xmlTextWriterEndElement(writer); /* */ - if (rc < 0) - return rc; + /* Append the IMAGE element to the 'images' array. */ + images = REALLOC(info->images, + (info->image_count + 1) * sizeof(info->images[0])); + if (unlikely(!images)) + return WIMLIB_ERR_NOMEM; + info->images = images; + images[info->image_count++] = image_node; + /* Add the IMAGE element to the document. */ + xmlAddChild(info->root, image_node); return 0; } -/* Writes the information contained in a `struct windows_info' to the XML - * document being written. This is the element. */ -static int -xml_write_windows_info(xmlTextWriter *writer, - const struct windows_info *windows_info) -{ - int rc; - rc = xmlTextWriterStartElement(writer, "WINDOWS"); - if (rc < 0) - return rc; - - rc = xmlTextWriterWriteFormatElement(writer, "ARCH", "%"PRIu64, - windows_info->arch); - if (rc < 0) - return rc; - - rc = xml_write_strings_from_specs(writer, - windows_info, - windows_info_xml_string_specs, - ARRAY_LEN(windows_info_xml_string_specs)); - if (rc) - return rc; - - if (windows_info->num_languages) { - rc = xmlTextWriterStartElement(writer, "LANGUAGES"); - if (rc < 0) - return rc; - - for (size_t i = 0; i < windows_info->num_languages; i++) { - rc = xml_write_string(writer, "LANGUAGE", - windows_info->languages[i]); - if (rc) - return rc; - } - - rc = xml_write_string(writer, "DEFAULT", - windows_info->default_language); - if (rc) - return rc; - - rc = xmlTextWriterEndElement(writer); /* */ - if (rc < 0) - return rc; - } - - if (windows_info->pkeyconfigversion) { - rc = xmlTextWriterStartElement(writer, "SERVICINGDATA"); - if (rc < 0) - return rc; - - rc = xml_write_string(writer, "PKEYCONFIGVERSION", - windows_info->pkeyconfigversion); - if (rc) - return rc; +/*----------------------------------------------------------------------------* + * Functions for internal library use * + *----------------------------------------------------------------------------*/ - rc = xmlTextWriterEndElement(writer); - if (rc < 0) - return rc; - } +/* Allocate an empty 'struct wim_xml_info', containing no images. */ +struct wim_xml_info * +xml_new_info_struct(void) +{ + struct wim_xml_info *info; - if (windows_info->windows_version_exists) { - rc = xml_write_windows_version(writer, &windows_info->windows_version); - if (rc) - return rc; - } + info = alloc_wim_xml_info(); + if (!info) + goto err; - rc = xml_write_string(writer, "SYSTEMROOT", windows_info->system_root); - if (rc) - return rc; + info->doc = xmlNewDoc("1.0"); + if (!info->doc) + goto err_free_info; - rc = xmlTextWriterEndElement(writer); /* */ - if (rc < 0) - return rc; + info->root = xmlNewNode(NULL, "WIM"); + if (!info->root) + goto err_free_doc; + xmlDocSetRootElement(info->doc, info->root); - return 0; -} + info->images = NULL; + info->image_count = 0; + return info; -/* Writes a time element to the XML document being constructed in memory. */ -static int -xml_write_time(xmlTextWriter *writer, const char *element_name, u64 time) -{ - int rc; - rc = xmlTextWriterStartElement(writer, element_name); - if (rc < 0) - return rc; - - rc = xmlTextWriterWriteFormatElement(writer, "HIGHPART", - "0x%08"PRIX32, (u32)(time >> 32)); - if (rc < 0) - return rc; - - rc = xmlTextWriterWriteFormatElement(writer, "LOWPART", - "0x%08"PRIX32, (u32)time); - if (rc < 0) - return rc; - - rc = xmlTextWriterEndElement(writer); /* */ - if (rc < 0) - return rc; - return 0; +err_free_doc: + xmlFreeDoc(info->doc); +err_free_info: + FREE(info); +err: + return NULL; } -/* Writes an element to the XML document. */ -static int -xml_write_image_info(xmlTextWriter *writer, const struct image_info *image_info, - int index) -{ - int rc; - - rc = xmlTextWriterStartElement(writer, "IMAGE"); - if (rc < 0) - return rc; - - rc = xmlTextWriterWriteFormatAttribute(writer, "INDEX", "%d", index); - if (rc < 0) - return rc; - - rc = xmlTextWriterWriteFormatElement(writer, "DIRCOUNT", "%"PRIu64, - image_info->dir_count); - if (rc < 0) - return rc; - - rc = xmlTextWriterWriteFormatElement(writer, "FILECOUNT", "%"PRIu64, - image_info->file_count); - if (rc < 0) - return rc; - - rc = xmlTextWriterWriteFormatElement(writer, "TOTALBYTES", "%"PRIu64, - image_info->total_bytes); - if (rc < 0) - return rc; - - rc = xmlTextWriterWriteFormatElement(writer, "HARDLINKBYTES", "%"PRIu64, - image_info->hard_link_bytes); - if (rc < 0) - return rc; - - rc = xml_write_time(writer, "CREATIONTIME", image_info->creation_time); - if (rc < 0) - return rc; - - rc = xml_write_time(writer, "LASTMODIFICATIONTIME", - image_info->last_modification_time); - if (rc < 0) - return rc; - - if (image_info->windows_info_exists) { - rc = xml_write_windows_info(writer, &image_info->windows_info); - if (rc) - return rc; +/* Free a 'struct wim_xml_info'. */ +void +xml_free_info_struct(struct wim_xml_info *info) +{ + if (info) { + xmlFreeDoc(info->doc); + FREE(info->images); + for (size_t i = 0; i < info->num_strings; i++) + FREE(info->strings[i]); + FREE(info); } +} - rc = xml_write_strings_from_specs(writer, image_info, - image_info_xml_string_specs, - ARRAY_LEN(image_info_xml_string_specs)); - if (rc) - return rc; - - if (image_info->wimboot) { - rc = xmlTextWriterWriteFormatElement(writer, "WIMBOOT", "%d", 1); - if (rc < 0) - return rc; - } +/* Retrieve the number of images for which there exist IMAGE elements in the XML + * document. */ +int +xml_get_image_count(const struct wim_xml_info *info) +{ + return info->image_count; +} - rc = xmlTextWriterEndElement(writer); /* */ - if (rc < 0) - return rc; +/* Retrieve the TOTALBYTES value for the WIM file, or 0 if this value is + * unavailable. */ +u64 +xml_get_total_bytes(const struct wim_xml_info *info) +{ + return xml_get_number_by_path(info->root, "TOTALBYTES"); +} - return 0; +/* Retrieve the TOTALBYTES value for the specified image, or 0 if this value is + * unavailable. */ +u64 +xml_get_image_total_bytes(const struct wim_xml_info *info, int image) +{ + return xml_get_number_by_path(info->images[image - 1], "TOTALBYTES"); } +/* Retrieve the HARDLINKBYTES value for the specified image, or 0 if this value + * is unavailable. */ +u64 +xml_get_image_hard_link_bytes(const struct wim_xml_info *info, int image) +{ + return xml_get_number_by_path(info->images[image - 1], "HARDLINKBYTES"); +} +/* Retrieve the WIMBOOT value for the specified image, or false if this value is + * unavailable. */ +bool +xml_get_wimboot(const struct wim_xml_info *info, int image) +{ + return xml_get_number_by_path(info->images[image - 1], "WIMBOOT"); +} -/* Makes space for another image in the XML information and return a pointer to - * it.*/ -static struct image_info * -add_image_info_struct(struct wim_info *wim_info) +/* Retrieve the Windows build number for the specified image, or 0 if this + * information is not available. */ +u64 +xml_get_windows_build_number(const struct wim_xml_info *info, int image) { - struct image_info *images; - - images = CALLOC(wim_info->num_images + 1, sizeof(struct image_info)); - if (!images) - return NULL; - memcpy(images, wim_info->images, - wim_info->num_images * sizeof(struct image_info)); - FREE(wim_info->images); - wim_info->images = images; - wim_info->num_images++; - return &images[wim_info->num_images - 1]; + return xml_get_number_by_path(info->images[image - 1], + "WINDOWS/VERSION/BUILD"); } -static int -clone_windows_info(const struct windows_info *old, struct windows_info *new) +/* Set the WIMBOOT value for the specified image. */ +int +xml_set_wimboot(struct wim_xml_info *info, int image) { - int ret; - - new->arch = old->arch; + return xml_set_ttext_by_path(info->images[image - 1], "WIMBOOT", T("1")); +} - ret = dup_strings_from_specs(old, new, windows_info_xml_string_specs, - ARRAY_LEN(windows_info_xml_string_specs)); - if (ret) - return ret; +/* + * Calculate what to put in the FILECOUNT, DIRCOUNT, TOTALBYTES, and + * HARDLINKBYTES elements of the specified WIM image. + * + * Note: since these stats are likely to be used for display purposes only, we + * no longer attempt to duplicate WIMGAPI's weird bugs when calculating them. + */ +int +xml_update_image_info(WIMStruct *wim, int image) +{ + const struct wim_image_metadata *imd = wim->image_metadata[image - 1]; + xmlNode *image_node = wim->xml_info->images[image - 1]; + const struct wim_inode *inode; + u64 dir_count = 0; + u64 file_count = 0; + u64 total_bytes = 0; + u64 hard_link_bytes = 0; + u64 size; + xmlNode *filecount_node; + xmlNode *dircount_node; + xmlNode *totalbytes_node; + xmlNode *hardlinkbytes_node; + xmlNode *lastmodificationtime_node; - if (old->pkeyconfigversion) { - new->pkeyconfigversion = TSTRDUP(old->pkeyconfigversion); - if (new->pkeyconfigversion == NULL) - return WIMLIB_ERR_NOMEM; + image_for_each_inode(inode, imd) { + if (inode_is_directory(inode)) + dir_count += inode->i_nlink; + else + file_count += inode->i_nlink; + size = inode_sum_stream_sizes(inode, wim->blob_table); + total_bytes += size * inode->i_nlink; + hard_link_bytes += size * (inode->i_nlink - 1); } - if (old->languages) { - new->languages = CALLOC(old->num_languages, sizeof(new->languages[0])); - if (!new->languages) - return WIMLIB_ERR_NOMEM; - new->num_languages = old->num_languages; - for (size_t i = 0; i < new->num_languages; i++) { - if (!old->languages[i]) - continue; - new->languages[i] = TSTRDUP(old->languages[i]); - if (!new->languages[i]) - return WIMLIB_ERR_NOMEM; - } - } - if (old->default_language && - !(new->default_language = TSTRDUP(old->default_language))) - return WIMLIB_ERR_NOMEM; - if (old->system_root && !(new->system_root = TSTRDUP(old->system_root))) + dircount_node = new_element_with_u64(NULL, "DIRCOUNT", dir_count); + filecount_node = new_element_with_u64(NULL, "FILECOUNT", file_count); + totalbytes_node = new_element_with_u64(NULL, "TOTALBYTES", total_bytes); + hardlinkbytes_node = new_element_with_u64(NULL, "HARDLINKBYTES", + hard_link_bytes); + lastmodificationtime_node = + new_element_with_timestamp(NULL, "LASTMODIFICATIONTIME", + now_as_wim_timestamp()); + + if (unlikely(!dircount_node || !filecount_node || !totalbytes_node || + !hardlinkbytes_node || !lastmodificationtime_node)) { + xmlFreeNode(dircount_node); + xmlFreeNode(filecount_node); + xmlFreeNode(totalbytes_node); + xmlFreeNode(hardlinkbytes_node); + xmlFreeNode(lastmodificationtime_node); + WARNING("Failed to update image information!"); return WIMLIB_ERR_NOMEM; - if (old->windows_version_exists) { - new->windows_version_exists = true; - memcpy(&new->windows_version, &old->windows_version, - sizeof(old->windows_version)); } + + node_replace_child_element(image_node, dircount_node); + node_replace_child_element(image_node, filecount_node); + node_replace_child_element(image_node, totalbytes_node); + node_replace_child_element(image_node, hardlinkbytes_node); + node_replace_child_element(image_node, lastmodificationtime_node); return 0; } -static int -clone_image_info(const struct image_info *old, struct image_info *new) +/* Add an image to the XML information. */ +int +xml_add_image(struct wim_xml_info *info, const tchar *name) { + const u64 now = now_as_wim_timestamp(); + xmlNode *image_node; int ret; - new->dir_count = old->dir_count; - new->file_count = old->file_count; - new->total_bytes = old->total_bytes; - new->hard_link_bytes = old->hard_link_bytes; - new->creation_time = old->creation_time; - new->last_modification_time = old->last_modification_time; - - ret = dup_strings_from_specs(old, new, - image_info_xml_string_specs, - ARRAY_LEN(image_info_xml_string_specs)); - if (ret) - return ret; + ret = WIMLIB_ERR_NOMEM; + image_node = xmlNewNode(NULL, "IMAGE"); + if (!image_node) + goto err; - if (old->windows_info_exists) { - new->windows_info_exists = true; - ret = clone_windows_info(&old->windows_info, - &new->windows_info); + if (name && *name) { + ret = new_element_with_ttext(image_node, "NAME", name, NULL); if (ret) - return ret; + goto err; } - new->wimboot = old->wimboot; + ret = WIMLIB_ERR_NOMEM; + if (!new_element_with_u64(image_node, "DIRCOUNT", 0)) + goto err; + if (!new_element_with_u64(image_node, "FILECOUNT", 0)) + goto err; + if (!new_element_with_u64(image_node, "TOTALBYTES", 0)) + goto err; + if (!new_element_with_u64(image_node, "HARDLINKBYTES", 0)) + goto err; + if (!new_element_with_timestamp(image_node, "CREATIONTIME", now)) + goto err; + if (!new_element_with_timestamp(image_node, "LASTMODIFICATIONTIME", now)) + goto err; + ret = append_image_node(info, image_node); + if (ret) + goto err; return 0; + +err: + xmlFreeNode(image_node); + return ret; } -/* Copies the XML information for an image between WIM files. - * - * @dest_image_name and @dest_image_description are ignored if they are NULL; - * otherwise, they are used to override the image name and/or image description - * from the XML data in the source WIM file. +/* + * Make a copy of the XML information for the image with index @src_image in the + * @src_info XML document and append it to the @dest_info XML document. * - * On failure, WIMLIB_ERR_NOMEM is returned and no changes are made. Otherwise, - * 0 is returned and the WIM information at *new_wim_info_p is modified. + * In the process, the image's name and description will be changed to the + * values specified by @dest_image_name and @dest_image_description. Either or + * both may be NULL, which indicates that the corresponding element will not be + * included in the destination image. */ int -xml_export_image(const struct wim_info *old_wim_info, - int image, - struct wim_info **new_wim_info_p, - const tchar *dest_image_name, - const tchar *dest_image_description) -{ - struct wim_info *new_wim_info; - struct image_info *image_info; +xml_export_image(const struct wim_xml_info *src_info, int src_image, + struct wim_xml_info *dest_info, const tchar *dest_image_name, + const tchar *dest_image_description, bool wimboot) +{ + xmlNode *dest_node; int ret; - wimlib_assert(old_wim_info != NULL); - wimlib_assert(image >= 1 && image <= old_wim_info->num_images); + ret = WIMLIB_ERR_NOMEM; + dest_node = xmlDocCopyNode(src_info->images[src_image - 1], + dest_info->doc, 1); + if (!dest_node) + goto err; - if (*new_wim_info_p) { - new_wim_info = *new_wim_info_p; - } else { - new_wim_info = CALLOC(1, sizeof(struct wim_info)); - if (!new_wim_info) - goto err; - } + ret = xml_set_ttext_by_path(dest_node, "NAME", dest_image_name); + if (ret) + goto err; - image_info = add_image_info_struct(new_wim_info); - if (!image_info) + ret = xml_set_ttext_by_path(dest_node, "DESCRIPTION", + dest_image_description); + if (ret) goto err; - ret = clone_image_info(&old_wim_info->images[image - 1], image_info); - if (ret != 0) - goto err_destroy_image_info; + if (wimboot) { + ret = xml_set_ttext_by_path(dest_node, "WIMBOOT", T("1")); + if (ret) + goto err; + } - image_info->index = new_wim_info->num_images; + xmlFreeProp(unlink_index_attribute(dest_node)); + + return append_image_node(dest_info, dest_node); - if (dest_image_name) { - FREE(image_info->name); - image_info->name = TSTRDUP(dest_image_name); - if (!image_info->name) - goto err_destroy_image_info; - } - if (dest_image_description) { - FREE(image_info->description); - image_info->description = TSTRDUP(dest_image_description); - if (!image_info->description) - goto err_destroy_image_info; - } - *new_wim_info_p = new_wim_info; - return 0; -err_destroy_image_info: - destroy_image_info(image_info); err: - if (new_wim_info != *new_wim_info_p) - free_wim_info(new_wim_info); - return WIMLIB_ERR_NOMEM; + xmlFreeNode(dest_node); + return ret; } -/* Removes an image from the XML information. */ +/* Remove the specified image from the XML document. */ void -xml_delete_image(struct wim_info **wim_info_p, int image) +xml_delete_image(struct wim_xml_info *info, int image) { - struct wim_info *wim_info; - - wim_info = *wim_info_p; - wimlib_assert(image >= 1 && image <= wim_info->num_images); - - destroy_image_info(&wim_info->images[image - 1]); - - memmove(&wim_info->images[image - 1], - &wim_info->images[image], - (wim_info->num_images - image) * sizeof(struct image_info)); - - if (--wim_info->num_images == 0) { - free_wim_info(wim_info); - *wim_info_p = NULL; - } else { - for (int i = image - 1; i < wim_info->num_images; i++) - wim_info->images[i].index--; + xmlNode *next_image; + xmlAttr *index_attr, *next_index_attr; + + /* Free the IMAGE element for the deleted image. Then, shift all + * higher-indexed IMAGE elements down by 1, in the process re-assigning + * their INDEX attributes. */ + + next_image = info->images[image - 1]; + next_index_attr = unlink_index_attribute(next_image); + unlink_and_free_tree(next_image); + + while (image < info->image_count) { + index_attr = next_index_attr; + next_image = info->images[image]; + next_index_attr = unlink_index_attribute(next_image); + xmlAddChild(next_image, (xmlNode *)index_attr); + info->images[image - 1] = next_image; + image++; } -} -size_t -xml_get_max_image_name_len(const WIMStruct *wim) -{ - size_t max_len = 0; - for (u32 i = 0; i < wim->hdr.image_count; i++) - max_len = max(max_len, tstrlen(wim->wim_info->images[i].name)); - return max_len; + xmlFreeProp(next_index_attr); + info->image_count--; } -void -xml_set_memory_allocator(void *(*malloc_func)(size_t), - void (*free_func)(void *), - void *(*realloc_func)(void *, size_t)) +/* Architecture constants are from w64 mingw winnt.h */ +#define PROCESSOR_ARCHITECTURE_INTEL 0 +#define PROCESSOR_ARCHITECTURE_MIPS 1 +#define PROCESSOR_ARCHITECTURE_ALPHA 2 +#define PROCESSOR_ARCHITECTURE_PPC 3 +#define PROCESSOR_ARCHITECTURE_SHX 4 +#define PROCESSOR_ARCHITECTURE_ARM 5 +#define PROCESSOR_ARCHITECTURE_IA64 6 +#define PROCESSOR_ARCHITECTURE_ALPHA64 7 +#define PROCESSOR_ARCHITECTURE_MSIL 8 +#define PROCESSOR_ARCHITECTURE_AMD64 9 +#define PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 10 + +static const tchar * +describe_arch(u64 arch) { - xmlMemSetup(free_func, malloc_func, realloc_func, STRDUP); + static const tchar * const descriptions[] = { + [PROCESSOR_ARCHITECTURE_INTEL] = T("x86"), + [PROCESSOR_ARCHITECTURE_MIPS] = T("MIPS"), + [PROCESSOR_ARCHITECTURE_ARM] = T("ARM"), + [PROCESSOR_ARCHITECTURE_IA64] = T("ia64"), + [PROCESSOR_ARCHITECTURE_AMD64] = T("x86_64"), + }; + + if (arch < ARRAY_LEN(descriptions) && descriptions[arch] != NULL) + return descriptions[arch]; + + return T("unknown"); } -static u64 -inode_sum_stream_sizes(const struct wim_inode *inode, - const struct blob_table *blob_table) +/* Print information from the WINDOWS element, if present. */ +static void +print_windows_info(struct wim_xml_info *info, xmlNode *image_node) { - u64 total_size = 0; + xmlNode *windows_node; + xmlNode *langs_node; + xmlNode *version_node; + const tchar *text; - for (unsigned i = 0; i < inode->i_num_streams; i++) { - const struct blob_descriptor *blob; + windows_node = xml_get_node_by_path(image_node, "WINDOWS"); + if (!windows_node) + return; - blob = stream_blob(&inode->i_streams[i], blob_table); - if (blob) - total_size += blob->size; - } - return total_size; -} + tprintf(T("Architecture: %"TS"\n"), + describe_arch(xml_get_number_by_path(windows_node, "ARCH"))); -/* - * Calculate what to put in the , , , and - * elements of the specified WIM image. - * - * Note: since these stats are likely to be used for display purposes only, we - * no longer attempt to duplicate WIMGAPI's weird bugs when calculating them. - */ -void -xml_update_image_info(WIMStruct *wim, int image) -{ - struct image_info *info; - struct wim_image_metadata *imd; - struct wim_inode *inode; - u64 size; + text = xml_get_ttext_by_path(info, windows_node, "PRODUCTNAME"); + if (text) + tprintf(T("Product Name: %"TS"\n"), text); - info = &wim->wim_info->images[image - 1]; - imd = wim->image_metadata[image - 1]; + text = xml_get_ttext_by_path(info, windows_node, "EDITIONID"); + if (text) + tprintf(T("Edition ID: %"TS"\n"), text); - info->file_count = 0; - info->dir_count = 0; - info->total_bytes = 0; - info->hard_link_bytes = 0; + text = xml_get_ttext_by_path(info, windows_node, "INSTALLATIONTYPE"); + if (text) + tprintf(T("Installation Type: %"TS"\n"), text); - image_for_each_inode(inode, imd) { - if (inode_is_directory(inode)) - info->dir_count += inode->i_nlink; - else - info->file_count += inode->i_nlink; - size = inode_sum_stream_sizes(inode, wim->blob_table); - info->total_bytes += size * inode->i_nlink; - info->hard_link_bytes += size * (inode->i_nlink - 1); - } + text = xml_get_ttext_by_path(info, windows_node, "HAL"); + if (text) + tprintf(T("HAL: %"TS"\n"), text); - info->last_modification_time = now_as_wim_timestamp(); -} + text = xml_get_ttext_by_path(info, windows_node, "PRODUCTTYPE"); + if (text) + tprintf(T("Product Type: %"TS"\n"), text); -/* Adds an image to the XML information. */ -int -xml_add_image(WIMStruct *wim, const tchar *name) -{ - struct wim_info *wim_info; - struct image_info *image_info; + text = xml_get_ttext_by_path(info, windows_node, "PRODUCTSUITE"); + if (text) + tprintf(T("Product Suite: %"TS"\n"), text); - wimlib_assert(name != NULL); + langs_node = xml_get_node_by_path(windows_node, "LANGUAGES"); + if (langs_node) { + xmlNode *lang_node; - /* If this is the first image, allocate the struct wim_info. Otherwise - * use the existing struct wim_info. */ - if (wim->wim_info) { - wim_info = wim->wim_info; - } else { - wim_info = CALLOC(1, sizeof(struct wim_info)); - if (!wim_info) - return WIMLIB_ERR_NOMEM; - } + tprintf(T("Languages: ")); + node_for_each_child(langs_node, lang_node) { + if (node_is_element(lang_node, "LANGUAGE")) { + tfputs(node_get_ttext(info, lang_node), stdout); + tputchar(T(' ')); + } - image_info = add_image_info_struct(wim_info); - if (!image_info) - goto out_free_wim_info; + } + tputchar(T('\n')); - if (!(image_info->name = TSTRDUP(name))) - goto out_destroy_image_info; + text = xml_get_ttext_by_path(info, langs_node, "DEFAULT"); + if (text) + tprintf(T("Default Language: %"TS"\n"), text); + } - wim->wim_info = wim_info; - image_info->index = wim_info->num_images; - image_info->creation_time = now_as_wim_timestamp(); - xml_update_image_info(wim, image_info->index); - return 0; + text = xml_get_ttext_by_path(info, windows_node, "SYSTEMROOT"); + if (text) + tprintf(T("System Root: %"TS"\n"), text); -out_destroy_image_info: - destroy_image_info(image_info); - wim_info->num_images--; -out_free_wim_info: - if (wim_info != wim->wim_info) - FREE(wim_info); - return WIMLIB_ERR_NOMEM; + version_node = xml_get_node_by_path(windows_node, "VERSION"); + if (version_node) { + tprintf(T("Major Version: %"PRIu64"\n"), + xml_get_number_by_path(version_node, "MAJOR")); + tprintf(T("Minor Version: %"PRIu64"\n"), + xml_get_number_by_path(version_node, "MINOR")); + tprintf(T("Build: %"PRIu64"\n"), + xml_get_number_by_path(version_node, "BUILD")); + tprintf(T("Service Pack Build: %"PRIu64"\n"), + xml_get_number_by_path(version_node, "SPBUILD")); + tprintf(T("Service Pack Level: %"PRIu64"\n"), + xml_get_number_by_path(version_node, "SPLEVEL")); + } } -/* Prints information about the specified image from struct wim_info structure. - * */ +/* Prints information about the specified image. */ void -print_image_info(const struct wim_info *wim_info, int image) +xml_print_image_info(struct wim_xml_info *info, int image) { - const struct image_info *image_info; - const tchar *desc; - tchar buf[50]; + xmlNode * const image_node = info->images[image - 1]; + const tchar *text; + tchar timebuf[64]; - wimlib_assert(image >= 1 && image <= wim_info->num_images); + tprintf(T("Index: %d\n"), image); - image_info = &wim_info->images[image - 1]; + /* Always print the Name and Description, even if the corresponding XML + * elements are not present. */ + text = xml_get_ttext_by_path(info, image_node, "NAME"); + tprintf(T("Name: %"TS"\n"), text ? text : T("")); + text = xml_get_ttext_by_path(info, image_node, "DESCRIPTION"); + tprintf(T("Description: %"TS"\n"), text ? text : T("")); - tprintf(T("Index: %d\n"), image_info->index); - tprintf(T("Name: %"TS"\n"), image_info->name); + text = xml_get_ttext_by_path(info, image_node, "DISPLAYNAME"); + if (text) + tprintf(T("Display Name: %"TS"\n"), text); - /* Always print the Description: part even if there is no - * description. */ - if (image_info->description) - desc = image_info->description; - else - desc = T(""); - tprintf(T("Description: %"TS"\n"), desc); + text = xml_get_ttext_by_path(info, image_node, "DISPLAYDESCRIPTION"); + if (text) + tprintf(T("Display Description: %"TS"\n"), text); - if (image_info->display_name) { - tprintf(T("Display Name: %"TS"\n"), - image_info->display_name); - } + tprintf(T("Directory Count: %"PRIu64"\n"), + xml_get_number_by_path(image_node, "DIRCOUNT")); - if (image_info->display_description) { - tprintf(T("Display Description: %"TS"\n"), - image_info->display_description); - } + tprintf(T("File Count: %"PRIu64"\n"), + xml_get_number_by_path(image_node, "FILECOUNT")); + + tprintf(T("Total Bytes: %"PRIu64"\n"), + xml_get_number_by_path(image_node, "TOTALBYTES")); + + tprintf(T("Hard Link Bytes: %"PRIu64"\n"), + xml_get_number_by_path(image_node, "HARDLINKBYTES")); - tprintf(T("Directory Count: %"PRIu64"\n"), image_info->dir_count); - tprintf(T("File Count: %"PRIu64"\n"), image_info->file_count); - tprintf(T("Total Bytes: %"PRIu64"\n"), image_info->total_bytes); - tprintf(T("Hard Link Bytes: %"PRIu64"\n"), image_info->hard_link_bytes); + wim_timestamp_to_str(xml_get_timestamp_by_path(image_node, + "CREATIONTIME"), + timebuf, ARRAY_LEN(timebuf)); + tprintf(T("Creation Time: %"TS"\n"), timebuf); - wim_timestamp_to_str(image_info->creation_time, buf, sizeof(buf)); - tprintf(T("Creation Time: %"TS"\n"), buf); + wim_timestamp_to_str(xml_get_timestamp_by_path(image_node, + "LASTMODIFICATIONTIME"), + timebuf, ARRAY_LEN(timebuf)); + tprintf(T("Last Modification Time: %"TS"\n"), timebuf); + + print_windows_info(info, image_node); + + text = xml_get_ttext_by_path(info, image_node, "FLAGS"); + if (text) + tprintf(T("Flags: %"TS"\n"), text); - wim_timestamp_to_str(image_info->last_modification_time, buf, sizeof(buf)); - tprintf(T("Last Modification Time: %"TS"\n"), buf); - if (image_info->windows_info_exists) - print_windows_info(&image_info->windows_info); - if (image_info->flags) - tprintf(T("Flags: %"TS"\n"), image_info->flags); tprintf(T("WIMBoot compatible: %"TS"\n"), - image_info->wimboot ? T("yes") : T("no")); + xml_get_number_by_path(image_node, "WIMBOOT") ? + T("yes") : T("no")); + tputchar('\n'); } -void -libxml_global_init(void) +/*----------------------------------------------------------------------------* + * Reading and writing the XML data * + *----------------------------------------------------------------------------*/ + +static unsigned +image_node_get_index(const xmlNode *node) { - xmlInitParser(); - xmlInitCharEncodingHandlers(); + return node_get_number((const xmlNode *)xmlHasProp(node, "INDEX"), 10); } -void -libxml_global_cleanup(void) +/* Prepare the 'images' array from the XML document tree. */ +static int +setup_images(struct wim_xml_info *info, xmlNode *root) { - xmlCleanupParser(); - xmlCleanupCharEncodingHandlers(); + xmlNode *child; + unsigned index; + unsigned max_index = 0; + int ret; + + info->images = NULL; + info->image_count = 0; + + node_for_each_child(root, child) { + if (!node_is_element(child, "IMAGE")) + continue; + index = image_node_get_index(child); + if (unlikely(index < 1 || info->image_count >= MAX_IMAGES)) + goto err_indices; + max_index = max(max_index, index); + info->image_count++; + } + if (unlikely(max_index != info->image_count)) + goto err_indices; + ret = WIMLIB_ERR_NOMEM; + info->images = CALLOC(info->image_count, sizeof(info->images[0])); + if (unlikely(!info->images)) + goto err; + node_for_each_child(root, child) { + if (!node_is_element(child, "IMAGE")) + continue; + index = image_node_get_index(child); + if (unlikely(info->images[index - 1])) + goto err_indices; + info->images[index - 1] = child; + } + return 0; + +err_indices: + ERROR("The WIM file's XML document does not contain exactly one IMAGE " + "element per image!"); + ret = WIMLIB_ERR_XML; +err: + FREE(info->images); + return ret; } /* Reads the XML data from a WIM file. */ int read_wim_xml_data(WIMStruct *wim) { + struct wim_xml_info *info; void *buf; size_t bufsize; - u8 *xml_data; xmlDoc *doc; xmlNode *root; int ret; + /* Allocate the 'struct wim_xml_info'. */ + ret = WIMLIB_ERR_NOMEM; + info = alloc_wim_xml_info(); + if (!info) + goto err; + + /* Read the raw UTF-16LE bytes. */ ret = wimlib_get_xml_data(wim, &buf, &bufsize); if (ret) - goto out; - xml_data = buf; + goto err_free_info; - doc = xmlReadMemory((const char *)xml_data, bufsize, - NULL, "UTF-16LE", 0); + /* Parse the document with libxml2, creating the document tree. */ + doc = xmlReadMemory(buf, bufsize, NULL, "UTF-16LE", XML_PARSE_NONET); + FREE(buf); + buf = NULL; if (!doc) { - ERROR("Failed to parse XML data"); + ERROR("Unable to parse the WIM file's XML document!"); ret = WIMLIB_ERR_XML; - goto out_free_xml_data; + goto err_free_info; } + /* Verify the root element. */ root = xmlDocGetRootElement(doc); - if (!root || !node_is_element(root) || !node_name_is(root, "WIM")) { - ERROR("WIM XML data is invalid"); + if (!node_is_element(root, "WIM")) { + ERROR("The WIM file's XML document has an unexpected format!"); ret = WIMLIB_ERR_XML; - goto out_free_doc; + goto err_free_doc; + } + + /* Verify the WIM file is not encrypted. */ + if (xml_get_node_by_path(root, "ESD/ENCRYPTED")) { + ret = WIMLIB_ERR_WIM_IS_ENCRYPTED; + goto err_free_doc; } - ret = xml_read_wim_info(root, &wim->wim_info); -out_free_doc: + /* Validate the image elements and set up the images[] array. */ + ret = setup_images(info, root); + if (ret) + goto err_free_doc; + + /* Save the document and return. */ + info->doc = doc; + info->root = root; + wim->xml_info = info; + return 0; + +err_free_doc: xmlFreeDoc(doc); -out_free_xml_data: - FREE(xml_data); -out: +err_free_info: + FREE(info); +err: return ret; } -/* Prepares an in-memory buffer containing the UTF-16LE XML data for a WIM file. - * - * total_bytes is the number to write in , or - * WIM_TOTALBYTES_USE_EXISTING to use the existing value in memory, or - * WIM_TOTALBYTES_OMIT to omit entirely. - */ -static int -prepare_wim_xml_data(WIMStruct *wim, int image, u64 total_bytes, - u8 **xml_data_ret, size_t *xml_len_ret) +/* Swap the INDEX attributes of two IMAGE elements. */ +static void +swap_index_attributes(xmlNode *image_node_1, xmlNode *image_node_2) { - xmlCharEncodingHandler *encoding_handler; - xmlBuffer *buf; - xmlOutputBuffer *outbuf; - xmlTextWriter *writer; - int ret; - const xmlChar *content; - int len; - u8 *xml_data; - size_t xml_len; - - /* Open an xmlTextWriter that writes to an in-memory buffer using - * UTF-16LE encoding. */ - - encoding_handler = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF16LE); - if (!encoding_handler) { - ERROR("Failed to get XML character encoding handler for UTF-16LE"); - ret = WIMLIB_ERR_LIBXML_UTF16_HANDLER_NOT_AVAILABLE; - goto out; - } - - buf = xmlBufferCreate(); - if (!buf) { - ERROR("Failed to create xmlBuffer"); - ret = WIMLIB_ERR_NOMEM; - goto out; - } + xmlAttr *attr_1, *attr_2; - outbuf = xmlOutputBufferCreateBuffer(buf, encoding_handler); - if (!outbuf) { - ERROR("Failed to allocate xmlOutputBuffer"); - ret = WIMLIB_ERR_NOMEM; - goto out_buffer_free; + if (image_node_1 != image_node_2) { + attr_1 = unlink_index_attribute(image_node_1); + attr_2 = unlink_index_attribute(image_node_2); + xmlAddChild(image_node_1, (xmlNode *)attr_2); + xmlAddChild(image_node_2, (xmlNode *)attr_1); } +} - writer = xmlNewTextWriter(outbuf); - if (!writer) { - ERROR("Failed to allocate xmlTextWriter"); - ret = WIMLIB_ERR_NOMEM; - goto out_output_buffer_close; +static int +prepare_document_for_write(struct wim_xml_info *info, int image, u64 total_bytes, + xmlNode **orig_totalbytes_node_ret) +{ + xmlNode *totalbytes_node = NULL; + + /* Allocate the new TOTALBYTES element if needed. */ + if (total_bytes != WIM_TOTALBYTES_USE_EXISTING && + total_bytes != WIM_TOTALBYTES_OMIT) { + totalbytes_node = new_element_with_u64(NULL, "TOTALBYTES", + total_bytes); + if (!totalbytes_node) + return WIMLIB_ERR_NOMEM; } - /* Write the XML document. */ - - ret = xmlTextWriterStartElement(writer, "WIM"); - if (ret < 0) - goto out_write_error; - - /* The contents of the element in the XML data, under the - * element (not the element), is for non-split WIMs the - * size of the WIM file excluding the XML data and integrity table. - * For split WIMs, takes into account the entire WIM, not - * just the current part. */ - if (total_bytes != WIM_TOTALBYTES_OMIT) { - if (total_bytes == WIM_TOTALBYTES_USE_EXISTING) { - if (wim->wim_info) - total_bytes = wim->wim_info->total_bytes; - else - total_bytes = 0; - } - ret = xmlTextWriterWriteFormatElement(writer, "TOTALBYTES", - "%"PRIu64, total_bytes); - if (ret < 0) - goto out_write_error; + /* Adjust the IMAGE elements if needed. */ + if (image != WIMLIB_ALL_IMAGES) { + /* We're writing a single image only. Temporarily unlink all + * other IMAGE elements from the document. */ + for (int i = 0; i < info->image_count; i++) + if (i + 1 != image) + xmlUnlinkNode(info->images[i]); + + /* Temporarily set the INDEX attribute of the needed IMAGE + * element to 1. */ + swap_index_attributes(info->images[0], info->images[image - 1]); } - if (image == WIMLIB_ALL_IMAGES) { - for (int i = 0; i < wim->hdr.image_count; i++) { - ret = xml_write_image_info(writer, - &wim->wim_info->images[i], - i + 1); - if (ret < 0) - goto out_write_error; - if (ret > 0) - goto out_free_text_writer; - } - } else { - ret = xml_write_image_info(writer, - &wim->wim_info->images[image - 1], - 1); - if (ret < 0) - goto out_write_error; - if (ret > 0) - goto out_free_text_writer; + /* Adjust (add, change, or remove) the TOTALBYTES element if needed. */ + *orig_totalbytes_node_ret = NULL; + if (total_bytes != WIM_TOTALBYTES_USE_EXISTING) { + /* Unlink the previous TOTALBYTES element, if any. */ + *orig_totalbytes_node_ret = xml_get_node_by_path(info->root, + "TOTALBYTES"); + if (*orig_totalbytes_node_ret) + xmlUnlinkNode(*orig_totalbytes_node_ret); + + /* Link in the new TOTALBYTES element, if any. */ + if (totalbytes_node) + xmlAddChild(info->root, totalbytes_node); } + return 0; +} - ret = xmlTextWriterEndElement(writer); - if (ret < 0) - goto out_write_error; - - ret = xmlTextWriterEndDocument(writer); - if (ret < 0) - goto out_write_error; - - ret = xmlTextWriterFlush(writer); - if (ret < 0) - goto out_write_error; - - /* Retrieve the buffer into which the document was written. */ - - content = xmlBufferContent(buf); - len = xmlBufferLength(buf); - - /* Copy the data into a new buffer, and prefix it with the UTF-16LE BOM - * (byte order mark), which is required by MS's software to understand - * the data. */ - - xml_len = len + 2; - xml_data = MALLOC(xml_len); - if (!xml_data) { - ret = WIMLIB_ERR_NOMEM; - goto out_free_text_writer; +static void +restore_document_after_write(struct wim_xml_info *info, int image, + xmlNode *orig_totalbytes_node) +{ + /* Restore the IMAGE elements if needed. */ + if (image != WIMLIB_ALL_IMAGES) { + /* We wrote a single image only. Re-link all other IMAGE + * elements to the document. */ + for (int i = 0; i < info->image_count; i++) + if (i + 1 != image) + xmlAddChild(info->root, info->images[i]); + + /* Restore the original INDEX attributes. */ + swap_index_attributes(info->images[0], info->images[image - 1]); } - xml_data[0] = 0xff; - xml_data[1] = 0xfe; - memcpy(&xml_data[2], content, len); - - /* Clean up libxml objects and return success. */ - *xml_data_ret = xml_data; - *xml_len_ret = xml_len; - ret = 0; -out_free_text_writer: - /* xmlFreeTextWriter will free the attached xmlOutputBuffer. */ - xmlFreeTextWriter(writer); - goto out_buffer_free; -out_output_buffer_close: - xmlOutputBufferClose(outbuf); -out_buffer_free: - xmlBufferFree(buf); -out: - return ret; -out_write_error: - ERROR("Error writing XML data"); - ret = WIMLIB_ERR_WRITE; - goto out_free_text_writer; + /* Restore the original TOTALBYTES element if needed. */ + if (orig_totalbytes_node) + node_replace_child_element(info->root, orig_totalbytes_node); } -/* Writes the XML data to a WIM file. */ +/* + * Writes the XML data to a WIM file. + * + * 'image' specifies the image(s) to include in the XML data. Normally it is + * WIMLIB_ALL_IMAGES, but it can also be a 1-based image index. + * + * 'total_bytes' is the number to use in the top-level TOTALBYTES element, or + * WIM_TOTALBYTES_USE_EXISTING to use the existing value from the XML document + * (if any), or WIM_TOTALBYTES_OMIT to omit the TOTALBYTES element entirely. + */ int write_wim_xml_data(WIMStruct *wim, int image, u64 total_bytes, - struct wim_reshdr *out_reshdr, - int write_resource_flags) + struct wim_reshdr *out_reshdr, int write_resource_flags) { - int ret; - u8 *xml_data; - size_t xml_len; - - ret = prepare_wim_xml_data(wim, image, total_bytes, - &xml_data, &xml_len); + struct wim_xml_info *info = wim->xml_info; + long ret; + long ret2; + xmlBuffer *buffer; + xmlNode *orig_totalbytes_node; + xmlSaveCtxt *save_ctx; + + /* Make any needed temporary changes to the document. */ + ret = prepare_document_for_write(info, image, total_bytes, + &orig_totalbytes_node); if (ret) - return ret; + goto out; + + /* Create an in-memory buffer to hold the encoded document. */ + ret = WIMLIB_ERR_NOMEM; + buffer = xmlBufferCreate(); + if (!buffer) + goto out_restore_document; + + /* Encode the document in UTF-16LE, with a byte order mark, and with no + * XML declaration. Some other WIM software requires all of these + * characteristics. */ + ret = WIMLIB_ERR_NOMEM; + if (xmlBufferCat(buffer, "\xff\xfe")) + goto out_free_buffer; + save_ctx = xmlSaveToBuffer(buffer, "UTF-16LE", XML_SAVE_NO_DECL); + if (!save_ctx) + goto out_free_buffer; + ret = xmlSaveDoc(save_ctx, info->doc); + ret2 = xmlSaveClose(save_ctx); + if (ret < 0 || ret2 < 0) { + ERROR("Unable to serialize the WIM file's XML document!"); + ret = WIMLIB_ERR_NOMEM; + goto out_free_buffer; + } /* Write the XML data uncompressed. Although wimlib can handle - * compressed XML data, MS software cannot. */ - ret = write_wim_resource_from_buffer(xml_data, - xml_len, + * compressed XML data, some other WIM software cannot. */ + ret = write_wim_resource_from_buffer(xmlBufferContent(buffer), + xmlBufferLength(buffer), true, &wim->out_fd, WIMLIB_COMPRESSION_TYPE_NONE, @@ -1537,42 +1273,43 @@ write_wim_xml_data(WIMStruct *wim, int image, u64 total_bytes, out_reshdr, NULL, write_resource_flags); - FREE(xml_data); +out_free_buffer: + xmlBufferFree(buffer); +out_restore_document: + /* Revert any temporary changes we made to the document. */ + restore_document_after_write(info, image, orig_totalbytes_node); +out: return ret; } -/* API function documented in wimlib.h */ -WIMLIBAPI const tchar * -wimlib_get_image_name(const WIMStruct *wim, int image) +/*----------------------------------------------------------------------------* + * Global setup functions * + *----------------------------------------------------------------------------*/ + +void +xml_global_init(void) { - if (image < 1 || image > wim->hdr.image_count) - return NULL; - return wim->wim_info->images[image - 1].name; + xmlInitParser(); } -/* API function documented in wimlib.h */ -WIMLIBAPI const tchar * -wimlib_get_image_description(const WIMStruct *wim, int image) +void +xml_global_cleanup(void) { - if (image < 1 || image > wim->hdr.image_count) - return NULL; - return wim->wim_info->images[image - 1].description; + xmlCleanupParser(); } -/* API function documented in wimlib.h */ -WIMLIBAPI bool -wimlib_image_name_in_use(const WIMStruct *wim, const tchar *name) +void +xml_set_memory_allocator(void *(*malloc_func)(size_t), + void (*free_func)(void *), + void *(*realloc_func)(void *, size_t)) { - if (!name || !*name) - return false; - for (int i = 1; i <= wim->hdr.image_count; i++) - if (!tstrcmp(wim->wim_info->images[i - 1].name, name)) - return true; - return false; + xmlMemSetup(free_func, malloc_func, realloc_func, wimlib_strdup); } +/*----------------------------------------------------------------------------* + * Library API functions * + *----------------------------------------------------------------------------*/ -/* API function documented in wimlib.h */ WIMLIBAPI int wimlib_get_xml_data(WIMStruct *wim, void **buf_ret, size_t *bufsize_ret) { @@ -1609,75 +1346,57 @@ wimlib_extract_xml_data(WIMStruct *wim, FILE *fp) return ret; } -/* API function documented in wimlib.h */ -WIMLIBAPI int -wimlib_set_image_name(WIMStruct *wim, int image, const tchar *name) +WIMLIBAPI bool +wimlib_image_name_in_use(const WIMStruct *wim, const tchar *name) { - tchar *p; - int i; + const struct wim_xml_info *info = wim->xml_info; + const xmlChar *name_utf8; + bool found = false; - if (name == NULL) - name = T(""); - - if (image < 1 || image > wim->hdr.image_count) - return WIMLIB_ERR_INVALID_IMAGE; + /* Any number of images can have "no name". */ + if (!name || !*name) + return false; - if (*name) { - for (i = 1; i <= wim->hdr.image_count; i++) { - if (i == image) - continue; - if (!tstrcmp(wim->wim_info->images[i - 1].name, name)) - return WIMLIB_ERR_IMAGE_NAME_COLLISION; - } + /* Check for images that have the specified name. */ + if (tstr_get_utf8(name, &name_utf8)) + return false; + for (int i = 0; i < info->image_count && !found; i++) { + found = xmlStrEqual(name_utf8, xml_get_text_by_path( + info->images[i], "NAME")); } + tstr_put_utf8(name_utf8); + return found; +} - p = TSTRDUP(name); - if (!p) - return WIMLIB_ERR_NOMEM; - - FREE(wim->wim_info->images[image - 1].name); - wim->wim_info->images[image - 1].name = p; - return 0; +WIMLIBAPI const tchar * +wimlib_get_image_name(const WIMStruct *wim, int image) +{ + return get_image_property(wim, image, "NAME", T("")); } -static int -do_set_image_info_str(WIMStruct *wim, int image, const tchar *tstr, - size_t offset) +WIMLIBAPI const tchar * +wimlib_get_image_description(const WIMStruct *wim, int image) { - tchar *tstr_copy; - tchar **dest_tstr_p; + return get_image_property(wim, image, "DESCRIPTION", NULL); +} - if (image < 1 || image > wim->hdr.image_count) { - ERROR("%d is not a valid image", image); - return WIMLIB_ERR_INVALID_IMAGE; - } - if (tstr) { - tstr_copy = TSTRDUP(tstr); - if (!tstr_copy) - return WIMLIB_ERR_NOMEM; - } else { - tstr_copy = NULL; - } - dest_tstr_p = (tchar**)((void*)&wim->wim_info->images[image - 1] + offset); +WIMLIBAPI int +wimlib_set_image_name(WIMStruct *wim, int image, const tchar *name) +{ + if (wimlib_image_name_in_use(wim, name)) + return WIMLIB_ERR_IMAGE_NAME_COLLISION; - FREE(*dest_tstr_p); - *dest_tstr_p = tstr_copy; - return 0; + return set_image_property(wim, image, "NAME", name); } -/* API function documented in wimlib.h */ WIMLIBAPI int -wimlib_set_image_descripton(WIMStruct *wim, int image, - const tchar *description) +wimlib_set_image_descripton(WIMStruct *wim, int image, const tchar *description) { - return do_set_image_info_str(wim, image, description, - offsetof(struct image_info, description)); + return set_image_property(wim, image, "DESCRIPTION", description); } -/* API function documented in wimlib.h */ WIMLIBAPI int wimlib_set_image_flags(WIMStruct *wim, int image, const tchar *flags) { - return do_set_image_info_str(wim, image, flags, - offsetof(struct image_info, flags)); + return set_image_property(wim, image, "FLAGS", flags); } diff --git a/tools/windeps/Makefile b/tools/windeps/Makefile index 75c68411..e53cce1b 100644 --- a/tools/windeps/Makefile +++ b/tools/windeps/Makefile @@ -53,6 +53,7 @@ libxml_$(1):$(LIBXML_SRCDIR) CFLAGS=-Os \ --with-minimum \ --without-lzma \ + --with-tree \ --with-writer; \ $(MAKE) install; \ rm -f ../sysroot_$(1)/lib/libxml2.la; -- 2.43.0