More logical behavior when canonicalizing WIM paths
authorEric Biggers <ebiggers3@gmail.com>
Tue, 29 Apr 2014 16:22:06 +0000 (11:22 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Tue, 29 Apr 2014 17:08:35 +0000 (12:08 -0500)
In addition to translating path separators and stripping trailing
slashes, also collapse consecutive path separators, and retain the
leading slash to emphasize that the paths are rooted.

Add WIMLIB_WIM_ROOT_PATH and WIMLIB_IS_WIM_ROOT_PATH macros to wimlib.h.

NEWS
include/wimlib.h
programs/imagex.c
src/add_image.c
src/extract.c
src/paths.c
src/update_image.c

diff --git a/NEWS b/NEWS
index d69f628..727f8ab 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -30,6 +30,9 @@ Version 1.6.3-BETA:
                - Removed deprecated functions: some (de)compression functions,
                  wimlib_extract_files(), and wimlib_print_metadata().
 
+               - WIM paths passed to progress functions now have a leading
+                 slash.
+
 Version 1.6.2:
        Case-insensitive comparisons of strings (e.g. filenames) containing
        UTF-16 codepoints above 32767 are now done correctly.
index b54682a..fc6526a 100644 (file)
@@ -392,6 +392,15 @@ typedef char wimlib_tchar;
 #  define WIMLIB_WIM_PATH_SEPARATOR_STRING "/"
 #endif
 
+/** Use this to specify the root directory of the WIM image.  */
+#define WIMLIB_WIM_ROOT_PATH WIMLIB_WIM_PATH_SEPARATOR_STRING
+
+/** Use this to test if the specified path refers to the root directory of the
+ * WIM image.  */
+#define WIMLIB_IS_WIM_ROOT_PATH(path) \
+               ((path)[0] == WIMLIB_WIM_PATH_SEPARATOR &&      \
+                (path)[1] == 0)
+
 #ifdef __GNUC__
 #  define _wimlib_deprecated __attribute__((deprecated))
 #else
@@ -702,10 +711,7 @@ union wimlib_progress_info {
                union {
                        /** Target path in the WIM image.  Only valid on
                         * messages ::WIMLIB_PROGRESS_MSG_SCAN_BEGIN and
-                        * ::WIMLIB_PROGRESS_MSG_SCAN_END.  If capturing a full
-                        * image, this will be the empty string; otherwise it
-                        * will name the place in the WIM image at which the
-                        * directory tree is being added.  */
+                        * ::WIMLIB_PROGRESS_MSG_SCAN_END.  */
                        const wimlib_tchar *wim_target_path;
 
                        /** For ::WIMLIB_PROGRESS_MSG_SCAN_DENTRY and a status
@@ -823,8 +829,8 @@ union wimlib_progress_info {
                 * data stream, or a reparse data buffer.  */
                uint64_t num_streams;
 
-               /** This will be the empty string.  */
-               const wimlib_tchar *extract_root_wim_source_path;
+               /** Reserved.  */
+               const wimlib_tchar *reserved_2;
 
                /** Currently only used for
                 * ::WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN.  */
@@ -949,9 +955,8 @@ struct wimlib_capture_source {
         * filesystem to be included in the WIM image. */
        wimlib_tchar *fs_source_path;
 
-       /** Destination path in the WIM image.  Leading and trailing slashes are
-        * ignored.  The empty string or @c NULL means the root directory of the
-        * WIM image. */
+       /** Destination path in the WIM image.  Use WIMLIB_WIM_ROOT_PATH to
+        * specify the root directory of the WIM image.  */
        wimlib_tchar *wim_target_path;
 
        /** Reserved; set to 0. */
@@ -1802,12 +1807,11 @@ enum wimlib_update_op {
 
 /** Data for a ::WIMLIB_UPDATE_OP_ADD operation. */
 struct wimlib_add_command {
-       /** Filesystem path to the file or directory tree to
-        * add. */
+       /** Filesystem path to the file or directory tree to add.  */
        wimlib_tchar *fs_source_path;
-       /** Path, specified from the root of the WIM image, at
-        * which to add the file or directory tree within the
-        * WIM image. */
+
+       /** Destination path in the WIM image.  Use WIMLIB_WIM_ROOT_PATH to
+        * specify the root directory of the WIM image.  */
        wimlib_tchar *wim_target_path;
 
        /** Path to capture configuration file to use, or @c NULL for default.
@@ -1820,25 +1824,27 @@ struct wimlib_add_command {
 
 /** Data for a ::WIMLIB_UPDATE_OP_DELETE operation. */
 struct wimlib_delete_command {
-       /** Path, specified from the root of the WIM image, for
-        * the file or directory tree within the WIM image to be
-        * deleted. */
+
+       /** Path, specified from the root of the WIM image, for the file or
+        * directory tree within the WIM image to be deleted.  */
        wimlib_tchar *wim_path;
-       /** Bitwise OR of WIMLIB_DELETE_FLAG_* flags. */
+
+       /** Bitwise OR of WIMLIB_DELETE_FLAG_* flags.  */
        int delete_flags;
 };
 
 /** Data for a ::WIMLIB_UPDATE_OP_RENAME operation. */
 struct wimlib_rename_command {
-       /** Path, specified from the root of the WIM image, for
-        * the source file or directory tree within the WIM
-        * image. */
+
+       /** Path, specified from the root of the WIM image, for the source file
+        * or directory tree within the WIM image.  */
        wimlib_tchar *wim_source_path;
-       /** Path, specified from the root of the WIM image, for
-        * the destination file or directory tree within the WIM
-        * image. */
+
+       /** Path, specified from the root of the WIM image, for the destination
+        * file or directory tree within the WIM image.  */
        wimlib_tchar *wim_target_path;
-       /** Reserved; set to 0. */
+
+       /** Reserved; set to 0.  */
        int rename_flags;
 };
 
index 644a2ba..32f24c3 100644 (file)
 
 #ifdef __WIN32__
 #  include "imagex-win32.h"
-#  define OS_PREFERRED_PATH_SEPARATOR L'\\'
-#  define OS_PREFERRED_PATH_SEPARATOR_STRING L"\\"
 #  define print_security_descriptor     win32_print_security_descriptor
 #else /* __WIN32__ */
 #  include <getopt.h>
 #  include <langinfo.h>
-#  define OS_PREFERRED_PATH_SEPARATOR '/'
-#  define OS_PREFERRED_PATH_SEPARATOR_STRING "/"
 #  define print_security_descriptor    default_print_security_descriptor
 static inline void set_fd_to_binary_mode(int fd)
 {
@@ -1067,12 +1063,11 @@ imagex_progress_func(enum wimlib_progress_msg msg,
                break;
        case WIMLIB_PROGRESS_MSG_SCAN_BEGIN:
                imagex_printf(T("Scanning \"%"TS"\""), info->scan.source);
-               if (*info->scan.wim_target_path) {
-                       imagex_printf(T(" (loading as WIM path: "
-                                 "\""WIMLIB_WIM_PATH_SEPARATOR_STRING"%"TS"\")...\n"),
-                              info->scan.wim_target_path);
-               } else {
+               if (WIMLIB_IS_WIM_ROOT_PATH(info->scan.wim_target_path)) {
                        imagex_printf(T("\n"));
+               } else {
+                       imagex_printf(T(" (loading as WIM path: \"%"TS"\")...\n"),
+                                     info->scan.wim_target_path);
                }
                memset(&last_scan_progress, 0, sizeof(last_scan_progress));
                break;
@@ -1141,17 +1136,6 @@ imagex_progress_func(enum wimlib_progress_msg msg,
                         T("NTFS volume") : T("directory")),
                        info->extract.target);
                break;
-       case WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN:
-               if (info->extract.extract_root_wim_source_path[0]) {
-                       imagex_printf(T("Extracting \"%"TS"\" from image %d "
-                                       "(\"%"TS"\") in \"%"TS"\" to \"%"TS"\"\n"),
-                                     info->extract.extract_root_wim_source_path,
-                                     info->extract.image,
-                                     info->extract.image_name,
-                                     info->extract.wimfile_name,
-                                     info->extract.target);
-               }
-               break;
        case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS:
                percent_done = TO_PERCENT(info->extract.completed_bytes,
                                          info->extract.total_bytes);
@@ -1174,8 +1158,7 @@ imagex_progress_func(enum wimlib_progress_msg msg,
                }
                break;
        case WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS:
-               if (info->extract.extract_root_wim_source_path[0] == T('\0'))
-                       imagex_printf(T("Setting timestamps on all extracted files...\n"));
+               imagex_printf(T("Setting timestamps on all extracted files...\n"));
                break;
        case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END:
                if (info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
@@ -1208,14 +1191,11 @@ imagex_progress_func(enum wimlib_progress_msg msg,
        case WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND:
                switch (info->update.command->op) {
                case WIMLIB_UPDATE_OP_DELETE:
-                       imagex_printf(T("Deleted WIM path "
-                                 "\""WIMLIB_WIM_PATH_SEPARATOR_STRING "%"TS"\"\n"),
+                       imagex_printf(T("Deleted WIM path \"%"TS"\"\n"),
                                info->update.command->delete_.wim_path);
                        break;
                case WIMLIB_UPDATE_OP_RENAME:
-                       imagex_printf(T("Renamed WIM path "
-                                 "\""WIMLIB_WIM_PATH_SEPARATOR_STRING "%"TS"\" => "
-                                 "\""WIMLIB_WIM_PATH_SEPARATOR_STRING "%"TS"\"\n"),
+                       imagex_printf(T("Renamed WIM path \"%"TS"\" => \"%"TS"\"\n"),
                                info->update.command->rename.wim_source_path,
                                info->update.command->rename.wim_target_path);
                        break;
@@ -1952,7 +1932,7 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd)
                /* Set up capture source in non-source-list mode.  */
                capture_sources = alloca(sizeof(struct wimlib_capture_source));
                capture_sources[0].fs_source_path = source;
-               capture_sources[0].wim_target_path = NULL;
+               capture_sources[0].wim_target_path = WIMLIB_WIM_ROOT_PATH;
                capture_sources[0].reserved = 0;
                num_sources = 1;
                capture_sources_malloced = false;
@@ -2479,7 +2459,7 @@ imagex_dir(int argc, tchar **argv, int cmd)
        WIMStruct *wim = NULL;
        int image;
        int ret;
-       const tchar *path = T("");
+       const tchar *path = WIMLIB_WIM_ROOT_PATH;
        int c;
        struct print_dentry_options options = {
                .detailed = false,
@@ -2836,7 +2816,7 @@ imagex_extract(int argc, tchar **argv, int cmd)
 
        STRING_SET(refglobs);
 
-       tchar *root_path = T("");
+       tchar *root_path = WIMLIB_WIM_ROOT_PATH;
 
        for_opt(c, extract_options) {
                switch (c) {
index c8aeaa6..7c3a6cb 100644 (file)
@@ -218,7 +218,7 @@ wimlib_add_image(WIMStruct *wim,
         * */
        const struct wimlib_capture_source capture_src = {
                .fs_source_path = (tchar*)source,
-               .wim_target_path = T(""),
+               .wim_target_path = WIMLIB_WIM_ROOT_PATH,
                .reserved = 0,
        };
        return wimlib_add_image_multisource(wim, &capture_src, 1, name,
index c6a3b56..1b7eb6b 100644 (file)
@@ -2435,7 +2435,6 @@ extract_trees(WIMStruct *wim, struct wim_dentry **trees, size_t num_trees,
                ctx.progress.extract.target = target;
        }
 
-       ctx.progress.extract.extract_root_wim_source_path = T("");
        ctx.target_dentry = wim_root_dentry(wim);
        /* Note: ctx.target_dentry represents the dentry that gets extracted to
         * @target.  There may be none, in which case it gets set to the image
@@ -2925,7 +2924,7 @@ extract_single_image(WIMStruct *wim, int image,
                     const tchar *target, int extract_flags,
                     wimlib_progress_func_t progress_func)
 {
-       const tchar *path = T("");
+       const tchar *path = WIMLIB_WIM_ROOT_PATH;
        extract_flags |= WIMLIB_EXTRACT_FLAG_IMAGEMODE;
        return do_wimlib_extract_paths(wim, image, target, &path, 1,
                                       extract_flags, progress_func);
index 68942f2..675b9f2 100644 (file)
@@ -87,38 +87,63 @@ canonicalize_fs_path(const tchar *fs_path)
  * canonicalize_wim_path() - Given a user-provided path to a file within a WIM
  * image, translate it into a "canonical" path.
  *
- * To do this, translate all supported path separators (is_any_path_separator())
- * into the WIM_PATH_SEPARATOR, and strip any leading and trailing slashes.  The
- * returned string is allocated.  Note that there still may be consecutive path
- * separators within the string.  Furthermore, the string may be empty, which
- * indicates the root dentry of the WIM image.
+ * - Translate both types of slash into a consistent type (WIM_PATH_SEPARATOR).
+ * - Collapse path separators.
+ * - Add leading slash if missing.
+ * - Strip trailing slashes.
+ *
+ * Examples (with WIM_PATH_SEPARATOR == '/'):
+ *
+ *             => /            [ either NULL or empty string ]
+ * /           => /
+ * \           => /
+ * hello       => /hello
+ * \hello      => /hello
+ * \hello      => /hello
+ * /hello/     => /hello
+ * \hello/     => /hello
+ * /hello//1   => /hello/1
+ * \\hello\\1\\        => /hello/1
  */
 tchar *
 canonicalize_wim_path(const tchar *wim_path)
 {
-       tchar *canonical_path;
-       tchar *p;
-
-       if (wim_path == NULL) {
-               wim_path = T("");
-       } else {
-               /* Strip leading path separators.  */
-               while (is_any_path_separator(*wim_path))
-                       wim_path++;
-       }
+       const tchar *in;
+       tchar *out;
+       tchar *result;
 
-       canonical_path = TSTRDUP(wim_path);
-       if (canonical_path == NULL)
+       in = wim_path;
+       if (!in)
+               in = T("");
+
+       result = MALLOC((1 + tstrlen(in) + 1) * sizeof(result[0]));
+       if (!result)
                return NULL;
 
-       /* Translate all path separators to WIM_PATH_SEPARATOR.  */
-       for (p = canonical_path; *p; p++)
-               if (is_any_path_separator(*p))
-                       *p = WIM_PATH_SEPARATOR;
+       out = result;
+
+       /* Add leading slash if missing  */
+       if (!is_any_path_separator(*in))
+               *out++ = WIM_PATH_SEPARATOR;
+
+       while (*in) {
+               if (is_any_path_separator(*in)) {
+                       /* Collapse multiple path separators into one  */
+                       *out++ = WIM_PATH_SEPARATOR;
+                       do {
+                               in++;
+                       } while (is_any_path_separator(*in));
+               } else {
+                       /* Copy non-path-separator character  */
+                       *out++ = *in++;
+               }
+       }
+
+       /* Remove trailing slash if existent  */
+       if (*(out - 1) == WIM_PATH_SEPARATOR && (out - 1) != result)
+               --out;
 
-       /* Strip trailing path separators.  */
-       while (p > canonical_path && *--p == WIM_PATH_SEPARATOR)
-               *p = T('\0');
+       *out = T('\0');
 
-       return canonical_path;
+       return result;
 }
index 5dc32fc..37d6593 100644 (file)
@@ -453,21 +453,33 @@ rollback_update(struct update_command_journal *j)
        free_update_command_journal(j);
 }
 
+/*
+ * Set the name of @branch for placing it at @target in the WIM image.  This
+ * assumes that @target is in "canonical form", as produced by
+ * canonicalize_wim_path().
+ *
+ * Note: for the root target this produces the empty name.
+ */
 static int
 set_branch_name(struct wim_dentry *branch, const utf16lechar *target)
 {
        const utf16lechar *p;
 
+       /* Find end of string.  (We can assume it contains at least one
+        * character, the leading slash.)   */
+       wimlib_assert(target[0] == cpu_to_le16(WIM_PATH_SEPARATOR));
        p = target;
-       while (*p)
+       do {
                p++;
+       } while (*p);
 
-       /* No trailing slashes allowed  */
-       wimlib_assert(p == target || *(p - 1) != cpu_to_le16(WIM_PATH_SEPARATOR));
-
-       while (p > target && *(p - 1) != cpu_to_le16(WIM_PATH_SEPARATOR))
+       while (*(p - 1) != cpu_to_le16(WIM_PATH_SEPARATOR))
                p--;
 
+
+       /* We're assuming no trailing slashes.  */
+       wimlib_assert(*p || p == &target[1]);
+
        return dentry_set_name_utf16le(branch, p);
 }
 
@@ -840,7 +852,7 @@ execute_add_command(struct update_command_journal *j,
        config.prefix = fs_source_path;
        config.prefix_num_tchars = tstrlen(fs_source_path);
 
-       if (wim_target_path[0] == T('\0'))
+       if (WIMLIB_IS_WIM_ROOT_PATH(wim_target_path))
                params.add_flags |= WIMLIB_ADD_FLAG_ROOT;
        ret = (*capture_tree)(&branch, fs_source_path, &params);
        if (ret)
@@ -849,7 +861,7 @@ execute_add_command(struct update_command_journal *j,
        if (progress_func)
                progress_func(WIMLIB_PROGRESS_MSG_SCAN_END, &params.progress);
 
-       if (wim_target_path[0] == T('\0') &&
+       if (WIMLIB_IS_WIM_ROOT_PATH(wim_target_path) &&
            branch && !dentry_is_directory(branch))
        {
                ERROR("\"%"TS"\" is not a directory!", fs_source_path);
@@ -864,7 +876,7 @@ execute_add_command(struct update_command_journal *j,
                goto out_cleanup_after_capture;
 
        if (config_file && (add_flags & WIMLIB_ADD_FLAG_WIMBOOT) &&
-           wim_target_path[0] == T('\0'))
+           WIMLIB_IS_WIM_ROOT_PATH(wim_target_path))
        {
                params.add_flags = 0;
                params.progress_func = NULL;
@@ -1243,12 +1255,7 @@ check_add_command(struct wimlib_update_command *cmd,
                          WIMLIB_ADD_FLAG_NO_REPLACE))
                return WIMLIB_ERR_INVALID_PARAM;
 
-       /* Are we adding the entire image or not?  An empty wim_target_path
-        * indicates that the tree we're adding is to be placed in the root of
-        * the image.  We consider this to be capturing the entire image,
-        * although it could potentially be an overlay on an existing root as
-        * well. */
-       bool is_entire_image = cmd->add.wim_target_path[0] == T('\0');
+       bool is_entire_image = WIMLIB_IS_WIM_ROOT_PATH(cmd->add.wim_target_path);
 
 #ifdef __WIN32__
        /* Check for flags not supported on Windows */