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