]> wimlib.net Git - wimlib/blob - src/write.c
Various changes
[wimlib] / src / write.c
1 /*
2  * write.c
3  *
4  * Support for writing WIM files; write a WIM file, overwrite a WIM file, write
5  * compressed file resources, etc.
6  */
7
8 /*
9  * Copyright (C) 2012, 2013 Eric Biggers
10  *
11  * This file is part of wimlib, a library for working with WIM files.
12  *
13  * wimlib is free software; you can redistribute it and/or modify it under the
14  * terms of the GNU General Public License as published by the Free
15  * Software Foundation; either version 3 of the License, or (at your option)
16  * any later version.
17  *
18  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
19  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
20  * A PARTICULAR PURPOSE. See the GNU General Public License for more
21  * details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with wimlib; if not, see http://www.gnu.org/licenses/.
25  */
26
27 #include "config.h"
28
29 #if defined(HAVE_SYS_FILE_H) && defined(HAVE_FLOCK)
30 /* On BSD, this should be included before "list.h" so that "list.h" can
31  * overwrite the LIST_HEAD macro. */
32 #include <sys/file.h>
33 #endif
34
35 #include "list.h"
36 #include "wimlib_internal.h"
37 #include "buffer_io.h"
38 #include "dentry.h"
39 #include "lookup_table.h"
40 #include "xml.h"
41 #include "lzx.h"
42 #include "xpress.h"
43
44 #ifdef ENABLE_MULTITHREADED_COMPRESSION
45 #include <pthread.h>
46 #endif
47
48 #include <unistd.h>
49 #include <errno.h>
50
51 #ifdef WITH_NTFS_3G
52 #include <time.h>
53 #include <ntfs-3g/attrib.h>
54 #include <ntfs-3g/inode.h>
55 #include <ntfs-3g/dir.h>
56 #endif
57
58 #ifdef HAVE_ALLOCA_H
59 #include <alloca.h>
60 #else
61 #include <stdlib.h>
62 #endif
63
64 static int fflush_and_ftruncate(FILE *fp, off_t size)
65 {
66         int ret;
67
68         ret = fflush(fp);
69         if (ret != 0) {
70                 ERROR_WITH_ERRNO("Failed to flush data to output WIM file");
71                 return WIMLIB_ERR_WRITE;
72         }
73         ret = ftruncate(fileno(fp), size);
74         if (ret != 0) {
75                 ERROR_WITH_ERRNO("Failed to truncate output WIM file to "
76                                  "%"PRIu64" bytes", size);
77                 return WIMLIB_ERR_WRITE;
78         }
79         return 0;
80 }
81
82 /* Chunk table that's located at the beginning of each compressed resource in
83  * the WIM.  (This is not the on-disk format; the on-disk format just has an
84  * array of offsets.) */
85 struct chunk_table {
86         off_t file_offset;
87         u64 num_chunks;
88         u64 original_resource_size;
89         u64 bytes_per_chunk_entry;
90         u64 table_disk_size;
91         u64 cur_offset;
92         u64 *cur_offset_p;
93         u64 offsets[0];
94 };
95
96 /*
97  * Allocates and initializes a chunk table, and reserves space for it in the
98  * output file.
99  */
100 static int
101 begin_wim_resource_chunk_tab(const struct wim_lookup_table_entry *lte,
102                              FILE *out_fp,
103                              off_t file_offset,
104                              struct chunk_table **chunk_tab_ret)
105 {
106         u64 size = wim_resource_size(lte);
107         u64 num_chunks = (size + WIM_CHUNK_SIZE - 1) / WIM_CHUNK_SIZE;
108         size_t alloc_size = sizeof(struct chunk_table) + num_chunks * sizeof(u64);
109         struct chunk_table *chunk_tab = CALLOC(1, alloc_size);
110         int ret;
111
112         if (!chunk_tab) {
113                 ERROR("Failed to allocate chunk table for %"PRIu64" byte "
114                       "resource", size);
115                 ret = WIMLIB_ERR_NOMEM;
116                 goto out;
117         }
118         chunk_tab->file_offset = file_offset;
119         chunk_tab->num_chunks = num_chunks;
120         chunk_tab->original_resource_size = size;
121         chunk_tab->bytes_per_chunk_entry = (size >= (1ULL << 32)) ? 8 : 4;
122         chunk_tab->table_disk_size = chunk_tab->bytes_per_chunk_entry *
123                                      (num_chunks - 1);
124         chunk_tab->cur_offset = 0;
125         chunk_tab->cur_offset_p = chunk_tab->offsets;
126
127         if (fwrite(chunk_tab, 1, chunk_tab->table_disk_size, out_fp) !=
128                    chunk_tab->table_disk_size) {
129                 ERROR_WITH_ERRNO("Failed to write chunk table in compressed "
130                                  "file resource");
131                 ret = WIMLIB_ERR_WRITE;
132                 goto out;
133         }
134
135         ret = 0;
136 out:
137         *chunk_tab_ret = chunk_tab;
138         return ret;
139 }
140
141 /*
142  * Pointer to function to compresses a chunk of a WIM resource.
143  *
144  * @chunk:              Uncompressed data of the chunk.
145  * @chunk_size:         Size of the uncompressed chunk in bytes.
146  * @compressed_chunk:   Pointer to output buffer of size at least
147  *                              (@chunk_size - 1) bytes.
148  * @compressed_chunk_len_ret:   Pointer to an unsigned int into which the size
149  *                                      of the compressed chunk will be
150  *                                      returned.
151  *
152  * Returns zero if compressed succeeded, and nonzero if the chunk could not be
153  * compressed to any smaller than @chunk_size.  This function cannot fail for
154  * any other reasons.
155  */
156 typedef int (*compress_func_t)(const void *, unsigned, void *, unsigned *);
157
158 compress_func_t get_compress_func(int out_ctype)
159 {
160         if (out_ctype == WIMLIB_COMPRESSION_TYPE_LZX)
161                 return lzx_compress;
162         else
163                 return xpress_compress;
164 }
165
166 /*
167  * Writes a chunk of a WIM resource to an output file.
168  *
169  * @chunk:        Uncompressed data of the chunk.
170  * @chunk_size:   Size of the chunk (<= WIM_CHUNK_SIZE)
171  * @out_fp:       FILE * to write tho chunk to.
172  * @out_ctype:    Compression type to use when writing the chunk (ignored if no
173  *                      chunk table provided)
174  * @chunk_tab:    Pointer to chunk table being created.  It is updated with the
175  *                      offset of the chunk we write.
176  *
177  * Returns 0 on success; nonzero on failure.
178  */
179 static int write_wim_resource_chunk(const u8 chunk[], unsigned chunk_size,
180                                     FILE *out_fp, compress_func_t compress,
181                                     struct chunk_table *chunk_tab)
182 {
183         const u8 *out_chunk;
184         unsigned out_chunk_size;
185         if (chunk_tab) {
186                 u8 *compressed_chunk = alloca(chunk_size);
187                 int ret;
188
189                 ret = compress(chunk, chunk_size, compressed_chunk,
190                                &out_chunk_size);
191                 if (ret == 0) {
192                         out_chunk = compressed_chunk;
193                 } else {
194                         out_chunk = chunk;
195                         out_chunk_size = chunk_size;
196                 }
197                 *chunk_tab->cur_offset_p++ = chunk_tab->cur_offset;
198                 chunk_tab->cur_offset += out_chunk_size;
199         } else {
200                 out_chunk = chunk;
201                 out_chunk_size = chunk_size;
202         }
203         if (fwrite(out_chunk, 1, out_chunk_size, out_fp) != out_chunk_size) {
204                 ERROR_WITH_ERRNO("Failed to write WIM resource chunk");
205                 return WIMLIB_ERR_WRITE;
206         }
207         return 0;
208 }
209
210 /*
211  * Finishes a WIM chunk table and writes it to the output file at the correct
212  * offset.
213  *
214  * The final size of the full compressed resource is returned in the
215  * @compressed_size_p.
216  */
217 static int
218 finish_wim_resource_chunk_tab(struct chunk_table *chunk_tab,
219                               FILE *out_fp, u64 *compressed_size_p)
220 {
221         size_t bytes_written;
222         if (fseeko(out_fp, chunk_tab->file_offset, SEEK_SET) != 0) {
223                 ERROR_WITH_ERRNO("Failed to seek to byte %"PRIu64" of output "
224                                  "WIM file", chunk_tab->file_offset);
225                 return WIMLIB_ERR_WRITE;
226         }
227
228         if (chunk_tab->bytes_per_chunk_entry == 8) {
229                 array_cpu_to_le64(chunk_tab->offsets, chunk_tab->num_chunks);
230         } else {
231                 for (u64 i = 0; i < chunk_tab->num_chunks; i++)
232                         ((u32*)chunk_tab->offsets)[i] =
233                                 cpu_to_le32(chunk_tab->offsets[i]);
234         }
235         bytes_written = fwrite((u8*)chunk_tab->offsets +
236                                         chunk_tab->bytes_per_chunk_entry,
237                                1, chunk_tab->table_disk_size, out_fp);
238         if (bytes_written != chunk_tab->table_disk_size) {
239                 ERROR_WITH_ERRNO("Failed to write chunk table in compressed "
240                                  "file resource");
241                 return WIMLIB_ERR_WRITE;
242         }
243         if (fseeko(out_fp, 0, SEEK_END) != 0) {
244                 ERROR_WITH_ERRNO("Failed to seek to end of output WIM file");
245                 return WIMLIB_ERR_WRITE;
246         }
247         *compressed_size_p = chunk_tab->cur_offset + chunk_tab->table_disk_size;
248         return 0;
249 }
250
251 /* Prepare for multiple reads to a resource by caching a FILE * or NTFS
252  * attribute pointer in the lookup table entry. */
253 static int prepare_resource_for_read(struct wim_lookup_table_entry *lte
254
255                                         #ifdef WITH_NTFS_3G
256                                         , ntfs_inode **ni_ret
257                                         #endif
258                 )
259 {
260         if (lte->resource_location == RESOURCE_IN_FILE_ON_DISK
261              && !lte->file_on_disk_fp)
262         {
263                 wimlib_assert(lte->file_on_disk);
264                 lte->file_on_disk_fp = fopen(lte->file_on_disk, "rb");
265                 if (!lte->file_on_disk_fp) {
266                         ERROR_WITH_ERRNO("Failed to open the file `%s' for "
267                                          "reading", lte->file_on_disk);
268                         return WIMLIB_ERR_OPEN;
269                 }
270         }
271 #ifdef WITH_NTFS_3G
272         else if (lte->resource_location == RESOURCE_IN_NTFS_VOLUME
273                   && !lte->attr)
274         {
275                 struct ntfs_location *loc = lte->ntfs_loc;
276                 ntfs_inode *ni;
277                 wimlib_assert(loc);
278                 ni = ntfs_pathname_to_inode(*loc->ntfs_vol_p, NULL, loc->path_utf8);
279                 if (!ni) {
280                         ERROR_WITH_ERRNO("Failed to open inode `%s' in NTFS "
281                                          "volume", loc->path_utf8);
282                         return WIMLIB_ERR_NTFS_3G;
283                 }
284                 lte->attr = ntfs_attr_open(ni,
285                                            loc->is_reparse_point ? AT_REPARSE_POINT : AT_DATA,
286                                            (ntfschar*)loc->stream_name_utf16,
287                                            loc->stream_name_utf16_num_chars);
288                 if (!lte->attr) {
289                         ERROR_WITH_ERRNO("Failed to open attribute of `%s' in "
290                                          "NTFS volume", loc->path_utf8);
291                         ntfs_inode_close(ni);
292                         return WIMLIB_ERR_NTFS_3G;
293                 }
294                 *ni_ret = ni;
295         }
296 #endif
297         return 0;
298 }
299
300 /* Undo prepare_resource_for_read() by closing the cached FILE * or NTFS
301  * attribute. */
302 static void end_wim_resource_read(struct wim_lookup_table_entry *lte
303                                 #ifdef WITH_NTFS_3G
304                                         , ntfs_inode *ni
305                                 #endif
306                                         )
307 {
308         if (lte->resource_location == RESOURCE_IN_FILE_ON_DISK
309             && lte->file_on_disk_fp) {
310                 fclose(lte->file_on_disk_fp);
311                 lte->file_on_disk_fp = NULL;
312         }
313 #ifdef WITH_NTFS_3G
314         else if (lte->resource_location == RESOURCE_IN_NTFS_VOLUME) {
315                 if (lte->attr) {
316                         ntfs_attr_close(lte->attr);
317                         lte->attr = NULL;
318                 }
319                 if (ni)
320                         ntfs_inode_close(ni);
321         }
322 #endif
323 }
324
325 static int
326 write_uncompressed_resource_and_truncate(struct wim_lookup_table_entry *lte,
327                                          FILE *out_fp,
328                                          off_t file_offset,
329                                          struct resource_entry *out_res_entry)
330 {
331         int ret;
332         if (fseeko(out_fp, file_offset, SEEK_SET) != 0) {
333                 ERROR_WITH_ERRNO("Failed to seek to byte %"PRIu64" of "
334                                  "output WIM file", file_offset);
335                 return WIMLIB_ERR_WRITE;
336         }
337         ret = write_wim_resource(lte, out_fp, WIMLIB_COMPRESSION_TYPE_NONE,
338                                  out_res_entry, 0);
339         if (ret != 0)
340                 return ret;
341
342         return fflush_and_ftruncate(out_fp,
343                                     file_offset + wim_resource_size(lte));
344 }
345
346 /*
347  * Writes a WIM resource to a FILE * opened for writing.  The resource may be
348  * written uncompressed or compressed depending on the @out_ctype parameter.
349  *
350  * If by chance the resource compresses to more than the original size (this may
351  * happen with random data or files than are pre-compressed), the resource is
352  * instead written uncompressed (and this is reflected in the @out_res_entry by
353  * removing the WIM_RESHDR_FLAG_COMPRESSED flag).
354  *
355  * @lte:        The lookup table entry for the WIM resource.
356  * @out_fp:     The FILE * to write the resource to.
357  * @out_ctype:  The compression type of the resource to write.  Note: if this is
358  *                      the same as the compression type of the WIM resource we
359  *                      need to read, we simply copy the data (i.e. we do not
360  *                      uncompress it, then compress it again).
361  * @out_res_entry:  If non-NULL, a resource entry that is filled in with the
362  *                  offset, original size, compressed size, and compression flag
363  *                  of the output resource.
364  *
365  * Returns 0 on success; nonzero on failure.
366  */
367 int write_wim_resource(struct wim_lookup_table_entry *lte,
368                        FILE *out_fp, int out_ctype,
369                        struct resource_entry *out_res_entry,
370                        int flags)
371 {
372         u64 bytes_remaining;
373         u64 original_size;
374         u64 old_compressed_size;
375         u64 new_compressed_size;
376         u64 offset;
377         int ret;
378         struct chunk_table *chunk_tab = NULL;
379         bool raw;
380         off_t file_offset;
381         compress_func_t compress = NULL;
382 #ifdef WITH_NTFS_3G
383         ntfs_inode *ni = NULL;
384 #endif
385
386         wimlib_assert(lte);
387
388         /* Original size of the resource */
389         original_size = wim_resource_size(lte);
390
391         /* Compressed size of the resource (as it exists now) */
392         old_compressed_size = wim_resource_compressed_size(lte);
393
394         /* Current offset in output file */
395         file_offset = ftello(out_fp);
396         if (file_offset == -1) {
397                 ERROR_WITH_ERRNO("Failed to get offset in output "
398                                  "stream");
399                 return WIMLIB_ERR_WRITE;
400         }
401
402         /* Are the compression types the same?  If so, do a raw copy (copy
403          * without decompressing and recompressing the data). */
404         raw = (wim_resource_compression_type(lte) == out_ctype
405                && out_ctype != WIMLIB_COMPRESSION_TYPE_NONE
406                && !(flags & WIMLIB_RESOURCE_FLAG_RECOMPRESS));
407
408         if (raw) {
409                 flags |= WIMLIB_RESOURCE_FLAG_RAW;
410                 bytes_remaining = old_compressed_size;
411         } else {
412                 flags &= ~WIMLIB_RESOURCE_FLAG_RAW;
413                 bytes_remaining = original_size;
414         }
415
416         /* Empty resource; nothing needs to be done, so just return success. */
417         if (bytes_remaining == 0)
418                 return 0;
419
420         /* Buffer for reading chunks for the resource */
421         u8 buf[min(WIM_CHUNK_SIZE, bytes_remaining)];
422
423         /* If we are writing a compressed resource and not doing a raw copy, we
424          * need to initialize the chunk table */
425         if (out_ctype != WIMLIB_COMPRESSION_TYPE_NONE && !raw) {
426                 ret = begin_wim_resource_chunk_tab(lte, out_fp, file_offset,
427                                                    &chunk_tab);
428                 if (ret != 0)
429                         goto out;
430         }
431
432         /* If the WIM resource is in an external file, open a FILE * to it so we
433          * don't have to open a temporary one in read_wim_resource() for each
434          * chunk. */
435 #ifdef WITH_NTFS_3G
436         ret = prepare_resource_for_read(lte, &ni);
437 #else
438         ret = prepare_resource_for_read(lte);
439 #endif
440         if (ret != 0)
441                 goto out;
442
443         /* If we aren't doing a raw copy, we will compute the SHA1 message
444          * digest of the resource as we read it, and verify it's the same as the
445          * hash given in the lookup table entry once we've finished reading the
446          * resource. */
447         SHA_CTX ctx;
448         if (!raw) {
449                 sha1_init(&ctx);
450                 compress = get_compress_func(out_ctype);
451         }
452         offset = 0;
453
454         /* While there are still bytes remaining in the WIM resource, read a
455          * chunk of the resource, update SHA1, then write that chunk using the
456          * desired compression type. */
457         do {
458                 u64 to_read = min(bytes_remaining, WIM_CHUNK_SIZE);
459                 ret = read_wim_resource(lte, buf, to_read, offset, flags);
460                 if (ret != 0)
461                         goto out_fclose;
462                 if (!raw)
463                         sha1_update(&ctx, buf, to_read);
464                 ret = write_wim_resource_chunk(buf, to_read, out_fp,
465                                                compress, chunk_tab);
466                 if (ret != 0)
467                         goto out_fclose;
468                 bytes_remaining -= to_read;
469                 offset += to_read;
470         } while (bytes_remaining);
471
472         /* Raw copy:  The new compressed size is the same as the old compressed
473          * size
474          *
475          * Using WIMLIB_COMPRESSION_TYPE_NONE:  The new compressed size is the
476          * original size
477          *
478          * Using a different compression type:  Call
479          * finish_wim_resource_chunk_tab() and it will provide the new
480          * compressed size.
481          */
482         if (raw) {
483                 new_compressed_size = old_compressed_size;
484         } else {
485                 if (out_ctype == WIMLIB_COMPRESSION_TYPE_NONE)
486                         new_compressed_size = original_size;
487                 else {
488                         ret = finish_wim_resource_chunk_tab(chunk_tab, out_fp,
489                                                             &new_compressed_size);
490                         if (ret != 0)
491                                 goto out_fclose;
492                 }
493         }
494
495         /* Verify SHA1 message digest of the resource, unless we are doing a raw
496          * write (in which case we never even saw the uncompressed data).  Or,
497          * if the hash we had before is all 0's, just re-set it to be the new
498          * hash. */
499         if (!raw) {
500                 u8 md[SHA1_HASH_SIZE];
501                 sha1_final(md, &ctx);
502                 if (is_zero_hash(lte->hash)) {
503                         copy_hash(lte->hash, md);
504                 } else if (!hashes_equal(md, lte->hash)) {
505                         ERROR("WIM resource has incorrect hash!");
506                         if (lte->resource_location == RESOURCE_IN_FILE_ON_DISK) {
507                                 ERROR("We were reading it from `%s'; maybe it changed "
508                                       "while we were reading it.",
509                                       lte->file_on_disk);
510                         }
511                         ret = WIMLIB_ERR_INVALID_RESOURCE_HASH;
512                         goto out_fclose;
513                 }
514         }
515
516         if (!raw && new_compressed_size >= original_size &&
517             out_ctype != WIMLIB_COMPRESSION_TYPE_NONE)
518         {
519                 /* Oops!  We compressed the resource to larger than the original
520                  * size.  Write the resource uncompressed instead. */
521                 ret = write_uncompressed_resource_and_truncate(lte,
522                                                                out_fp,
523                                                                file_offset,
524                                                                out_res_entry);
525                 if (ret != 0)
526                         goto out_fclose;
527         } else {
528                 if (out_res_entry) {
529                         out_res_entry->size          = new_compressed_size;
530                         out_res_entry->original_size = original_size;
531                         out_res_entry->offset        = file_offset;
532                         out_res_entry->flags         = lte->resource_entry.flags
533                                                         & ~WIM_RESHDR_FLAG_COMPRESSED;
534                         if (out_ctype != WIMLIB_COMPRESSION_TYPE_NONE)
535                                 out_res_entry->flags |= WIM_RESHDR_FLAG_COMPRESSED;
536                 }
537         }
538         ret = 0;
539 out_fclose:
540 #ifdef WITH_NTFS_3G
541         end_wim_resource_read(lte, ni);
542 #else
543         end_wim_resource_read(lte);
544 #endif
545 out:
546         FREE(chunk_tab);
547         return ret;
548 }
549
550 #ifdef ENABLE_MULTITHREADED_COMPRESSION
551
552 /* Blocking shared queue (solves the producer-consumer problem) */
553 struct shared_queue {
554         unsigned size;
555         unsigned front;
556         unsigned back;
557         unsigned filled_slots;
558         void **array;
559         pthread_mutex_t lock;
560         pthread_cond_t msg_avail_cond;
561         pthread_cond_t space_avail_cond;
562 };
563
564 static int shared_queue_init(struct shared_queue *q, unsigned size)
565 {
566         wimlib_assert(size != 0);
567         q->array = CALLOC(sizeof(q->array[0]), size);
568         if (!q->array)
569                 return WIMLIB_ERR_NOMEM;
570         q->filled_slots = 0;
571         q->front = 0;
572         q->back = size - 1;
573         q->size = size;
574         pthread_mutex_init(&q->lock, NULL);
575         pthread_cond_init(&q->msg_avail_cond, NULL);
576         pthread_cond_init(&q->space_avail_cond, NULL);
577         return 0;
578 }
579
580 static void shared_queue_destroy(struct shared_queue *q)
581 {
582         FREE(q->array);
583         pthread_mutex_destroy(&q->lock);
584         pthread_cond_destroy(&q->msg_avail_cond);
585         pthread_cond_destroy(&q->space_avail_cond);
586 }
587
588 static void shared_queue_put(struct shared_queue *q, void *obj)
589 {
590         pthread_mutex_lock(&q->lock);
591         while (q->filled_slots == q->size)
592                 pthread_cond_wait(&q->space_avail_cond, &q->lock);
593
594         q->back = (q->back + 1) % q->size;
595         q->array[q->back] = obj;
596         q->filled_slots++;
597
598         pthread_cond_broadcast(&q->msg_avail_cond);
599         pthread_mutex_unlock(&q->lock);
600 }
601
602 static void *shared_queue_get(struct shared_queue *q)
603 {
604         void *obj;
605
606         pthread_mutex_lock(&q->lock);
607         while (q->filled_slots == 0)
608                 pthread_cond_wait(&q->msg_avail_cond, &q->lock);
609
610         obj = q->array[q->front];
611         q->array[q->front] = NULL;
612         q->front = (q->front + 1) % q->size;
613         q->filled_slots--;
614
615         pthread_cond_broadcast(&q->space_avail_cond);
616         pthread_mutex_unlock(&q->lock);
617         return obj;
618 }
619
620 struct compressor_thread_params {
621         struct shared_queue *res_to_compress_queue;
622         struct shared_queue *compressed_res_queue;
623         compress_func_t compress;
624 };
625
626 #define MAX_CHUNKS_PER_MSG 2
627
628 struct message {
629         struct wim_lookup_table_entry *lte;
630         u8 *uncompressed_chunks[MAX_CHUNKS_PER_MSG];
631         u8 *out_compressed_chunks[MAX_CHUNKS_PER_MSG];
632         u8 *compressed_chunks[MAX_CHUNKS_PER_MSG];
633         unsigned uncompressed_chunk_sizes[MAX_CHUNKS_PER_MSG];
634         unsigned compressed_chunk_sizes[MAX_CHUNKS_PER_MSG];
635         unsigned num_chunks;
636         struct list_head list;
637         bool complete;
638         u64 begin_chunk;
639 };
640
641 static void compress_chunks(struct message *msg, compress_func_t compress)
642 {
643         for (unsigned i = 0; i < msg->num_chunks; i++) {
644                 DEBUG2("compress chunk %u of %u", i, msg->num_chunks);
645                 int ret = compress(msg->uncompressed_chunks[i],
646                                    msg->uncompressed_chunk_sizes[i],
647                                    msg->compressed_chunks[i],
648                                    &msg->compressed_chunk_sizes[i]);
649                 if (ret == 0) {
650                         msg->out_compressed_chunks[i] = msg->compressed_chunks[i];
651                 } else {
652                         msg->out_compressed_chunks[i] = msg->uncompressed_chunks[i];
653                         msg->compressed_chunk_sizes[i] = msg->uncompressed_chunk_sizes[i];
654                 }
655         }
656 }
657
658 /* Compressor thread routine.  This is a lot simpler than the main thread
659  * routine: just repeatedly get a group of chunks from the
660  * res_to_compress_queue, compress them, and put them in the
661  * compressed_res_queue.  A NULL pointer indicates that the thread should stop.
662  * */
663 static void *compressor_thread_proc(void *arg)
664 {
665         struct compressor_thread_params *params = arg;
666         struct shared_queue *res_to_compress_queue = params->res_to_compress_queue;
667         struct shared_queue *compressed_res_queue = params->compressed_res_queue;
668         compress_func_t compress = params->compress;
669         struct message *msg;
670
671         DEBUG("Compressor thread ready");
672         while ((msg = shared_queue_get(res_to_compress_queue)) != NULL) {
673                 compress_chunks(msg, compress);
674                 shared_queue_put(compressed_res_queue, msg);
675         }
676         DEBUG("Compressor thread terminating");
677         return NULL;
678 }
679 #endif
680
681 static int do_write_stream_list(struct list_head *my_resources,
682                                 FILE *out_fp,
683                                 int out_ctype,
684                                 wimlib_progress_func_t progress_func,
685                                 union wimlib_progress_info *progress,
686                                 int write_resource_flags)
687 {
688         int ret;
689         struct wim_lookup_table_entry *lte, *tmp;
690
691         list_for_each_entry_safe(lte, tmp, my_resources, staging_list) {
692                 ret = write_wim_resource(lte,
693                                          out_fp,
694                                          out_ctype,
695                                          &lte->output_resource_entry,
696                                          write_resource_flags);
697                 if (ret != 0)
698                         return ret;
699                 list_del(&lte->staging_list);
700                 progress->write_streams.completed_bytes +=
701                         wim_resource_size(lte);
702                 progress->write_streams.completed_streams++;
703                 if (progress_func) {
704                         progress_func(WIMLIB_PROGRESS_MSG_WRITE_STREAMS,
705                                       progress);
706                 }
707         }
708         return 0;
709 }
710
711 static int write_stream_list_serial(struct list_head *stream_list,
712                                     FILE *out_fp,
713                                     int out_ctype,
714                                     int write_flags,
715                                     wimlib_progress_func_t progress_func,
716                                     union wimlib_progress_info *progress)
717 {
718         int write_resource_flags;
719
720         if (write_flags & WIMLIB_WRITE_FLAG_RECOMPRESS)
721                 write_resource_flags = WIMLIB_RESOURCE_FLAG_RECOMPRESS;
722         else
723                 write_resource_flags = 0;
724         progress->write_streams.num_threads = 1;
725         if (progress_func)
726                 progress_func(WIMLIB_PROGRESS_MSG_WRITE_STREAMS, progress);
727         return do_write_stream_list(stream_list, out_fp,
728                                     out_ctype, progress_func,
729                                     progress, write_resource_flags);
730 }
731
732 #ifdef ENABLE_MULTITHREADED_COMPRESSION
733 static int write_wim_chunks(struct message *msg, FILE *out_fp,
734                             struct chunk_table *chunk_tab)
735 {
736         for (unsigned i = 0; i < msg->num_chunks; i++) {
737                 unsigned chunk_csize = msg->compressed_chunk_sizes[i];
738
739                 DEBUG2("Write wim chunk %u of %u (csize = %u)",
740                       i, msg->num_chunks, chunk_csize);
741
742                 if (fwrite(msg->out_compressed_chunks[i], 1, chunk_csize, out_fp)
743                     != chunk_csize)
744                 {
745                         ERROR_WITH_ERRNO("Failed to write WIM chunk");
746                         return WIMLIB_ERR_WRITE;
747                 }
748
749                 *chunk_tab->cur_offset_p++ = chunk_tab->cur_offset;
750                 chunk_tab->cur_offset += chunk_csize;
751         }
752         return 0;
753 }
754
755 /*
756  * This function is executed by the main thread when the resources are being
757  * compressed in parallel.  The main thread is in change of all reading of the
758  * uncompressed data and writing of the compressed data.  The compressor threads
759  * *only* do compression from/to in-memory buffers.
760  *
761  * Each unit of work given to a compressor thread is up to MAX_CHUNKS_PER_MSG
762  * chunks of compressed data to compress, represented in a `struct message'.
763  * Each message is passed from the main thread to a worker thread through the
764  * res_to_compress_queue, and it is passed back through the
765  * compressed_res_queue.
766  */
767 static int main_writer_thread_proc(struct list_head *stream_list,
768                                    FILE *out_fp,
769                                    int out_ctype,
770                                    struct shared_queue *res_to_compress_queue,
771                                    struct shared_queue *compressed_res_queue,
772                                    size_t num_messages,
773                                    int write_flags,
774                                    wimlib_progress_func_t progress_func,
775                                    union wimlib_progress_info *progress)
776 {
777         int ret;
778         struct chunk_table *cur_chunk_tab = NULL;
779         struct message *msgs = CALLOC(num_messages, sizeof(struct message));
780         struct wim_lookup_table_entry *next_lte = NULL;
781
782         // Initially, all the messages are available to use.
783         LIST_HEAD(available_msgs);
784
785         if (!msgs) {
786                 ret = WIMLIB_ERR_NOMEM;
787                 goto out;
788         }
789
790         for (size_t i = 0; i < num_messages; i++)
791                 list_add(&msgs[i].list, &available_msgs);
792
793         // outstanding_resources is the list of resources that currently have
794         // had chunks sent off for compression.
795         //
796         // The first stream in outstanding_resources is the stream that is
797         // currently being written (cur_lte).
798         //
799         // The last stream in outstanding_resources is the stream that is
800         // currently being read and chunks fed to the compressor threads
801         // (next_lte).
802         //
803         // Depending on the number of threads and the sizes of the resource,
804         // the outstanding streams list may contain streams between cur_lte and
805         // next_lte that have all their chunks compressed or being compressed,
806         // but haven't been written yet.
807         //
808         LIST_HEAD(outstanding_resources);
809         struct list_head *next_resource = stream_list->next;
810         u64 next_chunk = 0;
811         u64 next_num_chunks = 0;
812
813         // As in write_wim_resource(), each resource we read is checksummed.
814         SHA_CTX next_sha_ctx;
815         u8 next_hash[SHA1_HASH_SIZE];
816
817         // Resources that don't need any chunks compressed are added to this
818         // list and written directly by the main thread.
819         LIST_HEAD(my_resources);
820
821         struct wim_lookup_table_entry *cur_lte = NULL;
822         struct message *msg;
823
824 #ifdef WITH_NTFS_3G
825         ntfs_inode *ni = NULL;
826 #endif
827
828         DEBUG("Initializing buffers for uncompressed "
829               "and compressed data (%zu bytes needed)",
830               num_messages * MAX_CHUNKS_PER_MSG * WIM_CHUNK_SIZE * 2);
831
832         // Pre-allocate all the buffers that will be needed to do the chunk
833         // compression.
834         for (size_t i = 0; i < num_messages; i++) {
835                 for (size_t j = 0; j < MAX_CHUNKS_PER_MSG; j++) {
836                         msgs[i].compressed_chunks[j] = MALLOC(WIM_CHUNK_SIZE);
837
838                         // The extra 8 bytes is because longest_match() in
839                         // lz77.c may read a little bit off the end of the
840                         // uncompressed data.  It doesn't need to be
841                         // initialized--- we really just need to avoid accessing
842                         // an unmapped page.
843                         msgs[i].uncompressed_chunks[j] = MALLOC(WIM_CHUNK_SIZE + 8);
844                         if (msgs[i].compressed_chunks[j] == NULL ||
845                             msgs[i].uncompressed_chunks[j] == NULL)
846                         {
847                                 ret = WIMLIB_ERR_NOMEM;
848                                 goto out;
849                         }
850                 }
851         }
852
853         // This loop is executed until all resources have been written, except
854         // possibly a few that have been added to the @my_resources list for
855         // writing later.
856         while (1) {
857                 // Send chunks to the compressor threads until either (a) there
858                 // are no more messages available since they were all sent off,
859                 // or (b) there are no more resources that need to be
860                 // compressed.
861                 while (!list_empty(&available_msgs)) {
862                         if (next_chunk == next_num_chunks) {
863                                 // If next_chunk == next_num_chunks, there are
864                                 // no more chunks to write in the current
865                                 // stream.  So, check the SHA1 message digest of
866                                 // the stream that was just finished (unless
867                                 // next_lte == NULL, which is the case the very
868                                 // first time this loop is entered, and also
869                                 // near the very end of the compression when
870                                 // there are no more streams.)  Then, advance to
871                                 // the next stream (if there is one).
872                                 if (next_lte != NULL) {
873                                 #ifdef WITH_NTFS_3G
874                                         end_wim_resource_read(next_lte, ni);
875                                         ni = NULL;
876                                 #else
877                                         end_wim_resource_read(next_lte);
878                                 #endif
879                                         DEBUG2("Finalize SHA1 md (next_num_chunks=%zu)",
880                                                next_num_chunks);
881                                         sha1_final(next_hash, &next_sha_ctx);
882                                         if (!hashes_equal(next_lte->hash, next_hash)) {
883                                                 ERROR("WIM resource has incorrect hash!");
884                                                 if (next_lte->resource_location ==
885                                                     RESOURCE_IN_FILE_ON_DISK)
886                                                 {
887                                                         ERROR("We were reading it from `%s'; "
888                                                               "maybe it changed while we were "
889                                                               "reading it.",
890                                                               next_lte->file_on_disk);
891                                                 }
892                                                 ret = WIMLIB_ERR_INVALID_RESOURCE_HASH;
893                                                 goto out;
894                                         }
895                                 }
896
897                                 // Advance to the next resource.
898                                 //
899                                 // If the next resource needs no compression, just write
900                                 // it with this thread (not now though--- we could be in
901                                 // the middle of writing another resource.)  Keep doing
902                                 // this until we either get to the end of the resources
903                                 // list, or we get to a resource that needs compression.
904                                 while (1) {
905                                         if (next_resource == stream_list) {
906                                                 // No more resources to send for
907                                                 // compression
908                                                 next_lte = NULL;
909                                                 break;
910                                         }
911                                         next_lte = container_of(next_resource,
912                                                                 struct wim_lookup_table_entry,
913                                                                 staging_list);
914                                         next_resource = next_resource->next;
915                                         if ((!(write_flags & WIMLIB_WRITE_FLAG_RECOMPRESS)
916                                                && wim_resource_compression_type(next_lte) == out_ctype)
917                                             || wim_resource_size(next_lte) == 0)
918                                         {
919                                                 list_add_tail(&next_lte->staging_list,
920                                                               &my_resources);
921                                         } else {
922                                                 list_add_tail(&next_lte->staging_list,
923                                                               &outstanding_resources);
924                                                 next_chunk = 0;
925                                                 next_num_chunks = wim_resource_chunks(next_lte);
926                                                 sha1_init(&next_sha_ctx);
927                                                 INIT_LIST_HEAD(&next_lte->msg_list);
928                                         #ifdef WITH_NTFS_3G
929                                                 ret = prepare_resource_for_read(next_lte, &ni);
930                                         #else
931                                                 ret = prepare_resource_for_read(next_lte);
932                                         #endif
933
934                                                 if (ret != 0)
935                                                         goto out;
936                                                 if (cur_lte == NULL) {
937                                                         // Set cur_lte for the
938                                                         // first time
939                                                         cur_lte = next_lte;
940                                                 }
941                                                 break;
942                                         }
943                                 }
944                         }
945
946                         if (next_lte == NULL) {
947                                 // No more resources to send for compression
948                                 break;
949                         }
950
951                         // Get a message from the available messages
952                         // list
953                         msg = container_of(available_msgs.next,
954                                            struct message,
955                                            list);
956
957                         // ... and delete it from the available messages
958                         // list
959                         list_del(&msg->list);
960
961                         // Initialize the message with the chunks to
962                         // compress.
963                         msg->num_chunks = min(next_num_chunks - next_chunk,
964                                               MAX_CHUNKS_PER_MSG);
965                         msg->lte = next_lte;
966                         msg->complete = false;
967                         msg->begin_chunk = next_chunk;
968
969                         unsigned size = WIM_CHUNK_SIZE;
970                         for (unsigned i = 0; i < msg->num_chunks; i++) {
971
972                                 // Read chunk @next_chunk of the stream into the
973                                 // message so that a compressor thread can
974                                 // compress it.
975
976                                 if (next_chunk == next_num_chunks - 1) {
977                                         size = MODULO_NONZERO(wim_resource_size(next_lte),
978                                                               WIM_CHUNK_SIZE);
979                                 }
980
981                                 DEBUG2("Read resource (size=%u, offset=%zu)",
982                                       size, next_chunk * WIM_CHUNK_SIZE);
983
984                                 msg->uncompressed_chunk_sizes[i] = size;
985
986                                 ret = read_wim_resource(next_lte,
987                                                         msg->uncompressed_chunks[i],
988                                                         size,
989                                                         next_chunk * WIM_CHUNK_SIZE,
990                                                         0);
991                                 if (ret != 0)
992                                         goto out;
993                                 sha1_update(&next_sha_ctx,
994                                             msg->uncompressed_chunks[i], size);
995                                 next_chunk++;
996                         }
997
998                         // Send the compression request
999                         list_add_tail(&msg->list, &next_lte->msg_list);
1000                         shared_queue_put(res_to_compress_queue, msg);
1001                         DEBUG2("Compression request sent");
1002                 }
1003
1004                 // If there are no outstanding resources, there are no more
1005                 // resources that need to be written.
1006                 if (list_empty(&outstanding_resources)) {
1007                         ret = 0;
1008                         goto out;
1009                 }
1010
1011                 // Get the next message from the queue and process it.
1012                 // The message will contain 1 or more data chunks that have been
1013                 // compressed.
1014                 msg = shared_queue_get(compressed_res_queue);
1015                 msg->complete = true;
1016
1017                 // Is this the next chunk in the current resource?  If it's not
1018                 // (i.e., an earlier chunk in a same or different resource
1019                 // hasn't been compressed yet), do nothing, and keep this
1020                 // message around until all earlier chunks are received.
1021                 //
1022                 // Otherwise, write all the chunks we can.
1023                 while (cur_lte != NULL &&
1024                        !list_empty(&cur_lte->msg_list) &&
1025                        (msg = container_of(cur_lte->msg_list.next,
1026                                            struct message,
1027                                            list))->complete)
1028                 {
1029                         DEBUG2("Complete msg (begin_chunk=%"PRIu64")", msg->begin_chunk);
1030                         if (msg->begin_chunk == 0) {
1031                                 DEBUG2("Begin chunk tab");
1032
1033                                 // This is the first set of chunks.  Leave space
1034                                 // for the chunk table in the output file.
1035                                 off_t cur_offset = ftello(out_fp);
1036                                 if (cur_offset == -1) {
1037                                         ret = WIMLIB_ERR_WRITE;
1038                                         goto out;
1039                                 }
1040                                 ret = begin_wim_resource_chunk_tab(cur_lte,
1041                                                                    out_fp,
1042                                                                    cur_offset,
1043                                                                    &cur_chunk_tab);
1044                                 if (ret != 0)
1045                                         goto out;
1046                         }
1047
1048                         // Write the compressed chunks from the message.
1049                         ret = write_wim_chunks(msg, out_fp, cur_chunk_tab);
1050                         if (ret != 0)
1051                                 goto out;
1052
1053                         list_del(&msg->list);
1054
1055                         // This message is available to use for different chunks
1056                         // now.
1057                         list_add(&msg->list, &available_msgs);
1058
1059                         // Was this the last chunk of the stream?  If so, finish
1060                         // it.
1061                         if (list_empty(&cur_lte->msg_list) &&
1062                             msg->begin_chunk + msg->num_chunks == cur_chunk_tab->num_chunks)
1063                         {
1064                                 DEBUG2("Finish wim chunk tab");
1065                                 u64 res_csize;
1066                                 ret = finish_wim_resource_chunk_tab(cur_chunk_tab,
1067                                                                     out_fp,
1068                                                                     &res_csize);
1069                                 if (ret != 0)
1070                                         goto out;
1071
1072                                 if (res_csize >= wim_resource_size(cur_lte)) {
1073                                         /* Oops!  We compressed the resource to
1074                                          * larger than the original size.  Write
1075                                          * the resource uncompressed instead. */
1076                                         ret = write_uncompressed_resource_and_truncate(
1077                                                          cur_lte,
1078                                                          out_fp,
1079                                                          cur_chunk_tab->file_offset,
1080                                                          &cur_lte->output_resource_entry);
1081                                         if (ret != 0)
1082                                                 goto out;
1083                                 } else {
1084                                         cur_lte->output_resource_entry.size =
1085                                                 res_csize;
1086
1087                                         cur_lte->output_resource_entry.original_size =
1088                                                 cur_lte->resource_entry.original_size;
1089
1090                                         cur_lte->output_resource_entry.offset =
1091                                                 cur_chunk_tab->file_offset;
1092
1093                                         cur_lte->output_resource_entry.flags =
1094                                                 cur_lte->resource_entry.flags |
1095                                                         WIM_RESHDR_FLAG_COMPRESSED;
1096                                 }
1097
1098                                 progress->write_streams.completed_bytes +=
1099                                                 wim_resource_size(cur_lte);
1100                                 progress->write_streams.completed_streams++;
1101
1102                                 if (progress_func) {
1103                                         progress_func(WIMLIB_PROGRESS_MSG_WRITE_STREAMS,
1104                                                       progress);
1105                                 }
1106
1107                                 FREE(cur_chunk_tab);
1108                                 cur_chunk_tab = NULL;
1109
1110                                 struct list_head *next = cur_lte->staging_list.next;
1111                                 list_del(&cur_lte->staging_list);
1112
1113                                 if (next == &outstanding_resources)
1114                                         cur_lte = NULL;
1115                                 else
1116                                         cur_lte = container_of(cur_lte->staging_list.next,
1117                                                                struct wim_lookup_table_entry,
1118                                                                staging_list);
1119
1120                                 // Since we just finished writing a stream,
1121                                 // write any streams that have been added to the
1122                                 // my_resources list for direct writing by the
1123                                 // main thread (e.g. resources that don't need
1124                                 // to be compressed because the desired
1125                                 // compression type is the same as the previous
1126                                 // compression type).
1127                                 ret = do_write_stream_list(&my_resources,
1128                                                            out_fp,
1129                                                            out_ctype,
1130                                                            progress_func,
1131                                                            progress,
1132                                                            0);
1133                                 if (ret != 0)
1134                                         goto out;
1135                         }
1136                 }
1137         }
1138
1139 out:
1140         if (ret == WIMLIB_ERR_NOMEM) {
1141                 ERROR("Could not allocate enough memory for "
1142                       "multi-threaded compression");
1143         }
1144
1145         if (next_lte) {
1146 #ifdef WITH_NTFS_3G
1147                 end_wim_resource_read(next_lte, ni);
1148 #else
1149                 end_wim_resource_read(next_lte);
1150 #endif
1151         }
1152
1153         if (ret == 0) {
1154                 ret = do_write_stream_list(&my_resources, out_fp,
1155                                            out_ctype, progress_func,
1156                                            progress, 0);
1157         } else {
1158                 if (msgs) {
1159                         size_t num_available_msgs = 0;
1160                         struct list_head *cur;
1161
1162                         list_for_each(cur, &available_msgs) {
1163                                 num_available_msgs++;
1164                         }
1165
1166                         while (num_available_msgs < num_messages) {
1167                                 shared_queue_get(compressed_res_queue);
1168                                 num_available_msgs++;
1169                         }
1170                 }
1171         }
1172
1173         if (msgs) {
1174                 for (size_t i = 0; i < num_messages; i++) {
1175                         for (size_t j = 0; j < MAX_CHUNKS_PER_MSG; j++) {
1176                                 FREE(msgs[i].compressed_chunks[j]);
1177                                 FREE(msgs[i].uncompressed_chunks[j]);
1178                         }
1179                 }
1180                 FREE(msgs);
1181         }
1182
1183         FREE(cur_chunk_tab);
1184         return ret;
1185 }
1186
1187
1188 static int write_stream_list_parallel(struct list_head *stream_list,
1189                                       FILE *out_fp,
1190                                       int out_ctype,
1191                                       int write_flags,
1192                                       unsigned num_threads,
1193                                       wimlib_progress_func_t progress_func,
1194                                       union wimlib_progress_info *progress)
1195 {
1196         int ret;
1197         struct shared_queue res_to_compress_queue;
1198         struct shared_queue compressed_res_queue;
1199         pthread_t *compressor_threads = NULL;
1200
1201         if (num_threads == 0) {
1202                 long nthreads = sysconf(_SC_NPROCESSORS_ONLN);
1203                 if (nthreads < 1) {
1204                         WARNING("Could not determine number of processors! Assuming 1");
1205                         goto out_serial;
1206                 } else {
1207                         num_threads = nthreads;
1208                 }
1209         }
1210
1211         progress->write_streams.num_threads = num_threads;
1212         wimlib_assert(stream_list->next != stream_list);
1213
1214         static const double MESSAGES_PER_THREAD = 2.0;
1215         size_t queue_size = (size_t)(num_threads * MESSAGES_PER_THREAD);
1216
1217         DEBUG("Initializing shared queues (queue_size=%zu)", queue_size);
1218
1219         ret = shared_queue_init(&res_to_compress_queue, queue_size);
1220         if (ret != 0)
1221                 goto out_serial;
1222
1223         ret = shared_queue_init(&compressed_res_queue, queue_size);
1224         if (ret != 0)
1225                 goto out_destroy_res_to_compress_queue;
1226
1227         struct compressor_thread_params params;
1228         params.res_to_compress_queue = &res_to_compress_queue;
1229         params.compressed_res_queue = &compressed_res_queue;
1230         params.compress = get_compress_func(out_ctype);
1231
1232         compressor_threads = MALLOC(num_threads * sizeof(pthread_t));
1233         if (!compressor_threads) {
1234                 ret = WIMLIB_ERR_NOMEM;
1235                 goto out_destroy_compressed_res_queue;
1236         }
1237
1238         for (unsigned i = 0; i < num_threads; i++) {
1239                 DEBUG("pthread_create thread %u", i);
1240                 ret = pthread_create(&compressor_threads[i], NULL,
1241                                      compressor_thread_proc, &params);
1242                 if (ret != 0) {
1243                         ret = -1;
1244                         ERROR_WITH_ERRNO("Failed to create compressor "
1245                                          "thread %u", i);
1246                         num_threads = i;
1247                         goto out_join;
1248                 }
1249         }
1250
1251         if (progress_func)
1252                 progress_func(WIMLIB_PROGRESS_MSG_WRITE_STREAMS, progress);
1253
1254         ret = main_writer_thread_proc(stream_list,
1255                                       out_fp,
1256                                       out_ctype,
1257                                       &res_to_compress_queue,
1258                                       &compressed_res_queue,
1259                                       queue_size,
1260                                       write_flags,
1261                                       progress_func,
1262                                       progress);
1263 out_join:
1264         for (unsigned i = 0; i < num_threads; i++)
1265                 shared_queue_put(&res_to_compress_queue, NULL);
1266
1267         for (unsigned i = 0; i < num_threads; i++) {
1268                 if (pthread_join(compressor_threads[i], NULL)) {
1269                         WARNING_WITH_ERRNO("Failed to join compressor "
1270                                            "thread %u", i);
1271                 }
1272         }
1273         FREE(compressor_threads);
1274 out_destroy_compressed_res_queue:
1275         shared_queue_destroy(&compressed_res_queue);
1276 out_destroy_res_to_compress_queue:
1277         shared_queue_destroy(&res_to_compress_queue);
1278         if (ret >= 0 && ret != WIMLIB_ERR_NOMEM)
1279                 return ret;
1280 out_serial:
1281         WARNING("Falling back to single-threaded compression");
1282         return write_stream_list_serial(stream_list,
1283                                         out_fp,
1284                                         out_ctype,
1285                                         write_flags,
1286                                         progress_func,
1287                                         progress);
1288
1289 }
1290 #endif
1291
1292 /*
1293  * Write a list of streams to a WIM (@out_fp) using the compression type
1294  * @out_ctype and up to @num_threads compressor threads.
1295  */
1296 static int write_stream_list(struct list_head *stream_list, FILE *out_fp,
1297                              int out_ctype, int write_flags,
1298                              unsigned num_threads,
1299                              wimlib_progress_func_t progress_func)
1300 {
1301         struct wim_lookup_table_entry *lte;
1302         size_t num_streams = 0;
1303         u64 total_bytes = 0;
1304         u64 total_compression_bytes = 0;
1305         union wimlib_progress_info progress;
1306
1307         list_for_each_entry(lte, stream_list, staging_list) {
1308                 num_streams++;
1309                 total_bytes += wim_resource_size(lte);
1310                 if (out_ctype != WIMLIB_COMPRESSION_TYPE_NONE
1311                        && (wim_resource_compression_type(lte) != out_ctype ||
1312                            (write_flags & WIMLIB_WRITE_FLAG_RECOMPRESS)))
1313                 {
1314                         total_compression_bytes += wim_resource_size(lte);
1315                 }
1316         }
1317         progress.write_streams.total_bytes       = total_bytes;
1318         progress.write_streams.total_streams     = num_streams;
1319         progress.write_streams.completed_bytes   = 0;
1320         progress.write_streams.completed_streams = 0;
1321         progress.write_streams.num_threads       = num_threads;
1322         progress.write_streams.compression_type  = out_ctype;
1323
1324 #ifdef ENABLE_MULTITHREADED_COMPRESSION
1325         if (total_compression_bytes >= 1000000 && num_threads != 1)
1326                 return write_stream_list_parallel(stream_list,
1327                                                   out_fp,
1328                                                   out_ctype,
1329                                                   write_flags,
1330                                                   num_threads,
1331                                                   progress_func,
1332                                                   &progress);
1333         else
1334 #endif
1335                 return write_stream_list_serial(stream_list,
1336                                                 out_fp,
1337                                                 out_ctype,
1338                                                 write_flags,
1339                                                 progress_func,
1340                                                 &progress);
1341 }
1342
1343 struct lte_overwrite_prepare_args {
1344         WIMStruct *wim;
1345         off_t end_offset;
1346         struct list_head *stream_list;
1347 };
1348
1349 static int lte_overwrite_prepare(struct wim_lookup_table_entry *lte, void *arg)
1350 {
1351         struct lte_overwrite_prepare_args *args = arg;
1352
1353         if (lte->resource_location == RESOURCE_IN_WIM &&
1354             lte->wim == args->wim &&
1355             lte->resource_entry.offset + lte->resource_entry.size > args->end_offset)
1356         {
1357         #ifdef ENABLE_ERROR_MESSAGES
1358                 ERROR("The following resource is after the XML data:");
1359                 print_lookup_table_entry(lte, stderr);
1360         #endif
1361                 return WIMLIB_ERR_RESOURCE_ORDER;
1362         }
1363
1364         lte->out_refcnt = lte->refcnt;
1365         memcpy(&lte->output_resource_entry, &lte->resource_entry,
1366                sizeof(struct resource_entry));
1367         if (!(lte->resource_entry.flags & WIM_RESHDR_FLAG_METADATA)) {
1368                 wimlib_assert(lte->resource_location != RESOURCE_NONEXISTENT);
1369                 if (lte->resource_location != RESOURCE_IN_WIM || lte->wim != args->wim)
1370                         list_add(&lte->staging_list, args->stream_list);
1371         }
1372         return 0;
1373 }
1374
1375 static int wim_find_new_streams(WIMStruct *wim, off_t end_offset,
1376                                 struct list_head *stream_list)
1377 {
1378         struct lte_overwrite_prepare_args args = {
1379                 .wim         = wim,
1380                 .end_offset  = end_offset,
1381                 .stream_list = stream_list,
1382         };
1383
1384         return for_lookup_table_entry(wim->lookup_table,
1385                                       lte_overwrite_prepare, &args);
1386 }
1387
1388 static int inode_find_streams_to_write(struct wim_inode *inode,
1389                                        struct wim_lookup_table *table,
1390                                        struct list_head *stream_list)
1391 {
1392         struct wim_lookup_table_entry *lte;
1393         for (unsigned i = 0; i <= inode->i_num_ads; i++) {
1394                 lte = inode_stream_lte(inode, i, table);
1395                 if (lte) {
1396                         if (lte->out_refcnt == 0)
1397                                 list_add_tail(&lte->staging_list, stream_list);
1398                         lte->out_refcnt += inode->i_nlink;
1399                 }
1400         }
1401         return 0;
1402 }
1403
1404 static int image_find_streams_to_write(WIMStruct *w)
1405 {
1406         struct wim_inode *inode;
1407         struct hlist_node *cur;
1408         struct hlist_head *inode_list;
1409
1410         inode_list = &wim_get_current_image_metadata(w)->inode_list;
1411         hlist_for_each_entry(inode, cur, inode_list, i_hlist) {
1412                 inode_find_streams_to_write(inode, w->lookup_table,
1413                                             (struct list_head*)w->private);
1414         }
1415         return 0;
1416 }
1417
1418 static int write_wim_streams(WIMStruct *w, int image, int write_flags,
1419                              unsigned num_threads,
1420                              wimlib_progress_func_t progress_func)
1421 {
1422
1423         for_lookup_table_entry(w->lookup_table, lte_zero_out_refcnt, NULL);
1424         LIST_HEAD(stream_list);
1425         w->private = &stream_list;
1426         for_image(w, image, image_find_streams_to_write);
1427         return write_stream_list(&stream_list, w->out_fp,
1428                                  wimlib_get_compression_type(w), write_flags,
1429                                  num_threads, progress_func);
1430 }
1431
1432 /*
1433  * Finish writing a WIM file: write the lookup table, xml data, and integrity
1434  * table (optional), then overwrite the WIM header.
1435  *
1436  * write_flags is a bitwise OR of the following:
1437  *
1438  *      (public)  WIMLIB_WRITE_FLAG_CHECK_INTEGRITY:
1439  *              Include an integrity table.
1440  *
1441  *      (public)  WIMLIB_WRITE_FLAG_SHOW_PROGRESS:
1442  *              Show progress information when (if) writing the integrity table.
1443  *
1444  *      (private) WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE:
1445  *              Don't write the lookup table.
1446  *
1447  *      (private) WIMLIB_WRITE_FLAG_REUSE_INTEGRITY_TABLE:
1448  *              When (if) writing the integrity table, re-use entries from the
1449  *              existing integrity table, if possible.
1450  *
1451  *      (private) WIMLIB_WRITE_FLAG_CHECKPOINT_AFTER_XML:
1452  *              After writing the XML data but before writing the integrity
1453  *              table, write a temporary WIM header and flush the stream so that
1454  *              the WIM is less likely to become corrupted upon abrupt program
1455  *              termination.
1456  *
1457  *      (private) WIMLIB_WRITE_FLAG_FSYNC:
1458  *              fsync() the output file before closing it.
1459  *
1460  */
1461 int finish_write(WIMStruct *w, int image, int write_flags,
1462                  wimlib_progress_func_t progress_func)
1463 {
1464         int ret;
1465         struct wim_header hdr;
1466         FILE *out = w->out_fp;
1467
1468         /* @hdr will be the header for the new WIM.  First copy all the data
1469          * from the header in the WIMStruct; then set all the fields that may
1470          * have changed, including the resource entries, boot index, and image
1471          * count.  */
1472         memcpy(&hdr, &w->hdr, sizeof(struct wim_header));
1473
1474         if (!(write_flags & WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE)) {
1475                 ret = write_lookup_table(w->lookup_table, out, &hdr.lookup_table_res_entry);
1476                 if (ret != 0)
1477                         goto out;
1478         }
1479
1480         ret = write_xml_data(w->wim_info, image, out,
1481                              (write_flags & WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE) ?
1482                               wim_info_get_total_bytes(w->wim_info) : 0,
1483                              &hdr.xml_res_entry);
1484         if (ret != 0)
1485                 goto out;
1486
1487         if (write_flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) {
1488                 if (write_flags & WIMLIB_WRITE_FLAG_CHECKPOINT_AFTER_XML) {
1489                         struct wim_header checkpoint_hdr;
1490                         memcpy(&checkpoint_hdr, &hdr, sizeof(struct wim_header));
1491                         memset(&checkpoint_hdr.integrity, 0, sizeof(struct resource_entry));
1492                         if (fseeko(out, 0, SEEK_SET) != 0) {
1493                                 ERROR_WITH_ERRNO("Failed to seek to beginning "
1494                                                  "of WIM being written");
1495                                 ret = WIMLIB_ERR_WRITE;
1496                                 goto out;
1497                         }
1498                         ret = write_header(&checkpoint_hdr, out);
1499                         if (ret != 0)
1500                                 goto out;
1501
1502                         if (fflush(out) != 0) {
1503                                 ERROR_WITH_ERRNO("Can't write data to WIM");
1504                                 ret = WIMLIB_ERR_WRITE;
1505                                 goto out;
1506                         }
1507
1508                         if (fseeko(out, 0, SEEK_END) != 0) {
1509                                 ERROR_WITH_ERRNO("Failed to seek to end "
1510                                                  "of WIM being written");
1511                                 ret = WIMLIB_ERR_WRITE;
1512                                 goto out;
1513                         }
1514                 }
1515
1516                 off_t old_lookup_table_end;
1517                 off_t new_lookup_table_end;
1518                 if (write_flags & WIMLIB_WRITE_FLAG_REUSE_INTEGRITY_TABLE) {
1519                         old_lookup_table_end = w->hdr.lookup_table_res_entry.offset +
1520                                                w->hdr.lookup_table_res_entry.size;
1521                 } else {
1522                         old_lookup_table_end = 0;
1523                 }
1524                 new_lookup_table_end = hdr.lookup_table_res_entry.offset +
1525                                        hdr.lookup_table_res_entry.size;
1526
1527                 ret = write_integrity_table(out,
1528                                             &hdr.integrity,
1529                                             new_lookup_table_end,
1530                                             old_lookup_table_end,
1531                                             progress_func);
1532                 if (ret != 0)
1533                         goto out;
1534         } else {
1535                 memset(&hdr.integrity, 0, sizeof(struct resource_entry));
1536         }
1537
1538         /*
1539          * In the WIM header, there is room for the resource entry for a
1540          * metadata resource labeled as the "boot metadata".  This entry should
1541          * be zeroed out if there is no bootable image (boot_idx 0).  Otherwise,
1542          * it should be a copy of the resource entry for the image that is
1543          * marked as bootable.  This is not well documented...
1544          */
1545
1546         /* Set image count and boot index correctly for single image writes */
1547         if (image != WIMLIB_ALL_IMAGES) {
1548                 hdr.image_count = 1;
1549                 if (hdr.boot_idx == image)
1550                         hdr.boot_idx = 1;
1551                 else
1552                         hdr.boot_idx = 0;
1553         }
1554
1555         if (hdr.boot_idx == 0) {
1556                 memset(&hdr.boot_metadata_res_entry, 0,
1557                        sizeof(struct resource_entry));
1558         } else {
1559                 memcpy(&hdr.boot_metadata_res_entry,
1560                        &w->image_metadata[
1561                           hdr.boot_idx - 1].metadata_lte->output_resource_entry,
1562                        sizeof(struct resource_entry));
1563         }
1564
1565         if (fseeko(out, 0, SEEK_SET) != 0) {
1566                 ERROR_WITH_ERRNO("Failed to seek to beginning of WIM "
1567                                  "being written");
1568                 ret = WIMLIB_ERR_WRITE;
1569                 goto out;
1570         }
1571
1572         ret = write_header(&hdr, out);
1573         if (ret != 0)
1574                 goto out;
1575
1576         if (write_flags & WIMLIB_WRITE_FLAG_FSYNC) {
1577                 if (fflush(out) != 0
1578                     || fsync(fileno(out)) != 0)
1579                 {
1580                         ERROR_WITH_ERRNO("Error flushing data to WIM file");
1581                         ret = WIMLIB_ERR_WRITE;
1582                 }
1583         }
1584 out:
1585         if (fclose(out) != 0) {
1586                 ERROR_WITH_ERRNO("Failed to close the WIM file");
1587                 if (ret == 0)
1588                         ret = WIMLIB_ERR_WRITE;
1589         }
1590         w->out_fp = NULL;
1591         return ret;
1592 }
1593
1594 #if defined(HAVE_SYS_FILE_H) && defined(HAVE_FLOCK)
1595 int lock_wim(WIMStruct *w, FILE *fp)
1596 {
1597         int ret = 0;
1598         if (fp && !w->wim_locked) {
1599                 ret = flock(fileno(fp), LOCK_EX | LOCK_NB);
1600                 if (ret != 0) {
1601                         if (errno == EWOULDBLOCK) {
1602                                 ERROR("`%s' is already being modified or has been "
1603                                       "mounted read-write\n"
1604                                       "        by another process!", w->filename);
1605                                 ret = WIMLIB_ERR_ALREADY_LOCKED;
1606                         } else {
1607                                 WARNING_WITH_ERRNO("Failed to lock `%s'",
1608                                                    w->filename);
1609                                 ret = 0;
1610                         }
1611                 } else {
1612                         w->wim_locked = 1;
1613                 }
1614         }
1615         return ret;
1616 }
1617 #endif
1618
1619 static int open_wim_writable(WIMStruct *w, const char *path,
1620                              bool trunc, bool readable)
1621 {
1622         const char *mode;
1623         if (trunc)
1624                 if (readable)
1625                         mode = "w+b";
1626                 else
1627                         mode = "wb";
1628         else
1629                 mode = "r+b";
1630
1631         wimlib_assert(w->out_fp == NULL);
1632         w->out_fp = fopen(path, mode);
1633         if (w->out_fp) {
1634                 return 0;
1635         } else {
1636                 ERROR_WITH_ERRNO("Failed to open `%s' for writing", path);
1637                 return WIMLIB_ERR_OPEN;
1638         }
1639 }
1640
1641
1642 void close_wim_writable(WIMStruct *w)
1643 {
1644         if (w->out_fp) {
1645                 if (fclose(w->out_fp) != 0) {
1646                         WARNING_WITH_ERRNO("Failed to close output WIM");
1647                 }
1648                 w->out_fp = NULL;
1649         }
1650 }
1651
1652 /* Open file stream and write dummy header for WIM. */
1653 int begin_write(WIMStruct *w, const char *path, int write_flags)
1654 {
1655         int ret;
1656         ret = open_wim_writable(w, path, true,
1657                                 (write_flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) != 0);
1658         if (ret != 0)
1659                 return ret;
1660         /* Write dummy header. It will be overwritten later. */
1661         return write_header(&w->hdr, w->out_fp);
1662 }
1663
1664 /* Writes a stand-alone WIM to a file.  */
1665 WIMLIBAPI int wimlib_write(WIMStruct *w, const char *path,
1666                            int image, int write_flags, unsigned num_threads,
1667                            wimlib_progress_func_t progress_func)
1668 {
1669         int ret;
1670
1671         if (!path)
1672                 return WIMLIB_ERR_INVALID_PARAM;
1673
1674         write_flags &= WIMLIB_WRITE_MASK_PUBLIC;
1675
1676         if (image != WIMLIB_ALL_IMAGES &&
1677              (image < 1 || image > w->hdr.image_count))
1678                 return WIMLIB_ERR_INVALID_IMAGE;
1679
1680         if (w->hdr.total_parts != 1) {
1681                 ERROR("Cannot call wimlib_write() on part of a split WIM");
1682                 return WIMLIB_ERR_SPLIT_UNSUPPORTED;
1683         }
1684
1685         ret = begin_write(w, path, write_flags);
1686         if (ret != 0)
1687                 goto out;
1688
1689         ret = write_wim_streams(w, image, write_flags, num_threads,
1690                                 progress_func);
1691         if (ret != 0)
1692                 goto out;
1693
1694         if (progress_func)
1695                 progress_func(WIMLIB_PROGRESS_MSG_WRITE_METADATA_BEGIN, NULL);
1696
1697         ret = for_image(w, image, write_metadata_resource);
1698         if (ret != 0)
1699                 goto out;
1700
1701         if (progress_func)
1702                 progress_func(WIMLIB_PROGRESS_MSG_WRITE_METADATA_END, NULL);
1703
1704         ret = finish_write(w, image, write_flags, progress_func);
1705 out:
1706         close_wim_writable(w);
1707         return ret;
1708 }
1709
1710 static bool any_images_modified(WIMStruct *w)
1711 {
1712         for (int i = 0; i < w->hdr.image_count; i++)
1713                 if (w->image_metadata[i].modified)
1714                         return true;
1715         return false;
1716 }
1717
1718 /*
1719  * Overwrite a WIM, possibly appending streams to it.
1720  *
1721  * A WIM looks like (or is supposed to look like) the following:
1722  *
1723  *                   Header (212 bytes)
1724  *                   Streams and metadata resources (variable size)
1725  *                   Lookup table (variable size)
1726  *                   XML data (variable size)
1727  *                   Integrity table (optional) (variable size)
1728  *
1729  * If we are not adding any streams or metadata resources, the lookup table is
1730  * unchanged--- so we only need to overwrite the XML data, integrity table, and
1731  * header.  This operation is potentially unsafe if the program is abruptly
1732  * terminated while the XML data or integrity table are being overwritten, but
1733  * before the new header has been written.  To partially alleviate this problem,
1734  * a special flag (WIMLIB_WRITE_FLAG_CHECKPOINT_AFTER_XML) is passed to
1735  * finish_write() to cause a temporary WIM header to be written after the XML
1736  * data has been written.  This may prevent the WIM from becoming corrupted if
1737  * the program is terminated while the integrity table is being calculated (but
1738  * no guarantees, due to write re-ordering...).
1739  *
1740  * If we are adding new streams or images (metadata resources), the lookup table
1741  * needs to be changed, and those streams need to be written.  In this case, we
1742  * try to perform a safe update of the WIM file by writing the streams *after*
1743  * the end of the previous WIM, then writing the new lookup table, XML data, and
1744  * (optionally) integrity table following the new streams.  This will produce a
1745  * layout like the following:
1746  *
1747  *                   Header (212 bytes)
1748  *                   (OLD) Streams and metadata resources (variable size)
1749  *                   (OLD) Lookup table (variable size)
1750  *                   (OLD) XML data (variable size)
1751  *                   (OLD) Integrity table (optional) (variable size)
1752  *                   (NEW) Streams and metadata resources (variable size)
1753  *                   (NEW) Lookup table (variable size)
1754  *                   (NEW) XML data (variable size)
1755  *                   (NEW) Integrity table (optional) (variable size)
1756  *
1757  * At all points, the WIM is valid as nothing points to the new data yet.  Then,
1758  * the header is overwritten to point to the new lookup table, XML data, and
1759  * integrity table, to produce the following layout:
1760  *
1761  *                   Header (212 bytes)
1762  *                   Streams and metadata resources (variable size)
1763  *                   Nothing (variable size)
1764  *                   More Streams and metadata resources (variable size)
1765  *                   Lookup table (variable size)
1766  *                   XML data (variable size)
1767  *                   Integrity table (optional) (variable size)
1768  *
1769  * This method allows an image to be appended to a large WIM very quickly, and
1770  * is is crash-safe except in the case of write re-ordering, but the
1771  * disadvantage is that a small hole is left in the WIM where the old lookup
1772  * table, xml data, and integrity table were.  (These usually only take up a
1773  * small amount of space compared to the streams, however.)
1774  */
1775 static int overwrite_wim_inplace(WIMStruct *w, int write_flags,
1776                                  unsigned num_threads,
1777                                  wimlib_progress_func_t progress_func)
1778 {
1779         int ret;
1780         struct list_head stream_list;
1781         off_t old_wim_end;
1782         bool found_modified_image;
1783
1784         DEBUG("Overwriting `%s' in-place", w->filename);
1785
1786         /* Make sure that the integrity table (if present) is after the XML
1787          * data, and that there are no stream resources, metadata resources, or
1788          * lookup tables after the XML data.  Otherwise, these data would be
1789          * overwritten. */
1790         if (w->hdr.integrity.offset != 0 &&
1791             w->hdr.integrity.offset < w->hdr.xml_res_entry.offset) {
1792                 ERROR("Didn't expect the integrity table to be before the XML data");
1793                 return WIMLIB_ERR_RESOURCE_ORDER;
1794         }
1795
1796         if (w->hdr.lookup_table_res_entry.offset > w->hdr.xml_res_entry.offset) {
1797                 ERROR("Didn't expect the lookup table to be after the XML data");
1798                 return WIMLIB_ERR_RESOURCE_ORDER;
1799         }
1800
1801
1802         if (w->hdr.integrity.offset)
1803                 old_wim_end = w->hdr.integrity.offset + w->hdr.integrity.size;
1804         else
1805                 old_wim_end = w->hdr.xml_res_entry.offset + w->hdr.xml_res_entry.size;
1806
1807         if (!w->deletion_occurred && !any_images_modified(w)) {
1808                 /* If no images have been modified and no images have been
1809                  * deleted, a new lookup table does not need to be written. */
1810                 old_wim_end = w->hdr.lookup_table_res_entry.offset +
1811                               w->hdr.lookup_table_res_entry.size;
1812                 write_flags |= WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE |
1813                                WIMLIB_WRITE_FLAG_CHECKPOINT_AFTER_XML;
1814         }
1815         INIT_LIST_HEAD(&stream_list);
1816         ret = wim_find_new_streams(w, old_wim_end, &stream_list);
1817         if (ret != 0)
1818                 return ret;
1819
1820         ret = open_wim_writable(w, w->filename, false,
1821                                 (write_flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) != 0);
1822         if (ret != 0)
1823                 return ret;
1824
1825         ret = lock_wim(w, w->out_fp);
1826         if (ret != 0) {
1827                 fclose(w->out_fp);
1828                 w->out_fp = NULL;
1829                 return ret;
1830         }
1831
1832         if (fseeko(w->out_fp, old_wim_end, SEEK_SET) != 0) {
1833                 ERROR_WITH_ERRNO("Can't seek to end of WIM");
1834                 fclose(w->out_fp);
1835                 w->out_fp = NULL;
1836                 w->wim_locked = 0;
1837                 return WIMLIB_ERR_WRITE;
1838         }
1839
1840         if (!list_empty(&stream_list)) {
1841                 DEBUG("Writing newly added streams (offset = %"PRIu64")",
1842                       old_wim_end);
1843                 ret = write_stream_list(&stream_list, w->out_fp,
1844                                         wimlib_get_compression_type(w),
1845                                         write_flags, num_threads,
1846                                         progress_func);
1847                 if (ret != 0)
1848                         goto out_ftruncate;
1849         } else {
1850                 DEBUG("No new streams were added");
1851         }
1852
1853         found_modified_image = false;
1854         for (int i = 0; i < w->hdr.image_count; i++) {
1855                 if (!found_modified_image)
1856                         found_modified_image = w->image_metadata[i].modified;
1857                 if (found_modified_image) {
1858                         select_wim_image(w, i + 1);
1859                         ret = write_metadata_resource(w);
1860                         if (ret != 0)
1861                                 goto out_ftruncate;
1862                 }
1863         }
1864         write_flags |= WIMLIB_WRITE_FLAG_REUSE_INTEGRITY_TABLE;
1865         ret = finish_write(w, WIMLIB_ALL_IMAGES, write_flags,
1866                            progress_func);
1867 out_ftruncate:
1868         close_wim_writable(w);
1869         if (ret != 0 && !(write_flags & WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE)) {
1870                 WARNING("Truncating `%s' to its original size (%"PRIu64" bytes)",
1871                         w->filename, old_wim_end);
1872                 /* Return value of truncate() is ignored because this is already
1873                  * an error path. */
1874                 (void)truncate(w->filename, old_wim_end);
1875         }
1876         w->wim_locked = 0;
1877         return ret;
1878 }
1879
1880 static int overwrite_wim_via_tmpfile(WIMStruct *w, int write_flags,
1881                                      unsigned num_threads,
1882                                      wimlib_progress_func_t progress_func)
1883 {
1884         size_t wim_name_len;
1885         int ret;
1886
1887         DEBUG("Overwriting `%s' via a temporary file", w->filename);
1888
1889         /* Write the WIM to a temporary file in the same directory as the
1890          * original WIM. */
1891         wim_name_len = strlen(w->filename);
1892         char tmpfile[wim_name_len + 10];
1893         memcpy(tmpfile, w->filename, wim_name_len);
1894         randomize_char_array_with_alnum(tmpfile + wim_name_len, 9);
1895         tmpfile[wim_name_len + 9] = '\0';
1896
1897         ret = wimlib_write(w, tmpfile, WIMLIB_ALL_IMAGES,
1898                            write_flags | WIMLIB_WRITE_FLAG_FSYNC,
1899                            num_threads, progress_func);
1900         if (ret != 0) {
1901                 ERROR("Failed to write the WIM file `%s'", tmpfile);
1902                 goto err;
1903         }
1904
1905         DEBUG("Renaming `%s' to `%s'", tmpfile, w->filename);
1906
1907         /* Rename the new file to the old file .*/
1908         if (rename(tmpfile, w->filename) != 0) {
1909                 ERROR_WITH_ERRNO("Failed to rename `%s' to `%s'",
1910                                  tmpfile, w->filename);
1911                 ret = WIMLIB_ERR_RENAME;
1912                 goto err;
1913         }
1914
1915         if (progress_func) {
1916                 union wimlib_progress_info progress;
1917                 progress.rename.from = tmpfile;
1918                 progress.rename.to = w->filename;
1919                 progress_func(WIMLIB_PROGRESS_MSG_RENAME, &progress);
1920         }
1921
1922         /* Close the original WIM file that was opened for reading. */
1923         if (w->fp != NULL) {
1924                 fclose(w->fp);
1925                 w->fp = NULL;
1926         }
1927
1928         /* Re-open the WIM read-only. */
1929         w->fp = fopen(w->filename, "rb");
1930         if (w->fp == NULL) {
1931                 ret = WIMLIB_ERR_REOPEN;
1932                 WARNING_WITH_ERRNO("Failed to re-open `%s' read-only",
1933                                    w->filename);
1934                 FREE(w->filename);
1935                 w->filename = NULL;
1936         }
1937         return ret;
1938 err:
1939         /* Remove temporary file. */
1940         if (unlink(tmpfile) != 0)
1941                 WARNING_WITH_ERRNO("Failed to remove `%s'", tmpfile);
1942         return ret;
1943 }
1944
1945 /*
1946  * Writes a WIM file to the original file that it was read from, overwriting it.
1947  */
1948 WIMLIBAPI int wimlib_overwrite(WIMStruct *w, int write_flags,
1949                                unsigned num_threads,
1950                                wimlib_progress_func_t progress_func)
1951 {
1952         write_flags &= WIMLIB_WRITE_MASK_PUBLIC;
1953
1954         if (!w->filename)
1955                 return WIMLIB_ERR_NO_FILENAME;
1956
1957         if (w->hdr.total_parts != 1) {
1958                 ERROR("Cannot modify a split WIM");
1959                 return WIMLIB_ERR_SPLIT_UNSUPPORTED;
1960         }
1961
1962         if ((!w->deletion_occurred || (write_flags & WIMLIB_WRITE_FLAG_SOFT_DELETE))
1963             && !(write_flags & WIMLIB_WRITE_FLAG_REBUILD))
1964         {
1965                 int ret;
1966                 ret = overwrite_wim_inplace(w, write_flags, num_threads,
1967                                             progress_func);
1968                 if (ret == WIMLIB_ERR_RESOURCE_ORDER)
1969                         WARNING("Falling back to re-building entire WIM");
1970                 else
1971                         return ret;
1972         }
1973         return overwrite_wim_via_tmpfile(w, write_flags, num_threads,
1974                                          progress_func);
1975 }