- /* Compatability notes:
- *
- * On UNIX, an absolute symbolic link begins with '/'; everything else
- * 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 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
- * absolute link targets:
- *
- * (1) An absolute path beginning with one backslash character. similar
- * to UNIX-style, just with a different path separator. Print name same
- * as substitute name.
- *
- * (2) Absolute path beginning with drive letter followed by a
- * backslash. Print name same as substitute name.
- *
- * (3) Absolute path beginning with drive letter followed by a
- * backslash; substitute name prefixed with \??\, otherwise same as
- * print name.
- *
- * We choose option (3) here, and we just assume C: for the drive
- * letter. The reasoning for this is:
- *
- * (1) Microsoft imagex.exe has a bug where it does not attempt to do
- * reparse point fixups for these links, even though they are valid
- * absolute links. (Note: in this case prefixing the substitute name
- * with \??\ does not work; it just makes the data unable to be restored
- * at all.)
- * (2) Microsoft imagex.exe will fail when doing reparse point fixups
- * for these. It apparently contains a bug that causes it to create an
- * invalid reparse point, which then cannot be restored.
- * (3) This is the only option I tested for which reparse point fixups
- * worked properly in Microsoft imagex.exe.
- *
- * So option (3) it is.
- */
-
- rpdata.rptag = inode->i_reparse_tag;
- if (target[0] == '/') {
- rpdata.substitute_name_nbytes = name_utf16le_nbytes +
- sizeof(abs_subst_name_prefix);
- rpdata.print_name_nbytes = name_utf16le_nbytes +
- sizeof(abs_print_name_prefix);
- rpdata.substitute_name = alloca(rpdata.substitute_name_nbytes);
- rpdata.print_name = alloca(rpdata.print_name_nbytes);
- memcpy(rpdata.substitute_name, abs_subst_name_prefix,
- sizeof(abs_subst_name_prefix));
- memcpy(rpdata.print_name, abs_print_name_prefix,
- sizeof(abs_print_name_prefix));
- memcpy((void*)rpdata.substitute_name + sizeof(abs_subst_name_prefix),
- name_utf16le, name_utf16le_nbytes);
- memcpy((void*)rpdata.print_name + sizeof(abs_print_name_prefix),
- name_utf16le, name_utf16le_nbytes);
+ link.rptag = WIM_IO_REPARSE_TAG_SYMLINK;
+ link.rpreserved = 0;
+
+ /* Note: an absolute link that was rewritten to be relative to another
+ * directory is assumed to either be empty or to have a leading slash.
+ * See unix_relativize_link_target(). */
+ if (*target == cpu_to_le16('\\') || !*target) {
+ /*
+ * UNIX link target was absolute. In this case we represent the
+ * link as a symlink reparse point with SYMBOLIC_LINK_RELATIVE
+ * cleared. For this to work we need to assign it a path that
+ * can be resolved from the root of the Windows NT kernel object
+ * namespace. We do this by using "\??\C:" as a dummy prefix.
+ *
+ * Note that we could instead represent UNIX absolute links by
+ * setting SYMBOLIC_LINK_RELATIVE and then leaving the path
+ * backslash-prefixed like "\Users\Public". On Windows this is
+ * valid and denotes a path relative to the root of the
+ * filesystem on which the reparse point resides. The problem
+ * with this is that neither WIMGAPI nor wimlib (on Windows)
+ * will do "reparse point fixups" when extracting such links
+ * (modifying the link target to point into the actual
+ * extraction directory). So for the greatest cross-platform
+ * consistency, we have to use the fake C: drive approach.
+ */
+ static const utf16lechar prefix[6] = {
+ cpu_to_le16('\\'),
+ cpu_to_le16('?'),
+ cpu_to_le16('?'),
+ cpu_to_le16('\\'),
+ cpu_to_le16('C'),
+ cpu_to_le16(':'),
+ };
+
+ /* Do not show \??\ in print name */
+ const size_t num_unprintable_chars = 4;
+
+ link.symlink_flags = 0;
+ link.substitute_name_nbytes = sizeof(prefix) + target_nbytes;
+ link.substitute_name = alloca(link.substitute_name_nbytes);
+ memcpy(link.substitute_name, prefix, sizeof(prefix));
+ memcpy(link.substitute_name + ARRAY_LEN(prefix), target, target_nbytes);
+ link.print_name_nbytes = link.substitute_name_nbytes -
+ (num_unprintable_chars * sizeof(utf16lechar));
+ link.print_name = link.substitute_name + num_unprintable_chars;