]> wimlib.net Git - wimlib/blobdiff - src/reparse.c
Swap all slashes in UNIX <=> Windows symlink translation
[wimlib] / src / reparse.c
index 4df2a4b428831fd02cee77329a5ba198ba0a2514..0ae053b9cb06fa749d88dc8fb89edd5c6c88d82f 100644 (file)
@@ -5,42 +5,37 @@
 /*
  * Copyright (C) 2012, 2013 Eric Biggers
  *
- * This file is part of wimlib, a library for working with WIM files.
+ * This file is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
  *
- * wimlib is free software; you can redistribute it and/or modify it under the
- * terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- * A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * This file is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
  * details.
  *
- * You should have received a copy of the GNU General Public License
- * along with wimlib; if not, see http://www.gnu.org/licenses/.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this file; if not, see http://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
 #  include "config.h"
 #endif
 
+#include <errno.h>
+
+#include "wimlib/alloca.h"
 #include "wimlib/assert.h"
+#include "wimlib/blob_table.h"
 #include "wimlib/compiler.h"
 #include "wimlib/endianness.h"
 #include "wimlib/encoding.h"
 #include "wimlib/error.h"
 #include "wimlib/inode.h"
-#include "wimlib/lookup_table.h"
 #include "wimlib/reparse.h"
 #include "wimlib/resource.h"
 
-#ifdef HAVE_ALLOCA_H
-#  include <alloca.h>
-#endif
-#include <errno.h>
-#include <stdlib.h>
-
 /*
  * Read the data from a symbolic link, junction, or mount point reparse point
  * buffer into a `struct reparse_data'.
@@ -117,6 +112,19 @@ make_reparse_buffer(const struct reparse_data * restrict rpdata,
                (struct reparse_buffer_disk*)rpbuf;
        u8 *data;
 
+       if (rpdata->rptag == WIM_IO_REPARSE_TAG_SYMLINK)
+               data = rpbuf_disk->symlink.data;
+       else
+               data = rpbuf_disk->junction.data;
+
+       if ((data - rpbuf) + rpdata->substitute_name_nbytes +
+           rpdata->print_name_nbytes +
+           2 * sizeof(utf16lechar) > REPARSE_POINT_MAX_SIZE)
+       {
+               ERROR("Reparse data is too long!");
+               return WIMLIB_ERR_INVALID_REPARSE_DATA;
+       }
+
        rpbuf_disk->rptag = cpu_to_le32(rpdata->rptag);
        rpbuf_disk->rpreserved = cpu_to_le16(rpdata->rpreserved);
        rpbuf_disk->symlink.substitute_name_offset = cpu_to_le16(0);
@@ -124,30 +132,19 @@ make_reparse_buffer(const struct reparse_data * restrict rpdata,
        rpbuf_disk->symlink.print_name_offset = cpu_to_le16(rpdata->substitute_name_nbytes + 2);
        rpbuf_disk->symlink.print_name_nbytes = cpu_to_le16(rpdata->print_name_nbytes);
 
-       if (rpdata->rptag == WIM_IO_REPARSE_TAG_SYMLINK) {
+       if (rpdata->rptag == WIM_IO_REPARSE_TAG_SYMLINK)
                rpbuf_disk->symlink.rpflags = cpu_to_le32(rpdata->rpflags);
-               data = rpbuf_disk->symlink.data;
-       } else {
-               data = rpbuf_disk->junction.data;
-       }
 
        /* We null-terminate the substitute and print names, although this may
         * not be strictly necessary.  Note that the byte counts should not
         * include the null terminators. */
-       if (data + rpdata->substitute_name_nbytes +
-           rpdata->print_name_nbytes +
-           2 * sizeof(utf16lechar) - rpbuf > REPARSE_POINT_MAX_SIZE)
-       {
-               ERROR("Reparse data is too long!");
-               return WIMLIB_ERR_INVALID_REPARSE_DATA;
-       }
        data = mempcpy(data, rpdata->substitute_name, rpdata->substitute_name_nbytes);
        *(utf16lechar*)data = cpu_to_le16(0);
        data += 2;
        data = mempcpy(data, rpdata->print_name, rpdata->print_name_nbytes);
        *(utf16lechar*)data = cpu_to_le16(0);
        data += 2;
-       rpbuf_disk->rpdatalen = cpu_to_le16(data - rpbuf - 8);
+       rpbuf_disk->rpdatalen = cpu_to_le16(data - rpbuf - REPARSE_DATA_OFFSET);
        *rpbuflen_ret = data - rpbuf;
        return 0;
 }
