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:
* 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_*
******************************/
WIMLIB_ERR_VOLUME_LACKS_FEATURES,
WIMLIB_ERR_WRITE,
WIMLIB_ERR_XML,
- WIMLIB_ERR_WIM_IS_MARKED_READONLY,
+ WIMLIB_ERR_WIM_IS_READONLY,
};
*
* @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,
* 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);
* 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,
*
* @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
* 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
* 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
* @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.
*/
* @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.
* @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,
* @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);
* @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,
* @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);
* @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);
* 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,
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);
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)))
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;
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);
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;
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)
}
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);
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;
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) {
} 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;
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);
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) {
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) {
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;
wimfile = argv[0];
- ret = file_writable(wimfile);
- if (ret)
- return ret;
-
ret = wimlib_open_wim(wimfile, open_flags, &w,
imagex_progress_func);
if (ret)
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;
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;
}
/* 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;
}
#ifdef __WIN32__
- win32_acquire_capture_privileges();
+ if (have_add_command)
+ win32_acquire_capture_privileges();
#endif
/* Execute the update commands */
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:
/* 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);
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",
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];
= 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]
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 */
*/
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;
}
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)
{
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))