Refactor headers
[wimlib] / src / xml.c
1 /*
2  * xml.c
3  *
4  * Deals with the XML information in WIM files.  Uses the C library libxml2.
5  */
6
7 /*
8  * Copyright (C) 2012, 2013 Eric Biggers
9  *
10  * This file is part of wimlib, a library for working with WIM files.
11  *
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)
15  * any later version.
16  *
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
20  * details.
21  *
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/.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #  include "config.h"
28 #endif
29
30 #include "wimlib/dentry.h"
31 #include "wimlib/encoding.h"
32 #include "wimlib/metadata.h"
33 #include "wimlib/error.h"
34 #include "wimlib/file_io.h"
35 #include "wimlib/lookup_table.h"
36 #include "wimlib/resource.h"
37 #include "wimlib/timestamp.h"
38 #include "wimlib/xml.h"
39
40 #include <libxml/encoding.h>
41 #include <libxml/parser.h>
42 #include <libxml/tree.h>
43 #include <libxml/xmlwriter.h>
44 #include <limits.h>
45 #include <string.h>
46
47 /* Structures used to form an in-memory representation of the XML data (other
48  * than the raw parse tree from libxml). */
49
50 struct windows_version {
51         u64 major;
52         u64 minor;
53         u64 build;
54         u64 sp_build;
55         u64 sp_level;
56 };
57
58 struct windows_info {
59         u64      arch;
60         tchar   *product_name;
61         tchar   *edition_id;
62         tchar   *installation_type;
63         tchar   *hal;
64         tchar   *product_type;
65         tchar   *product_suite;
66         tchar  **languages;
67         tchar   *default_language;
68         size_t   num_languages;
69         tchar   *system_root;
70         bool     windows_version_exists;
71         struct   windows_version windows_version;
72 };
73
74 struct image_info {
75         int index;
76         bool windows_info_exists;
77         u64 dir_count;
78         u64 file_count;
79         u64 total_bytes;
80         u64 hard_link_bytes;
81         u64 creation_time;
82         u64 last_modification_time;
83         struct windows_info windows_info;
84         tchar *name;
85         tchar *description;
86         tchar *display_name;
87         tchar *display_description;
88         tchar *flags;
89         struct wim_lookup_table *lookup_table; /* temporary field */
90 };
91
92 struct xml_string_spec {
93         const char *name;
94         size_t offset;
95 };
96
97 #define ELEM(STRING_NAME, MEMBER_NAME) \
98         {STRING_NAME, offsetof(struct image_info, MEMBER_NAME)}
99 static const struct xml_string_spec
100 image_info_xml_string_specs[] = {
101         ELEM("NAME", name),
102         ELEM("DESCRIPTION", description),
103         ELEM("DISPLAYNAME", display_name),
104         ELEM("DISPLAYDESCRIPTION", display_description),
105         ELEM("FLAGS", flags),
106 };
107 #undef ELEM
108
109 #define ELEM(STRING_NAME, MEMBER_NAME) \
110         {STRING_NAME, offsetof(struct windows_info, MEMBER_NAME)}
111 static const struct xml_string_spec
112 windows_info_xml_string_specs[] = {
113         ELEM("PRODUCTNAME", product_name),
114         ELEM("EDITIONID", edition_id),
115         ELEM("INSTALLATIONTYPE", installation_type),
116         ELEM("HAL", hal),
117         ELEM("PRODUCTTYPE", product_type),
118         ELEM("PRODUCTSUITE", product_suite),
119 };
120 #undef ELEM
121
122
123 /* Returns a statically allocated string that is a string representation of the
124  * architecture number. */
125 static const tchar *
126 get_arch(int arch)
127 {
128         switch (arch) {
129         case 0:
130                 return T("x86");
131         case 6:
132                 return T("ia64");
133         case 9:
134                 return T("x86_64");
135         /* XXX Are there other arch values? */
136         default:
137                 return T("unknown");
138         }
139 }
140
141
142 /* Iterate through the children of an xmlNode. */
143 #define for_node_child(parent, child)   \
144         for (child = parent->children; child != NULL; child = child->next)
145
146 /* Utility functions for xmlNodes */
147 static inline bool
148 node_is_element(xmlNode *node)
149 {
150         return node->type == XML_ELEMENT_NODE;
151 }
152
153 static inline bool
154 node_is_text(xmlNode *node)
155 {
156         return node->type == XML_TEXT_NODE;
157 }
158
159 static inline bool
160 node_name_is(xmlNode *node, const char *name)
161 {
162         /* For now, both upper case and lower case element names are accepted. */
163         return strcasecmp((const char *)node->name, name) == 0;
164 }
165
166 static u64
167 node_get_number(const xmlNode *u64_node, int base)
168 {
169         xmlNode *child;
170         for_node_child(u64_node, child)
171                 if (node_is_text(child))
172                         return strtoull(child->content, NULL, base);
173         return 0;
174 }
175
176 /* Finds the text node that is a child of an element node and returns its
177  * content converted to a 64-bit unsigned integer.  Returns 0 if no text node is
178  * found. */
179 static u64
180 node_get_u64(const xmlNode *u64_node)
181 {
182         return node_get_number(u64_node, 10);
183 }
184
185 /* Like node_get_u64(), but expects a number in base 16. */
186 static u64
187 node_get_hex_u64(const xmlNode *u64_node)
188 {
189         return node_get_number(u64_node, 16);
190 }
191
192 static int
193 node_get_string(const xmlNode *string_node, tchar **tstr_ret)
194 {
195         xmlNode *child;
196         tchar *tstr = NULL;
197         int ret;
198
199         for_node_child(string_node, child) {
200                 if (node_is_text(child) && child->content) {
201                         ret = utf8_to_tstr_simple(child->content, &tstr);
202                         if (ret)
203                                 return ret;
204                         break;
205                 }
206         }
207         *tstr_ret = tstr;
208         return 0;
209 }
210
211 /* Returns the timestamp from a time node.  It has child elements <HIGHPART> and
212  * <LOWPART> that are then used to construct a 64-bit timestamp. */
213 static u64
214 node_get_timestamp(const xmlNode *time_node)
215 {
216         u32 high_part = 0;
217         u32 low_part = 0;
218         xmlNode *child;
219         for_node_child(time_node, child) {
220                 if (!node_is_element(child))
221                         continue;
222                 if (node_name_is(child, "HIGHPART"))
223                         high_part = node_get_hex_u64(child);
224                 else if (node_name_is(child, "LOWPART"))
225                         low_part = node_get_hex_u64(child);
226         }
227         return (u64)low_part | ((u64)high_part << 32);
228 }
229
230 /* Used to sort an array of struct image_infos by their image indices. */
231 static int
232 sort_by_index(const void *p1, const void *p2)
233 {
234         int index_1 = ((const struct image_info*)p1)->index;
235         int index_2 = ((const struct image_info*)p2)->index;
236         if (index_1 < index_2)
237                 return -1;
238         else if (index_1 > index_2)
239                 return 1;
240         else
241                 return 0;
242 }
243
244
245 /* Frees memory allocated inside a struct windows_info structure. */
246 static void
247 destroy_windows_info(struct windows_info *windows_info)
248 {
249         FREE(windows_info->product_name);
250         FREE(windows_info->edition_id);
251         FREE(windows_info->installation_type);
252         FREE(windows_info->hal);
253         FREE(windows_info->product_type);
254         FREE(windows_info->product_suite);
255         for (size_t i = 0; i < windows_info->num_languages; i++)
256                 FREE(windows_info->languages[i]);
257         FREE(windows_info->languages);
258         FREE(windows_info->default_language);
259         FREE(windows_info->system_root);
260 }
261
262 /* Frees memory allocated inside a struct image_info structure. */
263 static void
264 destroy_image_info(struct image_info *image_info)
265 {
266         FREE(image_info->name);
267         FREE(image_info->description);
268         FREE(image_info->flags);
269         FREE(image_info->display_name);
270         FREE(image_info->display_description);
271         destroy_windows_info(&image_info->windows_info);
272         memset(image_info, 0, sizeof(struct image_info));
273 }
274
275 void
276 free_wim_info(struct wim_info *info)
277 {
278         if (info) {
279                 if (info->images) {
280                         for (int i = 0; i < info->num_images; i++)
281                                 destroy_image_info(&info->images[i]);
282                         FREE(info->images);
283                 }
284                 FREE(info);
285         }
286 }
287
288 /* Reads the information from a <VERSION> element inside the <WINDOWS> element.
289  * */
290 static void
291 xml_read_windows_version(const xmlNode *version_node,
292                          struct windows_version* windows_version)
293 {
294         xmlNode *child;
295         for_node_child(version_node, child) {
296                 if (!node_is_element(child))
297                         continue;
298                 if (node_name_is(child, "MAJOR"))
299                         windows_version->major = node_get_u64(child);
300                 else if (node_name_is(child, "MINOR"))
301                         windows_version->minor = node_get_u64(child);
302                 else if (node_name_is(child, "BUILD"))
303                         windows_version->build = node_get_u64(child);
304                 else if (node_name_is(child, "SPBUILD"))
305                         windows_version->sp_build = node_get_u64(child);
306                 else if (node_name_is(child, "SPLEVEL"))
307                         windows_version->sp_level = node_get_u64(child);
308         }
309 }
310
311 /* Reads the information from a <LANGUAGE> element inside a <WINDOWS> element.
312  * */
313 static int
314 xml_read_languages(const xmlNode *languages_node,
315                    tchar ***languages_ret,
316                    size_t *num_languages_ret,
317                    tchar **default_language_ret)
318 {
319         xmlNode *child;
320         size_t num_languages = 0;
321         tchar **languages;
322         int ret;
323
324         for_node_child(languages_node, child)
325                 if (node_is_element(child) && node_name_is(child, "LANGUAGE"))
326                         num_languages++;
327
328         languages = CALLOC(num_languages, sizeof(languages[0]));
329         if (!languages)
330                 return WIMLIB_ERR_NOMEM;
331
332         *languages_ret = languages;
333         *num_languages_ret = num_languages;
334
335         ret = 0;
336         for_node_child(languages_node, child) {
337                 if (!node_is_element(child))
338                         continue;
339                 if (node_name_is(child, "LANGUAGE"))
340                         ret = node_get_string(child, languages++);
341                 else if (node_name_is(child, "DEFAULT"))
342                         ret = node_get_string(child, default_language_ret);
343                 if (ret != 0)
344                         break;
345         }
346         return ret;
347 }
348
349 /* Reads the information from a <WINDOWS> element inside an <IMAGE> element. */
350 static int
351 xml_read_windows_info(const xmlNode *windows_node,
352                       struct windows_info *windows_info)
353 {
354         xmlNode *child;
355         int ret = 0;
356
357         for_node_child(windows_node, child) {
358                 if (!node_is_element(child))
359                         continue;
360                 if (node_name_is(child, "ARCH")) {
361                         windows_info->arch = node_get_u64(child);
362                 } else if (node_name_is(child, "PRODUCTNAME")) {
363                         ret = node_get_string(child,
364                                               &windows_info->product_name);
365                 } else if (node_name_is(child, "EDITIONID")) {
366                         ret = node_get_string(child,
367                                               &windows_info->edition_id);
368                 } else if (node_name_is(child, "INSTALLATIONTYPE")) {
369                         ret = node_get_string(child,
370                                               &windows_info->installation_type);
371                 } else if (node_name_is(child, "PRODUCTTYPE")) {
372                         ret = node_get_string(child,
373                                               &windows_info->product_type);
374                 } else if (node_name_is(child, "PRODUCTSUITE")) {
375                         ret = node_get_string(child,
376                                               &windows_info->product_suite);
377                 } else if (node_name_is(child, "LANGUAGES")) {
378                         ret = xml_read_languages(child,
379                                                  &windows_info->languages,
380                                                  &windows_info->num_languages,
381                                                  &windows_info->default_language);
382                 } else if (node_name_is(child, "VERSION")) {
383                         xml_read_windows_version(child,
384                                                 &windows_info->windows_version);
385                         windows_info->windows_version_exists = true;
386                 } else if (node_name_is(child, "SYSTEMROOT")) {
387                         ret = node_get_string(child, &windows_info->system_root);
388                 } else if (node_name_is(child, "HAL")) {
389                         ret = node_get_string(child, &windows_info->hal);
390                 }
391                 if (ret != 0)
392                         return ret;
393         }
394         return ret;
395 }
396
397 /* Reads the information from an <IMAGE> element. */
398 static int
399 xml_read_image_info(xmlNode *image_node, struct image_info *image_info)
400 {
401         xmlNode *child;
402         xmlChar *index_prop;
403         int ret;
404
405         index_prop = xmlGetProp(image_node, "INDEX");
406         if (index_prop) {
407                 image_info->index = atoi(index_prop);
408                 FREE(index_prop);
409         } else {
410                 image_info->index = 1;
411         }
412
413         ret = 0;
414         for_node_child(image_node, child) {
415                 if (!node_is_element(child))
416                         continue;
417                 if (node_name_is(child, "DIRCOUNT"))
418                         image_info->dir_count = node_get_u64(child);
419                 else if (node_name_is(child, "FILECOUNT"))
420                         image_info->file_count = node_get_u64(child);
421                 else if (node_name_is(child, "TOTALBYTES"))
422                         image_info->total_bytes = node_get_u64(child);
423                 else if (node_name_is(child, "HARDLINKBYTES"))
424                         image_info->hard_link_bytes = node_get_u64(child);
425                 else if (node_name_is(child, "CREATIONTIME"))
426                         image_info->creation_time = node_get_timestamp(child);
427                 else if (node_name_is(child, "LASTMODIFICATIONTIME"))
428                         image_info->last_modification_time = node_get_timestamp(child);
429                 else if (node_name_is(child, "WINDOWS")) {
430                         DEBUG("Found <WINDOWS> tag");
431                         ret = xml_read_windows_info(child,
432                                                     &image_info->windows_info);
433                         image_info->windows_info_exists = true;
434                 } else if (node_name_is(child, "NAME")) {
435                         ret = node_get_string(child, &image_info->name);
436                 } else if (node_name_is(child, "DESCRIPTION")) {
437                         ret = node_get_string(child, &image_info->description);
438                 } else if (node_name_is(child, "FLAGS")) {
439                         ret = node_get_string(child, &image_info->flags);
440                 } else if (node_name_is(child, "DISPLAYNAME")) {
441                         ret = node_get_string(child, &image_info->display_name);
442                 } else if (node_name_is(child, "DISPLAYDESCRIPTION")) {
443                         ret = node_get_string(child, &image_info->display_description);
444                 }
445                 if (ret != 0)
446                         return ret;
447         }
448         if (!image_info->name) {
449                 tchar *empty_name;
450                 WARNING("Image with index %d has no name", image_info->index);
451                 empty_name = MALLOC(sizeof(tchar));
452                 if (!empty_name)
453                         return WIMLIB_ERR_NOMEM;
454                 *empty_name = T('\0');
455                 image_info->name = empty_name;
456         }
457         return ret;
458 }
459
460 /* Reads the information from a <WIM> element, which should be the root element
461  * of the XML tree. */
462 static int
463 xml_read_wim_info(const xmlNode *wim_node, struct wim_info **wim_info_ret)
464 {
465         struct wim_info *wim_info;
466         xmlNode *child;
467         int ret;
468         int num_images;
469         int i;
470
471         wim_info = CALLOC(1, sizeof(struct wim_info));
472         if (!wim_info)
473                 return WIMLIB_ERR_NOMEM;
474
475         /* Count how many images there are. */
476         num_images = 0;
477         for_node_child(wim_node, child) {
478                 if (node_is_element(child) && node_name_is(child, "IMAGE")) {
479                         if (num_images == INT_MAX) {
480                                 return WIMLIB_ERR_IMAGE_COUNT;
481                         }
482                         num_images++;
483                 }
484         }
485
486         if (num_images > 0) {
487                 /* Allocate the array of struct image_infos and fill them in. */
488                 wim_info->images = CALLOC(num_images, sizeof(wim_info->images[0]));
489                 if (!wim_info->images) {
490                         ret = WIMLIB_ERR_NOMEM;
491                         goto err;
492                 }
493                 wim_info->num_images = num_images;
494                 i = 0;
495                 for_node_child(wim_node, child) {
496                         if (!node_is_element(child))
497                                 continue;
498                         if (node_name_is(child, "IMAGE")) {
499                                 DEBUG("Found <IMAGE> tag");
500                                 ret = xml_read_image_info(child,
501                                                           &wim_info->images[i]);
502                                 if (ret != 0)
503                                         goto err;
504                                 i++;
505                         } else if (node_name_is(child, "TOTALBYTES")) {
506                                 wim_info->total_bytes = node_get_u64(child);
507                         }
508                 }
509
510                 /* Sort the array of image info by image index. */
511                 qsort(wim_info->images, num_images,
512                       sizeof(struct image_info), sort_by_index);
513
514                 /* Make sure the image indices make sense */
515                 for (i = 0; i < num_images; i++) {
516                         if (wim_info->images[i].index != i + 1) {
517                                 ERROR("WIM images are not indexed [1...%d] "
518                                       "in XML data as expected",
519                                       num_images);
520                                 return WIMLIB_ERR_IMAGE_COUNT;
521                         }
522                 }
523
524         }
525         *wim_info_ret = wim_info;
526         return 0;
527 err:
528         free_wim_info(wim_info);
529         return ret;
530 }
531
532 /* Prints the information contained in a `struct windows_info'.
533  *
534  * Warning: any strings printed here are in UTF-8 encoding.  If the locale
535  * character encoding is not UTF-8, the printed strings may be garbled. */
536 static void
537 print_windows_info(const struct windows_info *windows_info)
538 {
539         const struct windows_version *windows_version;
540
541         tprintf(T("Architecture:           %"TS"\n"),
542                 get_arch(windows_info->arch));
543
544         if (windows_info->product_name) {
545                 tprintf(T("Product Name:           %"TS"\n"),
546                         windows_info->product_name);
547         }
548
549         if (windows_info->edition_id) {
550                 tprintf(T("Edition ID:             %"TS"\n"),
551                         windows_info->edition_id);
552         }
553
554         if (windows_info->installation_type) {
555                 tprintf(T("Installation Type:      %"TS"\n"),
556                         windows_info->installation_type);
557         }
558
559         if (windows_info->hal) {
560                 tprintf(T("HAL:                    %"TS"\n"),
561                               windows_info->hal);
562         }
563
564         if (windows_info->product_type) {
565                 tprintf(T("Product Type:           %"TS"\n"),
566                         windows_info->product_type);
567         }
568
569         if (windows_info->product_suite) {
570                 tprintf(T("Product Suite:          %"TS"\n"),
571                         windows_info->product_suite);
572         }
573
574         tprintf(T("Languages:              "));
575         for (size_t i = 0; i < windows_info->num_languages; i++) {
576
577                 tfputs(windows_info->languages[i], stdout);
578                 tputchar(T(' '));
579         }
580         tputchar(T('\n'));
581         if (windows_info->default_language) {
582                 tprintf(T("Default Language:       %"TS"\n"),
583                         windows_info->default_language);
584         }
585         if (windows_info->system_root) {
586                 tprintf(T("System Root:            %"TS"\n"),
587                               windows_info->system_root);
588         }
589
590         if (windows_info->windows_version_exists) {
591                 windows_version = &windows_info->windows_version;
592                 tprintf(T("Major Version:          %"PRIu64"\n"),
593                         windows_version->major);
594                 tprintf(T("Minor Version:          %"PRIu64"\n"),
595                         windows_version->minor);
596                 tprintf(T("Build:                  %"PRIu64"\n"),
597                         windows_version->build);
598                 tprintf(T("Service Pack Build:     %"PRIu64"\n"),
599                         windows_version->sp_build);
600                 tprintf(T("Service Pack Level:     %"PRIu64"\n"),
601                         windows_version->sp_level);
602         }
603 }
604
605 static int
606 xml_write_string(xmlTextWriter *writer, const char *name,
607                  const tchar *tstr)
608 {
609         if (tstr) {
610                 char *utf8_str;
611                 int rc = tstr_to_utf8_simple(tstr, &utf8_str);
612                 if (rc)
613                         return rc;
614                 rc = xmlTextWriterWriteElement(writer, name, utf8_str);
615                 FREE(utf8_str);
616                 if (rc < 0)
617                         return rc;
618         }
619         return 0;
620 }
621
622 static int
623 xml_write_strings_from_specs(xmlTextWriter *writer,
624                              const void *struct_with_strings,
625                              const struct xml_string_spec specs[],
626                              size_t num_specs)
627 {
628         for (size_t i = 0; i < num_specs; i++) {
629                 int rc = xml_write_string(writer, specs[i].name,
630                                       *(const tchar * const *)
631                                         (struct_with_strings + specs[i].offset));
632                 if (rc)
633                         return rc;
634         }
635         return 0;
636 }
637
638 static int
639 dup_strings_from_specs(const void *old_struct_with_strings,
640                        void *new_struct_with_strings,
641                        const struct xml_string_spec specs[],
642                        size_t num_specs)
643 {
644         for (size_t i = 0; i < num_specs; i++) {
645                 const tchar *old_str = *(const tchar * const *)
646                                         ((const void*)old_struct_with_strings + specs[i].offset);
647                 tchar **new_str_p = (tchar **)((void*)new_struct_with_strings + specs[i].offset);
648                 if (old_str) {
649                         *new_str_p = TSTRDUP(old_str);
650                         if (!*new_str_p)
651                                 return WIMLIB_ERR_NOMEM;
652                 }
653         }
654         return 0;
655 }
656
657 /* Writes the information contained in a `struct windows_version' to the XML
658  * document being written.  This is the <VERSION> element inside the <WINDOWS>
659  * element. */
660 static int
661 xml_write_windows_version(xmlTextWriter *writer,
662                           const struct windows_version *version)
663 {
664         int rc;
665         rc = xmlTextWriterStartElement(writer, "VERSION");
666         if (rc < 0)
667                 return rc;
668
669         rc = xmlTextWriterWriteFormatElement(writer, "MAJOR", "%"PRIu64,
670                                              version->major);
671         if (rc < 0)
672                 return rc;
673
674         rc = xmlTextWriterWriteFormatElement(writer, "MINOR", "%"PRIu64,
675                                              version->minor);
676         if (rc < 0)
677                 return rc;
678
679         rc = xmlTextWriterWriteFormatElement(writer, "BUILD", "%"PRIu64,
680                                              version->build);
681         if (rc < 0)
682                 return rc;
683
684         rc = xmlTextWriterWriteFormatElement(writer, "SPBUILD", "%"PRIu64,
685                                              version->sp_build);
686         if (rc < 0)
687                 return rc;
688
689         rc = xmlTextWriterWriteFormatElement(writer, "SPLEVEL", "%"PRIu64,
690                                              version->sp_level);
691         if (rc < 0)
692                 return rc;
693
694         return xmlTextWriterEndElement(writer); /* </VERSION> */
695 }
696
697 /* Writes the information contained in a `struct windows_info' to the XML
698  * document being written. This is the <WINDOWS> element. */
699 static int
700 xml_write_windows_info(xmlTextWriter *writer,
701                        const struct windows_info *windows_info)
702 {
703         int rc;
704         rc = xmlTextWriterStartElement(writer, "WINDOWS");
705         if (rc < 0)
706                 return rc;
707
708         rc = xmlTextWriterWriteFormatElement(writer, "ARCH", "%"PRIu64,
709                                              windows_info->arch);
710         if (rc < 0)
711                 return rc;
712
713         rc = xml_write_strings_from_specs(writer,
714                                           windows_info,
715                                           windows_info_xml_string_specs,
716                                           ARRAY_LEN(windows_info_xml_string_specs));
717         if (rc)
718                 return rc;
719
720         if (windows_info->num_languages) {
721                 rc = xmlTextWriterStartElement(writer, "LANGUAGES");
722                 if (rc < 0)
723                         return rc;
724
725                 for (size_t i = 0; i < windows_info->num_languages; i++) {
726                         rc = xml_write_string(writer, "LANGUAGE",
727                                               windows_info->languages[i]);
728                         if (rc)
729                                 return rc;
730                 }
731
732                 rc = xml_write_string(writer, "DEFAULT",
733                                       windows_info->default_language);
734                 if (rc)
735                         return rc;
736
737                 rc = xmlTextWriterEndElement(writer); /* </LANGUAGES> */
738                 if (rc < 0)
739                         return rc;
740         }
741
742         if (windows_info->windows_version_exists) {
743                 rc = xml_write_windows_version(writer, &windows_info->windows_version);
744                 if (rc < 0)
745                         return rc;
746         }
747
748         rc = xml_write_string(writer, "SYSTEMROOT",
749                               windows_info->system_root);
750         if (rc)
751                 return rc;
752
753         return xmlTextWriterEndElement(writer); /* </WINDOWS> */
754 }
755
756 /* Writes a time element to the XML document being constructed in memory. */
757 static int
758 xml_write_time(xmlTextWriter *writer, const char *element_name, u64 time)
759 {
760         int rc;
761         rc = xmlTextWriterStartElement(writer, element_name);
762         if (rc < 0)
763                 return rc;
764
765         rc = xmlTextWriterWriteFormatElement(writer, "HIGHPART",
766                                              "0x%08"PRIX32, (u32)(time >> 32));
767         if (rc < 0)
768                 return rc;
769
770         rc = xmlTextWriterWriteFormatElement(writer, "LOWPART",
771                                              "0x%08"PRIX32, (u32)time);
772         if (rc < 0)
773                 return rc;
774
775         rc = xmlTextWriterEndElement(writer); /* </@element_name> */
776         if (rc < 0)
777                 return rc;
778         return 0;
779 }
780
781 /* Writes an <IMAGE> element to the XML document. */
782 static int
783 xml_write_image_info(xmlTextWriter *writer, const struct image_info *image_info)
784 {
785         int rc;
786         rc = xmlTextWriterStartElement(writer, "IMAGE");
787         if (rc < 0)
788                 return rc;
789
790         rc = xmlTextWriterWriteFormatAttribute(writer, "INDEX", "%d",
791                                                image_info->index);
792         if (rc < 0)
793                 return rc;
794
795         rc = xmlTextWriterWriteFormatElement(writer, "DIRCOUNT", "%"PRIu64,
796                                              image_info->dir_count);
797         if (rc < 0)
798                 return rc;
799
800         rc = xmlTextWriterWriteFormatElement(writer, "FILECOUNT", "%"PRIu64,
801                                              image_info->file_count);
802         if (rc < 0)
803                 return rc;
804
805         rc = xmlTextWriterWriteFormatElement(writer, "TOTALBYTES", "%"PRIu64,
806                                              image_info->total_bytes);
807         if (rc < 0)
808                 return rc;
809
810         rc = xmlTextWriterWriteFormatElement(writer, "HARDLINKBYTES", "%"PRIu64,
811                                              image_info->hard_link_bytes);
812         if (rc < 0)
813                 return rc;
814
815         rc = xml_write_time(writer, "CREATIONTIME", image_info->creation_time);
816         if (rc < 0)
817                 return rc;
818
819         rc = xml_write_time(writer, "LASTMODIFICATIONTIME",
820                             image_info->last_modification_time);
821         if (rc < 0)
822                 return rc;
823
824         if (image_info->windows_info_exists) {
825                 rc = xml_write_windows_info(writer, &image_info->windows_info);
826                 if (rc)
827                         return rc;
828         }
829
830         rc = xml_write_strings_from_specs(writer, image_info,
831                                           image_info_xml_string_specs,
832                                           ARRAY_LEN(image_info_xml_string_specs));
833         if (rc)
834                 return rc;
835
836         rc = xmlTextWriterEndElement(writer); /* </IMAGE> */
837         if (rc < 0)
838                 return rc;
839         return 0;
840 }
841
842
843
844 /* Makes space for another image in the XML information and return a pointer to
845  * it.*/
846 static struct image_info *
847 add_image_info_struct(struct wim_info *wim_info)
848 {
849         struct image_info *images;
850
851         images = CALLOC(wim_info->num_images + 1, sizeof(struct image_info));
852         if (!images)
853                 return NULL;
854         memcpy(images, wim_info->images,
855                wim_info->num_images * sizeof(struct image_info));
856         FREE(wim_info->images);
857         wim_info->images = images;
858         wim_info->num_images++;
859         return &images[wim_info->num_images - 1];
860 }
861
862 static int
863 clone_windows_info(const struct windows_info *old, struct windows_info *new)
864 {
865         int ret;
866
867         ret = dup_strings_from_specs(old, new, windows_info_xml_string_specs,
868                                      ARRAY_LEN(windows_info_xml_string_specs));
869         if (ret)
870                 return ret;
871
872         if (old->languages) {
873                 new->languages = CALLOC(old->num_languages, sizeof(new->languages[0]));
874                 if (!new->languages)
875                         return WIMLIB_ERR_NOMEM;
876                 new->num_languages = old->num_languages;
877                 for (size_t i = 0; i < new->num_languages; i++) {
878                         if (!old->languages[i])
879                                 continue;
880                         new->languages[i] = TSTRDUP(old->languages[i]);
881                         if (!new->languages[i])
882                                 return WIMLIB_ERR_NOMEM;
883                 }
884         }
885         if (old->default_language &&
886                         !(new->default_language = TSTRDUP(old->default_language)))
887                 return WIMLIB_ERR_NOMEM;
888         if (old->system_root && !(new->system_root = TSTRDUP(old->system_root)))
889                 return WIMLIB_ERR_NOMEM;
890         if (old->windows_version_exists) {
891                 new->windows_version_exists = true;
892                 memcpy(&new->windows_version, &old->windows_version,
893                        sizeof(old->windows_version));
894         }
895         return 0;
896 }
897
898 static int
899 clone_image_info(const struct image_info *old, struct image_info *new)
900 {
901         int ret;
902
903         new->dir_count              = old->dir_count;
904         new->file_count             = old->file_count;
905         new->total_bytes            = old->total_bytes;
906         new->hard_link_bytes        = old->hard_link_bytes;
907         new->creation_time          = old->creation_time;
908         new->last_modification_time = old->last_modification_time;
909
910         ret = dup_strings_from_specs(old, new,
911                                      image_info_xml_string_specs,
912                                      ARRAY_LEN(image_info_xml_string_specs));
913         if (ret)
914                 return ret;
915
916         if (old->windows_info_exists) {
917                 new->windows_info_exists = true;
918                 ret = clone_windows_info(&old->windows_info,
919                                          &new->windows_info);
920                 if (ret)
921                         return ret;
922         }
923         return 0;
924 }
925
926 /* Copies the XML information for an image between WIM files.
927  *
928  * @dest_image_name and @dest_image_description are ignored if they are NULL;
929  * otherwise, they are used to override the image name and/or image description
930  * from the XML data in the source WIM file.
931  *
932  * On failure, WIMLIB_ERR_NOMEM is returned and no changes are made.  Otherwise,
933  * 0 is returned and the WIM information at *new_wim_info_p is modified.
934  */
935 int
936 xml_export_image(const struct wim_info *old_wim_info,
937                  int image,
938                  struct wim_info **new_wim_info_p,
939                  const tchar *dest_image_name,
940                  const tchar *dest_image_description)
941 {
942         struct wim_info *new_wim_info;
943         struct image_info *image_info;
944         int ret;
945
946         DEBUG("Copying XML data between WIM files for source image %d.", image);
947
948         wimlib_assert(old_wim_info != NULL);
949         wimlib_assert(image >= 1 && image <= old_wim_info->num_images);
950
951         if (*new_wim_info_p) {
952                 new_wim_info = *new_wim_info_p;
953         } else {
954                 new_wim_info = CALLOC(1, sizeof(struct wim_info));
955                 if (!new_wim_info)
956                         goto err;
957         }
958
959         image_info = add_image_info_struct(new_wim_info);
960         if (!image_info)
961                 goto err;
962
963         ret = clone_image_info(&old_wim_info->images[image - 1], image_info);
964         if (ret != 0)
965                 goto err_destroy_image_info;
966
967         image_info->index = new_wim_info->num_images;
968
969         if (dest_image_name) {
970                 FREE(image_info->name);
971                 image_info->name = TSTRDUP(dest_image_name);
972                 if (!image_info->name)
973                         goto err_destroy_image_info;
974         }
975         if (dest_image_description) {
976                 FREE(image_info->description);
977                 image_info->description = TSTRDUP(dest_image_description);
978                 if (!image_info->description)
979                         goto err_destroy_image_info;
980         }
981         *new_wim_info_p = new_wim_info;
982         return 0;
983 err_destroy_image_info:
984         destroy_image_info(image_info);
985 err:
986         if (new_wim_info != *new_wim_info_p)
987                 free_wim_info(new_wim_info);
988         return WIMLIB_ERR_NOMEM;
989 }
990
991 /* Removes an image from the XML information. */
992 void
993 xml_delete_image(struct wim_info **wim_info_p, int image)
994 {
995         struct wim_info *wim_info;
996
997         wim_info = *wim_info_p;
998         wimlib_assert(image >= 1 && image <= wim_info->num_images);
999         DEBUG("Deleting image %d from the XML data.", image);
1000
1001         destroy_image_info(&wim_info->images[image - 1]);
1002
1003         memmove(&wim_info->images[image - 1],
1004                 &wim_info->images[image],
1005                 (wim_info->num_images - image) * sizeof(struct image_info));
1006
1007         if (--wim_info->num_images == 0) {
1008                 free_wim_info(wim_info);
1009                 *wim_info_p = NULL;
1010         } else {
1011                 for (int i = image - 1; i < wim_info->num_images; i++)
1012                         wim_info->images[i].index--;
1013         }
1014 }
1015
1016 size_t
1017 xml_get_max_image_name_len(const WIMStruct *w)
1018 {
1019         size_t max_len = 0;
1020         if (w->wim_info) {
1021                 for (int i = 0; i < w->wim_info->num_images; i++) {
1022                         size_t len = tstrlen(w->wim_info->images[i].name);
1023                         if (len > max_len)
1024                                 max_len = len;
1025                 }
1026         }
1027         return max_len;
1028 }
1029
1030 #ifdef ENABLE_CUSTOM_MEMORY_ALLOCATOR
1031 void
1032 xml_set_memory_allocator(void *(*malloc_func)(size_t),
1033                          void (*free_func)(void *),
1034                          void *(*realloc_func)(void *, size_t))
1035 {
1036         xmlMemSetup(free_func, malloc_func, realloc_func, STRDUP);
1037 }
1038 #endif
1039
1040 static int
1041 calculate_dentry_statistics(struct wim_dentry *dentry, void *arg)
1042 {
1043         struct image_info *info = arg;
1044         const struct wim_inode *inode = dentry->d_inode;
1045         struct wim_lookup_table_entry *lte;
1046
1047         /* Update directory count and file count.
1048          *
1049          * Each dentry counts as either a file or a directory, but not both.
1050          * The root directory is an exception: it is not counted at all.
1051          *
1052          * Symbolic links and junction points (and presumably other reparse
1053          * points) count as regular files.  This is despite the fact that
1054          * junction points have FILE_ATTRIBUTE_DIRECTORY set.
1055          */
1056         if (dentry_is_root(dentry))
1057                 return 0;
1058
1059         if (inode_is_directory(inode))
1060                 info->dir_count++;
1061         else
1062                 info->file_count++;
1063
1064         /*
1065          * Update total bytes and hard link bytes.
1066          *
1067          * Unfortunately there are some inconsistencies/bugs in the way this is
1068          * done.
1069          *
1070          * If there are no alternate data streams in the image, the "total
1071          * bytes" is the sum of the size of the un-named data stream of each
1072          * inode times the link count of that inode.  In other words, it would
1073          * be the total number of bytes of regular files you would have if you
1074          * extracted the full image without any hard-links.  The "hard link
1075          * bytes" is equal to the "total bytes" minus the size of the un-named
1076          * data stream of each inode.  In other words, the "hard link bytes"
1077          * counts the size of the un-named data stream for all the links to each
1078          * inode except the first one.
1079          *
1080          * Reparse points and directories don't seem to be counted in either the
1081          * total bytes or the hard link bytes.
1082          *
1083          * And now we get to the most confusing part, the alternate data
1084          * streams.  They are not counted in the "total bytes".  However, if the
1085          * link count of an inode with alternate data streams is 2 or greater,
1086          * the size of all the alternate data streams is included in the "hard
1087          * link bytes", and this size is multiplied by the link count (NOT one
1088          * less than the link count).
1089          */
1090         lte = inode_unnamed_lte(inode, info->lookup_table);
1091         if (lte) {
1092                 info->total_bytes += wim_resource_size(lte);
1093                 if (!dentry_is_first_in_inode(dentry))
1094                         info->hard_link_bytes += wim_resource_size(lte);
1095         }
1096
1097         if (inode->i_nlink >= 2 && dentry_is_first_in_inode(dentry)) {
1098                 for (unsigned i = 0; i < inode->i_num_ads; i++) {
1099                         if (inode->i_ads_entries[i].stream_name_nbytes) {
1100                                 lte = inode_stream_lte(inode, i + 1, info->lookup_table);
1101                                 if (lte) {
1102                                         info->hard_link_bytes += inode->i_nlink *
1103                                                                  wim_resource_size(lte);
1104                                 }
1105                         }
1106                 }
1107         }
1108         return 0;
1109 }
1110
1111 /*
1112  * Calculate what to put in the <FILECOUNT>, <DIRCOUNT>, <TOTALBYTES>, and
1113  * <HARDLINKBYTES> elements of each <IMAGE>.
1114  *
1115  * Please note there is no official documentation for exactly how this is done.
1116  * But, see calculate_dentry_statistics().
1117  */
1118 void
1119 xml_update_image_info(WIMStruct *w, int image)
1120 {
1121         struct image_info *image_info;
1122
1123         DEBUG("Updating the image info for image %d", image);
1124
1125         image_info = &w->wim_info->images[image - 1];
1126
1127         image_info->file_count      = 0;
1128         image_info->dir_count       = 0;
1129         image_info->total_bytes     = 0;
1130         image_info->hard_link_bytes = 0;
1131         image_info->lookup_table = w->lookup_table;
1132
1133         for_dentry_in_tree(w->image_metadata[image - 1]->root_dentry,
1134                            calculate_dentry_statistics,
1135                            image_info);
1136         image_info->last_modification_time = get_wim_timestamp();
1137 }
1138
1139 /* Adds an image to the XML information. */
1140 int
1141 xml_add_image(WIMStruct *w, const tchar *name)
1142 {
1143         struct wim_info *wim_info;
1144         struct image_info *image_info;
1145
1146         wimlib_assert(name != NULL);
1147
1148         /* If this is the first image, allocate the struct wim_info.  Otherwise
1149          * use the existing struct wim_info. */
1150         if (w->wim_info) {
1151                 wim_info = w->wim_info;
1152         } else {
1153                 wim_info = CALLOC(1, sizeof(struct wim_info));
1154                 if (!wim_info)
1155                         return WIMLIB_ERR_NOMEM;
1156         }
1157
1158         image_info = add_image_info_struct(wim_info);
1159         if (!image_info)
1160                 goto out_free_wim_info;
1161
1162         if (!(image_info->name = TSTRDUP(name)))
1163                 goto out_destroy_image_info;
1164
1165         w->wim_info = wim_info;
1166         image_info->index = wim_info->num_images;
1167         image_info->creation_time = get_wim_timestamp();
1168         xml_update_image_info(w, image_info->index);
1169         return 0;
1170
1171 out_destroy_image_info:
1172         destroy_image_info(image_info);
1173         wim_info->num_images--;
1174 out_free_wim_info:
1175         if (wim_info != w->wim_info)
1176                 FREE(wim_info);
1177         return WIMLIB_ERR_NOMEM;
1178 }
1179
1180 /* Prints information about the specified image from struct wim_info structure.
1181  * */
1182 void
1183 print_image_info(const struct wim_info *wim_info, int image)
1184 {
1185         const struct image_info *image_info;
1186         const tchar *desc;
1187         tchar buf[50];
1188
1189         wimlib_assert(image >= 1 && image <= wim_info->num_images);
1190
1191         image_info = &wim_info->images[image - 1];
1192
1193         tprintf(T("Index:                  %d\n"), image_info->index);
1194         tprintf(T("Name:                   %"TS"\n"), image_info->name);
1195
1196         /* Always print the Description: part even if there is no
1197          * description. */
1198         if (image_info->description)
1199                 desc = image_info->description;
1200         else
1201                 desc = T("");
1202         tprintf(T("Description:            %"TS"\n"), desc);
1203
1204         if (image_info->display_name) {
1205                 tprintf(T("Display Name:           %"TS"\n"),
1206                         image_info->display_name);
1207         }
1208
1209         if (image_info->display_description) {
1210                 tprintf(T("Display Description:    %"TS"\n"),
1211                         image_info->display_description);
1212         }
1213
1214         tprintf(T("Directory Count:        %"PRIu64"\n"), image_info->dir_count);
1215         tprintf(T("File Count:             %"PRIu64"\n"), image_info->file_count);
1216         tprintf(T("Total Bytes:            %"PRIu64"\n"), image_info->total_bytes);
1217         tprintf(T("Hard Link Bytes:        %"PRIu64"\n"), image_info->hard_link_bytes);
1218
1219         wim_timestamp_to_str(image_info->creation_time, buf, sizeof(buf));
1220         tprintf(T("Creation Time:          %"TS"\n"), buf);
1221
1222         wim_timestamp_to_str(image_info->last_modification_time, buf, sizeof(buf));
1223         tprintf(T("Last Modification Time: %"TS"\n"), buf);
1224         if (image_info->windows_info_exists)
1225                 print_windows_info(&image_info->windows_info);
1226         if (image_info->flags)
1227                 tprintf(T("Flags:                  %"TS"\n"), image_info->flags);
1228         tputchar('\n');
1229 }
1230
1231 void
1232 libxml_global_init(void)
1233 {
1234         xmlInitParser();
1235         xmlInitCharEncodingHandlers();
1236 }
1237
1238 void
1239 libxml_global_cleanup(void)
1240 {
1241         xmlCleanupParser();
1242         xmlCleanupCharEncodingHandlers();
1243 }
1244
1245 /*
1246  * Reads the XML data from a WIM file.
1247  */
1248 int
1249 read_xml_data(int in_fd,
1250               const struct resource_entry *res_entry,
1251               struct wim_info **info_ret)
1252 {
1253         utf16lechar *xml_data;
1254         xmlDoc *doc;
1255         xmlNode *root;
1256         int ret;
1257
1258         DEBUG("XML data is %"PRIu64" bytes at offset %"PRIu64"",
1259               (u64)res_entry->size, res_entry->offset);
1260
1261         if (resource_is_compressed(res_entry)) {
1262                 ERROR("XML data is supposed to be uncompressed");
1263                 ret = WIMLIB_ERR_XML;
1264                 goto out;
1265         }
1266
1267         if (res_entry->size < 2) {
1268                 ERROR("XML data must be at least 2 bytes long");
1269                 ret = WIMLIB_ERR_XML;
1270                 goto out;
1271         }
1272
1273         xml_data = MALLOC(res_entry->size + 3);
1274         if (!xml_data) {
1275                 ret = WIMLIB_ERR_NOMEM;
1276                 goto out;
1277         }
1278
1279         if (full_pread(in_fd, xml_data,
1280                        res_entry->size, res_entry->offset) != res_entry->size)
1281         {
1282                 ERROR_WITH_ERRNO("Error reading XML data");
1283                 ret = WIMLIB_ERR_READ;
1284                 goto out_free_xml_data;
1285         }
1286
1287         /* Null-terminate just in case */
1288         ((u8*)xml_data)[res_entry->size] = 0;
1289         ((u8*)xml_data)[res_entry->size + 1] = 0;
1290         ((u8*)xml_data)[res_entry->size + 2] = 0;
1291
1292         DEBUG("Parsing XML using libxml2 to create XML tree");
1293
1294         doc = xmlReadMemory((const char *)xml_data,
1295                             res_entry->size, "noname.xml", "UTF-16", 0);
1296
1297         if (!doc) {
1298                 ERROR("Failed to parse XML data");
1299                 ret = WIMLIB_ERR_XML;
1300                 goto out_free_xml_data;
1301         }
1302
1303         DEBUG("Constructing WIM information structure from XML tree.");
1304
1305         root = xmlDocGetRootElement(doc);
1306         if (!root) {
1307                 ERROR("WIM XML data is an empty XML document");
1308                 ret = WIMLIB_ERR_XML;
1309                 goto out_free_doc;
1310         }
1311
1312         if (!node_is_element(root) || !node_name_is(root, "WIM")) {
1313                 ERROR("Expected <WIM> for the root XML element");
1314                 ret = WIMLIB_ERR_XML;
1315                 goto out_free_doc;
1316         }
1317         ret = xml_read_wim_info(root, info_ret);
1318 out_free_doc:
1319         DEBUG("Freeing XML tree.");
1320         xmlFreeDoc(doc);
1321 out_free_xml_data:
1322         FREE(xml_data);
1323 out:
1324         return ret;
1325 }
1326
1327 #define CHECK_RET  ({   if (ret < 0)  { \
1328                                 ERROR("Error writing XML data"); \
1329                                 ret = WIMLIB_ERR_WRITE; \
1330                                 goto out_free_text_writer; \
1331                         } })
1332
1333 /*
1334  * Writes XML data to a WIM file.
1335  *
1336  * If @total_bytes is non-zero, it specifies what to write to the TOTALBYTES
1337  * element in the XML data.  If zero, TOTALBYTES is given the default value of
1338  * the offset of the XML data.
1339  */
1340 int
1341 write_xml_data(const struct wim_info *wim_info, int image, int out_fd,
1342                u64 total_bytes, struct resource_entry *out_res_entry)
1343 {
1344         xmlCharEncodingHandler *encoding_handler;
1345         xmlOutputBuffer *out_buffer;
1346         xmlTextWriter *writer;
1347         int ret;
1348         off_t start_offset;
1349         off_t end_offset;
1350
1351         wimlib_assert(image == WIMLIB_ALL_IMAGES ||
1352                         (wim_info != NULL && image >= 1 &&
1353                          image <= wim_info->num_images));
1354
1355         start_offset = filedes_offset(out_fd);
1356         if (start_offset == -1)
1357                 return WIMLIB_ERR_WRITE;
1358
1359         DEBUG("Writing XML data for image %d at offset %"PRIu64,
1360               image, start_offset);
1361
1362         /* 2 bytes endianness marker for UTF-16LE.  This is _required_ for WIM
1363          * XML data. */
1364         static u8 bom[2] = {0xff, 0xfe};
1365         if (full_write(out_fd, bom, 2) != 2) {
1366                 ERROR_WITH_ERRNO("Error writing XML data");
1367                 return WIMLIB_ERR_WRITE;
1368         }
1369
1370         /* The contents of the <TOTALBYTES> element in the XML data, under the
1371          * <WIM> element (not the <IMAGE> element), is for non-split WIMs the
1372          * size of the WIM file excluding the XML data and integrity table.
1373          * This should be equal to the current position in the output stream,
1374          * since the XML data and integrity table are the last elements of the
1375          * WIM.
1376          *
1377          * For split WIMs, <TOTALBYTES> takes into account the entire WIM, not
1378          * just the current part.  In that case, @total_bytes should be passed
1379          * in to this function. */
1380         if (total_bytes == 0)
1381                 total_bytes = start_offset;
1382
1383         /* The encoding of the XML data must be UTF-16LE. */
1384         encoding_handler = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF16LE);
1385         if (!encoding_handler) {
1386                 ERROR("Failed to get XML character encoding handler for UTF-16LE");
1387                 ret = WIMLIB_ERR_LIBXML_UTF16_HANDLER_NOT_AVAILABLE;
1388                 goto out;
1389         }
1390
1391         out_buffer = xmlOutputBufferCreateFd(out_fd, encoding_handler);
1392         if (!out_buffer) {
1393                 ERROR("Failed to allocate xmlOutputBuffer");
1394                 ret = WIMLIB_ERR_NOMEM;
1395                 goto out;
1396         }
1397
1398         writer = xmlNewTextWriter(out_buffer);
1399         if (!writer) {
1400                 ERROR("Failed to allocate xmlTextWriter");
1401                 ret = WIMLIB_ERR_NOMEM;
1402                 goto out_output_buffer_close;
1403         }
1404
1405         DEBUG("Writing <WIM> element");
1406
1407         ret = xmlTextWriterStartElement(writer, "WIM");
1408         CHECK_RET;
1409
1410         ret = xmlTextWriterWriteFormatElement(writer, "TOTALBYTES", "%"PRIu64,
1411                                               total_bytes);
1412         CHECK_RET;
1413
1414         if (wim_info != NULL) {
1415                 int first, last;
1416                 if (image == WIMLIB_ALL_IMAGES) {
1417                         first = 1;
1418                         last = wim_info->num_images;
1419                 } else {
1420                         first = image;
1421                         last = image;
1422                 }
1423                 DEBUG("Writing %d <IMAGE> elements", last - first + 1);
1424                 for (int i = first; i <= last; i++) {
1425                         ret = xml_write_image_info(writer, &wim_info->images[i - 1]);
1426                         if (ret) {
1427                                 CHECK_RET;
1428                                 goto out_free_text_writer;
1429                         }
1430                 }
1431         }
1432
1433         ret = xmlTextWriterEndElement(writer);
1434         CHECK_RET;
1435
1436         ret = xmlTextWriterEndDocument(writer);
1437         CHECK_RET;
1438
1439         DEBUG("Ended XML document");
1440
1441         end_offset = filedes_offset(out_fd);
1442         if (end_offset == -1) {
1443                 ret = WIMLIB_ERR_WRITE;
1444         } else {
1445                 ret = 0;
1446                 out_res_entry->offset        = start_offset;
1447                 out_res_entry->size          = end_offset - start_offset;
1448                 out_res_entry->original_size = end_offset - start_offset;
1449                 out_res_entry->flags         = WIM_RESHDR_FLAG_METADATA;
1450         }
1451 out_free_text_writer:
1452         /* xmlFreeTextWriter will free the attached xmlOutputBuffer. */
1453         xmlFreeTextWriter(writer);
1454         out_buffer = NULL;
1455 out_output_buffer_close:
1456         if (out_buffer != NULL)
1457                 xmlOutputBufferClose(out_buffer);
1458 out:
1459         if (ret == 0)
1460                 DEBUG("Successfully wrote XML data");
1461         return ret;
1462 }
1463
1464 /* Returns the name of the specified image. */
1465 WIMLIBAPI const tchar *
1466 wimlib_get_image_name(const WIMStruct *w, int image)
1467 {
1468         if (image < 1 || image > w->hdr.image_count)
1469                 return NULL;
1470         return w->wim_info->images[image - 1].name;
1471 }
1472
1473 /* Returns the description of the specified image. */
1474 WIMLIBAPI const tchar *
1475 wimlib_get_image_description(const WIMStruct *w, int image)
1476 {
1477         if (image < 1 || image > w->hdr.image_count)
1478                 return NULL;
1479         return w->wim_info->images[image - 1].description;
1480 }
1481
1482 /* Determines if an image name is already used by some image in the WIM. */
1483 WIMLIBAPI bool
1484 wimlib_image_name_in_use(const WIMStruct *w, const tchar *name)
1485 {
1486         if (!name || !*name)
1487                 return false;
1488         for (int i = 1; i <= w->hdr.image_count; i++)
1489                 if (!tstrcmp(w->wim_info->images[i - 1].name, name))
1490                         return true;
1491         return false;
1492 }
1493
1494
1495 /* Extracts the raw XML data to a file stream. */
1496 WIMLIBAPI int
1497 wimlib_extract_xml_data(WIMStruct *w, FILE *fp)
1498 {
1499         size_t size;
1500         void *buf;
1501         int ret;
1502
1503         size = w->hdr.xml_res_entry.size;
1504         if (sizeof(size_t) < sizeof(u64))
1505                 if (size != w->hdr.xml_res_entry.size)
1506                         return WIMLIB_ERR_INVALID_PARAM;
1507
1508         buf = MALLOC(size);
1509         if (!buf)
1510                 return WIMLIB_ERR_NOMEM;
1511
1512         if (full_pread(w->in_fd,
1513                        buf,
1514                        w->hdr.xml_res_entry.size,
1515                        w->hdr.xml_res_entry.offset) != w->hdr.xml_res_entry.size)
1516         {
1517                 ERROR_WITH_ERRNO("Error reading XML data");
1518                 ret = WIMLIB_ERR_READ;
1519                 goto out_free_buf;
1520         }
1521
1522         if (fwrite(buf, 1, size, fp) != size) {
1523                 ERROR_WITH_ERRNO("Failed to extract XML data");
1524                 ret = WIMLIB_ERR_WRITE;
1525         } else {
1526                 ret = 0;
1527         }
1528 out_free_buf:
1529         FREE(buf);
1530         return ret;
1531 }
1532
1533 /* Sets the name of an image in the WIM. */
1534 WIMLIBAPI int
1535 wimlib_set_image_name(WIMStruct *w, int image, const tchar *name)
1536 {
1537         tchar *p;
1538         int i;
1539
1540         DEBUG("Setting the name of image %d to %"TS, image, name);
1541
1542         if (!name || !*name) {
1543                 ERROR("Must specify a non-empty string for the image name");
1544                 return WIMLIB_ERR_INVALID_PARAM;
1545         }
1546
1547         if (image < 1 || image > w->hdr.image_count) {
1548                 ERROR("%d is not a valid image", image);
1549                 return WIMLIB_ERR_INVALID_IMAGE;
1550         }
1551
1552         for (i = 1; i <= w->hdr.image_count; i++) {
1553                 if (i == image)
1554                         continue;
1555                 if (tstrcmp(w->wim_info->images[i - 1].name, name) == 0) {
1556                         ERROR("The name \"%"TS"\" is already in use in the WIM!",
1557                               name);
1558                         return WIMLIB_ERR_IMAGE_NAME_COLLISION;
1559                 }
1560         }
1561
1562         p = TSTRDUP(name);
1563         if (!p)
1564                 return WIMLIB_ERR_NOMEM;
1565
1566         FREE(w->wim_info->images[image - 1].name);
1567         w->wim_info->images[image - 1].name = p;
1568         return 0;
1569 }
1570
1571 static int
1572 do_set_image_info_str(WIMStruct *w, int image, const tchar *tstr,
1573                       size_t offset)
1574 {
1575         tchar *tstr_copy;
1576         tchar **dest_tstr_p;
1577
1578         if (image < 1 || image > w->hdr.image_count) {
1579                 ERROR("%d is not a valid image", image);
1580                 return WIMLIB_ERR_INVALID_IMAGE;
1581         }
1582         if (tstr) {
1583                 tstr_copy = TSTRDUP(tstr);
1584                 if (!tstr_copy)
1585                         return WIMLIB_ERR_NOMEM;
1586         } else {
1587                 tstr_copy = NULL;
1588         }
1589         dest_tstr_p = (tchar**)((void*)&w->wim_info->images[image - 1] + offset);
1590
1591         FREE(*dest_tstr_p);
1592         *dest_tstr_p = tstr_copy;
1593         return 0;
1594 }
1595
1596 /* Sets the description of an image in the WIM. */
1597 WIMLIBAPI int
1598 wimlib_set_image_descripton(WIMStruct *w, int image,
1599                             const tchar *description)
1600 {
1601         return do_set_image_info_str(w, image, description,
1602                                      offsetof(struct image_info, description));
1603 }
1604
1605 /* Set the <FLAGS> element of a WIM image */
1606 WIMLIBAPI int
1607 wimlib_set_image_flags(WIMStruct *w, int image, const tchar *flags)
1608 {
1609         return do_set_image_info_str(w, image, flags,
1610                                      offsetof(struct image_info, flags));
1611 }