]> wimlib.net Git - wimlib/commitdiff
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 f17f0e6267a1c7fa18640bf05db1382cb1c12a7d..1323f4fe306219f1ae63ecf766efb61febd499d9 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
 
 \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.
 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
 
 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
 
 .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.
 
 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.
 
 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
 
 
 .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
 
 \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
 specified.
 
 .SH EXAMPLES
index 1431ad388d342cd508671f21ae0831514bbcf57f..bff551789e793dbe9e342663c40f0939dcf3c359 100644 (file)
@@ -42,7 +42,8 @@ There is a separate manual page for each \fBimagex\fR command.
 
 .SH SUPPORTED FEATURES
 
 
 .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)
 
 .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
 
 .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
 
 .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
 
 .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]
 
 .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
 
 .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.
 
 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
 .SH WARNING
 
 Note: \fBwimlib\fR and \fBimagex\fR are experimental.  Use Microsoft's
index 0b9af0053ec0bb8d67c407fa109d95b40eeaf570..b16579e74090782e7a50052dea7a02d563ce9cc6 100644 (file)
@@ -49,7 +49,7 @@
                                opts, NULL)) != -1)
 
 enum imagex_op_type {
                                opts, NULL)) != -1)
 
 enum imagex_op_type {
-       APPEND,
+       APPEND = 0,
        APPLY,
        CAPTURE,
        DELETE,
        APPLY,
        CAPTURE,
        DELETE,
@@ -115,12 +115,6 @@ static const char *usage_strings[] = {
 "imagex unmount DIRECTORY [--commit] [--check] [--rebuild]\n",
 };
 
 "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'},
 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;
 }
 
        return ret;
 }
 
+/* Parse the argument to --compress */
 static int get_compression_type(const char *optarg)
 {
        if (strcasecmp(optarg, "maximum") == 0 || strcasecmp(optarg, "lzx") == 0)
 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;
 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";
 
 "*.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)
 {
 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;
        char *buf = NULL;
        size_t newlen = 1024;
        size_t pos = 0;
@@ -343,12 +345,34 @@ enum {
        PARSE_FILENAME_NONE = 2,
 };
 
        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;
 {
        size_t len = *len_p;
        char *line = *line_p;
        char *fn;
-       int ret;
        char quote_char;
 
        /* Skip leading whitespace */
        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);
                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;
                }
                        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++;
                fn = line;
                do {
                        line++;
-                       len--;
                } while (!isspace(*line) && *line != '\0');
                } 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;
        *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)
 {
 static bool
 parse_source_list_line(char *line, size_t len,
                       struct wimlib_capture_source *source)
 {
+       /* SOURCE [DEST] */
        int ret;
        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;
        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;
 }
 
        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 (;;) {
 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)
 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;
 }
 
        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;
 static char *file_get_contents(const char *filename, size_t *len_ret)
 {
        struct stat stbuf;
@@ -498,6 +552,8 @@ out:
        return buf;
 }
 
        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;
 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)))
 
 #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) {
 static const char *get_data_type(int ctype)
 {
        switch (ctype) {
@@ -523,6 +581,7 @@ static const char *get_data_type(int ctype)
        return NULL;
 }
 
        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)
 {
 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;
 }
 
        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,
 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;
 static int imagex_apply(int argc, char **argv)
 {
        int c;
@@ -843,6 +906,9 @@ out:
        return ret;
 }
 
        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;
 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;
 
        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;
 
        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 {
        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));
        }
                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) {
        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 {
                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 {
                }
                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;
                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;
 }
 
        return -1;
 }
 
+/* Rebuild a WIM file */
 static int imagex_optimize(int argc, char **argv)
 {
        int c;
 static int imagex_optimize(int argc, char **argv)
 {
        int c;
@@ -1881,7 +1955,7 @@ static int imagex_split(int argc, char **argv)
        return ret;
 }
 
        return ret;
 }
 
