# include "imagex-win32.h"
# define tbasename win32_wbasename
# define tglob win32_wglob
+# define OS_PREFERRED_PATH_SEPARATOR L'\\'
+# define OS_PREFERRED_PATH_SEPARATOR_STRING L"\\"
#else /* __WIN32__ */
# include <glob.h>
# include <getopt.h>
# include <langinfo.h>
# define tbasename basename
# define tglob glob
+# define OS_PREFERRED_PATH_SEPARATOR '/'
+# define OS_PREFERRED_PATH_SEPARATOR_STRING "/"
#endif /* !__WIN32 */
" (DIRECTORY | NTFS_VOLUME) [--check] [--hardlink]\n"
" [--symlink] [--verbose] [--ref=\"GLOB\"] [--unix-data]\n"
" [--no-acls] [--strict-acls] [--rpfix] [--norpfix]\n"
+" [--include-invalid-names]\n"
),
[CAPTURE] =
T(
IMAGEX_PROGNAME" extract WIMFILE (IMAGE_NUM | IMAGE_NAME) [PATH...]\n"
" [--check] [--ref=\"GLOB\"] [--verbose] [--unix-data]\n"
" [--no-acls] [--strict-acls] [--to-stdout] [--dest-dir=DIR]\n"
+" [--include-invalid-names]\n"
),
[INFO] =
T(
T(
IMAGEX_PROGNAME" update WIMFILE [IMAGE_NUM | IMAGE_NAME] [--check] [--rebuild]\n"
" [--threads=NUM_THREADS] [DEFAULT_ADD_OPTIONS]\n"
-" [DEFAULT_DELETE_OPTIONS] < CMDFILE\n"
+" [DEFAULT_DELETE_OPTIONS] [--command=STRING] [< CMDFILE]\n"
),
};
IMAGEX_ALLOW_OTHER_OPTION,
IMAGEX_BOOT_OPTION,
IMAGEX_CHECK_OPTION,
+ IMAGEX_COMMAND_OPTION,
IMAGEX_COMMIT_OPTION,
IMAGEX_COMPRESS_OPTION,
IMAGEX_CONFIG_OPTION,
IMAGEX_FORCE_OPTION,
IMAGEX_HARDLINK_OPTION,
IMAGEX_HEADER_OPTION,
+ IMAGEX_INCLUDE_INVALID_NAMES_OPTION,
IMAGEX_LAZY_OPTION,
IMAGEX_LOOKUP_TABLE_OPTION,
IMAGEX_METADATA_OPTION,
{T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
{T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
{T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
+ {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
{NULL, 0, NULL, 0},
};
static const struct option capture_or_append_options[] = {
{T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
{T("dest-dir"), required_argument, NULL, IMAGEX_DEST_DIR_OPTION},
{T("to-stdout"), no_argument, NULL, IMAGEX_TO_STDOUT_OPTION},
+ {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
{NULL, 0, NULL, 0},
};
{T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
{T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
{T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
+ {T("command"), required_argument, NULL, IMAGEX_COMMAND_OPTION},
/* Default delete options */
{T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
{T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
{T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
{T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
+
{NULL, 0, NULL, 0},
};
return NULL;
}
+#define GIBIBYTE_MIN_NBYTES 10000000000ULL
+#define MEBIBYTE_MIN_NBYTES 10000000ULL
+#define KIBIBYTE_MIN_NBYTES 10000ULL
+
+static unsigned
+get_unit(uint64_t total_bytes, const tchar **name_ret)
+{
+ if (total_bytes >= GIBIBYTE_MIN_NBYTES) {
+ *name_ret = T("GiB");
+ return 30;
+ } else if (total_bytes >= MEBIBYTE_MIN_NBYTES) {
+ *name_ret = T("MiB");
+ return 20;
+ } else if (total_bytes >= KIBIBYTE_MIN_NBYTES) {
+ *name_ret = T("KiB");
+ return 10;
+ } else {
+ *name_ret = T("bytes");
+ return 0;
+ }
+}
+
/* Progress callback function passed to various wimlib functions. */
static int
imagex_progress_func(enum wimlib_progress_msg msg,
const union wimlib_progress_info *info)
{
unsigned percent_done;
+ unsigned unit_shift;
+ const tchar *unit_name;
if (imagex_be_quiet)
return 0;
switch (msg) {
case WIMLIB_PROGRESS_MSG_WRITE_STREAMS:
+ unit_shift = get_unit(info->write_streams.total_bytes, &unit_name);
percent_done = TO_PERCENT(info->write_streams.completed_bytes,
info->write_streams.total_bytes);
if (info->write_streams.completed_streams == 0) {
data_type, info->write_streams.num_threads,
(info->write_streams.num_threads == 1) ? T("") : T("s"));
}
- tprintf(T("\r%"PRIu64" MiB of %"PRIu64" MiB (uncompressed) "
+ tprintf(T("\r%"PRIu64" %"TS" of %"PRIu64" %"TS" (uncompressed) "
"written (%u%% done)"),
- info->write_streams.completed_bytes >> 20,
- info->write_streams.total_bytes >> 20,
+ info->write_streams.completed_bytes >> unit_shift,
+ unit_name,
+ info->write_streams.total_bytes >> unit_shift,
+ unit_name,
percent_done);
if (info->write_streams.completed_bytes >= info->write_streams.total_bytes)
tputchar(T('\n'));
case WIMLIB_PROGRESS_MSG_SCAN_BEGIN:
tprintf(T("Scanning \"%"TS"\""), info->scan.source);
if (*info->scan.wim_target_path) {
- tprintf(T(" (loading as WIM path: \"/%"TS"\")...\n"),
+ tprintf(T(" (loading as WIM path: "
+ "\""WIMLIB_WIM_PATH_SEPARATOR_STRING"%"TS"\")...\n"),
info->scan.wim_target_path);
} else {
tprintf(T(" (loading as root of WIM image)...\n"));
/*case WIMLIB_PROGRESS_MSG_SCAN_END:*/
/*break;*/
case WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY:
+ unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
percent_done = TO_PERCENT(info->integrity.completed_bytes,
info->integrity.total_bytes);
- tprintf(T("\rVerifying integrity of \"%"TS"\": %"PRIu64" MiB "
- "of %"PRIu64" MiB (%u%%) done"),
+ tprintf(T("\rVerifying integrity of \"%"TS"\": %"PRIu64" %"TS" "
+ "of %"PRIu64" %"TS" (%u%%) done"),
info->integrity.filename,
- info->integrity.completed_bytes >> 20,
- info->integrity.total_bytes >> 20,
+ info->integrity.completed_bytes >> unit_shift,
+ unit_name,
+ info->integrity.total_bytes >> unit_shift,
+ unit_name,
percent_done);
if (info->integrity.completed_bytes == info->integrity.total_bytes)
tputchar(T('\n'));
break;
case WIMLIB_PROGRESS_MSG_CALC_INTEGRITY:
+ unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
percent_done = TO_PERCENT(info->integrity.completed_bytes,
info->integrity.total_bytes);
- tprintf(T("\rCalculating integrity table for WIM: %"PRIu64" MiB "
- "of %"PRIu64" MiB (%u%%) done"),
- info->integrity.completed_bytes >> 20,
- info->integrity.total_bytes >> 20,
+ tprintf(T("\rCalculating integrity table for WIM: %"PRIu64" %"TS" "
+ "of %"PRIu64" %"TS" (%u%%) done"),
+ info->integrity.completed_bytes >> unit_shift,
+ unit_name,
+ info->integrity.total_bytes >> unit_shift,
+ unit_name,
percent_done);
if (info->integrity.completed_bytes == info->integrity.total_bytes)
tputchar(T('\n'));
info->extract.target);
break;
case WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN:
- tprintf(T("Extracting \"%"TS"\" from image %d (\"%"TS"\") "
+ tprintf(T("Extracting "
+ "\""WIMLIB_WIM_PATH_SEPARATOR_STRING"%"TS"\" from image %d (\"%"TS"\") "
"in \"%"TS"\" to \"%"TS"\"\n"),
info->extract.extract_root_wim_source_path,
info->extract.image,
case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS:
percent_done = TO_PERCENT(info->extract.completed_bytes,
info->extract.total_bytes);
+ unit_shift = get_unit(info->extract.total_bytes, &unit_name);
tprintf(T("\rExtracting files: "
- "%"PRIu64" MiB of %"PRIu64" MiB (%u%%) done"),
- info->extract.completed_bytes >> 20,
- info->extract.total_bytes >> 20,
+ "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
+ info->extract.completed_bytes >> unit_shift,
+ unit_name,
+ info->extract.total_bytes >> unit_shift,
+ unit_name,
percent_done);
if (info->extract.completed_bytes >= info->extract.total_bytes)
tputchar(T('\n'));
case WIMLIB_PROGRESS_MSG_JOIN_STREAMS:
percent_done = TO_PERCENT(info->join.completed_bytes,
info->join.total_bytes);
+ unit_shift = get_unit(info->join.total_bytes, &unit_name);
tprintf(T("Writing resources from part %u of %u: "
- "%"PRIu64 " MiB of %"PRIu64" MiB (%u%%) written\n"),
+ "%"PRIu64 " %"TS" of %"PRIu64" %"TS" (%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,
+ info->join.completed_bytes >> unit_shift,
+ unit_name,
+ info->join.total_bytes >> unit_shift,
+ unit_name,
percent_done);
break;
case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART:
percent_done = TO_PERCENT(info->split.completed_bytes,
info->split.total_bytes);
- tprintf(T("Writing \"%"TS"\": %"PRIu64" MiB of "
- "%"PRIu64" MiB (%u%%) written\n"),
+ unit_shift = get_unit(info->split.total_bytes, &unit_name);
+ tprintf(T("Writing \"%"TS"\": %"PRIu64" %"TS" of "
+ "%"PRIu64" %"TS" (%u%%) written\n"),
info->split.part_name,
- info->split.completed_bytes >> 20,
- info->split.total_bytes >> 20,
+ info->split.completed_bytes >> unit_shift,
+ unit_name,
+ info->split.total_bytes >> unit_shift,
+ unit_name,
percent_done);
break;
case WIMLIB_PROGRESS_MSG_SPLIT_END_PART:
info->split.cur_part_number);
}
break;
+ case WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND:
+ switch (info->update.command->op) {
+ case WIMLIB_UPDATE_OP_DELETE:
+ tprintf(T("Deleted WIM path "
+ "\""WIMLIB_WIM_PATH_SEPARATOR_STRING "%"TS"\"\n"),
+ info->update.command->delete.wim_path);
+ break;
+ case WIMLIB_UPDATE_OP_RENAME:
+ tprintf(T("Renamed WIM path "
+ "\""WIMLIB_WIM_PATH_SEPARATOR_STRING "%"TS"\" => "
+ "\""WIMLIB_WIM_PATH_SEPARATOR_STRING "%"TS"\"\n"),
+ info->update.command->rename.wim_source_path,
+ info->update.command->rename.wim_target_path);
+ break;
+ case WIMLIB_UPDATE_OP_ADD:
+ default:
+ break;
+ }
+ break;
default:
break;
}
case IMAGEX_RPFIX_OPTION:
extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
break;
+ case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
+ extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
+ extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
+ break;
default:
usage(APPLY);
return -1;
bool capture_sources_malloced;
struct wimlib_capture_source *capture_sources;
size_t num_sources;
+ bool name_defaulted;
for_opt(c, capture_or_append_options) {
switch (c) {
if (argc >= 3) {
name = argv[2];
+ name_defaulted = false;
} else {
/* Set default name to SOURCE argument, omitting any directory
* prefixes and trailing slashes. This requires making a copy
- * of @source. */
+ * of @source. Leave some free characters at the end in case we
+ * append a number to keep the name unique. */
source_name_len = tstrlen(source);
- source_copy = alloca((source_name_len + 1) * sizeof(tchar));
+ source_copy = alloca((source_name_len + 1 + 25) * sizeof(tchar));
name = tbasename(tstrcpy(source_copy, source));
+ name_defaulted = true;
}
/* Image description defaults to NULL if not given. */
desc = (argc >= 4) ? argv[3] : NULL;
}
}
}
+
+ if (cmd == APPEND && name_defaulted) {
+ /* If the user did not specify an image name, and the basename
+ * of the source already exists as an image name in the WIM
+ * file, append a suffix to make it unique. */
+ unsigned long conflict_idx;
+ tchar *name_end = tstrchr(name, T('\0'));
+ for (conflict_idx = 1;
+ wimlib_image_name_in_use(w, name);
+ conflict_idx++)
+ {
+ tsprintf(name_end, T(" (%lu)"), conflict_idx);
+ }
+ }
#ifdef __WIN32__
win32_acquire_capture_privileges();
#endif
free_extract_commands(cmds, num_cmds, dest_dir);
return NULL;
}
- tsprintf(cmds[i].fs_dest_path, T("%"TS"/%"TS), dest_dir,
- tbasename(paths[i]));
+ tsprintf(cmds[i].fs_dest_path, T("%"TS""OS_PREFERRED_PATH_SEPARATOR_STRING"%"TS),
+ dest_dir, tbasename(paths[i]));
}
}
*num_cmds_ret = num_cmds;
extract_flags |= WIMLIB_EXTRACT_FLAG_TO_STDOUT;
imagex_be_quiet = true;
break;
+ case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
+ extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
+ extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
+ break;
default:
goto out_usage;
}
int ret;
int open_flags = 0;
int write_flags = 0;
- int update_flags = 0;
+ int update_flags = WIMLIB_UPDATE_FLAG_SEND_PROGRESS;
int default_add_flags = WIMLIB_ADD_IMAGE_FLAG_EXCLUDE_VERBOSE;
int default_delete_flags = 0;
unsigned num_threads = 0;
int c;
- tchar *cmd_file_contents;
+ tchar *cmd_file_contents = NULL;
size_t cmd_file_nchars;
struct wimlib_update_command *cmds;
size_t num_cmds;
int num_images;
+ tchar *command_str = NULL;
const tchar *config_file = NULL;
tchar *config_str;
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."));
+ ret = -1;
+ goto out;
+ }
+ command_str = tstrdup(optarg);
+ if (!command_str) {
+ imagex_error(T("Out of memory!"));
+ ret = -1;
+ goto out;
+ }
+ break;
/* Default delete options */
case IMAGEX_FORCE_OPTION:
default_delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
config = &default_capture_config;
}
- /* Read update commands from standard input */
- if (isatty(STDIN_FILENO)) {
- tputs(T("Reading update commands from standard input..."));
- recommend_man_page(T("update"));
- }
- cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars);
- if (!cmd_file_contents) {
- ret = -1;
- goto out_free_config;
- }
+ /* Read update commands from standard input, or the command string if
+ * specified. */
+ if (command_str) {
+ cmds = parse_update_command_file(&command_str, tstrlen(command_str),
+ &num_cmds);
+ } else {
+ if (isatty(STDIN_FILENO)) {
+ tputs(T("Reading update commands from standard input..."));
+ recommend_man_page(T("update"));
+ }
+ cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars);
+ if (!cmd_file_contents) {
+ ret = -1;
+ goto out_free_config;
+ }
- /* Parse the update commands */
- cmds = parse_update_command_file(&cmd_file_contents, cmd_file_nchars,
- &num_cmds);
+ /* 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;
}
}
+#ifdef __WIN32__
+ win32_acquire_capture_privileges();
+#endif
+
/* Execute the update commands */
ret = wimlib_update_image(wim, image, cmds, num_cmds, update_flags,
imagex_progress_func);
if (ret)
- goto out_free_cmds;
+ goto out_release_privs;
/* Overwrite the updated WIM */
ret = wimlib_overwrite(wim, write_flags, num_threads,
imagex_progress_func);
-out_free_cmds:
+out_release_privs:
+#ifdef __WIN32__
+ win32_release_capture_privileges();
+#endif
free(cmds);
out_free_cmd_file_contents:
free(cmd_file_contents);
out_wimlib_free:
wimlib_free(wim);
out:
+ free(command_str);
return ret;
out_usage:
usage(UPDATE);