]> wimlib.net Git - wimlib/blob - src/wim.c
Preliminary support for native fds (UNIX only so far)
[wimlib] / src / wim.c
1 /*
2  * wim.c - Stuff that doesn't fit into any other file
3  */
4
5 /*
6  * Copyright (C) 2012, 2013 Eric Biggers
7  *
8  * wimlib - Library for working with WIM files
9  *
10  * This file is part of wimlib, a library for working with WIM files.
11  *
12  * wimlib is free software; you can redistribute it and/or modify it under the
13  * terms of the GNU General Public License as published by the Free
14  * Software Foundation; either version 3 of the License, or (at your option)
15  * any later version.
16  *
17  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
18  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
19  * A PARTICULAR PURPOSE. See the GNU General Public License for more
20  * details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with wimlib; if not, see http://www.gnu.org/licenses/.
24  */
25
26 #include "config.h"
27
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <limits.h>
31 #include <stdarg.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34
35 #ifdef __WIN32__
36 #  include "win32.h"
37 #else
38 #  include <langinfo.h>
39 #endif
40
41 #include "buffer_io.h"
42 #include "dentry.h"
43 #include "lookup_table.h"
44 #include "wimlib_internal.h"
45 #include "xml.h"
46
47 static int
48 image_print_metadata(WIMStruct *w)
49 {
50         DEBUG("Printing metadata for image %d", w->current_image);
51         print_security_data(wim_security_data(w));
52         return for_dentry_in_tree(wim_root_dentry(w), print_dentry,
53                                   w->lookup_table);
54 }
55
56
57 static int
58 image_print_files(WIMStruct *w)
59 {
60         return for_dentry_in_tree(wim_root_dentry(w), print_dentry_full_path,
61                                   NULL);
62 }
63
64 static WIMStruct *
65 new_wim_struct()
66 {
67         WIMStruct *w = CALLOC(1, sizeof(WIMStruct));
68         w->in_fd = INVALID_FILEDES;
69         w->out_fd = INVALID_FILEDES;
70         w->current_image = WIMLIB_NO_IMAGE;
71         return w;
72 }
73
74 /*
75  * Calls a function on images in the WIM.  If @image is WIMLIB_ALL_IMAGES, @visitor
76  * is called on the WIM once for each image, with each image selected as the
77  * current image in turn.  If @image is a certain image, @visitor is called on
78  * the WIM only once, with that image selected.
79  */
80 int
81 for_image(WIMStruct *w, int image, int (*visitor)(WIMStruct *))
82 {
83         int ret;
84         int start;
85         int end;
86         int i;
87
88         if (image == WIMLIB_ALL_IMAGES) {
89                 start = 1;
90                 end = w->hdr.image_count;
91         } else if (image >= 1 && image <= w->hdr.image_count) {
92                 start = image;
93                 end = image;
94         } else {
95                 return WIMLIB_ERR_INVALID_IMAGE;
96         }
97         for (i = start; i <= end; i++) {
98                 ret = select_wim_image(w, i);
99                 if (ret != 0)
100                         return ret;
101                 ret = visitor(w);
102                 if (ret != 0)
103                         return ret;
104         }
105         return 0;
106 }
107
108 /* Returns the compression type given in the flags of a WIM header. */
109 static int
110 wim_hdr_flags_compression_type(int wim_hdr_flags)
111 {
112         if (wim_hdr_flags & WIM_HDR_FLAG_COMPRESSION) {
113                 if (wim_hdr_flags & WIM_HDR_FLAG_COMPRESS_LZX)
114                         return WIMLIB_COMPRESSION_TYPE_LZX;
115                 else if (wim_hdr_flags & WIM_HDR_FLAG_COMPRESS_XPRESS)
116                         return WIMLIB_COMPRESSION_TYPE_XPRESS;
117                 else
118                         return WIMLIB_COMPRESSION_TYPE_INVALID;
119         } else {
120                 return WIMLIB_COMPRESSION_TYPE_NONE;
121         }
122 }
123
124 /*
125  * Creates a WIMStruct for a new WIM file.
126  */
127 WIMLIBAPI int
128 wimlib_create_new_wim(int ctype, WIMStruct **w_ret)
129 {
130         WIMStruct *w;
131         struct wim_lookup_table *table;
132         int ret;
133
134         DEBUG("Creating new WIM with %"TS" compression.",
135               wimlib_get_compression_type_string(ctype));
136
137         /* Allocate the WIMStruct. */
138         w = new_wim_struct();
139         if (!w)
140                 return WIMLIB_ERR_NOMEM;
141
142         ret = init_header(&w->hdr, ctype);
143         if (ret != 0)
144                 goto out_free;
145
146         table = new_lookup_table(9001);
147         if (!table) {
148                 ret = WIMLIB_ERR_NOMEM;
149                 goto out_free;
150         }
151         w->lookup_table = table;
152         *w_ret = w;
153         return 0;
154 out_free:
155         FREE(w);
156         return ret;
157 }
158
159 WIMLIBAPI int
160 wimlib_get_num_images(const WIMStruct *w)
161 {
162         return w->hdr.image_count;
163 }
164
165 int
166 select_wim_image(WIMStruct *w, int image)
167 {
168         struct wim_image_metadata *imd;
169         int ret;
170
171         DEBUG("Selecting image %d", image);
172
173         if (image == WIMLIB_NO_IMAGE) {
174                 ERROR("Invalid image: %d", WIMLIB_NO_IMAGE);
175                 return WIMLIB_ERR_INVALID_IMAGE;
176         }
177
178         if (image == w->current_image)
179                 return 0;
180
181         if (image < 1 || image > w->hdr.image_count) {
182                 ERROR("Cannot select image %d: There are only %u images",
183                       image, w->hdr.image_count);
184                 return WIMLIB_ERR_INVALID_IMAGE;
185         }
186
187         /* If a valid image is currently selected, it can be freed if it is not
188          * modified.  */
189         if (w->current_image != WIMLIB_NO_IMAGE) {
190                 imd = wim_get_current_image_metadata(w);
191                 if (!imd->modified) {
192                         wimlib_assert(list_empty(&imd->unhashed_streams));
193                         DEBUG("Freeing image %u", w->current_image);
194                         destroy_image_metadata(imd, NULL, false);
195                 }
196         }
197         w->current_image = image;
198         imd = wim_get_current_image_metadata(w);
199         if (imd->root_dentry) {
200                 ret = 0;
201         } else {
202                 #ifdef ENABLE_DEBUG
203                 DEBUG("Reading metadata resource specified by the following "
204                       "lookup table entry:");
205                 print_lookup_table_entry(imd->metadata_lte, stderr);
206                 #endif
207                 ret = read_metadata_resource(w, imd);
208                 if (ret)
209                         w->current_image = WIMLIB_NO_IMAGE;
210         }
211         return ret;
212 }
213
214
215 /* Returns the compression type of the WIM file. */
216 WIMLIBAPI int
217 wimlib_get_compression_type(const WIMStruct *w)
218 {
219         return wim_hdr_flags_compression_type(w->hdr.flags);
220 }
221
222 WIMLIBAPI const tchar *
223 wimlib_get_compression_type_string(int ctype)
224 {
225         switch (ctype) {
226                 case WIMLIB_COMPRESSION_TYPE_NONE:
227                         return T("None");
228                 case WIMLIB_COMPRESSION_TYPE_LZX:
229                         return T("LZX");
230                 case WIMLIB_COMPRESSION_TYPE_XPRESS:
231                         return T("XPRESS");
232                 default:
233                         return T("Invalid");
234         }
235 }
236
237 /*
238  * Returns the number of an image in the WIM file, given a string that is either
239  * the number of the image, or the name of the image.  The images are numbered
240  * starting at 1.
241  */
242 WIMLIBAPI int
243 wimlib_resolve_image(WIMStruct *w, const tchar *image_name_or_num)
244 {
245         tchar *p;
246         long image;
247         int i;
248
249         if (!image_name_or_num || !*image_name_or_num)
250                 return WIMLIB_NO_IMAGE;
251
252         if (!tstrcasecmp(image_name_or_num, T("all"))
253             || !tstrcasecmp(image_name_or_num, T("*")))
254                 return WIMLIB_ALL_IMAGES;
255         image = tstrtol(image_name_or_num, &p, 10);
256         if (p != image_name_or_num && *p == T('\0') && image > 0) {
257                 if (image > w->hdr.image_count)
258                         return WIMLIB_NO_IMAGE;
259                 return image;
260         } else {
261                 for (i = 1; i <= w->hdr.image_count; i++) {
262                         if (!tstrcmp(image_name_or_num,
263                                      wimlib_get_image_name(w, i)))
264                                 return i;
265                 }
266                 return WIMLIB_NO_IMAGE;
267         }
268 }
269
270 /* Prints some basic information about a WIM file. */
271 WIMLIBAPI void
272 wimlib_print_wim_information(const WIMStruct *w)
273 {
274         const struct wim_header *hdr;
275
276         hdr = &w->hdr;
277         tputs(T("WIM Information:"));
278         tputs(T("----------------"));
279         tprintf(T("Path:           %"TS"\n"), w->filename);
280         tfputs(T("GUID:           0x"), stdout);
281         print_byte_field(hdr->guid, WIM_GID_LEN, stdout);
282         tputchar(T('\n'));
283         tprintf(T("Image Count:    %d\n"), hdr->image_count);
284         tprintf(T("Compression:    %"TS"\n"),
285                 wimlib_get_compression_type_string(wimlib_get_compression_type(w)));
286         tprintf(T("Part Number:    %d/%d\n"), hdr->part_number, hdr->total_parts);
287         tprintf(T("Boot Index:     %d\n"), hdr->boot_idx);
288         tprintf(T("Size:           %"PRIu64" bytes\n"),
289                 wim_info_get_total_bytes(w->wim_info));
290         tprintf(T("Integrity Info: %"TS"\n"),
291                 (w->hdr.integrity.offset != 0) ? T("yes") : T("no"));
292         tprintf(T("Relative path junction: %"TS"\n"),
293                 (hdr->flags & WIM_HDR_FLAG_RP_FIX) ? T("yes") : T("no"));
294         tputchar(T('\n'));
295 }
296
297 WIMLIBAPI bool
298 wimlib_has_integrity_table(const WIMStruct *w)
299 {
300         return w->hdr.integrity.size != 0;
301 }
302
303 WIMLIBAPI void
304 wimlib_print_available_images(const WIMStruct *w, int image)
305 {
306         int first;
307         int last;
308         int i;
309         int n;
310         if (image == WIMLIB_ALL_IMAGES) {
311                 n = tprintf(T("Available Images:\n"));
312                 first = 1;
313                 last = w->hdr.image_count;
314         } else if (image >= 1 && image <= w->hdr.image_count) {
315                 n = tprintf(T("Information for Image %d\n"), image);
316                 first = image;
317                 last = image;
318         } else {
319                 tprintf(T("wimlib_print_available_images(): Invalid image %d"),
320                         image);
321                 return;
322         }
323         for (i = 0; i < n - 1; i++)
324                 tputchar(T('-'));
325         tputchar(T('\n'));
326         for (i = first; i <= last; i++)
327                 print_image_info(w->wim_info, i);
328 }
329
330
331 /* Prints the metadata for the specified image, which may be WIMLIB_ALL_IMAGES, but
332  * not WIMLIB_NO_IMAGE. */
333 WIMLIBAPI int
334 wimlib_print_metadata(WIMStruct *w, int image)
335 {
336         if (w->hdr.part_number != 1) {
337                 ERROR("Cannot show the metadata from part %hu of a %hu-part split WIM!",
338                        w->hdr.part_number, w->hdr.total_parts);
339                 ERROR("Select the first part of the split WIM to see the metadata.");
340                 return WIMLIB_ERR_SPLIT_UNSUPPORTED;
341         }
342         return for_image(w, image, image_print_metadata);
343 }
344
345 WIMLIBAPI int
346 wimlib_print_files(WIMStruct *w, int image)
347 {
348         if (w->hdr.part_number != 1) {
349                 ERROR("Cannot list the files from part %hu of a %hu-part split WIM!",
350                        w->hdr.part_number, w->hdr.total_parts);
351                 ERROR("Select the first part of the split WIM if you'd like to list the files.");
352                 return WIMLIB_ERR_SPLIT_UNSUPPORTED;
353         }
354         return for_image(w, image, image_print_files);
355 }
356
357 /* Sets the index of the bootable image. */
358 WIMLIBAPI int
359 wimlib_set_boot_idx(WIMStruct *w, int boot_idx)
360 {
361         if (w->hdr.total_parts != 1) {
362                 ERROR("Cannot modify the boot index of a split WIM!");
363                 return WIMLIB_ERR_SPLIT_UNSUPPORTED;
364         }
365         if (boot_idx < 0 || boot_idx > w->hdr.image_count)
366                 return WIMLIB_ERR_INVALID_IMAGE;
367         w->hdr.boot_idx = boot_idx;
368         return 0;
369 }
370
371 WIMLIBAPI int
372 wimlib_get_part_number(const WIMStruct *w, int *total_parts_ret)
373 {
374         if (total_parts_ret)
375                 *total_parts_ret = w->hdr.total_parts;
376         return w->hdr.part_number;
377 }
378
379
380 WIMLIBAPI int
381 wimlib_get_boot_idx(const WIMStruct *w)
382 {
383         return w->hdr.boot_idx;
384 }
385
386 static int
387 do_open_wim(const tchar *filename, filedes_t *fd_ret)
388 {
389         int fd;
390
391         fd = open(filename, O_RDONLY);
392         if (fd == -1) {
393                 ERROR_WITH_ERRNO("Can't open \"%"TS"\" read-only", filename);
394                 return WIMLIB_ERR_OPEN;
395         }
396         *fd_ret = fd;
397         return 0;
398 }
399
400 int
401 reopen_wim(WIMStruct *w)
402 {
403         wimlib_assert(w->in_fd == INVALID_FILEDES);
404         return do_open_wim(w->filename, &w->in_fd);
405 }
406
407 int
408 close_wim(WIMStruct *w)
409 {
410         close(w->in_fd);
411         w->in_fd = INVALID_FILEDES;
412         return 0;
413 }
414
415 /*
416  * Begins the reading of a WIM file; opens the file and reads its header and
417  * lookup table, and optionally checks the integrity.
418  */
419 static int
420 begin_read(WIMStruct *w, const tchar *in_wim_path, int open_flags,
421            wimlib_progress_func_t progress_func)
422 {
423         int ret;
424         int xml_num_images;
425
426         DEBUG("Reading the WIM file `%"TS"'", in_wim_path);
427
428         ret = do_open_wim(in_wim_path, &w->in_fd);
429         if (ret)
430                 return ret;
431
432         /* The absolute path to the WIM is requested so that wimlib_overwrite()
433          * still works even if the process changes its working directory.  This
434          * actually happens if a WIM is mounted read-write, since the FUSE
435          * thread changes directory to "/", and it needs to be able to find the
436          * WIM file again.
437          *
438          * This will break if the full path to the WIM changes in the
439          * intervening time...
440          *
441          * Warning: in Windows native builds, realpath() calls the replacement
442          * function in win32.c.
443          */
444         w->filename = realpath(in_wim_path, NULL);
445         if (!w->filename) {
446                 ERROR_WITH_ERRNO("Failed to resolve WIM filename");
447                 if (errno == ENOMEM)
448                         return WIMLIB_ERR_NOMEM;
449                 else
450                         return WIMLIB_ERR_OPEN;
451         }
452
453         ret = read_header(w->in_fd, &w->hdr, open_flags);
454         if (ret)
455                 return ret;
456
457         DEBUG("According to header, WIM contains %u images", w->hdr.image_count);
458
459         /* If the boot index is invalid, print a warning and set it to 0 */
460         if (w->hdr.boot_idx > w->hdr.image_count) {
461                 WARNING("In `%"TS"', image %u is marked as bootable, "
462                         "but there are only %u images in the WIM",
463                         in_wim_path, w->hdr.boot_idx, w->hdr.image_count);
464                 w->hdr.boot_idx = 0;
465         }
466
467         if (wimlib_get_compression_type(w) == WIMLIB_COMPRESSION_TYPE_INVALID) {
468                 ERROR("Invalid compression type (WIM header flags = 0x%x)",
469                       w->hdr.flags);
470                 return WIMLIB_ERR_INVALID_COMPRESSION_TYPE;
471         }
472
473         if (open_flags & WIMLIB_OPEN_FLAG_CHECK_INTEGRITY) {
474                 ret = check_wim_integrity(w, progress_func);
475                 if (ret == WIM_INTEGRITY_NONEXISTENT) {
476                         WARNING("No integrity information for `%"TS"'; skipping "
477                                 "integrity check.", in_wim_path);
478                 } else if (ret == WIM_INTEGRITY_NOT_OK) {
479                         ERROR("WIM is not intact! (Failed integrity check)");
480                         return WIMLIB_ERR_INTEGRITY;
481                 } else if (ret != WIM_INTEGRITY_OK) {
482                         return ret;
483                 }
484         }
485
486         if (w->hdr.image_count != 0 && w->hdr.part_number == 1) {
487                 w->image_metadata = new_image_metadata_array(w->hdr.image_count);
488                 if (!w->image_metadata)
489                         return WIMLIB_ERR_NOMEM;
490         }
491
492         ret = read_lookup_table(w);
493         if (ret)
494                 return ret;
495
496         ret = read_xml_data(w->in_fd, &w->hdr.xml_res_entry, &w->wim_info);
497         if (ret)
498                 return ret;
499
500         xml_num_images = wim_info_get_num_images(w->wim_info);
501         if (xml_num_images != w->hdr.image_count) {
502                 ERROR("In the file `%"TS"', there are %u <IMAGE> elements "
503                       "in the XML data,", in_wim_path, xml_num_images);
504                 ERROR("but %u images in the WIM!  There must be exactly one "
505                       "<IMAGE> element per image.", w->hdr.image_count);
506                 return WIMLIB_ERR_IMAGE_COUNT;
507         }
508
509         DEBUG("Done beginning read of WIM file `%"TS"'.", in_wim_path);
510         return 0;
511 }
512
513 /*
514  * Opens a WIM file and creates a WIMStruct for it.
515  */
516 WIMLIBAPI int
517 wimlib_open_wim(const tchar *wim_file, int open_flags,
518                 WIMStruct **w_ret,
519                 wimlib_progress_func_t progress_func)
520 {
521         WIMStruct *w;
522         int ret;
523
524         if (!wim_file || !w_ret)
525                 return WIMLIB_ERR_INVALID_PARAM;
526
527         w = new_wim_struct();
528         if (!w)
529                 return WIMLIB_ERR_NOMEM;
530
531         ret = begin_read(w, wim_file, open_flags, progress_func);
532         if (ret == 0)
533                 *w_ret = w;
534         else
535                 wimlib_free(w);
536         return ret;
537 }
538
539 void
540 destroy_image_metadata(struct wim_image_metadata *imd,
541                        struct wim_lookup_table *table,
542                        bool free_metadata_lte)
543 {
544         free_dentry_tree(imd->root_dentry, table);
545         imd->root_dentry = NULL;
546         free_security_data(imd->security_data);
547         imd->security_data = NULL;
548
549         if (free_metadata_lte) {
550                 free_lookup_table_entry(imd->metadata_lte);
551                 imd->metadata_lte = NULL;
552         }
553         if (!table) {
554                 struct wim_lookup_table_entry *lte, *tmp;
555                 list_for_each_entry_safe(lte, tmp, &imd->unhashed_streams, unhashed_list)
556                         free_lookup_table_entry(lte);
557         }
558         INIT_LIST_HEAD(&imd->unhashed_streams);
559         INIT_LIST_HEAD(&imd->inode_list);
560 #ifdef WITH_NTFS_3G
561         if (imd->ntfs_vol) {
562                 do_ntfs_umount(imd->ntfs_vol);
563                 imd->ntfs_vol = NULL;
564         }
565 #endif
566 }
567
568 void
569 put_image_metadata(struct wim_image_metadata *imd,
570                    struct wim_lookup_table *table)
571 {
572         if (imd && --imd->refcnt == 0) {
573                 destroy_image_metadata(imd, table, true);
574                 FREE(imd);
575         }
576 }
577
578 /* Appends the specified image metadata structure to the array of image metadata
579  * for a WIM, and increments the image count. */
580 int
581 append_image_metadata(WIMStruct *w, struct wim_image_metadata *imd)
582 {
583         struct wim_image_metadata **imd_array;
584
585         DEBUG("Reallocating image metadata array for image_count = %u",
586               w->hdr.image_count + 1);
587         imd_array = REALLOC(w->image_metadata,
588                             sizeof(w->image_metadata[0]) * (w->hdr.image_count + 1));
589
590         if (!imd_array)
591                 return WIMLIB_ERR_NOMEM;
592         w->image_metadata = imd_array;
593         imd_array[w->hdr.image_count++] = imd;
594         return 0;
595 }
596
597
598 struct wim_image_metadata *
599 new_image_metadata()
600 {
601         struct wim_image_metadata *imd;
602
603         imd = CALLOC(1, sizeof(*imd));
604         if (imd) {
605                 imd->refcnt = 1;
606                 INIT_LIST_HEAD(&imd->inode_list);
607                 INIT_LIST_HEAD(&imd->unhashed_streams);
608                 DEBUG("Created new image metadata (refcnt=1)");
609         } else {
610                 ERROR_WITH_ERRNO("Failed to allocate new image metadata structure");
611         }
612         return imd;
613 }
614
615 struct wim_image_metadata **
616 new_image_metadata_array(unsigned num_images)
617 {
618         struct wim_image_metadata **imd_array;
619
620         DEBUG("Creating new image metadata array for %u images",
621               num_images);
622
623         imd_array = CALLOC(num_images, sizeof(imd_array[0]));
624
625         if (!imd_array) {
626                 ERROR("Failed to allocate memory for %u image metadata structures",
627                       num_images);
628                 return NULL;
629         }
630         for (unsigned i = 0; i < num_images; i++) {
631                 imd_array[i] = new_image_metadata();
632                 if (!imd_array[i]) {
633                         for (unsigned j = 0; j < i; j++)
634                                 put_image_metadata(imd_array[j], NULL);
635                         FREE(imd_array);
636                         return NULL;
637                 }
638         }
639         return imd_array;
640 }
641
642 /* Checksum all streams that are unhashed (other than the metadata streams),
643  * merging them into the lookup table as needed.  This is a no-op unless the
644  * library has previously used to add or mount an image using the same
645  * WIMStruct. */
646 int
647 wim_checksum_unhashed_streams(WIMStruct *w)
648 {
649         int ret;
650         for (int i = 0; i < w->hdr.image_count; i++) {
651                 struct wim_lookup_table_entry *lte, *tmp;
652                 struct wim_image_metadata *imd = w->image_metadata[i];
653                 image_for_each_unhashed_stream_safe(lte, tmp, imd) {
654                         ret = hash_unhashed_stream(lte, w->lookup_table, NULL);
655                         if (ret)
656                                 return ret;
657                 }
658         }
659         return 0;
660 }
661
662 /* Frees the memory for the WIMStruct, including all internal memory; also
663  * closes all files associated with the WIMStruct.  */
664 WIMLIBAPI void
665 wimlib_free(WIMStruct *w)
666 {
667         DEBUG("Freeing WIMStruct");
668
669         if (!w)
670                 return;
671         if (w->in_fd != INVALID_FILEDES)
672                 close(w->in_fd);
673         if (w->out_fd != INVALID_FILEDES)
674                 close(w->out_fd);
675
676         free_lookup_table(w->lookup_table);
677
678         FREE(w->filename);
679         free_wim_info(w->wim_info);
680         if (w->image_metadata) {
681                 for (unsigned i = 0; i < w->hdr.image_count; i++)
682                         put_image_metadata(w->image_metadata[i], NULL);
683                 FREE(w->image_metadata);
684         }
685         FREE(w);
686         DEBUG("Freed WIMStruct");
687 }
688
689 static bool
690 test_locale_ctype_utf8()
691 {
692 #ifdef __WIN32__
693         return false;
694 #else
695         char *ctype = nl_langinfo(CODESET);
696
697         return (!strstr(ctype, "UTF-8") ||
698                 !strstr(ctype, "UTF8") ||
699                 !strstr(ctype, "utf8") ||
700                 !strstr(ctype, "utf-8"));
701 #endif
702 }
703
704 WIMLIBAPI int
705 wimlib_global_init(int init_flags)
706 {
707         libxml_global_init();
708         if (!(init_flags & WIMLIB_INIT_FLAG_ASSUME_UTF8)) {
709                 wimlib_mbs_is_utf8 = test_locale_ctype_utf8();
710         #ifdef WITH_NTFS_3G
711                 if (!wimlib_mbs_is_utf8)
712                         libntfs3g_global_init();
713         #endif
714         }
715 #ifdef __WIN32__
716         win32_global_init();
717 #endif
718         return 0;
719 }
720
721 /* Free global memory allocations.  Not strictly necessary if the process using
722  * wimlib is just about to exit (as is the case for 'imagex'). */
723 WIMLIBAPI void
724 wimlib_global_cleanup()
725 {
726         libxml_global_cleanup();
727         iconv_global_cleanup();
728 #ifdef __WIN32__
729         win32_global_cleanup();
730 #endif
731 }