-/* Unmounts an image. */
+/* Unmounts a mounted WIM image. */
 static int imagex_unmount(int argc, char **argv)
 {
        int c;
 static int imagex_unmount(int argc, char **argv)
 {
        int c;
@@ -2017,7 +2091,7 @@ static void usage_all()
        fputs(extra, stdout);
 }
 
        fputs(extra, stdout);
 }
 
-
+/* Entry point for the 'imagex' program. */
 int main(int argc, char **argv)
 {
        const struct imagex_command *cmd;
 int main(int argc, char **argv)
 {
        const struct imagex_command *cmd;
@@ -2029,15 +2103,22 @@ int main(int argc, char **argv)
                return 1;
        }
 
                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++;
 
        help_or_version(argc, argv);
        argc--;
        argv++;
 
+       /* The user may like to see more informative error messages. */
        wimlib_set_print_errors(true);
        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;
 
        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);
        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:
        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,
        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");
        }
                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;
 }
        wimlib_global_cleanup();
        return ret;
 }
index 0d1ffb29c3b4cc0836f3a152babaed6e81049fb2..fc2f6a200ca314e486942d66378bdd8ae1665833 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'. */
 
 /* 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 *config_str;
-       char *prefix;
        char *p;
        char *eol;
        char *next_p;
        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;
        }
                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;
 
        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;
        while (bytes_remaining) {
                line_no++;
                p = next_p;
@@ -554,6 +546,19 @@ out_destroy:
        return ret;
 }
 
        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)
 {
 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);
                      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)
 {
                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);
 }
 
        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) {
        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;
                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. */
 /* 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);
 {
        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... */
        }
 
        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)) {
                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);
                        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);
                              child->file_name_utf8, target->file_name_utf8);
-                       return WIMLIB_ERR_INVALID_DENTRY;
+                       return WIMLIB_ERR_INVALID_OVERLAY;
                }
        }
        return 0;
                }
        }
        return 0;
@@ -775,7 +779,8 @@ static int do_overlay(struct wim_dentry *target,
  * @branch
  *     Branch to add.
  * @target_path:
  * @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,
  */
 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;
                }
                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;
                do {
                        ++target_path;
-                       wimlib_assert(*target_path != '\0');
                } while (*target_path == '/');
        }
 
                } 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;
                            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;
        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);
        }
                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");
 
        DEBUG("Allocating security data");
-
        sd = CALLOC(1, sizeof(struct wim_security_data));
        if (!sd) {
                ret = WIMLIB_ERR_NOMEM;
        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;
        }
        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 {
                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 {
                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);
                        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);
                        }
                        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;
                        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) {
                                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));
                                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);
 
                                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);
                        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--;
        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_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;
        destroy_capture_config(&config);
 out:
        return ret;
index e566b52b2e21f52f28aafd6ccdde28501e3d872b..9db817ac5549d666a646280a154893a0fd7eec86 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",
                = "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]
        [WIMLIB_ERR_INVALID_PARAM]
                = "An invalid parameter was given",
        [WIMLIB_ERR_INVALID_PART_NUMBER]
index 36178298ac96c4a2e0aad48f2d7889895dd0fccb..a46608ee61047837641bbb9bdfdea47fa43654ca 100644 (file)
@@ -790,6 +790,7 @@ enum wimlib_error_code {
        WIMLIB_ERR_UNSUPPORTED,
        WIMLIB_ERR_WRITE,
        WIMLIB_ERR_XML,
        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
  * 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,
 extern int wimlib_add_image_multisource(WIMStruct *w,
                                        struct wimlib_capture_source *sources,
                                        size_t num_sources,
index 427da767a9977a85ad5ae95aeb34d12a4cd68d9d..737112f09fefd30c711786b1e92d2821e7bb43c1 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);
 
                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)
                        msgs[i].uncompressed_chunks[j] = MALLOC(WIM_CHUNK_SIZE + 8);
                        if (msgs[i].compressed_chunks[j] == NULL ||
                            msgs[i].uncompressed_chunks[j] == NULL)