]> wimlib.net Git - wimlib/blob - src/resource.c
1871cfcccb8617ba005124652507c555f933f480
[wimlib] / src / resource.c
1 /*
2  * resource.c
3  *
4  * Read uncompressed and compressed metadata and file resources.
5  */
6
7 /*
8  * Copyright (C) 2012 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 "config.h"
26
27 #include <stdlib.h>
28 #include <stdarg.h>
29
30 #include "dentry.h"
31
32 #ifdef WITH_NTFS_3G
33 #include <ntfs-3g/attrib.h>
34 #include <ntfs-3g/inode.h>
35 #include <ntfs-3g/dir.h>
36 #endif
37
38 #include "wimlib_internal.h"
39 #include "lookup_table.h"
40 #include "io.h"
41 #include "lzx.h"
42 #include "xpress.h"
43 #include "sha1.h"
44 #include <unistd.h>
45 #include <errno.h>
46 #ifdef HAVE_ALLOCA_H
47 #include <alloca.h>
48 #endif
49
50
51 /*
52  * Reads all or part of a compressed resource into an in-memory buffer.
53  *
54  * @fp:                 The FILE* for the WIM file.
55  * @resource_compressed_size:    The compressed size of the resource.
56  * @resource_uncompressed_size:  The uncompressed size of the resource.
57  * @resource_offset:             The offset of the start of the resource from
58  *                                      the start of the stream @fp.
59  * @resource_ctype:     The compression type of the resource.
60  * @len:                The number of bytes of uncompressed data to read from
61  *                              the resource.
62  * @offset:             The offset of the bytes to read within the uncompressed
63  *                              resource.
64  * @contents_len:       An array into which the uncompressed data is written.
65  *                              It must be at least @len bytes long.
66  *
67  * Returns zero on success, nonzero on failure.
68  */
69 static int read_compressed_resource(FILE *fp, u64 resource_compressed_size,
70                                     u64 resource_uncompressed_size,
71                                     u64 resource_offset, int resource_ctype,
72                                     u64 len, u64 offset, u8  contents_ret[])
73 {
74
75         DEBUG2("comp size = %"PRIu64", uncomp size = %"PRIu64", "
76                "res offset = %"PRIu64"",
77                resource_compressed_size,
78                resource_uncompressed_size,
79                resource_offset);
80         DEBUG2("resource_ctype = %s, len = %"PRIu64", offset = %"PRIu64"",
81                wimlib_get_compression_type_string(resource_ctype), len, offset);
82         /* Trivial case */
83         if (len == 0)
84                 return 0;
85
86         int (*decompress)(const void *, uint, void *, uint);
87         /* Set the appropriate decompress function. */
88         if (resource_ctype == WIM_COMPRESSION_TYPE_LZX)
89                 decompress = lzx_decompress;
90         else
91                 decompress = xpress_decompress;
92
93         /* The structure of a compressed resource consists of a table of chunk
94          * offsets followed by the chunks themselves.  Each chunk consists of
95          * compressed data, and there is one chunk for each WIM_CHUNK_SIZE =
96          * 32768 bytes of the uncompressed file, with the last chunk having any
97          * remaining bytes.
98          *
99          * The chunk offsets are measured relative to the end of the chunk
100          * table.  The first chunk is omitted from the table in the WIM file
101          * because its offset is implicitly given by the fact that it directly
102          * follows the chunk table and therefore must have an offset of 0.
103          */
104
105         /* Calculate how many chunks the resource conists of in its entirety. */
106         u64 num_chunks = (resource_uncompressed_size + WIM_CHUNK_SIZE - 1) /
107                                                                 WIM_CHUNK_SIZE;
108         /* As mentioned, the first chunk has no entry in the chunk table. */
109         u64 num_chunk_entries = num_chunks - 1;
110
111
112         /* The index of the chunk that the read starts at. */
113         u64 start_chunk = offset / WIM_CHUNK_SIZE;
114         /* The byte offset at which the read starts, within the start chunk. */
115         u64 start_chunk_offset = offset % WIM_CHUNK_SIZE;
116
117         /* The index of the chunk that contains the last byte of the read. */
118         u64 end_chunk   = (offset + len - 1) / WIM_CHUNK_SIZE;
119         /* The byte offset of the last byte of the read, within the end chunk */
120         u64 end_chunk_offset = (offset + len - 1) % WIM_CHUNK_SIZE;
121
122         /* Number of chunks that are actually needed to read the requested part
123          * of the file. */
124         u64 num_needed_chunks = end_chunk - start_chunk + 1;
125
126         /* If the end chunk is not the last chunk, an extra chunk entry is
127          * needed because we need to know the offset of the chunk after the last
128          * chunk read to figure out the size of the last read chunk. */
129         if (end_chunk != num_chunks - 1)
130                 num_needed_chunks++;
131
132         /* Declare the chunk table.  It will only contain offsets for the chunks
133          * that are actually needed for this read. */
134         u64 chunk_offsets[num_needed_chunks];
135
136         /* Set the implicit offset of the first chunk if it is included in the
137          * needed chunks.
138          *
139          * Note: M$'s documentation includes a picture that shows the first
140          * chunk starting right after the chunk entry table, labeled as offset
141          * 0x10.  However, in the actual file format, the offset is measured
142          * from the end of the chunk entry table, so the first chunk has an
143          * offset of 0. */
144         if (start_chunk == 0)
145                 chunk_offsets[0] = 0;
146
147         /* According to M$'s documentation, if the uncompressed size of
148          * the file is greater than 4 GB, the chunk entries are 8-byte
149          * integers.  Otherwise, they are 4-byte integers. */
150         u64 chunk_entry_size = (resource_uncompressed_size >= (u64)1 << 32) ?
151                                                                         8 : 4;
152
153         /* Size of the full chunk table in the WIM file. */
154         u64 chunk_table_size = chunk_entry_size * num_chunk_entries;
155
156         /* Read the needed chunk offsets from the table in the WIM file. */
157
158         /* Index, in the WIM file, of the first needed entry in the
159          * chunk table. */
160         u64 start_table_idx = (start_chunk == 0) ? 0 : start_chunk - 1;
161
162         /* Number of entries we need to actually read from the chunk
163          * table (excludes the implicit first chunk). */
164         u64 num_needed_chunk_entries = (start_chunk == 0) ?
165                                 num_needed_chunks - 1 : num_needed_chunks;
166
167         /* Skip over unneeded chunk table entries. */
168         u64 file_offset_of_needed_chunk_entries = resource_offset +
169                                 start_table_idx * chunk_entry_size;
170         if (fseeko(fp, file_offset_of_needed_chunk_entries, SEEK_SET) != 0) {
171                 ERROR_WITH_ERRNO("Failed to seek to byte %"PRIu64" to read "
172                                  "chunk table of compressed resource",
173                                  file_offset_of_needed_chunk_entries);
174                 return WIMLIB_ERR_READ;
175         }
176
177         /* Number of bytes we need to read from the chunk table. */
178         size_t size = num_needed_chunk_entries * chunk_entry_size;
179
180         u8 chunk_tab_buf[size];
181
182         if (fread(chunk_tab_buf, 1, size, fp) != size)
183                 goto err;
184
185         /* Now fill in chunk_offsets from the entries we have read in
186          * chunk_tab_buf. */
187
188         u64 *chunk_tab_p = chunk_offsets;
189         if (start_chunk == 0)
190                 chunk_tab_p++;
191
192         if (chunk_entry_size == 4) {
193                 u32 *entries = (u32*)chunk_tab_buf;
194                 while (num_needed_chunk_entries--)
195                         *chunk_tab_p++ = le32_to_cpu(*entries++);
196         } else {
197                 u64 *entries = (u64*)chunk_tab_buf;
198                 while (num_needed_chunk_entries--)
199                         *chunk_tab_p++ = le64_to_cpu(*entries++);
200         }
201
202         /* Done with the chunk table now.  We must now seek to the first chunk
203          * that is needed for the read. */
204
205         u64 file_offset_of_first_needed_chunk = resource_offset +
206                                 chunk_table_size + chunk_offsets[0];
207         if (fseeko(fp, file_offset_of_first_needed_chunk, SEEK_SET) != 0) {
208                 ERROR_WITH_ERRNO("Failed to seek to byte %"PRIu64" to read "
209                                  "first chunk of compressed resource",
210                                  file_offset_of_first_needed_chunk);
211                 return WIMLIB_ERR_READ;
212         }
213
214         /* Pointer to current position in the output buffer for uncompressed
215          * data. */
216         u8 *out_p = (u8*)contents_ret;
217
218         /* Buffer for compressed data.  While most compressed chunks will have a
219          * size much less than WIM_CHUNK_SIZE, WIM_CHUNK_SIZE - 1 is the maximum
220          * size in the worst-case.  This assumption is valid only if chunks that
221          * happen to compress to more than the uncompressed size (i.e. a
222          * sequence of random bytes) are always stored uncompressed. But this seems
223          * to be the case in M$'s WIM files, even though it is undocumented. */
224         u8 compressed_buf[WIM_CHUNK_SIZE - 1];
225
226
227         /* Decompress all the chunks. */
228         for (u64 i = start_chunk; i <= end_chunk; i++) {
229
230                 DEBUG2("Chunk %"PRIu64" (start %"PRIu64", end %"PRIu64").",
231                        i, start_chunk, end_chunk);
232
233                 /* Calculate the sizes of the compressed chunk and of the
234                  * uncompressed chunk. */
235                 uint compressed_chunk_size, uncompressed_chunk_size;
236                 if (i != num_chunks - 1) {
237                         /* All the chunks except the last one in the resource
238                          * expand to WIM_CHUNK_SIZE uncompressed, and the amount
239                          * of compressed data for the chunk is given by the
240                          * difference of offsets in the chunk offset table. */
241                         compressed_chunk_size = chunk_offsets[i + 1 - start_chunk] -
242                                                 chunk_offsets[i - start_chunk];
243                         uncompressed_chunk_size = WIM_CHUNK_SIZE;
244                 } else {
245                         /* The last compressed chunk consists of the remaining
246                          * bytes in the file resource, and the last uncompressed
247                          * chunk has size equal to however many bytes are left-
248                          * that is, the remainder of the uncompressed size when
249                          * divided by WIM_CHUNK_SIZE.
250                          *
251                          * Note that the resource_compressed_size includes the
252                          * chunk table, so the size of it must be subtracted. */
253                         compressed_chunk_size = resource_compressed_size -
254                                                 chunk_table_size -
255                                                 chunk_offsets[i - start_chunk];
256
257                         uncompressed_chunk_size = resource_uncompressed_size %
258                                                                 WIM_CHUNK_SIZE;
259
260                         /* If the remainder is 0, the last chunk actually
261                          * uncompresses to a full WIM_CHUNK_SIZE bytes. */
262                         if (uncompressed_chunk_size == 0)
263                                 uncompressed_chunk_size = WIM_CHUNK_SIZE;
264                 }
265
266                 DEBUG2("compressed_chunk_size = %u, "
267                        "uncompressed_chunk_size = %u",
268                        compressed_chunk_size, uncompressed_chunk_size);
269
270
271                 /* Figure out how much of this chunk we actually need to read */
272                 u64 start_offset;
273                 if (i == start_chunk)
274                         start_offset = start_chunk_offset;
275                 else
276                         start_offset = 0;
277                 u64 end_offset;
278                 if (i == end_chunk)
279                         end_offset = end_chunk_offset;
280                 else
281                         end_offset = WIM_CHUNK_SIZE - 1;
282
283                 u64 partial_chunk_size = end_offset + 1 - start_offset;
284                 bool is_partial_chunk = (partial_chunk_size !=
285                                                 uncompressed_chunk_size);
286
287                 DEBUG2("start_offset = %u, end_offset = %u", start_offset,
288                                         end_offset);
289                 DEBUG2("partial_chunk_size = %u", partial_chunk_size);
290
291                 /* This is undocumented, but chunks can be uncompressed.  This
292                  * appears to always be the case when the compressed chunk size
293                  * is equal to the uncompressed chunk size. */
294                 if (compressed_chunk_size == uncompressed_chunk_size) {
295                         /* Probably an uncompressed chunk */
296
297                         if (start_offset != 0) {
298                                 if (fseeko(fp, start_offset, SEEK_CUR) != 0) {
299                                         ERROR_WITH_ERRNO("Uncompressed partial "
300                                                          "chunk fseek() error");
301                                         return WIMLIB_ERR_READ;
302                                 }
303                         }
304                         if (fread(out_p, 1, partial_chunk_size, fp) !=
305                                         partial_chunk_size)
306                                 goto err;
307                 } else {
308                         /* Compressed chunk */
309                         int ret;
310
311                         /* Read the compressed data into compressed_buf. */
312                         if (fread(compressed_buf, 1, compressed_chunk_size,
313                                                 fp) != compressed_chunk_size)
314                                 goto err;
315
316                         /* For partial chunks we must buffer the uncompressed
317                          * data because we don't need all of it. */
318                         if (is_partial_chunk) {
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 != 0)
326                                         return WIMLIB_ERR_DECOMPRESSION;
327                                 memcpy(out_p, uncompressed_buf + start_offset,
328                                                 partial_chunk_size);
329                         } else {
330                                 ret = decompress(compressed_buf,
331                                                 compressed_chunk_size,
332                                                 out_p,
333                                                 uncompressed_chunk_size);
334                                 if (ret != 0)
335                                         return WIMLIB_ERR_DECOMPRESSION;
336                         }
337                 }
338
339                 /* Advance the pointer into the uncompressed output data by the
340                  * number of uncompressed bytes that were written.  */
341                 out_p += partial_chunk_size;
342         }
343
344         return 0;
345
346 err:
347         if (feof(fp))
348                 ERROR("Unexpected EOF in compressed file resource");
349         else
350                 ERROR_WITH_ERRNO("Error reading compressed file resource");
351         return WIMLIB_ERR_READ;
352 }
353
354 /*
355  * Reads uncompressed data from an open file stream.
356  */
357 int read_uncompressed_resource(FILE *fp, u64 offset, u64 len,
358                                u8 contents_ret[])
359 {
360         if (fseeko(fp, offset, SEEK_SET) != 0) {
361                 ERROR("Failed to seek to byte %"PRIu64" of input file "
362                       "to read uncompressed resource (len = %"PRIu64")",
363                       offset, len);
364                 return WIMLIB_ERR_READ;
365         }
366         if (fread(contents_ret, 1, len, fp) != len) {
367                 if (feof(fp)) {
368                         ERROR("Unexpected EOF in uncompressed file resource");
369                 } else {
370                         ERROR("Failed to read %"PRIu64" bytes from "
371                               "uncompressed resource at offset %"PRIu64,
372                               len, offset);
373                 }
374                 return WIMLIB_ERR_READ;
375         }
376         return 0;
377 }
378
379
380
381
382 /* Reads the contents of a struct resource_entry, as represented in the on-disk
383  * format, from the memory pointed to by @p, and fills in the fields of @entry.
384  * A pointer to the byte after the memory read at @p is returned. */
385 const u8 *get_resource_entry(const u8 *p, struct resource_entry *entry)
386 {
387         u64 size;
388         u8 flags;
389
390         p = get_u56(p, &size);
391         p = get_u8(p, &flags);
392         entry->size = size;
393         entry->flags = flags;
394
395         /* offset and original_size are truncated to 62 bits to avoid possible
396          * overflows, when converting to a signed 64-bit integer (off_t) or when
397          * adding size or original_size.  This is okay since no one would ever
398          * actually have a WIM bigger than 4611686018427387903 bytes... */
399         p = get_u64(p, &entry->offset);
400         if (entry->offset & 0xc000000000000000ULL) {
401                 WARNING("Truncating offset in resource entry");
402                 entry->offset &= 0x3fffffffffffffffULL;
403         }
404         p = get_u64(p, &entry->original_size);
405         if (entry->original_size & 0xc000000000000000ULL) {
406                 WARNING("Truncating original_size in resource entry");
407                 entry->original_size &= 0x3fffffffffffffffULL;
408         }
409         return p;
410 }
411
412 /* Copies the struct resource_entry @entry to the memory pointed to by @p in the
413  * on-disk format.  A pointer to the byte after the memory written at @p is
414  * returned. */
415 u8 *put_resource_entry(u8 *p, const struct resource_entry *entry)
416 {
417         p = put_u56(p, entry->size);
418         p = put_u8(p, entry->flags);
419         p = put_u64(p, entry->offset);
420         p = put_u64(p, entry->original_size);
421         return p;
422 }
423
424 static FILE *wim_get_fp(WIMStruct *w)
425 {
426         pthread_mutex_lock(&w->fp_tab_mutex);
427         FILE *fp;
428
429         wimlib_assert(w->filename != NULL);
430
431         for (size_t i = 0; i < w->num_allocated_fps; i++) {
432                 if (w->fp_tab[i]) {
433                         fp = w->fp_tab[i];
434                         w->fp_tab[i] = NULL;
435                         goto out;
436                 }
437         }
438         DEBUG("Opening extra file descriptor to `%s'", w->filename);
439         fp = fopen(w->filename, "rb");
440         if (!fp)
441                 ERROR_WITH_ERRNO("Failed to open `%s'", w->filename);
442 out:
443         pthread_mutex_unlock(&w->fp_tab_mutex);
444         return fp;
445 }
446
447 static int wim_release_fp(WIMStruct *w, FILE *fp)
448 {
449         int ret = 0;
450         FILE **fp_tab;
451
452         pthread_mutex_lock(&w->fp_tab_mutex);
453
454         for (size_t i = 0; i < w->num_allocated_fps; i++) {
455                 if (w->fp_tab[i] == NULL) {
456                         w->fp_tab[i] = fp;
457                         goto out;
458                 }
459         }
460
461         fp_tab = REALLOC(w->fp_tab, sizeof(FILE*) * (w->num_allocated_fps + 4));
462         if (!fp_tab) {
463                 ret = WIMLIB_ERR_NOMEM;
464                 goto out;
465         }
466         w->fp_tab = fp_tab;
467         memset(&w->fp_tab[w->num_allocated_fps], 0, 4 * sizeof(FILE*));
468         w->fp_tab[w->num_allocated_fps] = fp;
469         w->num_allocated_fps += 4;
470 out:
471         pthread_mutex_unlock(&w->fp_tab_mutex);
472         return ret;
473 }
474
475 /*
476  * Reads some data from the resource corresponding to a WIM lookup table entry.
477  *
478  * @lte:        The WIM lookup table entry for the resource.
479  * @buf:        Buffer into which to write the data.
480  * @size:       Number of bytes to read.
481  * @offset:     Offset at which to start reading the resource.
482  *
483  * Returns zero on success, nonzero on failure.
484  */
485 int read_wim_resource(const struct lookup_table_entry *lte, u8 buf[],
486                       size_t size, u64 offset, int flags)
487 {
488         int ctype;
489         int ret = 0;
490         FILE *fp;
491
492         /* We shouldn't be allowing read over-runs in any part of the library.
493          * */
494         if (flags & WIMLIB_RESOURCE_FLAG_RAW)
495                 wimlib_assert(offset + size <= lte->resource_entry.size);
496         else
497                 wimlib_assert(offset + size <= lte->resource_entry.original_size);
498
499         switch (lte->resource_location) {
500         case RESOURCE_IN_WIM:
501                 /* The resource is in a WIM file, and its WIMStruct is given by
502                  * the lte->wim member.  The resource may be either compressed
503                  * or uncompressed. */
504                 wimlib_assert(lte->wim != NULL);
505
506                 if (flags & WIMLIB_RESOURCE_FLAG_MULTITHREADED) {
507                         fp = wim_get_fp(lte->wim);
508                         if (!fp)
509                                 return WIMLIB_ERR_OPEN;
510                 } else {
511                         wimlib_assert(lte->wim->fp != NULL);
512                         fp = lte->wim->fp;
513                 }
514
515                 ctype = wim_resource_compression_type(lte);
516
517                 wimlib_assert(ctype != WIM_COMPRESSION_TYPE_NONE ||
518                               (lte->resource_entry.original_size ==
519                                lte->resource_entry.size));
520
521                 if ((flags & WIMLIB_RESOURCE_FLAG_RAW)
522                     || ctype == WIM_COMPRESSION_TYPE_NONE)
523                         ret = read_uncompressed_resource(fp,
524                                                          lte->resource_entry.offset + offset,
525                                                          size, buf);
526                 else
527                         ret = read_compressed_resource(fp,
528                                                        lte->resource_entry.size,
529                                                        lte->resource_entry.original_size,
530                                                        lte->resource_entry.offset,
531                                                        ctype, size, offset, buf);
532                 if (flags & WIMLIB_RESOURCE_FLAG_MULTITHREADED) {
533                         int ret2 = wim_release_fp(lte->wim, fp);
534                         if (ret == 0)
535                                 ret = ret2;
536                 }
537                 break;
538         case RESOURCE_IN_STAGING_FILE:
539         case RESOURCE_IN_FILE_ON_DISK:
540                 /* The resource is in some file on the external filesystem and
541                  * needs to be read uncompressed */
542                 wimlib_assert(lte->file_on_disk);
543                 wimlib_assert(&lte->file_on_disk == &lte->staging_file_name);
544                 /* Use existing file pointer if available; otherwise open one
545                  * temporarily */
546                 if (lte->file_on_disk_fp) {
547                         fp = lte->file_on_disk_fp;
548                 } else {
549                         fp = fopen(lte->file_on_disk, "rb");
550                         if (!fp) {
551                                 ERROR_WITH_ERRNO("Failed to open the file "
552                                                  "`%s'", lte->file_on_disk);
553                                 ret = WIMLIB_ERR_OPEN;
554                                 break;
555                         }
556                 }
557                 ret = read_uncompressed_resource(fp, offset, size, buf);
558                 if (fp != lte->file_on_disk_fp)
559                         fclose(fp);
560                 break;
561         case RESOURCE_IN_ATTACHED_BUFFER:
562                 /* The resource is directly attached uncompressed in an
563                  * in-memory buffer. */
564                 wimlib_assert(lte->attached_buffer != NULL);
565                 memcpy(buf, lte->attached_buffer + offset, size);
566                 break;
567 #ifdef WITH_NTFS_3G
568         case RESOURCE_IN_NTFS_VOLUME:
569                 wimlib_assert(lte->ntfs_loc != NULL);
570                 wimlib_assert(lte->attr != NULL);
571                 {
572                         if (lte->ntfs_loc->is_reparse_point)
573                                 offset += 8;
574                         if (ntfs_attr_pread(lte->attr, offset, size, buf) != size) {
575                                 ERROR_WITH_ERRNO("Error reading NTFS attribute "
576                                                  "at `%s'",
577                                                  lte->ntfs_loc->path_utf8);
578                                 ret = WIMLIB_ERR_NTFS_3G;
579                         }
580                         break;
581                 }
582 #endif
583         default:
584                 wimlib_assert(0);
585                 ret = -1;
586                 break;
587         }
588         return ret;
589 }
590
591 /*
592  * Reads all the data from the resource corresponding to a WIM lookup table
593  * entry.
594  *
595  * @lte:        The WIM lookup table entry for the resource.
596  * @buf:        Buffer into which to write the data.  It must be at least
597  *              wim_resource_size(lte) bytes long.
598  *
599  * Returns 0 on success; nonzero on failure.
600  */
601 int read_full_wim_resource(const struct lookup_table_entry *lte, u8 buf[],
602                            int flags)
603 {
604         return read_wim_resource(lte, buf, wim_resource_size(lte), 0, flags);
605 }
606
607 /* Chunk table that's located at the beginning of each compressed resource in
608  * the WIM.  (This is not the on-disk format; the on-disk format just has an
609  * array of offsets.) */
610 struct chunk_table {
611         off_t file_offset;
612         u64 num_chunks;
613         u64 original_resource_size;
614         u64 bytes_per_chunk_entry;
615         u64 table_disk_size;
616         u64 cur_offset;
617         u64 *cur_offset_p;
618         u64 offsets[0];
619 };
620
621 /*
622  * Allocates and initializes a chunk table, and reserves space for it in the
623  * output file.
624  */
625 static int
626 begin_wim_resource_chunk_tab(const struct lookup_table_entry *lte,
627                              FILE *out_fp,
628                              off_t file_offset,
629                              struct chunk_table **chunk_tab_ret)
630 {
631         u64 size = wim_resource_size(lte);
632         u64 num_chunks = (size + WIM_CHUNK_SIZE - 1) / WIM_CHUNK_SIZE;
633         size_t alloc_size = sizeof(struct chunk_table) + num_chunks * sizeof(u64);
634         struct chunk_table *chunk_tab = CALLOC(1, alloc_size);
635         int ret;
636
637         if (!chunk_tab) {
638                 ERROR("Failed to allocate chunk table for %"PRIu64" byte "
639                       "resource", size);
640                 ret = WIMLIB_ERR_NOMEM;
641                 goto out;
642         }
643         chunk_tab->file_offset = file_offset;
644         chunk_tab->num_chunks = num_chunks;
645         chunk_tab->original_resource_size = size;
646         chunk_tab->bytes_per_chunk_entry = (size >= (1ULL << 32)) ? 8 : 4;
647         chunk_tab->table_disk_size = chunk_tab->bytes_per_chunk_entry *
648                                      (num_chunks - 1);
649         chunk_tab->cur_offset = 0;
650         chunk_tab->cur_offset_p = chunk_tab->offsets;
651
652         if (fwrite(chunk_tab, 1, chunk_tab->table_disk_size, out_fp) !=
653                    chunk_tab->table_disk_size) {
654                 ERROR_WITH_ERRNO("Failed to write chunk table in compressed "
655                                  "file resource");
656                 ret = WIMLIB_ERR_WRITE;
657                 goto out;
658         }
659
660         ret = 0;
661 out:
662         *chunk_tab_ret = chunk_tab;
663         return ret;
664 }
665
666 /*
667  * Compresses a chunk of a WIM resource.
668  *
669  * @chunk:              Uncompressed data of the chunk.
670  * @chunk_size:         Size of the uncompressed chunk in bytes.
671  * @compressed_chunk:   Pointer to output buffer of size at least
672  *                              (@chunk_size - 1) bytes.
673  * @compressed_chunk_len_ret:   Pointer to an unsigned int into which the size
674  *                                      of the compressed chunk will be
675  *                                      returned.
676  * @ctype:      Type of compression to use.  Must be WIM_COMPRESSION_TYPE_LZX
677  *              or WIM_COMPRESSION_TYPE_XPRESS.
678  *
679  * Returns zero if compressed succeeded, and nonzero if the chunk could not be
680  * compressed to any smaller than @chunk_size.  This function cannot fail for
681  * any other reasons.
682  */
683 static int compress_chunk(const u8 chunk[], unsigned chunk_size,
684                           u8 compressed_chunk[],
685                           unsigned *compressed_chunk_len_ret,
686                           int ctype)
687 {
688         int (*compress)(const void *, unsigned, void *, unsigned *);
689         switch (ctype) {
690         case WIM_COMPRESSION_TYPE_LZX:
691                 compress = lzx_compress;
692                 break;
693         case WIM_COMPRESSION_TYPE_XPRESS:
694                 compress = xpress_compress;
695                 break;
696         default:
697                 wimlib_assert(0);
698                 break;
699         }
700         return (*compress)(chunk, chunk_size, compressed_chunk,
701                            compressed_chunk_len_ret);
702 }
703
704 /*
705  * Writes a chunk of a WIM resource to an output file.
706  *
707  * @chunk:        Uncompressed data of the chunk.
708  * @chunk_size:   Size of the chunk (<= WIM_CHUNK_SIZE)
709  * @out_fp:       FILE * to write tho chunk to.
710  * @out_ctype:    Compression type to use when writing the chunk (ignored if no
711  *                      chunk table provided)
712  * @chunk_tab:    Pointer to chunk table being created.  It is updated with the
713  *                      offset of the chunk we write.
714  *
715  * Returns 0 on success; nonzero on failure.
716  */
717 static int write_wim_resource_chunk(const u8 chunk[], unsigned chunk_size,
718                                     FILE *out_fp, int out_ctype,
719                                     struct chunk_table *chunk_tab)
720 {
721         const u8 *out_chunk;
722         unsigned out_chunk_size;
723
724         wimlib_assert(chunk_size <= WIM_CHUNK_SIZE);
725
726         if (!chunk_tab) {
727                 out_chunk = chunk;
728                 out_chunk_size = chunk_size;
729         } else {
730                 u8 *compressed_chunk = alloca(chunk_size);
731                 int ret;
732
733                 ret = compress_chunk(chunk, chunk_size, compressed_chunk,
734                                      &out_chunk_size, out_ctype);
735                 if (ret == 0) {
736                         out_chunk = compressed_chunk;
737                 } else {
738                         out_chunk = chunk;
739                         out_chunk_size = chunk_size;
740                 }
741                 *chunk_tab->cur_offset_p++ = chunk_tab->cur_offset;
742                 chunk_tab->cur_offset += out_chunk_size;
743         }
744
745         if (fwrite(out_chunk, 1, out_chunk_size, out_fp) != out_chunk_size) {
746                 ERROR_WITH_ERRNO("Failed to write WIM resource chunk");
747                 return WIMLIB_ERR_WRITE;
748         }
749         return 0;
750 }
751
752 /*
753  * Finishes a WIM chunk tale and writes it to the output file at the correct
754  * offset.
755  *
756  * The final size of the full compressed resource is returned in the
757  * @compressed_size_p.
758  */
759 static int
760 finish_wim_resource_chunk_tab(struct chunk_table *chunk_tab,
761                               FILE *out_fp, u64 *compressed_size_p)
762 {
763         size_t bytes_written;
764         if (fseeko(out_fp, chunk_tab->file_offset, SEEK_SET) != 0) {
765                 ERROR_WITH_ERRNO("Failed to seek to byte %"PRIu64" of output "
766                                  "WIM file", chunk_tab->file_offset);
767                 return WIMLIB_ERR_WRITE;
768         }
769
770         if (chunk_tab->bytes_per_chunk_entry == 8) {
771                 array_cpu_to_le64(chunk_tab->offsets, chunk_tab->num_chunks);
772         } else {
773                 for (u64 i = 0; i < chunk_tab->num_chunks; i++)
774                         ((u32*)chunk_tab->offsets)[i] =
775                                 cpu_to_le32(chunk_tab->offsets[i]);
776         }
777         bytes_written = fwrite((u8*)chunk_tab->offsets +
778                                         chunk_tab->bytes_per_chunk_entry,
779                                1, chunk_tab->table_disk_size, out_fp);
780         if (bytes_written != chunk_tab->table_disk_size) {
781                 ERROR_WITH_ERRNO("Failed to write chunk table in compressed "
782                                  "file resource");
783                 return WIMLIB_ERR_WRITE;
784         }
785         if (fseeko(out_fp, 0, SEEK_END) != 0) {
786                 ERROR_WITH_ERRNO("Failed to seek to end of output WIM file");
787                 return WIMLIB_ERR_WRITE;
788         }
789         *compressed_size_p = chunk_tab->cur_offset + chunk_tab->table_disk_size;
790         return 0;
791 }
792
793 /*
794  * Writes a WIM resource to a FILE * opened for writing.  The resource may be
795  * written uncompressed or compressed depending on the @out_ctype parameter.
796  *
797  * If by chance the resource compresses to more than the original size (this may
798  * happen with random data or files than are pre-compressed), the resource is
799  * instead written uncompressed (and this is reflected in the @out_res_entry by
800  * removing the WIM_RESHDR_FLAG_COMPRESSED flag).
801  *
802  * @lte:        The lookup table entry for the WIM resource.
803  * @out_fp:     The FILE * to write the resource to.
804  * @out_ctype:  The compression type of the resource to write.  Note: if this is
805  *                      the same as the compression type of the WIM resource we
806  *                      need to read, we simply copy the data (i.e. we do not
807  *                      uncompress it, then compress it again).
808  * @out_res_entry:  If non-NULL, a resource entry that is filled in with the
809  *                  offset, original size, compressed size, and compression flag
810  *                  of the output resource.
811  *
812  * Returns 0 on success; nonzero on failure.
813  */
814 static int write_wim_resource(struct lookup_table_entry *lte,
815                               FILE *out_fp, int out_ctype,
816                               struct resource_entry *out_res_entry,
817                               int flags)
818 {
819         u64 bytes_remaining;
820         u64 original_size;
821         u64 old_compressed_size;
822         u64 new_compressed_size;
823         u64 offset;
824         int ret;
825         struct chunk_table *chunk_tab = NULL;
826         bool raw;
827         off_t file_offset;
828 #ifdef WITH_NTFS_3G
829         ntfs_inode *ni = NULL;
830 #endif
831
832         wimlib_assert(lte);
833
834         /* Original size of the resource */
835         original_size = wim_resource_size(lte);
836
837         /* Compressed size of the resource (as it exists now) */
838         old_compressed_size = wim_resource_compressed_size(lte);
839
840         /* Current offset in output file */
841         file_offset = ftello(out_fp);
842         if (file_offset == -1) {
843                 ERROR_WITH_ERRNO("Failed to get offset in output "
844                                  "stream");
845                 return WIMLIB_ERR_WRITE;
846         }
847
848         /* Are the compression types the same?  If so, do a raw copy (copy
849          * without decompressing and recompressing the data). */
850         raw = (wim_resource_compression_type(lte) == out_ctype
851                && out_ctype != WIM_COMPRESSION_TYPE_NONE);
852
853         if (raw) {
854                 flags |= WIMLIB_RESOURCE_FLAG_RAW;
855                 bytes_remaining = old_compressed_size;
856         } else {
857                 flags &= ~WIMLIB_RESOURCE_FLAG_RAW;
858                 bytes_remaining = original_size;
859         }
860
861         /* Empty resource; nothing needs to be done, so just return success. */
862         if (bytes_remaining == 0)
863                 return 0;
864
865         /* Buffer for reading chunks for the resource */
866         u8 buf[min(WIM_CHUNK_SIZE, bytes_remaining)];
867
868         /* If we are writing a compressed resource and not doing a raw copy, we
869          * need to initialize the chunk table */
870         if (out_ctype != WIM_COMPRESSION_TYPE_NONE && !raw) {
871                 ret = begin_wim_resource_chunk_tab(lte, out_fp, file_offset,
872                                                    &chunk_tab);
873                 if (ret != 0)
874                         goto out;
875         }
876
877         /* If the WIM resource is in an external file, open a FILE * to it so we
878          * don't have to open a temporary one in read_wim_resource() for each
879          * chunk. */
880         if (lte->resource_location == RESOURCE_IN_FILE_ON_DISK
881              && !lte->file_on_disk_fp)
882         {
883                 wimlib_assert(lte->file_on_disk);
884                 lte->file_on_disk_fp = fopen(lte->file_on_disk, "rb");
885                 if (!lte->file_on_disk_fp) {
886                         ERROR_WITH_ERRNO("Failed to open the file `%s' for "
887                                          "reading", lte->file_on_disk);
888                         ret = WIMLIB_ERR_OPEN;
889                         goto out;
890                 }
891         }
892 #ifdef WITH_NTFS_3G
893         else if (lte->resource_location == RESOURCE_IN_NTFS_VOLUME
894                   && !lte->attr)
895         {
896                 struct ntfs_location *loc = lte->ntfs_loc;
897                 wimlib_assert(loc);
898                 ni = ntfs_pathname_to_inode(*loc->ntfs_vol_p, NULL, loc->path_utf8);
899                 if (!ni) {
900                         ERROR_WITH_ERRNO("Failed to open inode `%s' in NTFS "
901                                          "volume", loc->path_utf8);
902                         ret = WIMLIB_ERR_NTFS_3G;
903                         goto out;
904                 }
905                 lte->attr = ntfs_attr_open(ni,
906                                            loc->is_reparse_point ? AT_REPARSE_POINT : AT_DATA,
907                                            (ntfschar*)loc->stream_name_utf16,
908                                            loc->stream_name_utf16_num_chars);
909                 if (!lte->attr) {
910                         ERROR_WITH_ERRNO("Failed to open attribute of `%s' in "
911                                          "NTFS volume", loc->path_utf8);
912                         ret = WIMLIB_ERR_NTFS_3G;
913                         goto out_fclose;
914                 }
915         }
916 #endif
917
918         /* If we aren't doing a raw copy, we will compute the SHA1 message
919          * digest of the resource as we read it, and verify it's the same as the
920          * hash given in the lookup table entry once we've finished reading the
921          * resource. */
922         SHA_CTX ctx;
923         if (!raw)
924                 sha1_init(&ctx);
925
926         /* While there are still bytes remaining in the WIM resource, read a
927          * chunk of the resource, update SHA1, then write that chunk using the
928          * desired compression type. */
929         offset = 0;
930         do {
931                 u64 to_read = min(bytes_remaining, WIM_CHUNK_SIZE);
932                 ret = read_wim_resource(lte, buf, to_read, offset, flags);
933                 if (ret != 0)
934                         goto out_fclose;
935                 if (!raw)
936                         sha1_update(&ctx, buf, to_read);
937                 ret = write_wim_resource_chunk(buf, to_read, out_fp,
938                                                out_ctype, chunk_tab);
939                 if (ret != 0)
940                         goto out_fclose;
941                 bytes_remaining -= to_read;
942                 offset += to_read;
943         } while (bytes_remaining);
944
945         /* Raw copy:  The new compressed size is the same as the old compressed
946          * size
947          *
948          * Using WIM_COMPRESSION_TYPE_NONE:  The new compressed size is the
949          * original size
950          *
951          * Using a different compression type:  Call
952          * finish_wim_resource_chunk_tab() and it will provide the new
953          * compressed size.
954          */
955         if (raw) {
956                 new_compressed_size = old_compressed_size;
957         } else {
958                 if (out_ctype == WIM_COMPRESSION_TYPE_NONE)
959                         new_compressed_size = original_size;
960                 else {
961                         ret = finish_wim_resource_chunk_tab(chunk_tab, out_fp,
962                                                             &new_compressed_size);
963                         if (ret != 0)
964                                 goto out_fclose;
965                 }
966         }
967
968         /* Verify SHA1 message digest of the resource, unless we are doing a raw
969          * write (in which case we never even saw the uncompressed data).  Or,
970          * if the hash we had before is all 0's, just re-set it to be the new
971          * hash. */
972         if (!raw) {
973                 u8 md[SHA1_HASH_SIZE];
974                 sha1_final(md, &ctx);
975                 if (is_zero_hash(lte->hash)) {
976                         copy_hash(lte->hash, md);
977                 } else if (!hashes_equal(md, lte->hash)) {
978                         ERROR("WIM resource has incorrect hash!");
979                         if (lte->resource_location == RESOURCE_IN_FILE_ON_DISK) {
980                                 ERROR("We were reading it from `%s'; maybe it changed "
981                                       "while we were reading it.",
982                                       lte->file_on_disk);
983                         }
984                         ret = WIMLIB_ERR_INVALID_RESOURCE_HASH;
985                         goto out_fclose;
986                 }
987         }
988
989         if (!raw && new_compressed_size >= original_size &&
990             out_ctype != WIM_COMPRESSION_TYPE_NONE)
991         {
992                 /* Oops!  We compressed the resource to larger than the original
993                  * size.  Write the resource uncompressed instead. */
994                 if (fseeko(out_fp, file_offset, SEEK_SET) != 0) {
995                         ERROR_WITH_ERRNO("Failed to seek to byte %"PRIu64" "
996                                          "of output WIM file", file_offset);
997                         ret = WIMLIB_ERR_WRITE;
998                         goto out_fclose;
999                 }
1000                 ret = write_wim_resource(lte, out_fp, WIM_COMPRESSION_TYPE_NONE,
1001                                          out_res_entry, flags);
1002                 if (ret != 0)
1003                         goto out_fclose;
1004                 if (fflush(out_fp) != 0) {
1005                         ERROR_WITH_ERRNO("Failed to flush output WIM file");
1006                         ret = WIMLIB_ERR_WRITE;
1007                         goto out_fclose;
1008                 }
1009                 if (ftruncate(fileno(out_fp), file_offset + out_res_entry->size) != 0) {
1010                         ERROR_WITH_ERRNO("Failed to truncate output WIM file");
1011                         ret = WIMLIB_ERR_WRITE;
1012                         goto out_fclose;
1013                 }
1014         } else {
1015                 if (out_res_entry) {
1016                         out_res_entry->size          = new_compressed_size;
1017                         out_res_entry->original_size = original_size;
1018                         out_res_entry->offset        = file_offset;
1019                         out_res_entry->flags         = lte->resource_entry.flags
1020                                                         & ~WIM_RESHDR_FLAG_COMPRESSED;
1021                         if (out_ctype != WIM_COMPRESSION_TYPE_NONE)
1022                                 out_res_entry->flags |= WIM_RESHDR_FLAG_COMPRESSED;
1023                 }
1024         }
1025         ret = 0;
1026 out_fclose:
1027         if (lte->resource_location == RESOURCE_IN_FILE_ON_DISK
1028             && lte->file_on_disk_fp) {
1029                 fclose(lte->file_on_disk_fp);
1030                 lte->file_on_disk_fp = NULL;
1031         }
1032 #ifdef WITH_NTFS_3G
1033         else if (lte->resource_location == RESOURCE_IN_NTFS_VOLUME) {
1034                 if (lte->attr) {
1035                         ntfs_attr_close(lte->attr);
1036                         lte->attr = NULL;
1037                 }
1038                 if (ni)
1039                         ntfs_inode_close(ni);
1040         }
1041 #endif
1042 out:
1043         FREE(chunk_tab);
1044         return ret;
1045 }
1046
1047 /* Like write_wim_resource(), but the resource is specified by a buffer of
1048  * uncompressed data rather a lookup table entry; also writes the SHA1 hash of
1049  * the buffer to @hash.  */
1050 static int write_wim_resource_from_buffer(const u8 *buf, u64 buf_size,
1051                                           FILE *out_fp, int out_ctype,
1052                                           struct resource_entry *out_res_entry,
1053                                           u8 hash[SHA1_HASH_SIZE])
1054 {
1055         /* Set up a temporary lookup table entry to provide to
1056          * write_wim_resource(). */
1057         struct lookup_table_entry lte;
1058         int ret;
1059         lte.resource_entry.flags         = 0;
1060         lte.resource_entry.original_size = buf_size;
1061         lte.resource_entry.size          = buf_size;
1062         lte.resource_entry.offset        = 0;
1063         lte.resource_location            = RESOURCE_IN_ATTACHED_BUFFER;
1064         lte.attached_buffer              = (u8*)buf;
1065
1066         zero_out_hash(lte.hash);
1067         ret = write_wim_resource(&lte, out_fp, out_ctype, out_res_entry, 0);
1068         if (ret != 0)
1069                 return ret;
1070         copy_hash(hash, lte.hash);
1071         return 0;
1072 }
1073
1074 /*
1075  * Extracts the first @size bytes of the WIM resource specified by @lte to the
1076  * open file descriptor @fd.
1077  *
1078  * Returns 0 on success; nonzero on failure.
1079  */
1080 int extract_wim_resource_to_fd(const struct lookup_table_entry *lte, int fd,
1081                                u64 size)
1082 {
1083         u64 bytes_remaining = size;
1084         u8 buf[min(WIM_CHUNK_SIZE, bytes_remaining)];
1085         u64 offset = 0;
1086         int ret = 0;
1087         u8 hash[SHA1_HASH_SIZE];
1088
1089         SHA_CTX ctx;
1090         sha1_init(&ctx);
1091
1092         while (bytes_remaining) {
1093                 u64 to_read = min(bytes_remaining, WIM_CHUNK_SIZE);
1094                 ret = read_wim_resource(lte, buf, to_read, offset, 0);
1095                 if (ret != 0)
1096                         break;
1097                 sha1_update(&ctx, buf, to_read);
1098                 if (full_write(fd, buf, to_read) < to_read) {
1099                         ERROR_WITH_ERRNO("Error extracting WIM resource");
1100                         return WIMLIB_ERR_WRITE;
1101                 }
1102                 bytes_remaining -= to_read;
1103                 offset += to_read;
1104         }
1105         sha1_final(hash, &ctx);
1106         if (!hashes_equal(hash, lte->hash)) {
1107                 ERROR("Invalid checksum on a WIM resource "
1108                       "(detected when extracting to external file)");
1109                 ERROR("The following WIM resource is invalid:");
1110                 print_lookup_table_entry(lte);
1111                 return WIMLIB_ERR_INVALID_RESOURCE_HASH;
1112         }
1113         return 0;
1114 }
1115
1116 /*
1117  * Extracts the WIM resource specified by @lte to the open file descriptor @fd.
1118  *
1119  * Returns 0 on success; nonzero on failure.
1120  */
1121 int extract_full_wim_resource_to_fd(const struct lookup_table_entry *lte, int fd)
1122 {
1123         return extract_wim_resource_to_fd(lte, fd, wim_resource_size(lte));
1124 }
1125
1126 /*
1127  * Copies the file resource specified by the lookup table entry @lte from the
1128  * input WIM to the output WIM that has its FILE * given by
1129  * ((WIMStruct*)wim)->out_fp.
1130  *
1131  * The output_resource_entry, out_refcnt, and part_number fields of @lte are
1132  * updated.
1133  *
1134  * Metadata resources are not copied (they are handled elsewhere for joining and
1135  * splitting).
1136  */
1137 int copy_resource(struct lookup_table_entry *lte, void *wim)
1138 {
1139         WIMStruct *w = wim;
1140         int ret;
1141
1142         if ((lte->resource_entry.flags & WIM_RESHDR_FLAG_METADATA) &&
1143             !w->write_metadata)
1144                 return 0;
1145
1146         ret = write_wim_resource(lte, w->out_fp,
1147                                  wim_resource_compression_type(lte),
1148                                  &lte->output_resource_entry, 0);
1149         if (ret != 0)
1150                 return ret;
1151         lte->out_refcnt = lte->refcnt;
1152         lte->part_number = w->hdr.part_number;
1153         return 0;
1154 }
1155
1156 /*
1157  * Writes a dentry's resources, including the main file resource as well as all
1158  * alternate data streams, to the output file.
1159  *
1160  * @dentry:  The dentry for the file.
1161  * @wim_p:   A pointer to the WIMStruct containing @dentry.
1162  *
1163  * @return zero on success, nonzero on failure.
1164  */
1165 int write_dentry_resources(struct dentry *dentry, void *wim_p)
1166 {
1167         WIMStruct *w = wim_p;
1168         int ret = 0;
1169         struct lookup_table_entry *lte;
1170         int ctype = wimlib_get_compression_type(w);
1171
1172         if (w->write_flags & WIMLIB_WRITE_FLAG_VERBOSE) {
1173                 wimlib_assert(dentry->full_path_utf8);
1174                 printf("Writing streams for `%s'\n", dentry->full_path_utf8);
1175         }
1176
1177         for (unsigned i = 0; i <= dentry->d_inode->num_ads; i++) {
1178                 lte = inode_stream_lte(dentry->d_inode, i, w->lookup_table);
1179                 if (lte && ++lte->out_refcnt == 1) {
1180                         ret = write_wim_resource(lte, w->out_fp, ctype,
1181                                                  &lte->output_resource_entry, 0);
1182                         if (ret != 0)
1183                                 break;
1184                 }
1185         }
1186         return ret;
1187 }
1188
1189 /*
1190  * Reads the metadata metadata resource from the WIM file.  The metadata
1191  * resource consists of the security data, followed by the directory entry for
1192  * the root directory, followed by all the other directory entries in the
1193  * filesystem.  The subdir_offset field of each directory entry gives the start
1194  * of its child entries from the beginning of the metadata resource.  An
1195  * end-of-directory is signaled by a directory entry of length '0', really of
1196  * length 8, because that's how long the 'length' field is.
1197  *
1198  * @fp:         The FILE* for the input WIM file.
1199  * @wim_ctype:  The compression type of the WIM file.
1200  * @imd:        Pointer to the image metadata structure.  Its `metadata_lte'
1201  *              member specifies the lookup table entry for the metadata
1202  *              resource.  The rest of the image metadata entry will be filled
1203  *              in by this function.
1204  *
1205  * @return:     Zero on success, nonzero on failure.
1206  */
1207 int read_metadata_resource(WIMStruct *w, struct image_metadata *imd)
1208 {
1209         u8 *buf;
1210         u32 dentry_offset;
1211         int ret;
1212         struct dentry *dentry;
1213         struct inode_table inode_tab;
1214         const struct lookup_table_entry *metadata_lte;
1215         u64 metadata_len;
1216         u64 metadata_offset;
1217         struct hlist_head inode_list;
1218
1219         metadata_lte = imd->metadata_lte;
1220         metadata_len = wim_resource_size(metadata_lte);
1221         metadata_offset = metadata_lte->resource_entry.offset;
1222
1223         DEBUG("Reading metadata resource: length = %"PRIu64", "
1224               "offset = %"PRIu64"", metadata_len, metadata_offset);
1225
1226         /* There is no way the metadata resource could possibly be less than (8
1227          * + WIM_DENTRY_DISK_SIZE) bytes, where the 8 is for security data (with
1228          * no security descriptors) and WIM_DENTRY_DISK_SIZE is for the root
1229          * dentry. */
1230         if (metadata_len < 8 + WIM_DENTRY_DISK_SIZE) {
1231                 ERROR("Expected at least %u bytes for the metadata resource",
1232                       8 + WIM_DENTRY_DISK_SIZE);
1233                 return WIMLIB_ERR_INVALID_RESOURCE_SIZE;
1234         }
1235
1236         if (sizeof(size_t) < 8 && metadata_len > 0xffffffff) {
1237                 ERROR("Metadata resource is too large (%"PRIu64" bytes",
1238                       metadata_len);
1239                 return WIMLIB_ERR_INVALID_RESOURCE_SIZE;
1240         }
1241
1242         /* Allocate memory for the uncompressed metadata resource. */
1243         buf = MALLOC(metadata_len);
1244
1245         if (!buf) {
1246                 ERROR("Failed to allocate %"PRIu64" bytes for uncompressed "
1247                       "metadata resource", metadata_len);
1248                 return WIMLIB_ERR_NOMEM;
1249         }
1250
1251         /* Read the metadata resource into memory.  (It may be compressed.) */
1252         ret = read_full_wim_resource(metadata_lte, buf, 0);
1253         if (ret != 0)
1254                 goto out_free_buf;
1255
1256         DEBUG("Finished reading metadata resource into memory.");
1257
1258         /* The root directory entry starts after security data, aligned on an
1259          * 8-byte boundary within the metadata resource.
1260          *
1261          * The security data starts with a 4-byte integer giving its total
1262          * length, so if we round that up to an 8-byte boundary that gives us
1263          * the offset of the root dentry.
1264          *
1265          * Here we read the security data into a wim_security_data structure,
1266          * and if successful, go ahead and calculate the offset in the metadata
1267          * resource of the root dentry. */
1268
1269         wimlib_assert(imd->security_data == NULL);
1270         ret = read_security_data(buf, metadata_len, &imd->security_data);
1271         if (ret != 0)
1272                 goto out_free_buf;
1273
1274         dentry_offset = (imd->security_data->total_length + 7) & ~7;
1275
1276         if (dentry_offset == 0) {
1277                 ERROR("Integer overflow while reading metadata resource");
1278                 ret = WIMLIB_ERR_INVALID_SECURITY_DATA;
1279                 goto out_free_security_data;
1280         }
1281
1282         /* Allocate memory for the root dentry and read it into memory */
1283         dentry = MALLOC(sizeof(struct dentry));
1284         if (!dentry) {
1285                 ERROR("Failed to allocate %zu bytes for root dentry",
1286                       sizeof(struct dentry));
1287                 ret = WIMLIB_ERR_NOMEM;
1288                 goto out_free_security_data;
1289         }
1290
1291         ret = read_dentry(buf, metadata_len, dentry_offset, dentry);
1292
1293         /* This is the root dentry, so set its parent to itself. */
1294         dentry->parent = dentry;
1295
1296         if (ret != 0)
1297                 goto out_free_dentry_tree;
1298         inode_add_dentry(dentry, dentry->d_inode);
1299
1300         /* Now read the entire directory entry tree into memory. */
1301         DEBUG("Reading dentry tree");
1302         ret = read_dentry_tree(buf, metadata_len, dentry);
1303         if (ret != 0)
1304                 goto out_free_dentry_tree;
1305
1306         /* Calculate the full paths in the dentry tree. */
1307         DEBUG("Calculating dentry full paths");
1308         ret = for_dentry_in_tree(dentry, calculate_dentry_full_path, NULL);
1309         if (ret != 0)
1310                 goto out_free_dentry_tree;
1311
1312         /* Build hash table that maps hard link group IDs to dentry sets */
1313         DEBUG("Building link group table");
1314         ret = init_inode_table(&inode_tab, 9001);
1315         if (ret != 0)
1316                 goto out_free_dentry_tree;
1317
1318         for_dentry_in_tree(dentry, inode_table_insert, &inode_tab);
1319
1320         DEBUG("Fixing inconsistencies in the hard link groups");
1321         ret = fix_inodes(&inode_tab, &inode_list);
1322         destroy_inode_table(&inode_tab);
1323         if (ret != 0)
1324                 goto out_free_dentry_tree;
1325
1326         DEBUG("Running miscellaneous verifications on the dentry tree");
1327         for_lookup_table_entry(w->lookup_table, lte_zero_real_refcnt, NULL);
1328         ret = for_dentry_in_tree(dentry, verify_dentry, w);
1329         if (ret != 0)
1330                 goto out_free_dentry_tree;
1331
1332         DEBUG("Done reading image metadata");
1333
1334         imd->root_dentry = dentry;
1335         imd->inode_list  = inode_list;
1336         goto out_free_buf;
1337 out_free_dentry_tree:
1338         free_dentry_tree(dentry, NULL);
1339 out_free_security_data:
1340         free_security_data(imd->security_data);
1341         imd->security_data = NULL;
1342 out_free_buf:
1343         FREE(buf);
1344         return ret;
1345 }
1346
1347 /* Write the metadata resource for the current WIM image. */
1348 int write_metadata_resource(WIMStruct *w)
1349 {
1350         u8 *buf;
1351         u8 *p;
1352         int ret;
1353         u64 subdir_offset;
1354         struct dentry *root;
1355         struct lookup_table_entry *lte;
1356         u64 metadata_original_size;
1357         const struct wim_security_data *sd;
1358
1359         DEBUG("Writing metadata resource for image %d", w->current_image);
1360
1361         root = wim_root_dentry(w);
1362         sd = wim_security_data(w);
1363
1364         /* We do not allow the security data pointer to be NULL, although it may
1365          * point to an empty security data with no entries. */
1366         wimlib_assert(root != NULL);
1367         wimlib_assert(sd != NULL);
1368
1369         /* Offset of first child of the root dentry.  It's equal to:
1370          * - The total length of the security data, rounded to the next 8-byte
1371          *   boundary,
1372          * - plus the total length of the root dentry,
1373          * - plus 8 bytes for an end-of-directory entry following the root
1374          *   dentry (shouldn't really be needed, but just in case...)
1375          */
1376         subdir_offset = ((sd->total_length + 7) & ~7) +
1377                         dentry_correct_total_length(root) + 8;
1378
1379         /* Calculate the subdirectory offsets for the entire dentry tree. */
1380         calculate_subdir_offsets(root, &subdir_offset);
1381
1382         /* Total length of the metadata resource (uncompressed) */
1383         metadata_original_size = subdir_offset;
1384
1385         /* Allocate a buffer to contain the uncompressed metadata resource */
1386         buf = MALLOC(metadata_original_size);
1387         if (!buf) {
1388                 ERROR("Failed to allocate %"PRIu64" bytes for "
1389                       "metadata resource", metadata_original_size);
1390                 return WIMLIB_ERR_NOMEM;
1391         }
1392
1393         /* Write the security data into the resource buffer */
1394         p = write_security_data(sd, buf);
1395
1396         /* Write the dentry tree into the resource buffer */
1397         p = write_dentry_tree(root, p);
1398
1399         /* We MUST have exactly filled the buffer; otherwise we calculated its
1400          * size incorrectly or wrote the data incorrectly. */
1401         wimlib_assert(p - buf == metadata_original_size);
1402
1403         /* Get the lookup table entry for the metadata resource so we can update
1404          * it. */
1405         lte = wim_metadata_lookup_table_entry(w);
1406
1407         wimlib_assert(lte != NULL);
1408
1409         /* Write the metadata resource to the output WIM using the proper
1410          * compression type.  The lookup table entry for the metadata resource
1411          * is updated. */
1412         ret = write_wim_resource_from_buffer(buf, metadata_original_size,
1413                                              w->out_fp,
1414                                              wimlib_get_compression_type(w),
1415                                              &lte->output_resource_entry,
1416                                              lte->hash);
1417         if (ret != 0)
1418                 goto out;
1419
1420         /* It's very likely the SHA1 message digest of the metadata resource
1421          * changed, so re-insert the lookup table entry into the lookup table.
1422          *
1423          * We do not check for other lookup table entries having the same SHA1
1424          * message digest.  It's possible for 2 absolutely identical images to
1425          * be added, therefore causing 2 identical metadata resources to be in
1426          * the WIM.  However, in this case, it's expected for 2 separate lookup
1427          * table entries to be created, even though this doesn't make a whole
1428          * lot of sense since they will share the same SHA1 message digest.
1429          * */
1430         lookup_table_unlink(w->lookup_table, lte);
1431         lookup_table_insert(w->lookup_table, lte);
1432
1433         wimlib_assert(lte->out_refcnt == 0);
1434         lte->out_refcnt = 1;
1435
1436         /* Make sure that the resource entry is written marked with the metadata
1437          * flag. */
1438         lte->output_resource_entry.flags |= WIM_RESHDR_FLAG_METADATA;
1439 out:
1440         /* All the data has been written to the new WIM; no need for the buffer
1441          * anymore */
1442         FREE(buf);
1443         return ret;
1444 }