]> wimlib.net Git - wimlib/blobdiff - src/reparse.c
win32_capture_streams(): Handle not implemented/supported
[wimlib] / src / reparse.c
index 9ec264d5aeae58229df8b87ab5f7cc07f77663d0..1cbca56f146610f966df66a458346966e3c2c4a4 100644 (file)
 #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('\\'),
        cpu_to_le16('\\'),
@@ -65,15 +86,15 @@ static const utf16lechar volume_junction_prefix[11] = {
  * Return value is:
  *
  * Non-negative integer:
- *     The name is an absolute symbolic link in one of several formats,
- *     and the return value is the number of UTF-16LE characters that need to
- *     be advanced to reach a simple "absolute" path starting with a backslash
- *     (i.e. skip over \??\ and/or drive letter)
+ *     The name is an absolute symbolic link in one of several formats,
+ *     and the return value is the number of UTF-16LE characters that need to
+ *     be advanced to reach a simple "absolute" path starting with a backslash
+ *     (i.e. skip over \??\ and/or drive letter)
  * Negative integer:
  *     SUBST_NAME_IS_VOLUME_JUNCTION:
- *             The name is a volume junction.
+ *             The name is a volume junction.
  *     SUBST_NAME_IS_RELATIVE_LINK:
- *             The name is a relative symbolic link.
+ *             The name is a relative symbolic link.
  *     SUBST_NAME_IS_UNKNOWN:
  *             The name does not appear to be a valid symbolic link, junction,
  *             or mount point.
@@ -125,25 +146,6 @@ parse_substitute_name(const utf16lechar *substitute_name,
        }
 }
 
