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