]> wimlib.net Git - wimlib/blob - src/resource.c
90e45ca1092db0eb3f0a65617734613beceab1c0
[wimlib] / src / resource.c
1 /*
2  * resource.c
3  *
4  * Read uncompressed and compressed metadata and file resources from a WIM file.
5  */
6
7 /*
8  * Copyright (C) 2012, 2013 Eric Biggers
9  *
10  * This file is part of wimlib, a library for working with WIM files.
11  *
12  * wimlib is free software; you can redistribute it and/or modify it under the
13  * terms of the GNU General Public License as published by the Free Software
14  * Foundation; either version 3 of the License, or (at your option) any later
15  * version.
16  *
17  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
18  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
19  * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License along with
22  * wimlib; if not, see http://www.gnu.org/licenses/.
23  */
24
25 #include "wimlib_internal.h"
26 #include "dentry.h"
27 #include "lookup_table.h"
28 #include "buffer_io.h"
29 #include "sha1.h"
30
31 #ifdef __WIN32__
32 #  include "win32.h"
33 #endif
34
35 #include <errno.h>
36 #include <stdarg.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <fcntl.h>
40
41 /* Write @n bytes from @buf to the file descriptor @fd, retrying on internupt
42  * and on short writes.
43  *
44  * Returns short count and set errno on failure. */
45 static ssize_t
46 full_write(int fd, const void *buf, size_t n)
47 {
48         const void *p = buf;
49         ssize_t ret;
50         ssize_t total = 0;
51
52         while (total != n) {
53                 ret = write(fd, p, n);
54                 if (ret < 0) {
55                         if (errno == EINTR)
56                                 continue;
57                         else
58                                 break;
59                 }
60                 total += ret;
61                 p += ret;
62         }
63         return total;
64 }
65
66 /* Read @n bytes from the file descriptor @fd to the buffer @buf, retrying on
67  * internupt and on short reads.
68  *
69  * Returns short count and set errno on failure. */
70 static size_t
71 full_read(int fd, void *buf, size_t n)
72 {
73         size_t bytes_remaining = n;
74         while (bytes_remaining) {
75                 ssize_t bytes_read = read(fd, buf, bytes_remaining);
76                 if (bytes_read < 0) {
77                         if (errno == EINTR)
78                                 continue;
79                         break;
80                 }
81                 bytes_remaining -= bytes_read;
82                 buf += bytes_read;
83         }
84         return n - bytes_remaining;
85 }
86
87 /*
88  * Reads all or part of a compressed WIM resource.
89  *
90  * Returns zero on success, nonzero on failure.
91  */
92 static int
93 read_compressed_resource(FILE *fp, u64 resource_compressed_size,
94                          u64 resource_uncompressed_size,
95                          u64 resource_offset, int resource_ctype,
96                          u64 len, u64 offset,
97                          consume_data_callback_t cb,
98                          void *ctx_or_buf)
99 {
100         int ret;
101
102         /* Trivial case */
103         if (len == 0)
104                 return 0;
105
106         int (*decompress)(const void *, unsigned, void *, unsigned);
107         /* Set the appropriate decompress function. */
108         if (resource_ctype == WIMLIB_COMPRESSION_TYPE_LZX)
109                 decompress = wimlib_lzx_decompress;
110         else
111                 decompress = wimlib_xpress_decompress;
112
113         /* The structure of a compressed resource consists of a table of chunk
114          * offsets followed by the chunks themselves.  Each chunk consists of
115          * compressed data, and there is one chunk for each WIM_CHUNK_SIZE =
116          * 32768 bytes of the uncompressed file, with the last chunk having any
117          * remaining bytes.
118          *
119          * The chunk offsets are measured relative to the end of the chunk
120          * table.  The first chunk is omitted from the table in the WIM file
121          * because its offset is implicitly given by the fact that it directly
122          * follows the chunk table and therefore must have an offset of 0.
123          */
124
125         /* Calculate how many chunks the resource conists of in its entirety. */
126         u64 num_chunks = (resource_uncompressed_size + WIM_CHUNK_SIZE - 1) /
127                                                                 WIM_CHUNK_SIZE;
128         /* As mentioned, the first chunk has no entry in the chunk table. */
129         u64 num_chunk_entries = num_chunks - 1;
130
131
132         /* The index of the chunk that the read starts at. */
133         u64 start_chunk = offset / WIM_CHUNK_SIZE;
134         /* The byte offset at which the read starts, within the start chunk. */
135         u64 start_chunk_offset = offset % WIM_CHUNK_SIZE;
136
137         /* The index of the chunk that contains the last byte of the read. */
138         u64 end_chunk   = (offset + len - 1) / WIM_CHUNK_SIZE;
139         /* The byte offset of the last byte of the read, within the end chunk */
140         u64 end_chunk_offset = (offset + len - 1) % WIM_CHUNK_SIZE;
141
142         /* Number of chunks that are actually needed to read the requested part
143          * of the file. */
144         u64 num_needed_chunks = end_chunk - start_chunk + 1;
145
146         /* If the end chunk is not the last chunk, an extra chunk entry is
147          * needed because we need to know the offset of the chunk after the last
148          * chunk read to figure out the size of the last read chunk. */
149         if (end_chunk != num_chunks - 1)
150                 num_needed_chunks++;
151
152         /* Declare the chunk table.  It will only contain offsets for the chunks
153          * that are actually needed for this read. */
154         u64 chunk_offsets[num_needed_chunks];
155
156         /* Set the implicit offset of the first chunk if it is included in the
157          * needed chunks.
158          *
159          * Note: M$'s documentation includes a picture that shows the first
160          * chunk starting right after the chunk entry table, labeled as offset
161          * 0x10.  However, in the actual file format, the offset is measured
162          * from the end of the chunk entry table, so the first chunk has an
163          * offset of 0. */
164         if (start_chunk == 0)
165                 chunk_offsets[0] = 0;
166
167         /* According to M$'s documentation, if the uncompressed size of
168          * the file is greater than 4 GB, the chunk entries are 8-byte
169          * integers.  Otherwise, they are 4-byte integers. */
170         u64 chunk_entry_size = (resource_uncompressed_size >= (u64)1 << 32) ?
171                                                                         8 : 4;
172
173         /* Size of the full chunk table in the WIM file. */
174         u64 chunk_table_size = chunk_entry_size * num_chunk_entries;
175
176         /* Read the needed chunk offsets from the table in the WIM file. */
177
178         /* Index, in the WIM file, of the first needed entry in the
179          * chunk table. */
180         u64 start_table_idx = (start_chunk == 0) ? 0 : start_chunk - 1;
181
182         /* Number of entries we need to actually read from the chunk
183          * table (excludes the implicit first chunk). */
184         u64 num_needed_chunk_entries = (start_chunk == 0) ?
185                                 num_needed_chunks - 1 : num_needed_chunks;
186
187         /* Skip over unneeded chunk table entries. */
188         u64 file_offset_of_needed_chunk_entries = resource_offset +
189                                 start_table_idx * chunk_entry_size;
190         if (fseeko(fp, file_offset_of_needed_chunk_entries, SEEK_SET))
191                 goto read_error;
192
193         /* Number of bytes we need to read from the chunk table. */
194         size_t size = num_needed_chunk_entries * chunk_entry_size;
195
196         {
197                 u8 chunk_tab_buf[size];
198
199                 if (fread(chunk_tab_buf, 1, size, fp) != size)
200                         goto read_error;
201
202                 /* Now fill in chunk_offsets from the entries we have read in
203                  * chunk_tab_buf. */
204
205                 u64 *chunk_tab_p = chunk_offsets;
206                 if (start_chunk == 0)
207                         chunk_tab_p++;
208
209                 if (chunk_entry_size == 4) {
210                         u32 *entries = (u32*)chunk_tab_buf;
211                         while (num_needed_chunk_entries--)
212                                 *chunk_tab_p++ = le32_to_cpu(*entries++);
213                 } else {
214                         u64 *entries = (u64*)chunk_tab_buf;
215                         while (num_needed_chunk_entries--)
216                                 *chunk_tab_p++ = le64_to_cpu(*entries++);
217                 }
218         }
219
220         /* Done with the chunk table now.  We must now seek to the first chunk
221          * that is needed for the read. */
222
223         u64 file_offset_of_first_needed_chunk = resource_offset +
224                                 chunk_table_size + chunk_offsets[0];
225         if (fseeko(fp, file_offset_of_first_needed_chunk, SEEK_SET))
226                 goto read_error;
227
228         /* Pointer to current position in the output buffer for uncompressed
229          * data. */
230         u8 *out_p;
231         if (cb)
232                 out_p = alloca(32768);
233         else
234                 out_p = ctx_or_buf;
235
236         /* Buffer for compressed data.  While most compressed chunks will have a
237          * size much less than WIM_CHUNK_SIZE, WIM_CHUNK_SIZE - 1 is the maximum
238          * size in the worst-case.  This assumption is valid only if chunks that
239          * happen to compress to more than the uncompressed size (i.e. a
240          * sequence of random bytes) are always stored uncompressed. But this seems
241          * to be the case in M$'s WIM files, even though it is undocumented. */
242         void *compressed_buf = alloca(WIM_CHUNK_SIZE - 1);
243
244         /* Decompress all the chunks. */
245         for (u64 i = start_chunk; i <= end_chunk; i++) {
246
247                 /* Calculate the sizes of the compressed chunk and of the
248                  * uncompressed chunk. */
249                 unsigned compressed_chunk_size;
250                 unsigned uncompressed_chunk_size;
251                 if (i != num_chunks - 1) {
252                         /* All the chunks except the last one in the resource
253                          * expand to WIM_CHUNK_SIZE uncompressed, and the amount
254                          * of compressed data for the chunk is given by the
255                          * difference of offsets in the chunk offset table. */
256                         compressed_chunk_size = chunk_offsets[i + 1 - start_chunk] -
257                                                 chunk_offsets[i - start_chunk];
258                         uncompressed_chunk_size = WIM_CHUNK_SIZE;
259                 } else {
260                         /* The last compressed chunk consists of the remaining
261                          * bytes in the file resource, and the last uncompressed
262                          * chunk has size equal to however many bytes are left-
263                          * that is, the remainder of the uncompressed size when
264                          * divided by WIM_CHUNK_SIZE.
265                          *
266                          * Note that the resource_compressed_size includes the
267                          * chunk table, so the size of it must be subtracted. */
268                         compressed_chunk_size = resource_compressed_size -
269                                                 chunk_table_size -
270                                                 chunk_offsets[i - start_chunk];
271
272                         uncompressed_chunk_size = resource_uncompressed_size %
273                                                                 WIM_CHUNK_SIZE;
274
275                         /* If the remainder is 0, the last chunk actually
276                          * uncompresses to a full WIM_CHUNK_SIZE bytes. */
277                         if (uncompressed_chunk_size == 0)
278                                 uncompressed_chunk_size = WIM_CHUNK_SIZE;
279                 }
280
281                 /* Figure out how much of this chunk we actually need to read */
282                 u64 start_offset;
283                 if (i == start_chunk)
284                         start_offset = start_chunk_offset;
285                 else
286                         start_offset = 0;
287                 u64 end_offset;
288                 if (i == end_chunk)
289                         end_offset = end_chunk_offset;
290                 else
291                         end_offset = WIM_CHUNK_SIZE - 1;
292
293                 unsigned partial_chunk_size = end_offset + 1 - start_offset;
294                 bool is_partial_chunk = (partial_chunk_size != uncompressed_chunk_size);
295
296                 /* This is undocumented, but chunks can be uncompressed.  This
297                  * appears to always be the case when the compressed chunk size
298                  * is equal to the uncompressed chunk size. */
299                 if (compressed_chunk_size == uncompressed_chunk_size) {
300                         /* Uncompressed chunk */
301
302                         if (start_offset != 0)
303                                 if (fseeko(fp, start_offset, SEEK_CUR))
304                                         goto read_error;
305                         if (fread(out_p, 1, partial_chunk_size, fp) != partial_chunk_size)
306                                 goto read_error;
307                 } else {
308                         /* Compressed chunk */
309
310                         /* Read the compressed data into compressed_buf. */
311                         if (fread(compressed_buf, 1, compressed_chunk_size,
312                                                 fp) != compressed_chunk_size)
313                                 goto read_error;
314
315                         /* For partial chunks and when writing directly to a
316                          * buffer, we must buffer the uncompressed data because
317                          * we don't need all of it. */
318                         if (is_partial_chunk && !cb) {
319                                 u8 uncompressed_buf[uncompressed_chunk_size];
320
321                                 ret = decompress(compressed_buf,
322                                                  compressed_chunk_size,
323                                                  uncompressed_buf,
324                                                  uncompressed_chunk_size);
325                                 if (ret) {
326                                         ret = WIMLIB_ERR_DECOMPRESSION;
327                                         goto out;
328                                 }
329                                 memcpy(out_p, uncompressed_buf + start_offset,
330                                        partial_chunk_size);
331                         } else {
332                                 ret = decompress(compressed_buf,
333                                                  compressed_chunk_size,
334                                                  out_p,
335                                                  uncompressed_chunk_size);
336                                 if (ret) {
337                                         ret = WIMLIB_ERR_DECOMPRESSION;
338                                         goto out;
339                                 }
340                         }
341                 }
342                 if (cb) {
343                         /* Feed the data to the callback function */
344                         ret = cb(out_p, partial_chunk_size, ctx_or_buf);
345                         if (ret)
346                                 goto out;
347                 } else {
348                         /* No callback function provided; we are writing
349                          * directly to a buffer.  Advance the pointer into this
350                          * buffer by the number of uncompressed bytes that were
351                          * written.  */
352                         out_p += partial_chunk_size;
353                 }
354         }
355
356         ret = 0;
357 out:
358         return ret;
359
360 read_error:
361         if (feof(fp))
362                 ERROR("Unexpected EOF in compressed file resource");
363         else
364                 ERROR_WITH_ERRNO("Error reading compressed file resource");
365         ret = WIMLIB_ERR_READ;
366         goto out;
367 }
368
369 /*
370  * Reads uncompressed data from an open file stream.
371  */
372 int
373 read_uncompressed_resource(FILE *fp, u64 offset, u64 len, void *contents_ret)
374 {
375         if (fseeko(fp, offset, SEEK_SET) != 0) {
376                 ERROR("Failed to seek to byte %"PRIu64" of input file "
377                       "to read uncompressed resource (len = %"PRIu64")",
378                       offset, len);
379                 return WIMLIB_ERR_READ;
380         }
381         if (fread(contents_ret, 1, len, fp) != len) {
382                 if (feof(fp)) {
383                         ERROR("Unexpected EOF in uncompressed file resource");
384                 } else {
385                         ERROR("Failed to read %"PRIu64" bytes from "
386                               "uncompressed resource at offset %"PRIu64,
387                               len, offset);
388                 }
389                 return WIMLIB_ERR_READ;
390         }
391         return 0;
392 }
393
394 /* Reads the contents of a struct resource_entry, as represented in the on-disk
395  * format, from the memory pointed to by @p, and fills in the fields of @entry.
396  * A pointer to the byte after the memory read at @p is returned. */
397 const void *
398 get_resource_entry(const void *p, struct resource_entry *entry)
399 {
400         u64 size;
401         u8 flags;
402
403         p = get_u56(p, &size);
404         p = get_u8(p, &flags);
405         entry->size = size;
406         entry->flags = flags;
407
408         /* offset and original_size are truncated to 62 bits to avoid possible
409          * overflows, when converting to a signed 64-bit integer (off_t) or when
410          * adding size or original_size.  This is okay since no one would ever
411          * actually have a WIM bigger than 4611686018427387903 bytes... */
412         p = get_u64(p, &entry->offset);
413         if (entry->offset & 0xc000000000000000ULL) {
414                 WARNING("Truncating offset in resource entry");
415                 entry->offset &= 0x3fffffffffffffffULL;
416         }
417         p = get_u64(p, &entry->original_size);
418         if (entry->original_size & 0xc000000000000000ULL) {
419                 WARNING("Truncating original_size in resource entry");
420                 entry->original_size &= 0x3fffffffffffffffULL;
421         }
422         return p;
423 }
424
425 /* Copies the struct resource_entry @entry to the memory pointed to by @p in the
426  * on-disk format.  A pointer to the byte after the memory written at @p is
427  * returned. */
428 void *
429 put_resource_entry(void *p, const struct resource_entry *entry)
430 {
431         p = put_u56(p, entry->size);
432         p = put_u8(p, entry->flags);
433         p = put_u64(p, entry->offset);
434         p = put_u64(p, entry->original_size);
435         return p;
436 }
437
438 static FILE *
439 wim_get_fp(WIMStruct *w)
440 {
441 #ifdef WITH_FUSE
442         pthread_mutex_lock(&w->fp_tab_mutex);
443         FILE *fp;
444
445         wimlib_assert(w->filename != NULL);
446
447         for (size_t i = 0; i < w->num_allocated_fps; i++) {
448                 if (w->fp_tab[i]) {
449                         fp = w->fp_tab[i];
450                         w->fp_tab[i] = NULL;
451                         goto out_unlock;
452                 }
453         }
454         DEBUG("Opening extra file descriptor to `%"TS"'", w->filename);
455         fp = tfopen(w->filename, T("rb"));
456         if (!fp)
457                 ERROR_WITH_ERRNO("Failed to open `%"TS"'", w->filename);
458 out_unlock:
459         pthread_mutex_unlock(&w->fp_tab_mutex);
460 #else /* WITH_FUSE */
461         fp = w->fp;
462 #endif /* !WITH_FUSE */
463         return fp;
464 }
465
466 static int
467 wim_release_fp(WIMStruct *w, FILE *fp)
468 {
469         int ret = 0;
470 #ifdef WITH_FUSE
471         FILE **fp_tab;
472
473         pthread_mutex_lock(&w->fp_tab_mutex);
474
475         for (size_t i = 0; i < w->num_allocated_fps; i++) {
476                 if (w->fp_tab[i] == NULL) {
477                         w->fp_tab[i] = fp;
478                         goto out_unlock;
479                 }
480         }
481
482         fp_tab = REALLOC(w->fp_tab, sizeof(FILE*) * (w->num_allocated_fps + 4));
483         if (!fp_tab) {
484                 ret = WIMLIB_ERR_NOMEM;
485                 fclose(fp);
486                 goto out_unlock;
487         }
488         w->fp_tab = fp_tab;
489         memset(&w->fp_tab[w->num_allocated_fps], 0, 4 * sizeof(FILE*));
490         w->fp_tab[w->num_allocated_fps] = fp;
491         w->num_allocated_fps += 4;
492 out_unlock:
493         pthread_mutex_unlock(&w->fp_tab_mutex);
494 #endif /* WITH_FUSE */
495         return ret;
496 }
497
498 static int
499 read_partial_wim_resource(const struct wim_lookup_table_entry *lte,
500                           u64 size,
501                           consume_data_callback_t cb,
502                           void *ctx_or_buf,
503                           int flags,
504                           u64 offset)
505 {
506         FILE *wim_fp;
507         WIMStruct *wim;
508         int ret;
509
510         wimlib_assert(lte->resource_location == RESOURCE_IN_WIM);
511         wimlib_assert(offset + size <= lte->resource_entry.original_size);
512
513         wim = lte->wim;
514
515         if (flags & WIMLIB_RESOURCE_FLAG_MULTITHREADED) {
516                 wim_fp = wim_get_fp(wim);
517                 if (!wim_fp) {
518                         ret = -1;
519                         goto out;
520                 }
521         } else {
522                 wim_fp = lte->wim->fp;
523         }
524
525         wimlib_assert(wim_fp != NULL);
526
527         if (lte->resource_entry.flags & WIM_RESHDR_FLAG_COMPRESSED &&
528             !(flags & WIMLIB_RESOURCE_FLAG_RAW))
529         {
530                 ret = read_compressed_resource(wim_fp,
531                                                lte->resource_entry.size,
532                                                lte->resource_entry.original_size,
533                                                lte->resource_entry.offset,
534                                                wimlib_get_compression_type(wim),
535                                                size,
536                                                offset,
537                                                cb,
538                                                ctx_or_buf);
539         } else {
540                 if (fseeko(wim_fp, offset, SEEK_SET)) {
541                         ERROR_WITH_ERRNO("Failed to seek to offset %"PRIu64
542                                          " in WIM", offset);
543                         ret = WIMLIB_ERR_READ;
544                         goto out_release_fp;
545                 }
546                 if (cb) {
547                         char buf[min(32768, size)];
548                         while (size) {
549                                 size_t bytes_to_read = min(32768, size);
550                                 size_t bytes_read = fread(buf, 1, bytes_to_read, wim_fp);
551                                 
552                                 if (bytes_read != bytes_to_read)
553                                         goto read_error;
554                                 ret = cb(buf, bytes_read, ctx_or_buf);
555                                 if (ret)
556                                         goto out_release_fp;
557                         }
558                 } else {
559                         if (fread(ctx_or_buf, 1, size, wim_fp) != size)
560                                 goto read_error;
561                 }
562                 ret = 0;
563         }
564         goto out_release_fp;
565 read_error:
566         ERROR_WITH_ERRNO("Error reading data from WIM");
567         ret = WIMLIB_ERR_READ;
568 out_release_fp:
569         if (flags & WIMLIB_RESOURCE_FLAG_MULTITHREADED)
570                 ret |= wim_release_fp(wim, wim_fp);
571 out:
572         if (ret) {
573                 if (errno == 0)
574                         errno = EIO;
575                 ret = -1;
576         }
577         return ret;
578 }
579
580
581 int
582 read_partial_wim_resource_into_buf(const struct wim_lookup_table_entry *lte,
583                                    size_t size, u64 offset, void *buf,
584                                    bool threadsafe)
585 {
586         return read_partial_wim_resource(lte, size, NULL, buf,
587                                          threadsafe ? WIMLIB_RESOURCE_FLAG_MULTITHREADED : 0,
588                                          offset);
589 }
590
591 static int
592 read_wim_resource_prefix(const struct wim_lookup_table_entry *lte,
593                          u64 size,
594                          consume_data_callback_t cb,
595                          void *ctx_or_buf,
596                          int flags)
597 {
598         return read_partial_wim_resource(lte, size, cb, ctx_or_buf, flags, 0);
599 }
600
601
602 static int
603 read_file_on_disk_prefix(const struct wim_lookup_table_entry *lte,
604                          u64 size,
605                          consume_data_callback_t cb,
606                          void *ctx_or_buf,
607                          int _ignored_flags)
608 {
609         const tchar *filename = lte->file_on_disk;
610         int ret;
611         int fd;
612         size_t bytes_read;
613
614         fd = open(filename, O_RDONLY);
615         if (fd < 0) {
616                 ERROR_WITH_ERRNO("Can't open \"%"TS"\"", filename);
617                 return WIMLIB_ERR_OPEN;
618         }
619         if (cb) {
620                 /* Send data to callback function */
621                 char buf[min(32768, size)];
622                 size_t bytes_to_read;
623                 while (size) {
624                         bytes_to_read = min(32768, size);
625                         bytes_read = full_read(fd, buf, bytes_to_read);
626                         if (bytes_read != bytes_to_read)
627                                 goto read_error;
628                         ret = cb(buf, bytes_read, ctx_or_buf);
629                         if (ret)
630                                 goto out_close;
631                 }
632         } else {
633                 /* Send data directly to a buffer */
634                 bytes_read = full_read(fd, ctx_or_buf, size);
635                 if (bytes_read != size)
636                         goto read_error;
637         }
638         ret = 0;
639         goto out_close;
640 read_error:
641         ERROR_WITH_ERRNO("Error reading \"%"TS"\"", filename);
642         ret = WIMLIB_ERR_READ;
643 out_close:
644         close(fd);
645         return ret;
646 }
647
648 static int
649 read_buffer_prefix(const struct wim_lookup_table_entry *lte,
650                    u64 size, consume_data_callback_t cb,
651                    void *ctx_or_buf, int _ignored_flags)
652 {
653         const void *inbuf = lte->attached_buffer;
654         if (cb) {
655                 return cb(inbuf, size, ctx_or_buf);
656         } else {
657                 memcpy(ctx_or_buf, inbuf, size);
658                 return 0;
659         }
660 }
661
662 typedef int (*read_resource_prefix_handler_t)(const struct wim_lookup_table_entry *lte,
663                                               u64 size,
664                                               consume_data_callback_t cb,
665                                               void *ctx_or_buf,
666                                               int flags);
667
668 int
669 read_resource_prefix(const struct wim_lookup_table_entry *lte,
670                      u64 size, consume_data_callback_t cb, void *ctx_or_buf,
671                      int flags)
672 {
673         static const read_resource_prefix_handler_t handlers[] = {
674                 [RESOURCE_IN_WIM]             = read_wim_resource_prefix,
675                 [RESOURCE_IN_FILE_ON_DISK]    = read_file_on_disk_prefix,
676                 [RESOURCE_IN_ATTACHED_BUFFER] = read_buffer_prefix,
677         #ifdef WITH_FUSE
678                 [RESOURCE_IN_STAGING_FILE]    = read_file_on_disk_prefix,
679         #endif
680         #ifdef WITH_NTFS_3G
681                 [RESOURCE_IN_NTFS_VOLUME]     = read_ntfs_file_prefix,
682         #endif
683         #ifdef __WIN32__
684                 [RESOURCE_WIN32]              = read_win32_file_prefix,
685                 [RESOURCE_WIN32_ENCRYPTED]    = read_win32_encrypted_file_prefix,
686         #endif
687         };
688         wimlib_assert(lte->resource_location < ARRAY_LEN(handlers)
689                       && handlers[lte->resource_location] != NULL);
690         return handlers[lte->resource_location](lte, size, cb, ctx_or_buf, flags);
691 }
692
693 int
694 read_full_resource_into_buf(const struct wim_lookup_table_entry *lte,
695                             void *buf, bool thread_safe)
696 {
697         return read_resource_prefix(lte,
698                                     wim_resource_size(lte),
699                                     NULL, buf,
700                                     thread_safe ? WIMLIB_RESOURCE_FLAG_MULTITHREADED : 0);
701 }
702
703 /* Extracts the first @size bytes of a WIM resource to somewhere.  In the
704  * process, the SHA1 message digest of the resource is checked if the full
705  * resource is being extracted.
706  *
707  * @extract_chunk is a function that is called to extract each chunk of the
708  * resource. */
709 int
710 extract_wim_resource(const struct wim_lookup_table_entry *lte,
711                      u64 size,
712                      consume_data_callback_t extract_chunk,
713                      void *extract_chunk_arg)
714 {
715         return read_resource_prefix(lte, size, extract_chunk,
716                                     extract_chunk_arg, 0);
717 }
718
719 static int
720 extract_wim_chunk_to_fd(const void *buf, size_t len, void *_fd_p)
721 {
722         int fd = *(int*)_fd_p;
723         ssize_t ret = full_write(fd, buf, len);
724         if (ret < len) {
725                 ERROR_WITH_ERRNO("Error writing to file descriptor");
726                 return WIMLIB_ERR_WRITE;
727         } else {
728                 return 0;
729         }
730 }
731
732 int
733 extract_wim_resource_to_fd(const struct wim_lookup_table_entry *lte,
734                            int fd, u64 size)
735 {
736         return extract_wim_resource(lte, size, extract_wim_chunk_to_fd, &fd);
737 }
738
739 /*
740  * Copies the file resource specified by the lookup table entry @lte from the
741  * input WIM to the output WIM that has its FILE * given by
742  * ((WIMStruct*)wim)->out_fp.
743  *
744  * The output_resource_entry, out_refcnt, and part_number fields of @lte are
745  * updated.
746  *
747  * (This function is confusing and should be refactored somehow.)
748  */
749 int
750 copy_resource(struct wim_lookup_table_entry *lte, void *wim)
751 {
752         WIMStruct *w = wim;
753         int ret;
754
755         ret = write_wim_resource(lte, w->out_fp,
756                                  wim_resource_compression_type(lte),
757                                  &lte->output_resource_entry, 0);
758         if (ret == 0) {
759                 lte->out_refcnt = lte->refcnt;
760                 lte->part_number = w->hdr.part_number;
761         }
762         return ret;
763 }