9b5e4b24856d8b70deec0bfc50059158ec0c6e0f
[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 has not been modified.  */
329         deselect_current_wim_image(wim);
330         wim->current_image = image;
331         imd = wim_get_current_image_metadata(wim);
332         if (imd->root_dentry || imd->modified) {
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 (!imd->modified) {
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
737                 ret = read_wim_xml_data(wim);
738                 if (ret)
739                         return ret;
740
741                 if (xml_get_image_count(wim->xml_info) != wim->hdr.image_count) {
742                         ERROR("The WIM's header is inconsistent with its XML data.\n"
743                               "        Please submit a bug report if you believe this "
744                               "WIM file should be considered valid.");
745                         return WIMLIB_ERR_IMAGE_COUNT;
746                 }
747
748                 ret = read_blob_table(wim);
749                 if (ret)
750                         return ret;
751         }
752         return 0;
753 }
754
755 int
756 open_wim_as_WIMStruct(const void *wim_filename_or_fd, int open_flags,
757                       WIMStruct **wim_ret,
758                       wimlib_progress_func_t progfunc, void *progctx)
759 {
760         WIMStruct *wim;
761         int ret;
762
763         ret = wimlib_global_init(WIMLIB_INIT_FLAG_ASSUME_UTF8);
764         if (ret)
765                 return ret;
766
767         wim = new_wim_struct();
768         if (!wim)
769                 return WIMLIB_ERR_NOMEM;
770
771         wim->progfunc = progfunc;
772         wim->progctx = progctx;
773
774         ret = begin_read(wim, wim_filename_or_fd, open_flags);
775         if (ret) {
776                 wimlib_free(wim);
777                 return ret;
778         }
779
780         *wim_ret = wim;
781         return 0;
782 }
783
784 /* API function documented in wimlib.h  */
785 WIMLIBAPI int
786 wimlib_open_wim_with_progress(const tchar *wimfile, int open_flags,
787                               WIMStruct **wim_ret,
788                               wimlib_progress_func_t progfunc, void *progctx)
789 {
790         if (open_flags & ~(WIMLIB_OPEN_FLAG_CHECK_INTEGRITY |
791                            WIMLIB_OPEN_FLAG_ERROR_IF_SPLIT |
792                            WIMLIB_OPEN_FLAG_WRITE_ACCESS))
793                 return WIMLIB_ERR_INVALID_PARAM;
794
795         if (!wimfile || !*wimfile || !wim_ret)
796                 return WIMLIB_ERR_INVALID_PARAM;
797
798         return open_wim_as_WIMStruct(wimfile, open_flags, wim_ret,
799                                      progfunc, progctx);
800 }
801
802 /* API function documented in wimlib.h  */
803 WIMLIBAPI int
804 wimlib_open_wim(const tchar *wimfile, int open_flags, WIMStruct **wim_ret)
805 {
806         return wimlib_open_wim_with_progress(wimfile, open_flags, wim_ret,
807                                              NULL, NULL);
808 }
809
810 /* Checksum all blobs that are unhashed (other than the metadata blobs), merging
811  * them into the blob table as needed.  This is a no-op unless files have been
812  * added to an image in the same WIMStruct.  */
813 int
814 wim_checksum_unhashed_blobs(WIMStruct *wim)
815 {
816         int ret;
817
818         if (!wim_has_metadata(wim))
819                 return 0;
820         for (int i = 0; i < wim->hdr.image_count; i++) {
821                 struct blob_descriptor *blob, *tmp;
822                 struct wim_image_metadata *imd = wim->image_metadata[i];
823                 image_for_each_unhashed_blob_safe(blob, tmp, imd) {
824                         struct blob_descriptor *new_blob;
825                         ret = hash_unhashed_blob(blob, wim->blob_table, &new_blob);
826                         if (ret)
827                                 return ret;
828                         if (new_blob != blob)
829                                 free_blob_descriptor(blob);
830                 }
831         }
832         return 0;
833 }
834
835 /*
836  * can_modify_wim - Check if a given WIM is writeable.  This is only the case if
837  * it meets the following three conditions:
838  *
839  * 1. Write access is allowed to the underlying file (if any) at the filesystem level.
840  * 2. The WIM is not part of a spanned set.
841  * 3. The WIM_HDR_FLAG_READONLY flag is not set in the WIM header.
842  *
843  * Return value is 0 if writable; WIMLIB_ERR_WIM_IS_READONLY otherwise.
844  */
845 int
846 can_modify_wim(WIMStruct *wim)
847 {
848         if (wim->filename) {
849                 if (taccess(wim->filename, W_OK)) {
850                         ERROR_WITH_ERRNO("Can't modify \"%"TS"\"", wim->filename);
851                         return WIMLIB_ERR_WIM_IS_READONLY;
852                 }
853         }
854         if (wim->hdr.total_parts != 1) {
855                 ERROR("Cannot modify \"%"TS"\": is part of a split WIM",
856                       wim->filename);
857                 return WIMLIB_ERR_WIM_IS_READONLY;
858         }
859         if (wim->hdr.flags & WIM_HDR_FLAG_READONLY) {
860                 ERROR("Cannot modify \"%"TS"\": is marked read-only",
861                       wim->filename);
862                 return WIMLIB_ERR_WIM_IS_READONLY;
863         }
864         return 0;
865 }
866
867 /* API function documented in wimlib.h  */
868 WIMLIBAPI void
869 wimlib_free(WIMStruct *wim)
870 {
871         if (!wim)
872                 return;
873
874         while (!list_empty(&wim->subwims)) {
875                 WIMStruct *subwim;
876
877                 subwim = list_entry(wim->subwims.next, WIMStruct, subwim_node);
878                 list_del(&subwim->subwim_node);
879                 wimlib_free(subwim);
880         }
881
882         if (filedes_valid(&wim->in_fd))
883                 filedes_close(&wim->in_fd);
884         if (filedes_valid(&wim->out_fd))
885                 filedes_close(&wim->out_fd);
886
887         free_blob_table(wim->blob_table);
888
889         wimlib_free_decompressor(wim->decompressor);
890
891         FREE(wim->filename);
892         xml_free_info_struct(wim->xml_info);
893         if (wim->image_metadata) {
894                 for (unsigned i = 0; i < wim->hdr.image_count; i++)
895                         put_image_metadata(wim->image_metadata[i], NULL);
896                 FREE(wim->image_metadata);
897         }
898         FREE(wim);
899 }
900
901 static bool
902 test_locale_ctype_utf8(void)
903 {
904 #ifdef __WIN32__
905         return false;
906 #else
907         char *ctype = nl_langinfo(CODESET);
908
909         return (!strstr(ctype, "UTF-8") ||
910                 !strstr(ctype, "UTF8") ||
911                 !strstr(ctype, "utf8") ||
912                 !strstr(ctype, "utf-8"));
913 #endif
914 }
915
916 /* API function documented in wimlib.h  */
917 WIMLIBAPI u32
918 wimlib_get_version(void)
919 {
920         return (WIMLIB_MAJOR_VERSION << 20) |
921                (WIMLIB_MINOR_VERSION << 10) |
922                 WIMLIB_PATCH_VERSION;
923 }
924
925 static bool lib_initialized = false;
926 static pthread_mutex_t lib_initialization_mutex = PTHREAD_MUTEX_INITIALIZER;
927
928 /* API function documented in wimlib.h  */
929 WIMLIBAPI int
930 wimlib_global_init(int init_flags)
931 {
932         int ret = 0;
933
934         if (lib_initialized)
935                 goto out;
936
937         pthread_mutex_lock(&lib_initialization_mutex);
938
939         if (lib_initialized)
940                 goto out_unlock;
941
942 #ifdef ENABLE_ERROR_MESSAGES
943         if (!wimlib_error_file)
944                 wimlib_error_file = stderr;
945 #endif
946
947         ret = WIMLIB_ERR_INVALID_PARAM;
948         if (init_flags & ~(WIMLIB_INIT_FLAG_ASSUME_UTF8 |
949                            WIMLIB_INIT_FLAG_DONT_ACQUIRE_PRIVILEGES |
950                            WIMLIB_INIT_FLAG_STRICT_CAPTURE_PRIVILEGES |
951                            WIMLIB_INIT_FLAG_STRICT_APPLY_PRIVILEGES |
952                            WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE |
953                            WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE))
954                 goto out_unlock;
955
956         ret = WIMLIB_ERR_INVALID_PARAM;
957         if ((init_flags & (WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE |
958                            WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE))
959                         == (WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE |
960                             WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE))
961                 goto out_unlock;
962
963         xml_global_init();
964         if (!(init_flags & WIMLIB_INIT_FLAG_ASSUME_UTF8)) {
965                 wimlib_mbs_is_utf8 = test_locale_ctype_utf8();
966         #ifdef WITH_NTFS_3G
967                 if (!wimlib_mbs_is_utf8)
968                         libntfs3g_global_init();
969         #endif
970         }
971 #ifdef __WIN32__
972         ret = win32_global_init(init_flags);
973         if (ret)
974                 goto out_unlock;
975 #endif
976         iconv_global_init();
977         init_upcase();
978         if (init_flags & WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE)
979                 default_ignore_case = false;
980         else if (init_flags & WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE)
981                 default_ignore_case = true;
982         lib_initialized = true;
983         ret = 0;
984 out_unlock:
985         pthread_mutex_unlock(&lib_initialization_mutex);
986 out:
987         return ret;
988 }
989
990 /* API function documented in wimlib.h  */
991 WIMLIBAPI void
992 wimlib_global_cleanup(void)
993 {
994         if (!lib_initialized)
995                 return;
996
997         pthread_mutex_lock(&lib_initialization_mutex);
998
999         if (!lib_initialized)
1000                 goto out_unlock;
1001
1002         xml_global_cleanup();
1003         iconv_global_cleanup();
1004 #ifdef __WIN32__
1005         win32_global_cleanup();
1006 #endif
1007
1008         wimlib_set_error_file(NULL);
1009         lib_initialized = false;
1010
1011 out_unlock:
1012         pthread_mutex_unlock(&lib_initialization_mutex);
1013 }