X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Freparse.c;h=913ea5ce095de34eafb0c457086acaae07e36ce5;hp=9ec264d5aeae58229df8b87ab5f7cc07f77663d0;hb=668c363a59c521f48eecbc4dd549c07a3e36179e;hpb=5b81e7556e33e4af42dc1e07eb36f830ca0c4db3 diff --git a/src/reparse.c b/src/reparse.c index 9ec264d5..913ea5ce 100644 --- a/src/reparse.c +++ b/src/reparse.c @@ -45,6 +45,27 @@ #include #include +/* 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,26 +271,35 @@ 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) { + + if (lte->size > REPARSE_POINT_MAX_SIZE - 8) { ERROR("Reparse data is too long!"); return WIMLIB_ERR_INVALID_REPARSE_DATA; } + rpdatalen = lte->size; /* Read the data from the WIM file */ - ret = read_full_resource_into_buf(lte, rpbuf + 8); + ret = read_full_stream_into_buf(lte, rpbuf + 8); if (ret) return ret; @@ -297,43 +310,67 @@ 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; } /* UNIX version of getting and setting the data in reparse points */ #if !defined(__WIN32__) -/* Get the UNIX symlink target from a WIM inode. The inode may be either a - * "real" symlink (reparse tag WIM_IO_REPARSE_TAG_SYMLINK), or it may be a - * junction point (reparse tag WIM_IO_REPARSE_TAG_MOUNT_POINT). +/* + * 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 + * forward slashes. + * + * @inode + * The inode to read the symlink from. It must be a reparse point with + * tag WIM_IO_REPARSE_TAG_SYMLINK (a real symlink) or + * WIM_IO_REPARSE_TAG_MOUNT_POINT (a mount point or junction point). + * + * @buf + * Buffer into which to place the link target. * - * This has similar semantics to the UNIX readlink() function, except the path - * 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. */ + * @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. + * + * 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 + * the symbolic link target was too large to be placed in the buffer, the first + * @bufsize bytes of it are placed in the buffer and + * -ENAMETOOLONG is returned. Otherwise, a negative errno value indicating + * another error is returned. + */ 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 +391,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 +411,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 +429,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 +450,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 +512,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 +544,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 +592,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__