Add WIMLIB_OPEN_FLAG_WRITE_ACCESS flag
authorEric Biggers <ebiggers3@gmail.com>
Wed, 22 May 2013 01:42:33 +0000 (20:42 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Wed, 22 May 2013 01:42:33 +0000 (20:42 -0500)
NEWS
include/wimlib.h
include/wimlib/wim.h
programs/imagex.c
src/header.c
src/split.c
src/util.c
src/wim.c
src/write.c

diff --git a/NEWS b/NEWS
index bc18a41..75b2b7d 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -24,6 +24,22 @@ Version 1.4.1:
        wimlib-imagex will now choose different units for progress messages,
        depending on the amount of data that needs to be processed.
 
+       `wimlib-imagex append' will now generate a unique WIM image name if no
+       name is specified and the defaulted name already exists in the WIM.
+
+       wimlib now allows you to create unnamed WIM images, which can then only
+       be referred to by index.
+
+       wimlib now allows you to explicitly declare you want write access to a
+       WIM by providing the WIMLIB_OPEN_FLAG_WRITE_ACCESS flag to
+       wimlib_open_wim().
+
+       Progress callbacks have been added to wimlib's wimlib_update_image()
+       function.
+
+       NTFS-3g capture now only warns about two conditions previously treated
+       as errors.
+
        Fixed a couple issues with using wimlib-imagex on UDF filesystems.
 
 Version 1.4.0:
index b7a21cf..9951e50 100644 (file)
@@ -805,12 +805,24 @@ struct wimlib_capture_config {
  * WIMLIB_OPEN_FLAG_*
  ******************************/
 
-/** Verify the WIM contents against the WIM's integrity table, if present. */
+/** Verify the WIM contents against the WIM's integrity table, if present.  This
+ * causes the raw data of the WIM file, divided into 10 MB chunks, to be
+ * checksummed and checked against the SHA1 message digests specified in the
+ * integrity table.  WIMLIB_ERR_INTEGRITY is returned if there are any
+ * mismatches.  */
 #define WIMLIB_OPEN_FLAG_CHECK_INTEGRITY               0x00000001
 
-/** Do not issue an error if the WIM is part of a split WIM. */
+/** Do not issue an error if the WIM is part of a split WIM.  */
 #define WIMLIB_OPEN_FLAG_SPLIT_OK                      0x00000002
 
+/** Check if the WIM is writable and return ::WIMLIB_ERR_WIM_IS_READONLY if it
+ * is not.  A WIM is considered writable only if it is writable at the
+ * filesystem level, does not have the WIM_HDR_FLAG_READONLY flag set in its
+ * header (note: wimlib currently never sets this flag), and is not part of a
+ * spanned set.  It is not required to provide this flag to make changes to the
+ * WIM, but with this flag you get the error sooner rather than later. */
+#define WIMLIB_OPEN_FLAG_WRITE_ACCESS                  0x00000004
+
 /******************************
  * WIMLIB_UNMOUNT_FLAG_*
  ******************************/
@@ -1022,7 +1034,7 @@ enum wimlib_error_code {
        WIMLIB_ERR_VOLUME_LACKS_FEATURES,
        WIMLIB_ERR_WRITE,
        WIMLIB_ERR_XML,
-       WIMLIB_ERR_WIM_IS_MARKED_READONLY,
+       WIMLIB_ERR_WIM_IS_READONLY,
 };
 
 
@@ -1050,12 +1062,14 @@ enum wimlib_error_code {
  *
  * @return 0 on success; nonzero on failure.  The possible error codes are:
  *
- * @retval ::WIMLIB_ERR_SPLIT_UNSUPPORTED
- *     @a wim is part of a split WIM.
  * @retval ::WIMLIB_ERR_IMAGE_NAME_COLLISION
  *     There is already an image in @a wim named @a name.
  * @retval ::WIMLIB_ERR_NOMEM
  *     Failed to allocate the memory needed to add the new image.
+ * @retval ::WIMLIB_ERR_WIM_IS_READONLY
+ *     The WIM file is considered read-only because of any of the reasons
+ *     mentioned in the documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS
+ *     flag.
  */
 extern int
 wimlib_add_empty_image(WIMStruct *wim,
@@ -1203,9 +1217,10 @@ wimlib_create_new_wim(int ctype, WIMStruct **wim_ret);
  *     Failed to allocate needed memory.
  * @retval ::WIMLIB_ERR_READ
  *     Could not read the metadata resource for @a image from the WIM.
- * @retval ::WIMLIB_ERR_SPLIT_UNSUPPORTED
- *     @a wim is part of a split WIM.  Deleting an image from a split WIM is
- *     unsupported.
+ * @retval ::WIMLIB_ERR_WIM_IS_READONLY
+ *     The WIM file is considered read-only because of any of the reasons
+ *     mentioned in the documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS
+ *     flag.
  */
 extern int
 wimlib_delete_image(WIMStruct *wim, int image);
@@ -1306,9 +1321,10 @@ wimlib_delete_image(WIMStruct *wim, int image);
  *     complete split WIM because they do not include all the parts of the
  *     original WIM, there are duplicate parts, or not all the parts have the
  *     same GUID and compression type.
- * @retval ::WIMLIB_ERR_SPLIT_UNSUPPORTED
- *     @a dest_wim is part of a split WIM.  Exporting an image to a split WIM
- *     is unsupported.
+ * @retval ::WIMLIB_ERR_WIM_IS_READONLY
+ *     @a dest_wim is considered read-only because of any of the reasons
+ *     mentioned in the documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS
+ *     flag.
  */
 extern int
 wimlib_export_image(WIMStruct *src_wim, int src_image,
@@ -1758,7 +1774,7 @@ wimlib_image_name_in_use(const WIMStruct *wim, const wimlib_tchar *name);
  *
  * @return 0 on success; nonzero on error.  This function may return any value
  * returned by wimlib_open_wim() and wimlib_write() except
- * ::WIMLIB_ERR_SPLIT_UNSUPPORTED, as well as the following error code:
+ * ::WIMLIB_ERR_WIM_IS_READONLY, as well as the following error code:
  *
  * @retval ::WIMLIB_ERR_SPLIT_INVALID
  *     The split WIMs do not form a valid WIM because they do not include all
@@ -1926,9 +1942,10 @@ wimlib_lzx_decompress(const void *compressed_data, unsigned compressed_len,
  *     split WIM because they do not include all the parts of the original WIM,
  *     there are duplicate parts, or not all the parts have the same GUID and
  *     compression type.
- * @retval ::WIMLIB_ERR_SPLIT_UNSUPPORTED
- *     The WIM is a split WIM and a read-write mount was requested.  We only
- *     support mounting a split WIM read-only.
+ * @retval ::WIMLIB_ERR_WIM_IS_READONLY
+ *     ::WIMLIB_MOUNT_FLAG_READWRITE was specified in @a mount_flags, but @a
+ *     wim is considered read-only because of any of the reasons mentioned in
+ *     the documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS flag.
  * @retval ::WIMLIB_ERR_UNSUPPORTED
  *     Mounting is not supported, either because the platform is Windows, or
  *     because the platform is UNIX and wimlib was compiled with @c
@@ -1950,18 +1967,7 @@ wimlib_mount_image(WIMStruct *wim,
  *     The path to the WIM file to open.
  * @param open_flags
  *     Bitwise OR of flags ::WIMLIB_OPEN_FLAG_CHECK_INTEGRITY and/or
- *     ::WIMLIB_OPEN_FLAG_SPLIT_OK.
- *     <br/> <br/>
- *     If ::WIMLIB_OPEN_FLAG_CHECK_INTEGRITY is given, the integrity table of
- *     the WIM, if it exists, is checked, and this function will fail with an
- *     ::WIMLIB_ERR_INTEGRITY status if any of the computed SHA1 message
- *     digests of the WIM do not exactly match the corresponding message
- *     digests given in the integrity table.
- *     <br/> <br/>
- *     If ::WIMLIB_OPEN_FLAG_SPLIT_OK is given, no error will be issued if the
- *     WIM is part of a split WIM; otherwise ::WIMLIB_ERR_SPLIT_UNSUPPORTED is
- *     returned.  (This flag may be removed in the future, in which case no
- *     error will be issued when opening a split WIM.)
+ *     ::WIMLIB_OPEN_FLAG_SPLIT_OK and/or ::WIMLIB_OPEN_FLAG_WRITE_ACCESS.
  *
  * @param progress_func
  *     If non-NULL, a function that will be called periodically with the
@@ -2018,6 +2024,10 @@ wimlib_mount_image(WIMStruct *wim,
  * @retval ::WIMLIB_ERR_UNKNOWN_VERSION
  *     A number other than 0x10d00 is written in the version field of the WIM
  *     header of @a wim_file.  (Probably a pre-Vista WIM).
+ * @retval ::WIMLIB_ERR_WIM_IS_READONLY
+ *     ::WIMLIB_OPEN_FLAG_WRITE_ACCESS was specified and but the WIM file was
+ *     considered read-only because of any of the reasons mentioned in the
+ *     documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS flag.
  * @retval ::WIMLIB_ERR_XML
  *     The XML data for @a wim_file is invalid.
  */
@@ -2086,7 +2096,7 @@ wimlib_open_wim(const wimlib_tchar *wim_file,
  * @return 0 on success; nonzero on error.  This function may return any value
  * returned by wimlib_write() as well as the following error codes:
  * @retval ::WIMLIB_ERR_ALREADY_LOCKED
- *     The WIM was going to be modifien in-place (with no temporary file), but
+ *     The WIM was going to be modified in-place (with no temporary file), but
  *     an exclusive advisory lock on the on-disk WIM file could not be acquired
  *     because another thread or process has mounted an image from the WIM
  *     read-write or is currently modifying the WIM in-place.
@@ -2096,6 +2106,10 @@ wimlib_open_wim(const wimlib_tchar *wim_file,
  * @retval ::WIMLIB_ERR_RENAME
  *     The temporary file that the WIM was written to could not be renamed to
  *     the original filename of @a wim.
+ * @retval ::WIMLIB_ERR_WIM_IS_READONLY
+ *     The WIM file is considered read-only because of any of the reasons
+ *     mentioned in the documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS
+ *     flag.
  */
 extern int
 wimlib_overwrite(WIMStruct *wim, int write_flags, unsigned num_threads,
@@ -2275,8 +2289,9 @@ wimlib_resolve_image(WIMStruct *wim,
  * @retval ::WIMLIB_ERR_INVALID_IMAGE
  *     @a boot_idx does not specify an existing image in @a wim, and it was not
  *     0.
- * @retval ::WIMLIB_ERR_SPLIT_UNSUPPORTED
- *     @a wim is part of a split WIM.
+ * @retval ::WIMLIB_ERR_WIM_IS_READONLY
+ *     @a wim is considered read-only because of any of the reasons mentioned
+ *     in the documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS flag.
  */
 extern int
 wimlib_set_boot_idx(WIMStruct *wim, int boot_idx);
@@ -2300,8 +2315,9 @@ wimlib_set_boot_idx(WIMStruct *wim, int boot_idx);
  * @retval ::WIMLIB_ERR_NOMEM
  *     Failed to allocate the memory needed to duplicate the @a description
  *     string.
- * @retval ::WIMLIB_ERR_SPLIT_UNSUPPORTED
- *     @a wim is part of a split WIM.
+ * @retval ::WIMLIB_ERR_WIM_IS_READONLY
+ *     @a wim is considered read-only because of any of the reasons mentioned
+ *     in the documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS flag.
  */
 extern int
 wimlib_set_image_descripton(WIMStruct *wim, int image,
@@ -2326,8 +2342,9 @@ wimlib_set_image_descripton(WIMStruct *wim, int image,
  *     @a image does not specify a single existing image in @a wim.
  * @retval ::WIMLIB_ERR_NOMEM
  *     Failed to allocate the memory needed to duplicate the @a flags string.
- * @retval ::WIMLIB_ERR_SPLIT_UNSUPPORTED
- *     @a wim is part of a split WIM.
+ * @retval ::WIMLIB_ERR_WIM_IS_READONLY
+ *     @a wim is considered read-only because of any of the reasons mentioned
+ *     in the documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS flag.
  */
 extern int wimlib_set_image_flags(WIMStruct *wim, int image,
                                  const wimlib_tchar *flags);
@@ -2353,8 +2370,9 @@ extern int wimlib_set_image_flags(WIMStruct *wim, int image,
  *     @a image does not specify a single existing image in @a wim.
  * @retval ::WIMLIB_ERR_NOMEM
  *     Failed to allocate the memory needed to duplicate the @a name string.
- * @retval ::WIMLIB_ERR_SPLIT_UNSUPPORTED
- *     @a wim is part of a split WIM.
+ * @retval ::WIMLIB_ERR_WIM_IS_READONLY
+ *     @a wim is considered read-only because of any of the reasons mentioned
+ *     in the documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS flag.
  */
 extern int wimlib_set_image_name(WIMStruct *wim, int image,
                                 const wimlib_tchar *name);
@@ -2623,6 +2641,10 @@ wimlib_unmount_image(const wimlib_tchar *dir,
  *     or, the platform is Windows and either the ::WIMLIB_ADD_FLAG_UNIX_DATA
  *     or the ::WIMLIB_ADD_FLAG_DEREFERENCE flags were specified in the @a
  *     add_flags for an update command.
+ * @retval ::WIMLIB_ERR_WIM_IS_READONLY
+ *     The WIM file is considered read-only because of any of the reasons
+ *     mentioned in the documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS
+ *     flag.
  */
 extern int
 wimlib_update_image(WIMStruct *wim,
index c1e669f..9d01522 100644 (file)
@@ -55,8 +55,7 @@ extern int
 wim_run_full_verifications(WIMStruct *w);
 
 extern int
-read_header(const tchar *filename, int in_fd, struct wim_header *hdr,
-           int split_ok);
+read_header(const tchar *filename, int in_fd, struct wim_header *hdr);
 
 extern int
 write_header(const struct wim_header *hdr, int out_fd);
index 5d7aa23..ebbb650 100644 (file)
@@ -980,18 +980,6 @@ stdin_get_text_contents(size_t *num_tchars_ret)
        return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
 }
 
-/* Return 0 if a path names a file to which the current user has write access;
- * -1 otherwise (and print an error message). */
-static int
-file_writable(const tchar *path)
-{
-       int ret;
-       ret = taccess(path, W_OK);
-       if (ret != 0)
-               imagex_error_with_errno(T("Can't modify \"%"TS"\""), path);
-       return ret;
-}
-
 #define TO_PERCENT(numerator, denominator) \
        (((denominator) == 0) ? 0 : ((numerator) * 100 / (denominator)))
 
@@ -1656,7 +1644,7 @@ static int
 imagex_capture_or_append(int argc, tchar **argv)
 {
        int c;
-       int open_flags = 0;
+       int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
        int add_image_flags = WIMLIB_ADD_IMAGE_FLAG_EXCLUDE_VERBOSE;
        int write_flags = 0;
        int compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
@@ -1889,8 +1877,6 @@ imagex_capture_or_append(int argc, tchar **argv)
                ret = wimlib_write(w, wimfile, WIMLIB_ALL_IMAGES, write_flags,
                                   num_threads, imagex_progress_func);
        }
-       if (ret == WIMLIB_ERR_REOPEN)
-               ret = 0;
        if (ret != 0)
                imagex_error(T("Failed to write the WIM file \"%"TS"\""),
                             wimfile);
@@ -1919,7 +1905,7 @@ static int
 imagex_delete(int argc, tchar **argv)
 {
        int c;
-       int open_flags = 0;
+       int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
        int write_flags = 0;
        const tchar *wimfile;
        const tchar *image_num_or_name;
@@ -1955,10 +1941,6 @@ imagex_delete(int argc, tchar **argv)
        wimfile = argv[0];
        image_num_or_name = argv[1];
 
-       ret = file_writable(wimfile);
-       if (ret != 0)
-               return ret;
-
        ret = wimlib_open_wim(wimfile, open_flags, &w,
                              imagex_progress_func);
        if (ret != 0)
@@ -1977,8 +1959,6 @@ imagex_delete(int argc, tchar **argv)
        }
 
        ret = wimlib_overwrite(w, write_flags, 0, imagex_progress_func);
-       if (ret == WIMLIB_ERR_REOPEN)
-               ret = 0;
        if (ret != 0) {
                imagex_error(T("Failed to write the file \"%"TS"\" with image "
                               "deleted"), wimfile);
@@ -2130,12 +2110,8 @@ imagex_export(int argc, tchar **argv)
                        ret = -1;
                        goto out;
                }
-               ret = wimlib_open_wim(dest_wimfile, open_flags, &dest_w,
-                                     imagex_progress_func);
-               if (ret != 0)
-                       goto out;
-
-               ret = file_writable(dest_wimfile);
+               ret = wimlib_open_wim(dest_wimfile, open_flags | WIMLIB_OPEN_FLAG_WRITE_ACCESS,
+                                     &dest_w, imagex_progress_func);
                if (ret != 0)
                        goto out;
 
@@ -2194,8 +2170,6 @@ imagex_export(int argc, tchar **argv)
                ret = wimlib_overwrite(dest_w, write_flags, num_threads,
                                       imagex_progress_func);
 out:
-       if (ret == WIMLIB_ERR_REOPEN)
-               ret = 0;
        wimlib_free(src_w);
        wimlib_free(dest_w);
        if (additional_swms) {
@@ -2591,11 +2565,7 @@ imagex_info(int argc, tchar **argv)
        } else {
 
                /* Modification operations */
-               if (total_parts != 1) {
-                       imagex_error(T("Modifying a split WIM is not supported."));
-                       ret = -1;
-                       goto out;
-               }
+
                if (image == WIMLIB_ALL_IMAGES)
                        image = 1;
 
@@ -2653,21 +2623,12 @@ imagex_info(int argc, tchar **argv)
                if (boot || new_name || new_desc ||
                    (check && !wimlib_has_integrity_table(w)))
                {
-                       int write_flags;
-
-                       ret = file_writable(wimfile);
-                       if (ret != 0)
-                               goto out;
+                       int write_flags = 0;
 
                        if (check)
-                               write_flags = WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
-                       else
-                               write_flags = 0;
-
+                               write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
                        ret = wimlib_overwrite(w, write_flags, 1,
                                               imagex_progress_func);
-                       if (ret == WIMLIB_ERR_REOPEN)
-                               ret = 0;
                } else {
                        tprintf(T("The file \"%"TS"\" was not modified because nothing "
                                  "needed to be done.\n"), wimfile);
@@ -2736,8 +2697,10 @@ imagex_mount_rw_or_ro(int argc, tchar **argv)
        unsigned num_additional_swms = 0;
        const tchar *staging_dir = NULL;
 
-       if (!tstrcmp(argv[0], T("mountrw")))
+       if (!tstrcmp(argv[0], T("mountrw"))) {
                mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE;
+               open_flags |= WIMLIB_OPEN_FLAG_WRITE_ACCESS;
+       }
 
        for_opt(c, mount_options) {
                switch (c) {
@@ -2816,12 +2779,6 @@ imagex_mount_rw_or_ro(int argc, tchar **argv)
                        goto out;
        }
 
-       if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
-               ret = file_writable(wimfile);
-               if (ret != 0)
-                       goto out;
-       }
-
        ret = wimlib_mount_image(w, image, dir, mount_flags, additional_swms,
                                 num_additional_swms, staging_dir);
        if (ret != 0) {
@@ -2849,7 +2806,7 @@ static int
 imagex_optimize(int argc, tchar **argv)
 {
        int c;
-       int open_flags = 0;
+       int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
        int write_flags = WIMLIB_WRITE_FLAG_REBUILD;
        int ret;
        WIMStruct *w;
@@ -2887,10 +2844,6 @@ imagex_optimize(int argc, tchar **argv)
 
        wimfile = argv[0];
 
-       ret = file_writable(wimfile);
-       if (ret)
-               return ret;
-
        ret = wimlib_open_wim(wimfile, open_flags, &w,
                              imagex_progress_func);
        if (ret)
@@ -3024,7 +2977,7 @@ imagex_update(int argc, tchar **argv)
        int image;
        WIMStruct *wim;
        int ret;
-       int open_flags = 0;
+       int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
        int write_flags = 0;
        int update_flags = WIMLIB_UPDATE_FLAG_SEND_PROGRESS;
        int default_add_flags = WIMLIB_ADD_IMAGE_FLAG_EXCLUDE_VERBOSE;
@@ -3115,10 +3068,6 @@ imagex_update(int argc, tchar **argv)
                goto out_usage;
        wimfile = argv[0];
 
-       ret = file_writable(wimfile);
-       if (ret)
-               goto out;
-
        ret = wimlib_open_wim(wimfile, open_flags, &wim, imagex_progress_func);
        if (ret)
                goto out;
@@ -3188,11 +3137,13 @@ imagex_update(int argc, tchar **argv)
        }
 
        /* Set default flags and capture config on the update commands */
+       bool have_add_command = false;
        for (size_t i = 0; i < num_cmds; i++) {
                switch (cmds[i].op) {
                case WIMLIB_UPDATE_OP_ADD:
                        cmds[i].add.add_flags |= default_add_flags;
                        cmds[i].add.config = config;
+                       have_add_command = true;
                        break;
                case WIMLIB_UPDATE_OP_DELETE:
                        cmds[i].delete.delete_flags |= default_delete_flags;
@@ -3203,7 +3154,8 @@ imagex_update(int argc, tchar **argv)
        }
 
 #ifdef __WIN32__
-       win32_acquire_capture_privileges();
+       if (have_add_command)
+               win32_acquire_capture_privileges();
 #endif
 
        /* Execute the update commands */
@@ -3217,7 +3169,8 @@ imagex_update(int argc, tchar **argv)
                               imagex_progress_func);
 out_release_privs:
 #ifdef __WIN32__
-       win32_release_capture_privileges();
+       if (have_add_command)
+               win32_release_capture_privileges();
 #endif
        free(cmds);
 out_free_cmd_file_contents:
index 5b16416..332d350 100644 (file)
@@ -115,8 +115,7 @@ struct wim_header_disk {
 
 /* Reads the header from a WIM file.  */
 int
-read_header(const tchar *filename, int in_fd,
-           struct wim_header *hdr, int open_flags)
+read_header(const tchar *filename, int in_fd, struct wim_header *hdr)
 {
        struct wim_header_disk disk_hdr _aligned_attribute(8);
 
@@ -170,13 +169,6 @@ read_header(const tchar *filename, int in_fd,
                return WIMLIB_ERR_INVALID_PART_NUMBER;
        }
 
-       if (!(open_flags & WIMLIB_OPEN_FLAG_SPLIT_OK) && hdr->total_parts != 1)
-       {
-               ERROR("\"%"TS"\": This WIM is part %u of a %u-part WIM",
-                     filename, hdr->part_number, hdr->total_parts);
-               return WIMLIB_ERR_SPLIT_UNSUPPORTED;
-       }
-
        hdr->image_count = le32_to_cpu(disk_hdr.image_count);
 
        DEBUG("part_number = %u, total_parts = %u, image_count = %u",
index 78dc8ab..2636164 100644 (file)
@@ -143,10 +143,6 @@ wimlib_split(WIMStruct *w, const tchar *swm_name,
 
        write_flags &= WIMLIB_WRITE_MASK_PUBLIC;
 
-       ret = wim_checksum_unhashed_streams(w);
-       if (ret)
-               return ret;
-
        swm_name_len = tstrlen(swm_name);
        tchar swm_base_name[swm_name_len + 20];
 
index 87dd3c5..2aca3da 100644 (file)
@@ -389,8 +389,8 @@ static const tchar *error_strings[] = {
                = T("The requested operation is unsupported"),
        [WIMLIB_ERR_VOLUME_LACKS_FEATURES]
                = T("The volume did not support a feature necessary to complete the operation"),
-       [WIMLIB_ERR_WIM_IS_MARKED_READONLY]
-               = T("The WIM is marked as read-only"),
+       [WIMLIB_ERR_WIM_IS_READONLY]
+               = T("The WIM is read-only (file permissions, header flag, or split WIM)"),
        [WIMLIB_ERR_WRITE]
                = T("Failed to write data to a file"),
        [WIMLIB_ERR_XML]
index 4a8e6a8..7927af3 100644 (file)
--- a/src/wim.c
+++ b/src/wim.c
@@ -463,10 +463,22 @@ begin_read(WIMStruct *w, const tchar *in_wim_path, int open_flags,
                        return WIMLIB_ERR_OPEN;
        }
 
-       ret = read_header(w->filename, w->in_fd, &w->hdr, open_flags);
+       ret = read_header(w->filename, w->in_fd, &w->hdr);
        if (ret)
                return ret;
 
+       if (open_flags & WIMLIB_OPEN_FLAG_WRITE_ACCESS) {
+               ret = can_modify_wim(w);
+               if (ret)
+                       return ret;
+       }
+
+       if (w->hdr.total_parts != 1 && !(open_flags & WIMLIB_OPEN_FLAG_SPLIT_OK)) {
+               ERROR("\"%"TS"\": This WIM is part %u of a %u-part WIM",
+                     w->filename, w->hdr.part_number, w->hdr.total_parts);
+               return WIMLIB_ERR_SPLIT_UNSUPPORTED;
+       }
+
        DEBUG("According to header, WIM contains %u images", w->hdr.image_count);
 
        /* If the boot index is invalid, print a warning and set it to 0 */
@@ -528,24 +540,31 @@ begin_read(WIMStruct *w, const tchar *in_wim_path, int open_flags,
  */
 WIMLIBAPI int
 wimlib_open_wim(const tchar *wim_file, int open_flags,
-               WIMStruct **w_ret,
+               WIMStruct **wim_ret,
                wimlib_progress_func_t progress_func)
 {
-       WIMStruct *w;
+       WIMStruct *wim;
        int ret;
 
-       if (!wim_file || !w_ret)
-               return WIMLIB_ERR_INVALID_PARAM;
+       ret = WIMLIB_ERR_INVALID_PARAM;
+       if (!wim_file || !wim_ret)
+               goto out;
 
-       w = new_wim_struct();
-       if (!w)
-               return WIMLIB_ERR_NOMEM;
+       ret = WIMLIB_ERR_NOMEM;
+       wim = new_wim_struct();
+       if (!wim)
+               goto out;
 
-       ret = begin_read(w, wim_file, open_flags, progress_func);
-       if (ret == 0)
-               *w_ret = w;
-       else
-               wimlib_free(w);
+       ret = begin_read(wim, wim_file, open_flags, progress_func);
+       if (ret)
+               goto out_wimlib_free;
+
+       ret = 0;
+       *wim_ret = wim;
+       goto out;
+out_wimlib_free:
+       wimlib_free(wim);
+out:
        return ret;
 }
 
@@ -672,22 +691,49 @@ wim_checksum_unhashed_streams(WIMStruct *w)
        return 0;
 }
 
+/*
+ * can_modify_wim - Check if a given WIM is writeable.  This is only the case if
+ * it meets the following three conditions:
+ *
+ * 1. Write access is allowed to the underlying file (if any) at the filesystem level.
+ * 2. The WIM is not part of a spanned set.
+ * 3. The WIM_HDR_FLAG_READONLY flag is not set in the WIM header.
+ *
+ * Return value is 0 if writable; WIMLIB_ERR_WIM_IS_READONLY otherwise.
+ */
 int
 can_modify_wim(WIMStruct *wim)
 {
+       if (wim->filename) {
+               if (taccess(wim->filename, W_OK)) {
+                       ERROR_WITH_ERRNO("Can't modify \"%"TS"\"", wim->filename);
+                       return WIMLIB_ERR_WIM_IS_READONLY;
+               }
+       }
        if (wim->hdr.total_parts != 1) {
                ERROR("Cannot modify \"%"TS"\": is part of a spanned set",
                      wim->filename);
-               return WIMLIB_ERR_SPLIT_UNSUPPORTED;
+               return WIMLIB_ERR_WIM_IS_READONLY;
        }
        if (wim->hdr.flags & WIM_HDR_FLAG_READONLY) {
                ERROR("Cannot modify \"%"TS"\": is marked read-only",
                      wim->filename);
-               return WIMLIB_ERR_WIM_IS_MARKED_READONLY;
+               return WIMLIB_ERR_WIM_IS_READONLY;
        }
        return 0;
 }
 
+/*
+ * can_delete_from_wim - Check if files or images can be deleted from a given
+ * WIM file.
+ *
+ * This theoretically should be exactly the same as can_modify_wim(), but
+ * unfortunately, due to bugs in Microsoft's software that generate incorrect
+ * reference counts for some WIM resources, we need to run expensive
+ * verifications to make sure the reference counts are correct on all WIM
+ * resources.  Otherwise we might delete a WIM resource whose reference count
+ * has fallen to 0, but is actually still referenced somewhere.
+ */
 int
 can_delete_from_wim(WIMStruct *wim)
 {
index 90dcaed..2effd7d 100644 (file)
@@ -2212,15 +2212,16 @@ wimlib_overwrite(WIMStruct *w, int write_flags,
                 unsigned num_threads,
                 wimlib_progress_func_t progress_func)
 {
+       int ret;
+
        write_flags &= WIMLIB_WRITE_MASK_PUBLIC;
 
        if (!w->filename)
                return WIMLIB_ERR_NO_FILENAME;
 
-       if (w->hdr.total_parts != 1) {
-               ERROR("Cannot modify a split WIM");
-               return WIMLIB_ERR_SPLIT_UNSUPPORTED;
-       }
+       ret = can_modify_wim(w);
+       if (ret)
+               return ret;
 
        if ((!w->deletion_occurred || (write_flags & WIMLIB_WRITE_FLAG_SOFT_DELETE))
            && !(write_flags & WIMLIB_WRITE_FLAG_REBUILD))