@@ -160,41 +157,47 @@ make_reparse_buffer(const struct reparse_data * restrict rpdata,
  *
  * Note: in the WIM format, the first 8 bytes of the reparse point data buffer
  * are omitted, presumably because we already know the reparse tag from the
- * dentry, and we already know the reparse tag length from the lookup table
- * entry resource length.  However, we reconstruct the first 8 bytes in the
- * buffer returned by this function.
+ * dentry, and we already know the reparse tag length from the blob length.
+ * However, we reconstruct the first 8 bytes in the buffer returned by this
+ * function.
  */
-int
+static int
 wim_inode_get_reparse_data(const struct wim_inode * restrict inode,
                           u8 * restrict rpbuf,
                           u16 * restrict rpbuflen_ret,
-                          struct wim_lookup_table_entry *lte_override)
+                          struct blob_descriptor *blob_override)
 {
-       struct wim_lookup_table_entry *lte;
+       struct blob_descriptor *blob;
        int ret;
        struct reparse_buffer_disk *rpbuf_disk;
        u16 rpdatalen;
 
        wimlib_assert(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT);
 
-       if (!lte_override) {
-               lte = inode_unnamed_lte_resolved(inode);
-               if (!lte) {
+       if (blob_override) {
+               blob = blob_override;
+       } else {
+               struct wim_inode_stream *strm;
+
+               strm = inode_get_unnamed_stream(inode, STREAM_TYPE_REPARSE_POINT);
+               if (strm)
+                       blob = stream_blob_resolved(strm);
+               else
+                       blob = NULL;
+               if (!blob) {
                        ERROR("Reparse point has no reparse data!");
                        return WIMLIB_ERR_INVALID_REPARSE_DATA;
                }
-       } else {
-               lte = lte_override;
        }
 
-       if (lte->size > REPARSE_POINT_MAX_SIZE - 8) {
+       if (blob->size > REPARSE_DATA_MAX_SIZE) {
                ERROR("Reparse data is too long!");
                return WIMLIB_ERR_INVALID_REPARSE_DATA;
        }
-       rpdatalen = lte->size;
+       rpdatalen = blob->size;
 
-       /* Read the data from the WIM file */
-       ret = read_full_stream_into_buf(lte, rpbuf + 8);
+       /* Read the reparse data from blob  */
+       ret = read_full_blob_into_buf(blob, rpbuf + REPARSE_DATA_OFFSET);
        if (ret)
                return ret;
 
@@ -211,7 +214,7 @@ wim_inode_get_reparse_data(const struct wim_inode * restrict inode,
         * XXX this could be one of the unknown fields in the WIM dentry. */
        rpbuf_disk->rpreserved = cpu_to_le16(0);
 
-       *rpbuflen_ret = rpdatalen + 8;
+       *rpbuflen_ret = rpdatalen + REPARSE_DATA_OFFSET;
        return 0;
 }
 
@@ -307,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
@@ -321,9 +324,9 @@ parse_substitute_name(const utf16lechar *substitute_name,
  * @bufsize
  *     Available space in @buf, in bytes.
  *
- * @lte_override
- *     If not NULL, the stream from which to read the reparse data.  Otherwise,
- *     the reparse data will be read from the unnamed stream of @inode.
+ * @blob_override
+ *     If not NULL, the blob from which to read the reparse data.  Otherwise,
+ *     the reparse data will be read from the reparse point stream of @inode.
  *
  * If the entire symbolic link target was placed in the buffer, returns the
  * number of bytes written.  The resulting string is not null-terminated.  If
@@ -335,7 +338,7 @@ parse_substitute_name(const utf16lechar *substitute_name,
 ssize_t
 wim_inode_readlink(const struct wim_inode * restrict inode,
                   char * restrict buf, size_t bufsize,
-                  struct wim_lookup_table_entry *lte_override)
+                  struct blob_descriptor *blob_override)
 {
        int ret;
        struct reparse_buffer_disk rpbuf_disk _aligned_attribute(8);
@@ -348,7 +351,7 @@ wim_inode_readlink(const struct wim_inode * restrict inode,
        wimlib_assert(inode_is_symlink(inode));
 
        if (wim_inode_get_reparse_data(inode, (u8*)&rpbuf_disk, &rpbuflen,
-                                      lte_override))
+                                      blob_override))
                return -EIO;
 
        if (parse_reparse_data((const u8*)&rpbuf_disk, rpbuflen, &rpdata))
@@ -381,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;
@@ -397,10 +403,11 @@ out_free_link_target:
        return ret;
 }
 
+/* Given a UNIX-style symbolic link target, create a Windows-style reparse point
+ * buffer and assign it to the specified inode.  */
 int
-wim_inode_set_symlink(struct wim_inode *inode,
-                     const char *target,
-                     struct wim_lookup_table *lookup_table)
+wim_inode_set_symlink(struct wim_inode *inode, const char *target,
+                     struct blob_table *blob_table)
 
 {
        struct reparse_buffer_disk rpbuf_disk _aligned_attribute(8);
@@ -418,11 +425,14 @@ wim_inode_set_symlink(struct wim_inode *inode,
        ret = tstr_to_utf16le(target, strlen(target),
                              &name_utf16le, &name_utf16le_nbytes);
        if (ret)
-               return 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:
         *
@@ -430,12 +440,9 @@ wim_inode_set_symlink(struct wim_inode *inode,
         * 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
@@ -494,13 +501,22 @@ wim_inode_set_symlink(struct wim_inode *inode,
        }
 
        ret = make_reparse_buffer(&rpdata, (u8*)&rpbuf_disk, &rpbuflen);
-       if (ret == 0) {
-               ret = inode_set_unnamed_stream(inode,
-                                              (u8*)&rpbuf_disk + 8,
-                                              rpbuflen - 8,
-                                              lookup_table);
-       }
+       if (ret)
+               goto out_free_name;
+
+       ret = WIMLIB_ERR_NOMEM;
+       if (!inode_add_stream_with_data(inode,
+                                       STREAM_TYPE_REPARSE_POINT,
+                                       NO_STREAM_NAME,
+                                       (u8*)&rpbuf_disk + REPARSE_DATA_OFFSET,
+                                       rpbuflen - REPARSE_DATA_OFFSET,
+                                       blob_table))
+               goto out_free_name;
+
+       ret = 0;
+out_free_name:
        FREE(name_utf16le);
+out:
        return ret;
 }