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