]> wimlib.net Git - wimlib/blobdiff - src/reparse.c
Misc. fixes
[wimlib] / src / reparse.c
index a245a7e16a455e0575d8caffab6ae002d20f3503..b097212a6738afd803a911553f112dcb2055667d 100644 (file)
@@ -1,7 +1,5 @@
 /*
 /*
- * reparse.c
- *
- * Handle reparse data.
+ * reparse.c - Handle reparse data.
  */
 
 /*
  */
 
 /*
  * along with wimlib; if not, see http://www.gnu.org/licenses/.
  */
 
  * along with wimlib; if not, see http://www.gnu.org/licenses/.
  */
 
-#include "dentry.h"
-#include "buffer_io.h"
-#include "lookup_table.h"
-#include "sha1.h"
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "wimlib/assert.h"
+#include "wimlib/compiler.h"
+#include "wimlib/endianness.h"
+#include "wimlib/dentry.h"
+#include "wimlib/encoding.h"
+#include "wimlib/error.h"
+#include "wimlib/lookup_table.h"
+#include "wimlib/reparse.h"
+#include "wimlib/resource.h"
+
+#ifdef __WIN32__
+#  include "wimlib/win32.h" /* for win32_get_file_and_vol_ids() */
+#endif
+
+#ifdef HAVE_ALLOCA_H
+#  include <alloca.h>
+#endif
 #include <errno.h>
 #include <errno.h>
