WIMBoot / system compression: try WOFADK in addition to WOF
[wimlib] / src / wim.c
1 /*
2  * wim.c - High-level code dealing with WIMStructs and images.
3  */
4
5 /*
6  * Copyright (C) 2012, 2013, 2014, 2015 Eric Biggers
7  *
8  * This file is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU Lesser General Public License as published by the Free
10  * Software Foundation; either version 3 of the License, or (at your option) any
11  * later version.
12  *
13  * This file is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this file; if not, see http://www.gnu.org/licenses/.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #  include "config.h"
24 #endif
25
26 #include <errno.h>
27 #include <fcntl.h>
28 #ifndef __WIN32__
29 #  include <langinfo.h>
30 #endif
31 #include <pthread.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34
35 #include "wimlib.h"
36 #include "wimlib/assert.h"
37 #include "wimlib/blob_table.h"
38 #include "wimlib/dentry.h"
39 #include "wimlib/encoding.h"
40 #include "wimlib/file_io.h"
41 #include "wimlib/integrity.h"
42 #include "wimlib/metadata.h"
43 #include "wimlib/ntfs_3g.h" /* for libntfs3g_global_init() */
44 #include "wimlib/security.h"
45 #include "wimlib/wim.h"
46 #include "wimlib/xml.h"
47 #include "wimlib/win32.h"
48
49 /* Information about the available compression types for the WIM format.  */
50 static const struct {
51         const tchar *name;
52         u32 min_chunk_size;
53         u32 max_chunk_size;
54         u32 default_nonsolid_chunk_size;
55         u32 default_solid_chunk_size;
56 } wim_ctype_info[] = {
57         [WIMLIB_COMPRESSION_TYPE_NONE] = {
58                 .name = T("None"),
59                 .min_chunk_size = 0,
60                 .max_chunk_size = 0,
61                 .default_nonsolid_chunk_size = 0,
62                 .default_solid_chunk_size = 0,
63         },
64         [WIMLIB_COMPRESSION_TYPE_XPRESS] = {
65                 .name = T("XPRESS"),
66                 .min_chunk_size = 4096,
67                 .max_chunk_size = 65536,
68                 .default_nonsolid_chunk_size = 32768,
69                 .default_solid_chunk_size = 32768,
70         },
71         [WIMLIB_COMPRESSION_TYPE_LZX] = {
72                 .name = T("LZX"),
73                 .min_chunk_size = 32768,
74                 .max_chunk_size = 2097152,
75                 .default_nonsolid_chunk_size = 32768,
76                 .default_solid_chunk_size = 32768,
77         },
78         [WIMLIB_COMPRESSION_TYPE_LZMS] = {
79                 .name = T("LZMS"),
80                 .min_chunk_size = 32768,
81                 .max_chunk_size = 1073741824,
82                 .default_nonsolid_chunk_size = 131072,
83                 .default_solid_chunk_size = 67108864,
84         },
85 };
86
87 /* Is the specified compression type valid?  */
88 static bool
89 wim_compression_type_valid(enum wimlib_compression_type ctype)
90 {
91         return (unsigned)ctype < ARRAY_LEN(wim_ctype_info) &&
92                wim_ctype_info[(unsigned)ctype].name != NULL;
93 }
94
95 /* Is the specified chunk size valid for the compression type?  */
96 static bool
97 wim_chunk_size_valid(u32 chunk_size, enum wimlib_compression_type ctype)
98 {
99         if (!(chunk_size == 0 || is_power_of_2(chunk_size)))
100                 return false;
101
102         return chunk_size >= wim_ctype_info[(unsigned)ctype].min_chunk_size &&
103                chunk_size <= wim_ctype_info[(unsigned)ctype].max_chunk_size;
104 }
105
106 /* Return the default chunk size to use for the specified compression type in
107  * non-solid resources.  */
108 static u32
109 wim_default_nonsolid_chunk_size(enum wimlib_compression_type ctype)
110 {
111         return wim_ctype_info[(unsigned)ctype].default_nonsolid_chunk_size;
112 }
113
114 /* Return the default chunk size to use for the specified compression type in
115  * solid resources.  */
116 static u32
117 wim_default_solid_chunk_size(enum wimlib_compression_type ctype)
118 {
119         return wim_ctype_info[(unsigned)ctype].default_solid_chunk_size;
120 }
121
122 /* Return the default compression type to use in solid resources.  */
123 static enum wimlib_compression_type
124 wim_default_solid_compression_type(void)
125 {
126         return WIMLIB_COMPRESSION_TYPE_LZMS;
127 }
128
129 static int
130 is_blob_in_solid_resource(struct blob_descriptor *blob, void *_ignore)
131 {
132         return blob->blob_location == BLOB_IN_WIM &&
133                 (blob->rdesc->flags & WIM_RESHDR_FLAG_SOLID);
134 }
135
136 bool
137 wim_has_solid_resources(const WIMStruct *wim)
138 {
139         return for_blob_in_table(wim->blob_table, is_blob_in_solid_resource, NULL);
140 }
141
142 static WIMStruct *
143 new_wim_struct(void)
144 {
145         WIMStruct *wim = CALLOC(1, sizeof(WIMStruct));
146         if (!wim)
147                 return NULL;
148
149         filedes_invalidate(&wim->in_fd);
150         filedes_invalidate(&wim->out_fd);
151         wim->out_solid_compression_type = wim_default_solid_compression_type();
152         wim->out_solid_chunk_size = wim_default_solid_chunk_size(
153                                         wim->out_solid_compression_type);
154         INIT_LIST_HEAD(&wim->subwims);
155         return wim;
156 }
157
158 /* API function documented in wimlib.h  */
159 WIMLIBAPI int
160 wimlib_create_new_wim(enum wimlib_compression_type ctype, WIMStruct **wim_ret)
161 {
162         int ret;
163         WIMStruct *wim;
164
165         ret = wimlib_global_init(WIMLIB_INIT_FLAG_ASSUME_UTF8);
166         if (ret)
167                 return ret;
168
169         if (!wim_ret)
170                 return WIMLIB_ERR_INVALID_PARAM;
171
172         if (!wim_compression_type_valid(ctype))
173                 return WIMLIB_ERR_INVALID_COMPRESSION_TYPE;
174
175         wim = new_wim_struct();
176         if (!wim)
177                 return WIMLIB_ERR_NOMEM;
178
179         wim->blob_table = new_blob_table(9001);
180         if (!wim->blob_table) {
181                 wimlib_free(wim);
182                 return WIMLIB_ERR_NOMEM;
183         }
184
185         /* Fill in wim->hdr with default values  */
186         wim->hdr.magic = WIM_MAGIC;
187         wim->hdr.wim_version = WIM_VERSION_DEFAULT;
188         wim->hdr.flags = 0;
189         wim->hdr.chunk_size = 0;
190         generate_guid(wim->hdr.guid);
191         wim->hdr.part_number = 1;
192         wim->hdr.total_parts = 1;
193         wim->hdr.image_count = 0;
194         wim->hdr.boot_idx = 0;
195
196         wim->compression_type = WIMLIB_COMPRESSION_TYPE_NONE;
197         wim->chunk_size = wim->hdr.chunk_size;
198
199         /* Set the output compression type  */
200         wim->out_compression_type = ctype;
201         wim->out_chunk_size = wim_default_nonsolid_chunk_size(ctype);
202
203         *wim_ret = wim;
204         return 0;
205 }
206
207 static void
208 destroy_image_metadata(struct wim_image_metadata *imd,
209                        struct blob_table *table,
210                        bool free_metadata_blob_descriptor)
211 {
212         free_dentry_tree(imd->root_dentry, table);
213         imd->root_dentry = NULL;
214         free_wim_security_data(imd->security_data);
215         imd->security_data = NULL;
216
217         if (free_metadata_blob_descriptor) {
218                 free_blob_descriptor(imd->metadata_blob);
219                 imd->metadata_blob = NULL;
220         }
221         if (!table) {
222                 struct blob_descriptor *blob, *tmp;
223                 list_for_each_entry_safe(blob, tmp, &imd->unhashed_blobs, unhashed_list)
224                         free_blob_descriptor(blob);
225         }
226         INIT_LIST_HEAD(&imd->unhashed_blobs);
227         INIT_HLIST_HEAD(&imd->inode_list);
228 }
229
230 void
231 put_image_metadata(struct wim_image_metadata *imd, struct blob_table *table)
232 {
233         if (imd && --imd->refcnt == 0) {
234                 destroy_image_metadata(imd, table, true);
235                 FREE(imd);
236         }
237 }
238
239 /* Appends the specified image metadata structure to the array of image metadata
240  * for a WIM, and increments the image count. */
241 int
242 append_image_metadata(WIMStruct *wim, struct wim_image_metadata *imd)
243 {
244         struct wim_image_metadata **imd_array;
245
246         imd_array = REALLOC(wim->image_metadata,
247                             sizeof(wim->image_metadata[0]) * (wim->hdr.image_count + 1));
248
249         if (!imd_array)
250                 return WIMLIB_ERR_NOMEM;
251         wim->image_metadata = imd_array;
252         imd_array[wim->hdr.image_count++] = imd;
253         return 0;
254 }
255
256 struct wim_image_metadata *
257 new_image_metadata(void)
258 {
259         struct wim_image_metadata *imd;
260
261         imd = CALLOC(1, sizeof(*imd));
262         if (imd) {
263                 imd->refcnt = 1;
264                 INIT_HLIST_HEAD(&imd->inode_list);
265                 INIT_LIST_HEAD(&imd->unhashed_blobs);
266         }
267         return imd;
268 }
269
270 static struct wim_image_metadata **
271 new_image_metadata_array(unsigned num_images)
272 {
273         struct wim_image_metadata **imd_array;
274
275         imd_array = CALLOC(num_images, sizeof(imd_array[0]));
276
277         if (!imd_array)
278                 return NULL;
279         for (unsigned i = 0; i < num_images; i++) {
280                 imd_array[i] = new_image_metadata();
281                 if (unlikely(!imd_array[i])) {
282                         for (unsigned j = 0; j < i; j++)
283                                 put_image_metadata(imd_array[j], NULL);
284                         FREE(imd_array);
285                         return NULL;
286                 }
287         }
288         return imd_array;
289 }
290
291
292 /*
293  * Load the metadata for the specified WIM image into memory and set it
294  * as the WIMStruct's currently selected image.
295  *
296  * @wim
297  *      The WIMStruct for the WIM.
298  * @image
299  *      The 1-based index of the image in the WIM to select.
300  *
301  * On success, 0 will be returned, wim->current_image will be set to
302  * @image, and wim_get_current_image_metadata() can be used to retrieve
303  * metadata information for the image.
304  *
305  * On failure, WIMLIB_ERR_INVALID_IMAGE, WIMLIB_ERR_METADATA_NOT_FOUND,
306  * or another error code will be returned.
307  */
308 int
309 select_wim_image(WIMStruct *wim, int image)
310 {
311         struct wim_image_metadata *imd;
312         int ret;
313
314         if (image == WIMLIB_NO_IMAGE)
315                 return WIMLIB_ERR_INVALID_IMAGE;
316
317         if (image == wim->current_image)
318                 return 0;
319
320         if (image < 1 || image > wim->hdr.image_count)
321                 return WIMLIB_ERR_INVALID_IMAGE;
322
323         if (!wim_has_metadata(wim))
324                 return WIMLIB_ERR_METADATA_NOT_FOUND;
325
326         /* If a valid image is currently selected, its metadata can be freed if
327          * it has not been modified.  */
328         deselect_current_wim_image(wim);
329         wim->current_image = image;
330         imd = wim_get_current_image_metadata(wim);
331         if (imd->root_dentry || imd->modified) {
332                 ret = 0;
333         } else {
334                 ret = read_metadata_resource(imd);
335                 if (ret)
336                         wim->current_image = WIMLIB_NO_IMAGE;
337         }
338         return ret;
339 }
340
341 void
342 deselect_current_wim_image(WIMStruct *wim)
343 {
344         struct wim_image_metadata *imd;
345         if (wim->current_image == WIMLIB_NO_IMAGE)
346                 return;
347         imd = wim_get_current_image_metadata(wim);
348         if (!imd->modified) {
349                 wimlib_assert(list_empty(&imd->unhashed_blobs));
350                 destroy_image_metadata(imd, NULL, false);
351         }
352         wim->current_image = WIMLIB_NO_IMAGE;
353 }
354
355 /*
356  * Calls a function on images in the WIM.  If @image is WIMLIB_ALL_IMAGES,
357  * @visitor is called on the WIM once for each image, with each image selected
358  * as the current image in turn.  If @image is a certain image, @visitor is
359  * called on the WIM only once, with that image selected.
360  */
361 int
362 for_image(WIMStruct *wim, int image, int (*visitor)(WIMStruct *))
363 {
364         int ret;
365         int start;
366         int end;
367         int i;
368
369         if (image == WIMLIB_ALL_IMAGES) {
370                 start = 1;
371                 end = wim->hdr.image_count;
372         } else if (image >= 1 && image <= wim->hdr.image_count) {
373                 start = image;
374                 end = image;
375         } else {
376                 return WIMLIB_ERR_INVALID_IMAGE;
377         }
378         for (i = start; i <= end; i++) {
379                 ret = select_wim_image(wim, i);
380                 if (ret != 0)
381                         return ret;
382                 ret = visitor(wim);
383                 if (ret != 0)
384                         return ret;
385         }
386         return 0;
387 }
388
389 /* API function documented in wimlib.h  */
390 WIMLIBAPI int
391 wimlib_resolve_image(WIMStruct *wim, const tchar *image_name_or_num)
392 {
393         tchar *p;
394         long image;
395         int i;
396
397         if (!image_name_or_num || !*image_name_or_num)
398                 return WIMLIB_NO_IMAGE;
399
400         if (!tstrcasecmp(image_name_or_num, T("all"))
401             || !tstrcasecmp(image_name_or_num, T("*")))
402                 return WIMLIB_ALL_IMAGES;
403         image = tstrtol(image_name_or_num, &p, 10);
404         if (p != image_name_or_num && *p == T('\0') && image > 0) {
405                 if (image > wim->hdr.image_count)
406                         return WIMLIB_NO_IMAGE;
407                 return image;
408         } else {
409                 for (i = 1; i <= wim->hdr.image_count; i++) {
410                         if (!tstrcmp(image_name_or_num,
411                                      wimlib_get_image_name(wim, i)))
412                                 return i;
413                 }
414                 return WIMLIB_NO_IMAGE;
415         }
416 }
417
418 /* API function documented in wimlib.h  */
419 WIMLIBAPI void
420 wimlib_print_available_images(const WIMStruct *wim, int image)
421 {
422         int first;
423         int last;
424         int i;
425         int n;
426         if (image == WIMLIB_ALL_IMAGES) {
427                 n = tprintf(T("Available Images:\n"));
428                 first = 1;
429                 last = wim->hdr.image_count;
430         } else if (image >= 1 && image <= wim->hdr.image_count) {
431                 n = tprintf(T("Information for Image %d\n"), image);
432                 first = image;
433                 last = image;
434         } else {
435                 tprintf(T("wimlib_print_available_images(): Invalid image %d"),
436                         image);
437                 return;
438         }
439         for (i = 0; i < n - 1; i++)
440                 tputchar(T('-'));
441         tputchar(T('\n'));
442         for (i = first; i <= last; i++)
443                 print_image_info(wim->wim_info, i);
444 }
445
446 /* API function documented in wimlib.h  */
447 WIMLIBAPI int
448 wimlib_get_wim_info(WIMStruct *wim, struct wimlib_wim_info *info)
449 {
450         memset(info, 0, sizeof(struct wimlib_wim_info));
451         copy_guid(info->guid, wim->hdr.guid);
452         info->image_count = wim->hdr.image_count;
453         info->boot_index = wim->hdr.boot_idx;
454         info->wim_version = wim->hdr.wim_version;
455         info->chunk_size = wim->chunk_size;
456         info->part_number = wim->hdr.part_number;
457         info->total_parts = wim->hdr.total_parts;
458         info->compression_type = wim->compression_type;
459         info->total_bytes = wim_info_get_total_bytes(wim->wim_info);
460         info->has_integrity_table = wim_has_integrity_table(wim);
461         info->opened_from_file = (wim->filename != NULL);
462         info->is_readonly = (wim->hdr.flags & WIM_HDR_FLAG_READONLY) ||
463                              (wim->hdr.total_parts != 1) ||
464                              (wim->filename && taccess(wim->filename, W_OK));
465         info->has_rpfix = (wim->hdr.flags & WIM_HDR_FLAG_RP_FIX) != 0;
466         info->is_marked_readonly = (wim->hdr.flags & WIM_HDR_FLAG_READONLY) != 0;
467         info->write_in_progress = (wim->hdr.flags & WIM_HDR_FLAG_WRITE_IN_PROGRESS) != 0;
468         info->metadata_only = (wim->hdr.flags & WIM_HDR_FLAG_METADATA_ONLY) != 0;
469         info->resource_only = (wim->hdr.flags & WIM_HDR_FLAG_RESOURCE_ONLY) != 0;
470         info->spanned = (wim->hdr.flags & WIM_HDR_FLAG_SPANNED) != 0;
471         info->pipable = wim_is_pipable(wim);
472         return 0;
473 }
474
475 /* API function documented in wimlib.h  */
476 WIMLIBAPI int
477 wimlib_set_wim_info(WIMStruct *wim, const struct wimlib_wim_info *info, int which)
478 {
479         if (which & ~(WIMLIB_CHANGE_READONLY_FLAG |
480                       WIMLIB_CHANGE_GUID |
481                       WIMLIB_CHANGE_BOOT_INDEX |
482                       WIMLIB_CHANGE_RPFIX_FLAG))
483                 return WIMLIB_ERR_INVALID_PARAM;
484
485         if ((which & WIMLIB_CHANGE_BOOT_INDEX) &&
486             info->boot_index > wim->hdr.image_count)
487                 return WIMLIB_ERR_INVALID_IMAGE;
488
489         if (which & WIMLIB_CHANGE_READONLY_FLAG) {
490                 if (info->is_marked_readonly)
491                         wim->hdr.flags |= WIM_HDR_FLAG_READONLY;
492                 else
493                         wim->hdr.flags &= ~WIM_HDR_FLAG_READONLY;
494         }
495
496         if (which & WIMLIB_CHANGE_GUID)
497                 copy_guid(wim->hdr.guid, info->guid);
498
499         if (which & WIMLIB_CHANGE_BOOT_INDEX)
500                 wim->hdr.boot_idx = info->boot_index;
501
502         if (which & WIMLIB_CHANGE_RPFIX_FLAG) {
503                 if (info->has_rpfix)
504                         wim->hdr.flags |= WIM_HDR_FLAG_RP_FIX;
505                 else
506                         wim->hdr.flags &= ~WIM_HDR_FLAG_RP_FIX;
507         }
508         return 0;
509 }
510
511 /* API function documented in wimlib.h  */
512 WIMLIBAPI int
513 wimlib_set_output_compression_type(WIMStruct *wim,
514                                    enum wimlib_compression_type ctype)
515 {
516         if (!wim_compression_type_valid(ctype))
517                 return WIMLIB_ERR_INVALID_COMPRESSION_TYPE;
518
519         wim->out_compression_type = ctype;
520
521         /* Reset the chunk size if it's no longer valid.  */
522         if (!wim_chunk_size_valid(wim->out_chunk_size, ctype))
523                 wim->out_chunk_size = wim_default_nonsolid_chunk_size(ctype);
524         return 0;
525 }
526
527 /* API function documented in wimlib.h  */
528 WIMLIBAPI int
529 wimlib_set_output_pack_compression_type(WIMStruct *wim,
530                                         enum wimlib_compression_type ctype)
531 {
532         if (!wim_compression_type_valid(ctype))
533                 return WIMLIB_ERR_INVALID_COMPRESSION_TYPE;
534
535         /* Solid resources can't be uncompressed.  */
536         if (ctype == WIMLIB_COMPRESSION_TYPE_NONE)
537                 return WIMLIB_ERR_INVALID_COMPRESSION_TYPE;
538
539         wim->out_solid_compression_type = ctype;
540
541         /* Reset the chunk size if it's no longer valid.  */
542         if (!wim_chunk_size_valid(wim->out_solid_chunk_size, ctype))
543                 wim->out_solid_chunk_size = wim_default_solid_chunk_size(ctype);
544         return 0;
545 }
546
547 /* API function documented in wimlib.h  */
548 WIMLIBAPI int
549 wimlib_set_output_chunk_size(WIMStruct *wim, u32 chunk_size)
550 {
551         if (chunk_size == 0) {
552                 wim->out_chunk_size =
553                         wim_default_nonsolid_chunk_size(wim->out_compression_type);
554                 return 0;
555         }
556
557         if (!wim_chunk_size_valid(chunk_size, wim->out_compression_type))
558                 return WIMLIB_ERR_INVALID_CHUNK_SIZE;
559
560         wim->out_chunk_size = chunk_size;
561         return 0;
562 }
563
564 /* API function documented in wimlib.h  */
565 WIMLIBAPI int
566 wimlib_set_output_pack_chunk_size(WIMStruct *wim, u32 chunk_size)
567 {
568         if (chunk_size == 0) {
569                 wim->out_solid_chunk_size =
570                         wim_default_solid_chunk_size(wim->out_solid_compression_type);
571                 return 0;
572         }
573
574         if (!wim_chunk_size_valid(chunk_size, wim->out_solid_compression_type))
575                 return WIMLIB_ERR_INVALID_CHUNK_SIZE;
576
577         wim->out_solid_chunk_size = chunk_size;
578         return 0;
579 }
580
581 /* API function documented in wimlib.h  */
582 WIMLIBAPI const tchar *
583 wimlib_get_compression_type_string(enum wimlib_compression_type ctype)
584 {
585         if (!wim_compression_type_valid(ctype))
586                 return T("Invalid");
587
588         return wim_ctype_info[(unsigned)ctype].name;
589 }
590
591 WIMLIBAPI void
592 wimlib_register_progress_function(WIMStruct *wim,
593                                   wimlib_progress_func_t progfunc,
594                                   void *progctx)
595 {
596         wim->progfunc = progfunc;
597         wim->progctx = progctx;
598 }
599
600 static int
601 open_wim_file(const tchar *filename, struct filedes *fd_ret)
602 {
603         int raw_fd;
604
605         raw_fd = topen(filename, O_RDONLY | O_BINARY);
606         if (raw_fd < 0) {
607                 ERROR_WITH_ERRNO("Can't open \"%"TS"\" read-only", filename);
608                 return WIMLIB_ERR_OPEN;
609         }
610         filedes_init(fd_ret, raw_fd);
611         return 0;
612 }
613
614 /*
615  * Begins the reading of a WIM file; opens the file and reads its header and
616  * blob table, and optionally checks the integrity.
617  */
618 static int
619 begin_read(WIMStruct *wim, const void *wim_filename_or_fd, int open_flags)
620 {
621         int ret;
622         int xml_num_images;
623         const tchar *wimfile;
624
625         if (open_flags & WIMLIB_OPEN_FLAG_FROM_PIPE) {
626                 wimfile = NULL;
627                 filedes_init(&wim->in_fd, *(const int*)wim_filename_or_fd);
628                 wim->in_fd.is_pipe = 1;
629         } else {
630                 wimfile = wim_filename_or_fd;
631                 ret = open_wim_file(wimfile, &wim->in_fd);
632                 if (ret)
633                         return ret;
634
635                 /* The absolute path to the WIM is requested so that
636                  * wimlib_overwrite() still works even if the process changes
637                  * its working directory.  This actually happens if a WIM is
638                  * mounted read-write, since the FUSE thread changes directory
639                  * to "/", and it needs to be able to find the WIM file again.
640                  *
641                  * This will break if the full path to the WIM changes in the
642                  * intervening time...
643                  *
644                  * Warning: in Windows native builds, realpath() calls the
645                  * replacement function in win32_replacements.c.
646                  */
647                 wim->filename = realpath(wimfile, NULL);
648                 if (!wim->filename) {
649                         ERROR_WITH_ERRNO("Failed to get full path to file "
650                                          "\"%"TS"\"", wimfile);
651                         if (errno == ENOMEM)
652                                 return WIMLIB_ERR_NOMEM;
653                         else
654                                 return WIMLIB_ERR_NO_FILENAME;
655                 }
656         }
657
658         ret = read_wim_header(wim, &wim->hdr);
659         if (ret)
660                 return ret;
661
662         if (wim->hdr.flags & WIM_HDR_FLAG_WRITE_IN_PROGRESS) {
663                 WARNING("The WIM_HDR_FLAG_WRITE_IN_PROGRESS flag is set in the header of\n"
664                         "          \"%"TS"\".  It may be being changed by another process,\n"
665                         "          or a process may have crashed while writing the WIM.",
666                         wimfile);
667         }
668
669         if (open_flags & WIMLIB_OPEN_FLAG_WRITE_ACCESS) {
670                 ret = can_modify_wim(wim);
671                 if (ret)
672                         return ret;
673         }
674
675         if ((open_flags & WIMLIB_OPEN_FLAG_ERROR_IF_SPLIT) &&
676             (wim->hdr.total_parts != 1))
677                 return WIMLIB_ERR_IS_SPLIT_WIM;
678
679         /* If the boot index is invalid, print a warning and set it to 0 */
680         if (wim->hdr.boot_idx > wim->hdr.image_count) {
681                 WARNING("Ignoring invalid boot index.");
682                 wim->hdr.boot_idx = 0;
683         }
684
685         /* Check and cache the compression type */
686         if (wim->hdr.flags & WIM_HDR_FLAG_COMPRESSION) {
687                 if (wim->hdr.flags & WIM_HDR_FLAG_COMPRESS_LZX) {
688                         wim->compression_type = WIMLIB_COMPRESSION_TYPE_LZX;
689                 } else if (wim->hdr.flags & (WIM_HDR_FLAG_COMPRESS_XPRESS |
690                                              WIM_HDR_FLAG_COMPRESS_XPRESS_2)) {
691                         wim->compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
692                 } else if (wim->hdr.flags & WIM_HDR_FLAG_COMPRESS_LZMS) {
693                         wim->compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
694                 } else {
695                         return WIMLIB_ERR_INVALID_COMPRESSION_TYPE;
696                 }
697         } else {
698                 wim->compression_type = WIMLIB_COMPRESSION_TYPE_NONE;
699         }
700         wim->out_compression_type = wim->compression_type;
701
702         /* Check and cache the chunk size.  */
703         wim->chunk_size = wim->hdr.chunk_size;
704         wim->out_chunk_size = wim->chunk_size;
705         if (!wim_chunk_size_valid(wim->chunk_size, wim->compression_type)) {
706                 ERROR("Invalid chunk size (%"PRIu32" bytes) "
707                       "for compression type %"TS"!", wim->chunk_size,
708                       wimlib_get_compression_type_string(wim->compression_type));
709                 return WIMLIB_ERR_INVALID_CHUNK_SIZE;
710         }
711
712         if (open_flags & WIMLIB_OPEN_FLAG_CHECK_INTEGRITY) {
713                 ret = check_wim_integrity(wim);
714                 if (ret == WIM_INTEGRITY_NONEXISTENT) {
715                         WARNING("\"%"TS"\" does not contain integrity "
716                                 "information.  Skipping integrity check.",
717                                 wimfile);
718                 } else if (ret == WIM_INTEGRITY_NOT_OK) {
719                         return WIMLIB_ERR_INTEGRITY;
720                 } else if (ret != WIM_INTEGRITY_OK) {
721                         return ret;
722                 }
723         }
724
725         if (wim->hdr.image_count != 0 && wim->hdr.part_number == 1) {
726                 wim->image_metadata = new_image_metadata_array(wim->hdr.image_count);
727                 if (!wim->image_metadata)
728                         return WIMLIB_ERR_NOMEM;
729         }
730
731         if (open_flags & WIMLIB_OPEN_FLAG_FROM_PIPE) {
732                 wim->blob_table = new_blob_table(9001);
733                 if (!wim->blob_table)
734                         return WIMLIB_ERR_NOMEM;
735         } else {
736
737                 ret = read_wim_xml_data(wim);
738                 if (ret)
739                         return ret;
740
741                 xml_num_images = wim_info_get_num_images(wim->wim_info);
742                 if (xml_num_images != wim->hdr.image_count) {
743                         ERROR("The WIM's header is inconsistent with its XML data.\n"
744                               "        Please submit a bug report if you believe this "
745                               "WIM file should be considered valid.");
746                         return WIMLIB_ERR_IMAGE_COUNT;
747                 }
748
749                 ret = read_blob_table(wim);
750                 if (ret)
751                         return ret;
752         }
753         return 0;
754 }
755
756 int
757 open_wim_as_WIMStruct(const void *wim_filename_or_fd, int open_flags,
758                       WIMStruct **wim_ret,
759                       wimlib_progress_func_t progfunc, void *progctx)
760 {
761         WIMStruct *wim;
762         int ret;
763
764         ret = wimlib_global_init(WIMLIB_INIT_FLAG_ASSUME_UTF8);
765         if (ret)
766                 return ret;
767
768         wim = new_wim_struct();
769         if (!wim)
770                 return WIMLIB_ERR_NOMEM;
771
772         wim->progfunc = progfunc;
773         wim->progctx = progctx;
774
775         ret = begin_read(wim, wim_filename_or_fd, open_flags);
776         if (ret) {
777                 wimlib_free(wim);
778                 return ret;
779         }
780
781         *wim_ret = wim;
782         return 0;
783 }
784
785 /* API function documented in wimlib.h  */
786 WIMLIBAPI int
787 wimlib_open_wim_with_progress(const tchar *wimfile, int open_flags,
788                               WIMStruct **wim_ret,
789                               wimlib_progress_func_t progfunc, void *progctx)
790 {
791         if (open_flags & ~(WIMLIB_OPEN_FLAG_CHECK_INTEGRITY |
792                            WIMLIB_OPEN_FLAG_ERROR_IF_SPLIT |
793                            WIMLIB_OPEN_FLAG_WRITE_ACCESS))
794                 return WIMLIB_ERR_INVALID_PARAM;
795
796         if (!wimfile || !*wimfile || !wim_ret)
797                 return WIMLIB_ERR_INVALID_PARAM;
798
799         return open_wim_as_WIMStruct(wimfile, open_flags, wim_ret,
800                                      progfunc, progctx);
801 }
802
803 /* API function documented in wimlib.h  */
804 WIMLIBAPI int
805 wimlib_open_wim(const tchar *wimfile, int open_flags, WIMStruct **wim_ret)
806 {
807         return wimlib_open_wim_with_progress(wimfile, open_flags, wim_ret,
808                                              NULL, NULL);
809 }
810
811 /* Checksum all blobs that are unhashed (other than the metadata blobs), merging
812  * them into the blob table as needed.  This is a no-op unless files have been
813  * added to an image in the same WIMStruct.  */
814 int
815 wim_checksum_unhashed_blobs(WIMStruct *wim)
816 {
817         int ret;
818
819         if (!wim_has_metadata(wim))
820                 return 0;
821         for (int i = 0; i < wim->hdr.image_count; i++) {
822                 struct blob_descriptor *blob, *tmp;
823                 struct wim_image_metadata *imd = wim->image_metadata[i];
824                 image_for_each_unhashed_blob_safe(blob, tmp, imd) {
825                         struct blob_descriptor *new_blob;
826                         ret = hash_unhashed_blob(blob, wim->blob_table, &new_blob);
827                         if (ret)
828                                 return ret;
829                         if (new_blob != blob)
830                                 free_blob_descriptor(blob);
831                 }
832         }
833         return 0;
834 }
835
836 /*
837  * can_modify_wim - Check if a given WIM is writeable.  This is only the case if
838  * it meets the following three conditions:
839  *
840  * 1. Write access is allowed to the underlying file (if any) at the filesystem level.
841  * 2. The WIM is not part of a spanned set.
842  * 3. The WIM_HDR_FLAG_READONLY flag is not set in the WIM header.
843  *
844  * Return value is 0 if writable; WIMLIB_ERR_WIM_IS_READONLY otherwise.
845  */
846 int
847 can_modify_wim(WIMStruct *wim)
848 {
849         if (wim->filename) {
850                 if (taccess(wim->filename, W_OK)) {
851                         ERROR_WITH_ERRNO("Can't modify \"%"TS"\"", wim->filename);
852                         return WIMLIB_ERR_WIM_IS_READONLY;
853                 }
854         }
855         if (wim->hdr.total_parts != 1) {
856                 ERROR("Cannot modify \"%"TS"\": is part of a split WIM",
857                       wim->filename);
858                 return WIMLIB_ERR_WIM_IS_READONLY;
859         }
860         if (wim->hdr.flags & WIM_HDR_FLAG_READONLY) {
861                 ERROR("Cannot modify \"%"TS"\": is marked read-only",
862                       wim->filename);
863                 return WIMLIB_ERR_WIM_IS_READONLY;
864         }
865         return 0;
866 }
867
868 /* API function documented in wimlib.h  */
869 WIMLIBAPI void
870 wimlib_free(WIMStruct *wim)
871 {
872         if (!wim)
873                 return;
874
875         while (!list_empty(&wim->subwims)) {
876                 WIMStruct *subwim;
877
878                 subwim = list_entry(wim->subwims.next, WIMStruct, subwim_node);
879                 list_del(&subwim->subwim_node);
880                 wimlib_free(subwim);
881         }
882
883         if (filedes_valid(&wim->in_fd))
884                 filedes_close(&wim->in_fd);
885         if (filedes_valid(&wim->out_fd))
886                 filedes_close(&wim->out_fd);
887
888         free_blob_table(wim->blob_table);
889
890         wimlib_free_decompressor(wim->decompressor);
891
892         FREE(wim->filename);
893         free_wim_info(wim->wim_info);
894         if (wim->image_metadata) {
895                 for (unsigned i = 0; i < wim->hdr.image_count; i++)
896                         put_image_metadata(wim->image_metadata[i], NULL);
897                 FREE(wim->image_metadata);
898         }
899         FREE(wim);
900 }
901
902 static bool
903 test_locale_ctype_utf8(void)
904 {
905 #ifdef __WIN32__
906         return false;
907 #else
908         char *ctype = nl_langinfo(CODESET);
909
910         return (!strstr(ctype, "UTF-8") ||
911                 !strstr(ctype, "UTF8") ||
912                 !strstr(ctype, "utf8") ||
913                 !strstr(ctype, "utf-8"));
914 #endif
915 }
916
917 /* API function documented in wimlib.h  */
918 WIMLIBAPI u32
919 wimlib_get_version(void)
920 {
921         return (WIMLIB_MAJOR_VERSION << 20) |
922                (WIMLIB_MINOR_VERSION << 10) |
923                 WIMLIB_PATCH_VERSION;
924 }
925
926 static bool lib_initialized = false;
927 static pthread_mutex_t lib_initialization_mutex = PTHREAD_MUTEX_INITIALIZER;
928
929 /* API function documented in wimlib.h  */
930 WIMLIBAPI int
931 wimlib_global_init(int init_flags)
932 {
933         int ret = 0;
934
935         if (lib_initialized)
936                 goto out;
937
938         pthread_mutex_lock(&lib_initialization_mutex);
939
940         if (lib_initialized)
941                 goto out_unlock;
942
943 #ifdef ENABLE_ERROR_MESSAGES
944         if (!wimlib_error_file)
945                 wimlib_error_file = stderr;
946 #endif
947
948         ret = WIMLIB_ERR_INVALID_PARAM;
949         if (init_flags & ~(WIMLIB_INIT_FLAG_ASSUME_UTF8 |
950                            WIMLIB_INIT_FLAG_DONT_ACQUIRE_PRIVILEGES |
951                            WIMLIB_INIT_FLAG_STRICT_CAPTURE_PRIVILEGES |
952                            WIMLIB_INIT_FLAG_STRICT_APPLY_PRIVILEGES |
953                            WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE |
954                            WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE))
955                 goto out_unlock;
956
957         ret = WIMLIB_ERR_INVALID_PARAM;
958         if ((init_flags & (WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE |
959                            WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE))
960                         == (WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE |
961                             WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE))
962                 goto out_unlock;
963
964         libxml_global_init();
965         if (!(init_flags & WIMLIB_INIT_FLAG_ASSUME_UTF8)) {
966                 wimlib_mbs_is_utf8 = test_locale_ctype_utf8();
967         #ifdef WITH_NTFS_3G
968                 if (!wimlib_mbs_is_utf8)
969                         libntfs3g_global_init();
970         #endif
971         }
972 #ifdef __WIN32__
973         ret = win32_global_init(init_flags);
974         if (ret)
975                 goto out_unlock;
976 #endif
977         iconv_global_init();
978         init_upcase();
979         if (init_flags & WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE)
980                 default_ignore_case = false;
981         else if (init_flags & WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE)
982                 default_ignore_case = true;
983         lib_initialized = true;
984         ret = 0;
985 out_unlock:
986         pthread_mutex_unlock(&lib_initialization_mutex);
987 out:
988         return ret;
989 }
990
991 /* API function documented in wimlib.h  */
992 WIMLIBAPI void
993 wimlib_global_cleanup(void)
994 {
995         if (!lib_initialized)
996                 return;
997
998         pthread_mutex_lock(&lib_initialization_mutex);
999
1000         if (!lib_initialized)
1001                 goto out_unlock;
1002
1003         libxml_global_cleanup();
1004         iconv_global_cleanup();
1005 #ifdef __WIN32__
1006         win32_global_cleanup();
1007 #endif
1008
1009         wimlib_set_error_file(NULL);
1010         lib_initialized = false;
1011
1012 out_unlock:
1013         pthread_mutex_unlock(&lib_initialization_mutex);
1014 }