From 239adf84c95212272cef9a1d281bd5b8723ee86d Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 8 Dec 2013 03:10:25 -0600 Subject: [PATCH] Allow changing WIM compression type --- NEWS | 5 +++++ doc/imagex-optimize.1.in | 4 ++++ doc/imagex.1.in | 14 +++++++------- include/wimlib.h | 21 +++++++++++++++++++++ include/wimlib/wim.h | 9 +++++++++ programs/imagex.c | 34 ++++++++++++++++++++++++++++------ src/header.c | 31 ++++++++++++++++++------------- src/lzx-compress.c | 1 + src/metadata_resource.c | 2 +- src/wim.c | 2 ++ src/write.c | 27 ++++++++++++++++++++++++--- 11 files changed, 120 insertions(+), 30 deletions(-) diff --git a/NEWS b/NEWS index 435a3f70..5dc38020 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,11 @@ Version 1.5.3: produce an even better compression ratio at the cost of more time spent compressing. + `wimlib-imagex optimize' now supports the '--compress=TYPE' option, + which recompresses the WIM file using the specified compression TYPE. + The new library API function used for this is + wimlib_set_output_compression_type(). + Added the wimlib_get_xml_data() function to allow library clients to easily retrieve the raw XML data from a WIM file if needed. diff --git a/doc/imagex-optimize.1.in b/doc/imagex-optimize.1.in index 9f3856af..0009d92b 100644 --- a/doc/imagex-optimize.1.in +++ b/doc/imagex-optimize.1.in @@ -42,6 +42,10 @@ compression ratio. This only affects LZX-compressed WIM files. Compared to the default \fB--recompress\fR, this will make compression about twice as slow and will increase the compression ratio by maybe 1%, depending on the data. .TP +\fB--compress\fR=\fITYPE\fR +Recompress the WIM file using the specified compression type. \fITYPE\fR may be +"none", "fast", or "maximum". This implies \fB--recompress\fR. +.TP \fB--threads\fR=\fINUM_THREADS\fR Number of threads to use for compressing data. Default: autodetect (number of processors). This parameter is only meaningful when \fB--recompress\fR is also diff --git a/doc/imagex.1.in b/doc/imagex.1.in index 8ada205c..29508753 100644 --- a/doc/imagex.1.in +++ b/doc/imagex.1.in @@ -97,14 +97,17 @@ Combine split WIMs into one standalone WIM (\fB@IMAGEX_PROGNAME@ join\fR) .IP \[bu] Split a standalone WIM into multiple parts (\fB@IMAGEX_PROGNAME@ split\fR) .IP \[bu] +Easily remove wasted space in a WIM file and optionally recompress it (\fB +@IMAGEX_PROGNAME@ optimize\fR) +.IP \[bu] Support for all WIM compression types, both compression and decompression (LZX, XPRESS, and none) .IP \[bu] WIM integrity table is supported (\fB--check\fR option to many commands) .SH DIFFERENCES FROM MICROSOFT IMAGEX Although \fB@IMAGEX_PROGNAME@\fR shares some similarities with Microsoft's -implementation of ImageX, this section lists some noteworthy differences between -the two programs: +implementation of ImageX, this section lists some of the many noteworthy +differences between the two programs: .IP \[bu] 4 \fB@IMAGEX_PROGNAME@\fR is supported on both UNIX-like systems and Windows; thus, some functionality was designed around this. @@ -131,7 +134,8 @@ files from or make changes to a WIM image without mounting it. .IP \[bu] \fB@IMAGEX_PROGNAME@\fR offers the extra command \fB@IMAGEX_PROGNAME@ optimize\fR, which lets you easily remove wasted space in a WIM (which can arise -after a WIM image is appended or mounted read-write). +after a WIM image is appended or mounted read-write). It also makes it easy to +recompress a WIM file at the highest compression level. .IP \[bu] \fB@IMAGEX_PROGNAME@\fR also offers the command \fB@IMAGEX_PROGNAME@ join\fR, which lets you easily join the parts of a split WIM. @@ -165,10 +169,6 @@ wimlib (and \fB@IMAGEX_PROGNAME@\fR via \fB@IMAGEX_PROGNAME@ mount\fR) support mounting an image from a split WIM, but Microsoft's software does not. (Note: this functionality is not available in Windows builds of wimlib and \fB@IMAGEX_PROGNAME@\fR.) -.IP \[bu] -\fB@IMAGEX_PROGNAME@ capture\fR supports a special mode where UNIX file modes, -owners, and groups are stored. (Note: this functionality is only available in -builds of wimlib for UNIX-like systems.) .SH LOCALES AND CHARACTER ENCODINGS On Windows, wimlib works in UTF-16LE, and there should be no problems with character encodings. diff --git a/include/wimlib.h b/include/wimlib.h index 692ef773..f4b0dc5a 100644 --- a/include/wimlib.h +++ b/include/wimlib.h @@ -3356,6 +3356,27 @@ extern int wimlib_set_image_descripton(WIMStruct *wim, int image, const wimlib_tchar *description); +/** + * @ingroup G_writing_and_overwriting_wims + * + * Set the compression type of a WIM to use in subsequent calls to + * wimlib_write() or wimlib_overwrite(). + * + * @return 0 on success; nonzero on error. + * + * @param wim + * ::WIMStruct for a WIM. + * @param ctype + * The compression type to set (one of ::wimlib_compression_type). + * + * @return 0 on success; nonzero on error. + * + * @retval ::WIMLIB_ERR_INVALID_PARAM + * @p ctype did not specify a valid compression type. + */ +extern int +wimlib_set_output_compression_type(WIMStruct *wim, int ctype); + /** * @ingroup G_modifying_wims * diff --git a/include/wimlib/wim.h b/include/wimlib/wim.h index d1e09713..b6cf84a7 100644 --- a/include/wimlib/wim.h +++ b/include/wimlib/wim.h @@ -70,6 +70,12 @@ struct WIMStruct { /* One of WIMLIB_COMPRESSION_TYPE_*, cached from the header flags. */ u8 compression_type : 2; + + /* Overwritten compression type for wimlib_overwrite() or + * wimlib_write(). Can be changed by + * wimlib_set_output_compression_type(); otherwise is the same as + * compression_type. */ + u8 out_compression_type : 2; }; static inline bool wim_is_pipable(const WIMStruct *wim) @@ -90,6 +96,9 @@ static inline bool wim_has_metadata(const WIMStruct *wim) extern void wim_recalculate_refcnts(WIMStruct *wim); +extern u32 +get_wim_hdr_cflags(int ctype); + extern int init_wim_header(struct wim_header *hdr, int ctype); diff --git a/programs/imagex.c b/programs/imagex.c index 67903256..2564b4c7 100644 --- a/programs/imagex.c +++ b/programs/imagex.c @@ -282,6 +282,7 @@ static const struct option optimize_options[] = { {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION}, {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION}, {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION}, + {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION}, {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION}, {T("compress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION}, {T("recompress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION}, @@ -419,9 +420,10 @@ get_compression_type(const tchar *optarg) } } -static void +static int set_compress_slow(void) { + int ret; static const struct wimlib_lzx_params slow_params = { .size_of_this = sizeof(struct wimlib_lzx_params), .algorithm = WIMLIB_LZX_ALGORITHM_SLOW, @@ -438,8 +440,10 @@ set_compress_slow(void) }, }, }; - if (wimlib_lzx_set_default_params(&slow_params)) + ret = wimlib_lzx_set_default_params(&slow_params); + if (ret) imagex_error(T("Couldn't set slow compression parameters.!")); + return ret; } struct string_set { @@ -1722,7 +1726,10 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd) goto out_err; break; case IMAGEX_COMPRESS_SLOW_OPTION: - set_compress_slow(); + ret = set_compress_slow(); + if (ret) + goto out_err; + compression_type = WIMLIB_COMPRESSION_TYPE_LZX; break; case IMAGEX_FLAGS_OPTION: flags_element = optarg; @@ -3240,6 +3247,7 @@ imagex_optimize(int argc, tchar **argv, int cmd) int c; int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS; int write_flags = WIMLIB_WRITE_FLAG_REBUILD; + int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID; int ret; WIMStruct *wim; const tchar *wimfile; @@ -3256,12 +3264,20 @@ imagex_optimize(int argc, tchar **argv, int cmd) case IMAGEX_NOCHECK_OPTION: write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY; break; + case IMAGEX_COMPRESS_OPTION: + write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS; + compression_type = get_compression_type(optarg); + if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) + goto out_err; + break; case IMAGEX_RECOMPRESS_OPTION: write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS; break; case IMAGEX_COMPRESS_SLOW_OPTION: write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS; - set_compress_slow(); + ret = set_compress_slow(); + if (ret) + goto out_err; break; case IMAGEX_THREADS_OPTION: num_threads = parse_num_threads(optarg); @@ -3290,6 +3306,12 @@ imagex_optimize(int argc, tchar **argv, int cmd) if (ret) goto out; + if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) { + ret = wimlib_set_output_compression_type(wim, compression_type); + if (ret) + goto out_wimlib_free; + } + old_size = file_get_size(wimfile); tprintf(T("\"%"TS"\" original size: "), wimfile); if (old_size == -1) @@ -3754,8 +3776,8 @@ T( [CMD_OPTIMIZE] = T( " %"TS" WIMFILE [--check] [--nocheck] [--recompress]\n" -" [--recompress-slow] [--threads=NUM_THREADS] [--pipable]\n" -" [--not-pipable]\n" +" [--recompress-slow] [--compress=TYPE]\n" +" [--threads=NUM_THREADS] [--pipable] [--not-pipable]\n" ), [CMD_SPLIT] = T( diff --git a/src/header.c b/src/header.c index ad7f3ce0..9492e7f2 100644 --- a/src/header.c +++ b/src/header.c @@ -229,6 +229,22 @@ write_wim_header_flags(u32 hdr_flags, struct filedes *out_fd) offsetof(struct wim_header_disk, wim_flags)); } +u32 +get_wim_hdr_cflags(int ctype) +{ + switch (ctype) { + case WIMLIB_COMPRESSION_TYPE_NONE: + return 0; + case WIMLIB_COMPRESSION_TYPE_LZX: + return WIM_HDR_FLAG_COMPRESSION | WIM_HDR_FLAG_COMPRESS_LZX; + case WIMLIB_COMPRESSION_TYPE_XPRESS: + return WIM_HDR_FLAG_COMPRESSION | WIM_HDR_FLAG_COMPRESS_XPRESS; + case WIMLIB_COMPRESSION_TYPE_INVALID: + break; + } + return (u32)~0U; +} + /* * Initializes the header for a WIM file. */ @@ -236,19 +252,8 @@ int init_wim_header(struct wim_header *hdr, int ctype) { memset(hdr, 0, sizeof(struct wim_header)); - switch (ctype) { - case WIMLIB_COMPRESSION_TYPE_NONE: - hdr->flags = 0; - break; - case WIMLIB_COMPRESSION_TYPE_LZX: - hdr->flags = WIM_HDR_FLAG_COMPRESSION | - WIM_HDR_FLAG_COMPRESS_LZX; - break; - case WIMLIB_COMPRESSION_TYPE_XPRESS: - hdr->flags = WIM_HDR_FLAG_COMPRESSION | - WIM_HDR_FLAG_COMPRESS_XPRESS; - break; - default: + hdr->flags = get_wim_hdr_cflags(ctype); + if (hdr->flags == (u32)~0U) { ERROR("Invalid compression type specified (%d)", ctype); return WIMLIB_ERR_INVALID_COMPRESSION_TYPE; } diff --git a/src/lzx-compress.c b/src/lzx-compress.c index 20a35802..4c0e6ce1 100644 --- a/src/lzx-compress.c +++ b/src/lzx-compress.c @@ -2388,6 +2388,7 @@ lzx_params_valid(const struct wimlib_lzx_params *params) return true; } +/* API function documented in wimlib.h */ WIMLIBAPI int wimlib_lzx_set_default_params(const struct wimlib_lzx_params * params) { diff --git a/src/metadata_resource.c b/src/metadata_resource.c index 006871c6..d69ad82c 100644 --- a/src/metadata_resource.c +++ b/src/metadata_resource.c @@ -296,7 +296,7 @@ write_metadata_resource(WIMStruct *wim, int image, int write_resource_flags) * the metadata resource. */ ret = write_wim_resource_from_buffer(buf, len, WIM_RESHDR_FLAG_METADATA, &wim->out_fd, - wim->compression_type, + wim->out_compression_type, &imd->metadata_lte->output_resource_entry, imd->metadata_lte->hash, write_resource_flags, diff --git a/src/wim.c b/src/wim.c index bf6ebbf0..2a6ac129 100644 --- a/src/wim.c +++ b/src/wim.c @@ -139,6 +139,7 @@ wimlib_create_new_wim(int ctype, WIMStruct **wim_ret) wim->lookup_table = table; wim->refcnts_ok = 1; wim->compression_type = ctype; + wim->out_compression_type = ctype; *wim_ret = wim; return 0; out_free: @@ -482,6 +483,7 @@ begin_read(WIMStruct *wim, const void *wim_filename_or_fd, } else { wim->compression_type = WIMLIB_COMPRESSION_TYPE_NONE; } + wim->out_compression_type = wim->compression_type; if (open_flags & WIMLIB_OPEN_FLAG_CHECK_INTEGRITY) { ret = check_wim_integrity(wim, progress_func); diff --git a/src/write.c b/src/write.c index ca5a230e..a127a99b 100644 --- a/src/write.c +++ b/src/write.c @@ -2029,7 +2029,7 @@ write_wim_streams(WIMStruct *wim, int image, int write_flags, return write_stream_list(stream_list, wim->lookup_table, &wim->out_fd, - wim->compression_type, + wim->out_compression_type, &wim->lzx_context, write_flags, num_threads, @@ -2089,7 +2089,7 @@ write_wim_metadata_resources(WIMStruct *wim, int image, int write_flags, "metadata resource.", i); ret = write_wim_resource(imd->metadata_lte, &wim->out_fd, - wim->compression_type, + wim->out_compression_type, &imd->metadata_lte->output_resource_entry, write_resource_flags, &wim->lzx_context); @@ -2447,6 +2447,22 @@ write_pipable_wim(WIMStruct *wim, int image, int write_flags, * finish_write(). */ } +/* API function documented in wimlib.h */ +WIMLIBAPI int +wimlib_set_output_compression_type(WIMStruct *wim, int ctype) +{ + switch (ctype) { + case WIMLIB_COMPRESSION_TYPE_INVALID: + break; + case WIMLIB_COMPRESSION_TYPE_NONE: + case WIMLIB_COMPRESSION_TYPE_LZX: + case WIMLIB_COMPRESSION_TYPE_XPRESS: + wim->out_compression_type = ctype; + return 0; + } + return WIMLIB_ERR_INVALID_PARAM; +} + /* Write a standalone WIM or split WIM (SWM) part to a new file or to a file * descriptor. */ int @@ -2572,6 +2588,10 @@ write_wim_part(WIMStruct *wim, wim->hdr.part_number = part_number; wim->hdr.total_parts = total_parts; + /* Set compression type if different. */ + if (wim->compression_type != wim->out_compression_type) + wim->hdr.flags = get_wim_hdr_cflags(wim->out_compression_type); + /* Use GUID if specified; otherwise generate a new one. */ if (guid) memcpy(wim->hdr.guid, guid, WIMLIB_GUID_LEN); @@ -3045,7 +3065,8 @@ wimlib_overwrite(WIMStruct *wim, int write_flags, if ((!wim->deletion_occurred || (write_flags & WIMLIB_WRITE_FLAG_SOFT_DELETE)) && !(write_flags & (WIMLIB_WRITE_FLAG_REBUILD | WIMLIB_WRITE_FLAG_PIPABLE)) - && !(wim_is_pipable(wim))) + && !(wim_is_pipable(wim)) + && wim->compression_type == wim->out_compression_type) { ret = overwrite_wim_inplace(wim, write_flags, num_threads, progress_func); -- 2.43.0