+#include <stdlib.h>
+
+/* On-disk format of a symbolic link (WIM_IO_REPARSE_TAG_SYMLINK) or junction
+ * point (WIM_IO_REPARSE_TAG_MOUNT_POINT) reparse data buffer.  */
+struct reparse_buffer_disk {
+       le32 rptag;
+       le16 rpdatalen;
+       le16 rpreserved;
+       le16 substitute_name_offset;
+       le16 substitute_name_nbytes;
+       le16 print_name_offset;
+       le16 print_name_nbytes;
+       union {
+               struct {
+                       le32 rpflags;
+                       u8 data[REPARSE_POINT_MAX_SIZE - 20];
+               } _packed_attribute symlink;
+               struct {
+                       u8 data[REPARSE_POINT_MAX_SIZE - 16];
+               } _packed_attribute junction;
+       };
+} _packed_attribute;
 
 static const utf16lechar volume_junction_prefix[11] = {
        cpu_to_le16('\\'),
 
 static const utf16lechar volume_junction_prefix[11] = {
        cpu_to_le16('\\'),
@@ -117,37 +154,51 @@ parse_substitute_name(const utf16lechar *substitute_name,
  * description of the format of the reparse point buffers.
  */
 int
  * description of the format of the reparse point buffers.
  */
 int
-parse_reparse_data(const u8 *rpbuf, u16 rpbuflen, struct reparse_data *rpdata)
+parse_reparse_data(const u8 * restrict rpbuf, u16 rpbuflen,
+                  struct reparse_data * restrict rpdata)
 {
 {
-       const u8 *p = rpbuf;
        u16 substitute_name_offset;
        u16 print_name_offset;
        u16 substitute_name_offset;
        u16 print_name_offset;
+       const struct reparse_buffer_disk *rpbuf_disk =
+               (const struct reparse_buffer_disk*)rpbuf;
+       const u8 *data;
 
        memset(rpdata, 0, sizeof(*rpdata));
        if (rpbuflen < 16)
                goto out_invalid;
 
        memset(rpdata, 0, sizeof(*rpdata));
        if (rpbuflen < 16)
                goto out_invalid;
-       p = get_u32(p, &rpdata->rptag);
+       rpdata->rptag = le32_to_cpu(rpbuf_disk->rptag);
        wimlib_assert(rpdata->rptag == WIM_IO_REPARSE_TAG_SYMLINK ||
                      rpdata->rptag == WIM_IO_REPARSE_TAG_MOUNT_POINT);
        wimlib_assert(rpdata->rptag == WIM_IO_REPARSE_TAG_SYMLINK ||
                      rpdata->rptag == WIM_IO_REPARSE_TAG_MOUNT_POINT);
-       p = get_u16(p, &rpdata->rpdatalen);
-       p = get_u16(p, &rpdata->rpreserved);
-       p = get_u16(p, &substitute_name_offset);
-       p = get_u16(p, &rpdata->substitute_name_nbytes);
-       p = get_u16(p, &print_name_offset);
-       p = get_u16(p, &rpdata->print_name_nbytes);
+       rpdata->rpdatalen = le16_to_cpu(rpbuf_disk->rpdatalen);
+       rpdata->rpreserved = le16_to_cpu(rpbuf_disk->rpreserved);
+       substitute_name_offset = le16_to_cpu(rpbuf_disk->substitute_name_offset);
+       rpdata->substitute_name_nbytes = le16_to_cpu(rpbuf_disk->substitute_name_nbytes);
+       print_name_offset = le16_to_cpu(rpbuf_disk->print_name_offset);
+       rpdata->print_name_nbytes = le16_to_cpu(rpbuf_disk->print_name_nbytes);
+
+       if ((substitute_name_offset & 1) | (print_name_offset & 1) |
+           (rpdata->substitute_name_nbytes & 1) | (rpdata->print_name_nbytes & 1))
+       {
+               /* Names would be unaligned... */
+               goto out_invalid;
+       }
+
        if (rpdata->rptag == WIM_IO_REPARSE_TAG_SYMLINK) {
                if (rpbuflen < 20)
                        goto out_invalid;
        if (rpdata->rptag == WIM_IO_REPARSE_TAG_SYMLINK) {
                if (rpbuflen < 20)
                        goto out_invalid;
-               p = get_u32(p, &rpdata->rpflags);
+               rpdata->rpflags = le32_to_cpu(rpbuf_disk->symlink.rpflags);
+               data = rpbuf_disk->symlink.data;
+       } else {
+               data = rpbuf_disk->junction.data;
        }
        if ((size_t)substitute_name_offset + rpdata->substitute_name_nbytes +
        }
        if ((size_t)substitute_name_offset + rpdata->substitute_name_nbytes +
-           (p - rpbuf) > rpbuflen)
+           (data - rpbuf) > rpbuflen)
                goto out_invalid;
        if ((size_t)print_name_offset + rpdata->print_name_nbytes +
                goto out_invalid;
        if ((size_t)print_name_offset + rpdata->print_name_nbytes +
-           (p - rpbuf) > rpbuflen)
+           (data - rpbuf) > rpbuflen)
                goto out_invalid;
                goto out_invalid;
-       rpdata->substitute_name = (utf16lechar*)&p[substitute_name_offset];
-       rpdata->print_name = (utf16lechar*)&p[print_name_offset];
+       rpdata->substitute_name = (utf16lechar*)&data[substitute_name_offset];
+       rpdata->print_name = (utf16lechar*)&data[print_name_offset];
        return 0;
 out_invalid:
        ERROR("Invalid reparse data");
        return 0;
 out_invalid:
        ERROR("Invalid reparse data");
@@ -163,34 +214,44 @@ out_invalid:
  *             at least REPARSE_POINT_MAX_SIZE bytes long.
  */
 int
  *             at least REPARSE_POINT_MAX_SIZE bytes long.
  */
 int
-make_reparse_buffer(const struct reparse_data *rpdata, u8 *rpbuf)
+make_reparse_buffer(const struct reparse_data * restrict rpdata,
+                   u8 * restrict rpbuf)
 {
 {
-       u8 *p = rpbuf;
-
-       p = put_u32(p, rpdata->rptag);
-       p += 2; /* We set ReparseDataLength later */
-       p = put_u16(p, rpdata->rpreserved);
-       p = put_u16(p, 0); /* substitute name offset */
-       p = put_u16(p, rpdata->substitute_name_nbytes); /* substitute name nbytes */
-       p = put_u16(p, rpdata->substitute_name_nbytes + 2); /* print name offset */
-       p = put_u16(p, rpdata->print_name_nbytes); /* print name nbytes */
-       if (rpdata->rptag == WIM_IO_REPARSE_TAG_SYMLINK)
-               p = put_u32(p, rpdata->rpflags);
+       struct reparse_buffer_disk *rpbuf_disk =
+               (struct reparse_buffer_disk*)rpbuf;
+       u8 *data;
+
+       rpbuf_disk->rptag = cpu_to_le32(rpdata->rptag);
+       rpbuf_disk->rpreserved = cpu_to_le16(rpdata->rpreserved);
+       rpbuf_disk->substitute_name_offset = cpu_to_le16(0);
+       rpbuf_disk->substitute_name_nbytes = cpu_to_le16(rpdata->substitute_name_nbytes);
+       rpbuf_disk->print_name_offset = cpu_to_le16(rpdata->substitute_name_nbytes + 2);
+       rpbuf_disk->print_name_nbytes = cpu_to_le16(rpdata->print_name_nbytes);
+
+       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. */
        /* 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 (p + rpdata->substitute_name_nbytes +
+       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;
        }
            rpdata->print_name_nbytes +
            2 * sizeof(utf16lechar) - rpbuf > REPARSE_POINT_MAX_SIZE)
        {
                ERROR("Reparse data is too long!");
                return WIMLIB_ERR_INVALID_REPARSE_DATA;
        }
-       p = put_bytes(p, rpdata->substitute_name_nbytes, rpdata->substitute_name);
-       p = put_u16(p, 0);
-       p = put_bytes(p, rpdata->print_name_nbytes, rpdata->print_name);
-       p = put_u16(p, 0);
-       put_u16(rpbuf + 4, p - rpbuf - 8); /* Set ReparseDataLength */
+       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);
        return 0;
 }
 
        return 0;
 }
 
@@ -207,10 +268,14 @@ make_reparse_buffer(const struct reparse_data *rpdata, u8 *rpbuf)
  * buffer returned by this function.
  */
 int
  * buffer returned by this function.
  */
 int
-wim_inode_get_reparse_data(const struct wim_inode *inode, u8 *rpbuf)
+wim_inode_get_reparse_data(const struct wim_inode * restrict inode,
+                          u8 * restrict rpbuf,
+                          u16 * restrict rpbuflen_ret)
 {
        struct wim_lookup_table_entry *lte;
        int ret;
 {
        struct wim_lookup_table_entry *lte;
        int ret;
+       struct reparse_buffer_disk *rpbuf_disk;
+       u16 rpdatalen;
 
        wimlib_assert(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT);
 
 
        wimlib_assert(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT);
 
@@ -219,27 +284,32 @@ wim_inode_get_reparse_data(const struct wim_inode *inode, u8 *rpbuf)
                ERROR("Reparse point has no reparse data!");
                return WIMLIB_ERR_INVALID_REPARSE_DATA;
        }
                ERROR("Reparse point has no reparse data!");
                return WIMLIB_ERR_INVALID_REPARSE_DATA;
        }
+
        if (wim_resource_size(lte) > REPARSE_POINT_MAX_SIZE - 8) {
                ERROR("Reparse data is too long!");
                return WIMLIB_ERR_INVALID_REPARSE_DATA;
        }
        if (wim_resource_size(lte) > REPARSE_POINT_MAX_SIZE - 8) {
                ERROR("Reparse data is too long!");
                return WIMLIB_ERR_INVALID_REPARSE_DATA;
        }
+       rpdatalen = wim_resource_size(lte);
 
        /* Read the data from the WIM file */
 
        /* Read the data from the WIM file */
-       ret = read_full_resource_into_buf(lte, rpbuf + 8, true);
+       ret = read_full_resource_into_buf(lte, rpbuf + 8);
        if (ret)
                return ret;
 
        /* Reconstruct the first 8 bytes of the reparse point buffer */
        if (ret)
                return ret;
 
        /* Reconstruct the first 8 bytes of the reparse point buffer */
+       rpbuf_disk = (struct reparse_buffer_disk*)rpbuf;
 
        /* ReparseTag */
 
        /* ReparseTag */
-       put_u32(rpbuf, inode->i_reparse_tag);
+       rpbuf_disk->rptag = cpu_to_le32(inode->i_reparse_tag);
 
        /* ReparseDataLength */
 
        /* ReparseDataLength */
-       put_u16(rpbuf + 4, wim_resource_size(lte));
+       rpbuf_disk->rpdatalen = cpu_to_le16(rpdatalen);
 
        /* ReparseReserved
         * XXX this could be one of the unknown fields in the WIM dentry. */
 
        /* ReparseReserved
         * XXX this could be one of the unknown fields in the WIM dentry. */
-       put_u16(rpbuf + 6, 0);
+       rpbuf_disk->rpreserved = cpu_to_le16(0);
+
+       *rpbuflen_ret = rpdatalen + 8;
        return 0;
 }
 
        return 0;
 }
 
@@ -254,24 +324,23 @@ wim_inode_get_reparse_data(const struct wim_inode *inode, u8 *rpbuf)
  * argument is swapped out with the `struct wim_inode' for a reparse point, and
  * on failure a negated error code is returned rather than -1 with errno set.  */
 ssize_t
  * argument is swapped out with the `struct wim_inode' for a reparse point, and
  * on failure a negated error code is returned rather than -1 with errno set.  */
 ssize_t
-wim_inode_readlink(const struct wim_inode *inode, char *buf, size_t bufsize)
+wim_inode_readlink(const struct wim_inode * restrict inode,
+                  char * restrict buf, size_t bufsize)
 {
        int ret;
 {
        int ret;
-       u8 rpbuf[REPARSE_POINT_MAX_SIZE];
-       u16 rpdatalen;
+       struct reparse_buffer_disk rpbuf_disk _aligned_attribute(8);
        struct reparse_data rpdata;
        char *link_target;
        char *translated_target;
        size_t link_target_len;
        struct reparse_data rpdata;
        char *link_target;
        char *translated_target;
        size_t link_target_len;
+       u16 rpbuflen;
 
        wimlib_assert(inode_is_symlink(inode));
 
 
        wimlib_assert(inode_is_symlink(inode));
 
-       if (wim_inode_get_reparse_data(inode, rpbuf))
+       if (wim_inode_get_reparse_data(inode, (u8*)&rpbuf_disk, &rpbuflen))
                return -EIO;
 
                return -EIO;
 
-       get_u16(rpbuf + 4, &rpdatalen);
-
-       if (parse_reparse_data(rpbuf, rpdatalen + 8, &rpdata))
+       if (parse_reparse_data((const u8*)&rpbuf_disk, rpbuflen, &rpdata))
                return -EIO;
 
        ret = utf16le_to_tstr(rpdata.substitute_name,
                return -EIO;
 
        ret = utf16le_to_tstr(rpdata.substitute_name,
@@ -292,7 +361,8 @@ wim_inode_readlink(const struct wim_inode *inode, char *buf, size_t bufsize)
        case SUBST_NAME_IS_UNKNOWN:
                ERROR("Can't understand reparse point "
                      "substitute name \"%s\"", link_target);
        case SUBST_NAME_IS_UNKNOWN:
                ERROR("Can't understand reparse point "
                      "substitute name \"%s\"", link_target);
-               return -EIO;
+               ret = -EIO;
+               goto out_free_link_target;
        default:
                translated_target += ret;
                link_target_len -= ret;
        default:
                translated_target += ret;
                link_target_len -= ret;
@@ -311,22 +381,18 @@ out_have_link:
                ret = link_target_len;
        }
        memcpy(buf, translated_target, link_target_len);
                ret = link_target_len;
        }
        memcpy(buf, translated_target, link_target_len);
+out_free_link_target:
        FREE(link_target);
        return ret;
 }
 
        FREE(link_target);
        return ret;
 }
 
-#ifdef HAVE_ALLOCA_H
-#  include <alloca.h>
-#endif
-
 int
 wim_inode_set_symlink(struct wim_inode *inode,
                      const char *target,
                      struct wim_lookup_table *lookup_table)
 
 {
 int
 wim_inode_set_symlink(struct wim_inode *inode,
                      const char *target,
                      struct wim_lookup_table *lookup_table)
 
 {
-       u8 rpbuf[REPARSE_POINT_MAX_SIZE];
-       u16 rpdatalen;
+       struct reparse_buffer_disk rpbuf_disk _aligned_attribute(8);
        struct reparse_data rpdata;
        static const char abs_subst_name_prefix[12] = "\\\0?\0?\0\\\0C\0:\0";
        static const char abs_print_name_prefix[4] = "C\0:\0";
        struct reparse_data rpdata;
        static const char abs_subst_name_prefix[12] = "\\\0?\0?\0\\\0C\0:\0";
        static const char abs_print_name_prefix[4] = "C\0:\0";
@@ -415,10 +481,11 @@ wim_inode_set_symlink(struct wim_inode *inode,
                rpdata.rpflags = SYMBOLIC_LINK_RELATIVE;
        }
 
                rpdata.rpflags = SYMBOLIC_LINK_RELATIVE;
        }
 
-       ret = make_reparse_buffer(&rpdata, rpbuf);
+       ret = make_reparse_buffer(&rpdata, (u8*)&rpbuf_disk);
        if (ret == 0) {
        if (ret == 0) {
-               get_u16(rpbuf + 4, &rpdatalen);
-               ret = inode_set_unnamed_stream(inode, rpbuf + 8, rpdatalen,
+               ret = inode_set_unnamed_stream(inode,
+                                              (u8*)&rpbuf_disk + 8,
+                                              le16_to_cpu(rpbuf_disk.rpdatalen),
                                               lookup_table);
        }
        FREE(name_utf16le);
                                               lookup_table);
        }
        FREE(name_utf16le);
@@ -447,7 +514,6 @@ unix_get_ino_and_dev(const char *path, u64 *ino_ret, u64 *dev_ret)
 #endif /* !defined(__WIN32__) */
 
 #ifdef __WIN32__
 #endif /* !defined(__WIN32__) */
 
 #ifdef __WIN32__
-#  include "win32.h"
 #  define RP_PATH_SEPARATOR L'\\'
 #  define is_rp_path_separator(c) ((c) == L'\\' || (c) == L'/')
 #  define os_get_ino_and_dev win32_get_file_and_vol_ids
 #  define RP_PATH_SEPARATOR L'\\'
 #  define is_rp_path_separator(c) ((c) == L'\\' || (c) == L'/')
 #  define os_get_ino_and_dev win32_get_file_and_vol_ids
@@ -465,6 +531,12 @@ capture_fixup_absolute_symlink(tchar *dest,
 {
        tchar *p = dest;
 
 {
        tchar *p = dest;
 
+#ifdef __WIN32__
+       /* Skip drive letter */
+       if (!is_rp_path_separator(*dest))
+               p += 2;
+#endif
+
        DEBUG("Fixing symlink or junction \"%"TS"\"", dest);
        for (;;) {
                tchar save;
        DEBUG("Fixing symlink or junction \"%"TS"\"", dest);
        for (;;) {
                tchar save;
@@ -491,6 +563,13 @@ capture_fixup_absolute_symlink(tchar *dest,
                                *(p - 1) = RP_PATH_SEPARATOR;
                        while (p - 1 >= dest && is_rp_path_separator(*(p - 1)))
                                p--;
                                *(p - 1) = RP_PATH_SEPARATOR;
                        while (p - 1 >= dest && is_rp_path_separator(*(p - 1)))
                                p--;
+               #ifdef __WIN32__
+                       if (!is_rp_path_separator(dest[0])) {
+                               *--p = dest[1];
+                               *--p = dest[0];
+                       }
+               #endif
+                       wimlib_assert(p >= dest);
                        return p;
                }
 
                        return p;
                }