]> wimlib.net Git - wimlib/blobdiff - src/write.c
Add ADS entry workaround
[wimlib] / src / write.c
index 42b9fcfff84e38f682d05b5406dcfa0b58d7118a..026d2f13cf45f98591d5b263ab0a67a29f0ed7d3 100644 (file)
@@ -2083,9 +2083,11 @@ close_wim_writable(WIMStruct *wim, int write_flags)
 }
 
 /*
+ * 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:
  *
@@ -2095,7 +2097,7 @@ close_wim_writable(WIMStruct *wim, int write_flags)
  *     (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
@@ -2118,6 +2120,9 @@ close_wim_writable(WIMStruct *wim, int write_flags)
  *             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
@@ -2158,7 +2163,7 @@ finish_write(WIMStruct *wim, int image, int write_flags,
                                             &wim->hdr.lookup_table_res_entry,
                                             stream_list_override);
                if (ret)
-                       goto out_close_wim;
+                       return ret;
        }
 
        /* Write XML data.  */
@@ -2169,8 +2174,9 @@ finish_write(WIMStruct *wim, int image, int write_flags,
                                 &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;
@@ -2180,7 +2186,7 @@ finish_write(WIMStruct *wim, int image, int write_flags,
                        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))
@@ -2194,36 +2200,43 @@ finish_write(WIMStruct *wim, int image, int write_flags,
                                            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)
@@ -2253,8 +2266,10 @@ lock_wim(WIMStruct *wim, int fd)
 #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
@@ -2263,7 +2278,7 @@ lock_wim(WIMStruct *wim, int fd)
  *
  * - 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
@@ -2285,10 +2300,13 @@ lock_wim(WIMStruct *wim, int fd)
  *   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,
@@ -2305,15 +2323,20 @@ lock_wim(WIMStruct *wim, int fd)
  *
  *   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
@@ -2597,7 +2620,7 @@ write_wim_part(WIMStruct *wim,
                           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;
 }
 
@@ -2751,7 +2774,7 @@ overwrite_wim_inplace(WIMStruct *wim, int write_flags,
        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);
 
@@ -2822,25 +2845,24 @@ overwrite_wim_inplace(WIMStruct *wim, int write_flags,
                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,
@@ -2861,15 +2883,25 @@ overwrite_wim_inplace(WIMStruct *wim, int write_flags,
        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;