Swap all slashes in UNIX <=> Windows symlink translation
authorEric Biggers <ebiggers3@gmail.com>
Tue, 5 May 2015 04:12:32 +0000 (23:12 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Tue, 5 May 2015 04:48:34 +0000 (23:48 -0500)
If we swap both slash types when translating each way, then the
translation is lossless.

NEWS
src/reparse.c

diff --git a/NEWS b/NEWS
index f5703ca4518f83dcdc4aae0d357e2c7d3f5a755d..55235ead2f5ba9096c4d0ae64c7ac452a72a5f24 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -31,6 +31,9 @@ Version 1.8.1-BETA:
        The performance of the NTFS-3g and Windows capture modes has been
        slightly improved.
 
+       On UNIX-like systems, symbolic links whose targets contain the backslash
+       character are now handled correctly (losslessly).
+
 Version 1.8.0:
        Improved the LZX compressor.  It is now 15-20% faster than before and
        provides a slightly better compression ratio.
index c27fef6ebe862ff5bc122cc48f783bb02ccd0785..0ae053b9cb06fa749d88dc8fb89edd5c6c88d82f 100644 (file)
@@ -310,7 +310,7 @@ parse_substitute_name(const utf16lechar *substitute_name,
 /*
  * Get the UNIX-style symlink target from the WIM inode for a reparse point.
  * Specifically, this translates the target from UTF-16 to the current multibyte
- * encoding, strips the drive prefix if present, and replaces backslashes with
+ * encoding, strips the drive prefix if present, and swaps backslashes and
  * forward slashes.
  *
  * @inode
@@ -384,9 +384,12 @@ wim_inode_readlink(const struct wim_inode * restrict inode,
        }
 
 out_translate_slashes:
-       for (size_t i = 0; i < link_target_len; i++)
+       for (size_t i = 0; i < link_target_len; i++) {
                if (translated_target[i] == '\\')
                        translated_target[i] = '/';
+               else if (translated_target[i] == '/')
+                       translated_target[i] = '\\';
+       }
 out_have_link:
        if (link_target_len > bufsize) {
                link_target_len = bufsize;
@@ -424,9 +427,12 @@ wim_inode_set_symlink(struct wim_inode *inode, const char *target,
        if (ret)
                goto out;
 
-       for (size_t i = 0; i < name_utf16le_nbytes / 2; i++)
+       for (size_t i = 0; i < name_utf16le_nbytes / 2; i++) {
                if (name_utf16le[i] == cpu_to_le16('/'))
                        name_utf16le[i] = cpu_to_le16('\\');
+               else if (name_utf16le[i] == cpu_to_le16('\\'))
+                       name_utf16le[i] = cpu_to_le16('/');
+       }
 
        /* Compatability notes:
         *
@@ -434,12 +440,9 @@ wim_inode_set_symlink(struct wim_inode *inode, const char *target,
         * is a relative symbolic link.  (Quite simple compared to the various
         * ways to provide Windows paths.)
         *
-        * To change a UNIX relative symbolic link to Windows format, we only
-        * need to translate it to UTF-16LE and replace forward slashes with
-        * backslashes.  We do not make any attempt to handle filename character
-        * problems, such as a link target that itself contains backslashes on
-        * UNIX.  Then, for these relative links, we set the reparse header
-        * @flags field to SYMBOLIC_LINK_RELATIVE.
+        * To change a UNIX relative symbolic link to Windows format, we need to
+        * translate it to UTF-16LE, swap forward slashes and backslashes, and
+        * set 'rpflags' to SYMBOLIC_LINK_RELATIVE.
         *
         * For UNIX absolute symbolic links, we must set the @flags field to 0.
         * Then, there are multiple options as to actually represent the