Various changes
authorEric Biggers <ebiggers3@gmail.com>
Sat, 9 Mar 2013 07:08:29 +0000 (01:08 -0600)
committerEric Biggers <ebiggers3@gmail.com>
Sat, 9 Mar 2013 07:08:29 +0000 (01:08 -0600)
* 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
doc/imagex.1.in
programs/imagex.c
src/add_image.c
src/util.c
src/wimlib.h
src/write.c

index f17f0e6..1323f4f 100644 (file)
@@ -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
index 1431ad3..bff5517 100644 (file)
@@ -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
index 0b9af00..b16579e 100644 (file)
@@ -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;
 }
index 0d1ffb2..fc2f6a2 100644 (file)
@@ -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;
index e566b52..9db817a 100644 (file)
@@ -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]
index 3617829..a46608e 100644 (file)
@@ -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 <b>imagex capture</b> 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,
index 427da76..737112f 100644 (file)
@@ -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)