From e5d5e659df8cddbd5e336e5c91513f8c435e3633 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 9 Mar 2013 01:08:29 -0600 Subject: [PATCH] Various changes * imagex.c: Add more comments in imagex.c * imagex.1.in: Documentation updates/fixes * imagex-capture.1.in: Documentation updates/fixes * wimlib.h, util.c: Add new error code WIMLIB_ERR_INVALID_OVERLAY * wimlib.h: Improve docs for wimlib_add_image_multisource() * add_image.c: Only parse capture config one time * imagex.c: Check for stdout write errors --- doc/imagex-capture.1.in | 22 +++--- doc/imagex.1.in | 29 +++++--- programs/imagex.c | 148 +++++++++++++++++++++++++++++++++------- src/add_image.c | 108 ++++++++++++++++------------- src/util.c | 2 + src/wimlib.h | 13 +++- src/write.c | 9 +-- 7 files changed, 233 insertions(+), 98 deletions(-) diff --git a/doc/imagex-capture.1.in b/doc/imagex-capture.1.in index f17f0e62..1323f4fe 100644 --- a/doc/imagex-capture.1.in +++ b/doc/imagex-capture.1.in @@ -23,8 +23,8 @@ in the entire WIM, regardless of how many images the file appears in. \fISOURCE\fR specifies the location of the files to create the new WIM image from. If \fISOURCE\fR is a directory, the WIM image is captured from that -directory. Alternatively, \fISOURCE\fR is a regular file or block device, it is -interpreted as a NTFS volume from which a WIM image is to be captured. Still +directory. Alternatively, if \fISOURCE\fR is a regular file or block device, it +is interpreted as a NTFS volume from which a WIM image is to be captured. Still alternatively, if the \fB--source-list\fR option is given, \fISOURCE\fR is interpreted as a file that itself provides a list of files and directories to include in the new WIM image. @@ -103,8 +103,9 @@ Win32+DOS namespace, and POSIX namespace. This includes hard links. Yet another capture mode is entered when the \fB--source-list\fR option is given. It is essentially an extension of the \fBNORMAL MODE\fR that allows -multiple files or directories to be incorporated into a WIM image in a single -command. See the documentation for \fB--source-list\fR below. +multiple files or directories to be incorporated into a WIM image using a single +\fBimagex capture\fR command. See the documentation for \fB--source-list\fR +below. .SH OPTIONS .TP 6 @@ -244,13 +245,14 @@ if the same file appears in different overlays. Filenames containing whitespace may be quoted with either single quotes or double quotes. Quotes may not be escaped. -Empty lines, and lines beginning with '#' followed by optional whitespace, are -ignored. +Lines consisting only of whitespace and lines beginning with '#' preceded by +optional whitespace are ignored. -As a special case, if \fISOURCE\fR is "-" the source list is read from standard +As a special case, if \fISOURCE\fR is "-", the source list is read from standard input rather than an external file. -The NTFS capture mode cannot be used with \fB--source-list\fR. +The NTFS capture mode cannot be used with \fB--source-list\fR, as only capturing +a full NTFS volume is supported. .SH NOTES @@ -285,8 +287,8 @@ as a duplicate once it's been checksummed. \fISOURCE\fR may be a symbolic link to a directory rather than a directory itself. However, additional symbolic links in subdirectories, or in additional -source directories not destined for the WIM image root with -(\fB--source-list\fR), are not dereferenced unless \fB--dereference\fR is +source directories not destined for the WIM image root (with +\fB--source-list\fR), are not dereferenced unless \fB--dereference\fR is specified. .SH EXAMPLES diff --git a/doc/imagex.1.in b/doc/imagex.1.in index 1431ad38..bff55178 100644 --- a/doc/imagex.1.in +++ b/doc/imagex.1.in @@ -42,7 +42,8 @@ There is a separate manual page for each \fBimagex\fR command. .SH SUPPORTED FEATURES -The following general features are currently supported: +The following general features are currently supported (note: this is not a +complete list): .IP \[bu] 3 Create a stand-alone WIM from a directory or NTFS volume (\fBimagex capture\fR) @@ -87,10 +88,10 @@ when appropriate. .IP \[bu] 4 Because Microsoft designed the WIM file format to accomodate Windows-specific -and NTFS-specific features, \fBimagex\fR must have two separate image capture -and application modes (although the \fBimagex\fR subcommands for the modes are -the same): one for general image capture and application, and one for the -capture or application of an image specifically from/to an NTFS volume. +and NTFS-specific features, wimlib must have two separate image capture and +application modes (although the \fBimagex\fR subcommands for the modes are the +same): one for general image capture and application, and one for the capture or +application of an image specifically from/to an NTFS volume. .IP \[bu] Microsoft's version has some weird limitations, like it won't let you extract a @@ -100,13 +101,17 @@ unusual limitations, although it won't actually run on Windows anyway. .IP \[bu] There are bugs in Microsoft's WIM library and I obviously have not included the -same bugs in wimlib, although in some cases I have had to work around some -bugs for compatibility purposes. +same bugs in wimlib, although in some cases I have had to work around bugs for +compatibility purposes. .IP \[bu] -wimlib's \fBimagex\fR offers the extra commands \fBimagex optimize\fR and -\fBimagex join\fR to easily remove holes in a WIM or join the parts of a split -WIM, respectively. +wimlib's \fBimagex\fR offers the extra command \fBimagex optimize\fR, +which lets you easily remove wasted space in a WIM (which can arise after +a WIM image is appended or mounted read-write). + +.IP \[bu] +wimlib's \fBimagex\fR also offers the command \fBimagex join\fR, which lets you +easily join the parts of a split WIM. .IP \[bu] wimlib's \fBimagex apply\fR supports keeping files hard-linked or symlinked @@ -133,6 +138,10 @@ wimlib's \fBimagex mount\fR and \fBimagex mountrw\fR are much faster than Microsoft's versions for some reason. I don't know what they have their program do that takes so long to simply set up a mountpoint. +.IP \[bu] +wimlib's \fBimagex mount\fR supports mounting an image from a split WIM, but +Microsoft's software does not. + .SH WARNING Note: \fBwimlib\fR and \fBimagex\fR are experimental. Use Microsoft's diff --git a/programs/imagex.c b/programs/imagex.c index 0b9af005..b16579e7 100644 --- a/programs/imagex.c +++ b/programs/imagex.c @@ -49,7 +49,7 @@ opts, NULL)) != -1) enum imagex_op_type { - APPEND, + APPEND = 0, APPLY, CAPTURE, DELETE, @@ -115,12 +115,6 @@ static const char *usage_strings[] = { "imagex unmount DIRECTORY [--commit] [--check] [--rebuild]\n", }; -static const struct option common_options[] = { - {"help", 0, NULL, 'h'}, - {"version", 0, NULL, 'v'}, - {NULL, 0, NULL, 0}, -}; - static const struct option apply_options[] = { {"check", no_argument, NULL, 'c'}, {"hardlink", no_argument, NULL, 'h'}, @@ -264,6 +258,7 @@ static int verify_image_exists_and_is_single(int image, const char *image_name, return ret; } +/* Parse the argument to --compress */ static int get_compression_type(const char *optarg) { if (strcasecmp(optarg, "maximum") == 0 || strcasecmp(optarg, "lzx") == 0) @@ -279,6 +274,8 @@ static int get_compression_type(const char *optarg) } } +/* Returns the size of a file given its name, or -1 if the file does not exist + * or its size cannot be determined. */ static off_t file_get_size(const char *filename) { struct stat st; @@ -303,8 +300,13 @@ static const char *default_capture_config = "*.cab\n" "\\WINDOWS\\inf\\*.pnf\n"; +/* Read standard input until EOF and return the full contents in a malloc()ed + * buffer and the number of bytes of data in @len_ret. Returns NULL on read + * error. */ static char *stdin_get_contents(size_t *len_ret) { + /* stdin can, of course, be a pipe or other non-seekable file, so the + * total length of the data cannot be pre-determined */ char *buf = NULL; size_t newlen = 1024; size_t pos = 0; @@ -343,12 +345,34 @@ enum { PARSE_FILENAME_NONE = 2, }; -static int parse_filename(char **fn_ret, char **line_p, size_t *len_p) +/* + * Parses a filename in the source list file format. (See the man page for + * 'imagex capture' for details on this format and the meaning.) Accepted + * formats for filenames are an unquoted string, whitespace-delimited, or a + * double or single-quoted string. + * + * @line_p: Pointer to the pointer to the line of data. Will be updated + * to point past the filename iff the return value is + * PARSE_FILENAME_SUCCESS. If *len_p > 0, (*line_p)[*len_p - 1] must + * be '\0'. + * + * @len_p: @len_p initially stores the length of the line of data, which may + * be 0, and it will be updated to the number of bytes remaining in + * the line iff the return value is PARSE_FILENAME_SUCCESS. + * + * @fn_ret: Iff the return value is PARSE_FILENAME_SUCCESS, a pointer to the + * parsed filename will be returned here. + * + * Returns: PARSE_FILENAME_SUCCESS if a filename was successfully parsed; or + * PARSE_FILENAME_FAILURE if the data was invalid due to a missing + * closing quote; or PARSE_FILENAME_NONE if the line ended before the + * beginning of a filename was found. + */ +static int parse_filename(char **line_p, size_t *len_p, char **fn_ret) { size_t len = *len_p; char *line = *line_p; char *fn; - int ret; char quote_char; /* Skip leading whitespace */ @@ -367,11 +391,7 @@ static int parse_filename(char **fn_ret, char **line_p, size_t *len_p) len--; fn = line; line = memchr(line, quote_char, len); - if (line) { - *line = '\0'; - len -= line - fn; - ret = PARSE_FILENAME_SUCCESS; - } else { + if (!line) { imagex_error("Missing closing quote: %s", fn - 1); return PARSE_FILENAME_FAILURE; } @@ -381,31 +401,47 @@ static int parse_filename(char **fn_ret, char **line_p, size_t *len_p) fn = line; do { line++; - len--; } while (!isspace(*line) && *line != '\0'); - *line = '\0'; - ret = PARSE_FILENAME_SUCCESS; } + *line = '\0'; + len -= line - fn; *len_p = len; *line_p = line; *fn_ret = fn; - return ret; + return PARSE_FILENAME_SUCCESS; } +/* Parses a line of data (not an empty line or comment) in the source list file + * format. (See the man page for 'imagex capture' for details on this format + * and the meaning.) + * + * @line: Line of data to be parsed. line[len - 1] must be '\0', unless + * len == 0. The data in @line will be modified by this function call. + * + * @len: Length of the line of data. + * + * @source: On success, the capture source and target described by the line is + * written into this destination. Note that it will contain pointers + * to data in the @line array. + * + * Returns true if the line was valid; false otherwise. */ static bool parse_source_list_line(char *line, size_t len, struct wimlib_capture_source *source) { + /* SOURCE [DEST] */ int ret; - ret = parse_filename(&source->fs_source_path, &line, &len); + ret = parse_filename(&line, &len, &source->fs_source_path); if (ret != PARSE_FILENAME_SUCCESS) return false; - ret = parse_filename(&source->wim_target_path, &line, &len); + ret = parse_filename(&line, &len, &source->wim_target_path); if (ret == PARSE_FILENAME_NONE) source->wim_target_path = source->fs_source_path; return ret != PARSE_FILENAME_FAILURE; } +/* Returns %true if the given line of length @len > 0 is a comment or empty line + * in the source list file format. */ static bool is_comment_line(const char *line, size_t len) { for (;;) { @@ -420,6 +456,23 @@ static bool is_comment_line(const char *line, size_t len) } } +/* Parses a file in the source list format. (See the man page for 'imagex + * capture' for details on this format and the meaning.) + * + * @source_list_contents: Contents of the source list file. Note that this + * buffer will be modified to save memory allocations, + * and cannot be freed until the returned array of + * wimlib_capture_source's has also been freed. + * + * @source_list_nbytes: Number of bytes of data in the @source_list_contents + * buffer. + * + * @nsources_ret: On success, the length of the returned array is + * returned here. + * + * Returns: An array of `struct wimlib_capture_source's that can be passed to + * the wimlib_add_image_multisource() function to specify how a WIM image is to + * be created. */ static struct wimlib_capture_source * parse_source_list(char *source_list_contents, size_t source_list_nbytes, size_t *nsources_ret) @@ -457,6 +510,7 @@ parse_source_list(char *source_list_contents, size_t source_list_nbytes, return sources; } +/* Reads the contents of a file into memory. */ static char *file_get_contents(const char *filename, size_t *len_ret) { struct stat stbuf; @@ -498,6 +552,8 @@ out: return buf; } +/* Return 0 if a path names a file to which the current user has write access; + * -1 otherwise (and print an error message). */ static int file_writable(const char *path) { int ret; @@ -510,6 +566,8 @@ static int file_writable(const char *path) #define TO_PERCENT(numerator, denominator) \ (((denominator) == 0) ? 0 : ((numerator) * 100 / (denominator))) +/* Given an enumerated value for WIM compression type, return a descriptive + * string. */ static const char *get_data_type(int ctype) { switch (ctype) { @@ -523,6 +581,7 @@ static const char *get_data_type(int ctype) return NULL; } +/* Progress callback function passed to various wimlib functions. */ static int imagex_progress_func(enum wimlib_progress_msg msg, const union wimlib_progress_info *info) { @@ -657,7 +716,10 @@ static int imagex_progress_func(enum wimlib_progress_msg msg, return 0; } - +/* Open all the split WIM parts that correspond to a file glob. + * + * @first_part specifies the first part of the split WIM and it may be either + * included or omitted from the glob. */ static int open_swms_from_glob(const char *swm_glob, const char *first_part, int open_flags, @@ -729,7 +791,8 @@ static unsigned parse_num_threads(const char *optarg) } -/* Extract one image, or all images, from a WIM file into a directory. */ +/* Apply one image, or all images, from a WIM file into a directory, OR apply + * one image from a WIM file to a NTFS volume. */ static int imagex_apply(int argc, char **argv) { int c; @@ -843,6 +906,9 @@ out: return ret; } +/* Create a WIM image from a directory tree, NTFS volume, or multiple files or + * directory trees. 'imagex capture': create a new WIM file containing the + * desired image. 'imagex append': add a new image to an existing WIM file. */ static int imagex_capture_or_append(int argc, char **argv) { int c; @@ -866,7 +932,7 @@ static int imagex_capture_or_append(int argc, char **argv) const char *config_file = NULL; char *config_str = NULL; - size_t config_len = 0; + size_t config_len; bool source_list = false; size_t source_list_nbytes; @@ -934,13 +1000,18 @@ static int imagex_capture_or_append(int argc, char **argv) if (argc >= 3) { name = argv[2]; } else { + /* Set default name to SOURCE argument, omitting any directory + * prefixes and trailing slashes. This requires making a copy + * of @source. */ source_name_len = strlen(source); source_copy = alloca(source_name_len + 1); name = basename(strcpy(source_copy, source)); } + /* Image description defaults to NULL if not given. */ desc = (argc >= 4) ? argv[3] : NULL; if (source_list) { + /* Set up capture sources in source list mode */ if (source[0] == '-' && source[1] == '\0') { source_list_contents = stdin_get_contents(&source_list_nbytes); } else { @@ -959,6 +1030,8 @@ static int imagex_capture_or_append(int argc, char **argv) } capture_sources_malloced = true; } else { + /* Set up capture source in non-source-list mode (could be + * either "normal" mode or "NTFS mode"--- see the man page). */ capture_sources = alloca(sizeof(struct wimlib_capture_source)); capture_sources[0].fs_source_path = source; capture_sources[0].wim_target_path = NULL; @@ -1768,6 +1841,7 @@ mount_usage: return -1; } +/* Rebuild a WIM file */ static int imagex_optimize(int argc, char **argv) { int c; @@ -1881,7 +1955,7 @@ static int imagex_split(int argc, char **argv) return ret; } -/* Unmounts an image. */ +/* Unmounts a mounted WIM image. */ static int imagex_unmount(int argc, char **argv) { int c; @@ -2017,7 +2091,7 @@ static void usage_all() fputs(extra, stdout); } - +/* Entry point for the 'imagex' program. */ int main(int argc, char **argv) { const struct imagex_command *cmd; @@ -2029,15 +2103,22 @@ int main(int argc, char **argv) return 1; } + /* Handle --help and --version for all commands. Note that this will + * not return if either of these arguments are present. */ help_or_version(argc, argv); argc--; argv++; + /* The user may like to see more informative error messages. */ wimlib_set_print_errors(true); + + /* Calling wimlib_global_init() is not strictly necessary because + * 'imagex' is single-threaded. */ ret = wimlib_global_init(); if (ret) goto out; + /* Search for the function to handle the 'imagex' subcommand. */ for_imagex_command(cmd) { if (strcmp(cmd->name, *argv) == 0) { ret = cmd->func(argc, argv); @@ -2049,6 +2130,21 @@ int main(int argc, char **argv) usage_all(); return 1; out: + /* For 'imagex info' and 'imagex dir', data printed to standard output + * is part of the program's actual behavior and not just for + * informational purposes, so we should set a failure exit status if + * there was a write error. */ + if (cmd == &imagex_commands[INFO] || cmd == &imagex_commands[DIR]) { + if (ferror(stdout) || fclose(stdout)) { + imagex_error_with_errno("output error"); + if (ret == 0) + ret = -1; + } + } + + /* Exit status (ret): -1 indicates an error found by 'imagex' outside + * of the wimlib library code. 0 indicates success. > 0 indicates a + * wimlib error code from which an error message can be printed. */ if (ret > 0) { imagex_error("Exiting with error code %d:\n" " %s.", ret, @@ -2056,6 +2152,8 @@ out: if (ret == WIMLIB_ERR_NTFS_3G && errno != 0) imagex_error_with_errno("errno"); } + /* Calling wimlib_global_cleanup() is not strictly necessary because the + * process is exiting anyway. */ wimlib_global_cleanup(); return ret; } diff --git a/src/add_image.c b/src/add_image.c index 0d1ffb29..fc2f6a20 100644 --- a/src/add_image.c +++ b/src/add_image.c @@ -448,11 +448,10 @@ static int pattern_list_add_pattern(struct pattern_list *list, /* Parses the contents of the image capture configuration file and fills in a * `struct capture_config'. */ -static int init_capture_config(const char *_config_str, size_t config_len, - const char *_prefix, struct capture_config *config) +static int init_capture_config(struct capture_config *config, + const char *_config_str, size_t config_len) { char *config_str; - char *prefix; char *p; char *eol; char *next_p; @@ -469,17 +468,10 @@ static int init_capture_config(const char *_config_str, size_t config_len, ERROR("Could not duplicate capture config string"); return WIMLIB_ERR_NOMEM; } - prefix = STRDUP(_prefix); - if (!prefix) { - FREE(config_str); - return WIMLIB_ERR_NOMEM; - } memcpy(config_str, _config_str, config_len); next_p = config_str; config->config_str = config_str; - config->prefix = prefix; - config->prefix_len = strlen(prefix); while (bytes_remaining) { line_no++; p = next_p; @@ -554,6 +546,19 @@ out_destroy: return ret; } +static int capture_config_set_prefix(struct capture_config *config, + const char *_prefix) +{ + char *prefix = STRDUP(_prefix); + + if (!prefix) + return WIMLIB_ERR_NOMEM; + FREE(config->prefix); + config->prefix = prefix; + config->prefix_len = strlen(prefix); + return 0; +} + static bool match_pattern(const char *path, const char *path_basename, const struct pattern_list *list) { @@ -641,17 +646,14 @@ static void canonicalize_targets(struct wimlib_capture_source *sources, sources->wim_target_path); sources->wim_target_path = (char*)canonicalize_target_path(sources->wim_target_path); - DEBUG("\"%s\"", sources->wim_target_path); + DEBUG("Canonical target: \"%s\"", sources->wim_target_path); sources++; } } static int capture_source_cmp(const void *p1, const void *p2) { - const struct wimlib_capture_source *s1, *s2; - - s1 = p1; - s2 = p2; + const struct wimlib_capture_source *s1 = p1, *s2 = p2; return strcmp(s1->wim_target_path, s2->wim_target_path); } @@ -728,6 +730,8 @@ new_filler_directory(const char *name) DEBUG("Creating filler directory \"%s\"", name); dentry = new_dentry_with_inode(name); if (dentry) { + /* Set the inode number to 0 for now. The final inode number + * will be assigned later by assign_inode_numbers(). */ dentry->d_inode->i_ino = 0; dentry->d_inode->i_resolved = 1; dentry->d_inode->i_attributes = FILE_ATTRIBUTE_DIRECTORY; @@ -738,29 +742,29 @@ new_filler_directory(const char *name) /* Transfers the children of @branch to @target. It is an error if @target is * not a directory or if both @branch and @target contain a child dentry with * the same name. */ -static int do_overlay(struct wim_dentry *target, - struct wim_dentry *branch) +static int do_overlay(struct wim_dentry *target, struct wim_dentry *branch) { struct rb_root *rb_root; if (!dentry_is_directory(target)) { ERROR("Cannot overlay directory `%s' over non-directory", branch->file_name_utf8); - /* XXX Use a different error code */ - return WIMLIB_ERR_INVALID_DENTRY; + return WIMLIB_ERR_INVALID_OVERLAY; } rb_root = &branch->d_inode->i_children; while (rb_root->rb_node) { /* While @branch has children... */ - struct wim_dentry *child; - - child = container_of(rb_root->rb_node, struct wim_dentry, rb_node); + struct wim_dentry *child = rbnode_dentry(rb_root->rb_node); + /* Move @child to the directory @target */ unlink_dentry(child); if (!dentry_add_child(target, child)) { + /* Revert the change to avoid leaking the directory tree + * rooted at @child */ dentry_add_child(branch, child); - ERROR("Overlay error: file `%s' already exists as child of `%s'", + ERROR("Overlay error: file `%s' already exists " + "as a child of `%s'", child->file_name_utf8, target->file_name_utf8); - return WIMLIB_ERR_INVALID_DENTRY; + return WIMLIB_ERR_INVALID_OVERLAY; } } return 0; @@ -775,7 +779,8 @@ static int do_overlay(struct wim_dentry *target, * @branch * Branch to add. * @target_path: - * Path in the WIM image to add the branch. + * Path in the WIM image to add the branch, with leading and trailing + * slashes stripped. */ static int attach_branch(struct wim_dentry **root_p, struct wim_dentry *branch, @@ -818,9 +823,11 @@ static int attach_branch(struct wim_dentry **root_p, } parent = dentry; target_path = slash; + /* Skip over slashes. Note: this cannot overrun the length of + * the string because the last character cannot be a slash, as + * trailing slashes were tripped. */ do { ++target_path; - wimlib_assert(*target_path != '\0'); } while (*target_path == '/'); } @@ -850,7 +857,8 @@ WIMLIBAPI int wimlib_add_image_multisource(WIMStruct *w, const struct capture_config *, int, wimlib_progress_func_t, void *); void *extra_arg; - struct wim_dentry *root_dentry = NULL; + struct wim_dentry *root_dentry; + struct wim_dentry *branch; struct wim_security_data *sd; struct capture_config config; struct wim_image_metadata *imd; @@ -900,14 +908,15 @@ WIMLIBAPI int wimlib_add_image_multisource(WIMStruct *w, config_str = default_config; config_len = strlen(default_config); } - memset(&config, 0, sizeof(struct capture_config)); + ret = init_capture_config(&config, config_str, config_len); + if (ret) + goto out; DEBUG("Allocating security data"); - sd = CALLOC(1, sizeof(struct wim_security_data)); if (!sd) { ret = WIMLIB_ERR_NOMEM; - goto out_destroy_config; + goto out_destroy_capture_config; } sd->total_length = 8; sd->refcnt = 1; @@ -927,24 +936,26 @@ WIMLIBAPI int wimlib_add_image_multisource(WIMStruct *w, if (!root_dentry) goto out_free_security_data; } else { - size_t i = 0; - struct wim_dentry *branch; - int flags; + size_t i; + + root_dentry = NULL; + i = 0; do { + int flags; + union wimlib_progress_info progress; + DEBUG("Building dentry tree for source %zu of %zu " "(\"%s\" => \"%s\")", i + 1, num_sources, sources[i].fs_source_path, sources[i].wim_target_path); - union wimlib_progress_info progress; if (progress_func) { memset(&progress, 0, sizeof(progress)); progress.scan.source = sources[i].fs_source_path; progress.scan.wim_target_path = sources[i].wim_target_path; progress_func(WIMLIB_PROGRESS_MSG_SCAN_BEGIN, &progress); } - ret = init_capture_config(config_str, config_len, - sources[i].fs_source_path, - &config); + ret = capture_config_set_prefix(&config, + sources[i].fs_source_path); if (ret) goto out_free_dentry_tree; flags = add_image_flags | WIMLIB_ADD_IMAGE_FLAG_SOURCE; @@ -961,21 +972,20 @@ WIMLIBAPI int wimlib_add_image_multisource(WIMStruct *w, goto out_free_dentry_tree; } if (branch) { + /* Use the target name, not the source name, for + * the root of each branch from a capture + * source. (This will also set the root dentry + * of the entire image to be unnamed.) */ ret = set_dentry_name(branch, path_basename(sources[i].wim_target_path)); - if (ret) { - free_dentry_tree(branch, w->lookup_table); - goto out_free_dentry_tree; - } + if (ret) + goto out_free_branch; ret = attach_branch(&root_dentry, branch, sources[i].wim_target_path); - if (ret) { - free_dentry_tree(branch, w->lookup_table); - goto out_free_dentry_tree; - } + if (ret) + goto out_free_branch; } - destroy_capture_config(&config); if (progress_func) progress_func(WIMLIB_PROGRESS_MSG_SCAN_END, &progress); } while (++i != num_sources); @@ -1011,12 +1021,14 @@ out_destroy_imd: destroy_image_metadata(&w->image_metadata[w->hdr.image_count - 1], w->lookup_table); w->hdr.image_count--; - return ret; + goto out; +out_free_branch: + free_dentry_tree(branch, w->lookup_table); out_free_dentry_tree: free_dentry_tree(root_dentry, w->lookup_table); out_free_security_data: free_security_data(sd); -out_destroy_config: +out_destroy_capture_config: destroy_capture_config(&config); out: return ret; diff --git a/src/util.c b/src/util.c index e566b52b..9db817ac 100644 --- a/src/util.c +++ b/src/util.c @@ -165,6 +165,8 @@ static const char *error_strings[] = { = "The WIM's integrity table is invalid", [WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY] = "An entry in the WIM's lookup table is invalid", + [WIMLIB_ERR_INVALID_OVERLAY] + = "Conflicting files in overlay when creating a WIM image", [WIMLIB_ERR_INVALID_PARAM] = "An invalid parameter was given", [WIMLIB_ERR_INVALID_PART_NUMBER] diff --git a/src/wimlib.h b/src/wimlib.h index 36178298..a46608ee 100644 --- a/src/wimlib.h +++ b/src/wimlib.h @@ -790,6 +790,7 @@ enum wimlib_error_code { WIMLIB_ERR_UNSUPPORTED, WIMLIB_ERR_WRITE, WIMLIB_ERR_XML, + WIMLIB_ERR_INVALID_OVERLAY, }; @@ -892,7 +893,17 @@ extern int wimlib_add_image(WIMStruct *wim, const char *source, * specifying the @a sources and @a num_sources parameters instead of the @a * source parameter. The rest of the parameters are the same as * wimlib_add_image(). See the documentation for imagex capture for full - * details on how this mode works. */ + * details on how this mode works. + * + * Additional notes: @a sources is not a @c const parameter and you cannot + * assume that its contents are valid after this function returns. You must + * save pointers to the strings in these structures if you need to free them + * later, and/or save copies if needed. + * + * It is also possible for this function to return ::WIMLIB_ERR_INVALID_OVERLAY + * when trying to overlay a non-directory on a directory or when otherwise + * trying to overlay multiple conflicting files to the same location in the WIM + * image. */ extern int wimlib_add_image_multisource(WIMStruct *w, struct wimlib_capture_source *sources, size_t num_sources, diff --git a/src/write.c b/src/write.c index 427da767..737112f0 100644 --- a/src/write.c +++ b/src/write.c @@ -835,10 +835,11 @@ static int main_writer_thread_proc(struct list_head *stream_list, for (size_t j = 0; j < MAX_CHUNKS_PER_MSG; j++) { msgs[i].compressed_chunks[j] = MALLOC(WIM_CHUNK_SIZE); - // The extra 8 bytes is because longest_match() in lz.c - // may read a little bit off the end of the uncompressed - // data. It doesn't need to be initialized--- we really - // just need to avoid accessing an unmapped page. + // The extra 8 bytes is because longest_match() in + // lz77.c may read a little bit off the end of the + // uncompressed data. It doesn't need to be + // initialized--- we really just need to avoid accessing + // an unmapped page. msgs[i].uncompressed_chunks[j] = MALLOC(WIM_CHUNK_SIZE + 8); if (msgs[i].compressed_chunks[j] == NULL || msgs[i].uncompressed_chunks[j] == NULL) -- 2.43.0