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