-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;
-
 /*
  * Read the data from a symbolic link, junction, or mount point reparse point
  * buffer into a `struct reparse_data'.
@@ -184,7 +186,7 @@ parse_reparse_data(const u8 * restrict rpbuf, u16 rpbuflen,
        if (rpdata->rptag == WIM_IO_REPARSE_TAG_SYMLINK) {
                if (rpbuflen < 20)
                        goto out_invalid;
-               rpdata->rpflags = le16_to_cpu(rpbuf_disk->symlink.rpflags);
+               rpdata->rpflags = le32_to_cpu(rpbuf_disk->symlink.rpflags);
                data = rpbuf_disk->symlink.data;
        } else {
                data = rpbuf_disk->junction.data;
@@ -213,7 +215,8 @@ out_invalid:
  */
 int
 make_reparse_buffer(const struct reparse_data * restrict rpdata,
-                   u8 * restrict rpbuf)
+                   u8 * restrict rpbuf,
+                   u16 * restrict rpbuflen_ret)
 {
        struct reparse_buffer_disk *rpbuf_disk =
                (struct reparse_buffer_disk*)rpbuf;
@@ -250,6 +253,7 @@ make_reparse_buffer(const struct reparse_data * restrict rpdata,
        *(utf16lechar*)data = cpu_to_le16(0);
        data += 2;
        rpbuf_disk->rpdatalen = cpu_to_le16(data - rpbuf - 8);
+       *rpbuflen_ret = data - rpbuf;
        return 0;
 }
 
@@ -267,23 +271,32 @@ make_reparse_buffer(const struct reparse_data * restrict rpdata,
  */
 int
 wim_inode_get_reparse_data(const struct wim_inode * restrict inode,
-                          u8 * restrict rpbuf)
+                          u8 * restrict rpbuf,
+                          u16 * restrict rpbuflen_ret,
+                          struct wim_lookup_table_entry *lte_override)
 {
        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);
 
-       lte = inode_unnamed_lte_resolved(inode);
-       if (!lte) {
-               ERROR("Reparse point has no reparse data!");
-               return WIMLIB_ERR_INVALID_REPARSE_DATA;
+       if (!lte_override) {
+               lte = inode_unnamed_lte_resolved(inode);
+               if (!lte) {
+                       ERROR("Reparse point has no reparse data!");
+                       return WIMLIB_ERR_INVALID_REPARSE_DATA;
+               }
+       } else {
+               lte = lte_override;
        }
+
        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 */
        ret = read_full_resource_into_buf(lte, rpbuf + 8);
@@ -297,11 +310,13 @@ wim_inode_get_reparse_data(const struct wim_inode * restrict inode,
        rpbuf_disk->rptag = cpu_to_le32(inode->i_reparse_tag);
 
        /* ReparseDataLength */
-       rpbuf_disk->rpdatalen = cpu_to_le16(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. */
        rpbuf_disk->rpreserved = cpu_to_le16(0);
+
+       *rpbuflen_ret = rpdatalen + 8;
        return 0;
 }
 
@@ -317,23 +332,24 @@ wim_inode_get_reparse_data(const struct wim_inode * restrict inode,
  * on failure a negated error code is returned rather than -1 with errno set.  */
 ssize_t
 wim_inode_readlink(const struct wim_inode * restrict inode,
-                  char * restrict buf, size_t bufsize)
+                  char * restrict buf, size_t bufsize,
+                  struct wim_lookup_table_entry *lte_override)
 {
        int ret;
        struct reparse_buffer_disk rpbuf_disk _aligned_attribute(8);
-       u16 rpdatalen;
        struct reparse_data rpdata;
        char *link_target;
        char *translated_target;
        size_t link_target_len;
+       u16 rpbuflen;
 
        wimlib_assert(inode_is_symlink(inode));
 
-       if (wim_inode_get_reparse_data(inode, (u8*)&rpbuf_disk))
+       if (wim_inode_get_reparse_data(inode, (u8*)&rpbuf_disk, &rpbuflen,
+                                      lte_override))
                return -EIO;
 
-       if (parse_reparse_data((const u8*)&rpbuf_disk,
-                              le16_to_cpu(rpbuf_disk.rpdatalen) + 8, &rpdata))
+       if (parse_reparse_data((const u8*)&rpbuf_disk, rpbuflen, &rpdata))
                return -EIO;
 
        ret = utf16le_to_tstr(rpdata.substitute_name,
@@ -354,7 +370,8 @@ wim_inode_readlink(const struct wim_inode * restrict inode,
        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;
@@ -373,6 +390,7 @@ out_have_link:
                ret = link_target_len;
        }
        memcpy(buf, translated_target, link_target_len);
+out_free_link_target:
        FREE(link_target);
        return ret;
 }
@@ -390,6 +408,7 @@ wim_inode_set_symlink(struct wim_inode *inode,
        utf16lechar *name_utf16le;
        size_t name_utf16le_nbytes;
        int ret;
+       u16 rpbuflen;
 
        DEBUG("Creating reparse point data buffer for UNIX "
              "symlink target \"%s\"", target);
@@ -410,8 +429,8 @@ wim_inode_set_symlink(struct wim_inode *inode,
         * 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 backslashes with forward
-        * slashes.  We do not make any attempt to handle filename character
+        * 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.
@@ -472,11 +491,11 @@ wim_inode_set_symlink(struct wim_inode *inode,
                rpdata.rpflags = SYMBOLIC_LINK_RELATIVE;
        }
 
-       ret = make_reparse_buffer(&rpdata, (u8*)&rpbuf_disk);
+       ret = make_reparse_buffer(&rpdata, (u8*)&rpbuf_disk, &rpbuflen);
        if (ret == 0) {
                ret = inode_set_unnamed_stream(inode,
                                               (u8*)&rpbuf_disk + 8,
-                                              le16_to_cpu(rpbuf_disk.rpdatalen),
+                                              rpbuflen - 8,
                                               lookup_table);
        }
        FREE(name_utf16le);
@@ -504,12 +523,13 @@ unix_get_ino_and_dev(const char *path, u64 *ino_ret, u64 *dev_ret)
 
 #endif /* !defined(__WIN32__) */
 
+/* is_rp_path_separator() - characters treated as path separators in absolute
+ * symbolic link targets */
+
 #ifdef __WIN32__
-#  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
 #else
-#  define RP_PATH_SEPARATOR '/'
 #  define is_rp_path_separator(c) ((c) == '/')
 #  define os_get_ino_and_dev unix_get_ino_and_dev
 #endif
@@ -551,7 +571,7 @@ capture_fixup_absolute_symlink(tchar *dest,
                        /* Link points inside capture root.  Return abbreviated
                         * path. */
                        if (*p == T('\0'))
-                               *(p - 1) = RP_PATH_SEPARATOR;
+                               *(p - 1) = OS_PREFERRED_PATH_SEPARATOR;
                        while (p - 1 >= dest && is_rp_path_separator(*(p - 1)))
                                p--;
                #ifdef __WIN32__