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