+ return write_header(&w->hdr, w->out_fp);
+}
+
+/* Writes a stand-alone WIM to a file. */
+WIMLIBAPI int wimlib_write(WIMStruct *w, const char *path,
+ int image, int write_flags, unsigned num_threads,
+ wimlib_progress_func_t progress_func)
+{
+ int ret;
+
+ if (!path)
+ return WIMLIB_ERR_INVALID_PARAM;
+
+ write_flags &= WIMLIB_WRITE_MASK_PUBLIC;
+
+ if (image != WIMLIB_ALL_IMAGES &&
+ (image < 1 || image > w->hdr.image_count))
+ return WIMLIB_ERR_INVALID_IMAGE;
+
+ if (w->hdr.total_parts != 1) {
+ ERROR("Cannot call wimlib_write() on part of a split WIM");
+ return WIMLIB_ERR_SPLIT_UNSUPPORTED;
+ }
+
+ ret = begin_write(w, path, write_flags);
+ if (ret != 0)
+ goto out;
+
+ ret = write_wim_streams(w, image, write_flags, num_threads,
+ progress_func);
+ if (ret != 0)
+ goto out;
+
+ if (progress_func)
+ progress_func(WIMLIB_PROGRESS_MSG_WRITE_METADATA_BEGIN, NULL);
+
+ ret = for_image(w, image, write_metadata_resource);
+ if (ret != 0)
+ goto out;
+
+ if (progress_func)
+ progress_func(WIMLIB_PROGRESS_MSG_WRITE_METADATA_END, NULL);
+
+ ret = finish_write(w, image, write_flags, progress_func);
+out:
+ close_wim_writable(w);
+ return ret;
+}
+
+static bool any_images_modified(WIMStruct *w)
+{
+ for (int i = 0; i < w->hdr.image_count; i++)
+ if (w->image_metadata[i].modified)
+ return true;
+ return false;
+}
+
+/*
+ * Overwrite a WIM, possibly appending streams to it.
+ *
+ * A WIM looks like (or is supposed to look like) the following:
+ *
+ * Header (212 bytes)
+ * Streams and metadata resources (variable size)
+ * Lookup table (variable size)
+ * XML data (variable size)
+ * Integrity table (optional) (variable size)
+ *
+ * If we are not adding any streams or metadata resources, the lookup table is
+ * unchanged--- so we only need to overwrite the XML data, integrity table, and
+ * header. This operation is potentially unsafe if the program is abruptly
+ * terminated while the XML data or integrity table are being overwritten, but
+ * before the new header has been written. To partially alleviate this problem,
+ * a special flag (WIMLIB_WRITE_FLAG_CHECKPOINT_AFTER_XML) is passed to
+ * finish_write() to cause a temporary WIM header to be written after the XML
+ * data has been written. This may prevent the WIM from becoming corrupted if
+ * the program is terminated while the integrity table is being calculated (but
+ * no guarantees, due to write re-ordering...).
+ *
+ * If we are adding new streams or images (metadata resources), the lookup table
+ * needs to be changed, and those streams need to be written. In this case, we
+ * try to perform a safe update of the WIM file by writing the streams *after*
+ * the end of the previous WIM, then writing the new lookup table, XML data, and
+ * (optionally) integrity table following the new streams. This will produce a
+ * layout like the following:
+ *
+ * Header (212 bytes)
+ * (OLD) Streams and metadata resources (variable size)
+ * (OLD) Lookup table (variable size)
+ * (OLD) XML data (variable size)
+ * (OLD) Integrity table (optional) (variable size)
+ * (NEW) Streams and metadata resources (variable size)
+ * (NEW) Lookup table (variable size)
+ * (NEW) XML data (variable size)
+ * (NEW) Integrity table (optional) (variable size)
+ *
+ * At all points, the WIM is valid as nothing points to the new data yet. Then,
+ * the header is overwritten to point to the new lookup table, XML data, and
+ * integrity table, to produce the following layout:
+ *
+ * Header (212 bytes)
+ * Streams and metadata resources (variable size)
+ * Nothing (variable size)
+ * More Streams and metadata resources (variable size)
+ * Lookup table (variable size)
+ * XML data (variable size)
+ * Integrity table (optional) (variable size)
+ *
+ * This method allows an image to be appended to a large WIM very quickly, and
+ * is is crash-safe except in the case of write re-ordering, but the
+ * disadvantage is that a small hole is left in the WIM where the old lookup
+ * table, xml data, and integrity table were. (These usually only take up a
+ * small amount of space compared to the streams, however.)
+ */
+static int overwrite_wim_inplace(WIMStruct *w, int write_flags,
+ unsigned num_threads,
+ wimlib_progress_func_t progress_func)
+{
+ int ret;
+ struct list_head stream_list;
+ off_t old_wim_end;
+ bool found_modified_image;
+
+ DEBUG("Overwriting `%s' in-place", w->filename);
+
+ /* 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
+ * overwritten. */
+ 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;
+ }
+
+
+ if (w->hdr.integrity.offset)
+ old_wim_end = w->hdr.integrity.offset + w->hdr.integrity.size;
+ else
+ old_wim_end = w->hdr.xml_res_entry.offset + w->hdr.xml_res_entry.size;
+
+ if (!w->deletion_occurred && !any_images_modified(w)) {
+ /* If no images have been modified and no images have been
+ * deleted, a new lookup table does not need to be written. */
+ old_wim_end = w->hdr.lookup_table_res_entry.offset +
+ w->hdr.lookup_table_res_entry.size;
+ write_flags |= WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE |
+ WIMLIB_WRITE_FLAG_CHECKPOINT_AFTER_XML;
+ }
+ INIT_LIST_HEAD(&stream_list);
+ ret = wim_find_new_streams(w, old_wim_end, &stream_list);