Cleanup WIM writing
authorEric Biggers <ebiggers3@gmail.com>
Sun, 28 Oct 2012 02:59:10 +0000 (21:59 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Sun, 28 Oct 2012 02:59:10 +0000 (21:59 -0500)
src/dentry.c
src/integrity.c
src/join.c
src/resource.c
src/split.c
src/util.c
src/wimlib.h
src/wimlib_internal.h
src/write.c

index bca9f35e2afc0ca9fa5fdbb38e3536e5d183361b..7d17fb9c9db0408d3486c0ef0d1f2e705607d42c 100644 (file)
@@ -40,6 +40,9 @@
 #include "wimlib_internal.h"
 
 
+/* Calculates the unaligned length, in bytes, of an on-disk WIM dentry that has
+ * a file name and short name that take the specified numbers of bytes.  This
+ * excludes any alternate data stream entries that may follow the dentry. */
 static u64 __dentry_correct_length_unaligned(u16 file_name_len,
                                             u16 short_name_len)
 {
@@ -51,23 +54,25 @@ static u64 __dentry_correct_length_unaligned(u16 file_name_len,
        return length;
 }
 
+/* Calculates the unaligned length, in bytes, of an on-disk WIM dentry, based on
+ * the file name length and short name length.  Note that dentry->length is
+ * ignored; also, this excludes any alternate data stream entries that may
+ * follow the dentry. */
 static u64 dentry_correct_length_unaligned(const struct dentry *dentry)
 {
        return __dentry_correct_length_unaligned(dentry->file_name_len,
                                                 dentry->short_name_len);
 }
 
-/* Return the "correct" value to write in the length field of the dentry, based
- * on the file name length and short name length */
+/* Return the "correct" value to write in the length field of a WIM dentry,
+ * based on the file name length and short name length. */
 static u64 dentry_correct_length(const struct dentry *dentry)
 {
        return (dentry_correct_length_unaligned(dentry) + 7) & ~7;
 }
 
-/*
- * Returns true if @dentry has the UTF-8 file name @name that has length
- * @name_len.
- */
+/* Return %true iff @dentry has the UTF-8 file name @name that has length
+ * @name_len bytes. */
 static bool dentry_has_name(const struct dentry *dentry, const char *name,
                            size_t name_len)
 {
@@ -76,6 +81,8 @@ static bool dentry_has_name(const struct dentry *dentry, const char *name,
        return memcmp(dentry->file_name_utf8, name, name_len) == 0;
 }
 
+/* Return %true iff the alternate data stream entry @entry has the UTF-8 stream
+ * name @name that has length @name_len bytes. */
 static inline bool ads_entry_has_name(const struct ads_entry *entry,
                                      const char *name, size_t name_len)
 {
@@ -163,14 +170,16 @@ static u64 __dentry_total_length(const struct dentry *dentry, u64 length)
        return (length + 7) & ~7;
 }
 
+/* Calculate the aligned *total* length of an on-disk WIM dentry.  This includes
+ * all alternate data streams. */
 u64 dentry_correct_total_length(const struct dentry *dentry)
 {
        return __dentry_total_length(dentry,
                                     dentry_correct_length_unaligned(dentry));
 }
 
-/* Real length of a dentry, including the alternate data stream entries, which
- * are not included in the dentry->length field... */
+/* Like dentry_correct_total_length(), but use the existing dentry->length field
+ * instead of calculating its "correct" value. */
 static u64 dentry_total_length(const struct dentry *dentry)
 {
        return __dentry_total_length(dentry, dentry->length);
@@ -352,13 +361,14 @@ oom:
  *
  * @dentry:  The root of the directory tree.
  * @subdir_offset_p:  The current subdirectory offset; i.e., the subdirectory
- *     offset for @dentry.
+ *                   offset for @dentry.
  */
 void calculate_subdir_offsets(struct dentry *dentry, u64 *subdir_offset_p)
 {
-       struct dentry *child;
+       struct dentry *child, *children;
 
-       child = dentry->d_inode->children;
+       children = dentry->d_inode->children;
+       child = children;
        dentry->subdir_offset = *subdir_offset_p;
 
        if (child) {
@@ -367,7 +377,7 @@ void calculate_subdir_offsets(struct dentry *dentry, u64 *subdir_offset_p)
                do {
                        *subdir_offset_p += dentry_correct_total_length(child);
                        child = child->next;
-               } while (child != dentry->d_inode->children);
+               } while (child != children);
 
                /* End-of-directory dentry on disk. */
                *subdir_offset_p += 8;
@@ -377,7 +387,7 @@ void calculate_subdir_offsets(struct dentry *dentry, u64 *subdir_offset_p)
                do {
                        calculate_subdir_offsets(child, subdir_offset_p);
                        child = child->next;
-               } while (child != dentry->d_inode->children);
+               } while (child != children);
        } else {
                /* On disk, childless directories have a valid subdir_offset
                 * that points to an 8-byte end-of-directory dentry.  Regular
@@ -1675,7 +1685,7 @@ static u8 *write_dentry(const struct dentry *dentry, u8 *p)
  * @parent itself, which has already been written. */
 static u8 *write_dentry_tree_recursive(const struct dentry *parent, u8 *p)
 {
-       const struct dentry *child;
+       const struct dentry *child, *children;
 
        /* Nothing to do if this dentry has no children. */
        if (parent->subdir_offset == 0)
@@ -1687,12 +1697,13 @@ static u8 *write_dentry_tree_recursive(const struct dentry *parent, u8 *p)
         * recursively writing the directory trees rooted at each of the child
         * dentries, since the on-disk dentries for a dentry's children are
         * always located at consecutive positions in the metadata resource! */
-       child = parent->d_inode->children;
+       children = parent->d_inode->children;
+       child = children;
        if (child) {
                do {
                        p = write_dentry(child, p);
                        child = child->next;
-               } while (child != parent->d_inode->children);
+               } while (child != children);
        }
 
        /* write end of directory entry */
@@ -1703,7 +1714,7 @@ static u8 *write_dentry_tree_recursive(const struct dentry *parent, u8 *p)
                do {
                        p = write_dentry_tree_recursive(child, p);
                        child = child->next;
-               } while (child != parent->d_inode->children);
+               } while (child != children);
        }
        return p;
 }
@@ -1717,6 +1728,7 @@ static u8 *write_dentry_tree_recursive(const struct dentry *parent, u8 *p)
  */
 u8 *write_dentry_tree(const struct dentry *root, u8 *p)
 {
+       DEBUG("Writing dentry tree.");
        wimlib_assert(dentry_is_root(root));
 
        /* If we're the root dentry, we have no parent that already
index 73dd94f6884498b2ff806ee08df821d1d73756eb..f1e208f17d2a4a1fc26363ddba8580a8aae55b90 100644 (file)
@@ -252,16 +252,16 @@ out:
 int write_integrity_table(FILE *out, u64 end_header_offset,
                          u64 end_lookup_table_offset, int show_progress)
 {
-       u64   bytes_to_check;
-       u64   bytes_remaining;
-       u8   *buf;
-       u8   *p;
-       u8   *chunk_buf;
-       u32   num_entries;
-       u32   integrity_table_size;
-       int   ret;
-
-       DEBUG("Writing integrity table");
+       u64  bytes_to_check;
+       u64  bytes_remaining;
+       u8  *buf;
+       u8  *p;
+       u8  *chunk_buf;
+       u32  num_entries;
+       u32  integrity_table_size;
+       int  ret;
+
+       DEBUG("Calculating integrity table");
        if (fseeko(out, end_header_offset, SEEK_SET) != 0) {
                ERROR_WITH_ERRNO("Failed to seek to byte %"PRIu64" of WIM to "
                                 "calculate integrity data", end_header_offset);
@@ -269,12 +269,11 @@ int write_integrity_table(FILE *out, u64 end_header_offset,
        }
 
        bytes_to_check = end_lookup_table_offset - end_header_offset;
-       num_entries = bytes_to_check / INTEGRITY_CHUNK_SIZE +
-                       (bytes_to_check % INTEGRITY_CHUNK_SIZE != 0);
+       num_entries = (bytes_to_check + INTEGRITY_CHUNK_SIZE - 1) /
+                       INTEGRITY_CHUNK_SIZE;
        integrity_table_size = num_entries * SHA1_HASH_SIZE + 3 * sizeof(u32);
 
-       DEBUG("integrity table size = %u", integrity_table_size);
-
+       DEBUG("integrity_table_size = %u", integrity_table_size);
 
        buf = MALLOC(integrity_table_size);
        if (!buf) {
@@ -292,7 +291,7 @@ int write_integrity_table(FILE *out, u64 end_header_offset,
                ERROR("Failed to allocate %u bytes for integrity chunk buffer",
                      INTEGRITY_CHUNK_SIZE);
                ret = WIMLIB_ERR_NOMEM;
-               goto err2;
+               goto out_free_buf;
        }
 
        bytes_remaining = bytes_to_check;
@@ -325,7 +324,7 @@ int write_integrity_table(FILE *out, u64 end_header_offset,
                                                 "checksums");
                        }
                        ret = WIMLIB_ERR_READ;
-                       goto err2;
+                       goto out_free_chunk_buf;
                }
                sha1_buffer(chunk_buf, bytes_read, p);
                p += SHA1_HASH_SIZE;
@@ -340,19 +339,19 @@ int write_integrity_table(FILE *out, u64 end_header_offset,
                ERROR_WITH_ERRNO("Failed to seek to end of WIM to write "
                                 "integrity table");
                ret = WIMLIB_ERR_WRITE;
-               goto err1;
+               goto out_free_chunk_buf;
        }
 
        if (fwrite(buf, 1, integrity_table_size, out) != integrity_table_size) {
                ERROR_WITH_ERRNO("Failed to write integrity table to end of "
                                 "WIM");
                ret = WIMLIB_ERR_WRITE;
-               goto err1;
+               goto out_free_chunk_buf;
        }
        ret = 0;
-err1:
+out_free_chunk_buf:
        FREE(chunk_buf);
-err2:
+out_free_buf:
        FREE(buf);
        return ret;
 }
index 8bdd24be83605b1ad9a7c71bfd6200f24e8424cb..d32fc1fe936c2f364965fa7a6fa1a31eb87c22e2 100644 (file)
@@ -272,7 +272,8 @@ static int join_wims(WIMStruct **swms, uint num_swms, WIMStruct *joined_wim,
         * attached to it.  */
        swms[0]->hdr.flags &= ~WIM_HDR_FLAG_SPANNED;
        swms[0]->hdr.total_parts = 1;
-       return finish_write(swms[0], WIM_ALL_IMAGES, write_flags, 0);
+       return finish_write(swms[0], WIM_ALL_IMAGES,
+                           write_flags | WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE);
 }
 
 
index ffcd7530a35f5ab590ad4ac9cc226701d7c389a1..69ccf3844111c70536420bd3ff8c862ee6f432bc 100644 (file)
@@ -435,6 +435,10 @@ u8 *put_resource_entry(u8 *p, const struct resource_entry *entry)
 int read_wim_resource(const struct lookup_table_entry *lte, u8 buf[],
                      size_t size, u64 offset, bool raw)
 {
+       int ctype;
+       int ret = 0;
+       FILE *fp;
+
        /* We shouldn't be allowing read over-runs in any part of the library.
         * */
        if (raw)
@@ -442,16 +446,13 @@ int read_wim_resource(const struct lookup_table_entry *lte, u8 buf[],
        else
                wimlib_assert(offset + size <= lte->resource_entry.original_size);
 
-       int ctype;
-       int ret;
-       FILE *fp;
        switch (lte->resource_location) {
        case RESOURCE_IN_WIM:
                /* The resource is in a WIM file, and its WIMStruct is given by
                 * the lte->wim member.  The resource may be either compressed
                 * or uncompressed. */
-               wimlib_assert(lte->wim);
-               wimlib_assert(lte->wim->fp);
+               wimlib_assert(lte->wim != NULL);
+               wimlib_assert(lte->wim->fp != NULL);
                ctype = wim_resource_compression_type(lte);
 
                wimlib_assert(ctype != WIM_COMPRESSION_TYPE_NONE ||
@@ -459,15 +460,15 @@ int read_wim_resource(const struct lookup_table_entry *lte, u8 buf[],
                               lte->resource_entry.size));
 
                if (raw || ctype == WIM_COMPRESSION_TYPE_NONE)
-                       return read_uncompressed_resource(lte->wim->fp,
-                                                         lte->resource_entry.offset + offset,
-                                                         size, buf);
+                       ret = read_uncompressed_resource(lte->wim->fp,
+                                                        lte->resource_entry.offset + offset,
+                                                        size, buf);
                else
-                       return read_compressed_resource(lte->wim->fp,
-                                                       lte->resource_entry.size,
-                                                       lte->resource_entry.original_size,
-                                                       lte->resource_entry.offset,
-                                                       ctype, size, offset, buf);
+                       ret = read_compressed_resource(lte->wim->fp,
+                                                      lte->resource_entry.size,
+                                                      lte->resource_entry.original_size,
+                                                      lte->resource_entry.offset,
+                                                      ctype, size, offset, buf);
                break;
        case RESOURCE_IN_STAGING_FILE:
        case RESOURCE_IN_FILE_ON_DISK:
@@ -484,46 +485,45 @@ int read_wim_resource(const struct lookup_table_entry *lte, u8 buf[],
                        if (!fp) {
                                ERROR_WITH_ERRNO("Failed to open the file "
                                                 "`%s'", lte->file_on_disk);
-                               return WIMLIB_ERR_OPEN;
+                               ret = WIMLIB_ERR_OPEN;
+                               break;
                        }
                }
                ret = read_uncompressed_resource(fp, offset, size, buf);
                if (fp != lte->file_on_disk_fp)
                        fclose(fp);
-               return ret;
                break;
        case RESOURCE_IN_ATTACHED_BUFFER:
                /* The resource is directly attached uncompressed in an
                 * in-memory buffer. */
-               wimlib_assert(lte->attached_buffer);
+               wimlib_assert(lte->attached_buffer != NULL);
                memcpy(buf, lte->attached_buffer + offset, size);
-               return 0;
                break;
 #ifdef WITH_NTFS_3G
        case RESOURCE_IN_NTFS_VOLUME:
-               wimlib_assert(lte->ntfs_loc);
-               if (lte->attr) {
+               wimlib_assert(lte->ntfs_loc != NULL);
+               wimlib_assert(lte->attr != NULL);
+               {
                        u64 adjusted_offset;
                        if (lte->ntfs_loc->is_reparse_point)
                                adjusted_offset = offset + 8;
                        else
                                adjusted_offset = offset;
-                       if (ntfs_attr_pread(lte->attr, offset, size, buf) == size) {
-                               return 0;
-                       } else {
+                       if (ntfs_attr_pread(lte->attr, offset, size, buf) != size) {
                                ERROR_WITH_ERRNO("Error reading NTFS attribute "
                                                 "at `%s'",
                                                 lte->ntfs_loc->path_utf8);
-                               return WIMLIB_ERR_NTFS_3G;
+                               ret = WIMLIB_ERR_NTFS_3G;
                        }
-               } else {
-                       wimlib_assert(0);
+                       break;
                }
-               break;
 #endif
        default:
-               assert(0);
+               wimlib_assert(0);
+               ret = -1;
+               break;
        }
+       return ret;
 }
 
 /*
@@ -571,7 +571,6 @@ begin_wim_resource_chunk_tab(const struct lookup_table_entry *lte,
        struct chunk_table *chunk_tab = CALLOC(1, alloc_size);
        int ret;
 
-
        if (!chunk_tab) {
                ERROR("Failed to allocate chunk table for %"PRIu64" byte "
                      "resource", size);
@@ -757,8 +756,8 @@ static int write_wim_resource(struct lookup_table_entry *lte,
        u64 original_size;
        u64 old_compressed_size;
        u64 new_compressed_size;
-       u64 offset = 0;
-       int ret = 0;
+       u64 offset;
+       int ret;
        struct chunk_table *chunk_tab = NULL;
        bool raw;
        off_t file_offset;
@@ -859,6 +858,7 @@ static int write_wim_resource(struct lookup_table_entry *lte,
        /* While there are still bytes remaining in the WIM resource, read a
         * chunk of the resource, update SHA1, then write that chunk using the
         * desired compression type. */
+       offset = 0;
        do {
                u64 to_read = min(bytes_remaining, WIM_CHUNK_SIZE);
                ret = read_wim_resource(lte, buf, to_read, offset, raw);
@@ -941,22 +941,23 @@ static int write_wim_resource(struct lookup_table_entry *lte,
                if (ftruncate(fileno(out_fp), file_offset + out_res_entry->size) != 0) {
                        ERROR_WITH_ERRNO("Failed to truncate output WIM file");
                        ret = WIMLIB_ERR_WRITE;
+                       goto out_fclose;
+               }
+       } else {
+               if (out_res_entry) {
+                       out_res_entry->size          = new_compressed_size;
+                       out_res_entry->original_size = original_size;
+                       out_res_entry->offset        = file_offset;
+                       out_res_entry->flags         = lte->resource_entry.flags
+                                                       & ~WIM_RESHDR_FLAG_COMPRESSED;
+                       if (out_ctype != WIM_COMPRESSION_TYPE_NONE)
+                               out_res_entry->flags |= WIM_RESHDR_FLAG_COMPRESSED;
                }
-               goto out_fclose;
-       }
-       wimlib_assert(new_compressed_size <= original_size || raw);
-       if (out_res_entry) {
-               out_res_entry->size          = new_compressed_size;
-               out_res_entry->original_size = original_size;
-               out_res_entry->offset        = file_offset;
-               out_res_entry->flags         = lte->resource_entry.flags
-                                               & ~WIM_RESHDR_FLAG_COMPRESSED;
-               if (out_ctype != WIM_COMPRESSION_TYPE_NONE)
-                       out_res_entry->flags |= WIM_RESHDR_FLAG_COMPRESSED;
        }
+       ret = 0;
 out_fclose:
        if (lte->resource_location == RESOURCE_IN_FILE_ON_DISK
-            && lte->file_on_disk_fp) {
+           && lte->file_on_disk_fp) {
                fclose(lte->file_on_disk_fp);
                lte->file_on_disk_fp = NULL;
        }
@@ -965,9 +966,9 @@ out_fclose:
                if (lte->attr) {
                        ntfs_attr_close(lte->attr);
                        lte->attr = NULL;
-               } if (ni) {
-                       ntfs_inode_close(ni);
                }
+               if (ni)
+                       ntfs_inode_close(ni);
        }
 #endif
 out:
@@ -983,7 +984,7 @@ static int write_wim_resource_from_buffer(const u8 *buf, u64 buf_size,
                                          struct resource_entry *out_res_entry,
                                          u8 hash[SHA1_HASH_SIZE])
 {
-       /* Set up a temporary lookup table entry that we provide to
+       /* Set up a temporary lookup table entry to provide to
         * write_wim_resource(). */
        struct lookup_table_entry lte;
        int ret;
@@ -1286,7 +1287,8 @@ int write_metadata_resource(WIMStruct *w)
 
        /* We do not allow the security data pointer to be NULL, although it may
         * point to an empty security data with no entries. */
-       wimlib_assert(sd);
+       wimlib_assert(root != NULL);
+       wimlib_assert(sd != NULL);
 
        /* Offset of first child of the root dentry.  It's equal to:
         * - The total length of the security data, rounded to the next 8-byte
@@ -1316,7 +1318,6 @@ int write_metadata_resource(WIMStruct *w)
        p = write_security_data(sd, buf);
 
        /* Write the dentry tree into the resource buffer */
-       DEBUG("Writing dentry tree.");
        p = write_dentry_tree(root, p);
 
        /*
@@ -1336,6 +1337,8 @@ int write_metadata_resource(WIMStruct *w)
         * it. */
        lte = wim_metadata_lookup_table_entry(w);
 
+       wimlib_assert(lte != NULL);
+
        /* Write the metadata resource to the output WIM using the proper
         * compression type.  The lookup table entry for the metadata resource
         * is updated. */
index 2cb5a8e44c9575b35f2fa1d36fecc8f0c7daf1f2..971306c3bed775596babb9ae627f3225915e8f54 100644 (file)
@@ -67,7 +67,8 @@ static int finish_swm(WIMStruct *w, struct lookup_table_entry *lte_chain_head,
        w->hdr.lookup_table_res_entry.offset = lookup_table_offset;
        w->hdr.lookup_table_res_entry.size =
                                xml_data_offset - lookup_table_offset;
-       ret = finish_write(w, WIM_ALL_IMAGES, write_flags, 0);
+       ret = finish_write(w, WIM_ALL_IMAGES,
+                          write_flags | WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE);
        if (ret != 0)
                return ret;
 
index b68db910ad1abab7ad3576f72cfc955bb71e4419..c0167826e1ba02ce47dba53c4b184743965e3c14 100644 (file)
@@ -180,6 +180,8 @@ static const char *error_strings[] = {
                = "Could not read the target of a symbolic link",
        [WIMLIB_ERR_RENAME]
                = "Could not rename a file",
+       [WIMLIB_ERR_RESOURCE_ORDER]
+               = "The components of the WIM were arranged in an unexpected order",
        [WIMLIB_ERR_SPECIAL_FILE]
                = "Encountered a special file that cannot be archived",
        [WIMLIB_ERR_SPLIT_INVALID]
index dddfd2ad6a02674caeeec144e33904acf64991f2..076aee176fab516609ee461ed2a33b1b70477b2b 100644 (file)
@@ -346,6 +346,7 @@ enum wimlib_error_code {
        WIMLIB_ERR_READLINK,
        WIMLIB_ERR_READ,
        WIMLIB_ERR_RENAME,
+       WIMLIB_ERR_RESOURCE_ORDER,
        WIMLIB_ERR_SPECIAL_FILE,
        WIMLIB_ERR_SPLIT_INVALID,
        WIMLIB_ERR_SPLIT_UNSUPPORTED,
@@ -1113,6 +1114,11 @@ extern int wimlib_overwrite(WIMStruct *wim, int flags);
  *     from the WIM file associated with @a wim could not be read to compute
  *     the SHA1 message digests, or the old integrity table (if it existed)
  *     could not be read.
+ * @retval ::WIMLIB_ERR_RESOURCE_ORDER
+ *     Stream resources appeared in the WIM after the XML data or integrity
+ *     table, so we could not safely overwrite the XML data and integrity
+ *     table.  Note: this error should never be received from WIMs that were
+ *     written by this library.
  * @retval ::WIMLIB_ERR_WRITE
  *     Failed to write the WIM header, the XML data, or the integrity table to
  *     the WIM file associated with @a wim.
index 08d764d152a70eaa9909d37c3eb9059564d3c12d..edadad2ab0bfdc6a5ddd26191e358e58b9384cd5 100644 (file)
@@ -470,11 +470,12 @@ extern int select_wim_image(WIMStruct *w, int image);
 extern int wim_hdr_flags_compression_type(int wim_hdr_flags);
 extern int for_image(WIMStruct *w, int image, int (*visitor)(WIMStruct *));
 
-/* write.c */
-extern int finish_write(WIMStruct *w, int image, int flags,
-                       int write_lookup_table);
+/* Internal use only */
+#define WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE 0x80000000
 
-extern int begin_write(WIMStruct *w, const char *path, int flags);
+/* write.c */
+extern int begin_write(WIMStruct *w, const char *path, int write_flags);
+extern int finish_write(WIMStruct *w, int image, int write_flags);
 
 
 #include "wimlib.h"
index 124e1bbc1e622d1371f410ec8134f01cbf6e1806..fd61ae1d747da0e427b8c45e8ebd869db5841385 100644 (file)
@@ -32,6 +32,7 @@
 #include "xml.h"
 #include <unistd.h>
 
+
 /* Reopens the FILE* for a WIM read-write. */
 static int reopen_rw(WIMStruct *w)
 {
@@ -39,6 +40,7 @@ static int reopen_rw(WIMStruct *w)
 
        if (fclose(w->fp) != 0)
                ERROR_WITH_ERRNO("Failed to close the file `%s'", w->filename);
+       w->fp = NULL;
        fp = fopen(w->filename, "r+b");
        if (!fp) {
                ERROR_WITH_ERRNO("Failed to open `%s' for reading and writing",
@@ -54,7 +56,7 @@ static int reopen_rw(WIMStruct *w)
 /*
  * Writes a WIM file to the original file that it was read from, overwriting it.
  */
-WIMLIBAPI int wimlib_overwrite(WIMStruct *w, int flags)
+WIMLIBAPI int wimlib_overwrite(WIMStruct *w, int write_flags)
 {
        const char *wimfile_name;
        size_t wim_name_len;
@@ -63,6 +65,8 @@ WIMLIBAPI int wimlib_overwrite(WIMStruct *w, int flags)
        if (!w)
                return WIMLIB_ERR_INVALID_PARAM;
 
+       write_flags &= ~WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE;
+
        wimfile_name = w->filename;
 
        DEBUG("Replacing WIM file `%s'.", wimfile_name);
@@ -78,7 +82,7 @@ WIMLIBAPI int wimlib_overwrite(WIMStruct *w, int flags)
        randomize_char_array_with_alnum(tmpfile + wim_name_len, 9);
        tmpfile[wim_name_len + 9] = '\0';
 
-       ret = wimlib_write(w, tmpfile, WIM_ALL_IMAGES, flags);
+       ret = wimlib_write(w, tmpfile, WIM_ALL_IMAGES, write_flags);
        if (ret != 0) {
                ERROR("Failed to write the WIM file `%s'", tmpfile);
                return ret;
@@ -108,8 +112,18 @@ WIMLIBAPI int wimlib_overwrite(WIMStruct *w, int flags)
        return 0;
 }
 
+static int check_resource_offset(struct lookup_table_entry *lte, void *arg)
+{
+       u64 xml_data_offset = *(u64*)arg;
+       if (lte->resource_entry.offset > xml_data_offset) {
+               ERROR("The following resource is *after* the XML data:");
+               print_lookup_table_entry(lte);
+               return WIMLIB_ERR_RESOURCE_ORDER;
+       }
+       return 0;
+}
 
-WIMLIBAPI int wimlib_overwrite_xml_and_header(WIMStruct *w, int flags)
+WIMLIBAPI int wimlib_overwrite_xml_and_header(WIMStruct *w, int write_flags)
 {
        int ret;
        FILE *fp;
@@ -118,11 +132,35 @@ WIMLIBAPI int wimlib_overwrite_xml_and_header(WIMStruct *w, int flags)
        off_t xml_size;
        size_t bytes_written;
 
-       DEBUG("Overwriting XML and header of `%s', flags = %d",
-             w->filename, flags);
+       DEBUG("Overwriting XML and header of `%s', write_flags = %#x",
+             w->filename, write_flags);
+
        if (!w->filename)
                return WIMLIB_ERR_NO_FILENAME;
 
+       write_flags &= ~WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE;
+
+       /* Make sure that the integrity table (if present) is after the XML
+        * data, and that there are no stream resources, metadata resources, or
+        * lookup tables after the XML data.  Otherwise, these data would be
+        * destroyed by this function. */
+       if (w->hdr.integrity.offset != 0 &&
+           w->hdr.integrity.offset < w->hdr.xml_res_entry.offset) {
+               ERROR("Didn't expect the integrity table to be before the XML data");
+               return WIMLIB_ERR_RESOURCE_ORDER;
+       }
+
+       if (w->hdr.lookup_table_res_entry.offset >
+           w->hdr.xml_res_entry.offset) {
+               ERROR("Didn't expect the lookup table to be after the XML data");
+               return WIMLIB_ERR_RESOURCE_ORDER;
+       }
+
+       ret = for_lookup_table_entry(w->lookup_table, check_resource_offset,
+                                    &w->hdr.xml_res_entry.offset);
+       if (ret != 0)
+               return ret;
+
        ret = reopen_rw(w);
        if (ret != 0)
                return ret;
@@ -133,8 +171,9 @@ WIMLIBAPI int wimlib_overwrite_xml_and_header(WIMStruct *w, int flags)
         * the integrity table include neither the header nor the XML data.
         * Save it for later if it exists and an integrity table was required.
         * */
-       if (flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY &&
-                       w->hdr.integrity.offset != 0) {
+       if ((write_flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY)
+            && w->hdr.integrity.offset != 0)
+       {
                DEBUG("Reading existing integrity table.");
                integrity_table = MALLOC(w->hdr.integrity.size);
                if (!integrity_table)
@@ -170,10 +209,11 @@ WIMLIBAPI int wimlib_overwrite_xml_and_header(WIMStruct *w, int flags)
        xml_size = xml_end - w->hdr.xml_res_entry.offset;
        w->hdr.xml_res_entry.size = xml_size;
        w->hdr.xml_res_entry.original_size = xml_size;
+       /* XML data offset is unchanged. */
 
-       if (flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) {
+       if (write_flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) {
                DEBUG("Writing integrity table.");
-               w->hdr.integrity.offset        = xml_end;
+               w->hdr.integrity.offset = xml_end;
                if (integrity_table) {
                        /* The existing integrity table was saved. */
                        bytes_written = fwrite(integrity_table, 1,
@@ -191,11 +231,15 @@ WIMLIBAPI int wimlib_overwrite_xml_and_header(WIMStruct *w, int flags)
                        ret = write_integrity_table(fp, WIM_HEADER_DISK_SIZE,
                                        w->hdr.lookup_table_res_entry.offset +
                                        w->hdr.lookup_table_res_entry.size,
-                                       flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS);
+                                       write_flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS);
                        if (ret != 0)
-                               goto err;
+                               return ret;
+
+                       off_t end_integrity = ftello(fp);
+                       if (end_integrity == -1)
+                               return WIMLIB_ERR_WRITE;
 
-                       off_t integrity_size           = ftello(fp) - xml_end;
+                       off_t integrity_size           = end_integrity - xml_end;
                        w->hdr.integrity.size          = integrity_size;
                        w->hdr.integrity.original_size = integrity_size;
                        w->hdr.integrity.flags         = 0;
@@ -252,12 +296,11 @@ static int write_file_resources(WIMStruct *w)
        return for_dentry_in_tree(wim_root_dentry(w), write_dentry_resources, w);
 }
 
-/* Write the lookup table, xml data, and integrity table, then overwrite the WIM
+/*
+ * Write the lookup table, xml data, and integrity table, then overwrite the WIM
  * header.
- *
- * write_lt is zero iff the lookup table is not to be written; i.e. it is
- * handled elsewhere. */
-int finish_write(WIMStruct *w, int image, int flags, int write_lt)
+ */
+int finish_write(WIMStruct *w, int image, int write_flags)
 {
        off_t lookup_table_offset;
        off_t xml_data_offset;
@@ -270,39 +313,40 @@ int finish_write(WIMStruct *w, int image, int flags, int write_lt)
        struct wim_header hdr;
        FILE *out = w->out_fp;
 
-       if (write_lt) {
+       if (!(write_flags & WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE)) {
+               /* Write the lookup table. */
                lookup_table_offset = ftello(out);
                if (lookup_table_offset == -1)
                        return WIMLIB_ERR_WRITE;
 
-               DEBUG("Writing lookup table (offset %"PRIu64")", lookup_table_offset);
-               /* Write the lookup table. */
+               DEBUG("Writing lookup table (offset %"PRIu64")",
+                     lookup_table_offset);
                ret = write_lookup_table(w->lookup_table, out);
                if (ret != 0)
                        return ret;
        }
 
-
        xml_data_offset = ftello(out);
        if (xml_data_offset == -1)
                return WIMLIB_ERR_WRITE;
-       DEBUG("Writing XML data (offset %"PRIu64")", xml_data_offset);
 
        /* @hdr will be the header for the new WIM.  First copy all the data
         * from the header in the WIMStruct; then set all the fields that may
         * have changed, including the resource entries, boot index, and image
         * count.  */
        memcpy(&hdr, &w->hdr, sizeof(struct wim_header));
-       if (write_lt) {
+       if (!(write_flags & WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE)) {
                lookup_table_size = xml_data_offset - lookup_table_offset;
-               hdr.lookup_table_res_entry.offset        = lookup_table_offset;
-               hdr.lookup_table_res_entry.size          = lookup_table_size;
+               hdr.lookup_table_res_entry.offset = lookup_table_offset;
+               hdr.lookup_table_res_entry.size = lookup_table_size;
        }
        hdr.lookup_table_res_entry.original_size = hdr.lookup_table_res_entry.size;
-       hdr.lookup_table_res_entry.flags         = WIM_RESHDR_FLAG_METADATA;
+       hdr.lookup_table_res_entry.flags = WIM_RESHDR_FLAG_METADATA;
 
+       DEBUG("Writing XML data (offset %"PRIu64")", xml_data_offset);
        ret = write_xml_data(w->wim_info, image, out,
-                            write_lt ? 0 : wim_info_get_total_bytes(w->wim_info));
+                            (write_flags & WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE) ?
+                               wim_info_get_total_bytes(w->wim_info) : 0);
        if (ret != 0)
                return ret;
 
@@ -316,18 +360,18 @@ int finish_write(WIMStruct *w, int image, int flags, int write_lt)
        hdr.xml_res_entry.original_size          = xml_data_size;
        hdr.xml_res_entry.flags                  = 0;
 
-       if (flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) {
+       if (write_flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) {
                ret = write_integrity_table(out, WIM_HEADER_DISK_SIZE,
                                            xml_data_offset,
-                                           flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS);
+                                           write_flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS);
                if (ret != 0)
                        return ret;
                end_offset = ftello(out);
                if (end_offset == -1)
                        return WIMLIB_ERR_WRITE;
-               integrity_size = end_offset - integrity_offset;
-               hdr.integrity.offset = integrity_offset;
-               hdr.integrity.size   = integrity_size;
+               integrity_size              = end_offset - integrity_offset;
+               hdr.integrity.offset        = integrity_offset;
+               hdr.integrity.size          = integrity_size;
                hdr.integrity.original_size = integrity_size;
        } else {
                hdr.integrity.offset        = 0;
@@ -373,16 +417,16 @@ int finish_write(WIMStruct *w, int image, int flags, int write_lt)
 }
 
 /* Open file stream and write dummy header for WIM. */
-int begin_write(WIMStruct *w, const char *path, int flags)
+int begin_write(WIMStruct *w, const char *path, int write_flags)
 {
        const char *mode;
        DEBUG("Opening `%s' for new WIM", path);
 
        /* checking the integrity requires going back over the file to read it.
         * XXX
-        * (It also would be possible to keep a running sha1sum as the file
-        * as written-- this would be faster, but a bit more complicated) */
-       if (flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY)
+        * (It also would be possible to keep a running sha1sum as the file is
+        * written-- this would be faster, but a bit more complicated) */
+       if (write_flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY)
                mode = "w+b";
        else
                mode = "wb";
@@ -398,14 +442,17 @@ int begin_write(WIMStruct *w, const char *path, int flags)
        return write_header(&w->hdr, w->out_fp);
 }
 
-/* Writes the WIM to a file.  */
-WIMLIBAPI int wimlib_write(WIMStruct *w, const char *path, int image, int flags)
+/* Writes a stand-alone WIM to a file.  */
+WIMLIBAPI int wimlib_write(WIMStruct *w, const char *path,
+                          int image, int write_flags)
 {
        int ret;
 
        if (!w || !path)
                return WIMLIB_ERR_INVALID_PARAM;
 
+       write_flags &= ~WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE;
+
        if (image != WIM_ALL_IMAGES &&
             (image < 1 || image > w->hdr.image_count))
                return WIMLIB_ERR_INVALID_IMAGE;
@@ -421,30 +468,30 @@ WIMLIBAPI int wimlib_write(WIMStruct *w, const char *path, int image, int flags)
        else
                DEBUG("Writing image %d to `%s'.", image, path);
 
-       ret = begin_write(w, path, flags);
+       ret = begin_write(w, path, write_flags);
        if (ret != 0)
-               goto done;
+               goto out;
 
        for_lookup_table_entry(w->lookup_table, lte_zero_out_refcnt, NULL);
 
-       w->write_flags = flags;
+       w->write_flags = write_flags;
 
        ret = for_image(w, image, write_file_resources);
        if (ret != 0) {
                ERROR("Failed to write WIM file resources to `%s'", path);
-               goto done;
+               goto out;
        }
 
        ret = for_image(w, image, write_metadata_resource);
 
        if (ret != 0) {
                ERROR("Failed to write WIM image metadata to `%s'", path);
-               goto done;
+               goto out;
        }
 
-       ret = finish_write(w, image, flags, 1);
+       ret = finish_write(w, image, write_flags);
 
-done:
+out:
        DEBUG("Closing output file.");
        if (w->out_fp != NULL) {
                if (fclose(w->out_fp) != 0) {