From 9b9a502863318d6208da2818b1d522346e5eee9e Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 21 Nov 2012 02:34:34 -0600 Subject: [PATCH] Lots of changes - Add progress functions - Rename wimlib_mount() and wimlib_unmount() to wimlib_mount_image() and wimlib_unmount_image() - Remove wimlib_add_image_from_ntfs_volume() and wimlib_apply_image_to_ntfs_volume() in favor of flags to wimlib_add_image() and wimlib_extract_image() - Add progress callback functions, and make them work for NTFS apply as well as normal apply - Consolidate some of the extraction code into extract_single_image(). - Make serial WIM reading work when applying to NTFS as well as in the normal extraction mode. - Update documentation in wimlib.h. --- Makefile.am | 8 +- NEWS | 3 + programs/imagex.c | 338 +++++++++++++++------- src/dentry.h | 15 +- src/extract.c | 534 ++++++++++++++++++++-------------- src/header.c | 6 +- src/integrity.c | 116 ++++---- src/join.c | 194 ++++++------- src/lookup_table.c | 17 +- src/lookup_table.h | 37 +-- src/modify.c | 119 ++++---- src/mount.c | 44 +-- src/ntfs-apply.c | 187 ++---------- src/ntfs-capture.c | 48 +--- src/resource.c | 14 +- src/split.c | 186 ++++++------ src/wim.c | 51 ++-- src/wimlib.h | 654 +++++++++++++++++++++++++----------------- src/wimlib_internal.h | 61 ++-- src/write.c | 369 +++++++++++------------- src/xml.c | 4 +- 21 files changed, 1592 insertions(+), 1413 deletions(-) diff --git a/Makefile.am b/Makefile.am index 1835be1c..5bcf8c1f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -33,8 +33,6 @@ libwim_la_SOURCES = \ src/lzx.h \ src/modify.c \ src/mount.c \ - src/ntfs-apply.c \ - src/ntfs-capture.c \ src/resource.c \ src/rbtree.c \ src/rbtree.h \ @@ -58,6 +56,12 @@ libwim_la_SOURCES = \ src/xpress-decomp.c \ src/xpress.h +if WITH_NTFS_3G +libwim_la_SOURCES += src/ntfs-apply.c \ + src/ntfs-capture.c +endif + + EXTRA_libwim_la_SOURCES = src/sha1-ssse3.asm libwim_la_DEPENDENCIES = $(SSSE3_SHA1_OBJ) STRIP_FPIC = sh $(top_srcdir)/build-aux/strip_fPIC.sh diff --git a/NEWS b/NEWS index 30f02dbf..3651a6bb 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,9 @@ Version 1.1.1: interface changes. The soname is now updated to 1.0.0 and will now be updated each release. + Progress information has been improved, and now arbitrary callback + functions can be used to show the progress of a WIM operation. + A possible bug with changing the bootable image of a WIM was fixed. Version 1.1.0: diff --git a/programs/imagex.c b/programs/imagex.c index 9b8af6e0..883b1e05 100644 --- a/programs/imagex.c +++ b/programs/imagex.c @@ -217,7 +217,7 @@ static void imagex_error_with_errno(const char *format, ...) static int verify_image_exists(int image, const char *image_name, const char *wim_name) { - if (image == WIM_NO_IMAGE) { + if (image == WIMLIB_NO_IMAGE) { imagex_error("\"%s\" is not a valid image in `%s'!\n" " Please specify a 1-based imagex index or " "image name.\n" @@ -231,7 +231,7 @@ static int verify_image_exists(int image, const char *image_name, static int verify_image_is_single(int image) { - if (image == WIM_ALL_IMAGES) { + if (image == WIMLIB_ALL_IMAGES) { imagex_error("Cannot specify all images for this action!"); return -1; } @@ -251,15 +251,15 @@ static int verify_image_exists_and_is_single(int image, const char *image_name, static int get_compression_type(const char *optarg) { if (strcasecmp(optarg, "maximum") == 0 || strcasecmp(optarg, "lzx") == 0) - return WIM_COMPRESSION_TYPE_LZX; + return WIMLIB_COMPRESSION_TYPE_LZX; else if (strcasecmp(optarg, "fast") == 0 || strcasecmp(optarg, "xpress") == 0) - return WIM_COMPRESSION_TYPE_XPRESS; + return WIMLIB_COMPRESSION_TYPE_XPRESS; else if (strcasecmp(optarg, "none") == 0) - return WIM_COMPRESSION_TYPE_NONE; + return WIMLIB_COMPRESSION_TYPE_NONE; else { imagex_error("Invalid compression type `%s'! Must be " "\"maximum\", \"fast\", or \"none\".", optarg); - return WIM_COMPRESSION_TYPE_INVALID; + return WIMLIB_COMPRESSION_TYPE_INVALID; } } @@ -320,6 +320,134 @@ static int file_writable(const char *path) return ret; } +#define TO_PERCENT(numerator, denominator) \ + (((denominator) == 0) ? 0 : ((numerator) * 100 / (denominator))) + +static const char *get_data_type(int ctype) +{ + switch (ctype) { + case WIMLIB_COMPRESSION_TYPE_NONE: + return "uncompressed"; + case WIMLIB_COMPRESSION_TYPE_LZX: + return "LZX-compressed"; + case WIMLIB_COMPRESSION_TYPE_XPRESS: + return "XPRESS-compressed"; + } + return NULL; +} + +static int imagex_progress_func(enum wimlib_progress_msg msg, + const union wimlib_progress_info *info) +{ + unsigned percent_done; + switch (msg) { + case WIMLIB_PROGRESS_MSG_WRITE_STREAMS: + percent_done = TO_PERCENT(info->write_streams.completed_bytes, + info->write_streams.total_bytes); + if (info->write_streams.completed_streams == 0) { + const char *data_type; + + data_type = get_data_type(info->write_streams.compression_type); + printf("Writing %s data using %u thread%s\n", + data_type, info->write_streams.num_threads, + (info->write_streams.num_threads == 1) ? "" : "s"); + } + printf("\r%"PRIu64" MiB of %"PRIu64" MiB (uncompressed) " + "written (%u%% done)", + info->write_streams.completed_bytes >> 20, + info->write_streams.total_bytes >> 20, + percent_done); + if (info->write_streams.completed_bytes == info->write_streams.total_bytes) + putchar('\n'); + break; + case WIMLIB_PROGRESS_MSG_SCAN_BEGIN: + printf("Scanning `%s'...\n", info->scan.source); + break; + /*case WIMLIB_PROGRESS_MSG_SCAN_END:*/ + /*break;*/ + case WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY: + percent_done = TO_PERCENT(info->integrity.completed_bytes, + info->integrity.total_bytes); + printf("\rVerifying integrity of `%s': %"PRIu64" MiB " + "of %"PRIu64" MiB (%u%%) done", + info->integrity.filename, + info->integrity.completed_bytes >> 20, + info->integrity.total_bytes >> 20, + percent_done); + if (info->integrity.completed_bytes == info->integrity.total_bytes) + putchar('\n'); + break; + case WIMLIB_PROGRESS_MSG_CALC_INTEGRITY: + percent_done = TO_PERCENT(info->integrity.completed_bytes, + info->integrity.total_bytes); + printf("\rCalculating integrity table for WIM: %"PRIu64" MiB " + "of %"PRIu64" MiB (%u%%) done", + info->integrity.completed_bytes >> 20, + info->integrity.total_bytes >> 20, + percent_done); + if (info->integrity.completed_bytes == info->integrity.total_bytes) + putchar('\n'); + break; + case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN: + /*printf("Applying image %d (%s) to `%s'\n",*/ + /*info->extract.image,*/ + /*info->extract.image_name,*/ + /*info->extract.target);*/ + break; + /*case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END:*/ + /*printf("Done applying image %d!\n",*/ + /*info->extract.image);*/ + /*break;*/ + /*case WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN:*/ + /*printf("Applying directory structure to %s\n",*/ + /*info->extract.target);*/ + /*break;*/ + case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS: + percent_done = TO_PERCENT(info->extract.completed_bytes, + info->extract.total_bytes); + printf("\rExtracting files: " + "%"PRIu64" MiB of %"PRIu64" MiB (%u%%) done", + info->extract.completed_bytes >> 20, + info->extract.total_bytes >> 20, + percent_done); + if (info->extract.completed_bytes == info->extract.total_bytes) + putchar('\n'); + break; + case WIMLIB_PROGRESS_MSG_JOIN_STREAMS: + percent_done = TO_PERCENT(info->join.completed_bytes, + info->join.total_bytes); + printf("Writing resources from part %u of %u: " + "%"PRIu64 " MiB of %"PRIu64" MiB (%u%%) written\n", + (info->join.completed_parts == info->join.total_parts) ? + info->join.completed_parts : info->join.completed_parts + 1, + info->join.total_parts, + info->join.completed_bytes >> 20, + info->join.total_bytes >> 20, + percent_done); + break; + case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART: + percent_done = TO_PERCENT(info->split.completed_bytes, + info->split.total_bytes); + printf("Writing `%s': %"PRIu64" MiB of %"PRIu64" MiB (%u%%) written\n", + info->split.part_name, + info->split.completed_bytes >> 20, + info->split.total_bytes >> 20, + percent_done); + break; + case WIMLIB_PROGRESS_MSG_SPLIT_END_PART: + if (info->split.completed_bytes == info->split.total_bytes) { + printf("Finished writing %u split WIM parts\n", + info->split.cur_part_number); + } + break; + default: + break; + } + fflush(stdout); + return 0; +} + + static int open_swms_from_glob(const char *swm_glob, const char *first_part, int open_flags, @@ -358,7 +486,8 @@ static int open_swms_from_glob(const char *swm_glob, } ret = wimlib_open_wim(globbuf.gl_pathv[i], open_flags | WIMLIB_OPEN_FLAG_SPLIT_OK, - &additional_swms[i - offset]); + &additional_swms[i - offset], + imagex_progress_func); if (ret != 0) goto out_close_swms; } @@ -394,17 +523,15 @@ static unsigned parse_num_threads(const char *optarg) static int imagex_apply(int argc, const char **argv) { int c; - int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS | - WIMLIB_OPEN_FLAG_SPLIT_OK; + int open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK; int image; int num_images; WIMStruct *w; int ret; const char *wimfile; - const char *dir; + const char *target; const char *image_num_or_name; - int extract_flags = WIMLIB_EXTRACT_FLAG_SEQUENTIAL | - WIMLIB_EXTRACT_FLAG_SHOW_PROGRESS; + int extract_flags = WIMLIB_EXTRACT_FLAG_SEQUENTIAL; const char *swm_glob = NULL; WIMStruct **additional_swms = NULL; @@ -442,13 +569,13 @@ static int imagex_apply(int argc, const char **argv) wimfile = argv[0]; if (argc == 2) { image_num_or_name = "1"; - dir = argv[1]; + target = argv[1]; } else { image_num_or_name = argv[1]; - dir = argv[2]; + target = argv[2]; } - ret = wimlib_open_wim(wimfile, open_flags, &w); + ret = wimlib_open_wim(wimfile, open_flags, &w, imagex_progress_func); if (ret != 0) return ret; @@ -477,30 +604,25 @@ static int imagex_apply(int argc, const char **argv) #ifdef WITH_NTFS_3G struct stat stbuf; - ret = stat(dir, &stbuf); + ret = stat(target, &stbuf); if (ret == 0) { if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) { - const char *ntfs_device = dir; - printf("Applying image %d of `%s' to NTFS filesystem on `%s'\n", - image, wimfile, ntfs_device); - ret = wimlib_apply_image_to_ntfs_volume(w, image, - ntfs_device, - extract_flags, - additional_swms, - num_additional_swms); - goto out; + extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS; + printf("Applying `%s' image %d to NTFS volume `%s'\n", + wimfile, image, target); } } else { if (errno != ENOENT) { - imagex_error_with_errno("Failed to stat `%s'", dir); + imagex_error_with_errno("Failed to stat `%s'", target); ret = -1; goto out; } } #endif - ret = wimlib_extract_image(w, image, dir, extract_flags, - additional_swms, num_additional_swms); + ret = wimlib_extract_image(w, image, target, extract_flags, + additional_swms, num_additional_swms, + imagex_progress_func); out: wimlib_free(w); if (additional_swms) { @@ -514,11 +636,11 @@ out: static int imagex_capture_or_append(int argc, const char **argv) { int c; - int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS; - int add_image_flags = WIMLIB_ADD_IMAGE_FLAG_SHOW_PROGRESS; - int write_flags = WIMLIB_WRITE_FLAG_SHOW_PROGRESS; - int compression_type = WIM_COMPRESSION_TYPE_XPRESS; - const char *dir; + int open_flags = 0; + int add_image_flags = 0; + int write_flags = 0; + int compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS; + const char *source; const char *wimfile; const char *name; const char *desc; @@ -547,7 +669,7 @@ static int imagex_capture_or_append(int argc, const char **argv) break; case 'x': compression_type = get_compression_type(optarg); - if (compression_type == WIM_COMPRESSION_TYPE_INVALID) + if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) return -1; break; case 'f': @@ -558,7 +680,6 @@ static int imagex_capture_or_append(int argc, const char **argv) break; case 'v': add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_VERBOSE; - write_flags |= WIMLIB_WRITE_FLAG_VERBOSE; break; case 't': num_threads = parse_num_threads(optarg); @@ -579,12 +700,12 @@ static int imagex_capture_or_append(int argc, const char **argv) usage(cmd); return -1; } - dir = argv[0]; + source = argv[0]; wimfile = argv[1]; - char dir_copy[strlen(dir) + 1]; - memcpy(dir_copy, dir, strlen(dir) + 1); - default_name = basename(dir_copy); + char source_copy[strlen(source) + 1]; + memcpy(source_copy, source, strlen(source) + 1); + default_name = basename(source_copy); name = (argc >= 3) ? argv[2] : default_name; desc = (argc >= 4) ? argv[3] : NULL; @@ -596,7 +717,8 @@ static int imagex_capture_or_append(int argc, const char **argv) } if (cmd == APPEND) - ret = wimlib_open_wim(wimfile, open_flags, &w); + ret = wimlib_open_wim(wimfile, open_flags, &w, + imagex_progress_func); else ret = wimlib_create_new_wim(compression_type, &w); if (ret != 0) @@ -605,31 +727,24 @@ static int imagex_capture_or_append(int argc, const char **argv) #ifdef WITH_NTFS_3G struct stat stbuf; - ret = stat(dir, &stbuf); + ret = stat(source, &stbuf); if (ret == 0) { if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) { - const char *ntfs_device = dir; - printf("Capturing WIM image NTFS filesystem on `%s'\n", - ntfs_device); - ret = wimlib_add_image_from_ntfs_volume(w, ntfs_device, - name, - config_str, - config_len, - add_image_flags); - goto out_write; + printf("Capturing WIM image from NTFS filesystem on `%s'\n", + source); + add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_NTFS; } } else { if (errno != ENOENT) { - imagex_error_with_errno("Failed to stat `%s'", dir); + imagex_error_with_errno("Failed to stat `%s'", source); ret = -1; goto out; } } #endif - ret = wimlib_add_image(w, dir, name, config_str, config_len, - add_image_flags); + ret = wimlib_add_image(w, source, name, config_str, config_len, + add_image_flags, imagex_progress_func); -out_write: if (ret != 0) goto out; cur_image = wimlib_get_num_images(w); @@ -644,10 +759,11 @@ out_write: goto out; } if (cmd == APPEND) { - ret = wimlib_overwrite(w, write_flags, num_threads); + ret = wimlib_overwrite(w, write_flags, num_threads, + imagex_progress_func); } else { - ret = wimlib_write(w, wimfile, WIM_ALL_IMAGES, write_flags, - num_threads); + ret = wimlib_write(w, wimfile, WIMLIB_ALL_IMAGES, write_flags, + num_threads, imagex_progress_func); } if (ret == WIMLIB_ERR_REOPEN) ret = 0; @@ -663,8 +779,8 @@ out: static int imagex_delete(int argc, const char **argv) { int c; - int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS; - int write_flags = WIMLIB_WRITE_FLAG_SHOW_PROGRESS; + int open_flags = 0; + int write_flags = 0; const char *wimfile; const char *image_num_or_name; WIMStruct *w; @@ -703,7 +819,8 @@ static int imagex_delete(int argc, const char **argv) if (ret != 0) return ret; - ret = wimlib_open_wim(wimfile, open_flags, &w); + ret = wimlib_open_wim(wimfile, open_flags, &w, + imagex_progress_func); if (ret != 0) return ret; @@ -719,7 +836,7 @@ static int imagex_delete(int argc, const char **argv) goto out; } - ret = wimlib_overwrite(w, write_flags, 0); + ret = wimlib_overwrite(w, write_flags, 0, imagex_progress_func); if (ret == WIMLIB_ERR_REOPEN) ret = 0; if (ret != 0) { @@ -752,7 +869,8 @@ static int imagex_dir(int argc, const char **argv) } wimfile = argv[1]; - ret = wimlib_open_wim(wimfile, WIMLIB_OPEN_FLAG_SPLIT_OK, &w); + ret = wimlib_open_wim(wimfile, WIMLIB_OPEN_FLAG_SPLIT_OK, &w, + imagex_progress_func); if (ret != 0) return ret; @@ -786,10 +904,10 @@ out: static int imagex_export(int argc, const char **argv) { int c; - int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS; + int open_flags = 0; int export_flags = 0; - int write_flags = WIMLIB_WRITE_FLAG_SHOW_PROGRESS; - int compression_type; + int write_flags = 0; + int compression_type = WIMLIB_COMPRESSION_TYPE_NONE; bool compression_type_specified = false; const char *src_wimfile; const char *src_image_num_or_name; @@ -818,7 +936,7 @@ static int imagex_export(int argc, const char **argv) break; case 'x': compression_type = get_compression_type(optarg); - if (compression_type == WIM_COMPRESSION_TYPE_INVALID) + if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) return -1; compression_type_specified = true; break; @@ -850,7 +968,8 @@ static int imagex_export(int argc, const char **argv) dest_name = (argc >= 4) ? argv[3] : NULL; dest_desc = (argc >= 5) ? argv[4] : NULL; ret = wimlib_open_wim(src_wimfile, - open_flags | WIMLIB_OPEN_FLAG_SPLIT_OK, &src_w); + open_flags | WIMLIB_OPEN_FLAG_SPLIT_OK, &src_w, + imagex_progress_func); if (ret != 0) return ret; @@ -868,7 +987,8 @@ static int imagex_export(int argc, const char **argv) ret = -1; goto out; } - ret = wimlib_open_wim(dest_wimfile, open_flags, &dest_w); + ret = wimlib_open_wim(dest_wimfile, open_flags, &dest_w, + imagex_progress_func); if (ret != 0) goto out; @@ -919,16 +1039,18 @@ static int imagex_export(int argc, const char **argv) ret = wimlib_export_image(src_w, image, dest_w, dest_name, dest_desc, export_flags, additional_swms, - num_additional_swms); + num_additional_swms, imagex_progress_func); if (ret != 0) goto out; if (wim_is_new) - ret = wimlib_write(dest_w, dest_wimfile, WIM_ALL_IMAGES, - write_flags, num_threads); + ret = wimlib_write(dest_w, dest_wimfile, WIMLIB_ALL_IMAGES, + write_flags, num_threads, + imagex_progress_func); else - ret = wimlib_overwrite(dest_w, write_flags, num_threads); + ret = wimlib_overwrite(dest_w, write_flags, num_threads, + imagex_progress_func); out: if (ret == WIMLIB_ERR_REOPEN) ret = 0; @@ -963,8 +1085,7 @@ static int imagex_info(int argc, const char **argv) FILE *fp; int image; int ret; - int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS | - WIMLIB_OPEN_FLAG_SPLIT_OK; + int open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK; int part_number; int total_parts; @@ -1022,14 +1143,15 @@ static int imagex_info(int argc, const char **argv) if (check) open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; - ret = wimlib_open_wim(wimfile, open_flags, &w); + ret = wimlib_open_wim(wimfile, open_flags, &w, + imagex_progress_func); if (ret != 0) return ret; part_number = wimlib_get_part_number(w, &total_parts); image = wimlib_resolve_image(w, image_num_or_name); - if (image == WIM_NO_IMAGE && strcmp(image_num_or_name, "0") != 0) { + if (image == WIMLIB_NO_IMAGE && strcmp(image_num_or_name, "0") != 0) { imagex_error("The image `%s' does not exist", image_num_or_name); if (boot) @@ -1040,7 +1162,7 @@ static int imagex_info(int argc, const char **argv) goto out; } - if (image == WIM_ALL_IMAGES && wimlib_get_num_images(w) > 1) { + if (image == WIMLIB_ALL_IMAGES && wimlib_get_num_images(w) > 1) { if (boot) { imagex_error("Cannot specify the --boot flag " "without specifying a specific " @@ -1063,14 +1185,14 @@ static int imagex_info(int argc, const char **argv) /* Read-only operations */ - if (image == WIM_NO_IMAGE) { + if (image == WIMLIB_NO_IMAGE) { imagex_error("`%s' is not a valid image", image_num_or_name); ret = WIMLIB_ERR_INVALID_IMAGE; goto out; } - if (image == WIM_ALL_IMAGES && short_header) + if (image == WIMLIB_ALL_IMAGES && short_header) wimlib_print_wim_information(w); if (header) @@ -1126,10 +1248,10 @@ static int imagex_info(int argc, const char **argv) imagex_error("Modifying a split WIM is not supported."); return -1; } - if (image == WIM_ALL_IMAGES) + if (image == WIMLIB_ALL_IMAGES) image = 1; - if (image == WIM_NO_IMAGE && new_name) { + if (image == WIMLIB_NO_IMAGE && new_name) { imagex_error("Cannot specify new_name (`%s') when " "using image 0", new_name); return -1; @@ -1188,13 +1310,13 @@ static int imagex_info(int argc, const char **argv) int write_flags; if (check) { - write_flags = WIMLIB_WRITE_FLAG_CHECK_INTEGRITY | - WIMLIB_WRITE_FLAG_SHOW_PROGRESS; + write_flags = WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; } else { write_flags = 0; } - ret = wimlib_overwrite(w, write_flags, 1); + ret = wimlib_overwrite(w, write_flags, 1, + imagex_progress_func); if (ret == WIMLIB_ERR_REOPEN) ret = 0; } else { @@ -1212,13 +1334,15 @@ out: static int imagex_join(int argc, const char **argv) { int c; - int flags = WIMLIB_OPEN_FLAG_SPLIT_OK | WIMLIB_OPEN_FLAG_SHOW_PROGRESS; + int swm_open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK; + int wim_write_flags = 0; const char *output_path; for_opt(c, join_options) { switch (c) { case 'c': - flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; + swm_open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; + wim_write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; break; default: goto err; @@ -1228,12 +1352,13 @@ static int imagex_join(int argc, const char **argv) argv += optind; if (argc < 2) { - imagex_error("Must specify at least one split WIM (.swm) parts " + imagex_error("Must specify one or more split WIM (.swm) parts " "to join"); goto err; } output_path = argv[0]; - return wimlib_join(++argv, --argc, output_path, flags); + return wimlib_join(++argv, --argc, output_path, swm_open_flags, + wim_write_flags, imagex_progress_func); err: usage(JOIN); return -1; @@ -1244,8 +1369,7 @@ static int imagex_mount_rw_or_ro(int argc, const char **argv) { int c; int mount_flags = 0; - int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS | - WIMLIB_OPEN_FLAG_SPLIT_OK; + int open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK; const char *wimfile; const char *dir; WIMStruct *w; @@ -1293,7 +1417,8 @@ static int imagex_mount_rw_or_ro(int argc, const char **argv) wimfile = argv[0]; - ret = wimlib_open_wim(wimfile, open_flags, &w); + ret = wimlib_open_wim(wimfile, open_flags, &w, + imagex_progress_func); if (ret != 0) return ret; @@ -1331,8 +1456,8 @@ static int imagex_mount_rw_or_ro(int argc, const char **argv) return ret; } - ret = wimlib_mount(w, image, dir, mount_flags, additional_swms, - num_additional_swms); + ret = wimlib_mount_image(w, image, dir, mount_flags, additional_swms, + num_additional_swms); if (ret != 0) { imagex_error("Failed to mount image %d from `%s' on `%s'", image, wimfile, dir); @@ -1355,9 +1480,8 @@ mount_usage: static int imagex_optimize(int argc, const char **argv) { int c; - int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS; - int write_flags = WIMLIB_WRITE_FLAG_REBUILD | - WIMLIB_WRITE_FLAG_SHOW_PROGRESS; + int open_flags = 0; + int write_flags = WIMLIB_WRITE_FLAG_REBUILD; int ret; WIMStruct *w; const char *wimfile; @@ -1388,7 +1512,8 @@ static int imagex_optimize(int argc, const char **argv) wimfile = argv[0]; - ret = wimlib_open_wim(wimfile, open_flags, &w); + ret = wimlib_open_wim(wimfile, open_flags, &w, + imagex_progress_func); if (ret != 0) return ret; @@ -1399,7 +1524,7 @@ static int imagex_optimize(int argc, const char **argv) else printf("%"PRIu64" KiB\n", old_size >> 10); - ret = wimlib_overwrite(w, write_flags, 0); + ret = wimlib_overwrite(w, write_flags, 0, imagex_progress_func); new_size = file_get_size(argv[0]); printf("`%s' optimized size: ", wimfile); @@ -1424,14 +1549,18 @@ static int imagex_optimize(int argc, const char **argv) static int imagex_split(int argc, const char **argv) { int c; - int flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS; + int open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK; + int write_flags = 0; unsigned long part_size; char *tmp; + int ret; + WIMStruct *w; for_opt(c, split_options) { switch (c) { case 'c': - flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; + open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; + write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; break; default: usage(SPLIT); @@ -1451,7 +1580,12 @@ static int imagex_split(int argc, const char **argv) imagex_error("The part size must be an integer or floating-point number of megabytes."); return -1; } - return wimlib_split(argv[0], argv[1], part_size, flags); + ret = wimlib_open_wim(argv[0], open_flags, &w, imagex_progress_func); + if (ret != 0) + return ret; + ret = wimlib_split(w, argv[1], part_size, write_flags, imagex_progress_func); + wimlib_free(w); + return ret; } /* Unmounts an image. */ @@ -1481,7 +1615,7 @@ static int imagex_unmount(int argc, const char **argv) return -1; } - ret = wimlib_unmount(argv[0], unmount_flags); + ret = wimlib_unmount_image(argv[0], unmount_flags); if (ret != 0) imagex_error("Failed to unmount `%s'", argv[0]); return ret; diff --git a/src/dentry.h b/src/dentry.h index 2e2ba8bf..afa6aaa4 100644 --- a/src/dentry.h +++ b/src/dentry.h @@ -157,6 +157,8 @@ struct dentry { u16 file_name_utf8_len; u8 is_extracted : 1; + u8 visited : 1; + u8 canonical : 1; /* Byte 40 */ @@ -253,7 +255,7 @@ struct inode { u8 verified : 1; /* temporary flag */ - u8 found : 1; + u8 visited : 1; /* Number of alternate data streams associated with this inode */ u16 num_ads; @@ -286,7 +288,13 @@ struct inode { /* List of dentries that reference this inode (there should be * link_count of them) */ struct list_head dentry_list; + struct hlist_node hlist; + + struct list_head tmp_list; + + struct list_head lte_inode_list; + char *extracted_file; /* Root of a red-black tree storing the children of this inode (if @@ -319,11 +327,6 @@ struct inode { list_add(&(dentry)->inode_dentry_list, &(inode)->dentry_list); \ }) -static inline bool dentry_is_extracted(const struct dentry *dentry) -{ - return dentry->is_extracted; -} - static inline bool dentry_is_first_in_inode(const struct dentry *dentry) { return container_of(dentry->d_inode->dentry_list.next, diff --git a/src/extract.c b/src/extract.c index 90b6ce39..a3f00cc8 100644 --- a/src/extract.c +++ b/src/extract.c @@ -44,6 +44,7 @@ #include #endif + #include #include "dentry.h" @@ -52,20 +53,21 @@ #include "wimlib_internal.h" #include "xml.h" +#ifdef WITH_NTFS_3G +#include +#endif -static int extract_regular_file_linked(const struct dentry *dentry, - const char *output_dir, +static int extract_regular_file_linked(struct dentry *dentry, const char *output_path, - int extract_flags, + struct apply_args *args, struct lookup_table_entry *lte) { /* This mode overrides the normal hard-link extraction and * instead either symlinks or hardlinks *all* identical files in * the WIM, even if they are in a different image (in the case * of a multi-image extraction) */ - wimlib_assert(lte->extracted_file != NULL); - if (extract_flags & WIMLIB_EXTRACT_FLAG_HARDLINK) { + if (args->extract_flags & WIMLIB_EXTRACT_FLAG_HARDLINK) { if (link(lte->extracted_file, output_path) != 0) { ERROR_WITH_ERRNO("Failed to hard link " "`%s' to `%s'", @@ -80,14 +82,12 @@ static int extract_regular_file_linked(const struct dentry *dentry, const char *p2; size_t i; - wimlib_assert(extract_flags & WIMLIB_EXTRACT_FLAG_SYMLINK); - num_path_components = get_num_path_components(dentry->full_path_utf8) - 1; num_output_dir_path_components = - get_num_path_components(output_dir); + get_num_path_components(args->target); - if (extract_flags & WIMLIB_EXTRACT_FLAG_MULTI_IMAGE) { + if (args->extract_flags & WIMLIB_EXTRACT_FLAG_MULTI_IMAGE) { num_path_components++; num_output_dir_path_components--; } @@ -113,15 +113,13 @@ static int extract_regular_file_linked(const struct dentry *dentry, buf, lte->extracted_file); return WIMLIB_ERR_LINK; } - } return 0; } -static int extract_regular_file_unlinked(WIMStruct *w, - struct dentry *dentry, +static int extract_regular_file_unlinked(struct dentry *dentry, + struct apply_args *args, const char *output_path, - int extract_flags, struct lookup_table_entry *lte) { /* Normal mode of extraction. Regular files and hard links are @@ -131,8 +129,8 @@ static int extract_regular_file_unlinked(WIMStruct *w, int ret; struct inode *inode = dentry->d_inode; - if (!((extract_flags & WIMLIB_EXTRACT_FLAG_MULTI_IMAGE) - && (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | + if (!((args->extract_flags & WIMLIB_EXTRACT_FLAG_MULTI_IMAGE) + && (args->extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | WIMLIB_EXTRACT_FLAG_HARDLINK)))) { /* If the dentry is one of a hard link set of at least 2 @@ -184,7 +182,7 @@ static int extract_regular_file_unlinked(WIMStruct *w, ERROR("Failed to extract resource to `%s'", output_path); goto out; } - + args->progress.extract.completed_bytes += wim_resource_size(lte); out: if (close(out_fd) != 0) { ERROR_WITH_ERRNO("Failed to close file `%s'", output_path); @@ -196,41 +194,38 @@ out: /* * Extracts a regular file from the WIM archive. */ -static int extract_regular_file(WIMStruct *w, - struct dentry *dentry, - const char *output_dir, - const char *output_path, - int extract_flags) +static int extract_regular_file(struct dentry *dentry, + struct apply_args *args, + const char *output_path) { struct lookup_table_entry *lte; const struct inode *inode = dentry->d_inode; - lte = inode_unnamed_lte(inode, w->lookup_table); + lte = inode_unnamed_lte_resolved(inode); - if ((extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | - WIMLIB_EXTRACT_FLAG_HARDLINK)) && lte) { + if (lte && (args->extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | + WIMLIB_EXTRACT_FLAG_HARDLINK))) + { if (lte->extracted_file) { - return extract_regular_file_linked(dentry, output_dir, - output_path, - extract_flags, lte); + return extract_regular_file_linked(dentry, output_path, args, lte); } else { lte->extracted_file = STRDUP(output_path); if (!lte->extracted_file) return WIMLIB_ERR_NOMEM; } } - - return extract_regular_file_unlinked(w, dentry, output_path, - extract_flags, lte); - + return extract_regular_file_unlinked(dentry, args, output_path, lte); } -static int extract_symlink(const struct dentry *dentry, const char *output_path, - const WIMStruct *w) +static int extract_symlink(struct dentry *dentry, + struct apply_args *args, + const char *output_path) { char target[4096]; ssize_t ret = inode_readlink(dentry->d_inode, target, - sizeof(target), w, 0); + sizeof(target), args->w, 0); + struct lookup_table_entry *lte; + if (ret <= 0) { ERROR("Could not read the symbolic link from dentry `%s'", dentry->full_path_utf8); @@ -242,6 +237,8 @@ static int extract_symlink(const struct dentry *dentry, const char *output_path, output_path, target); return WIMLIB_ERR_LINK; } + lte = inode_unnamed_lte_resolved(dentry->d_inode); + args->progress.extract.completed_bytes += wim_resource_size(lte); return 0; } @@ -282,58 +279,55 @@ static int extract_directory(const char *output_path, bool is_root) return 0; } -struct extract_args { - WIMStruct *w; - int extract_flags; - const char *output_dir; - unsigned num_lutimes_warnings; -}; - /* * Extracts a file, directory, or symbolic link from the WIM archive. For use * in for_dentry_in_tree(). */ -static int extract_dentry(struct dentry *dentry, void *arg) +static int apply_dentry_normal(struct dentry *dentry, void *arg) { - struct extract_args *args = arg; - WIMStruct *w = args->w; + struct apply_args *args = arg; int extract_flags = args->extract_flags; - size_t len = strlen(args->output_dir); - char output_path[len + dentry->full_path_utf8_len + 1]; + struct inode *inode = dentry->d_inode; + size_t len; + int ret; + + if (dentry->is_extracted) + return 0; if (extract_flags & WIMLIB_EXTRACT_FLAG_NO_STREAMS) - if (inode_unnamed_lte(dentry->d_inode, w->lookup_table) != NULL) + if (inode_unnamed_lte_resolved(inode)) return 0; - if (extract_flags & WIMLIB_EXTRACT_FLAG_VERBOSE) { - wimlib_assert(dentry->full_path_utf8); + if (extract_flags & WIMLIB_EXTRACT_FLAG_VERBOSE) puts(dentry->full_path_utf8); - } - memcpy(output_path, args->output_dir, len); + len = strlen(args->target); + char output_path[len + dentry->full_path_utf8_len + 1]; + memcpy(output_path, args->target, len); memcpy(output_path + len, dentry->full_path_utf8, dentry->full_path_utf8_len); output_path[len + dentry->full_path_utf8_len] = '\0'; - - if (dentry_is_symlink(dentry)) - return extract_symlink(dentry, output_path, w); - else if (dentry_is_directory(dentry)) - return extract_directory(output_path, dentry_is_root(dentry)); + if (inode_is_symlink(inode)) + ret = extract_symlink(dentry, args, output_path); + else if (inode_is_directory(inode)) + ret = extract_directory(output_path, false); else - return extract_regular_file(w, dentry, args->output_dir, - output_path, extract_flags); + ret = extract_regular_file(dentry, args, output_path); + if (ret == 0) + dentry->is_extracted = 1; + return ret; } /* Apply timestamp to extracted file */ -static int apply_dentry_timestamps(struct dentry *dentry, void *arg) +static int apply_dentry_timestamps_normal(struct dentry *dentry, void *arg) { - struct extract_args *args = arg; - size_t len = strlen(args->output_dir); + struct apply_args *args = arg; + size_t len = strlen(args->target); char output_path[len + dentry->full_path_utf8_len + 1]; const struct inode *inode = dentry->d_inode; int ret; - memcpy(output_path, args->output_dir, len); + memcpy(output_path, args->target, len); memcpy(output_path + len, dentry->full_path_utf8, dentry->full_path_utf8_len); output_path[len + dentry->full_path_utf8_len] = '\0'; @@ -365,26 +359,6 @@ static int apply_dentry_timestamps(struct dentry *dentry, void *arg) return 0; } - -static int dentry_add_streams_for_extraction(struct dentry *dentry, - void *wim) -{ - WIMStruct *w = wim; - struct list_head *stream_list; - struct lookup_table_entry *lte; - - lte = inode_unnamed_lte(dentry->d_inode, w->lookup_table); - if (lte) { - if (++lte->out_refcnt == 1) { - INIT_LIST_HEAD(<e->dentry_list); - stream_list = w->private; - list_add_tail(<e->staging_list, stream_list); - } - list_add_tail(&dentry->tmp_list, <e->dentry_list); - } - return 0; -} - static int cmp_streams_by_wim_position(const void *p1, const void *p2) { const struct lookup_table_entry *lte1, *lte2; @@ -406,15 +380,10 @@ static int sort_stream_list_by_wim_position(struct list_head *stream_list) size_t i; size_t array_size; - DEBUG("Sorting stream list by wim position"); - num_streams = 0; list_for_each(cur, stream_list) num_streams++; array_size = num_streams * sizeof(array[0]); - - DEBUG("num_streams = %zu", num_streams); - array = MALLOC(array_size); if (!array) { ERROR("Failed to allocate %zu bytes to sort stream entries", @@ -436,166 +405,296 @@ static int sort_stream_list_by_wim_position(struct list_head *stream_list) return 0; } -static u64 calculate_bytes_to_extract(struct list_head *stream_list, - int extract_flags) +static void calculate_bytes_to_extract(struct list_head *stream_list, + int extract_flags, + union wimlib_progress_info *progress) { struct lookup_table_entry *lte; - struct dentry *dentry; - u64 total_size = 0; + struct inode *inode; + u64 total_bytes = 0; + u64 num_streams = 0; + + /* For each stream to be extracted... */ list_for_each_entry(lte, stream_list, staging_list) { - u64 size = wim_resource_size(lte); if (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | WIMLIB_EXTRACT_FLAG_HARDLINK)) { - total_size += size; + /* In the symlink or hard link extraction mode, each + * stream will be extracted one time regardless of how + * many dentries share the stream. */ + wimlib_assert(!(extract_flags & WIMLIB_EXTRACT_FLAG_NTFS)); + if (!lte->extracted_file) { + num_streams++; + total_bytes += wim_resource_size(lte); + } } else { - list_for_each_entry(dentry, <e->dentry_list, - tmp_list) + list_for_each_entry(inode, <e->inode_list, + lte_inode_list) { - dentry->d_inode->found = false; + num_streams++; + total_bytes += wim_resource_size(lte); } - list_for_each_entry(dentry, <e->dentry_list, - tmp_list) - { - if (!dentry->d_inode->found) { - dentry->d_inode->found = true; - total_size += size; + } + } + progress->extract.num_streams = num_streams; + progress->extract.total_bytes = total_bytes; + progress->extract.completed_bytes = 0; +} + +static void maybe_add_stream_for_extraction(struct lookup_table_entry *lte, + struct list_head *stream_list) +{ + if (lte->out_refcnt == 0) { + lte->out_refcnt = 1; + INIT_LIST_HEAD(<e->inode_list); + list_add_tail(<e->staging_list, stream_list); + } +} + +static void inode_find_streams_for_extraction(struct inode *inode, + struct list_head *stream_list, + int extract_flags) +{ + struct lookup_table_entry *lte; + bool inode_added = false; + + lte = inode_unnamed_lte_resolved(inode); + + if (lte) { + maybe_add_stream_for_extraction(lte, stream_list); + list_add_tail(&inode->lte_inode_list, <e->inode_list); + inode_added = true; + } +#ifdef WITH_NTFS_3G + if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) { + for (unsigned i = 0; i < inode->num_ads; i++) { + if (inode->ads_entries[i].stream_name_len != 0) { + lte = inode_stream_lte_resolved(inode, i + 1); + if (lte) { + maybe_add_stream_for_extraction(lte, + stream_list); + if (!inode_added) { + list_add_tail(&inode->lte_inode_list, + <e->inode_list); + inode_added = true; + } + } + } + } + } +#endif +} + +static void find_streams_for_extraction(struct hlist_head *inode_list, + struct list_head *stream_list, + struct lookup_table *lookup_table, + int extract_flags) +{ + struct inode *inode; + struct hlist_node *cur; + struct dentry *dentry; + + for_lookup_table_entry(lookup_table, lte_zero_out_refcnt, NULL); + INIT_LIST_HEAD(stream_list); + hlist_for_each_entry(inode, cur, inode_list, hlist) { + if (!inode->resolved) + inode_resolve_ltes(inode, lookup_table); + inode_for_each_dentry(dentry, inode) + dentry->is_extracted = 0; + inode_find_streams_for_extraction(inode, stream_list, + extract_flags); + } +} + +struct apply_operations { + int (*apply_dentry)(struct dentry *dentry, void *arg); + int (*apply_dentry_timestamps)(struct dentry *dentry, void *arg); +}; + +static const struct apply_operations normal_apply_operations = { + .apply_dentry = apply_dentry_normal, + .apply_dentry_timestamps = apply_dentry_timestamps_normal, +}; + +#ifdef WITH_NTFS_3G +static const struct apply_operations ntfs_apply_operations = { + .apply_dentry = wim_apply_dentry_ntfs, + .apply_dentry_timestamps = wim_apply_dentry_timestamps, +}; +#endif + +static int apply_stream_list(struct list_head *stream_list, + struct apply_args *args, + const struct apply_operations *ops, + wimlib_progress_func_t progress_func) +{ + uint64_t bytes_per_progress = args->progress.extract.total_bytes / 100; + uint64_t next_progress = bytes_per_progress; + struct lookup_table_entry *lte; + struct inode *inode; + struct dentry *dentry; + int ret = 0; + list_for_each_entry(lte, stream_list, staging_list) { + list_for_each_entry(inode, <e->inode_list, lte_inode_list) { + inode_for_each_dentry(dentry, inode) { + ret = ops->apply_dentry(dentry, args); + if (ret != 0) + goto out; + if (args->progress.extract.completed_bytes >= next_progress) { + progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS, + &args->progress); + next_progress += bytes_per_progress; } } } } - return total_size; +out: + return ret; } static int extract_single_image(WIMStruct *w, int image, - const char *output_dir, int extract_flags) + const char *target, int extract_flags, + wimlib_progress_func_t progress_func) { int ret; - struct dentry *root; - const char *image_name; + struct list_head stream_list; + struct hlist_head *inode_list; + + struct apply_args args; + const struct apply_operations *ops; + + args.w = w; + args.target = target; + args.extract_flags = extract_flags; + args.num_lutimes_warnings = 0; + args.target = target; + args.stream_list = &stream_list; + + if (progress_func) { + args.progress.extract.image = image; + args.progress.extract.image_name = wimlib_get_image_name(w, image); + args.progress.extract.target = target; + } - DEBUG("Extracting image %d", image); +#ifdef WITH_NTFS_3G + if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) { + args.vol = ntfs_mount(target, 0); + if (!args.vol) { + ERROR_WITH_ERRNO("Failed to mount NTFS volume `%s'", target); + return WIMLIB_ERR_NTFS_3G; + } + } +#endif + +#ifdef WITH_NTFS_3G + if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) + ops = &ntfs_apply_operations; + else +#endif + ops = &normal_apply_operations; ret = select_wim_image(w, image); if (ret != 0) - return ret; + goto out; - root = wim_root_dentry(w); + inode_list = &w->image_metadata[image - 1].inode_list; + find_streams_for_extraction(inode_list, + &stream_list, + w->lookup_table, + extract_flags); - struct extract_args args = { - .w = w, - .extract_flags = extract_flags, - .output_dir = output_dir, - .num_lutimes_warnings = 0, - }; + calculate_bytes_to_extract(&stream_list, extract_flags, + &args.progress); - image_name = wimlib_get_image_name(w, image); - if (!image_name) - image_name = "unnamed"; + if (progress_func) { + progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN, + &args.progress); + } if (extract_flags & WIMLIB_EXTRACT_FLAG_SEQUENTIAL) { - for_lookup_table_entry(w->lookup_table, lte_zero_out_refcnt, - NULL); - args.extract_flags |= WIMLIB_EXTRACT_FLAG_NO_STREAMS; - if (args.extract_flags & WIMLIB_EXTRACT_FLAG_SHOW_PROGRESS) { - printf("Creating directory structure for image %d (%s)...\n", - image, image_name); - } - } else { - if (args.extract_flags & WIMLIB_EXTRACT_FLAG_SHOW_PROGRESS) { - printf("Extracting image %d (%s)...\n", - image, image_name); + ret = sort_stream_list_by_wim_position(&stream_list); + if (ret != 0) { + WARNING("Falling back to non-sequential extraction"); + extract_flags &= ~WIMLIB_EXTRACT_FLAG_SEQUENTIAL; } } - ret = for_dentry_in_tree(root, extract_dentry, &args); + if (progress_func) { + progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN, + &args.progress); + } + + + args.extract_flags |= WIMLIB_EXTRACT_FLAG_NO_STREAMS; + ret = for_dentry_in_tree(wim_root_dentry(w), ops->apply_dentry, &args); + args.extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_STREAMS; if (ret != 0) - return ret; + goto out; - if (extract_flags & WIMLIB_EXTRACT_FLAG_SEQUENTIAL) { - struct list_head stream_list; - struct lookup_table_entry *lte; - struct lookup_table_entry *tmp; - struct dentry *dentry; - u64 total_size; - u64 cur_size; - u64 next_size; - u64 one_percent; - unsigned cur_percent; - - INIT_LIST_HEAD(&stream_list); - w->private = &stream_list; - for_dentry_in_tree(root, dentry_add_streams_for_extraction, w); - ret = sort_stream_list_by_wim_position(&stream_list); - args.extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_STREAMS; - if (ret != 0) { - WARNING("Falling back to non-sequential image extraction"); - ret = for_dentry_in_tree(root, extract_dentry, &args); - if (ret != 0) - return ret; - goto out; - } + if (progress_func) { + progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_END, + &args.progress); + } - total_size = calculate_bytes_to_extract(&stream_list, args.extract_flags); - one_percent = total_size / 100; - cur_size = 0; - next_size = 0; - cur_percent = 0; - puts("Extracting files..."); - list_for_each_entry_safe(lte, tmp, &stream_list, staging_list) { - list_del(<e->staging_list); - list_for_each_entry(dentry, <e->dentry_list, tmp_list) { - if ((!dentry->d_inode->extracted_file) && - (args.extract_flags & WIMLIB_EXTRACT_FLAG_SHOW_PROGRESS)) - { - show_stream_op_progress(&cur_size, &next_size, - total_size, one_percent, - &cur_percent, lte, - "extracted"); - } - ret = extract_dentry(dentry, &args); - if (ret != 0) - return ret; - } - } - finish_stream_op_progress(total_size, "extracted"); + ret = apply_stream_list(&stream_list, &args, ops, progress_func); + if (ret != 0) + goto out; + + if (progress_func) + progress_func(WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS, NULL); + + ret = for_dentry_in_tree_depth(wim_root_dentry(w), + ops->apply_dentry_timestamps, &args); + if (ret != 0) + goto out; + + if (progress_func) { + progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END, + &args.progress); } out: - return for_dentry_in_tree_depth(root, apply_dentry_timestamps, &args); +#ifdef WITH_NTFS_3G + if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) { + if (ntfs_umount(args.vol, FALSE) != 0) { + ERROR_WITH_ERRNO("Failed to unmount NTFS volume `%s'", args.target); + if (ret == 0) + ret = WIMLIB_ERR_NTFS_3G; + } + } +#endif + return ret; } /* Extracts all images from the WIM to @output_dir, with the images placed in * subdirectories named by their image names. */ -static int extract_all_images(WIMStruct *w, const char *output_dir, - int extract_flags) +static int extract_all_images(WIMStruct *w, const char *target, + int extract_flags, + wimlib_progress_func_t progress_func) { size_t image_name_max_len = max(xml_get_max_image_name_len(w), 20); - size_t output_path_len = strlen(output_dir); + size_t output_path_len = strlen(target); char buf[output_path_len + 1 + image_name_max_len + 1]; int ret; int image; const char *image_name; - DEBUG("Attempting to extract all images from `%s' to `%s'", - w->filename, output_dir); - - ret = extract_directory(output_dir, true); + ret = extract_directory(target, true); if (ret != 0) return ret; - memcpy(buf, output_dir, output_path_len); + memcpy(buf, target, output_path_len); buf[output_path_len] = '/'; for (image = 1; image <= w->hdr.image_count; image++) { - image_name = wimlib_get_image_name(w, image); - if (*image_name) { + if (image_name && *image_name) { strcpy(buf + output_path_len + 1, image_name); } else { /* Image name is empty. Use image number instead */ sprintf(buf + output_path_len + 1, "%d", image); } - ret = extract_single_image(w, image, buf, extract_flags); + ret = extract_single_image(w, image, buf, extract_flags, + progress_func); if (ret != 0) return ret; } @@ -604,19 +703,16 @@ static int extract_all_images(WIMStruct *w, const char *output_dir, /* Extracts a single image or all images from a WIM file. */ WIMLIBAPI int wimlib_extract_image(WIMStruct *w, int image, - const char *output_dir, + const char *target, int extract_flags, WIMStruct **additional_swms, - unsigned num_additional_swms) + unsigned num_additional_swms, + wimlib_progress_func_t progress_func) { struct lookup_table *joined_tab, *w_tab_save; int ret; - DEBUG("w->filename = %s, image = %d, output_dir = %s, flags = 0x%x, " - "num_additional_swms = %u", - w->filename, image, output_dir, extract_flags, num_additional_swms); - - if (!w || !output_dir) + if (!w || !target) return WIMLIB_ERR_INVALID_PARAM; extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC; @@ -625,6 +721,25 @@ WIMLIBAPI int wimlib_extract_image(WIMStruct *w, int image, == (WIMLIB_EXTRACT_FLAG_SYMLINK | WIMLIB_EXTRACT_FLAG_HARDLINK)) return WIMLIB_ERR_INVALID_PARAM; + if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) { +#ifdef WITH_NTFS_3G + if ((extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | WIMLIB_EXTRACT_FLAG_HARDLINK))) { + ERROR("Cannot specify symlink or hardlink flags when applying "); + ERROR("directly to a NTFS volume"); + return WIMLIB_ERR_INVALID_PARAM; + } + if (image == WIMLIB_ALL_IMAGES) { + ERROR("Can only apply a single image when applying " + "directly to a NTFS volume"); + return WIMLIB_ERR_INVALID_PARAM; + } +#else + ERROR("wimlib was compiled without support for NTFS-3g, so"); + ERROR("we cannot apply a WIM image directly to a NTFS volume"); + return WIMLIB_ERR_UNSUPPORTED; +#endif + } + ret = verify_swm_set(w, additional_swms, num_additional_swms); if (ret != 0) return ret; @@ -638,25 +753,14 @@ WIMLIBAPI int wimlib_extract_image(WIMStruct *w, int image, w->lookup_table = joined_tab; } - if (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | - WIMLIB_EXTRACT_FLAG_HARDLINK)) - { - for_lookup_table_entry(w->lookup_table, - lte_zero_extracted_file, - NULL); - extract_flags &= ~WIMLIB_EXTRACT_FLAG_SEQUENTIAL; - } - - if (image == WIM_ALL_IMAGES) { + if (image == WIMLIB_ALL_IMAGES) { extract_flags |= WIMLIB_EXTRACT_FLAG_MULTI_IMAGE; - ret = extract_all_images(w, output_dir, extract_flags); + ret = extract_all_images(w, target, extract_flags, + progress_func); } else { extract_flags &= ~WIMLIB_EXTRACT_FLAG_MULTI_IMAGE; - ret = extract_single_image(w, image, output_dir, extract_flags); - } - if (num_additional_swms) { - free_lookup_table(w->lookup_table); - w->lookup_table = w_tab_save; + ret = extract_single_image(w, image, target, extract_flags, + progress_func); } if (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | @@ -666,6 +770,10 @@ WIMLIBAPI int wimlib_extract_image(WIMStruct *w, int image, lte_free_extracted_file, NULL); } - return ret; + if (num_additional_swms) { + free_lookup_table(w->lookup_table); + w->lookup_table = w_tab_save; + } + return ret; } diff --git a/src/header.c b/src/header.c index 69d4f695..2a7fe32b 100644 --- a/src/header.c +++ b/src/header.c @@ -197,14 +197,14 @@ int init_header(struct wim_header *hdr, int ctype) { memset(hdr, 0, sizeof(struct wim_header)); switch (ctype) { - case WIM_COMPRESSION_TYPE_NONE: + case WIMLIB_COMPRESSION_TYPE_NONE: hdr->flags = 0; break; - case WIM_COMPRESSION_TYPE_LZX: + case WIMLIB_COMPRESSION_TYPE_LZX: hdr->flags = WIM_HDR_FLAG_COMPRESSION | WIM_HDR_FLAG_COMPRESS_LZX; break; - case WIM_COMPRESSION_TYPE_XPRESS: + case WIMLIB_COMPRESSION_TYPE_XPRESS: hdr->flags = WIM_HDR_FLAG_COMPRESSION | WIM_HDR_FLAG_COMPRESS_XPRESS; break; diff --git a/src/integrity.c b/src/integrity.c index d7f81eb6..67f21330 100644 --- a/src/integrity.c +++ b/src/integrity.c @@ -222,9 +222,9 @@ out: * If @old_table is non-NULL, the byte after the last byte that was checked * in the old table. Must be less than or equal to new_check_end. * - * @show_progress: - * True if progress information is to be shown while calculating the - * integrity data. + * @progress_func: + * If non-NULL, a progress function that will be called after every + * calculated chunk. * * @integrity_table_ret: * On success, a pointer to the calculated integrity table is written into @@ -236,7 +236,7 @@ static int calculate_integrity_table(FILE *fp, off_t new_check_end, const struct integrity_table *old_table, off_t old_check_end, - bool show_progress, + wimlib_progress_func_t progress_func, struct integrity_table **integrity_table_ret) { int ret = 0; @@ -272,6 +272,18 @@ static int calculate_integrity_table(FILE *fp, new_table->chunk_size = chunk_size; u64 offset = WIM_HEADER_DISK_SIZE; + union wimlib_progress_info progress; + + if (progress_func) { + progress.integrity.total_bytes = new_check_bytes; + progress.integrity.total_chunks = new_num_chunks; + progress.integrity.completed_chunks = 0; + progress.integrity.completed_bytes = 0; + progress.integrity.chunk_size = chunk_size; + progress.integrity.filename = NULL; + progress_func(WIMLIB_PROGRESS_MSG_CALC_INTEGRITY, + &progress); + } for (u32 i = 0; i < new_num_chunks; i++) { size_t this_chunk_size; @@ -279,18 +291,6 @@ static int calculate_integrity_table(FILE *fp, this_chunk_size = new_last_chunk_size; else this_chunk_size = chunk_size; - if (show_progress) { - unsigned percent_done; - u64 checked_bytes = offset - WIM_HEADER_DISK_SIZE; - percent_done = checked_bytes * 100 / new_check_bytes; - printf("\rCalculating integrity checksums for WIM: " - "%"PRIu64" MiB of %"PRIu64" MiB (%u%%) done", - checked_bytes >> 20, - new_check_bytes >> 20, - percent_done); - fflush(stdout); - } - if (old_table && ((this_chunk_size == chunk_size && i < old_num_chunks - 1) || (i == old_num_chunks - 1 && this_chunk_size == old_last_chunk_size))) @@ -306,17 +306,17 @@ static int calculate_integrity_table(FILE *fp, break; } offset += this_chunk_size; + if (progress_func) { + progress.integrity.completed_chunks++; + progress.integrity.completed_bytes += this_chunk_size; + progress_func(WIMLIB_PROGRESS_MSG_CALC_INTEGRITY, + &progress); + } } - if (ret != 0) { - FREE(new_table); - } else { - printf("\rCalculating integrity checksums for WIM: " - "%"PRIu64" MiB of %"PRIu64" MiB (100%%) done\n", - new_check_bytes >> 20, - new_check_bytes >> 20); - fflush(stdout); + if (ret == 0) *integrity_table_ret = new_table; - } + else + FREE(new_table); return ret; } @@ -350,9 +350,9 @@ static int calculate_integrity_table(FILE *fp, * If nonzero, the offset of the byte directly following the old lookup * table in the WIM. * - * @show_progress: - * True if progress information is to be shown while writing the integrity - * table. + * @progress_func + * If non-NULL, a progress function that will be called after every + * calculated chunk. * * Returns: * 0 on success, nonzero on failure. The possible error codes are: @@ -364,7 +364,7 @@ int write_integrity_table(FILE *fp, struct resource_entry *integrity_res_entry, off_t new_lookup_table_end, off_t old_lookup_table_end, - bool show_progress) + wimlib_progress_func_t progress_func) { struct integrity_table *old_table; struct integrity_table *new_table; @@ -397,7 +397,7 @@ int write_integrity_table(FILE *fp, ret = calculate_integrity_table(fp, new_lookup_table_end, old_table, old_lookup_table_end, - show_progress, &new_table); + progress_func, &new_table); if (ret != 0) goto out_free_old_table; @@ -444,9 +444,9 @@ out_free_old_table: * Number of bytes in the WIM that need to be checked (offset of end of the * lookup table minus offset of end of the header). * - * @show_progress: - * True if progress information is to be shown while checking the - * integrity. + * @progress_func + * If non-NULL, a progress function that will be called after every + * verified chunk. * * Returns: * > 0 (WIMLIB_ERR_*) on error @@ -454,12 +454,26 @@ out_free_old_table: * were no inconsistencies. * -1 (WIM_INTEGRITY_NOT_OK) if the WIM failed the integrity check. */ -static int verify_integrity(FILE *fp, const struct integrity_table *table, - u64 bytes_to_check, bool show_progress) +static int verify_integrity(FILE *fp, const char *filename, + const struct integrity_table *table, + u64 bytes_to_check, + wimlib_progress_func_t progress_func) { int ret; u64 offset = WIM_HEADER_DISK_SIZE; u8 sha1_md[SHA1_HASH_SIZE]; + union wimlib_progress_info progress; + + if (progress_func) { + progress.integrity.total_bytes = bytes_to_check; + progress.integrity.total_chunks = table->num_entries; + progress.integrity.completed_chunks = 0; + progress.integrity.completed_bytes = 0; + progress.integrity.chunk_size = table->chunk_size; + progress.integrity.filename = filename; + progress_func(WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY, + &progress); + } for (u32 i = 0; i < table->num_entries; i++) { size_t this_chunk_size; if (i == table->num_entries - 1) @@ -475,23 +489,14 @@ static int verify_integrity(FILE *fp, const struct integrity_table *table, if (!hashes_equal(sha1_md, table->sha1sums[i])) return WIM_INTEGRITY_NOT_OK; - if (show_progress) { - u64 checked_bytes = offset - WIM_HEADER_DISK_SIZE; - unsigned percent_done = checked_bytes * 100 / bytes_to_check; - printf("\rVerifying integrity of WIM: " - "%"PRIu64" MiB of %"PRIu64" MiB (%u%%) done", - checked_bytes >> 20, - bytes_to_check >> 20, - percent_done); - fflush(stdout); - } offset += this_chunk_size; + if (progress_func) { + progress.integrity.completed_chunks++; + progress.integrity.completed_bytes += this_chunk_size; + progress_func(WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY, + &progress); + } } - printf("\rVerifying integrity of WIM: " - "%"PRIu64" MiB of %"PRIu64" MiB (100%%) done\n", - bytes_to_check >> 20, - bytes_to_check >> 20); - fflush(stdout); return WIM_INTEGRITY_OK; } @@ -504,9 +509,9 @@ static int verify_integrity(FILE *fp, const struct integrity_table *table, * @w: * The WIM, opened for reading, and with the header already read. * - * @show_progress: - * True if progress information is to be shown while checking the - * integrity. + * @progress_func + * If non-NULL, a progress function that will be called after every + * verified chunk. * * Returns: * > 0 (WIMLIB_ERR_*) on error @@ -516,7 +521,7 @@ static int verify_integrity(FILE *fp, const struct integrity_table *table, * -2 (WIM_INTEGRITY_NONEXISTENT) if the WIM contains no integrity * information. */ -int check_wim_integrity(WIMStruct *w, bool show_progress) +int check_wim_integrity(WIMStruct *w, wimlib_progress_func_t progress_func) { int ret; u64 bytes_to_check; @@ -542,7 +547,8 @@ int check_wim_integrity(WIMStruct *w, bool show_progress) bytes_to_check, &table); if (ret != 0) return ret; - ret = verify_integrity(w->fp, table, bytes_to_check, show_progress); + ret = verify_integrity(w->fp, w->filename, table, + bytes_to_check, progress_func); FREE(table); return ret; } diff --git a/src/join.c b/src/join.c index 222c9a9a..34e5df91 100644 --- a/src/join.c +++ b/src/join.c @@ -28,29 +28,18 @@ #include "xml.h" #include -static int copy_lte_to_table(struct lookup_table_entry *lte, void *table) +static int move_lte_to_table(struct lookup_table_entry *lte, + void *other_tab) { - struct lookup_table_entry *copy; - copy = MALLOC(sizeof(struct lookup_table_entry)); - if (!copy) - return WIMLIB_ERR_NOMEM; - memcpy(copy, lte, sizeof(struct lookup_table_entry)); - lookup_table_insert(table, copy); + hlist_del(<e->hash_list); + lookup_table_insert((struct lookup_table*)other_tab, lte); return 0; } static int lookup_table_join(struct lookup_table *table, struct lookup_table *new) { - return for_lookup_table_entry(new, copy_lte_to_table, table); -} - - -static int cmp_swms_by_part_number(const void *swm1, const void *swm2) -{ - u16 partno_1 = (*(WIMStruct**)swm1)->hdr.part_number; - u16 partno_2 = (*(WIMStruct**)swm2)->hdr.part_number; - return (int)partno_1 - (int)partno_2; + return for_lookup_table_entry(new, move_lte_to_table, table); } /* @@ -158,7 +147,9 @@ int verify_swm_set(WIMStruct *w, WIMStruct **additional_swms, * Joins lookup tables from the parts of a split WIM. * * @w specifies the first part, while @additional_swms and @num_additional_swms - * specify an array of points to the WIMStruct's for additional split WIM parts. + * specify an array of pointers to the WIMStruct's for additional split WIM parts. + * + * The lookup table entries are *moved* to the new table. * * On success, 0 is returned on a pointer to the joined lookup table is returned * in @table_ret. @@ -178,13 +169,13 @@ int new_joined_lookup_table(WIMStruct *w, int ret; unsigned i; - table = new_lookup_table(9001); if (!table) return WIMLIB_ERR_NOMEM; - ret = lookup_table_join(table, w->lookup_table); - if (ret != 0) - goto out_free_table; + + if (w) + lookup_table_join(table, w->lookup_table); + for (i = 0; i < num_additional_swms; i++) { ret = lookup_table_join(table, additional_swms[i]->lookup_table); if (ret != 0) @@ -198,111 +189,106 @@ out_free_table: } -static int join_wims(WIMStruct **swms, uint num_swms, WIMStruct *joined_wim, - int write_flags) +static int join_wims(WIMStruct **swms, unsigned num_swms, + WIMStruct *joined_wim, int write_flags, + wimlib_progress_func_t progress_func) { - uint i; int ret; - FILE *out_fp = joined_wim->out_fp; - u64 total_bytes = wim_info_get_total_bytes(swms[0]->wim_info); + unsigned i; + union wimlib_progress_info progress; + u64 total_bytes = 0; + u64 part_bytes; + u64 swm_part_sizes[num_swms]; - swms[0]->write_metadata = false; + /* Calculate total size of the streams in the split WIM parts. */ + for (i = 0; i < num_swms; i++) { + part_bytes = lookup_table_total_stream_size(swms[i]->lookup_table); + swm_part_sizes[i] = part_bytes; + total_bytes += part_bytes; + } + + if (progress_func) { + progress.join.total_bytes = total_bytes; + progress.join.total_parts = swms[0]->hdr.total_parts; + progress.join.completed_bytes = 0; + progress.join.completed_parts = 0; + progress_func(WIMLIB_PROGRESS_MSG_JOIN_STREAMS, &progress); + } + + /* Write the resources (streams and metadata resources) from each SWM + * part */ + swms[0]->write_metadata = true; for (i = 0; i < num_swms; i++) { - if (write_flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS) { - off_t cur_offset = ftello(out_fp); - printf("Writing resources from part %u of %u " - "(%"PRIu64" of %"PRIu64" bytes, %.0f%% done)\n", - i + 1, num_swms, cur_offset, total_bytes, - (double)cur_offset / total_bytes * 100.0); - } swms[i]->fp = fopen(swms[i]->filename, "rb"); if (!swms[i]->fp) { ERROR_WITH_ERRNO("Failed to reopen `%s'", swms[i]->filename); return WIMLIB_ERR_OPEN; } - swms[i]->out_fp = out_fp; + swms[i]->out_fp = joined_wim->out_fp; swms[i]->hdr.part_number = 1; ret = for_lookup_table_entry(swms[i]->lookup_table, copy_resource, swms[i]); - if (ret != 0) - return ret; - if (i != 0) { - fclose(swms[i]->fp); - swms[i]->fp = NULL; - } - } - swms[0]->write_metadata = true; - if (write_flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS) - printf("Writing %d metadata resources\n", - swms[0]->hdr.image_count); + swms[i]->out_fp = NULL; + fclose(swms[i]->fp); + swms[i]->fp = NULL; - for (i = 0; i < swms[0]->hdr.image_count; i++) { - ret = copy_resource(swms[0]->image_metadata[i].metadata_lte, - swms[0]); if (ret != 0) return ret; - } - off_t lookup_table_offset = ftello(out_fp); - - if (write_flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS) - printf("Writing lookup tables, XML data, and header\n"); - /* Now write the lookup table for the joined wim. Since the lookup - * table has no header, we can just concatenate the lookup tables of all - * the SWM parts. */ - for (i = 0; i < num_swms; i++) { - ret = for_lookup_table_entry(swms[i]->lookup_table, - write_lookup_table_entry, - out_fp); - if (ret != 0) - return ret; + if (progress_func) { + progress.join.completed_bytes += swm_part_sizes[i]; + progress.join.completed_parts++; + progress_func(WIMLIB_PROGRESS_MSG_JOIN_STREAMS, &progress); + } } - off_t xml_data_offset = ftello(out_fp); - if (lookup_table_offset == -1 || xml_data_offset == -1) { - ERROR_WITH_ERRNO("Failed to get file offset"); - return WIMLIB_ERR_WRITE; - } - swms[0]->hdr.lookup_table_res_entry.offset = lookup_table_offset; - swms[0]->hdr.lookup_table_res_entry.size = - xml_data_offset - lookup_table_offset; - swms[0]->hdr.lookup_table_res_entry.original_size = - xml_data_offset - lookup_table_offset; - swms[0]->hdr.lookup_table_res_entry.flags = - WIM_RESHDR_FLAG_METADATA; - - - /* finish_write is called on the first swm, not the joined_wim, because - * the first swm is the one that has the image metadata and XML data - * attached to it. */ - swms[0]->hdr.flags &= ~WIM_HDR_FLAG_SPANNED; - swms[0]->hdr.total_parts = 1; - return finish_write(swms[0], WIM_ALL_IMAGES, - write_flags | WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE); + joined_wim->hdr.image_count = swms[0]->hdr.image_count; + for (i = 0; i < num_swms; i++) + lookup_table_join(joined_wim->lookup_table, swms[i]->lookup_table); + + free_wim_info(joined_wim->wim_info); + joined_wim->wim_info = swms[0]->wim_info; + ret = finish_write(joined_wim, WIMLIB_ALL_IMAGES, write_flags, progress_func); + joined_wim->wim_info = NULL; + return ret; } +static int cmp_swms_by_part_number(const void *swm1, const void *swm2) +{ + u16 partno_1 = (*(const WIMStruct**)swm1)->hdr.part_number; + u16 partno_2 = (*(const WIMStruct**)swm2)->hdr.part_number; + return (int)partno_1 - (int)partno_2; +} +/* + * Join a set of split WIMs into a stand-alone WIM. + */ WIMLIBAPI int wimlib_join(const char **swm_names, unsigned num_swms, - const char *output_path, int flags) + const char *output_path, int swm_open_flags, + int wim_write_flags, + wimlib_progress_func_t progress_func) { int ret; - int write_flags = 0; WIMStruct *joined_wim = NULL; - WIMStruct *swms[num_swms]; + unsigned i; + + swm_open_flags |= WIMLIB_OPEN_FLAG_SPLIT_OK; + wim_write_flags &= WIMLIB_WRITE_MASK_PUBLIC; - if (num_swms < 1) + if (num_swms < 1 || num_swms > 0xffff) return WIMLIB_ERR_INVALID_PARAM; + WIMStruct *swms[num_swms]; ZERO_ARRAY(swms); - for (unsigned i = 0; i < num_swms; i++) { - ret = wimlib_open_wim(swm_names[i], - flags | WIMLIB_OPEN_FLAG_SPLIT_OK, &swms[i]); + for (i = 0; i < num_swms; i++) { + ret = wimlib_open_wim(swm_names[i], swm_open_flags, &swms[i], + progress_func); if (ret != 0) goto out; - /* don't open all the parts at the same time, in case there are + /* Don't open all the parts at the same time, in case there are * a lot of them */ fclose(swms[i]->fp); swms[i]->fp = NULL; @@ -314,29 +300,19 @@ WIMLIBAPI int wimlib_join(const char **swm_names, unsigned num_swms, if (ret != 0) goto out; - joined_wim = new_wim_struct(); - if (!joined_wim) { - ret = WIMLIB_ERR_NOMEM; + ret = wimlib_create_new_wim(wimlib_get_compression_type(swms[0]), + &joined_wim); + if (ret != 0) goto out; - } - if (flags & WIMLIB_OPEN_FLAG_CHECK_INTEGRITY) - write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; - if (flags & WIMLIB_OPEN_FLAG_SHOW_PROGRESS) - write_flags |= WIMLIB_WRITE_FLAG_SHOW_PROGRESS; - - ret = begin_write(joined_wim, output_path, write_flags); + ret = begin_write(joined_wim, output_path, wim_write_flags); if (ret != 0) goto out; - ret = join_wims(swms, num_swms, joined_wim, write_flags); + ret = join_wims(swms, num_swms, joined_wim, wim_write_flags, + progress_func); out: - /* out_fp is the same in all the swms and joined_wim. And it was - * already closed in the call to finish_write(). */ - for (unsigned i = 0; i < num_swms; i++) { - swms[i]->out_fp = NULL; + for (i = 0; i < num_swms; i++) wimlib_free(swms[i]); - } - joined_wim->out_fp = NULL; wimlib_free(joined_wim); return ret; } diff --git a/src/lookup_table.c b/src/lookup_table.c index d1be89b8..364f469e 100644 --- a/src/lookup_table.c +++ b/src/lookup_table.c @@ -626,7 +626,7 @@ out: } #endif -static void inode_resolve_ltes(struct inode *inode, struct lookup_table *table) +void inode_resolve_ltes(struct inode *inode, struct lookup_table *table) { struct lookup_table_entry *lte; @@ -704,7 +704,7 @@ int dentry_unresolve_ltes(struct dentry *dentry, void *ignore) */ struct lookup_table_entry * inode_unnamed_lte(const struct inode *inode, - const struct lookup_table *table) + const struct lookup_table *table) { if (inode->resolved) return inode_unnamed_lte_resolved(inode); @@ -712,3 +712,16 @@ inode_unnamed_lte(const struct inode *inode, return inode_unnamed_lte_unresolved(inode, table); } +static int lte_add_stream_size(struct lookup_table_entry *lte, + void *total_bytes_p) +{ + *(u64*)total_bytes_p += lte->resource_entry.size; + return 0; +} + +u64 lookup_table_total_stream_size(struct lookup_table *table) +{ + u64 total_size = 0; + for_lookup_table_entry(table, lte_add_stream_size, &total_size); + return total_size; +} diff --git a/src/lookup_table.h b/src/lookup_table.h index 4458cc7e..592116bc 100644 --- a/src/lookup_table.h +++ b/src/lookup_table.h @@ -185,24 +185,22 @@ struct lookup_table_entry { struct resource_entry output_resource_entry; struct list_head msg_list; - struct list_head dentry_list; + struct list_head inode_list; }; - union { - /* This field is used for the special hardlink or symlink image - * extraction mode. In these mode, all identical files are linked - * together, and @extracted_file will be set to the filename of the - * first extracted file containing this stream. */ - struct { - char *extracted_file; - bool extracted; - }; - - /* List of lookup table entries that correspond to streams that have - * been extracted to the staging directory when modifying a read-write - * mounted WIM. */ - struct list_head staging_list; - }; + /* List of lookup table entries that correspond to streams that have + * been extracted to the staging directory when modifying a read-write + * mounted WIM. + * + * This field is also used to make other lists of lookup table entries. + * */ + struct list_head staging_list; + + /* This field is used for the special hardlink or symlink image + * extraction mode. In these mode, all identical files are linked + * together, and @extracted_file will be set to the filename of the + * first extracted file containing this stream. */ + char *extracted_file; }; static inline u64 wim_resource_size(const struct lookup_table_entry *lte) @@ -230,7 +228,7 @@ wim_resource_compression_type(const struct lookup_table_entry *lte) { if (!(lte->resource_entry.flags & WIM_RESHDR_FLAG_COMPRESSED) || lte->resource_location != RESOURCE_IN_WIM) - return WIM_COMPRESSION_TYPE_NONE; + return WIMLIB_COMPRESSION_TYPE_NONE; return wimlib_get_compression_type(lte->wim); } @@ -286,6 +284,9 @@ extern int write_lookup_table_entry(struct lookup_table_entry *lte, void *__out) extern void free_lookup_table_entry(struct lookup_table_entry *lte); +extern void inode_resolve_ltes(struct inode *inode, + struct lookup_table *table); + extern int dentry_resolve_ltes(struct dentry *dentry, void *__table); extern int dentry_unresolve_ltes(struct dentry *dentry, void *ignore); @@ -418,5 +419,7 @@ extern struct lookup_table_entry * inode_unnamed_lte(const struct inode *inode, const struct lookup_table *table); +extern u64 lookup_table_total_stream_size(struct lookup_table *table); + #endif diff --git a/src/modify.c b/src/modify.c index 3180c507..a8ce13ca 100644 --- a/src/modify.c +++ b/src/modify.c @@ -482,9 +482,10 @@ WIMLIBAPI int wimlib_export_image(WIMStruct *src_wim, WIMStruct *dest_wim, const char *dest_name, const char *dest_description, - int flags, + int export_flags, WIMStruct **additional_swms, - unsigned num_additional_swms) + unsigned num_additional_swms, + wimlib_progress_func_t progress_func) { int i; int ret; @@ -502,12 +503,12 @@ WIMLIBAPI int wimlib_export_image(WIMStruct *src_wim, return WIMLIB_ERR_SPLIT_UNSUPPORTED; } - if (src_image == WIM_ALL_IMAGES) { + if (src_image == WIMLIB_ALL_IMAGES) { if (src_wim->hdr.image_count > 1) { /* multi-image export. */ - if ((flags & WIMLIB_EXPORT_FLAG_BOOT) && + if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) && (src_wim->hdr.boot_idx == 0)) { /* Specifying the boot flag on a multi-image @@ -527,16 +528,17 @@ WIMLIBAPI int wimlib_export_image(WIMStruct *src_wim, return WIMLIB_ERR_INVALID_PARAM; } for (i = 1; i <= src_wim->hdr.image_count; i++) { - int export_flags = flags; + int new_flags = export_flags; if (i != src_wim->hdr.boot_idx) - export_flags &= ~WIMLIB_EXPORT_FLAG_BOOT; + new_flags &= ~WIMLIB_EXPORT_FLAG_BOOT; ret = wimlib_export_image(src_wim, i, dest_wim, NULL, NULL, - export_flags, + new_flags, additional_swms, - num_additional_swms); + num_additional_swms, + progress_func); if (ret != 0) return ret; } @@ -624,9 +626,9 @@ WIMLIBAPI int wimlib_export_image(WIMStruct *src_wim, for_dentry_in_tree(root, add_lte_to_dest_wim, &wims); wimlib_assert(list_empty(&wims.lte_list_head)); - if (flags & WIMLIB_EXPORT_FLAG_BOOT) { + if (export_flags & WIMLIB_EXPORT_FLAG_BOOT) { DEBUG("Setting boot_idx to %d", dest_wim->hdr.image_count); - dest_wim->hdr.boot_idx = dest_wim->hdr.image_count; + wimlib_set_boot_idx(dest_wim, dest_wim->hdr.image_count); } ret = 0; goto out; @@ -661,7 +663,7 @@ WIMLIBAPI int wimlib_delete_image(WIMStruct *w, int image) return WIMLIB_ERR_SPLIT_UNSUPPORTED; } - if (image == WIM_ALL_IMAGES) { + if (image == WIMLIB_ALL_IMAGES) { for (i = w->hdr.image_count; i >= 1; i--) { ret = wimlib_delete_image(w, i); if (ret != 0) @@ -699,7 +701,7 @@ WIMLIBAPI int wimlib_delete_image(WIMStruct *w, int image) else if (w->hdr.boot_idx > image) w->hdr.boot_idx--; - w->current_image = WIM_NO_IMAGE; + w->current_image = WIMLIB_NO_IMAGE; /* Remove the image from the XML information. */ xml_delete_image(&w->wim_info, image); @@ -942,30 +944,18 @@ bool exclude_path(const char *path, const struct capture_config *config, } - - -/* - * Adds an image to the WIM, delegating the capture of the dentry tree and - * security data to the function @capture_tree passed as a parameter. - * Currently, @capture_tree may be build_dentry_tree() for capturing a "regular" - * directory tree on disk, or build_dentry_tree_ntfs() for capturing a WIM image - * directory from a NTFS volume using libntfs-3g. - * - * The @capture_tree function is also expected to create lookup table entries - * for all the file streams it captures and insert them into @lookup_table, - * being careful to look for identical entries that already exist and simply - * increment the reference count for them rather than duplicating the entry. - */ -int do_add_image(WIMStruct *w, const char *dir, const char *name, - const char *config_str, size_t config_len, - int flags, - int (*capture_tree)(struct dentry **, const char *, - struct lookup_table *, - struct wim_security_data *, - const struct capture_config *, - int, void *), - void *extra_arg) +WIMLIBAPI int wimlib_add_image(WIMStruct *w, const char *source, + const char *name, const char *config_str, + size_t config_len, int add_image_flags, + wimlib_progress_func_t progress_func) { + int (*capture_tree)(struct dentry **, const char *, + struct lookup_table *, + struct wim_security_data *, + const struct capture_config *, + int, void *); + void *extra_arg; + struct dentry *root_dentry = NULL; struct wim_security_data *sd; struct capture_config config; @@ -973,13 +963,31 @@ int do_add_image(WIMStruct *w, const char *dir, const char *name, struct hlist_head inode_list; int ret; + if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_NTFS) { +#ifdef WITH_NTFS_3G + if (add_image_flags & (WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE)) { + ERROR("Cannot dereference files when capturing directly from NTFS"); + return WIMLIB_ERR_INVALID_PARAM; + } + capture_tree = build_dentry_tree_ntfs; + extra_arg = &w->ntfs_vol; +#else + ERROR("wimlib was compiled without support for NTFS-3g, so"); + ERROR("we cannot capture a WIM image directly from a NTFS volume"); + return WIMLIB_ERR_UNSUPPORTED; +#endif + } else { + capture_tree = build_dentry_tree; + extra_arg = NULL; + } + DEBUG("Adding dentry tree from directory or NTFS volume `%s'.", dir); if (!name || !*name) { ERROR("Must specify a non-empty string for the image name"); return WIMLIB_ERR_INVALID_PARAM; } - if (!dir) { + if (!source) { ERROR("Must specify the name of a directory or NTFS volume"); return WIMLIB_ERR_INVALID_PARAM; } @@ -1001,7 +1009,7 @@ int do_add_image(WIMStruct *w, const char *dir, const char *name, config_str = default_config; config_len = strlen(default_config); } - ret = init_capture_config(config_str, config_len, dir, &config); + ret = init_capture_config(config_str, config_len, source, &config); if (ret != 0) return ret; print_capture_config(&config); @@ -1016,20 +1024,29 @@ int do_add_image(WIMStruct *w, const char *dir, const char *name, sd->total_length = 8; sd->refcnt = 1; - DEBUG("Building dentry tree."); - if (flags & WIMLIB_ADD_IMAGE_FLAG_SHOW_PROGRESS) { - printf("Scanning `%s'...\n", dir); + if (progress_func) { + union wimlib_progress_info progress; + progress.scan.source = source; + progress_func(WIMLIB_PROGRESS_MSG_SCAN_BEGIN, &progress); } - ret = (*capture_tree)(&root_dentry, dir, w->lookup_table, sd, - &config, flags | WIMLIB_ADD_IMAGE_FLAG_ROOT, + + DEBUG("Building dentry tree."); + ret = (*capture_tree)(&root_dentry, source, w->lookup_table, sd, + &config, add_image_flags | WIMLIB_ADD_IMAGE_FLAG_ROOT, extra_arg); destroy_capture_config(&config); if (ret != 0) { - ERROR("Failed to build dentry tree for `%s'", dir); + ERROR("Failed to build dentry tree for `%s'", source); goto out_free_security_data; } + if (progress_func) { + union wimlib_progress_info progress; + progress.scan.source = source; + progress_func(WIMLIB_PROGRESS_MSG_SCAN_END, &progress); + } + DEBUG("Calculating full paths of dentries."); ret = for_dentry_in_tree(root_dentry, calculate_dentry_full_path, NULL); if (ret != 0) @@ -1059,9 +1076,8 @@ int do_add_image(WIMStruct *w, const char *dir, const char *name, if (ret != 0) goto out_destroy_imd; - if (flags & WIMLIB_ADD_IMAGE_FLAG_BOOT) - w->hdr.boot_idx = w->hdr.image_count; - + if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_BOOT) + wimlib_set_boot_idx(w, w->hdr.image_count); return 0; out_destroy_imd: destroy_image_metadata(&w->image_metadata[w->hdr.image_count - 1], @@ -1076,14 +1092,3 @@ out_destroy_config: destroy_capture_config(&config); return ret; } - -/* - * Adds an image to a WIM file from a directory tree on disk. - */ -WIMLIBAPI int wimlib_add_image(WIMStruct *w, const char *dir, - const char *name, const char *config_str, - size_t config_len, int flags) -{ - return do_add_image(w, dir, name, config_str, config_len, flags, - build_dentry_tree, NULL); -} diff --git a/src/mount.c b/src/mount.c index 15d7444e..67a6eddb 100644 --- a/src/mount.c +++ b/src/mount.c @@ -896,7 +896,7 @@ static int rebuild_wim(struct wimfs_context *ctx, bool check_integrity) xml_update_image_info(w, w->current_image); - ret = wimlib_overwrite(w, check_integrity, 0); + ret = wimlib_overwrite(w, check_integrity, 0, NULL); if (ret != 0) { ERROR("Failed to commit changes"); return ret; @@ -1853,10 +1853,10 @@ static struct fuse_operations wimfs_operations = { }; -/* Mounts a WIM file. */ -WIMLIBAPI int wimlib_mount(WIMStruct *wim, int image, const char *dir, - int flags, WIMStruct **additional_swms, - unsigned num_additional_swms) +/* Mounts an image from a WIM file. */ +WIMLIBAPI int wimlib_mount_image(WIMStruct *wim, int image, const char *dir, + int mount_flags, WIMStruct **additional_swms, + unsigned num_additional_swms) { int argc = 0; char *argv[16]; @@ -1867,7 +1867,7 @@ WIMLIBAPI int wimlib_mount(WIMStruct *wim, int image, const char *dir, struct wimfs_context ctx; DEBUG("Mount: wim = %p, image = %d, dir = %s, flags = %d, ", - wim, image, dir, flags); + wim, image, dir, mount_flags); if (!wim || !dir) return WIMLIB_ERR_INVALID_PARAM; @@ -1876,7 +1876,7 @@ WIMLIBAPI int wimlib_mount(WIMStruct *wim, int image, const char *dir, if (ret != 0) return ret; - if ((flags & WIMLIB_MOUNT_FLAG_READWRITE) && (wim->hdr.total_parts != 1)) { + if ((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) && (wim->hdr.total_parts != 1)) { ERROR("Cannot mount a split WIM read-write"); return WIMLIB_ERR_SPLIT_UNSUPPORTED; } @@ -1914,10 +1914,10 @@ WIMLIBAPI int wimlib_mount(WIMStruct *wim, int image, const char *dir, goto out; } - if (!(flags & (WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE | + if (!(mount_flags & (WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE | WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR | WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS))) - flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR; + mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR; DEBUG("Initializing struct wimfs_context"); @@ -1947,10 +1947,10 @@ WIMLIBAPI int wimlib_mount(WIMStruct *wim, int image, const char *dir, argv[argc++] = dir_copy; /* disable multi-threaded operation for read-write mounts */ - if (flags & WIMLIB_MOUNT_FLAG_READWRITE) + if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) argv[argc++] = "-s"; - if (flags & WIMLIB_MOUNT_FLAG_DEBUG) + if (mount_flags & WIMLIB_MOUNT_FLAG_DEBUG) argv[argc++] = "-d"; /* @@ -1962,7 +1962,7 @@ WIMLIBAPI int wimlib_mount(WIMStruct *wim, int image, const char *dir, char optstring[256] = "use_ino,subtype=wimfs,attr_timeout=0"; argv[argc++] = "-o"; argv[argc++] = optstring; - if ((flags & WIMLIB_MOUNT_FLAG_READWRITE)) { + if ((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)) { /* Read-write mount. Make the staging directory */ ret = make_staging_dir(&ctx); if (ret != 0) @@ -1987,7 +1987,7 @@ WIMLIBAPI int wimlib_mount(WIMStruct *wim, int image, const char *dir, #endif /* Mark dentry tree as modified if read-write mount. */ - if (flags & WIMLIB_MOUNT_FLAG_READWRITE) { + if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) { imd->modified = true; imd->has_been_mounted_rw = true; } @@ -2002,7 +2002,7 @@ WIMLIBAPI int wimlib_mount(WIMStruct *wim, int image, const char *dir, /* Finish initializing the filesystem context. */ ctx.wim = wim; - ctx.mount_flags = flags; + ctx.mount_flags = mount_flags; DEBUG("Calling fuse_main()"); @@ -2029,9 +2029,9 @@ out: /* * Unmounts the WIM file that was previously mounted on @dir by using - * wimlib_mount(). + * wimlib_mount_image(). */ -WIMLIBAPI int wimlib_unmount(const char *dir, int flags) +WIMLIBAPI int wimlib_unmount_image(const char *dir, int unmount_flags) { pid_t pid; int status; @@ -2057,8 +2057,8 @@ WIMLIBAPI int wimlib_unmount(const char *dir, int flags) /* Send a message to the filesystem daemon saying whether to commit or * not. */ - msg[0] = (flags & WIMLIB_UNMOUNT_FLAG_COMMIT) ? 1 : 0; - msg[1] = (flags & WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY) ? 1 : 0; + msg[0] = (unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) ? 1 : 0; + msg[1] = (unmount_flags & WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY) ? 1 : 0; DEBUG("Sending message: %scommit, %scheck", (msg[0] ? "" : "don't "), @@ -2208,14 +2208,14 @@ static inline int mount_unsupported_error() return WIMLIB_ERR_UNSUPPORTED; } -WIMLIBAPI int wimlib_unmount(const char *dir, int flags) +WIMLIBAPI int wimlib_unmount_image(const char *dir, int unmount_flags) { return mount_unsupported_error(); } -WIMLIBAPI int wimlib_mount(WIMStruct *wim_p, int image, const char *dir, - int flags, WIMStruct **additional_swms, - unsigned num_additional_swms) +WIMLIBAPI int wimlib_mount_image(WIMStruct *wim_p, int image, const char *dir, + int mount_flags, WIMStruct **additional_swms, + unsigned num_additional_swms) { return mount_unsupported_error(); } diff --git a/src/ntfs-apply.c b/src/ntfs-apply.c index 32d98108..61199b81 100644 --- a/src/ntfs-apply.c +++ b/src/ntfs-apply.c @@ -27,15 +27,11 @@ #include "config.h" -#ifdef WITH_NTFS_3G + #include #include -#endif #include "wimlib_internal.h" - - -#ifdef WITH_NTFS_3G #include "dentry.h" #include "lookup_table.h" #include "io.h" @@ -48,12 +44,6 @@ #include #include -struct ntfs_apply_args { - ntfs_volume *vol; - int extract_flags; - WIMStruct *w; -}; - /* * Extracts a WIM resource to a NTFS attribute. */ @@ -98,12 +88,11 @@ extract_wim_resource_to_ntfs_attr(const struct lookup_table_entry *lte, * * @ni: The NTFS inode for the file. * @inode: The WIM dentry that has an inode containing the streams. - * @w: The WIMStruct for the WIM containing the image we are applying. * * Returns 0 on success, nonzero on failure. */ static int write_ntfs_data_streams(ntfs_inode *ni, const struct dentry *dentry, - WIMStruct *w) + struct apply_args *args) { int ret = 0; unsigned stream_idx = 0; @@ -120,7 +109,7 @@ static int write_ntfs_data_streams(ntfs_inode *ni, const struct dentry *dentry, struct lookup_table_entry *lte; ntfs_attr *na; - lte = inode_stream_lte(inode, stream_idx, w->lookup_table); + lte = inode_stream_lte_resolved(inode, stream_idx); if (stream_name_len) { /* Create an empty named stream. */ @@ -151,6 +140,7 @@ static int write_ntfs_data_streams(ntfs_inode *ni, const struct dentry *dentry, ret = extract_wim_resource_to_ntfs_attr(lte, na); if (ret != 0) break; + args->progress.extract.completed_bytes += wim_resource_size(lte); ntfs_attr_close(na); } if (stream_idx == inode->num_ads) @@ -281,14 +271,12 @@ apply_file_attributes_and_security_data(ntfs_inode *ni, } static int apply_reparse_data(ntfs_inode *ni, const struct dentry *dentry, - const WIMStruct *w) + struct apply_args *args) { struct lookup_table_entry *lte; int ret = 0; - wimlib_assert(dentry->d_inode->attributes & FILE_ATTRIBUTE_REPARSE_POINT); - - lte = inode_unnamed_lte(dentry->d_inode, w->lookup_table); + lte = inode_unnamed_lte_resolved(dentry->d_inode); DEBUG("Applying reparse data to `%s'", dentry->full_path_utf8); @@ -321,11 +309,12 @@ static int apply_reparse_data(ntfs_inode *ni, const struct dentry *dentry, dentry->full_path_utf8); return WIMLIB_ERR_NTFS_3G; } + args->progress.extract.completed_bytes += wim_resource_size(lte); return 0; } static int do_wim_apply_dentry_ntfs(struct dentry *dentry, ntfs_inode *dir_ni, - WIMStruct *w); + struct apply_args *args); /* * If @dentry is part of a hard link group, search for hard-linked dentries in @@ -337,7 +326,7 @@ static int do_wim_apply_dentry_ntfs(struct dentry *dentry, ntfs_inode *dir_ni, */ static int preapply_dentry_with_dos_name(struct dentry *dentry, ntfs_inode **dir_ni_p, - WIMStruct *w) + struct apply_args *args) { struct dentry *other; struct dentry *dentry_with_dos_name; @@ -357,7 +346,7 @@ static int preapply_dentry_with_dos_name(struct dentry *dentry, } } /* If there's a dentry with a DOS name, extract it first */ - if (dentry_with_dos_name && !dentry_is_extracted(dentry)) { + if (dentry_with_dos_name && !dentry_with_dos_name->is_extracted) { char *p; const char *dir_name; char orig; @@ -367,7 +356,7 @@ static int preapply_dentry_with_dos_name(struct dentry *dentry, DEBUG("pre-applying DOS name `%s'", dentry_with_dos_name->full_path_utf8); ret = do_wim_apply_dentry_ntfs(dentry_with_dos_name, - *dir_ni_p, w); + *dir_ni_p, args); if (ret != 0) return ret; p = dentry->full_path_utf8 + dentry->full_path_utf8_len; @@ -395,12 +384,11 @@ static int preapply_dentry_with_dos_name(struct dentry *dentry, * * @dentry: The WIM dentry to apply * @dir_ni: The NTFS inode for the parent directory - * @w: The WIMStruct for the WIM containing the image we are applying. * * @return: 0 on success; nonzero on failure. */ static int do_wim_apply_dentry_ntfs(struct dentry *dentry, ntfs_inode *dir_ni, - WIMStruct *w) + struct apply_args *args) { int ret = 0; mode_t type; @@ -417,7 +405,7 @@ static int do_wim_apply_dentry_ntfs(struct dentry *dentry, ntfs_inode *dir_ni, * (if there is one) before this dentry */ if (dentry->short_name_len == 0) { ret = preapply_dentry_with_dos_name(dentry, - &dir_ni, w); + &dir_ni, args); if (ret != 0) return ret; } @@ -467,18 +455,19 @@ static int do_wim_apply_dentry_ntfs(struct dentry *dentry, ntfs_inode *dir_ni, * */ if (!(inode->attributes & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY))) { - ret = write_ntfs_data_streams(ni, dentry, w); + ret = write_ntfs_data_streams(ni, dentry, args); if (ret != 0) goto out_close_dir_ni; } - ret = apply_file_attributes_and_security_data(ni, dir_ni, dentry, w); + ret = apply_file_attributes_and_security_data(ni, dir_ni, + dentry, args->w); if (ret != 0) goto out_close_dir_ni; if (inode->attributes & FILE_ATTR_REPARSE_POINT) { - ret = apply_reparse_data(ni, dentry, w); + ret = apply_reparse_data(ni, dentry, args); if (ret != 0) goto out_close_dir_ni; } @@ -593,9 +582,9 @@ static int wim_apply_root_dentry_ntfs(const struct dentry *dentry, } /* Applies a WIM dentry to the NTFS volume */ -static int wim_apply_dentry_ntfs(struct dentry *dentry, void *arg) +int wim_apply_dentry_ntfs(struct dentry *dentry, void *arg) { - struct ntfs_apply_args *args = arg; + struct apply_args *args = arg; ntfs_volume *vol = args->vol; int extract_flags = args->extract_flags; WIMStruct *w = args->w; @@ -604,10 +593,12 @@ static int wim_apply_dentry_ntfs(struct dentry *dentry, void *arg) char orig; const char *dir_name; - if (dentry_is_extracted(dentry)) + if (dentry->is_extracted) return 0; - wimlib_assert(dentry->full_path_utf8); + if (extract_flags & WIMLIB_EXTRACT_FLAG_NO_STREAMS) + if (inode_unnamed_lte_resolved(dentry->d_inode)) + return 0; DEBUG("Applying dentry `%s' to NTFS", dentry->full_path_utf8); @@ -633,19 +624,18 @@ static int wim_apply_dentry_ntfs(struct dentry *dentry, void *arg) dir_name); return WIMLIB_ERR_NTFS_3G; } - return do_wim_apply_dentry_ntfs(dentry, dir_ni, w); + return do_wim_apply_dentry_ntfs(dentry, dir_ni, arg); } -static int wim_apply_dentry_timestamps(struct dentry *dentry, void *arg) +int wim_apply_dentry_timestamps(struct dentry *dentry, void *arg) { - struct ntfs_apply_args *args = arg; - ntfs_volume *vol = args->vol; + struct apply_args *args = arg; + ntfs_volume *vol = args->vol; u8 *p; u8 buf[24]; ntfs_inode *ni; int ret = 0; - DEBUG("Setting timestamps on `%s'", dentry->full_path_utf8); ni = ntfs_pathname_to_inode(vol, NULL, dentry->full_path_utf8); @@ -674,126 +664,3 @@ static int wim_apply_dentry_timestamps(struct dentry *dentry, void *arg) } return ret; } - -static int dentry_set_unextracted(struct dentry *dentry, void *ignore) -{ - dentry->is_extracted = false; - return 0; -} - -static int do_wim_apply_image_ntfs(WIMStruct *w, const char *device, int extract_flags) -{ - ntfs_volume *vol; - int ret; - struct dentry *root; - struct ntfs_apply_args args; - - DEBUG("Mounting NTFS volume `%s'", device); - vol = ntfs_mount(device, 0); - if (!vol) { - ERROR_WITH_ERRNO("Failed to mount NTFS volume `%s'", device); - return WIMLIB_ERR_NTFS_3G; - } - args.vol = vol; - args.extract_flags = extract_flags; - args.w = w; - root = wim_root_dentry(w); - - for_dentry_in_tree(root, dentry_set_unextracted, NULL); - ret = for_dentry_in_tree(root, wim_apply_dentry_ntfs, &args); - if (ret != 0) - goto out; - - if (extract_flags & WIMLIB_EXTRACT_FLAG_VERBOSE) - printf("Setting timestamps of extracted files on NTFS " - "volume `%s'\n", device); - ret = for_dentry_in_tree_depth(root, wim_apply_dentry_timestamps, - &args); - - if (ret == 0 && (extract_flags & WIMLIB_EXTRACT_FLAG_VERBOSE)) - printf("Finished applying image %d of %s to NTFS " - "volume `%s'\n", - w->current_image, - w->filename ? w->filename : "WIM", - device); -out: - DEBUG("Unmounting NTFS volume `%s'", device); - if (ntfs_umount(vol, FALSE) != 0) { - ERROR_WITH_ERRNO("Failed to unmount NTFS volume `%s'", device); - if (ret == 0) - ret = WIMLIB_ERR_NTFS_3G; - } - return ret; -} - - -/* - * API entry point for applying a WIM image to a NTFS volume. - * - * Please note that this is a NTFS *volume* and not a directory. The intention - * is that the volume contain an empty filesystem, and the WIM image contain a - * full filesystem to be applied to the volume. - */ -WIMLIBAPI int wimlib_apply_image_to_ntfs_volume(WIMStruct *w, int image, - const char *device, int flags, - WIMStruct **additional_swms, - unsigned num_additional_swms) -{ - struct lookup_table *joined_tab, *w_tab_save; - int ret; - - DEBUG("w->filename = %s, image = %d, device = %s, flags = 0x%x, " - "num_additional_swms = %u", - w->filename, image, device, flags, num_additional_swms); - - if (!w || !device) - return WIMLIB_ERR_INVALID_PARAM; - if (image == WIM_ALL_IMAGES) { - ERROR("Can only apply a single image when applying " - "directly to a NTFS volume"); - return WIMLIB_ERR_INVALID_PARAM; - } - if (flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | WIMLIB_EXTRACT_FLAG_HARDLINK)) { - ERROR("Cannot specify symlink or hardlink flags when applying "); - ERROR("directly to a NTFS volume"); - return WIMLIB_ERR_INVALID_PARAM; - } - - ret = verify_swm_set(w, additional_swms, num_additional_swms); - if (ret != 0) - return ret; - - if (num_additional_swms) { - ret = new_joined_lookup_table(w, additional_swms, - num_additional_swms, &joined_tab); - if (ret != 0) - return ret; - w_tab_save = w->lookup_table; - w->lookup_table = joined_tab; - } - - ret = select_wim_image(w, image); - if (ret != 0) - goto out; - - ret = do_wim_apply_image_ntfs(w, device, flags); - -out: - if (num_additional_swms) { - free_lookup_table(w->lookup_table); - w->lookup_table = w_tab_save; - } - return ret; -} - -#else /* WITH_NTFS_3G */ -WIMLIBAPI int wimlib_apply_image_to_ntfs_volume(WIMStruct *w, int image, - const char *device, int flags, - WIMStruct **additional_swms, - unsigned num_additional_swms) -{ - ERROR("wimlib was compiled without support for NTFS-3g, so"); - ERROR("we cannot apply a WIM image directly to a NTFS volume"); - return WIMLIB_ERR_UNSUPPORTED; -} -#endif /* WITH_NTFS_3G */ diff --git a/src/ntfs-capture.c b/src/ntfs-capture.c index fb670dc0..c4770a5d 100644 --- a/src/ntfs-capture.c +++ b/src/ntfs-capture.c @@ -27,15 +27,12 @@ #include "config.h" -#ifdef WITH_NTFS_3G #include #include -#endif #include "wimlib_internal.h" -#ifdef WITH_NTFS_3G #include "dentry.h" #include "lookup_table.h" #include "io.h" @@ -646,13 +643,13 @@ static int build_dentry_tree_ntfs_recursive(struct dentry **root_p, return ret; } -static int build_dentry_tree_ntfs(struct dentry **root_p, - const char *device, - struct lookup_table *lookup_table, - struct wim_security_data *sd, - const struct capture_config *config, - int flags, - void *extra_arg) +int build_dentry_tree_ntfs(struct dentry **root_p, + const char *device, + struct lookup_table *lookup_table, + struct wim_security_data *sd, + const struct capture_config *config, + int flags, + void *extra_arg) { ntfs_volume *vol; ntfs_inode *root_ni; @@ -721,34 +718,3 @@ out: } return ret; } - - - -WIMLIBAPI int wimlib_add_image_from_ntfs_volume(WIMStruct *w, - const char *device, - const char *name, - const char *config_str, - size_t config_len, - int flags) -{ - if (flags & (WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE)) { - ERROR("Cannot dereference files when capturing directly from NTFS"); - return WIMLIB_ERR_INVALID_PARAM; - } - return do_add_image(w, device, name, config_str, config_len, flags, - build_dentry_tree_ntfs, &w->ntfs_vol); -} - -#else /* WITH_NTFS_3G */ -WIMLIBAPI int wimlib_add_image_from_ntfs_volume(WIMStruct *w, - const char *device, - const char *name, - const char *config_str, - size_t config_len, - int flags) -{ - ERROR("wimlib was compiled without support for NTFS-3g, so"); - ERROR("we cannot capture a WIM image directly from a NTFS volume"); - return WIMLIB_ERR_UNSUPPORTED; -} -#endif /* WITH_NTFS_3G */ diff --git a/src/resource.c b/src/resource.c index c29ed4fb..86a2be6a 100644 --- a/src/resource.c +++ b/src/resource.c @@ -83,7 +83,7 @@ static int read_compressed_resource(FILE *fp, u64 resource_compressed_size, int (*decompress)(const void *, uint, void *, uint); /* Set the appropriate decompress function. */ - if (resource_ctype == WIM_COMPRESSION_TYPE_LZX) + if (resource_ctype == WIMLIB_COMPRESSION_TYPE_LZX) decompress = lzx_decompress; else decompress = xpress_decompress; @@ -518,12 +518,12 @@ int read_wim_resource(const struct lookup_table_entry *lte, u8 buf[], ctype = wim_resource_compression_type(lte); - wimlib_assert(ctype != WIM_COMPRESSION_TYPE_NONE || + wimlib_assert(ctype != WIMLIB_COMPRESSION_TYPE_NONE || (lte->resource_entry.original_size == lte->resource_entry.size)); if ((flags & WIMLIB_RESOURCE_FLAG_RAW) - || ctype == WIM_COMPRESSION_TYPE_NONE) + || ctype == WIMLIB_COMPRESSION_TYPE_NONE) ret = read_uncompressed_resource(fp, lte->resource_entry.offset + offset, size, buf); @@ -694,9 +694,6 @@ int extract_full_wim_resource_to_fd(const struct lookup_table_entry *lte, int fd * * The output_resource_entry, out_refcnt, and part_number fields of @lte are * updated. - * - * Metadata resources are not copied (they are handled elsewhere for joining and - * splitting). */ int copy_resource(struct lookup_table_entry *lte, void *wim) { @@ -744,15 +741,14 @@ int read_metadata_resource(WIMStruct *w, struct image_metadata *imd) struct inode_table inode_tab; const struct lookup_table_entry *metadata_lte; u64 metadata_len; - u64 metadata_offset; struct hlist_head inode_list; metadata_lte = imd->metadata_lte; metadata_len = wim_resource_size(metadata_lte); - metadata_offset = metadata_lte->resource_entry.offset; DEBUG("Reading metadata resource: length = %"PRIu64", " - "offset = %"PRIu64"", metadata_len, metadata_offset); + "offset = %"PRIu64"", metadata_len, + metadata_lte->resource_entry.offset); /* There is no way the metadata resource could possibly be less than (8 * + WIM_DENTRY_DISK_SIZE) bytes, where the 8 is for security data (with diff --git a/src/split.c b/src/split.c index df384f82..091fa3fe 100644 --- a/src/split.c +++ b/src/split.c @@ -33,25 +33,22 @@ struct split_args { char *swm_base_name; size_t swm_base_name_len; const char *swm_suffix; - struct list_head *lte_list; - int part_number; + struct list_head lte_list; + int cur_part_number; int write_flags; long size_remaining; size_t part_size; - u64 total_bytes; - u64 total_bytes_written; + wimlib_progress_func_t progress_func; + union wimlib_progress_info progress; }; static int finish_swm(WIMStruct *w, struct list_head *lte_list, - int write_flags) + int write_flags, wimlib_progress_func_t progress_func) { off_t lookup_table_offset = ftello(w->out_fp); int ret; struct lookup_table_entry *lte; - DEBUG("Writing lookup table for SWM (offset %"PRIu64")", - lookup_table_offset); - list_for_each_entry(lte, lte_list, staging_list) { ret = write_lookup_table_entry(lte, w->out_fp); if (ret != 0) @@ -68,8 +65,9 @@ static int finish_swm(WIMStruct *w, struct list_head *lte_list, w->hdr.lookup_table_res_entry.original_size = xml_data_offset - lookup_table_offset; w->hdr.lookup_table_res_entry.flags = WIM_RESHDR_FLAG_METADATA; - return finish_write(w, WIM_ALL_IMAGES, - write_flags | WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE); + return finish_write(w, WIMLIB_ALL_IMAGES, + write_flags | WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE, + progress_func); } static int copy_resource_to_swm(struct lookup_table_entry *lte, void *__args) @@ -88,24 +86,29 @@ static int copy_resource_to_swm(struct lookup_table_entry *lte, void *__args) /* No space for this resource. Finish the previous swm and * start a new one. */ - ret = finish_swm(w, args->lte_list, args->write_flags); + ret = finish_swm(w, &args->lte_list, args->write_flags, + args->progress_func); if (ret != 0) return ret; - INIT_LIST_HEAD(args->lte_list); + + if (args->progress_func) { + args->progress_func(WIMLIB_PROGRESS_MSG_SPLIT_END_PART, + &args->progress); + } + + INIT_LIST_HEAD(&args->lte_list); + args->cur_part_number++; sprintf(args->swm_base_name + args->swm_base_name_len, "%d%s", - ++args->part_number, args->swm_suffix); + args->cur_part_number, args->swm_suffix); - w->hdr.part_number = args->part_number; + w->hdr.part_number = args->cur_part_number; - if (args->write_flags & WIMLIB_OPEN_FLAG_SHOW_PROGRESS) - printf("Writing `%s' (%"PRIu64" of %"PRIu64" bytes, " - "%.0f%% done)\n", - args->swm_base_name, - args->total_bytes_written, - args->total_bytes, - (double)args->total_bytes_written / - (double)args->total_bytes * 100.0); + if (args->progress_func) { + args->progress.split.cur_part_number = args->cur_part_number; + args->progress_func(WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART, + &args->progress); + } ret = begin_write(w, args->swm_base_name, args->write_flags); if (ret != 0) @@ -113,119 +116,120 @@ static int copy_resource_to_swm(struct lookup_table_entry *lte, void *__args) args->size_remaining = args->part_size; } args->size_remaining -= lte->resource_entry.size; - args->total_bytes_written += lte->resource_entry.size; - list_add(<e->staging_list, args->lte_list); + args->progress.split.completed_bytes += lte->resource_entry.size; + list_add(<e->staging_list, &args->lte_list); return copy_resource(lte, w); } /* Splits the WIM file @wimfile into multiple parts prefixed by @swm_name with * size at most @part_size. */ -WIMLIBAPI int wimlib_split(const char *wimfile, const char *swm_name, - size_t part_size, int flags) +WIMLIBAPI int wimlib_split(WIMStruct *w, const char *swm_name, + size_t part_size, int write_flags, + wimlib_progress_func_t progress_func) { int ret; - WIMStruct *w; - int write_flags = 0; - size_t swm_name_len = strlen(swm_name); + struct wim_header hdr_save; + struct split_args args; + const char *swm_suffix; + size_t swm_name_len; size_t swm_base_name_len; - char name[swm_name_len + 20]; - char *swm_suffix; - long size_remaining = part_size; - u64 total_bytes_written = 0; - u64 total_bytes; + if (!w || !swm_name || part_size == 0) + return WIMLIB_ERR_INVALID_PARAM; - ret = wimlib_open_wim(wimfile, flags, &w); - if (ret != 0) - return ret; + if (w->hdr.total_parts != 1) + return WIMLIB_ERR_SPLIT_UNSUPPORTED; - total_bytes = wim_info_get_total_bytes(w->wim_info); + write_flags &= WIMLIB_WRITE_MASK_PUBLIC; - if (flags & WIMLIB_OPEN_FLAG_CHECK_INTEGRITY) - write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; - if (flags & WIMLIB_OPEN_FLAG_SHOW_PROGRESS) - write_flags |= WIMLIB_WRITE_FLAG_SHOW_PROGRESS; + swm_name_len = strlen(swm_name); + char swm_base_name[swm_name_len + 20]; + memcpy(&hdr_save, &w->hdr, sizeof(struct wim_header)); w->hdr.flags |= WIM_HDR_FLAG_SPANNED; w->hdr.boot_idx = 0; randomize_byte_array(w->hdr.guid, WIM_GID_LEN); ret = begin_write(w, swm_name, write_flags); if (ret != 0) - return ret; + goto out; + + memcpy(swm_base_name, swm_name, swm_name_len + 1); swm_suffix = strchr(swm_name, '.'); - memcpy(name, swm_name, swm_name_len + 1); if (swm_suffix) { swm_base_name_len = swm_suffix - swm_name; } else { swm_base_name_len = swm_name_len; - name[sizeof(name) - 1] = '\0'; - swm_suffix = &name[sizeof(name) - 1]; + swm_base_name[sizeof(swm_base_name) - 1] = '\0'; + swm_suffix = &swm_base_name[sizeof(swm_base_name) - 1]; } - if (write_flags & WIMLIB_OPEN_FLAG_SHOW_PROGRESS) - printf("Writing `%s' (%.2f %% done)\n", - swm_name, - (double)total_bytes_written / - (double)total_bytes * 100.0); + args.w = w; + args.swm_base_name = swm_base_name; + args.swm_base_name_len = swm_base_name_len; + args.swm_suffix = swm_suffix; + INIT_LIST_HEAD(&args.lte_list); + args.cur_part_number = 1; + args.write_flags = write_flags; + args.size_remaining = part_size; + args.part_size = part_size; + args.progress_func = progress_func; + args.progress.split.total_bytes = lookup_table_total_stream_size(w->lookup_table); + args.progress.split.cur_part_number = 1; + args.progress.split.completed_bytes = 0; + args.progress.split.part_name = swm_base_name; + + if (progress_func) { + progress_func(WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART, + &args.progress); + } w->write_metadata = true; - LIST_HEAD(lte_list); for (int i = 0; i < w->hdr.image_count; i++) { struct lookup_table_entry *metadata_lte; - - DEBUG("Writing metadata resource %d", i); - metadata_lte = w->image_metadata[i].metadata_lte; ret = copy_resource(metadata_lte, w); if (ret != 0) - return ret; - size_remaining -= metadata_lte->resource_entry.size; - total_bytes_written += metadata_lte->resource_entry.size; - list_add(&metadata_lte->staging_list, <e_list); + goto out; + args.size_remaining -= metadata_lte->resource_entry.size; + args.progress.split.completed_bytes += metadata_lte->resource_entry.size; + list_add(&metadata_lte->staging_list, &args.lte_list); } w->write_metadata = false; - struct split_args args = { - .w = w, - .swm_base_name = name, - .swm_base_name_len = swm_base_name_len, - .swm_suffix = swm_suffix, - .lte_list = <e_list, - .part_number = 1, - .write_flags = write_flags, - .size_remaining = size_remaining, - .part_size = part_size, - .total_bytes = total_bytes, - .total_bytes_written = total_bytes_written, - }; - - ret = for_lookup_table_entry(w->lookup_table, copy_resource_to_swm, &args); + ret = for_lookup_table_entry(w->lookup_table, + copy_resource_to_swm, &args); if (ret != 0) - return ret; + goto out; - ret = finish_swm(w, <e_list, write_flags); + ret = finish_swm(w, &args.lte_list, write_flags, progress_func); if (ret != 0) - return ret; + goto out; + if (progress_func) { + progress_func(WIMLIB_PROGRESS_MSG_SPLIT_END_PART, + &args.progress); + } /* The swms are all ready now, except the total_parts and part_number * fields in their headers are wrong (we don't know the total parts * until they are all written). Fix them. */ - int total_parts = args.part_number; + int total_parts = args.cur_part_number; for (int i = 1; i <= total_parts; i++) { - const char *p; + const char *part_name; if (i == 1) { - p = swm_name; + part_name = swm_name; } else { - sprintf(name + swm_base_name_len, "%d", i); - p = strcat(name, swm_suffix); + sprintf(swm_base_name + swm_base_name_len, "%d%s", + i, swm_suffix); + part_name = swm_base_name; } - FILE *fp = fopen(p, "r+b"); + FILE *fp = fopen(part_name, "r+b"); if (!fp) { - ERROR_WITH_ERRNO("Failed to open `%s'", p); - return WIMLIB_ERR_OPEN; + ERROR_WITH_ERRNO("Failed to open `%s'", part_name); + ret = WIMLIB_ERR_OPEN; + goto out; } u8 buf[4]; put_u16(&buf[0], i); @@ -235,12 +239,12 @@ WIMLIBAPI int wimlib_split(const char *wimfile, const char *swm_name, fwrite(buf, 1, sizeof(buf), fp) != sizeof(buf) || fclose(fp) != 0) { ERROR_WITH_ERRNO("Error overwriting header of `%s'", - name); - return WIMLIB_ERR_WRITE; + part_name); + ret = WIMLIB_ERR_WRITE; + break; } } - if (write_flags & WIMLIB_OPEN_FLAG_SHOW_PROGRESS) - puts("Done!"); - wimlib_free(w); - return 0; +out: + memcpy(&w->hdr, &hdr_save, sizeof(struct wim_header)); + return ret; } diff --git a/src/wim.c b/src/wim.c index f40915fd..9dd9718d 100644 --- a/src/wim.c +++ b/src/wim.c @@ -74,7 +74,7 @@ WIMStruct *new_wim_struct() } /* - * Calls a function on images in the WIM. If @image is WIM_ALL_IMAGES, @visitor + * Calls a function on images in the WIM. If @image is WIMLIB_ALL_IMAGES, @visitor * is called on the WIM once for each image, with each image selected as the * current image in turn. If @image is a certain image, @visitor is called on * the WIM only once, with that image selected. @@ -86,7 +86,7 @@ int for_image(WIMStruct *w, int image, int (*visitor)(WIMStruct *)) int end; int i; - if (image == WIM_ALL_IMAGES) { + if (image == WIMLIB_ALL_IMAGES) { start = 1; end = w->hdr.image_count; } else if (image >= 1 && image <= w->hdr.image_count) { @@ -153,13 +153,13 @@ int wim_hdr_flags_compression_type(int wim_hdr_flags) { if (wim_hdr_flags & WIM_HDR_FLAG_COMPRESSION) { if (wim_hdr_flags & WIM_HDR_FLAG_COMPRESS_LZX) - return WIM_COMPRESSION_TYPE_LZX; + return WIMLIB_COMPRESSION_TYPE_LZX; else if (wim_hdr_flags & WIM_HDR_FLAG_COMPRESS_XPRESS) - return WIM_COMPRESSION_TYPE_XPRESS; + return WIMLIB_COMPRESSION_TYPE_XPRESS; else - return WIM_COMPRESSION_TYPE_INVALID; + return WIMLIB_COMPRESSION_TYPE_INVALID; } else { - return WIM_COMPRESSION_TYPE_NONE; + return WIMLIB_COMPRESSION_TYPE_NONE; } } @@ -219,7 +219,7 @@ int select_wim_image(WIMStruct *w, int image) /* If a valid image is currently selected, it can be freed if it is not * modified. */ - if (w->current_image != WIM_NO_IMAGE) { + if (w->current_image != WIMLIB_NO_IMAGE) { imd = wim_get_current_image_metadata(w); if (!imd->modified) { DEBUG("Freeing image %u", w->current_image); @@ -254,11 +254,11 @@ WIMLIBAPI int wimlib_get_compression_type(const WIMStruct *w) WIMLIBAPI const char *wimlib_get_compression_type_string(int ctype) { switch (ctype) { - case WIM_COMPRESSION_TYPE_NONE: + case WIMLIB_COMPRESSION_TYPE_NONE: return "None"; - case WIM_COMPRESSION_TYPE_LZX: + case WIMLIB_COMPRESSION_TYPE_LZX: return "LZX"; - case WIM_COMPRESSION_TYPE_XPRESS: + case WIMLIB_COMPRESSION_TYPE_XPRESS: return "XPRESS"; default: return "Invalid"; @@ -277,15 +277,15 @@ WIMLIBAPI int wimlib_resolve_image(WIMStruct *w, const char *image_name_or_num) int i; if (!image_name_or_num) - return WIM_NO_IMAGE; + return WIMLIB_NO_IMAGE; if (strcmp(image_name_or_num, "all") == 0 || strcmp(image_name_or_num, "*") == 0) - return WIM_ALL_IMAGES; + return WIMLIB_ALL_IMAGES; image = strtol(image_name_or_num, &p, 10); if (p != image_name_or_num && *p == '\0') { if (image < 1 || image > w->hdr.image_count) - return WIM_NO_IMAGE; + return WIMLIB_NO_IMAGE; return image; } else { for (i = 1; i <= w->hdr.image_count; i++) { @@ -293,7 +293,7 @@ WIMLIBAPI int wimlib_resolve_image(WIMStruct *w, const char *image_name_or_num) wimlib_get_image_name(w, i)) == 0) return i; } - return WIM_NO_IMAGE; + return WIMLIB_NO_IMAGE; } } @@ -332,7 +332,7 @@ WIMLIBAPI void wimlib_print_available_images(const WIMStruct *w, int image) int last; int i; int n; - if (image == WIM_ALL_IMAGES) { + if (image == WIMLIB_ALL_IMAGES) { n = printf("Available Images:\n"); first = 1; last = w->hdr.image_count; @@ -353,8 +353,8 @@ WIMLIBAPI void wimlib_print_available_images(const WIMStruct *w, int image) } -/* Prints the metadata for the specified image, which may be WIM_ALL_IMAGES, but - * not WIM_NO_IMAGE. */ +/* Prints the metadata for the specified image, which may be WIMLIB_ALL_IMAGES, but + * not WIMLIB_NO_IMAGE. */ WIMLIBAPI int wimlib_print_metadata(WIMStruct *w, int image) { if (!w) @@ -463,7 +463,8 @@ int open_wim_writable(WIMStruct *w, const char *path, * Begins the reading of a WIM file; opens the file and reads its header and * lookup table, and optionally checks the integrity. */ -static int begin_read(WIMStruct *w, const char *in_wim_path, int open_flags) +static int begin_read(WIMStruct *w, const char *in_wim_path, int open_flags, + wimlib_progress_func_t progress_func) { int ret; uint xml_num_images; @@ -495,7 +496,7 @@ static int begin_read(WIMStruct *w, const char *in_wim_path, int open_flags) w->hdr.boot_idx = 0; } - if (wimlib_get_compression_type(w) == WIM_COMPRESSION_TYPE_INVALID) { + if (wimlib_get_compression_type(w) == WIMLIB_COMPRESSION_TYPE_INVALID) { ERROR("Invalid compression type (WIM header flags = %x)", w->hdr.flags); ret = WIMLIB_ERR_INVALID_COMPRESSION_TYPE; @@ -503,8 +504,7 @@ static int begin_read(WIMStruct *w, const char *in_wim_path, int open_flags) } if (open_flags & WIMLIB_OPEN_FLAG_CHECK_INTEGRITY) { - ret = check_wim_integrity(w, - (open_flags & WIMLIB_OPEN_FLAG_SHOW_PROGRESS) != 0); + ret = check_wim_integrity(w, progress_func); if (ret == WIM_INTEGRITY_NONEXISTENT) { WARNING("No integrity information for `%s'; skipping " "integrity check.", w->filename); @@ -566,7 +566,7 @@ static int begin_read(WIMStruct *w, const char *in_wim_path, int open_flags) qsort(w->image_metadata, w->current_image, sizeof(struct image_metadata), sort_image_metadata_by_position); - w->current_image = WIM_NO_IMAGE; + w->current_image = WIMLIB_NO_IMAGE; /* Read the XML data. */ ret = read_xml_data(w->fp, &w->hdr.xml_res_entry, @@ -600,7 +600,7 @@ out_free_xml_data: out_free_image_metadata: /*FREE(w->image_metadata);*/ /*w->image_metadata = NULL;*/ - /*w->current_image = WIM_NO_IMAGE;*/ + /*w->current_image = WIMLIB_NO_IMAGE;*/ out_free_lookup_table: /*free_lookup_table(w->lookup_table);*/ /*w->lookup_table = NULL;*/ @@ -616,7 +616,8 @@ out: * Opens a WIM file and creates a WIMStruct for it. */ WIMLIBAPI int wimlib_open_wim(const char *wim_file, int open_flags, - WIMStruct **w_ret) + WIMStruct **w_ret, + wimlib_progress_func_t progress_func) { WIMStruct *w; int ret; @@ -628,7 +629,7 @@ WIMLIBAPI int wimlib_open_wim(const char *wim_file, int open_flags, return WIMLIB_ERR_NOMEM; } - ret = begin_read(w, wim_file, open_flags); + ret = begin_read(w, wim_file, open_flags, progress_func); if (ret == 0) { *w_ret = w; } else { diff --git a/src/wimlib.h b/src/wimlib.h index 0618ff38..ed1e4494 100644 --- a/src/wimlib.h +++ b/src/wimlib.h @@ -119,25 +119,21 @@ * * To add an image to a WIM file from a directory tree on your filesystem, call * wimlib_add_image(). This can be done with a ::WIMStruct gotten from - * wimlib_open_wim() or from wimlib_create_new_wim(). Alternatively, if you - * want to capture a WIM image directly from a NTFS volume while preserving - * NTFS-specific data such as security descriptors, call - * wimlib_add_image_from_ntfs_volume() instead. + * wimlib_open_wim() or from wimlib_create_new_wim(). Alternatively, you can + * capture a WIM directory from a NTFS volume if you provide the + * ::WIMLIB_ADD_IMAGE_FLAG_NTFS flag, provided that wimlib was not compiled with + * the --without-ntfs-3g flag. * - * To extract an image from a WIM file, call wimlib_extract_image(). - * Alternatively, if you want to apply a WIM image directly to a NTFS volume - * while setting NTFS-specific data such as security descriptors, call - * wimlib_apply_image_to_ntfs_volume(). - * - * The NTFS functions will fail if wimlib was compiled with the - * --without-ntfs-3g flag. + * To extract an image from a WIM file, call wimlib_extract_image(). You may + * extract an image to a directory or directly to a NTFS volume, the latter of + * which will preserve NTFS-specific data such as security descriptors. * * wimlib supports mounting WIM files either read-only or read-write. Mounting - * is done using wimlib_mount() and unmounting is done using wimlib_unmount(). - * Mounting can be done without root privileges because it is implemented using - * FUSE (Filesystem in Userspace). If wimlib is compiled with the - * --without-fuse flag, these functions will be available but will - * fail. + * is done using wimlib_mount_image() and unmounting is done using + * wimlib_unmount_image(). Mounting can be done without root privileges because + * it is implemented using FUSE (Filesystem in Userspace). If wimlib is + * compiled with the --without-fuse flag, these functions will be + * available but will fail. * * After creating or modifying a WIM file, you can write it to a file using * wimlib_write(). Alternatively, if the WIM was originally read from a file, @@ -145,19 +141,20 @@ * * Please not: merely by calling wimlib_add_image() or many of the other * functions in this library that operate on ::WIMStruct's, you are @b not - * modifing the WIM file on disk. Changes are not saved until you explicitly + * modifying the WIM file on disk. Changes are not saved until you explicitly * call wimlib_write() or wimlib_overwrite(). * * After you are done with the WIM file, use wimlib_free() to free all memory * associated with a ::WIMStruct and close all files associated with it. * + * A number of functions take a pointer to a progress function of type + * ::wimlib_progress_func_t. This function will be called periodically during + * the WIM operation(s) to report on the progress of the operation (e.g. how + * many bytes have been written so far). + * * To see an example of how to use wimlib, see the file @c programs/imagex.c in * wimlib's source tree. * - * wimlib supports custom memory allocators; use wimlib_set_memory_allocator() - * for this. However, if wimlib calls into @c libntfs-3g, the custom memory - * allocator may not be used. - * * \section imagex imagex * * wimlib comes with the imagex program, which is documented in man pages. @@ -212,77 +209,222 @@ #include #include #include +#include -#ifndef _WIMLIB_INTERNAL_H /** * Opaque structure that represents a WIM file. */ typedef struct WIMStruct WIMStruct; -#endif /** * Specifies the compression type of a WIM file. */ - -enum wim_compression_type { +enum wimlib_compression_type { /** An invalid compression type. */ - WIM_COMPRESSION_TYPE_INVALID = -1, + WIMLIB_COMPRESSION_TYPE_INVALID = -1, /** The WIM does not include any compressed resources. */ - WIM_COMPRESSION_TYPE_NONE = 0, + WIMLIB_COMPRESSION_TYPE_NONE = 0, /** Compressed resources in the WIM use LZX compression. */ - WIM_COMPRESSION_TYPE_LZX = 1, + WIMLIB_COMPRESSION_TYPE_LZX = 1, /** Compressed resources in the WIM use XPRESS compression. */ - WIM_COMPRESSION_TYPE_XPRESS = 2, + WIMLIB_COMPRESSION_TYPE_XPRESS = 2, +}; + +enum wimlib_progress_msg { + WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN, + WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN, + WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_END, + WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS, + WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS, + WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END, + + WIMLIB_PROGRESS_MSG_SCAN_BEGIN, + WIMLIB_PROGRESS_MSG_SCAN_END, + + WIMLIB_PROGRESS_MSG_WRITE_STREAMS, + WIMLIB_PROGRESS_MSG_WRITE_METADATA_BEGIN, + WIMLIB_PROGRESS_MSG_WRITE_METADATA_END, + WIMLIB_PROGRESS_MSG_RENAME, + + WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY, + + WIMLIB_PROGRESS_MSG_CALC_INTEGRITY, + + WIMLIB_PROGRESS_MSG_JOIN_STREAMS, + + WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART, + WIMLIB_PROGRESS_MSG_SPLIT_END_PART, +}; + +union wimlib_progress_info { + struct { + uint64_t total_bytes; + uint64_t total_streams; + uint64_t completed_bytes; + uint64_t completed_streams; + unsigned num_threads; + int compression_type; + } write_streams; + struct { + const char *source; + } scan; + struct { + int image; + const char *image_name; + const char *target; + uint64_t total_bytes; + uint64_t completed_bytes; + uint64_t num_streams; + } extract; + struct { + const char *from; + const char *to; + } rename; + struct { + uint64_t total_bytes; + uint64_t completed_bytes; + uint32_t completed_chunks; + uint32_t total_chunks; + uint32_t chunk_size; + const char *filename; + } integrity; + struct { + uint64_t total_bytes; + uint64_t completed_bytes; + unsigned completed_parts; + unsigned total_parts; + const char *filename; + } join; + struct { + uint64_t completed_bytes; + unsigned cur_part_number; + const char *part_name; + uint64_t total_bytes; + } split; }; +/** A user-supplied function that will be called periodically during certain WIM + * operations. The first argument specifies the type of operation that is being + * performed or is about to be started or has been completed. The second + * argument is a pointer to one of a number of structures depending on the first + * argument. It may be @c NULL for some message types. + * + * The return value + * is currently ignored, but it may do something in the future. (Set it to 0 + * for now.) + */ +typedef int (*wimlib_progress_func_t)(enum wimlib_progress_msg msg_type, + const union wimlib_progress_info *info); + +/***************************** + * WIMLIB_ADD_IMAGE_FLAG_* * + *****************************/ + +/** Directly capture a NTFS volume, not a generic directory. */ +#define WIMLIB_ADD_IMAGE_FLAG_NTFS 0x00000001 + +/** Follow symlinks; archive and dump the files they point to. Cannot be used + * with ::WIMLIB_ADD_IMAGE_FLAG_NTFS. */ +#define WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE 0x00000002 + +/** Print the name of each file or directory as it is scanned to be included in + * the WIM image. */ +#define WIMLIB_ADD_IMAGE_FLAG_VERBOSE 0x00000004 + +/** Mark the image being added as the bootable image of the WIM. */ +#define WIMLIB_ADD_IMAGE_FLAG_BOOT 0x00000008 + +/****************************** + * WIMLIB_EXPORT_FLAG_* * + ******************************/ + +/** See documentation for wimlib_export_image(). */ +#define WIMLIB_EXPORT_FLAG_BOOT 0x00000001 + +/****************************** + * WIMLIB_EXTRACT_FLAG_* * + ******************************/ + +/** Apply the image directly to a NTFS volume, not a generic directory. */ +#define WIMLIB_EXTRACT_FLAG_NTFS 0x00000001 + +/** When identical files are extracted from the WIM, always hard link them + * together. Not valid with ::WIMLIB_EXTRACT_FLAG_NTFS. */ +#define WIMLIB_EXTRACT_FLAG_HARDLINK 0x00000002 + +/** When identical files are extracted from the WIM, always symlink them + * together. Not valid with ::WIMLIB_EXTRACT_FLAG_NTFS. */ +#define WIMLIB_EXTRACT_FLAG_SYMLINK 0x00000004 + +/** Print the name of each file as it is extracted from the WIM image. */ +#define WIMLIB_EXTRACT_FLAG_VERBOSE 0x00000008 + +/** Read the WIM file sequentially while extracting the image. */ +#define WIMLIB_EXTRACT_FLAG_SEQUENTIAL 0x00000010 + +/****************************** + * WIMLIB_MOUNT_FLAG_* * + ******************************/ + /** Mount the WIM read-write. */ -#define WIMLIB_MOUNT_FLAG_READWRITE 0x00000001 +#define WIMLIB_MOUNT_FLAG_READWRITE 0x00000001 /** For debugging only. (This passes the @c -d flag to @c fuse_main()).*/ -#define WIMLIB_MOUNT_FLAG_DEBUG 0x00000002 +#define WIMLIB_MOUNT_FLAG_DEBUG 0x00000002 /** Do not allow accessing alternate data streams. */ -#define WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE 0x00000010 +#define WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE 0x00000004 /** Access alternate data streams through extended file attributes. This is the * default mode. */ -#define WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR 0x00000020 +#define WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR 0x00000008 /** Access alternate data streams by specifying the file name, a colon, then the * alternate file stream name. */ -#define WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS 0x00000040 +#define WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS 0x00000010 + +/****************************** + * WIMLIB_OPEN_FLAG_* * + ******************************/ + +/** See documentation for wimlib_open_wim(). */ +#define WIMLIB_OPEN_FLAG_CHECK_INTEGRITY 0x00000001 + +/** See documentation for wimlib_open_wim(). */ +#define WIMLIB_OPEN_FLAG_SPLIT_OK 0x00000002 + +/****************************** + * WIMLIB_UNMOUNT_FLAG_* * + ******************************/ /** Include an integrity table in the new WIM being written during the unmount. * Ignored for read-only mounts. */ -#define WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY 0x00000001 +#define WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY 0x00000001 /** Unless this flag is given, changes to a mounted WIM are discarded. Ignored * for read-only mounts. */ -#define WIMLIB_UNMOUNT_FLAG_COMMIT 0x00000002 - -/** Include an integrity table in the new WIM file. */ -#define WIMLIB_WRITE_FLAG_CHECK_INTEGRITY 0x00000001 - -/** Print progress information when writing streams and when writing the - * integrity table. */ -#define WIMLIB_WRITE_FLAG_SHOW_PROGRESS 0x00000002 +#define WIMLIB_UNMOUNT_FLAG_COMMIT 0x00000002 -/** Print file paths as we write then */ -#define WIMLIB_WRITE_FLAG_VERBOSE 0x00000004 +/****************************** + * WIMLIB_WRITE_FLAG_* * + ******************************/ -/** Call fsync() when the WIM file is closed */ -#define WIMLIB_WRITE_FLAG_FSYNC 0x00000008 +/** Include an integrity table in the new WIM file. */ +#define WIMLIB_WRITE_FLAG_CHECK_INTEGRITY 0x00000001 /** Re-build the entire WIM file rather than appending data to it, if possible. * (Applies to wimlib_overwrite(), not wimlib_write()). */ -#define WIMLIB_WRITE_FLAG_REBUILD 0x00000010 +#define WIMLIB_WRITE_FLAG_REBUILD 0x00000002 /** Do not copy compressed resources between WIMs if the compression type is the * same. Instead, recompress them. */ -#define WIMLIB_WRITE_FLAG_RECOMPRESS 0x00000020 +#define WIMLIB_WRITE_FLAG_RECOMPRESS 0x00000004 + +/** Call fsync() when the WIM file is closed */ +#define WIMLIB_WRITE_FLAG_FSYNC 0x00000008 /** Specifying this flag overrides the default behavior of wimlib_overwrite() * after one or more calls to wimlib_delete_image(), which is to rebuild the @@ -291,51 +433,7 @@ enum wim_compression_type { * particular, all streams will be left alone, even if they are no longer * references. This is probably not what you want, because almost no space will * be spaced by deleting an image in this way. */ -#define WIMLIB_WRITE_FLAG_SOFT_DELETE 0x00000040 - -/** Mark the image being added as the bootable image of the WIM. */ -#define WIMLIB_ADD_IMAGE_FLAG_BOOT 0x00000001 - -/** Print the name of each file or directory as it is scanned to be included in - * the WIM image. */ -#define WIMLIB_ADD_IMAGE_FLAG_VERBOSE 0x00000002 - -/** Follow symlinks; archive and dump the files they point to. */ -#define WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE 0x00000004 - -/** Show progress information when scanning a directory tree */ -#define WIMLIB_ADD_IMAGE_FLAG_SHOW_PROGRESS 0x00000008 - -/** See documentation for wimlib_export_image(). */ -#define WIMLIB_EXPORT_FLAG_BOOT 0x00000001 - -/** Verify the integrity of the WIM if an integrity table is present. */ -#define WIMLIB_OPEN_FLAG_CHECK_INTEGRITY 0x00000001 - -/** Print progress information when verifying integrity table. */ -#define WIMLIB_OPEN_FLAG_SHOW_PROGRESS 0x00000002 - -/** If this flag is not given, an error is issued if the WIM is part of a split - * WIM. */ -#define WIMLIB_OPEN_FLAG_SPLIT_OK 0x00000004 - - -/** When identical files are extracted from the WIM, always hard link them - * together. */ -#define WIMLIB_EXTRACT_FLAG_HARDLINK 0x00000001 - -/** When identical files are extracted from the WIM, always symlink them - * together. */ -#define WIMLIB_EXTRACT_FLAG_SYMLINK 0x00000002 - -/** Print the name of each file as it is extracted from the WIM image. */ -#define WIMLIB_EXTRACT_FLAG_VERBOSE 0x00000008 - -/** Read the WIM file sequentially while extracting the image. */ -#define WIMLIB_EXTRACT_FLAG_SEQUENTIAL 0x00000010 - -/** Print progress information while extracting the image. */ -#define WIMLIB_EXTRACT_FLAG_SHOW_PROGRESS 0x00000020 +#define WIMLIB_WRITE_FLAG_SOFT_DELETE 0x00000010 /** * Possible values of the error code returned by many functions in wimlib. @@ -394,11 +492,10 @@ enum wimlib_error_code { /** Used to indicate that no WIM image is currently selected. */ -#define WIM_NO_IMAGE 0 +#define WIMLIB_NO_IMAGE 0 /** Used to specify all images in the WIM. */ -#define WIM_ALL_IMAGES (-1) - +#define WIMLIB_ALL_IMAGES (-1) /** * Adds an image to a WIM file from a directory tree on disk. @@ -410,6 +507,9 @@ enum wimlib_error_code { * permanently, and instead references to their paths saved. The files are then * read on-demand if wimlib_write() or wimlib_overwrite() is called. * + * Please note that @b no changes are committed to the underlying WIM file (if + * any) until wimlib_write() or wimlib_overwrite() is called. + * * @param wim * Pointer to the ::WIMStruct for a WIM file to which the image will be * added. @@ -426,7 +526,7 @@ enum wimlib_error_code { * Length of the string @a config in bytes. Ignored if @a config is @c * NULL. * - * @param flags + * @param add_image_flags * Bitwise OR of flags prefixed with WIMLIB_ADD_IMAGE_FLAG. If * ::WIMLIB_ADD_IMAGE_FLAG_BOOT is specified, the image in @a wim that is * marked as bootable is changed to the one being added. If @@ -434,9 +534,11 @@ enum wimlib_error_code { * printed as it is scanned or captured. If * ::WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE is specified, the files or * directories pointed to by symbolic links are archived rather than the - * symbolic links themselves. If ::WIMLIB_ADD_IMAGE_FLAG_SHOW_PROGRESS is - * specified, progress information will be printed (distinct from the - * verbose information). + * symbolic links themselves. + * + * @param progress_func + * If non-NULL, a function that will be called periodically with the + * progress of the current operation. * * @return 0 on success; nonzero on error. On error, changes to @a wim are * discarded so that it appears to be in the same state as when this function @@ -456,8 +558,8 @@ enum wimlib_error_code { * @retval ::WIMLIB_ERR_READ * Failed to read a file in the directory tree rooted at @a dir. * @retval ::WIMLIB_ERR_SPECIAL_FILE - * The directory tree rooted at @dir contains a special file that is not a - * directory, regular file, or symbolic link. + * The directory tree rooted at @a dir contains a special file that is not + * a directory, regular file, or symbolic link. * @retval ::WIMLIB_ERR_STAT * Failed obtain the metadata for a file or directory in the directory tree * rooted at @a dir. @@ -467,42 +569,16 @@ enum wimlib_error_code { */ extern int wimlib_add_image(WIMStruct *wim, const char *dir, const char *name, const char *config, - size_t config_len, int flags); - -/** - * This function is similar to wimlib_add_image(), except instead of capturing - * the WIM image from a directory, it is captured from a NTFS volume specified - * by @a device. NTFS-3g errors are reported as ::WIMLIB_ERR_NTFS_3G. - * ::WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE may not be specified because we capture - * the reparse points exactly as they are. - */ -extern int wimlib_add_image_from_ntfs_volume(WIMStruct *wim, const char *device, - const char *name, - const char *config, - size_t config_len, - int flags); - -/** - * This function is similar to wimlib_extract_image(), except that @a image may - * not be ::WIM_ALL_IMAGES, and @a device specifies the name of a file or block - * device containing a NTFS volume to apply the image to. NTFS-3g errors are - * reported as ::WIMLIB_ERR_NTFS_3G, and ::WIMLIB_EXTRACT_FLAG_HARDLINK or - * ::WIMLIB_EXTRACT_FLAG_SYMLINK may not be specified because in the NTFS - * apply mode we apply the reparse points and hard links exactly as they are in - * the WIM. - */ -extern int wimlib_apply_image_to_ntfs_volume(WIMStruct *wim, int image, - const char *device, int flags, - WIMStruct **additional_swms, - unsigned num_additional_swms); + size_t config_len, int add_image_flags, + wimlib_progress_func_t progress_func); /** * Creates a WIMStruct for a new WIM file. * * @param ctype * The type of compression to be used in the new WIM file. Must be - * ::WIM_COMPRESSION_TYPE_NONE, ::WIM_COMPRESSION_TYPE_LZX, or - * ::WIM_COMPRESSION_TYPE_XPRESS. + * ::WIMLIB_COMPRESSION_TYPE_NONE, ::WIMLIB_COMPRESSION_TYPE_LZX, or + * ::WIMLIB_COMPRESSION_TYPE_XPRESS. * @param wim_ret * On success, a pointer to an opaque ::WIMStruct for the new WIM file is * written to the memory location pointed to by this paramater. The @@ -510,8 +586,8 @@ extern int wimlib_apply_image_to_ntfs_volume(WIMStruct *wim, int image, * it. * @return 0 on success; nonzero on error. * @retval ::WIMLIB_ERR_INVALID_COMPRESSION_TYPE - * @a ctype was not ::WIM_COMPRESSION_TYPE_NONE, - * ::WIM_COMPRESSION_TYPE_LZX, or ::WIM_COMPRESSION_TYPE_XPRESS. + * @a ctype was not ::WIMLIB_COMPRESSION_TYPE_NONE, + * ::WIMLIB_COMPRESSION_TYPE_LZX, or ::WIMLIB_COMPRESSION_TYPE_XPRESS. * @retval ::WIMLIB_ERR_NOMEM * Failed to allocate needed memory. */ @@ -524,15 +600,18 @@ extern int wimlib_create_new_wim(int ctype, WIMStruct **wim_ret); * lookup table of the WIM if they are not referenced by any other images in the * WIM. * + * Please note that @b no changes are committed to the underlying WIM file (if + * any) until wimlib_write() or wimlib_overwrite() is called. + * * @param wim * Pointer to the ::WIMStruct for the WIM file that contains the image(s) * being deleted. * @param image - * The number of the image to delete, or ::WIM_ALL_IMAGES to delete all + * The number of the image to delete, or ::WIMLIB_ALL_IMAGES to delete all * images. * @return 0 on success; nonzero on failure. On failure, @a wim is guaranteed * to be left unmodified only if @a image specified a single image. If instead - * @a image was ::WIM_ALL_IMAGES and @a wim contained more than one image, it's + * @a image was ::WIMLIB_ALL_IMAGES and @a wim contained more than one image, it's * possible for some but not all of the images to have been deleted when a * failure status is returned. * @@ -542,7 +621,7 @@ extern int wimlib_create_new_wim(int ctype, WIMStruct **wim_ret); * A directory entry in the metadata resource for @a image in the WIM is * invalid. * @retval ::WIMLIB_ERR_INVALID_IMAGE - * @a image does not exist in the WIM and is not ::WIM_ALL_IMAGES. + * @a image does not exist in the WIM and is not ::WIMLIB_ALL_IMAGES. * @retval ::WIMLIB_ERR_INVALID_RESOURCE_SIZE * The metadata resource for @a image in the WIM is invalid. * @retval ::WIMLIB_ERR_INVALID_SECURITY_DATA @@ -562,10 +641,10 @@ extern int wimlib_delete_image(WIMStruct *wim, int image); * * The destination image is made to share the same dentry tree and security data * structure as the source image. This places some restrictions on additional - * functions that may be called. wimlib_mount() may not be called on either the - * source image or the destination image without an intervening call to a - * function that un-shares the images, such as wimlib_free() on @a dest_wim, or - * wimlib_delete_image() on either the source or destination image. + * functions that may be called. wimlib_mount_image() may not be called on + * either the source image or the destination image without an intervening call + * to a function that un-shares the images, such as wimlib_free() on @a + * dest_wim, or wimlib_delete_image() on either the source or destination image. * Furthermore, you may not call wimlib_free() or wimlib_overwrite() on @a * src_wim before any calls to functions such as wimlib_write() on @a dest_wim * because @a dest_wim will have references back to @a src_wim. @@ -581,30 +660,33 @@ extern int wimlib_delete_image(WIMStruct *wim, int image); * Regardless of whether this function succeeds or fails, no user-visible * changes are made to @a src_wim. * + * Please note that @b no changes are committed to the underlying WIM file (if + * any) until wimlib_write() or wimlib_overwrite() is called. + * * @param src_wim * Pointer to the ::WIMStruct for a stand-alone WIM or part 1 of a split * WIM that contains the image(s) being exported. * @param src_image * The image to export from @a src_wim. Can be the number of an image, or - * ::WIM_ALL_IMAGES to export all images. + * ::WIMLIB_ALL_IMAGES to export all images. * @param dest_wim * Pointer to the ::WIMStruct for a WIM file that will receive the images * being exported. * @param dest_name * The name to give the exported image in the new WIM file. If left @c NULL, * the name from @a src_wim is used. This parameter must be left @c NULL - * if @a src_image is ::WIM_ALL_IMAGES and @a src_wim contains more than one + * if @a src_image is ::WIMLIB_ALL_IMAGES and @a src_wim contains more than one * image; in that case, the names are all taken from the @a src_wim. * @param dest_description * The description to give the exported image in the new WIM file. If left * @c NULL, the description from the @a src_wim is used. This parameter must - * be left @c NULL if @a src_image is ::WIM_ALL_IMAGES and @a src_wim contains + * be left @c NULL if @a src_image is ::WIMLIB_ALL_IMAGES and @a src_wim contains * more than one image; in that case, the descriptions are all taken from * @a src_wim. - * @param flags + * @param export_flags * ::WIMLIB_EXPORT_FLAG_BOOT if the image being exported is to be made * bootable, or 0 if which image is marked as bootable in the destination - * WIM is to be left unchanged. If @a src_image is ::WIM_ALL_IMAGES and + * WIM is to be left unchanged. If @a src_image is ::WIMLIB_ALL_IMAGES and * there are multiple images in @a src_wim, specifying * ::WIMLIB_EXPORT_FLAG_BOOT is valid only if one of the exported images is * currently marked as bootable in @a src_wim; if that is the case, then @@ -619,6 +701,9 @@ extern int wimlib_delete_image(WIMStruct *wim, int image); * Number of additional WIM parts provided in the @a additional_swms array. * This number should be one less than the total number of parts in the * split WIM. Set to 0 if the WIM is a standalone WIM. + * @param progress_func + * If non-NULL, a function that will be called periodically with the + * progress of the current operation. * * @return 0 on success; nonzero on error. * @retval ::WIMLIB_ERR_DECOMPRESSION @@ -634,10 +719,10 @@ extern int wimlib_delete_image(WIMStruct *wim, int image); * @a src_image does not exist in @a src_wim. * @retval ::WIMLIB_ERR_INVALID_PARAM * ::WIMLIB_EXPORT_FLAG_BOOT was specified in @a flags, @a src_image was - * ::WIM_ALL_IMAGES, @a src_wim contains multiple images, and no images in + * ::WIMLIB_ALL_IMAGES, @a src_wim contains multiple images, and no images in * @a src_wim are marked as bootable; or @a dest_name and/or @a * dest_description were non-NULL, @a src_image was - * ::WIM_ALL_IMAGES, and @a src_wim contains multiple images; or @a src_wim + * ::WIMLIB_ALL_IMAGES, and @a src_wim contains multiple images; or @a src_wim * or @a dest_wim was @c NULL. * @retval ::WIMLIB_ERR_INVALID_RESOURCE_SIZE * The metadata resource for @a src_image in @a src_wim is invalid. @@ -658,9 +743,10 @@ extern int wimlib_delete_image(WIMStruct *wim, int image); */ extern int wimlib_export_image(WIMStruct *src_wim, int src_image, WIMStruct *dest_wim, const char *dest_name, - const char *dest_description, int flags, + const char *dest_description, int export_flags, WIMStruct **additional_swms, - unsigned num_additional_swms); + unsigned num_additional_swms, + wimlib_progress_func_t progress_func); /** * Extracts an image, or all images, from a standalone or split WIM file. @@ -669,26 +755,41 @@ extern int wimlib_export_image(WIMStruct *src_wim, int src_image, * Pointer to the ::WIMStruct for a standalone WIM file, or part 1 of a * split WIM. * @param image - * The image to extract. Can be the number of an image, or ::WIM_ALL_IMAGES - * to specify that all images are to be extracted. - * @param output_dir - * Directory to extract the WIM image(s) to. It is created if it does not - * already exist. - * @param flags - * Bitwise or of the flags prefixed with WIMLIB_EXTRACT_FLAG. - * - * One or none of ::WIMLIB_EXTRACT_FLAG_HARDLINK or - * ::WIMLIB_EXTRACT_FLAG_SYMLINK may be specified. These flags cause - * extracted files that are identical to be hardlinked or symlinked - * together, depending on the flag. These flags override the hard link - * groups that are specified in the WIM file itself. If ::WIM_ALL_IMAGES - * is provided as the @a image parameter, files may be hardlinked or - * symlinked across images if a file is found to occur in more than one - * image. + * The image to extract. Can be the number of an image, or ::WIMLIB_ALL_IMAGES + * to specify that all images are to be extracted. ::WIMLIB_ALL_IMAGES cannot + * be used if ::WIMLIB_EXTRACT_FLAG_NTFS is specified in @a extract_flags. + * @param target + * Directory to extract the WIM image(s) to (created if it does not already + * exist); or, with ::WIMLIB_EXTRACT_FLAG_NTFS in @a extract_flags, the + * name of the NTFS volume to extract the image to. + * @param extract_flags + * Bitwise OR of the flags prefixed with WIMLIB_EXTRACT_FLAG. + * + * If ::WIMLIB_EXTRACT_FLAG_NTFS is specified, @a target is interpreted as + * a NTFS volume to extract the image to. The volume will be opened using + * NTFS-3g and the image will be extracted to the root of the NTFS volume. + * Otherwise, @a target is interpreted as a directory to extract the + * image(s) to. + * + * If ::WIMLIB_EXTRACT_FLAG_NTFS is not specified, one or none of + * ::WIMLIB_EXTRACT_FLAG_HARDLINK or ::WIMLIB_EXTRACT_FLAG_SYMLINK may be + * specified. These flags cause extracted files that are identical to be + * hardlinked or symlinked together, depending on the flag. These flags + * override the hard link groups that are specified in the WIM file itself. + * If ::WIMLIB_ALL_IMAGES is provided as the @a image parameter, files may be + * hardlinked or symlinked across images if a file is found to occur in + * more than one image. * * You may also specify the flag ::WIMLIB_EXTRACT_FLAG_VERBOSE to cause * informational messages to be printed during the extraction, including * the name of each extracted file or directory. + * + * If ::WIMLIB_EXTRACT_FLAG_SEQUENTIAL is specified, streams are read from + * the WIM sequentially, if possible. If ::WIMLIB_ALL_IMAGES is specified, + * each image is considered separately with regards to the sequential + * order. It is also possible for alternate data streams to break the + * sequential order (this only applies if ::WIMLIB_EXTRACT_FLAG_NTFS is + * specified). * @param additional_swms * Array of pointers to the ::WIMStruct for each additional part in the * split WIM. Ignored if @a num_additional_swms is 0. The pointers do not @@ -700,6 +801,10 @@ extern int wimlib_export_image(WIMStruct *src_wim, int src_image, * This number should be one less than the total number of parts in the * split WIM. Set to 0 if the WIM is a standalone WIM. * + * @param progress_func + * If non-NULL, a function that will be called periodically with the + * progress of the current operation. + * * @return 0 on success; nonzero on error. * @retval ::WIMLIB_ERR_DECOMPRESSION * Could not decompress a resource (file or metadata) for @a image in @a @@ -734,9 +839,10 @@ extern int wimlib_export_image(WIMStruct *src_wim, int src_image, * Failed to write a file being extracted. */ extern int wimlib_extract_image(WIMStruct *wim, int image, - const char *output_dir, int flags, + const char *target, int extract_flags, WIMStruct **additional_swms, - unsigned num_additional_swms); + unsigned num_additional_swms, + wimlib_progress_func_t progress_func); /** * Extracts the XML data for a WIM file to a file stream. Every WIM file @@ -784,8 +890,8 @@ extern int wimlib_get_boot_idx(const WIMStruct *wim); * Pointer to the ::WIMStruct for a WIM file * * @return - * ::WIM_COMPRESSION_TYPE_NONE, ::WIM_COMPRESSION_TYPE_LZX, or - * ::WIM_COMPRESSION_TYPE_XPRESS. + * ::WIMLIB_COMPRESSION_TYPE_NONE, ::WIMLIB_COMPRESSION_TYPE_LZX, or + * ::WIMLIB_COMPRESSION_TYPE_XPRESS. */ extern int wimlib_get_compression_type(const WIMStruct *wim); @@ -793,8 +899,8 @@ extern int wimlib_get_compression_type(const WIMStruct *wim); * Converts a compression type enumeration value into a string. * * @param ctype - * ::WIM_COMPRESSION_TYPE_NONE, ::WIM_COMPRESSION_TYPE_LZX, - * ::WIM_COMPRESSION_TYPE_XPRESS, or another value. + * ::WIMLIB_COMPRESSION_TYPE_NONE, ::WIMLIB_COMPRESSION_TYPE_LZX, + * ::WIMLIB_COMPRESSION_TYPE_XPRESS, or another value. * * @return * A statically allocated string: "None", "LZX", "XPRESS", or "Invalid", @@ -903,55 +1009,57 @@ extern bool wimlib_has_integrity_table(const WIMStruct *wim); extern bool wimlib_image_name_in_use(const WIMStruct *wim, const char *name); /** - * Joins a set of split WIMs into a one-part WIM. + * Joins a set of split WIMs into a stand-alone one-part WIM. * * @param swms * An array of strings that give the filenames of all parts of the split * WIM. * @param num_swms * Number of filenames in @a swms. + * @param swm_open_flags + * Open flags to use when opening the split WIM pats. + * @param wim_write_flags + * Write flags to use when writing the joined WIM. * @param output_path * The path to write the one-part WIM to. - * @param flags - * ::WIMLIB_OPEN_FLAG_CHECK_INTEGRITY to check the split WIMs' integrity - * tables (if present) when opening them, and include an integrity table in - * the output WIM. + * @param progress_func + * If non-NULL, a function that will be called periodically with the + * progress of the current operation. * * @return 0 on success; nonzero on error. This function may return any value - * returned by wimlib_open_wim() except ::WIMLIB_ERR_SPLIT_UNSUPPORTED, as well - * as the following error codes: + * returned by wimlib_open_wim() and wimlib_write() except + * ::WIMLIB_ERR_SPLIT_UNSUPPORTED, 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 * 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_WRITE - * An error occurred when trying to write data to the new WIM at @a output_path. * * Note that this function merely copies the resources, so it will not check to - * see if the resources, including the metadata resources, are valid or not. - * - * Also, after this function is called, the only function that may be called on - * the ::WIMStruct's in the @a swms array is wimlib_free(). + * see if the resources, including the metadata resources, are valid or not + * (unless ::WIMLIB_WRITE_FLAG_RECOMPRESS is specified). */ extern int wimlib_join(const char **swms, unsigned num_swms, - const char *output_path, int flags); + const char *output_path, int swm_open_flags, + int wim_write_flags, + wimlib_progress_func_t progress_func); /** * Mounts an image in a WIM file on a directory read-only or read-write. * - * A daemon will be forked to service the filesystem. + * A daemon will be forked to service the filesystem. In other words, this + * function returns @b before the image is unmounted. * * If the mount is read-write, modifications to the WIM are staged in a staging * directory. * - * wimlib_mount() may be called from multiple threads without intervening calls - * to wimlib_unmount(), provided that different ::WIMStruct's are used. (This + * wimlib_mount_image() may be called from multiple threads without intervening calls + * to wimlib_unmount_image(), provided that different ::WIMStruct's are used. (This * was not the case for versions of this library 1.0.3 and earlier.) * - * wimlib_mount() cannot be used on an image that was exported with + * wimlib_mount_image() cannot be used on an image that was exported with * wimlib_export_image() while the dentry trees for both images are still in - * memory. In addition, wimlib_mount() may not be used to mount an image that + * memory. In addition, wimlib_mount_image() may not be used to mount an image that * has just been added with wimlib_add_image() or * wimlib_add_image_from_ntfs_volume(), unless the WIM has been written and read * into a new ::WIMStruct. @@ -963,11 +1071,12 @@ extern int wimlib_join(const char **swms, unsigned num_swms, * existing, single image. * @param dir * The path to an existing directory to mount the image on. - * @param flags + * @param mount_flags * Bitwise OR of the flags prefixed with WIMLIB_MOUNT_FLAG. If - * ::WIMLIB_MOUNT_FLAG_READWRITE is not given, the WIM is mounted - * read-only. The interface to the WIM named data streams is specified by - * exactly one of ::WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE, + * ::WIMLIB_MOUNT_FLAG_READWRITE is given, the WIM is mounted read-write + * rather than the default of read-only. The interface to the WIM named + * data streams is specified by exactly one of + * ::WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE, * ::WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR, or * ::WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS. The default interface is * the XATTR interface. @@ -1001,8 +1110,8 @@ extern int wimlib_join(const char **swms, unsigned num_swms, * @retval ::WIMLIB_ERR_INVALID_SECURITY_DATA * The security data for @a image in @a wim is invalid. * @retval ::WIMLIB_ERR_MKDIR - * ::WIMLIB_MOUNT_FLAG_READWRITE was specified in @a flags, but the staging - * directory could not be created. + * ::WIMLIB_MOUNT_FLAG_READWRITE was specified in @a mount_flags, but the + * staging directory could not be created. * @retval ::WIMLIB_ERR_NOMEM * Failed to allocate needed memory. * @retval ::WIMLIB_ERR_NOTDIR @@ -1019,30 +1128,34 @@ extern int wimlib_join(const char **swms, unsigned num_swms, * The WIM is a split WIM and a read-write mount was requested. We only * support mounting a split WIM read-only. */ -extern int wimlib_mount(WIMStruct *wim, int image, const char *dir, int flags, - WIMStruct **additional_swms, - unsigned num_additional_swms); +extern int wimlib_mount_image(WIMStruct *wim, int image, const char *dir, + int mount_flags, WIMStruct **additional_swms, + unsigned num_additional_swms); /** * Opens a WIM file and creates a ::WIMStruct for it. * * @param wim_file * The path to the WIM file to open. - * @param flags - * Bitwise OR of ::WIMLIB_OPEN_FLAG_CHECK_INTEGRITY and/or - * ::WIMLIB_OPEN_FLAG_SHOW_PROGRESS. - * If ::WIMLIB_OPEN_FLAG_CHECK_INTEGRITY is given, the integrity table - * of the WIM, if it exists, is checked, and the function will fail with an + * @param open_flags + * Bitwise OR of flags ::WIMLIB_OPEN_FLAG_CHECK_INTEGRITY and/or + * ::WIMLIB_OPEN_FLAG_SPLIT_OK. + * + * 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. - * If ::WIMLIB_OPEN_FLAG_SHOW_PROGRESS is given, progress information will - * be shown if the integrity of the WIM is checked. + * * 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.) * + * @param progress_func + * If non-NULL, a function that will be called periodically with the + * progress of the current operation. + * * @param wim_ret * On success, a pointer to an opaque ::WIMStruct for the opened WIM file * is written to the memory location pointed to by this parameter. The @@ -1060,7 +1173,7 @@ extern int wimlib_mount(WIMStruct *wim, int image, const char *dir, int flags, * in the WIM header, or the number of <IMAGE> elements in the XML * data for the WIM did not match the image count given in the WIM header. * @retval ::WIMLIB_ERR_INTEGRITY - * ::WIMLIB_OPEN_FLAG_CHECK_INTEGRITY was specified in @a flags and @a + * ::WIMLIB_OPEN_FLAG_CHECK_INTEGRITY was specified in @a open_flags and @a * wim_file contains an integrity table, but the SHA1 message digest for a * chunk of the WIM does not match the corresponding message digest given * in the integrity table. @@ -1072,7 +1185,7 @@ extern int wimlib_mount(WIMStruct *wim, int image, const char *dir, int flags, * @retval ::WIMLIB_ERR_INVALID_HEADER_SIZE * The length field of the WIM header is not 208. * @retval ::WIMLIB_ERR_INVALID_INTEGRITY_TABLE - * ::WIMLIB_OPEN_FLAG_CHECK_INTEGRITY was specified in @a flags and @a + * ::WIMLIB_OPEN_FLAG_CHECK_INTEGRITY was specified in @a open_flags and @a * wim_file contains an integrity table, but the integrity table is * invalid. * @retval ::WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY @@ -1089,15 +1202,16 @@ extern int wimlib_mount(WIMStruct *wim, int image, const char *dir, int flags, * data from @a wim_file. * @retval ::WIMLIB_ERR_SPLIT_UNSUPPORTED * @a wim_file is a split WIM, but ::WIMLIB_OPEN_FLAG_SPLIT_OK was not - * givin in @a flags. + * givin in @a open_flags. * @retval ::WIMLIB_ERR_UNKNOWN_VERSION * A number other than 0x10d00 is written in the version field of the WIM * header of @a wim_file. * @retval ::WIMLIB_ERR_XML * The XML data for @a wim_file is invalid. */ -extern int wimlib_open_wim(const char *wim_file, int flags, - WIMStruct **wim_ret); +extern int wimlib_open_wim(const char *wim_file, int open_flags, + WIMStruct **wim_ret, + wimlib_progress_func_t progress_func); /** * Overwrites the file that the WIM was originally read from, with changes made. @@ -1128,17 +1242,21 @@ extern int wimlib_open_wim(const char *wim_file, int flags, * termination of the program will result in the temporary file being orphaned. * In the direct append mode, the WIM is truncated to the original length on * failure, while abnormal termination of the program will result in extra data - * appended to the original WIM, but it will still be a valid WIM. + * appended to the original WIM, but it should still be a valid WIM. * * @param wim * Pointer to the ::WIMStruct for the WIM file to write. There may have * been in-memory changes made to it, which are then reflected in the * output file. * @param write_flags - * Bitwise OR of ::WIMLIB_WRITE_FLAG_CHECK_INTEGRITY and/or - * ::WIMLIB_WRITE_FLAG_SHOW_PROGRESS. + * Bitwise OR of the flags ::WIMLIB_WRITE_FLAG_CHECK_INTEGRITY, + * ::WIMLIB_WRITE_FLAG_REBUILD, ::WIMLIB_WRITE_FLAG_RECOMPRESS, and/or + * ::WIMLIB_WRITE_FLAG_SOFT_DELETE. * @param num_threads * Number of threads to use for compression (see wimlib_write()). + * @param progress_func + * If non-NULL, a function that will be called periodically with the + * progress of the current operation. * * @return 0 on success; nonzero on error. This function may return any value * returned by wimlib_write() as well as the following error codes: @@ -1155,14 +1273,8 @@ extern int wimlib_open_wim(const char *wim_file, int flags, * before calling wimlib_free(). */ extern int wimlib_overwrite(WIMStruct *wim, int write_flags, - unsigned num_threads); - -/** - * This function is deprecated; call wimlib_overwrite() instead. - * (wimlib_overwrite() no longer rebuilds the full WIM unless it has to or is - * specified explicitly with ::WIMLIB_WRITE_FLAG_REBUILD) - */ -extern int wimlib_overwrite_xml_and_header(WIMStruct *wim, int write_flags); + unsigned num_threads, + wimlib_progress_func_t progress_func); /** * Prints information about one image, or all images, contained in a WIM. @@ -1171,7 +1283,7 @@ extern int wimlib_overwrite_xml_and_header(WIMStruct *wim, int write_flags); * Pointer to the ::WIMStruct for a WIM file. * @param image * The image about which to print information. Can be the number of an - * image, or ::WIM_ALL_IMAGES to print information about all images in the + * image, or ::WIMLIB_ALL_IMAGES to print information about all images in the * WIM. * * @return This function has no return value. No error checking is done when @@ -1188,7 +1300,7 @@ extern void wimlib_print_available_images(const WIMStruct *wim, int image); * Pointer to the ::WIMStruct for a WIM file. * @param image * Which image to print files for. Can be the number of an image, or - * ::WIM_ALL_IMAGES to print the files contained in all images. + * ::WIMLIB_ALL_IMAGES to print the files contained in all images. * * @return 0 on success; nonzero on error. * @retval ::WIMLIB_ERR_DECOMPRESSION @@ -1199,7 +1311,7 @@ extern void wimlib_print_available_images(const WIMStruct *wim, int image); * images is invaled. * @retval ::WIMLIB_ERR_INVALID_IMAGE * @a image does not specify a valid image in @a wim, and is not - * ::WIM_ALL_IMAGES. + * ::WIMLIB_ALL_IMAGES. * @retval ::WIMLIB_ERR_INVALID_PARAM * @a wim was @c NULL. * @retval ::WIMLIB_ERR_INVALID_RESOURCE_SIZE @@ -1252,7 +1364,7 @@ extern void wimlib_print_lookup_table(WIMStruct *wim); * Pointer to the ::WIMStruct for a WIM file. * @param image * Which image to print the metadata for. Can be the number of an image, - * or ::WIM_ALL_IMAGES to print the metadata for all images in the WIM. + * or ::WIMLIB_ALL_IMAGES to print the metadata for all images in the WIM. * * @return 0 on success; nonzero on error. * @retval ::WIMLIB_ERR_DECOMPRESSION @@ -1263,7 +1375,7 @@ extern void wimlib_print_lookup_table(WIMStruct *wim); * images is invaled. * @retval ::WIMLIB_ERR_INVALID_IMAGE * @a image does not specify a valid image in @a wim, and is not - * ::WIM_ALL_IMAGES. + * ::WIMLIB_ALL_IMAGES. * @retval ::WIMLIB_ERR_INVALID_PARAM * @a wim was @c NULL. * @retval ::WIMLIB_ERR_INVALID_RESOURCE_SIZE @@ -1305,12 +1417,12 @@ extern void wimlib_print_wim_information(const WIMStruct *wim); * taken to be a string specifying the image number. Otherwise, it is * taken to be the name of an image, as specified in the XML data for the * WIM file. It also may be the keyword "all" or the string "*", both of - * which will resolve to ::WIM_ALL_IMAGES. + * which will resolve to ::WIMLIB_ALL_IMAGES. * * @return * If the string resolved to a single existing image, the number of that * image, counting starting at 1, is returned. If the keyword "all" was - * specified, ::WIM_ALL_IMAGES is returned. Otherwise, ::WIM_NO_IMAGE is + * specified, ::WIMLIB_ALL_IMAGES is returned. Otherwise, ::WIMLIB_NO_IMAGE is * returned. */ extern int wimlib_resolve_image(WIMStruct *wim, const char *image_name_or_num); @@ -1465,38 +1577,44 @@ extern int wimlib_set_print_errors(bool show_messages); /** * Splits a WIM into multiple parts. * - * @param wimfile - * Name of the WIM file to split. It must be a standalone, one-part WIM. + * @param wim + * The ::WIMStruct for the WIM to split. It must be a standalone, one-part + * WIM. * @param swm_name * Name of the SWM file to create. This will be the name of the first * part. The other parts will have the same name with 2, 3, 4, ..., etc. - * appended. + * appended before the suffix. * @param part_size * The maximum size per part, in bytes. It is not guaranteed that this * will really be the maximum size per part, because some file resources in * the WIM may be larger than this size, and the WIM file format provides * no way to split up file resources among multiple WIMs. - * @param flags - * Bitwise OR of ::WIMLIB_OPEN_FLAG_CHECK_INTEGRITY and/or - * ::WIMLIB_OPEN_FLAG_SHOW_PROGRESS. + * @param write_flags + * Bitwise OR of the flags ::WIMLIB_WRITE_FLAG_CHECK_INTEGRITY and/or + * ::WIMLIB_WRITE_FLAG_RECOMPRESS. + * @param progress_func + * If non-NULL, a function that will be called periodically with the + * progress of the current operation. * * @return 0 on success; nonzero on error. This function may return any value - * returned by wimlib_open_wim() as well as the following error codes: - * - * @retval ::WIMLIB_ERR_WRITE - * An error occurred when trying to write data to one of the split WIMs. + * returned by wimlib_write() as well as the following error codes: * + * @retval ::WIMLIB_ERR_SPLIT_UNSUPPORTED: + * @a wim is not part 1 of a stand-alone WIM. + * @retval ::WIMLIB_ERR_INVALID_PARAM + * @a w was @c NULL, @a swm_name was @c NULL, or @a part_size was 0. */ -extern int wimlib_split(const char *wimfile, const char *swm_name, - size_t part_size, int flags); +extern int wimlib_split(WIMStruct *wim, const char *swm_name, + size_t part_size, int write_flags, + wimlib_progress_func_t progress_func); /** - * Unmounts a WIM image that was mounted using wimlib_mount(). + * Unmounts a WIM image that was mounted using wimlib_mount_image(). * * Blocks until it is known whether the mount succeeded or failed. * - * To perform this operation, the process calling wimlib_unmount() communicates - * with the process that had called wimlib_mount(). + * To perform this operation, the process calling wimlib_unmount_image() + * communicates with the process that had called wimlib_mount_image(). * * There is currently a design problem with this function because it is hard to * know whether the filesystem daemon is still working or whether it has @@ -1508,8 +1626,8 @@ extern int wimlib_split(const char *wimfile, const char *swm_name, * * @param dir * The directory that the WIM image was mounted on. - * @param flags - * Bitwise OR of the flags ::WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY or + * @param unmount_flags + * Bitwise OR of the flags ::WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY and/or * ::WIMLIB_UNMOUNT_FLAG_COMMIT. Neither of these flags affect read-only * mounts. * @@ -1546,33 +1664,33 @@ extern int wimlib_split(const char *wimfile, const char *swm_name, * WIM file, or the filesystem daemon was unable to flush changes that had * been made to files in the staging directory. */ -extern int wimlib_unmount(const char *dir, int flags); +extern int wimlib_unmount_image(const char *dir, int unmount_flags); /** - * Writes the WIM to a file. + * Writes a standalone WIM to a file. * * @param wim - * Pointer to the ::WIMStruct for a WIM file. There may have been - * in-memory changes made to it, which are then reflected in the output - * file. + * Pointer to the ::WIMStruct for a WIM. There may have been in-memory + * changes made to it, which are then reflected in the output file. * @param path * The path to the file to write the WIM to. * @param image - * The image inside the WIM to write. Use ::WIM_ALL_IMAGES to include all + * The image inside the WIM to write. Use ::WIMLIB_ALL_IMAGES to include all * images. * @param write_flags - * Bitwise OR of ::WIMLIB_WRITE_FLAG_CHECK_INTEGRITY and/or - * ::WIMLIB_WRITE_FLAG_SHOW_PROGRESS. If - * ::WIMLIB_WRITE_FLAG_CHECK_INTEGRITY is given, an integrity table is - * included in the WIM being written. If ::WIMLIB_WRITE_FLAG_SHOW_PROGRESS - * is given, the progress of the calculation of the integrity table is - * shown. + * Bitwise OR of the flags ::WIMLIB_WRITE_FLAG_CHECK_INTEGRITY, + * ::WIMLIB_WRITE_FLAG_RECOMPRESS, ::WIMLIB_WRITE_FLAG_FSYNC, and/or + * ::WIMLIB_WRITE_FLAG_SOFT_DELETE. * @param num_threads * Number of threads to use for compressing data. Autodetected if set to - * 0. Note: if no data compression needs to be done, no threads will be - * created regardless of this parameter (e.g. if writing an uncompressed - * WIM, or exporting an image from a compressed WIM to another WIM of the - * same compression type). + * 0. Note: if no data compression needs to be done, no additional threads + * will be created regardless of this parameter (e.g. if writing an + * uncompressed WIM, or exporting an image from a compressed WIM to another + * WIM of the same compression type without ::WIMLIB_WRITE_FLAG_RECOMPRESS + * specified in @a write_flags). + * @param progress_func + * If non-NULL, a function that will be called periodically with the + * progress of the current operation. * * @return 0 on success; nonzero on error. * @retval ::WIMLIB_ERR_DECOMPRESSION @@ -1582,7 +1700,7 @@ extern int wimlib_unmount(const char *dir, int flags); * invalid. * @retval ::WIMLIB_ERR_INVALID_IMAGE * @a image does not specify a single existing image in @a wim, and is not - * ::WIM_ALL_IMAGES. + * ::WIMLIB_ALL_IMAGES. * @retval ::WIMLIB_ERR_INVALID_RESOURCE_HASH * A file that had previously been scanned for inclusion in the WIM by the * wimlib_add_image() or wimlib_add_image_from_ntfs_volume() functions was @@ -1612,9 +1730,7 @@ extern int wimlib_unmount(const char *dir, int flags); * path. */ extern int wimlib_write(WIMStruct *wim, const char *path, int image, - int write_flags, unsigned num_threads); - - + int write_flags, unsigned num_threads, + wimlib_progress_func_t progress_func); #endif /* _WIMLIB_H */ - diff --git a/src/wimlib_internal.h b/src/wimlib_internal.h index 49d1cbbc..87829957 100644 --- a/src/wimlib_internal.h +++ b/src/wimlib_internal.h @@ -39,6 +39,8 @@ struct stat; struct dentry; struct inode; +#include "wimlib.h" + #define WIM_MAGIC_LEN 8 #define WIM_GID_LEN 16 #define WIM_UNUSED_LEN 60 @@ -387,14 +389,14 @@ struct inode_table { struct hlist_head extra_inodes; }; -int init_inode_table(struct inode_table *table, size_t capacity); +extern int init_inode_table(struct inode_table *table, size_t capacity); static inline void destroy_inode_table(struct inode_table *table) { FREE(table->array); } -int inode_table_insert(struct dentry *dentry, void *__table); -u64 assign_inode_numbers(struct hlist_head *inode_list); -int fix_inodes(struct inode_table *table, struct hlist_head *inode_list); +extern int inode_table_insert(struct dentry *dentry, void *__table); +extern u64 assign_inode_numbers(struct hlist_head *inode_list); +extern int fix_inodes(struct inode_table *table, struct hlist_head *inode_list); /* header.c */ @@ -407,8 +409,10 @@ extern int write_integrity_table(FILE *out, struct resource_entry *integrity_res_entry, off_t new_lookup_table_end, off_t old_lookup_table_end, - bool show_progress); -extern int check_wim_integrity(WIMStruct *w, bool show_progress); + wimlib_progress_func_t progress_func); + +extern int check_wim_integrity(WIMStruct *w, + wimlib_progress_func_t progress_func); /* join.c */ @@ -434,7 +438,35 @@ extern int do_add_image(WIMStruct *w, const char *dir, const char *name, struct wim_security_data *, const struct capture_config *, int, void *), - void *extra_arg); + void *extra_arg, + wimlib_progress_func_t progress_func); + +/* ntfs-apply.c */ + +struct apply_args { + WIMStruct *w; + const char *target; + int extract_flags; + unsigned num_lutimes_warnings; + struct list_head *stream_list; + union wimlib_progress_info progress; +#ifdef WITH_NTFS_3G + struct _ntfs_volume *vol; +#endif + struct list_head empty_files; +}; + +extern int wim_apply_dentry_ntfs(struct dentry *dentry, void *arg); +extern int wim_apply_dentry_timestamps(struct dentry *dentry, void *arg); + +/* ntfs-capture.c */ +int build_dentry_tree_ntfs(struct dentry **root_p, + const char *device, + struct lookup_table *lookup_table, + struct wim_security_data *sd, + const struct capture_config *config, + int flags, + void *extra_arg); /* resource.c */ extern const u8 *get_resource_entry(const u8 *p, struct resource_entry *entry); @@ -487,14 +519,6 @@ extern int inode_set_symlink(struct inode *inode, struct lookup_table *lookup_table, struct lookup_table_entry **lte_ret); -extern void show_stream_op_progress(u64 *cur_size, u64 *next_size, - u64 total_size, u64 one_percent, - unsigned *cur_percent, - const struct lookup_table_entry *cur_lte, - const char *op); - -extern void finish_stream_op_progress(u64 total_size, const char *op); - /* wim.c */ extern WIMStruct *new_wim_struct(); extern int select_wim_image(WIMStruct *w, int image); @@ -513,15 +537,14 @@ extern int open_wim_writable(WIMStruct *w, const char *path, /* Internal use only */ #define WIMLIB_EXTRACT_FLAG_MULTI_IMAGE 0x80000000 #define WIMLIB_EXTRACT_FLAG_NO_STREAMS 0x40000000 -#define WIMLIB_EXTRACT_MASK_PUBLIC 0x2fffffff +#define WIMLIB_EXTRACT_MASK_PUBLIC 0x3fffffff /* write.c */ extern int begin_write(WIMStruct *w, const char *path, int write_flags); -extern int finish_write(WIMStruct *w, int image, int write_flags); - -#include "wimlib.h" +extern int finish_write(WIMStruct *w, int image, int write_flags, + wimlib_progress_func_t progress_func); #endif /* _WIMLIB_INTERNAL_H */ diff --git a/src/write.c b/src/write.c index 61a29962..a08bc850 100644 --- a/src/write.c +++ b/src/write.c @@ -158,7 +158,7 @@ typedef int (*compress_func_t)(const void *, unsigned, void *, unsigned *); compress_func_t get_compress_func(int out_ctype) { - if (out_ctype == WIM_COMPRESSION_TYPE_LZX) + if (out_ctype == WIMLIB_COMPRESSION_TYPE_LZX) return lzx_compress; else return xpress_compress; @@ -358,7 +358,7 @@ int write_wim_resource(struct lookup_table_entry *lte, struct chunk_table *chunk_tab = NULL; bool raw; off_t file_offset; - compress_func_t compress; + compress_func_t compress = NULL; #ifdef WITH_NTFS_3G ntfs_inode *ni = NULL; #endif @@ -382,7 +382,7 @@ int write_wim_resource(struct lookup_table_entry *lte, /* Are the compression types the same? If so, do a raw copy (copy * without decompressing and recompressing the data). */ raw = (wim_resource_compression_type(lte) == out_ctype - && out_ctype != WIM_COMPRESSION_TYPE_NONE + && out_ctype != WIMLIB_COMPRESSION_TYPE_NONE && !(flags & WIMLIB_RESOURCE_FLAG_RECOMPRESS)); if (raw) { @@ -402,7 +402,7 @@ int write_wim_resource(struct lookup_table_entry *lte, /* If we are writing a compressed resource and not doing a raw copy, we * need to initialize the chunk table */ - if (out_ctype != WIM_COMPRESSION_TYPE_NONE && !raw) { + if (out_ctype != WIMLIB_COMPRESSION_TYPE_NONE && !raw) { ret = begin_wim_resource_chunk_tab(lte, out_fp, file_offset, &chunk_tab); if (ret != 0) @@ -452,7 +452,7 @@ int write_wim_resource(struct lookup_table_entry *lte, /* Raw copy: The new compressed size is the same as the old compressed * size * - * Using WIM_COMPRESSION_TYPE_NONE: The new compressed size is the + * Using WIMLIB_COMPRESSION_TYPE_NONE: The new compressed size is the * original size * * Using a different compression type: Call @@ -462,7 +462,7 @@ int write_wim_resource(struct lookup_table_entry *lte, if (raw) { new_compressed_size = old_compressed_size; } else { - if (out_ctype == WIM_COMPRESSION_TYPE_NONE) + if (out_ctype == WIMLIB_COMPRESSION_TYPE_NONE) new_compressed_size = original_size; else { ret = finish_wim_resource_chunk_tab(chunk_tab, out_fp, @@ -494,7 +494,7 @@ int write_wim_resource(struct lookup_table_entry *lte, } if (!raw && new_compressed_size >= original_size && - out_ctype != WIM_COMPRESSION_TYPE_NONE) + out_ctype != WIMLIB_COMPRESSION_TYPE_NONE) { /* Oops! We compressed the resource to larger than the original * size. Write the resource uncompressed instead. */ @@ -504,7 +504,7 @@ int write_wim_resource(struct lookup_table_entry *lte, ret = WIMLIB_ERR_WRITE; goto out_fclose; } - ret = write_wim_resource(lte, out_fp, WIM_COMPRESSION_TYPE_NONE, + ret = write_wim_resource(lte, out_fp, WIMLIB_COMPRESSION_TYPE_NONE, out_res_entry, flags); if (ret != 0) goto out_fclose; @@ -519,7 +519,7 @@ int write_wim_resource(struct lookup_table_entry *lte, out_res_entry->offset = file_offset; out_res_entry->flags = lte->resource_entry.flags & ~WIM_RESHDR_FLAG_COMPRESSED; - if (out_ctype != WIM_COMPRESSION_TYPE_NONE) + if (out_ctype != WIMLIB_COMPRESSION_TYPE_NONE) out_res_entry->flags |= WIM_RESHDR_FLAG_COMPRESSED; } } @@ -647,68 +647,61 @@ static void *compressor_thread_proc(void *arg) shared_queue_put(compressed_res_queue, msg); } DEBUG("Compressor thread terminating"); + return NULL; } #endif -void show_stream_op_progress(u64 *cur_size, u64 *next_size, - u64 total_size, u64 one_percent, - unsigned *cur_percent, - const struct lookup_table_entry *cur_lte, - const char *op) -{ - if (*cur_size >= *next_size) { - printf("\r%"PRIu64" MiB of %"PRIu64" MiB " - "(uncompressed) %s (%u%% done)", - *cur_size >> 20, - total_size >> 20, op, *cur_percent); - fflush(stdout); - *next_size += one_percent; - (*cur_percent)++; - } - *cur_size += wim_resource_size(cur_lte); -} - -void finish_stream_op_progress(u64 total_size, const char *op) -{ - printf("\r%"PRIu64" MiB of %"PRIu64" MiB " - "(uncompressed) %s (100%% done)\n", - total_size >> 20, total_size >> 20, op); - fflush(stdout); -} - -static int write_stream_list_serial(struct list_head *stream_list, - FILE *out_fp, int out_ctype, - int write_flags, u64 total_size) +static int do_write_stream_list(struct list_head *my_resources, + FILE *out_fp, + int out_ctype, + wimlib_progress_func_t progress_func, + union wimlib_progress_info *progress, + int write_resource_flags) { - struct lookup_table_entry *lte; int ret; + struct lookup_table_entry *lte, *tmp; - u64 one_percent = total_size / 100; - u64 cur_size = 0; - u64 next_size = 0; - unsigned cur_percent = 0; - int write_resource_flags = 0; - - if (write_flags & WIMLIB_WRITE_FLAG_RECOMPRESS) - write_resource_flags |= WIMLIB_RESOURCE_FLAG_RECOMPRESS; - - list_for_each_entry(lte, stream_list, staging_list) { - if (write_flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS) { - show_stream_op_progress(&cur_size, &next_size, - total_size, one_percent, - &cur_percent, lte, "written"); - } - ret = write_wim_resource(lte, out_fp, out_ctype, + list_for_each_entry_safe(lte, tmp, my_resources, staging_list) { + ret = write_wim_resource(lte, + out_fp, + out_ctype, <e->output_resource_entry, write_resource_flags); if (ret != 0) return ret; + list_del(<e->staging_list); + progress->write_streams.completed_bytes += + wim_resource_size(lte); + progress->write_streams.completed_streams++; + if (progress_func) { + progress_func(WIMLIB_PROGRESS_MSG_WRITE_STREAMS, + progress); + } } - if (write_flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS) - finish_stream_op_progress(total_size, "written"); return 0; } +static int write_stream_list_serial(struct list_head *stream_list, + FILE *out_fp, + int out_ctype, + int write_flags, + wimlib_progress_func_t progress_func, + union wimlib_progress_info *progress) +{ + int write_resource_flags; + + if (write_flags & WIMLIB_WRITE_FLAG_RECOMPRESS) + write_resource_flags = WIMLIB_RESOURCE_FLAG_RECOMPRESS; + else + write_resource_flags = 0; + progress->write_streams.num_threads = 1; + if (progress_func) + progress_func(WIMLIB_PROGRESS_MSG_WRITE_STREAMS, progress); + return do_write_stream_list(stream_list, out_fp, + out_ctype, progress_func, + progress, write_resource_flags); +} + #ifdef ENABLE_MULTITHREADED_COMPRESSION static int write_wim_chunks(struct message *msg, FILE *out_fp, struct chunk_table *chunk_tab) @@ -751,7 +744,8 @@ static int main_writer_thread_proc(struct list_head *stream_list, struct shared_queue *compressed_res_queue, size_t queue_size, int write_flags, - u64 total_size) + wimlib_progress_func_t progress_func, + union wimlib_progress_info *progress) { int ret; @@ -800,14 +794,8 @@ static int main_writer_thread_proc(struct list_head *stream_list, struct lookup_table_entry *cur_lte = next_lte; struct chunk_table *cur_chunk_tab = NULL; - struct lookup_table_entry *lte; struct message *msg; - u64 one_percent = total_size / 100; - u64 cur_size = 0; - u64 next_size = 0; - unsigned cur_percent = 0; - #ifdef WITH_NTFS_3G ntfs_inode *ni = NULL; #endif @@ -1010,15 +998,6 @@ static int main_writer_thread_proc(struct list_head *stream_list, DEBUG2("Complete msg (begin_chunk=%"PRIu64")", msg->begin_chunk); if (msg->begin_chunk == 0) { DEBUG2("Begin chunk tab"); - if (write_flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS) { - show_stream_op_progress(&cur_size, - &next_size, - total_size, - one_percent, - &cur_percent, - cur_lte, - "written"); - } // This is the first set of chunks. Leave space // for the chunk table in the output file. @@ -1059,6 +1038,14 @@ static int main_writer_thread_proc(struct list_head *stream_list, if (ret != 0) goto out; + progress->write_streams.completed_bytes += + wim_resource_size(cur_lte); + progress->write_streams.completed_streams++; + + if (progress_func) { + progress_func(WIMLIB_PROGRESS_MSG_WRITE_STREAMS, + progress); + } cur_lte->output_resource_entry.size = res_csize; @@ -1096,31 +1083,14 @@ static int main_writer_thread_proc(struct list_head *stream_list, // to be compressed because the desired // compression type is the same as the previous // compression type). - struct lookup_table_entry *tmp; - list_for_each_entry_safe(lte, - tmp, - &my_resources, - staging_list) - { - if (write_flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS) { - show_stream_op_progress(&cur_size, - &next_size, - total_size, - one_percent, - &cur_percent, - lte, - "written"); - } - - ret = write_wim_resource(lte, - out_fp, - out_ctype, - <e->output_resource_entry, - 0); - list_del(<e->staging_list); - if (ret != 0) - goto out; - } + ret = do_write_stream_list(&my_resources, + out_fp, + out_ctype, + progress_func, + progress, + 0); + if (ret != 0) + goto out; } } } @@ -1132,25 +1102,9 @@ out: end_wim_resource_read(cur_lte); #endif if (ret == 0) { - list_for_each_entry(lte, &my_resources, staging_list) { - if (write_flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS) { - show_stream_op_progress(&cur_size, - &next_size, - total_size, - one_percent, - &cur_percent, - lte, - "written"); - } - ret = write_wim_resource(lte, out_fp, - out_ctype, - <e->output_resource_entry, - 0); - if (ret != 0) - break; - } - if (write_flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS) - finish_stream_op_progress(total_size, "written"); + ret = do_write_stream_list(&my_resources, out_fp, + out_ctype, progress_func, + progress, 0); } else { size_t num_available_msgs = 0; struct list_head *cur; @@ -1165,8 +1119,6 @@ out: } } - DEBUG("Freeing messages"); - for (size_t i = 0; i < ARRAY_LEN(msgs); i++) { for (size_t j = 0; j < MAX_CHUNKS_PER_MSG; j++) { FREE(msgs[i].compressed_chunks[j]); @@ -1180,22 +1132,13 @@ out: } -static const char *get_data_type(int ctype) -{ - switch (ctype) { - case WIM_COMPRESSION_TYPE_NONE: - return "uncompressed"; - case WIM_COMPRESSION_TYPE_LZX: - return "LZX-compressed"; - case WIM_COMPRESSION_TYPE_XPRESS: - return "XPRESS-compressed"; - } -} - static int write_stream_list_parallel(struct list_head *stream_list, - FILE *out_fp, int out_ctype, - int write_flags, u64 total_size, - unsigned num_threads) + FILE *out_fp, + int out_ctype, + int write_flags, + unsigned num_threads, + wimlib_progress_func_t progress_func, + union wimlib_progress_info *progress) { int ret; struct shared_queue res_to_compress_queue; @@ -1212,6 +1155,7 @@ static int write_stream_list_parallel(struct list_head *stream_list, } } + progress->write_streams.num_threads = num_threads; wimlib_assert(stream_list->next != stream_list); static const double MESSAGES_PER_THREAD = 2.0; @@ -1247,10 +1191,8 @@ static int write_stream_list_parallel(struct list_head *stream_list, } } - if (write_flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS) { - printf("Writing %s data using %u threads...\n", - get_data_type(out_ctype), num_threads); - } + if (progress_func) + progress_func(WIMLIB_PROGRESS_MSG_WRITE_STREAMS, progress); ret = main_writer_thread_proc(stream_list, out_fp, @@ -1259,8 +1201,8 @@ static int write_stream_list_parallel(struct list_head *stream_list, &compressed_res_queue, queue_size, write_flags, - total_size); - + progress_func, + progress); out_join: for (unsigned i = 0; i < num_threads; i++) shared_queue_put(&res_to_compress_queue, NULL); @@ -1279,8 +1221,13 @@ out_destroy_res_to_compress_queue: return ret; out_serial: WARNING("Falling back to single-threaded compression"); - return write_stream_list_serial(stream_list, out_fp, - out_ctype, write_flags, total_size); + return write_stream_list_serial(stream_list, + out_fp, + out_ctype, + write_flags, + progress_func, + progress); + } #endif @@ -1290,61 +1237,62 @@ out_serial: */ static int write_stream_list(struct list_head *stream_list, FILE *out_fp, int out_ctype, int write_flags, - unsigned num_threads) + unsigned num_threads, + wimlib_progress_func_t progress_func) { struct lookup_table_entry *lte; size_t num_streams = 0; - u64 total_size = 0; + u64 total_bytes = 0; bool compression_needed = false; + union wimlib_progress_info progress; + int ret; list_for_each_entry(lte, stream_list, staging_list) { num_streams++; - total_size += wim_resource_size(lte); + total_bytes += wim_resource_size(lte); if (!compression_needed && - (out_ctype != WIM_COMPRESSION_TYPE_NONE + (out_ctype != WIMLIB_COMPRESSION_TYPE_NONE && (lte->resource_location != RESOURCE_IN_WIM || wimlib_get_compression_type(lte->wim) != out_ctype || (write_flags & WIMLIB_WRITE_FLAG_REBUILD))) && wim_resource_size(lte) != 0) compression_needed = true; } + progress.write_streams.total_bytes = total_bytes; + progress.write_streams.total_streams = num_streams; + progress.write_streams.completed_bytes = 0; + progress.write_streams.completed_streams = 0; + progress.write_streams.num_threads = num_threads; + progress.write_streams.compression_type = out_ctype; if (num_streams == 0) { - if (write_flags & WIMLIB_WRITE_FLAG_VERBOSE) - printf("No streams to write\n"); - return 0; - } - - if (write_flags & WIMLIB_WRITE_FLAG_VERBOSE) { - printf("Preparing to write %zu streams " - "(%"PRIu64" total bytes uncompressed)\n", - num_streams, total_size); - printf("Using compression type %s\n", - wimlib_get_compression_type_string(out_ctype)); + ret = 0; + goto out; } #ifdef ENABLE_MULTITHREADED_COMPRESSION - if (compression_needed && total_size >= 1000000 && num_threads != 1) { - return write_stream_list_parallel(stream_list, out_fp, - out_ctype, write_flags, - total_size, num_threads); + if (compression_needed && total_bytes >= 1000000 && num_threads != 1) { + ret = write_stream_list_parallel(stream_list, + out_fp, + out_ctype, + write_flags, + num_threads, + progress_func, + &progress); } else #endif { - if (write_flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS) { - const char *reason = ""; - if (!compression_needed) - reason = " (no compression needed)"; - printf("Writing %s data using 1 thread%s\n", - get_data_type(out_ctype), reason); - } - - return write_stream_list_serial(stream_list, out_fp, - out_ctype, write_flags, - total_size); + ret = write_stream_list_serial(stream_list, + out_fp, + out_ctype, + write_flags, + progress_func, + &progress); } +out: + return ret; } @@ -1369,7 +1317,8 @@ static int find_streams_to_write(WIMStruct *w) } static int write_wim_streams(WIMStruct *w, int image, int write_flags, - unsigned num_threads) + unsigned num_threads, + wimlib_progress_func_t progress_func) { for_lookup_table_entry(w->lookup_table, lte_zero_out_refcnt, NULL); @@ -1378,7 +1327,7 @@ static int write_wim_streams(WIMStruct *w, int image, int write_flags, for_image(w, image, find_streams_to_write); return write_stream_list(&stream_list, w->out_fp, wimlib_get_compression_type(w), write_flags, - num_threads); + num_threads, progress_func); } /* @@ -1410,7 +1359,8 @@ static int write_wim_streams(WIMStruct *w, int image, int write_flags, * fsync() the output file before closing it. * */ -int finish_write(WIMStruct *w, int image, int write_flags) +int finish_write(WIMStruct *w, int image, int write_flags, + wimlib_progress_func_t progress_func) { int ret; struct wim_header hdr; @@ -1462,7 +1412,6 @@ int finish_write(WIMStruct *w, int image, int write_flags) off_t old_lookup_table_end; off_t new_lookup_table_end; - bool show_progress; if (write_flags & WIMLIB_WRITE_FLAG_REUSE_INTEGRITY_TABLE) { old_lookup_table_end = w->hdr.lookup_table_res_entry.offset + w->hdr.lookup_table_res_entry.size; @@ -1471,13 +1420,12 @@ int finish_write(WIMStruct *w, int image, int write_flags) } new_lookup_table_end = hdr.lookup_table_res_entry.offset + hdr.lookup_table_res_entry.size; - show_progress = ((write_flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS) != 0); ret = write_integrity_table(out, &hdr.integrity, new_lookup_table_end, old_lookup_table_end, - show_progress); + progress_func); if (ret != 0) goto out; } else { @@ -1492,7 +1440,7 @@ int finish_write(WIMStruct *w, int image, int write_flags) * marked as bootable. This is not well documented... */ if (hdr.boot_idx == 0 || !w->image_metadata - || (image != WIM_ALL_IMAGES && image != hdr.boot_idx)) { + || (image != WIMLIB_ALL_IMAGES && image != hdr.boot_idx)) { memset(&hdr.boot_metadata_res_entry, 0, sizeof(struct resource_entry)); } else { @@ -1503,7 +1451,7 @@ int finish_write(WIMStruct *w, int image, int write_flags) } /* Set image count and boot index correctly for single image writes */ - if (image != WIM_ALL_IMAGES) { + if (image != WIMLIB_ALL_IMAGES) { hdr.image_count = 1; if (hdr.boot_idx == image) hdr.boot_idx = 1; @@ -1567,7 +1515,8 @@ int begin_write(WIMStruct *w, const char *path, int write_flags) /* Writes a stand-alone WIM to a file. */ WIMLIBAPI int wimlib_write(WIMStruct *w, const char *path, - int image, int write_flags, unsigned num_threads) + int image, int write_flags, unsigned num_threads, + wimlib_progress_func_t progress_func) { int ret; @@ -1576,7 +1525,7 @@ WIMLIBAPI int wimlib_write(WIMStruct *w, const char *path, write_flags &= WIMLIB_WRITE_MASK_PUBLIC; - if (image != WIM_ALL_IMAGES && + if (image != WIMLIB_ALL_IMAGES && (image < 1 || image > w->hdr.image_count)) return WIMLIB_ERR_INVALID_IMAGE; @@ -1585,29 +1534,26 @@ WIMLIBAPI int wimlib_write(WIMStruct *w, const char *path, return WIMLIB_ERR_SPLIT_UNSUPPORTED; } - if (image == WIM_ALL_IMAGES) - DEBUG("Writing all images to `%s'.", path); - else - DEBUG("Writing image %d to `%s'.", image, path); - ret = begin_write(w, path, write_flags); if (ret != 0) goto out; - ret = write_wim_streams(w, image, write_flags, num_threads); + ret = write_wim_streams(w, image, write_flags, num_threads, + progress_func); if (ret != 0) goto out; - if (write_flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS) - printf("Writing image metadata...\n"); + if (progress_func) + progress_func(WIMLIB_PROGRESS_MSG_WRITE_METADATA_BEGIN, NULL); ret = for_image(w, image, write_metadata_resource); if (ret != 0) goto out; - ret = finish_write(w, image, write_flags); - if (ret == 0 && (write_flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS)) - printf("Successfully wrote `%s'\n", path); + if (progress_func) + progress_func(WIMLIB_PROGRESS_MSG_WRITE_METADATA_END, NULL); + + ret = finish_write(w, image, write_flags, progress_func); out: close_wim_writable(w); return ret; @@ -1705,6 +1651,7 @@ static int find_new_streams(struct lookup_table_entry *lte, void *arg) */ static int overwrite_wim_inplace(WIMStruct *w, int write_flags, unsigned num_threads, + wimlib_progress_func_t progress_func, int modified_image_idx) { int ret; @@ -1781,7 +1728,8 @@ static int overwrite_wim_inplace(WIMStruct *w, int write_flags, old_wim_end); ret = write_stream_list(&stream_list, w->out_fp, wimlib_get_compression_type(w), - write_flags, num_threads); + write_flags, num_threads, + progress_func); if (ret != 0) goto out_ftruncate; } else { @@ -1795,7 +1743,8 @@ static int overwrite_wim_inplace(WIMStruct *w, int write_flags, goto out_ftruncate; } write_flags |= WIMLIB_WRITE_FLAG_REUSE_INTEGRITY_TABLE; - ret = finish_write(w, WIM_ALL_IMAGES, write_flags); + ret = finish_write(w, WIMLIB_ALL_IMAGES, write_flags, + progress_func); out_ftruncate: close_wim_writable(w); if (ret != 0) { @@ -1807,12 +1756,13 @@ out_ftruncate: } static int overwrite_wim_via_tmpfile(WIMStruct *w, int write_flags, - unsigned num_threads) + unsigned num_threads, + wimlib_progress_func_t progress_func) { size_t wim_name_len; int ret; - DEBUG("Overwrining `%s' via a temporary file", w->filename); + DEBUG("Overwriting `%s' via a temporary file", w->filename); /* Write the WIM to a temporary file in the same directory as the * original WIM. */ @@ -1822,9 +1772,9 @@ static int overwrite_wim_via_tmpfile(WIMStruct *w, int write_flags, randomize_char_array_with_alnum(tmpfile + wim_name_len, 9); tmpfile[wim_name_len + 9] = '\0'; - ret = wimlib_write(w, tmpfile, WIM_ALL_IMAGES, + ret = wimlib_write(w, tmpfile, WIMLIB_ALL_IMAGES, write_flags | WIMLIB_WRITE_FLAG_FSYNC, - num_threads); + num_threads, progress_func); if (ret != 0) { ERROR("Failed to write the WIM file `%s'", tmpfile); goto err; @@ -1846,8 +1796,12 @@ static int overwrite_wim_via_tmpfile(WIMStruct *w, int write_flags, goto err; } - if (write_flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS) - printf("Successfully renamed `%s' to `%s'\n", tmpfile, w->filename); + if (progress_func) { + union wimlib_progress_info progress; + progress.rename.from = tmpfile; + progress.rename.to = w->filename; + progress_func(WIMLIB_PROGRESS_MSG_RENAME, &progress); + } /* Re-open the WIM read-only. */ w->fp = fopen(w->filename, "rb"); @@ -1868,7 +1822,8 @@ err: * Writes a WIM file to the original file that it was read from, overwriting it. */ WIMLIBAPI int wimlib_overwrite(WIMStruct *w, int write_flags, - unsigned num_threads) + unsigned num_threads, + wimlib_progress_func_t progress_func) { if (!w) return WIMLIB_ERR_INVALID_PARAM; @@ -1895,14 +1850,10 @@ WIMLIBAPI int wimlib_overwrite(WIMStruct *w, int write_flags, ; if (i == w->hdr.image_count) { return overwrite_wim_inplace(w, write_flags, num_threads, + progress_func, modified_image_idx); } } - return overwrite_wim_via_tmpfile(w, write_flags, num_threads); -} - -/* Deprecated */ -WIMLIBAPI int wimlib_overwrite_xml_and_header(WIMStruct *wim, int write_flags) -{ - return wimlib_overwrite(wim, write_flags, 1); + return overwrite_wim_via_tmpfile(w, write_flags, num_threads, + progress_func); } diff --git a/src/xml.c b/src/xml.c index f9e93fc3..09f567e3 100644 --- a/src/xml.c +++ b/src/xml.c @@ -1299,7 +1299,7 @@ int write_xml_data(const struct wim_info *wim_info, int image, FILE *out, size_t bytes_written; off_t start_offset, end_offset; - wimlib_assert(image == WIM_ALL_IMAGES || + wimlib_assert(image == WIMLIB_ALL_IMAGES || (wim_info != NULL && image >= 1 && image <= wim_info->num_images)); @@ -1353,7 +1353,7 @@ int write_xml_data(const struct wim_info *wim_info, int image, FILE *out, if (wim_info != NULL) { DEBUG("Writing %d elements", (int)wim_info->num_images); for (int i = 1; i <= (int)wim_info->num_images; i++) { - if (image != WIM_ALL_IMAGES && i != image) + if (image != WIMLIB_ALL_IMAGES && i != image) continue; DEBUG("Writing element for image %d", i); ret = xml_write_image_info(writer, &wim_info->images[i - 1]); -- 2.43.0