+ if (argc != 1)
+ goto out_usage;
+
+ if (unmount_flags & WIMLIB_UNMOUNT_FLAG_NEW_IMAGE) {
+ if (!(unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT)) {
+ imagex_error(T("--new-image is meaningless "
+ "without --commit also specified!"));
+ goto out_err;
+ }
+ }
+
+ ret = wimlib_unmount_image_with_progress(argv[0], unmount_flags,
+ imagex_progress_func, NULL);
+ if (ret) {
+ imagex_error(T("Failed to unmount \"%"TS"\""), argv[0]);
+ if (ret == WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY) {
+ imagex_printf(T(
+ "\tNote: Use --commit --force to force changes "
+ "to be committed, regardless\n"
+ "\t of open files.\n"));
+ }
+ }
+out:
+ return ret;
+
+out_usage:
+ usage(CMD_UNMOUNT, stderr);
+out_err:
+ ret = -1;
+ goto out;
+}
+#endif /* WIM_MOUNTING_SUPPORTED */
+
+/*
+ * Add, delete, or rename files in a WIM image.
+ */
+static int
+imagex_update(int argc, tchar **argv, int cmd)
+{
+ const tchar *wimfile;
+ int image;
+ WIMStruct *wim;
+ int ret;
+ int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
+ int write_flags = 0;
+ int update_flags = WIMLIB_UPDATE_FLAG_SEND_PROGRESS;
+ int default_add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
+ WIMLIB_ADD_FLAG_VERBOSE |
+ WIMLIB_ADD_FLAG_WINCONFIG;
+ int default_delete_flags = 0;
+ unsigned num_threads = 0;
+ int c;
+ tchar *cmd_file_contents;
+ size_t cmd_file_nchars;
+ struct wimlib_update_command *cmds;
+ size_t num_cmds;
+ tchar *command_str = NULL;
+ tchar *config_file = NULL;
+ tchar *wimboot_config = NULL;
+
+ for_opt(c, update_options) {
+ switch (c) {
+ /* Generic or write options */
+ case IMAGEX_THREADS_OPTION:
+ num_threads = parse_num_threads(optarg);
+ if (num_threads == UINT_MAX)
+ goto out_err;
+ break;
+ case IMAGEX_CHECK_OPTION:
+ open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
+ write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
+ break;
+ case IMAGEX_REBUILD_OPTION:
+ write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
+ break;
+ case IMAGEX_COMMAND_OPTION:
+ if (command_str) {
+ imagex_error(T("--command may only be specified "
+ "one time. Please provide\n"
+ " the update commands "
+ "on standard input instead."));
+ goto out_err;
+ }
+ command_str = tstrdup(optarg);
+ if (!command_str) {
+ imagex_error(T("Out of memory!"));
+ goto out_err;
+ }
+ break;
+ case IMAGEX_WIMBOOT_CONFIG_OPTION:
+ wimboot_config = optarg;
+ break;
+ /* Default delete options */
+ case IMAGEX_FORCE_OPTION:
+ default_delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
+ break;
+ case IMAGEX_RECURSIVE_OPTION:
+ default_delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
+ break;
+
+ /* Global add option */
+ case IMAGEX_CONFIG_OPTION:
+ default_add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
+ config_file = optarg;
+ break;
+
+ /* Default add options */
+ case IMAGEX_VERBOSE_OPTION:
+ /* No longer does anything. */
+ break;
+ case IMAGEX_DEREFERENCE_OPTION:
+ default_add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
+ break;
+ case IMAGEX_UNIX_DATA_OPTION:
+ default_add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
+ break;
+ case IMAGEX_NO_ACLS_OPTION:
+ default_add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
+ break;
+ case IMAGEX_STRICT_ACLS_OPTION:
+ default_add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
+ break;
+ case IMAGEX_NO_REPLACE_OPTION:
+ default_add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
+ break;
+ case IMAGEX_UNSAFE_COMPACT_OPTION:
+ write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
+ break;
+ default:
+ goto out_usage;
+ }
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (argc != 1 && argc != 2)
+ goto out_usage;
+ wimfile = argv[0];
+
+ ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
+ imagex_progress_func, NULL);
+ if (ret)
+ goto out_free_command_str;
+
+ if (argc >= 2) {
+ /* Image explicitly specified. */
+ image = wimlib_resolve_image(wim, argv[1]);
+ ret = verify_image_exists_and_is_single(image, argv[1],
+ wimfile);
+ if (ret)
+ goto out_wimlib_free;
+ } else {
+ /* No image specified; default to image 1, but only if the WIM
+ * contains exactly one image. */
+ struct wimlib_wim_info info;
+
+ wimlib_get_wim_info(wim, &info);
+ if (info.image_count != 1) {
+ imagex_error(T("\"%"TS"\" contains %d images; Please select one."),
+ wimfile, info.image_count);
+ wimlib_free(wim);
+ goto out_usage;
+ }
+ image = 1;
+ }
+
+ /* Read update commands from standard input, or the command string if
+ * specified. */
+ if (command_str) {
+ cmd_file_contents = NULL;
+ cmds = parse_update_command_file(&command_str, tstrlen(command_str),
+ &num_cmds);
+ if (!cmds) {
+ ret = -1;
+ goto out_free_cmd_file_contents;
+ }
+ } else if (!wimboot_config) {
+ if (isatty(STDIN_FILENO)) {
+ tputs(T("Reading update commands from standard input..."));
+ recommend_man_page(CMD_UPDATE, stdout);
+ }
+ cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars);
+ if (!cmd_file_contents) {
+ ret = -1;
+ goto out_wimlib_free;
+ }
+
+ /* Parse the update commands */
+ cmds = parse_update_command_file(&cmd_file_contents, cmd_file_nchars,
+ &num_cmds);
+ if (!cmds) {
+ ret = -1;
+ goto out_free_cmd_file_contents;
+ }
+ } else {
+ cmd_file_contents = NULL;
+ cmds = NULL;
+ num_cmds = 0;
+ }
+
+ /* Set default flags and capture config on the update commands */
+ for (size_t i = 0; i < num_cmds; i++) {
+ switch (cmds[i].op) {
+ case WIMLIB_UPDATE_OP_ADD:
+ cmds[i].add.add_flags |= default_add_flags;
+ cmds[i].add.config_file = config_file;
+ break;
+ case WIMLIB_UPDATE_OP_DELETE:
+ cmds[i].delete_.delete_flags |= default_delete_flags;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Execute the update commands */
+ ret = wimlib_update_image(wim, image, cmds, num_cmds, update_flags);
+ if (ret)
+ goto out_free_cmds;
+
+ if (wimboot_config) {
+ /* --wimboot-config=FILE is short for an
+ * "add FILE /Windows/System32/WimBootCompress.ini" command.
+ */
+ struct wimlib_update_command cmd;
+
+ cmd.op = WIMLIB_UPDATE_OP_ADD;
+ cmd.add.fs_source_path = wimboot_config;
+ cmd.add.wim_target_path = T("/Windows/System32/WimBootCompress.ini");
+ cmd.add.config_file = NULL;
+ cmd.add.add_flags = 0;
+
+ ret = wimlib_update_image(wim, image, &cmd, 1, update_flags);
+ if (ret)
+ goto out_free_cmds;
+ }
+
+ /* Overwrite the updated WIM */
+ ret = wimlib_overwrite(wim, write_flags, num_threads);
+out_free_cmds:
+ free(cmds);
+out_free_cmd_file_contents:
+ free(cmd_file_contents);
+out_wimlib_free:
+ wimlib_free(wim);
+out_free_command_str:
+ free(command_str);
+ return ret;
+
+out_usage:
+ usage(CMD_UPDATE, stderr);
+out_err:
+ ret = -1;
+ goto out_free_command_str;
+}
+
+/* Verify a WIM file. */
+static int
+imagex_verify(int argc, tchar **argv, int cmd)
+{
+ int ret;
+ const tchar *wimfile;
+ WIMStruct *wim;
+ int open_flags = WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
+ int verify_flags = 0;
+ STRING_SET(refglobs);
+ int c;
+
+ for_opt(c, verify_options) {
+ switch (c) {
+ case IMAGEX_REF_OPTION:
+ ret = string_set_append(&refglobs, optarg);
+ if (ret)
+ goto out_free_refglobs;
+ break;
+ case IMAGEX_NOCHECK_OPTION:
+ open_flags &= ~WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
+ break;
+ default:
+ goto out_usage;
+ }
+ }
+
+ argv += optind;
+ argc -= optind;
+