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