]> wimlib.net Git - wimlib/blob - src/wim.c
e77d71704ece213305bf3b45e878793692e19813
[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->xml_info = xml_new_info_struct();
180         wim->blob_table = new_blob_table(9001);
181         if (!wim->xml_info || !wim->blob_table) {
182                 wimlib_free(wim);
183                 return WIMLIB_ERR_NOMEM;
184         }
185
186         /* Fill in wim->hdr with default values  */
187         wim->hdr.magic = WIM_MAGIC;
188         wim->hdr.wim_version = WIM_VERSION_DEFAULT;
189         wim->hdr.flags = 0;
190         wim->hdr.chunk_size = 0;
191         generate_guid(wim->hdr.guid);
192         wim->hdr.part_number = 1;
193         wim->hdr.total_parts = 1;
194         wim->hdr.image_count = 0;
195         wim->hdr.boot_idx = 0;
196
197         wim->compression_type = WIMLIB_COMPRESSION_TYPE_NONE;
198         wim->chunk_size = wim->hdr.chunk_size;
199
200         /* Set the output compression type  */
201         wim->out_compression_type = ctype;
202         wim->out_chunk_size = wim_default_nonsolid_chunk_size(ctype);
203
204         *wim_ret = wim;
205         return 0;
206 }
207
208 static void
209 destroy_image_metadata(struct wim_image_metadata *imd,
210                        struct blob_table *table,
211                        bool free_metadata_blob_descriptor)
212 {
213         free_dentry_tree(imd->root_dentry, table);
214         imd->root_dentry = NULL;
215         free_wim_security_data(imd->security_data);
216         imd->security_data = NULL;
217
218         if (free_metadata_blob_descriptor) {
219                 free_blob_descriptor(imd->metadata_blob);
220                 imd->metadata_blob = NULL;
221         }
222         if (!table) {
223                 struct blob_descriptor *blob, *tmp;
224                 list_for_each_entry_safe(blob, tmp, &imd->unhashed_blobs, unhashed_list)
225                         free_blob_descriptor(blob);
226         }
227         INIT_LIST_HEAD(&imd->unhashed_blobs);
228         INIT_HLIST_HEAD(&imd->inode_list);
229 }
230
231 void
232 put_image_metadata(struct wim_image_metadata *imd, struct blob_table *table)
233 {
234         if (imd && --imd->refcnt == 0) {
235                 destroy_image_metadata(imd, table, true);
236                 FREE(imd);
237         }
238 }
239
240 /* Appends the specified image metadata structure to the array of image metadata
241  * for a WIM, and increments the image count. */
242 int
243 append_image_metadata(WIMStruct *wim, struct wim_image_metadata *imd)
244 {
245         struct wim_image_metadata **imd_array;
246
247         imd_array = REALLOC(wim->image_metadata,
248                             sizeof(wim->image_metadata[0]) * (wim->hdr.image_count + 1));
249
250         if (!imd_array)
251                 return WIMLIB_ERR_NOMEM;
252         wim->image_metadata = imd_array;
253         imd_array[wim->hdr.image_count++] = imd;
254         return 0;
255 }
256
257 struct wim_image_metadata *
258 new_image_metadata(void)
259 {
260         struct wim_image_metadata *imd;
261
262         imd = CALLOC(1, sizeof(*imd));
263         if (imd) {
264                 imd->refcnt = 1;
265                 INIT_HLIST_HEAD(&imd->inode_list);
266                 INIT_LIST_HEAD(&imd->unhashed_blobs);
267         }
268         return imd;
269 }
270
271 static struct wim_image_metadata **
272 new_image_metadata_array(unsigned num_images)
273 {
274         struct wim_image_metadata **imd_array;
275
276         imd_array = CALLOC(num_images, sizeof(imd_array[0]));
277
278         if (!imd_array)
279                 return NULL;
280         for (unsigned i = 0; i < num_images; i++) {
281                 imd_array[i] = new_image_metadata();
282                 if (unlikely(!imd_array[i])) {
283                         for (unsigned j = 0; j < i; j++)
284                                 put_image_metadata(imd_array[j], NULL);
285                         FREE(imd_array);
286                         return NULL;
287                 }
288         }
289         return imd_array;
290 }
291
292
293 /*
294  * Load the metadata for the specified WIM image into memory and set it
295  * as the WIMStruct's currently selected image.
296  *
297  * @wim
298  *      The WIMStruct for the WIM.
299  * @image
300  *      The 1-based index of the image in the WIM to select.
301  *
302  * On success, 0 will be returned, wim->current_image will be set to
303  * @image, and wim_get_current_image_metadata() can be used to retrieve
304  * metadata information for the image.
305  *
306  * On failure, WIMLIB_ERR_INVALID_IMAGE, WIMLIB_ERR_METADATA_NOT_FOUND,
307  * or another error code will be returned.
308  */
309 int
310 select_wim_image(WIMStruct *wim, int image)
311 {
312         struct wim_image_metadata *imd;
313         int ret;
314
315         if (image == WIMLIB_NO_IMAGE)
316                 return WIMLIB_ERR_INVALID_IMAGE;
317
318         if (image == wim->current_image)
319                 return 0;
320
321         if (image < 1 || image > wim->hdr.image_count)
322                 return WIMLIB_ERR_INVALID_IMAGE;
323
324         if (!wim_has_metadata(wim))
325                 return WIMLIB_ERR_METADATA_NOT_FOUND;
326
327         /* If a valid image is currently selected, its metadata can be freed if
328          * it is not dirty and no other WIMStructs may have it selected.  */
329         deselect_current_wim_image(wim);
330         wim->current_image = image;
331         imd = wim_get_current_image_metadata(wim);
332         if (imd->root_dentry || is_image_dirty(imd)) {
333                 ret = 0;
334         } else {
335                 ret = read_metadata_resource(imd);
336                 if (ret)
337                         wim->current_image = WIMLIB_NO_IMAGE;
338         }
339         return ret;
340 }
341
342 void
343 deselect_current_wim_image(WIMStruct *wim)
344 {
345         struct wim_image_metadata *imd;
346         if (wim->current_image == WIMLIB_NO_IMAGE)
347                 return;
348         imd = wim_get_current_image_metadata(wim);
349         if (!is_image_dirty(imd) && imd->refcnt == 1) {
350                 wimlib_assert(list_empty(&imd->unhashed_blobs));
351                 destroy_image_metadata(imd, NULL, false);
352         }
353         wim->current_image = WIMLIB_NO_IMAGE;
354 }
355
356 /*
357  * Calls a function on images in the WIM.  If @image is WIMLIB_ALL_IMAGES,
358  * @visitor is called on the WIM once for each image, with each image selected
359  * as the current image in turn.  If @image is a certain image, @visitor is
360  * called on the WIM only once, with that image selected.
361  */
362 int
363 for_image(WIMStruct *wim, int image, int (*visitor)(WIMStruct *))
364 {
365         int ret;
366         int start;
367         int end;
368         int i;
369
370         if (image == WIMLIB_ALL_IMAGES) {
371                 start = 1;
372                 end = wim->hdr.image_count;
373         } else if (image >= 1 && image <= wim->hdr.image_count) {
374                 start = image;
375                 end = image;
376         } else {
377                 return WIMLIB_ERR_INVALID_IMAGE;
378         }
379         for (i = start; i <= end; i++) {
380                 ret = select_wim_image(wim, i);
381                 if (ret != 0)
382                         return ret;
383                 ret = visitor(wim);
384                 if (ret != 0)
385                         return ret;
386         }
387         return 0;
388 }
389
390 /* API function documented in wimlib.h  */
391 WIMLIBAPI int
392 wimlib_resolve_image(WIMStruct *wim, const tchar *image_name_or_num)
393 {
394         tchar *p;
395         long image;
396         int i;
397
398         if (!image_name_or_num || !*image_name_or_num)
399                 return WIMLIB_NO_IMAGE;
400
401         if (!tstrcasecmp(image_name_or_num, T("all"))
402             || !tstrcasecmp(image_name_or_num, T("*")))
403                 return WIMLIB_ALL_IMAGES;
404         image = tstrtol(image_name_or_num, &p, 10);
405         if (p != image_name_or_num && *p == T('\0') && image > 0) {
406                 if (image > wim->hdr.image_count)
407                         return WIMLIB_NO_IMAGE;
408                 return image;
409         } else {
410                 for (i = 1; i <= wim->hdr.image_count; i++) {
411                         if (!tstrcmp(image_name_or_num,
412                                      wimlib_get_image_name(wim, i)))
413                                 return i;
414                 }
415                 return WIMLIB_NO_IMAGE;
416         }
417 }
418
419 /* API function documented in wimlib.h  */
420 WIMLIBAPI void
421 wimlib_print_available_images(const WIMStruct *wim, int image)
422 {
423         int first;
424         int last;
425         int i;
426         int n;
427         if (image == WIMLIB_ALL_IMAGES) {
428                 n = tprintf(T("Available Images:\n"));
429                 first = 1;
430                 last = wim->hdr.image_count;
431         } else if (image >= 1 && image <= wim->hdr.image_count) {
432                 n = tprintf(T("Information for Image %d\n"), image);
433                 first = image;
434                 last = image;
435         } else {
436                 tprintf(T("wimlib_print_available_images(): Invalid image %d"),
437                         image);
438                 return;
439         }
440         for (i = 0; i < n - 1; i++)
441                 tputchar(T('-'));
442         tputchar(T('\n'));
443         for (i = first; i <= last; i++)
444                 xml_print_image_info(wim->xml_info, i);
445 }
446
447 /* API function documented in wimlib.h  */
448 WIMLIBAPI int
449 wimlib_get_wim_info(WIMStruct *wim, struct wimlib_wim_info *info)
450 {
451         memset(info, 0, sizeof(struct wimlib_wim_info));
452         copy_guid(info->guid, wim->hdr.guid);
453         info->image_count = wim->hdr.image_count;
454         info->boot_index = wim->hdr.boot_idx;
455         info->wim_version = wim->hdr.wim_version;
456         info->chunk_size = wim->chunk_size;
457         info->part_number = wim->hdr.part_number;
458         info->total_parts = wim->hdr.total_parts;
459         info->compression_type = wim->compression_type;
460         info->total_bytes = xml_get_total_bytes(wim->xml_info);
461         info->has_integrity_table = wim_has_integrity_table(wim);
462         info->opened_from_file = (wim->filename != NULL);
463         info->is_readonly = (wim->hdr.flags & WIM_HDR_FLAG_READONLY) ||
464                              (wim->hdr.total_parts != 1) ||
465                              (wim->filename && taccess(wim->filename, W_OK));
466         info->has_rpfix = (wim->hdr.flags & WIM_HDR_FLAG_RP_FIX) != 0;
467         info->is_marked_readonly = (wim->hdr.flags & WIM_HDR_FLAG_READONLY) != 0;
468         info->write_in_progress = (wim->hdr.flags & WIM_HDR_FLAG_WRITE_IN_PROGRESS) != 0;
469         info->metadata_only = (wim->hdr.flags & WIM_HDR_FLAG_METADATA_ONLY) != 0;
470         info->resource_only = (wim->hdr.flags & WIM_HDR_FLAG_RESOURCE_ONLY) != 0;
471         info->spanned = (wim->hdr.flags & WIM_HDR_FLAG_SPANNED) != 0;
472         info->pipable = wim_is_pipable(wim);
473         return 0;
474 }
475
476 /* API function documented in wimlib.h  */
477 WIMLIBAPI int
478 wimlib_set_wim_info(WIMStruct *wim, const struct wimlib_wim_info *info, int which)
479 {
480         if (which & ~(WIMLIB_CHANGE_READONLY_FLAG |
481                       WIMLIB_CHANGE_GUID |
482                       WIMLIB_CHANGE_BOOT_INDEX |
483                       WIMLIB_CHANGE_RPFIX_FLAG))
484                 return WIMLIB_ERR_INVALID_PARAM;
485
486         if ((which & WIMLIB_CHANGE_BOOT_INDEX) &&
487             info->boot_index > wim->hdr.image_count)
488                 return WIMLIB_ERR_INVALID_IMAGE;
489
490         if (which & WIMLIB_CHANGE_READONLY_FLAG) {
491                 if (info->is_marked_readonly)
492                         wim->hdr.flags |= WIM_HDR_FLAG_READONLY;
493                 else
494                         wim->hdr.flags &= ~WIM_HDR_FLAG_READONLY;
495         }
496
497         if (which & WIMLIB_CHANGE_GUID)
498                 copy_guid(wim->hdr.guid, info->guid);
499
500         if (which & WIMLIB_CHANGE_BOOT_INDEX)
501                 wim->hdr.boot_idx = info->boot_index;
502
503         if (which & WIMLIB_CHANGE_RPFIX_FLAG) {
504                 if (info->has_rpfix)
505                         wim->hdr.flags |= WIM_HDR_FLAG_RP_FIX;
506                 else
507                         wim->hdr.flags &= ~WIM_HDR_FLAG_RP_FIX;
508         }
509         return 0;
510 }
511
512 /* API function documented in wimlib.h  */
513 WIMLIBAPI int
514 wimlib_set_output_compression_type(WIMStruct *wim,
515                                    enum wimlib_compression_type ctype)
516 {
517         if (!wim_compression_type_valid(ctype))
518                 return WIMLIB_ERR_INVALID_COMPRESSION_TYPE;
519
520         wim->out_compression_type = ctype;
521
522         /* Reset the chunk size if it's no longer valid.  */
523         if (!wim_chunk_size_valid(wim->out_chunk_size, ctype))
524                 wim->out_chunk_size = wim_default_nonsolid_chunk_size(ctype);
525         return 0;
526 }
527
528 /* API function documented in wimlib.h  */
529 WIMLIBAPI int
530 wimlib_set_output_pack_compression_type(WIMStruct *wim,
531                                         enum wimlib_compression_type ctype)
532 {
533         if (!wim_compression_type_valid(ctype))
534                 return WIMLIB_ERR_INVALID_COMPRESSION_TYPE;
535
536         /* Solid resources can't be uncompressed.  */
537         if (ctype == WIMLIB_COMPRESSION_TYPE_NONE)
538                 return WIMLIB_ERR_INVALID_COMPRESSION_TYPE;
539
540         wim->out_solid_compression_type = ctype;
541
542         /* Reset the chunk size if it's no longer valid.  */
543         if (!wim_chunk_size_valid(wim->out_solid_chunk_size, ctype))
544                 wim->out_solid_chunk_size = wim_default_solid_chunk_size(ctype);
545         return 0;
546 }
547
548 /* API function documented in wimlib.h  */
549 WIMLIBAPI int
550 wimlib_set_output_chunk_size(WIMStruct *wim, u32 chunk_size)
551 {
552         if (chunk_size == 0) {
553                 wim->out_chunk_size =
554                         wim_default_nonsolid_chunk_size(wim->out_compression_type);
555                 return 0;
556         }
557
558         if (!wim_chunk_size_valid(chunk_size, wim->out_compression_type))
559                 return WIMLIB_ERR_INVALID_CHUNK_SIZE;
560
561         wim->out_chunk_size = chunk_size;
562         return 0;
563 }
564
565 /* API function documented in wimlib.h  */
566 WIMLIBAPI int
567 wimlib_set_output_pack_chunk_size(WIMStruct *wim, u32 chunk_size)
568 {
569         if (chunk_size == 0) {
570                 wim->out_solid_chunk_size =
571                         wim_default_solid_chunk_size(wim->out_solid_compression_type);
572                 return 0;
573         }
574
575         if (!wim_chunk_size_valid(chunk_size, wim->out_solid_compression_type))
576                 return WIMLIB_ERR_INVALID_CHUNK_SIZE;
577
578         wim->out_solid_chunk_size = chunk_size;
579         return 0;
580 }
581
582 /* API function documented in wimlib.h  */
583 WIMLIBAPI const tchar *
584 wimlib_get_compression_type_string(enum wimlib_compression_type ctype)
585 {
586         if (!wim_compression_type_valid(ctype))
587                 return T("Invalid");
588
589         return wim_ctype_info[(unsigned)ctype].name;
590 }
591
592 WIMLIBAPI void
593 wimlib_register_progress_function(WIMStruct *wim,
594                                   wimlib_progress_func_t progfunc,
595                                   void *progctx)
596 {
597         wim->progfunc = progfunc;
598         wim->progctx = progctx;
599 }
600
601 static int
602 open_wim_file(const tchar *filename, struct filedes *fd_ret)
603 {
604         int raw_fd;
605
606         raw_fd = topen(filename, O_RDONLY | O_BINARY);
607         if (raw_fd < 0) {
608                 ERROR_WITH_ERRNO("Can't open \"%"TS"\" read-only", filename);
609                 return WIMLIB_ERR_OPEN;
610         }
611         filedes_init(fd_ret, raw_fd);
612         return 0;
613 }
614
615 /*
616  * Begins the reading of a WIM file; opens the file and reads its header and
617  * blob table, and optionally checks the integrity.
618  */
619 static int
620 begin_read(WIMStruct *wim, const void *wim_filename_or_fd, int open_flags)
621 {
622         int ret;
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                 if (wim->hdr.blob_table_reshdr.uncompressed_size == 0 &&
737                     wim->hdr.xml_data_reshdr.uncompressed_size == 0)
738                         return WIMLIB_ERR_WIM_IS_INCOMPLETE;
739
740                 ret = read_wim_xml_data(wim);
741                 if (ret)
742                         return ret;
743
744                 if (xml_get_image_count(wim->xml_info) != wim->hdr.image_count) {
745                         ERROR("The WIM's header is inconsistent with its XML data.\n"
746                               "        Please submit a bug report if you believe this "
747                               "WIM file should be considered valid.");
748                         return WIMLIB_ERR_IMAGE_COUNT;
749                 }
750
751                 ret = read_blob_table(wim);
752                 if (ret)
753                         return ret;
754         }
755         return 0;
756 }
757
758 int
759 open_wim_as_WIMStruct(const void *wim_filename_or_fd, int open_flags,
760                       WIMStruct **wim_ret,
761                       wimlib_progress_func_t progfunc, void *progctx)
762 {
763         WIMStruct *wim;
764         int ret;
765
766         ret = wimlib_global_init(WIMLIB_INIT_FLAG_ASSUME_UTF8);
767         if (ret)
768                 return ret;
769
770         wim = new_wim_struct();
771         if (!wim)
772                 return WIMLIB_ERR_NOMEM;
773
774         wim->progfunc = progfunc;
775         wim->progctx = progctx;
776
777         ret = begin_read(wim, wim_filename_or_fd, open_flags);
778         if (ret) {
779                 wimlib_free(wim);
780                 return ret;
781         }
782
783         *wim_ret = wim;
784         return 0;
785 }
786
787 /* API function documented in wimlib.h  */
788 WIMLIBAPI int
789 wimlib_open_wim_with_progress(const tchar *wimfile, int open_flags,
790                               WIMStruct **wim_ret,
791                               wimlib_progress_func_t progfunc, void *progctx)
792 {
793         if (open_flags & ~(WIMLIB_OPEN_FLAG_CHECK_INTEGRITY |
794                            WIMLIB_OPEN_FLAG_ERROR_IF_SPLIT |
795                            WIMLIB_OPEN_FLAG_WRITE_ACCESS))
796                 return WIMLIB_ERR_INVALID_PARAM;
797
798         if (!wimfile || !*wimfile || !wim_ret)
799                 return WIMLIB_ERR_INVALID_PARAM;
800
801         return open_wim_as_WIMStruct(wimfile, open_flags, wim_ret,
802                                      progfunc, progctx);
803 }
804
805 /* API function documented in wimlib.h  */
806 WIMLIBAPI int
807 wimlib_open_wim(const tchar *wimfile, int open_flags, WIMStruct **wim_ret)
808 {
809         return wimlib_open_wim_with_progress(wimfile, open_flags, wim_ret,
810                                              NULL, NULL);
811 }
812
813 /* Checksum all blobs that are unhashed (other than the metadata blobs), merging
814  * them into the blob table as needed.  This is a no-op unless files have been
815  * added to an image in the same WIMStruct.  */
816 int
817 wim_checksum_unhashed_blobs(WIMStruct *wim)
818 {
819         int ret;
820
821         if (!wim_has_metadata(wim))
822                 return 0;
823         for (int i = 0; i < wim->hdr.image_count; i++) {
824                 struct blob_descriptor *blob, *tmp;
825                 struct wim_image_metadata *imd = wim->image_metadata[i];
826                 image_for_each_unhashed_blob_safe(blob, tmp, imd) {
827                         struct blob_descriptor *new_blob;
828                         ret = hash_unhashed_blob(blob, wim->blob_table, &new_blob);
829                         if (ret)
830                                 return ret;
831                         if (new_blob != blob)
832                                 free_blob_descriptor(blob);
833                 }
834         }
835         return 0;
836 }
837
838 /*
839  * can_modify_wim - Check if a given WIM is writeable.  This is only the case if
840  * it meets the following three conditions:
841  *
842  * 1. Write access is allowed to the underlying file (if any) at the filesystem level.
843  * 2. The WIM is not part of a spanned set.
844  * 3. The WIM_HDR_FLAG_READONLY flag is not set in the WIM header.
845  *
846  * Return value is 0 if writable; WIMLIB_ERR_WIM_IS_READONLY otherwise.
847  */
848 int
849 can_modify_wim(WIMStruct *wim)
850 {
851         if (wim->filename) {
852                 if (taccess(wim->filename, W_OK)) {
853                         ERROR_WITH_ERRNO("Can't modify \"%"TS"\"", wim->filename);
854                         return WIMLIB_ERR_WIM_IS_READONLY;
855                 }
856         }
857         if (wim->hdr.total_parts != 1) {
858                 ERROR("Cannot modify \"%"TS"\": is part of a split WIM",
859                       wim->filename);
860                 return WIMLIB_ERR_WIM_IS_READONLY;
861         }
862         if (wim->hdr.flags & WIM_HDR_FLAG_READONLY) {
863                 ERROR("Cannot modify \"%"TS"\": is marked read-only",
864                       wim->filename);
865                 return WIMLIB_ERR_WIM_IS_READONLY;
866         }
867         return 0;
868 }
869
870 /* API function documented in wimlib.h  */
871 WIMLIBAPI void
872 wimlib_free(WIMStruct *wim)
873 {
874         if (!wim)
875                 return;
876
877         while (!list_empty(&wim->subwims)) {
878                 WIMStruct *subwim;
879
880                 subwim = list_entry(wim->subwims.next, WIMStruct, subwim_node);
881                 list_del(&subwim->subwim_node);
882                 wimlib_free(subwim);
883         }
884
885         if (filedes_valid(&wim->in_fd))
886                 filedes_close(&wim->in_fd);
887         if (filedes_valid(&wim->out_fd))
888                 filedes_close(&wim->out_fd);
889
890         free_blob_table(wim->blob_table);
891
892         wimlib_free_decompressor(wim->decompressor);
893
894         FREE(wim->filename);
895         xml_free_info_struct(wim->xml_info);
896         if (wim->image_metadata) {
897                 for (unsigned i = 0; i < wim->hdr.image_count; i++)
898                         put_image_metadata(wim->image_metadata[i], NULL);
899                 FREE(wim->image_metadata);
900         }
901         FREE(wim);
902 }
903
904 static bool
905 test_locale_ctype_utf8(void)
906 {
907 #ifdef __WIN32__
908         return false;
909 #else
910         char *ctype = nl_langinfo(CODESET);
911
912         return (!strstr(ctype, "UTF-8") ||
913                 !strstr(ctype, "UTF8") ||
914                 !strstr(ctype, "utf8") ||
915                 !strstr(ctype, "utf-8"));
916 #endif
917 }
918
919 /* API function documented in wimlib.h  */
920 WIMLIBAPI u32
921 wimlib_get_version(void)
922 {
923         return (WIMLIB_MAJOR_VERSION << 20) |
924                (WIMLIB_MINOR_VERSION << 10) |
925                 WIMLIB_PATCH_VERSION;
926 }
927
928 static bool lib_initialized = false;
929 static pthread_mutex_t lib_initialization_mutex = PTHREAD_MUTEX_INITIALIZER;
930
931 /* API function documented in wimlib.h  */
932 WIMLIBAPI int
933 wimlib_global_init(int init_flags)
934 {
935         int ret = 0;
936
937         if (lib_initialized)
938                 goto out;
939
940         pthread_mutex_lock(&lib_initialization_mutex);
941
942         if (lib_initialized)
943                 goto out_unlock;
944
945 #ifdef ENABLE_ERROR_MESSAGES
946         if (!wimlib_error_file)
947                 wimlib_error_file = stderr;
948 #endif
949
950         ret = WIMLIB_ERR_INVALID_PARAM;
951         if (init_flags & ~(WIMLIB_INIT_FLAG_ASSUME_UTF8 |
952                            WIMLIB_INIT_FLAG_DONT_ACQUIRE_PRIVILEGES |
953                            WIMLIB_INIT_FLAG_STRICT_CAPTURE_PRIVILEGES |
954                            WIMLIB_INIT_FLAG_STRICT_APPLY_PRIVILEGES |
955                            WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE |
956                            WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE))
957                 goto out_unlock;
958
959         ret = WIMLIB_ERR_INVALID_PARAM;
960         if ((init_flags & (WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE |
961                            WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE))
962                         == (WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE |
963                             WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE))
964                 goto out_unlock;
965
966         xml_global_init();
967         if (!(init_flags & WIMLIB_INIT_FLAG_ASSUME_UTF8)) {
968                 wimlib_mbs_is_utf8 = test_locale_ctype_utf8();
969         #ifdef WITH_NTFS_3G
970                 if (!wimlib_mbs_is_utf8)
971                         libntfs3g_global_init();
972         #endif
973         }
974 #ifdef __WIN32__
975         ret = win32_global_init(init_flags);
976         if (ret)
977                 goto out_unlock;
978 #endif
979         iconv_global_init();
980         init_upcase();
981         if (init_flags & WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE)
982                 default_ignore_case = false;
983         else if (init_flags & WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE)
984                 default_ignore_case = true;
985         lib_initialized = true;
986         ret = 0;
987 out_unlock:
988         pthread_mutex_unlock(&lib_initialization_mutex);
989 out:
990         return ret;
991 }
992
993 /* API function documented in wimlib.h  */
994 WIMLIBAPI void
995 wimlib_global_cleanup(void)
996 {
997         if (!lib_initialized)
998                 return;
999
1000         pthread_mutex_lock(&lib_initialization_mutex);
1001
1002         if (!lib_initialized)
1003                 goto out_unlock;
1004
1005         xml_global_cleanup();
1006         iconv_global_cleanup();
1007 #ifdef __WIN32__
1008         win32_global_cleanup();
1009 #endif
1010
1011         wimlib_set_error_file(NULL);
1012         lib_initialized = false;
1013
1014 out_unlock:
1015         pthread_mutex_unlock(&lib_initialization_mutex);
1016 }