4 * Deals with the XML information in WIM files. Uses the C library libxml2.
8 * Copyright (C) 2012 Eric Biggers
10 * This file is part of wimlib, a library for working with WIM files.
12 * wimlib is free software; you can redistribute it and/or modify it under the
13 * terms of the GNU General Public License as published by the Free
14 * Software Foundation; either version 3 of the License, or (at your option)
17 * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
18 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
19 * A PARTICULAR PURPOSE. See the GNU General Public License for more
22 * You should have received a copy of the GNU General Public License
23 * along with wimlib; if not, see http://www.gnu.org/licenses/.
26 #include "wimlib_internal.h"
29 #include "timestamp.h"
30 #include "lookup_table.h"
33 #include <libxml/parser.h>
34 #include <libxml/tree.h>
35 #include <libxml/xmlwriter.h>
37 /* Structures used to form an in-memory representation of the XML data (other
38 * than the raw parse tree from libxml). */
40 struct windows_version {
52 char *installation_type;
57 char *default_language;
60 bool windows_version_exists;
61 struct windows_version windows_version;
71 u64 last_modification_time;
72 bool windows_info_exists;
73 struct windows_info windows_info;
77 char *display_description;
80 struct lookup_table *lookup_table;
85 /* Returns a statically allocated string that is a string representation of the
86 * architecture number. */
87 static const char *get_arch(int arch)
97 /* XXX Are there other arch values? */
99 snprintf(buf, sizeof(buf), "%d (unknown)", arch);
105 /* Iterate through the children of an xmlNode. */
106 #define for_node_child(parent, child) \
107 for (child = parent->children; child != NULL; child = child->next)
109 /* Utility functions for xmlNodes */
110 static inline bool node_is_element(xmlNode *node)
112 return node->type == XML_ELEMENT_NODE;
115 static inline bool node_is_text(xmlNode *node)
117 return node->type == XML_TEXT_NODE;
120 static inline bool node_is_attribute(xmlNode *node)
122 return node->type == XML_ATTRIBUTE_NODE;
125 static inline bool node_name_is(xmlNode *node, const char *name)
127 /* For now, both upper case and lower case element names are accepted. */
128 return strcasecmp((const char *)node->name, name) == 0;
131 /* Finds the text node that is a child of an element node and returns its
132 * content converted to a 64-bit unsigned integer. Returns 0 if no text node is
134 static u64 node_get_u64(const xmlNode *u64_node)
137 for_node_child(u64_node, child)
138 if (node_is_text(child))
139 return strtoull((const char *)child->content, NULL, 10);
143 /* Like node_get_u64(), but expects a number in base 16. */
144 static u64 node_get_hex_u64(const xmlNode *u64_node)
147 for_node_child(u64_node, child)
148 if (node_is_text(child))
149 return strtoull(child->content, NULL, 16);
153 static int node_get_string(const xmlNode *string_node, char **str)
158 for_node_child(string_node, child) {
159 if (node_is_text(child) && child->content) {
160 p = STRDUP(child->content);
162 ERROR("Out of memory");
163 return WIMLIB_ERR_NOMEM;
172 /* Returns the timestamp from a time node. It has child elements <HIGHPART> and
173 * <LOWPART> that are then used to construct a 64-bit timestamp. */
174 static u64 node_get_timestamp(const xmlNode *time_node)
179 for_node_child(time_node, child) {
180 if (!node_is_element(child))
182 if (node_name_is(child, "HIGHPART"))
183 high_part = node_get_hex_u64(child);
184 else if (node_name_is(child, "LOWPART"))
185 low_part = node_get_hex_u64(child);
187 return (u64)low_part | ((u64)high_part << 32);
190 /* Used to sort an array of struct image_infos by their image indices. */
191 static int sort_by_index(const void *p1, const void *p2)
193 u64 index_1 = ((struct image_info*)p1)->index;
194 u64 index_2 = ((struct image_info*)p1)->index;
195 if (index_1 < index_2)
197 else if (index_1 > index_2)
204 /* Frees memory allocated inside a struct windows_info structure. */
205 static void destroy_windows_info(struct windows_info *windows_info)
209 FREE(windows_info->product_name);
210 FREE(windows_info->edition_id);
211 FREE(windows_info->installation_type);
212 FREE(windows_info->hal);
213 FREE(windows_info->product_type);
214 FREE(windows_info->product_suite);
215 for (i = 0; i < windows_info->num_languages; i++)
216 FREE(windows_info->languages[i]);
217 FREE(windows_info->languages);
218 FREE(windows_info->default_language);
219 FREE(windows_info->system_root);
222 /* Frees memory allocated inside a struct image_info structure. */
223 static void destroy_image_info(struct image_info *image_info)
225 FREE(image_info->name);
226 FREE(image_info->description);
227 FREE(image_info->flags);
228 FREE(image_info->display_name);
229 FREE(image_info->display_description);
230 destroy_windows_info(&image_info->windows_info);
231 memset(image_info, 0, sizeof(struct image_info));
234 void free_wim_info(struct wim_info *info)
239 for (i = 0; i < info->num_images; i++)
240 destroy_image_info(&info->images[i]);
247 /* Reads the information from a <VERSION> element inside the <WINDOWS> element.
249 static void xml_read_windows_version(const xmlNode *version_node,
250 struct windows_version* windows_version)
253 for_node_child(version_node, child) {
254 if (!node_is_element(child))
256 if (node_name_is(child, "MAJOR"))
257 windows_version->major = node_get_u64(child);
258 else if (node_name_is(child, "MINOR"))
259 windows_version->minor = node_get_u64(child);
260 else if (node_name_is(child, "BUILD"))
261 windows_version->build = node_get_u64(child);
262 else if (node_name_is(child, "SPBUILD"))
263 windows_version->sp_build = node_get_u64(child);
264 else if (node_name_is(child, "SPLEVEL"))
265 windows_version->sp_level = node_get_u64(child);
269 /* Reads the information from a <LANGUAGE> element inside a <WINDOWS> element.
271 static int xml_read_languages(const xmlNode *languages_node,
272 char ***languages_ret,
273 u64 *num_languages_ret,
274 char **default_language_ret)
283 for_node_child(languages_node, child)
284 if (node_is_element(child) && node_name_is(child, "LANGUAGE"))
287 languages = CALLOC(num_languages, sizeof(char*));
289 ERROR("Out of memory");
290 return WIMLIB_ERR_NOMEM;
293 *languages_ret = languages;
294 *num_languages_ret = num_languages;
298 for_node_child(languages_node, child) {
299 if (!node_is_element(child))
301 if (node_name_is(child, "LANGUAGE"))
302 ret = node_get_string(child, &languages[i++]);
303 else if (node_name_is(child, "DEFAULT"))
304 ret = node_get_string(child, default_language_ret);
311 /* Reads the information from a <WINDOWS> element inside an <IMAGE> element. */
312 static int xml_read_windows_info(const xmlNode *windows_node,
313 struct windows_info *windows_info)
318 for_node_child(windows_node, child) {
319 if (!node_is_element(child))
321 if (node_name_is(child, "ARCH")) {
322 windows_info->arch = node_get_u64(child);
323 } else if (node_name_is(child, "PRODUCTNAME")) {
324 ret = node_get_string(child,
325 &windows_info->product_name);
326 } else if (node_name_is(child, "EDITIONID")) {
327 ret = node_get_string(child,
328 &windows_info->edition_id);
329 } else if (node_name_is(child, "INSTALLATIONTYPE")) {
330 ret = node_get_string(child,
331 &windows_info->installation_type);
332 } else if (node_name_is(child, "PRODUCTTYPE")) {
333 ret = node_get_string(child,
334 &windows_info->product_type);
335 } else if (node_name_is(child, "PRODUCTSUITE")) {
336 ret = node_get_string(child,
337 &windows_info->product_suite);
338 } else if (node_name_is(child, "LANGUAGES")) {
339 ret = xml_read_languages(child,
340 &windows_info->languages,
341 &windows_info->num_languages,
342 &windows_info->default_language);
343 } else if (node_name_is(child, "VERSION")) {
344 xml_read_windows_version(child,
345 &windows_info->windows_version);
346 windows_info->windows_version_exists = true;
347 } else if (node_name_is(child, "SYSTEMROOT")) {
348 ret = node_get_string(child, &windows_info->system_root);
349 } else if (node_name_is(child, "HAL")) {
350 ret = node_get_string(child, &windows_info->hal);
359 /* Reads the information from an <IMAGE> element. */
360 static int xml_read_image_info(xmlNode *image_node,
361 struct image_info *image_info)
367 index_prop = xmlGetProp(image_node, "INDEX");
370 image_info->index = strtoul(index_prop, &tmp, 10);
373 image_info->index = 0;
377 for_node_child(image_node, child) {
378 if (!node_is_element(child))
380 if (node_name_is(child, "DIRCOUNT"))
381 image_info->dir_count = node_get_u64(child);
382 else if (node_name_is(child, "FILECOUNT"))
383 image_info->file_count = node_get_u64(child);
384 else if (node_name_is(child, "TOTALBYTES"))
385 image_info->total_bytes = node_get_u64(child);
386 else if (node_name_is(child, "HARDLINKBYTES"))
387 image_info->hard_link_bytes = node_get_u64(child);
388 else if (node_name_is(child, "CREATIONTIME"))
389 image_info->creation_time = node_get_timestamp(child);
390 else if (node_name_is(child, "LASTMODIFICATIONTIME"))
391 image_info->last_modification_time = node_get_timestamp(child);
392 else if (node_name_is(child, "WINDOWS")) {
393 DEBUG("Found <WINDOWS> tag");
394 ret = xml_read_windows_info(child,
395 &image_info->windows_info);
396 image_info->windows_info_exists = true;
397 } else if (node_name_is(child, "NAME")) {
398 ret = node_get_string(child, &image_info->name);
399 } else if (node_name_is(child, "DESCRIPTION")) {
400 ret = node_get_string(child, &image_info->description);
401 } else if (node_name_is(child, "FLAGS")) {
402 ret = node_get_string(child, &image_info->flags);
403 } else if (node_name_is(child, "DISPLAYNAME")) {
404 ret = node_get_string(child, &image_info->display_name);
405 } else if (node_name_is(child, "DISPLAYDESCRIPTION")) {
406 ret = node_get_string(child, &image_info->display_description);
411 if (!image_info->name) {
412 WARNING("Image with index %"PRIu64" has no name",
414 image_info->name = MALLOC(1);
415 if (!image_info->name) {
416 ERROR("Out of memory");
417 return WIMLIB_ERR_NOMEM;
419 image_info->name[0] = '\0';
424 /* Reads the information from a <WIM> element, which should be the root element
425 * of the XML tree. */
426 static int xml_read_wim_info(const xmlNode *wim_node,
427 struct wim_info **wim_info_ret)
429 struct wim_info *wim_info;
433 struct image_info *cur_image_info;
435 wim_info = CALLOC(1, sizeof(struct wim_info));
437 ERROR("Out of memory");
438 return WIMLIB_ERR_NOMEM;
441 /* Count how many images there are. */
443 for_node_child(wim_node, child)
444 if (node_is_element(child) && node_name_is(child, "IMAGE"))
450 /* Allocate the array of struct image_infos and fill them in. */
451 wim_info->images = CALLOC(num_images, sizeof(wim_info->images[0]));
452 if (!wim_info->images) {
453 ret = WIMLIB_ERR_NOMEM;
454 ERROR("Out of memory!");
457 wim_info->num_images = num_images;
458 cur_image_info = wim_info->images;
459 for_node_child(wim_node, child) {
460 if (!node_is_element(child))
462 if (node_name_is(child, "IMAGE")) {
463 DEBUG("Found <IMAGE> tag");
464 ret = xml_read_image_info(child, cur_image_info++);
467 } else if (node_name_is(child, "TOTALBYTES")) {
468 wim_info->total_bytes = node_get_u64(child);
472 /* Sort the array of struct image_infos by image index. */
473 qsort(wim_info->images, wim_info->num_images,
474 sizeof(struct image_info), sort_by_index);
476 *wim_info_ret = wim_info;
479 free_wim_info(wim_info);
483 /* Prints the information contained in a struct windows_info structure. */
484 static void print_windows_info(const struct windows_info *windows_info)
487 const struct windows_version *windows_version;
489 printf("Architecture: %s\n", get_arch(windows_info->arch));
491 if (windows_info->product_name)
492 printf("Product Name: %s\n",
493 windows_info->product_name);
495 if (windows_info->edition_id)
496 printf("Edition ID: %s\n",
497 windows_info->edition_id);
499 if (windows_info->installation_type)
500 printf("Installation Type: %s\n",
501 windows_info->installation_type);
503 if (windows_info->hal)
504 printf("HAL: %s\n", windows_info->hal);
506 if (windows_info->product_type)
507 printf("Product Type: %s\n",
508 windows_info->product_type);
510 if (windows_info->product_suite)
511 printf("Product Suite: %s\n",
512 windows_info->product_suite);
513 printf("Languages: ");
514 for (i = 0; i < windows_info->num_languages; i++) {
515 fputs(windows_info->languages[i], stdout);
519 if (windows_info->default_language)
520 printf("Default Language: %s\n",
521 windows_info->default_language);
522 if (windows_info->system_root)
523 printf("System Root: %s\n",
524 windows_info->system_root);
525 if (windows_info->windows_version_exists) {
526 windows_version = &windows_info->windows_version;
527 printf("Major Version: %"PRIu64"\n",
528 windows_version->major);
529 printf("Minor Version: %"PRIu64"\n",
530 windows_version->minor);
531 printf("Build: %"PRIu64"\n",
532 windows_version->build);
533 printf("Service Pack Build: %"PRIu64"\n",
534 windows_version->sp_build);
535 printf("Service Pack Level: %"PRIu64"\n",
536 windows_version->sp_level);
541 /* Writes the information contained in a struct windows_version structure to the XML
542 * document being constructed in memory. This is the <VERSION> element inside
543 * the <WINDOWS> element. */
544 static int xml_write_windows_version(xmlTextWriter *writer,
545 const struct windows_version *version)
548 rc = xmlTextWriterStartElement(writer, "VERSION");
552 rc = xmlTextWriterWriteFormatElement(writer, "MAJOR", "%"PRIu64,
557 rc = xmlTextWriterWriteFormatElement(writer, "MINOR", "%"PRIu64,
562 rc = xmlTextWriterWriteFormatElement(writer, "BUILD", "%"PRIu64,
567 rc = xmlTextWriterWriteFormatElement(writer, "SPBUILD", "%"PRIu64,
572 rc = xmlTextWriterWriteFormatElement(writer, "SPLEVEL", "%"PRIu64,
577 return xmlTextWriterEndElement(writer); /* </VERSION> */
580 /* Writes the information contained in a struct windows_info structure to the XML
581 * document being constructed in memory. This is the <WINDOWS> element. */
582 static int xml_write_windows_info(xmlTextWriter *writer,
583 const struct windows_info *windows_info)
586 rc = xmlTextWriterStartElement(writer, "WINDOWS");
591 rc = xmlTextWriterWriteFormatElement(writer, "ARCH", "%"PRIu64,
596 if (windows_info->product_name) {
597 rc = xmlTextWriterWriteElement(writer, "PRODUCTNAME",
598 windows_info->product_name);
603 if (windows_info->edition_id) {
604 rc = xmlTextWriterWriteElement(writer, "EDITIONID",
605 windows_info->edition_id);
610 if (windows_info->installation_type) {
611 rc = xmlTextWriterWriteElement(writer, "INSTALLATIONTYPE",
612 windows_info->installation_type);
617 if (windows_info->hal) {
618 rc = xmlTextWriterWriteElement(writer, "HAL",
624 if (windows_info->system_root) {
625 rc = xmlTextWriterWriteElement(writer, "SYSTEMROOT",
626 windows_info->system_root);
631 if (windows_info->product_type) {
632 rc = xmlTextWriterWriteElement(writer, "PRODUCTTYPE",
633 windows_info->product_type);
638 if (windows_info->product_suite) {
639 rc = xmlTextWriterWriteElement(writer, "PRODUCTSUITE",
640 windows_info->product_suite);
645 if (windows_info->num_languages) {
646 rc = xmlTextWriterStartElement(writer, "LANGUAGES");
650 for (int i = 0; i < windows_info->num_languages; i++) {
651 rc = xmlTextWriterWriteElement(writer, "LANGUAGE",
652 windows_info->languages[i]);
656 rc = xmlTextWriterWriteElement(writer, "DEFAULT",
657 windows_info->default_language);
661 rc = xmlTextWriterEndElement(writer); /* </LANGUAGES> */
666 if (windows_info->windows_version_exists) {
667 rc = xml_write_windows_version(writer, &windows_info->windows_version);
672 return xmlTextWriterEndElement(writer); /* </WINDOWS> */
675 /* Writes a time element to the XML document being constructed in memory. */
676 static int xml_write_time(xmlTextWriter *writer, const char *element_name,
680 rc = xmlTextWriterStartElement(writer, element_name);
684 rc = xmlTextWriterWriteFormatElement(writer, "HIGHPART",
685 "0x%"PRIX32, (u32)(time >> 32));
689 rc = xmlTextWriterWriteFormatElement(writer, "LOWPART",
690 "0x%"PRIX32, (u32)time);
694 rc = xmlTextWriterEndElement(writer); /* </@element_name> */
701 /* Writes an <IMAGE> element to the XML document. */
702 static int xml_write_image_info(xmlTextWriter *writer,
703 const struct image_info *image_info)
706 rc = xmlTextWriterStartElement(writer, "IMAGE");
710 rc = xmlTextWriterWriteFormatAttribute(writer, "INDEX", "%"PRIu64,
715 rc = xmlTextWriterWriteFormatElement(writer, "DIRCOUNT", "%"PRIu64,
716 image_info->dir_count);
720 rc = xmlTextWriterWriteFormatElement(writer, "FILECOUNT", "%"PRIu64,
721 image_info->file_count);
725 rc = xmlTextWriterWriteFormatElement(writer, "TOTALBYTES", "%"PRIu64,
726 image_info->total_bytes);
730 rc = xmlTextWriterWriteFormatElement(writer, "HARDLINKBYTES", "%"PRIu64,
731 image_info->hard_link_bytes);
735 rc = xml_write_time(writer, "CREATIONTIME",
736 image_info->creation_time);
740 rc = xml_write_time(writer, "LASTMODIFICATIONTIME",
741 image_info->last_modification_time);
745 if (image_info->windows_info_exists) {
746 rc = xml_write_windows_info(writer, &image_info->windows_info);
750 DEBUG("<WINDOWS> tag does not exist.");
753 if (image_info->name) {
754 rc = xmlTextWriterWriteElement(writer, "NAME", image_info->name);
758 if (image_info->description) {
759 rc = xmlTextWriterWriteElement(writer, "DESCRIPTION",
760 image_info->description);
764 if (image_info->display_name) {
765 rc = xmlTextWriterWriteElement(writer, "DISPLAYNAME",
766 image_info->display_name);
770 if (image_info->display_description) {
771 rc = xmlTextWriterWriteElement(writer, "DISPLAYDESCRIPTION",
772 image_info->display_description);
777 if (image_info->flags) {
778 rc = xmlTextWriterWriteElement(writer, "FLAGS",
784 return xmlTextWriterEndElement(writer); /* </IMAGE> */
789 /* Makes space for another image in the XML information and return a pointer to
791 static struct image_info *add_image_info_struct(struct wim_info *wim_info)
793 struct image_info *images;
795 images = CALLOC(wim_info->num_images + 1, sizeof(struct image_info));
798 memcpy(images, wim_info->images,
799 wim_info->num_images * sizeof(struct image_info));
800 FREE(wim_info->images);
801 wim_info->images = images;
802 wim_info->num_images++;
803 return &images[wim_info->num_images - 1];
806 static int clone_windows_info(const struct windows_info *old,
807 struct windows_info *new)
811 if (old->product_name && !(new->product_name = STRDUP(old->product_name)))
812 return WIMLIB_ERR_NOMEM;
813 if (old->edition_id && !(new->edition_id = STRDUP(old->edition_id)))
814 return WIMLIB_ERR_NOMEM;
815 if (old->installation_type && !(new->installation_type =
816 STRDUP(old->installation_type)))
817 return WIMLIB_ERR_NOMEM;
818 if (old->hal && !(new->hal = STRDUP(old->hal)))
819 return WIMLIB_ERR_NOMEM;
820 if (old->product_type && !(new->product_type = STRDUP(old->product_type)))
821 return WIMLIB_ERR_NOMEM;
822 if (old->product_suite && !(new->product_suite = STRDUP(old->product_suite)))
823 return WIMLIB_ERR_NOMEM;
825 if (old->languages) {
826 new->languages = CALLOC(old->num_languages, sizeof(char*));
828 return WIMLIB_ERR_NOMEM;
829 new->num_languages = old->num_languages;
830 for (i = 0; i < new->num_languages; i++) {
831 if (!old->languages[i])
833 new->languages[i] = STRDUP(old->languages[i]);
834 if (!new->languages[i])
835 return WIMLIB_ERR_NOMEM;
838 if (old->default_language &&
839 !(new->default_language = STRDUP(old->default_language)))
840 return WIMLIB_ERR_NOMEM;
841 if (old->system_root && !(new->system_root = STRDUP(old->system_root)))
842 return WIMLIB_ERR_NOMEM;
843 if (old->windows_version_exists) {
844 new->windows_version_exists = true;
845 memcpy(&new->windows_version, &old->windows_version,
846 sizeof(old->windows_version));
851 static int clone_image_info(const struct image_info *old, struct image_info *new)
853 new->dir_count = old->dir_count;
854 new->file_count = old->file_count;
855 new->total_bytes = old->total_bytes;
856 new->hard_link_bytes = old->hard_link_bytes;
857 new->creation_time = old->creation_time;
858 new->last_modification_time = old->last_modification_time;
860 if (!(new->name = STRDUP(old->name)))
861 return WIMLIB_ERR_NOMEM;
863 if (old->description)
864 if (!(new->description = STRDUP(old->description)))
865 return WIMLIB_ERR_NOMEM;
867 if (old->display_name)
868 if (!(new->display_name = STRDUP(old->display_name)))
869 return WIMLIB_ERR_NOMEM;
871 if (old->display_description)
872 if (!(new->display_description = STRDUP(old->display_description)))
873 return WIMLIB_ERR_NOMEM;
876 if (!(new->flags = STRDUP(old->flags)))
877 return WIMLIB_ERR_NOMEM;
879 if (old->windows_info_exists) {
880 new->windows_info_exists = true;
881 return clone_windows_info(&old->windows_info,
887 /* Copies the XML information for an image between WIM files.
889 * @dest_image_name and @dest_image_description are ignored if they are NULL;
890 * otherwise, they are used to override the image name and/or image description
891 * from the XML data in the source WIM file.
893 * On failure, WIMLIB_ERR_NOMEM is returned and no changes are made. Otherwise,
894 * 0 is returned and the WIM information at *new_wim_info_p is modified.
896 int xml_export_image(const struct wim_info *old_wim_info,
898 struct wim_info **new_wim_info_p,
899 const char *dest_image_name,
900 const char *dest_image_description)
902 struct wim_info *new_wim_info;
903 struct image_info *image_info;
906 DEBUG("Copying XML data between WIM files for source image %d.", image);
908 wimlib_assert(image >= 1 && image <= old_wim_info->num_images);
910 if (*new_wim_info_p) {
911 new_wim_info = *new_wim_info_p;
913 new_wim_info = CALLOC(1, sizeof(struct wim_info));
918 image_info = add_image_info_struct(new_wim_info);
922 ret = clone_image_info(&old_wim_info->images[image - 1], image_info);
926 image_info->index = new_wim_info->num_images;
928 if (dest_image_name) {
929 FREE(image_info->name);
930 image_info->name = STRDUP(dest_image_name);
931 if (!image_info->name)
934 if (dest_image_description) {
935 FREE(image_info->description);
936 image_info->description = STRDUP(dest_image_description);
937 if (!image_info->description)
940 *new_wim_info_p = new_wim_info;
943 ERROR("Out of memory");
944 free_wim_info(new_wim_info);
945 return WIMLIB_ERR_NOMEM;
948 /* Removes an image from the XML information. */
949 void xml_delete_image(struct wim_info **wim_info_p, int image)
951 struct wim_info *wim_info;
954 DEBUG("Deleting image %d from the XML data.", image);
956 wim_info = *wim_info_p;
958 wimlib_assert(wim_info);
959 wimlib_assert(image >= 1 && image <= wim_info->num_images);
961 destroy_image_info(&wim_info->images[image - 1]);
963 for (i = image - 1; i < wim_info->num_images - 1; i++) {
964 memcpy(&wim_info->images[i], &wim_info->images[i + 1],
965 sizeof(struct image_info));
966 wim_info->images[i].index--;
969 if (--wim_info->num_images == 0) {
970 free_wim_info(wim_info);
975 size_t xml_get_max_image_name_len(const WIMStruct *w)
979 uint num_images = w->wim_info->num_images;
980 for (i = 0; i < num_images; i++)
981 len = max(len, strlen(w->wim_info->images[i].name));
985 #ifdef ENABLE_CUSTOM_MEMORY_ALLOCATOR
986 void xml_set_memory_allocator(void *(*malloc_func)(size_t),
987 void (*free_func)(void *),
988 void *(*realloc_func)(void *, size_t))
990 xmlMemSetup(free_func, malloc_func, realloc_func, STRDUP);
994 static int calculate_dentry_statistics(struct dentry *dentry, void *arg)
996 struct image_info *info = arg;
997 struct lookup_table *lookup_table = info->lookup_table;
998 const struct inode *inode = dentry->d_inode;
999 struct lookup_table_entry *lte;
1001 /* Update directory count and file count.
1003 * Each dentry counts as either a file or a directory, but not both.
1004 * The root directory is an exception: it is not counted at all.
1006 * Symbolic links and junction points (and presumably other reparse
1007 * points) count as regular files. This is despite the fact that
1008 * junction points have FILE_ATTRIBUTE_DIRECTORY set.
1010 if (dentry_is_root(dentry))
1013 if (inode_is_directory(inode))
1019 * Update total bytes and hard link bytes.
1021 * Unfortunately there are some inconsistencies/bugs in the way this is
1024 * If there are no alternate data streams in the image, the "total
1025 * bytes" is the sum of the size of the un-named data stream of each
1026 * inode times the link count of that inode. In other words, it would
1027 * be the total number of bytes of regular files you would have if you
1028 * extracted the full image without any hard-links. The "hard link
1029 * bytes" is equal to the "total bytes" minus the size of the un-named
1030 * data stream of each inode. In other words, the "hard link bytes"
1031 * counts the size of the un-named data stream for all the links to each
1032 * inode except the first one.
1034 * Reparse points and directories don't seem to be counted in either the
1035 * total bytes or the hard link bytes.
1037 * And now we get to the most confusing part, the alternate data
1038 * streams. They are not counted in the "total bytes". However, if the
1039 * link count of an inode with alternate data streams is 2 or greater,
1040 * the size of all the alternate data streams is included in the "hard
1041 * link bytes", and this size is multiplied by the link count (NOT one
1042 * less than the link count).
1044 lte = inode_unnamed_lte(inode, info->lookup_table);
1046 info->total_bytes += wim_resource_size(lte);
1047 if (!dentry_is_first_in_inode(dentry))
1048 info->hard_link_bytes += wim_resource_size(lte);
1051 if (inode->link_count >= 2 && dentry_is_first_in_inode(dentry)) {
1052 for (unsigned i = 0; i < inode->num_ads; i++) {
1053 if (inode->ads_entries[i].stream_name_len) {
1054 lte = inode_stream_lte(inode, i + 1, lookup_table);
1056 info->hard_link_bytes += inode->link_count *
1057 wim_resource_size(lte);
1065 void xml_update_image_info(WIMStruct *w, int image)
1067 struct image_info *image_info;
1070 DEBUG("Updating the image info for image %d", image);
1072 image_info = &w->wim_info->images[image - 1];
1074 image_info->file_count = 0;
1075 image_info->dir_count = 0;
1076 image_info->total_bytes = 0;
1077 image_info->hard_link_bytes = 0;
1079 flags_save = image_info->flags;
1080 image_info->lookup_table = w->lookup_table;
1082 for_dentry_in_tree(w->image_metadata[image - 1].root_dentry,
1083 calculate_dentry_statistics,
1086 image_info->lookup_table = NULL;
1087 image_info->flags = flags_save;
1088 image_info->last_modification_time = get_wim_timestamp();
1091 /* Adds an image to the XML information. */
1092 int xml_add_image(WIMStruct *w, const char *name)
1094 struct wim_info *wim_info;
1095 struct image_info *image_info;
1097 wimlib_assert(name);
1099 DEBUG("Adding image: name = %s", name);
1101 /* If this is the first image, allocate the struct wim_info. Otherwise
1102 * use the existing struct wim_info. */
1104 wim_info = w->wim_info;
1106 DEBUG("Allocing struct wim_info with 1 image");
1107 wim_info = CALLOC(1, sizeof(struct wim_info));
1109 ERROR("Could not allocate WIM information struct--- "
1111 return WIMLIB_ERR_NOMEM;
1115 image_info = add_image_info_struct(wim_info);
1117 goto out_free_wim_info;
1119 if (!(image_info->name = STRDUP(name)))
1120 goto out_destroy_image_info;
1122 w->wim_info = wim_info;
1123 image_info->index = wim_info->num_images;
1124 image_info->creation_time = get_wim_timestamp();
1125 xml_update_image_info(w, image_info->index);
1128 out_destroy_image_info:
1129 destroy_image_info(image_info);
1130 wim_info->num_images--;
1132 if (wim_info != w->wim_info)
1134 ERROR("Out of memory");
1135 return WIMLIB_ERR_NOMEM;
1138 /* Prints information about the specified image from struct wim_info structure.
1140 void print_image_info(const struct wim_info *wim_info, int image)
1142 const struct image_info *image_info;
1147 wimlib_assert(image >= 1 && image <= wim_info->num_images);
1149 image_info = &wim_info->images[image - 1];
1151 printf("Index: %"PRIu64"\n", image_info->index);
1152 printf("Name: %s\n", image_info->name);
1154 /* Always print the Description: part even if there is no
1156 if (image_info->description)
1157 desc = image_info->description;
1160 printf("Description: %s\n", desc);
1162 if (image_info->display_name)
1163 printf("Display Name: %s\n",
1164 image_info->display_name);
1166 if (image_info->display_description)
1167 printf("Display Description: %s\n",
1168 image_info->display_description);
1170 printf("Directory Count: %"PRIu64"\n", image_info->dir_count);
1171 printf("File Count: %"PRIu64"\n", image_info->file_count);
1172 printf("Total Bytes: %"PRIu64"\n", image_info->total_bytes);
1173 printf("Hard Link Bytes: %"PRIu64"\n", image_info->hard_link_bytes);
1175 time = wim_timestamp_to_unix(image_info->creation_time);
1176 p = asctime(gmtime(&time));
1177 *(strrchr(p, '\n')) = '\0';
1179 printf("Creation Time: %s UTC\n", p);
1181 time = wim_timestamp_to_unix(image_info->last_modification_time);
1182 p = asctime(gmtime(&time));
1183 *(strrchr(p, '\n')) = '\0';
1185 printf("Last Modification Time: %s UTC\n", p);
1186 if (image_info->windows_info_exists)
1187 print_windows_info(&image_info->windows_info);
1188 if (image_info->flags)
1189 printf("Flags: %s\n", image_info->flags);
1194 * Reads the XML data from a WIM file.
1196 int read_xml_data(FILE *fp, const struct resource_entry *res,
1197 u8 **xml_data_ret, struct wim_info **info_ret)
1204 DEBUG("XML data is %"PRIu64" bytes at offset %"PRIu64"",
1205 (u64)res->size, res->offset);
1207 if (resource_is_compressed(res)) {
1208 ERROR("XML data is supposed to be uncompressed");
1209 ret = WIMLIB_ERR_XML;
1210 goto out_cleanup_parser;
1212 if (res->size < 2) {
1213 ERROR("XML data must be at least 2 bytes");
1214 ret = WIMLIB_ERR_XML;
1215 goto out_cleanup_parser;
1218 xml_data = MALLOC(res->size + 2);
1220 ret = WIMLIB_ERR_NOMEM;
1221 goto out_cleanup_parser;
1223 ret = read_uncompressed_resource(fp, res->offset, res->size, xml_data);
1225 goto out_free_xml_data;
1227 xml_data[res->size] = 0;
1228 xml_data[res->size + 1] = 0;
1230 DEBUG("Parsing XML using libxml2 to create XML tree.");
1232 doc = xmlReadMemory(xml_data, res->size, "noname.xml", "UTF-16", 0);
1236 ERROR("Failed to parse XML data");
1237 ret = WIMLIB_ERR_XML;
1238 goto out_free_xml_data;
1241 DEBUG("Constructing WIM information structure from XML tree.");
1243 root = xmlDocGetRootElement(doc);
1245 ERROR("Empty XML document");
1246 ret = WIMLIB_ERR_XML;
1250 if (!node_is_element(root) || !node_name_is(root, "WIM")) {
1251 ERROR("Expected <WIM> for the root XML element (found <%s>)",
1253 ret = WIMLIB_ERR_XML;
1257 ret = xml_read_wim_info(root, info_ret);
1261 DEBUG("Freeing XML tree.");
1265 *xml_data_ret = xml_data;
1276 #define CHECK_RET ({ if (ret < 0) { \
1277 ERROR("Error writing XML data"); \
1278 ret = WIMLIB_ERR_WRITE; \
1279 goto out_free_text_writer; \
1283 * Writes XML data to a WIM file.
1285 * If @total_bytes is non-zero, it specifies what to write to the TOTALBYTES
1286 * element in the XML data. If zero, TOTALBYTES is given the default value of
1287 * the offset of the XML data.
1289 int write_xml_data(const struct wim_info *wim_info, int image, FILE *out,
1293 xmlTextWriter *writer;
1296 const xmlChar *content;
1299 size_t bytes_written;
1301 wimlib_assert(image == WIM_ALL_IMAGES ||
1302 (wim_info != NULL && image >= 1 &&
1303 image <= wim_info->num_images));
1305 /* The contents of the <TOTALBYTES> element in the XML data, under the
1306 * <WIM> element not the <IMAGE> element, is (for non-spit WIMs) the
1307 * size of the WIM file excluding the XML data and integrity table,
1308 * which is the current offset, since the XML data goes at the end of
1309 * the WIM file before the integrity table. */
1310 if (total_bytes == 0) {
1311 total_bytes = ftello(out);
1312 if (total_bytes == (u64)-1)
1313 return WIMLIB_ERR_WRITE;
1316 DEBUG("Creating XML buffer and text writer.");
1317 buf = xmlBufferCreate();
1319 ERROR("Failed to allocate XML buffer");
1320 ret = WIMLIB_ERR_NOMEM;
1323 writer = xmlNewTextWriterMemory(buf, 0);
1325 ERROR("Failed to allocate XML writer");
1326 ret = WIMLIB_ERR_NOMEM;
1327 goto out_buffer_free;
1331 /* M$'s WIM files do not have XML declarations, so do not write one.
1332 * I'm not sure how we can force the document to be written in UTF-16
1333 * without calling xmlTextWriterStartDocument(), though, so currently it
1334 * is composed in a buffer UTF-8, then converted to UTF-16. */
1336 ret = xmlTextWriterStartDocument(writer, NULL, "UTF-16", NULL);
1340 DEBUG("Writing <WIM> element");
1341 ret = xmlTextWriterStartElement(writer, "WIM");
1344 ret = xmlTextWriterWriteFormatElement(writer, "TOTALBYTES", "%"PRIu64,
1348 if (wim_info != NULL) {
1349 DEBUG("Writing %d <IMAGE> elements", (int)wim_info->num_images);
1350 for (int i = 1; i <= (int)wim_info->num_images; i++) {
1351 if (image != WIM_ALL_IMAGES && i != image)
1353 DEBUG("Writing <IMAGE> element for image %d", i);
1354 ret = xml_write_image_info(writer, &wim_info->images[i - 1]);
1359 ret = xmlTextWriterEndElement(writer);
1362 ret = xmlTextWriterEndDocument(writer);
1365 xmlFreeTextWriter(writer);
1367 DEBUG("Done composing XML document. Now converting to UTF-16 and "
1368 "writing it to the output file.");
1370 content = xmlBufferContent(buf);
1371 len = xmlBufferLength(buf);
1373 DEBUG("XML UTF-8 length = %zu", len);
1375 utf16_str = utf8_to_utf16(content, len, &utf16_len);
1377 ret = WIMLIB_ERR_NOMEM;
1378 goto out_free_text_writer;
1381 DEBUG("XML UTF-16 length = %zu", utf16_len);
1383 if ((putc(0xff, out)) == EOF || (putc(0xfe, out) == EOF) ||
1384 ((bytes_written = fwrite(utf16_str, 1, utf16_len, out))
1386 ERROR_WITH_ERRNO("Error writing XML data");
1387 ret = WIMLIB_ERR_WRITE;
1388 goto out_free_utf16_str;
1391 DEBUG("Cleaning up.");
1396 out_free_text_writer:
1397 xmlFreeTextWriter(writer);
1404 /* Returns the name of the specified image. */
1405 WIMLIBAPI const char *wimlib_get_image_name(const WIMStruct *w, int image)
1407 DEBUG("Getting the name of image %d", image);
1408 if (image < 1 || image > w->hdr.image_count)
1411 return w->wim_info->images[image - 1].name;
1414 /* Returns the description of the specified image. */
1415 WIMLIBAPI const char *wimlib_get_image_description(const WIMStruct *w,
1418 DEBUG("Getting the description of image %d", image);
1419 if (image < 1 || image > w->hdr.image_count)
1422 return w->wim_info->images[image - 1].description;
1425 /* Determines if an image name is already used by some image in the WIM. */
1426 WIMLIBAPI bool wimlib_image_name_in_use(const WIMStruct *w, const char *name)
1430 DEBUG("Checking to see if the image name `%s' is already in use", name);
1431 if (!name || !w->wim_info)
1433 for (i = 1; i <= w->wim_info->num_images; i++)
1434 if (strcmp(w->wim_info->images[i - 1].name, name) == 0)
1440 WIMLIBAPI int wimlib_extract_xml_data(WIMStruct *w, FILE *fp)
1442 DEBUG("Extracting the XML data.");
1443 if (fwrite(w->xml_data, 1, w->hdr.xml_res_entry.size, fp) !=
1444 w->hdr.xml_res_entry.size) {
1445 ERROR_WITH_ERRNO("Failed to extract XML data");
1446 return WIMLIB_ERR_WRITE;
1451 /* Sets the name of an image in the WIM. */
1452 WIMLIBAPI int wimlib_set_image_name(WIMStruct *w, int image, const char *name)
1457 DEBUG("Setting the name of image %d to %s", image, name);
1460 return WIMLIB_ERR_INVALID_PARAM;
1462 if (!name || !*name) {
1463 ERROR("Must specify a non-empty string for the image name");
1464 return WIMLIB_ERR_INVALID_PARAM;
1466 if (image < 1 || image > w->hdr.image_count) {
1467 ERROR("%d is not a valid image", image);
1468 return WIMLIB_ERR_INVALID_IMAGE;
1471 for (i = 1; i <= w->hdr.image_count; i++) {
1474 if (strcmp(w->wim_info->images[i - 1].name, name) == 0) {
1475 ERROR("The name `%s' is already used for image %d",
1477 return WIMLIB_ERR_IMAGE_NAME_COLLISION;
1483 ERROR("Out of memory");
1484 return WIMLIB_ERR_NOMEM;
1486 FREE(w->wim_info->images[image - 1].name);
1487 w->wim_info->images[image - 1].name = p;
1491 /* Sets the description of an image in the WIM. */
1492 WIMLIBAPI int wimlib_set_image_descripton(WIMStruct *w, int image,
1493 const char *description)
1497 DEBUG("Setting the description of image %d to %s", image, description);
1500 return WIMLIB_ERR_INVALID_PARAM;
1502 if (image < 1 || image > w->hdr.image_count) {
1503 ERROR("%d is not a valid image", image);
1504 return WIMLIB_ERR_INVALID_IMAGE;
1507 p = STRDUP(description);
1509 ERROR("Out of memory");
1510 return WIMLIB_ERR_NOMEM;
1515 FREE(w->wim_info->images[image - 1].description);
1516 w->wim_info->images[image - 1].description = p;
1520 WIMLIBAPI int wimlib_set_image_flags(WIMStruct *w, int image,
1525 DEBUG("Setting the flags of image %d to %s", image, flags);
1527 if (image < 1 || image > w->hdr.image_count) {
1528 ERROR("%d is not a valid image", image);
1529 return WIMLIB_ERR_INVALID_IMAGE;
1534 ERROR("Out of memory");
1535 return WIMLIB_ERR_NOMEM;
1540 FREE(w->wim_info->images[image - 1].flags);
1541 w->wim_info->images[image - 1].flags = p;