]> wimlib.net Git - wimlib/blob - src/resource.c
344ba0d2128e1a4692ed915165529be1d272705f
[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                         /* Send data to callback function */
548                         u8 buf[min(WIM_CHUNK_SIZE, size)];
549                         while (size) {
550                                 size_t bytes_to_read = min(WIM_CHUNK_SIZE, size);
551                                 size_t bytes_read = fread(buf, 1, bytes_to_read, wim_fp);
552                                 
553                                 if (bytes_read != bytes_to_read)
554                                         goto read_error;
555                                 ret = cb(buf, bytes_read, ctx_or_buf);
556                                 if (ret)
557                                         goto out_release_fp;
558                                 size -= bytes_read;
559                         }
560                 } else {
561                         /* Send data directly to a buffer */
562                         if (fread(ctx_or_buf, 1, size, wim_fp) != size)
563                                 goto read_error;
564                 }
565                 ret = 0;
566         }
567         goto out_release_fp;
568 read_error:
569         ERROR_WITH_ERRNO("Error reading data from WIM");
570         ret = WIMLIB_ERR_READ;
571 out_release_fp:
572         if (flags & WIMLIB_RESOURCE_FLAG_MULTITHREADED)
573                 ret |= wim_release_fp(wim, wim_fp);
574 out:
575         if (ret) {
576                 if (errno == 0)
577                         errno = EIO;
578         }
579         return ret;
580 }
581
582
583 int
584 read_partial_wim_resource_into_buf(const struct wim_lookup_table_entry *lte,
585                                    size_t size, u64 offset, void *buf,
586                                    bool threadsafe)
587 {
588         return read_partial_wim_resource(lte, size, NULL, buf,
589                                          threadsafe ? WIMLIB_RESOURCE_FLAG_MULTITHREADED : 0,
590                                          offset);
591 }
592
593 static int
594 read_wim_resource_prefix(const struct wim_lookup_table_entry *lte,
595                          u64 size,
596                          consume_data_callback_t cb,
597                          void *ctx_or_buf,
598                          int flags)
599 {
600         return read_partial_wim_resource(lte, size, cb, ctx_or_buf, flags, 0);
601 }
602
603
604 static int
605 read_file_on_disk_prefix(const struct wim_lookup_table_entry *lte,
606                          u64 size,
607                          consume_data_callback_t cb,
608                          void *ctx_or_buf,
609                          int _ignored_flags)
610 {
611         const tchar *filename = lte->file_on_disk;
612         int ret;
613         int fd;
614         size_t bytes_read;
615
616         fd = open(filename, O_RDONLY);
617         if (fd < 0) {
618                 ERROR_WITH_ERRNO("Can't open \"%"TS"\"", filename);
619                 return WIMLIB_ERR_OPEN;
620         }
621         if (cb) {
622                 /* Send data to callback function */
623                 u8 buf[min(WIM_CHUNK_SIZE, size)];
624                 size_t bytes_to_read;
625                 while (size) {
626                         bytes_to_read = min(WIM_CHUNK_SIZE, size);
627                         bytes_read = full_read(fd, buf, bytes_to_read);
628                         if (bytes_read != bytes_to_read)
629                                 goto read_error;
630                         ret = cb(buf, bytes_read, ctx_or_buf);
631                         if (ret)
632                                 goto out_close;
633                         size -= bytes_read;
634                 }
635         } else {
636                 /* Send data directly to a buffer */
637                 bytes_read = full_read(fd, ctx_or_buf, size);
638                 if (bytes_read != size)
639                         goto read_error;
640         }
641         ret = 0;
642         goto out_close;
643 read_error:
644         ERROR_WITH_ERRNO("Error reading \"%"TS"\"", filename);
645         ret = WIMLIB_ERR_READ;
646 out_close:
647         close(fd);
648         return ret;
649 }
650
651 static int
652 read_buffer_prefix(const struct wim_lookup_table_entry *lte,
653                    u64 size, consume_data_callback_t cb,
654                    void *ctx_or_buf, int _ignored_flags)
655 {
656         const void *inbuf = lte->attached_buffer;
657         if (cb) {
658                 return cb(inbuf, size, ctx_or_buf);
659         } else {
660                 memcpy(ctx_or_buf, inbuf, size);
661                 return 0;
662         }
663 }
664
665 typedef int (*read_resource_prefix_handler_t)(const struct wim_lookup_table_entry *lte,
666                                               u64 size,
667                                               consume_data_callback_t cb,
668                                               void *ctx_or_buf,
669                                               int flags);
670
671 int
672 read_resource_prefix(const struct wim_lookup_table_entry *lte,
673                      u64 size, consume_data_callback_t cb, void *ctx_or_buf,
674                      int flags)
675 {
676         static const read_resource_prefix_handler_t handlers[] = {
677                 [RESOURCE_IN_WIM]             = read_wim_resource_prefix,
678                 [RESOURCE_IN_FILE_ON_DISK]    = read_file_on_disk_prefix,
679                 [RESOURCE_IN_ATTACHED_BUFFER] = read_buffer_prefix,
680         #ifdef WITH_FUSE
681                 [RESOURCE_IN_STAGING_FILE]    = read_file_on_disk_prefix,
682         #endif
683         #ifdef WITH_NTFS_3G
684                 [RESOURCE_IN_NTFS_VOLUME]     = read_ntfs_file_prefix,
685         #endif
686         #ifdef __WIN32__
687                 [RESOURCE_WIN32]              = read_win32_file_prefix,
688                 [RESOURCE_WIN32_ENCRYPTED]    = read_win32_encrypted_file_prefix,
689         #endif
690         };
691         wimlib_assert(lte->resource_location < ARRAY_LEN(handlers)
692                       && handlers[lte->resource_location] != NULL);
693         return handlers[lte->resource_location](lte, size, cb, ctx_or_buf, flags);
694 }
695
696 int
697 read_full_resource_into_buf(const struct wim_lookup_table_entry *lte,
698                             void *buf, bool thread_safe)
699 {
700         return read_resource_prefix(lte,
701                                     wim_resource_size(lte),
702                                     NULL, buf,
703                                     thread_safe ? WIMLIB_RESOURCE_FLAG_MULTITHREADED : 0);
704 }
705
706 /* Extracts the first @size bytes of a WIM resource to somewhere.  In the
707  * process, the SHA1 message digest of the resource is checked if the full
708  * resource is being extracted.
709  *
710  * @extract_chunk is a function that is called to extract each chunk of the
711  * resource. */
712 int
713 extract_wim_resource(const struct wim_lookup_table_entry *lte,
714                      u64 size,
715                      consume_data_callback_t extract_chunk,
716                      void *extract_chunk_arg)
717 {
718         return read_resource_prefix(lte, size, extract_chunk,
719                                     extract_chunk_arg, 0);
720 }
721
722 static int
723 extract_wim_chunk_to_fd(const void *buf, size_t len, void *_fd_p)
724 {
725         int fd = *(int*)_fd_p;
726         ssize_t ret = full_write(fd, buf, len);
727         if (ret < len) {
728                 ERROR_WITH_ERRNO("Error writing to file descriptor");
729                 return WIMLIB_ERR_WRITE;
730         } else {
731                 return 0;
732         }
733 }
734
735 int
736 extract_wim_resource_to_fd(const struct wim_lookup_table_entry *lte,
737                            int fd, u64 size)
738 {
739         return extract_wim_resource(lte, size, extract_wim_chunk_to_fd, &fd);
740 }
741
742 /*
743  * Copies the file resource specified by the lookup table entry @lte from the
744  * input WIM to the output WIM that has its FILE * given by
745  * ((WIMStruct*)wim)->out_fp.
746  *
747  * The output_resource_entry, out_refcnt, and part_number fields of @lte are
748  * updated.
749  *
750  * (This function is confusing and should be refactored somehow.)
751  */
752 int
753 copy_resource(struct wim_lookup_table_entry *lte, void *wim)
754 {
755         WIMStruct *w = wim;
756         int ret;
757
758         ret = write_wim_resource(lte, w->out_fp,
759                                  wim_resource_compression_type(lte),
760                                  &lte->output_resource_entry, 0);
761         if (ret == 0) {
762                 lte->out_refcnt = lte->refcnt;
763                 lte->part_number = w->hdr.part_number;
764         }
765         return ret;
766 }