#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('\\'),
}
}
-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'.
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;
*/
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;
*(utf16lechar*)data = cpu_to_le16(0);
data += 2;
rpbuf_disk->rpdatalen = cpu_to_le16(data - rpbuf - 8);
+ *rpbuflen_ret = data - rpbuf;
return 0;
}
*/
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);
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;
}
* 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,
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;
ret = link_target_len;
}
memcpy(buf, translated_target, link_target_len);
+out_free_link_target:
FREE(link_target);
return ret;
}
utf16lechar *name_utf16le;
size_t name_utf16le_nbytes;
int ret;
+ u16 rpbuflen;
DEBUG("Creating reparse point data buffer for UNIX "
"symlink target \"%s\"", target);
* 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.
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);
#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
/* 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__