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