}
/*
+ * finish_write():
+ *
* Finish writing a WIM file: write the lookup table, xml data, and integrity
- * table, then overwrite the WIM header. Always closes the WIM file descriptor
- * (wim->out_fd).
+ * table, then overwrite the WIM header. By default, closes the WIM file
+ * descriptor (@wim->out_fd) if successful.
*
* write_flags is a bitwise OR of the following:
*
* (public) WIMLIB_WRITE_FLAG_FSYNC:
* fsync() the output file before closing it.
*
- * (public) WIMLIB_WRITE_FLAG_PIPABLE:
+ * (public) WIMLIB_WRITE_FLAG_PIPABLE:
* Writing a pipable WIM, possibly to a pipe; include pipable WIM
* stream headers before the lookup table and XML data, and also
* write the WIM header at the end instead of seeking to the
* Instead of overwriting the WIM header at the beginning of the
* file, simply append it to the end of the file. (Used when
* writing to pipe.)
+ * (private) WIMLIB_WRITE_FLAG_FILE_DESCRIPTOR:
+ * Do not close the file descriptor @wim->out_fd on either success
+ * on failure.
* (private) WIMLIB_WRITE_FLAG_USE_EXISTING_TOTALBYTES:
* Use the existing <TOTALBYTES> stored in the in-memory XML
* information, rather than setting it to the offset of the XML
&wim->hdr.lookup_table_res_entry,
stream_list_override);
if (ret)
- goto out_close_wim;
+ return ret;
}
/* Write XML data. */
&wim->hdr.xml_res_entry,
write_resource_flags);
if (ret)
- goto out_close_wim;
+ return ret;
+ /* Write integrity table (optional). */
if (write_flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) {
if (write_flags & WIMLIB_WRITE_FLAG_CHECKPOINT_AFTER_XML) {
struct wim_header checkpoint_hdr;
ret = write_wim_header_at_offset(&checkpoint_hdr,
&wim->out_fd, 0);
if (ret)
- goto out_close_wim;
+ return ret;
}
if (!(write_flags & WIMLIB_WRITE_FLAG_REUSE_INTEGRITY_TABLE))
old_lookup_table_end,
progress_func);
if (ret)
- goto out_close_wim;
+ return ret;
} else {
+ /* No integrity table. */
zero_resource_entry(&wim->hdr.integrity);
}
+ /* Now that all information in the WIM header has been determined, the
+ * preliminary header written earlier can be overwritten, the header of
+ * the existing WIM file can be overwritten, or the final header can be
+ * written to the end of the pipable WIM. */
wim->hdr.flags &= ~WIM_HDR_FLAG_WRITE_IN_PROGRESS;
hdr_offset = 0;
if (write_flags & WIMLIB_WRITE_FLAG_HEADER_AT_END)
hdr_offset = wim->out_fd.offset;
ret = write_wim_header_at_offset(&wim->hdr, &wim->out_fd, hdr_offset);
if (ret)
- goto out_close_wim;
+ return ret;
+ /* Possibly sync file data to disk before closing. On POSIX systems, it
+ * is necessary to do this before using rename() to overwrite an
+ * existing file with a new file. Otherwise, data loss would occur if
+ * the system is abruptly terminated when the metadata for the rename
+ * operation has been written to disk, but the new file data has not.
+ */
if (write_flags & WIMLIB_WRITE_FLAG_FSYNC) {
if (fsync(wim->out_fd.fd)) {
ERROR_WITH_ERRNO("Error syncing data to WIM file");
- ret = WIMLIB_ERR_WRITE;
- goto out_close_wim;
+ return WIMLIB_ERR_WRITE;
}
}
- ret = 0;
-out_close_wim:
if (close_wim_writable(wim, write_flags)) {
- if (ret == 0) {
- ERROR_WITH_ERRNO("Failed to close the output WIM file");
- ret = WIMLIB_ERR_WRITE;
- }
+ ERROR_WITH_ERRNO("Failed to close the output WIM file");
+ return WIMLIB_ERR_WRITE;
}
- return ret;
+
+ return 0;
}
#if defined(HAVE_SYS_FILE_H) && defined(HAVE_FLOCK)
#endif
/*
+ * write_pipable_wim():
+ *
* Perform the intermediate stages of creating a "pipable" WIM (i.e. a WIM
- * capable of being applied from a pipe). Such a WIM looks like:
+ * capable of being applied from a pipe).
*
* Pipable WIMs are a wimlib-specific modification of the WIM format such that
* images can be applied from them sequentially when the file data is sent over
*
* - Magic characters in header are "WLPWM\0\0\0" (wimlib pipable WIM) instead
* of "MSWIM\0\0\0". This lets wimlib know that the WIM is pipable and also
- * should stop other software from trying to read the file as a normal WIM.
+ * stops other software from trying to read the file as a normal WIM.
*
* - The header at the beginning of the file does not contain all the normal
* information; in particular it will have all 0's for the lookup table and
* uncompressed stream size, and flags that indicate whether the stream is
* compressed. The data of uncompressed streams then follows literally, while
* the data of compressed streams follows in a modified format. Compressed
- * streams have no chunk table, since the chunk table cannot be written until
- * all chunks have been compressed; instead, each compressed chunk is prefixed
- * by a `struct pwm_chunk_hdr' that gives its size. However, the offsets are
- * given in the chunk table as if these chunk headers were not present.
+ * streams do not begin with a chunk table, since the chunk table cannot be
+ * written until all chunks have been compressed. Instead, each compressed
+ * chunk is prefixed by a `struct pwm_chunk_hdr' that gives its size.
+ * Furthermore, the chunk table is written at the end of the resource instead
+ * of the start. Note: chunk offsets are given in the chunk table as if the
+ * `struct pwm_chunk_hdr's were not present; also, the chunk table is only
+ * used if the WIM is being read from a seekable file (not a pipe).
*
* - Metadata resources always come before other file resources (streams).
* (This does not by itself constitute an incompatibility with normal WIMs,
*
* Layout of pipable WIM:
*
- * ----------+----------+--------------------+----------------+--------------+------------+--------+
- * | Header | XML data | Metadata resources | File resources | Lookup table | XML data | Header |
- * ----------+----------+--------------------+----------------+--------------+------------+--------+
+ * ---------+----------+--------------------+----------------+--------------+-----------+--------+
+ * | Header | XML data | Metadata resources | File resources | Lookup table | XML data | Header |
+ * ---------+----------+--------------------+----------------+--------------+-----------+--------+
*
* Layout of normal WIM:
*
- * +---------+--------------------+----------------+--------------+----------+
- * | Header | Metadata resources | File resources | Lookup table | XML data |
- * +---------+--------------------+----------------+--------------+----------+
+ * +--------+-----------------------------+-------------------------+
+ * | Header | File and metadata resources | Lookup table | XML data |
+ * +--------+-----------------------------+-------------------------+
+ *
+ * An optional integrity table can follow the final XML data in both normal and
+ * pipable WIMs. However, due to implementation details, wimlib currently can
+ * only include an integrity table in a pipable WIM when writing it to a
+ * seekable file (not a pipe).
*
* Do note that since pipable WIMs are not supported by Microsoft's software,
* wimlib does not create them unless explicitly requested (with
stream_list_override);
out_restore_hdr:
memcpy(&wim->hdr, &hdr_save, sizeof(struct wim_header));
- close_wim_writable(wim, write_flags);
+ (void)close_wim_writable(wim, write_flags);
return ret;
}
struct list_head stream_list;
off_t old_wim_end;
u64 old_lookup_table_end, old_xml_begin, old_xml_end;
-
+ struct wim_header hdr_save;
DEBUG("Overwriting `%"TS"' in-place", wim->filename);
return ret;
ret = lock_wim(wim, wim->out_fd.fd);
- if (ret) {
- close_wim_writable(wim, write_flags);
- return ret;
- }
+ if (ret)
+ goto out_close_wim;
+
+ /* Save original header so it can be restored in case of error */
+ memcpy(&hdr_save, &wim->hdr, sizeof(struct wim_header));
/* Set WIM_HDR_FLAG_WRITE_IN_PROGRESS flag in header. */
- ret = write_wim_header_flags(wim->hdr.flags | WIM_HDR_FLAG_WRITE_IN_PROGRESS,
- &wim->out_fd);
+ wim->hdr.flags |= WIM_HDR_FLAG_WRITE_IN_PROGRESS;
+ ret = write_wim_header_flags(wim->hdr.flags, &wim->out_fd);
if (ret) {
ERROR_WITH_ERRNO("Error updating WIM header flags");
- close_wim_writable(wim, write_flags);
- goto out_unlock_wim;
+ goto out_restore_memory_hdr;
}
if (filedes_seek(&wim->out_fd, old_wim_end) == -1) {
ERROR_WITH_ERRNO("Can't seek to end of WIM");
- close_wim_writable(wim, write_flags);
ret = WIMLIB_ERR_WRITE;
- goto out_unlock_wim;
+ goto out_restore_physical_hdr;
}
ret = write_stream_list(&stream_list,
write_flags |= WIMLIB_WRITE_FLAG_REUSE_INTEGRITY_TABLE;
ret = finish_write(wim, WIMLIB_ALL_IMAGES, write_flags,
progress_func, NULL);
+ if (ret)
+ goto out_truncate;
+
+ goto out_unlock_wim;
+
out_truncate:
- close_wim_writable(wim, write_flags);
- if (ret && !(write_flags & WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE)) {
+ if (!(write_flags & WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE)) {
WARNING("Truncating `%"TS"' to its original size (%"PRIu64" bytes)",
wim->filename, old_wim_end);
- /* Return value of truncate() is ignored because this is already
- * an error path. */
- (void)ttruncate(wim->filename, old_wim_end);
+ /* Return value of ftruncate() is ignored because this is
+ * already an error path. */
+ (void)ftruncate(wim->out_fd.fd, old_wim_end);
}
+out_restore_physical_hdr:
+ (void)write_wim_header_flags(hdr_save.flags, &wim->out_fd);
+out_restore_memory_hdr:
+ memcpy(&wim->hdr, &hdr_save, sizeof(struct wim_header));
+out_close_wim:
+ (void)close_wim_writable(wim, write_flags);
out_unlock_wim:
wim->wim_locked = 0;
return ret;