X-Git-Url: https://wimlib.net/git/?a=blobdiff_plain;f=src%2Fsymlink.c;h=f005cd715f025ef2730472001669f1e68007afab;hb=f3bfbdd5c07b4b096a2e8a35a03e9dfc6269e41f;hp=587b71cb8f07f5c31b802b53df1cdda71c398ff7;hpb=94005a5254bb7029e4793875b0ad9c356bf63ad7;p=wimlib diff --git a/src/symlink.c b/src/symlink.c index 587b71cb..f005cd71 100644 --- a/src/symlink.c +++ b/src/symlink.c @@ -34,6 +34,10 @@ #include +#ifdef HAVE_ALLOCA_H +# include +#endif + /* * Find the symlink target of a symbolic link or junction point in the WIM. * @@ -77,7 +81,8 @@ get_symlink_name(const void *resource, size_t resource_len, char *buf, header_size = 12; p += 4; } - if (header_size + substitute_name_offset + substitute_name_len > resource_len) + if (header_size + + substitute_name_offset + substitute_name_len > resource_len) return -EIO; ret = utf16le_to_tstr((const utf16lechar*)(p + substitute_name_offset), @@ -86,11 +91,6 @@ get_symlink_name(const void *resource, size_t resource_len, char *buf, if (ret) return -errno; - if (link_target_len + 1 > buf_len) { - ret = -ENAMETOOLONG; - goto out; - } - DEBUG("Interpeting substitute name \"%s\" (ReparseTag=0x%x)", link_target, reparse_tag); translate_slashes = true; @@ -133,7 +133,7 @@ get_symlink_name(const void *resource, size_t resource_len, char *buf, /* "Relative" symlink, without drive letter */ ; } else { - ERROR("Invalid reparse point: \"%s\"", translated_target); + ERROR("Invalid reparse point substitute name: \"%s\"", translated_target); ret = -EIO; goto out; } @@ -142,8 +142,14 @@ get_symlink_name(const void *resource, size_t resource_len, char *buf, for (size_t i = 0; i < link_target_len; i++) if (translated_target[i] == '\\') translated_target[i] = '/'; - memcpy(buf, translated_target, link_target_len + 1); - ret = link_target_len; + + if (link_target_len > buf_len) { + link_target_len = buf_len; + ret = -ENAMETOOLONG; + } else { + ret = link_target_len; + } + memcpy(buf, translated_target, link_target_len); out: FREE(link_target); return ret; @@ -154,8 +160,8 @@ out: /* Given a UNIX symlink target, prepare the corresponding symbolic link reparse * data buffer. */ static int -make_symlink_reparse_data_buf(const char *symlink_target, - size_t *len_ret, void **buf_ret) +make_symlink_reparse_data_buf(const char *symlink_target, void *rpdata, + size_t *rplen_ret) { int ret; utf16lechar *name_utf16le; @@ -165,8 +171,7 @@ make_symlink_reparse_data_buf(const char *symlink_target, 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"; u32 flags; - size_t len; - void *buf; + size_t rplen; void *p; ret = tstr_to_utf16le(symlink_target, strlen(symlink_target), @@ -230,16 +235,15 @@ make_symlink_reparse_data_buf(const char *symlink_target, print_name_nbytes += sizeof(abs_print_name_prefix); } - len = 12 + substitute_name_nbytes + print_name_nbytes + + rplen = 12 + substitute_name_nbytes + print_name_nbytes + 2 * sizeof(utf16lechar); - buf = MALLOC(len); - if (!buf) { - ret = WIMLIB_ERR_NOMEM; - goto out_free_name_utf16le; + if (rplen > REPARSE_POINT_MAX_SIZE) { + ERROR("Symlink \"%s\" is too long!", symlink_target); + return WIMLIB_ERR_LINK; } - p = buf; + p = rpdata; /* Substitute name offset */ p = put_u16(p, 0); @@ -271,8 +275,7 @@ make_symlink_reparse_data_buf(const char *symlink_target, p = put_bytes(p, name_utf16le_nbytes, name_utf16le); p = put_u16(p, 0); - *len_ret = len; - *buf_ret = buf; + *rplen_ret = rplen; ret = 0; out_free_name_utf16le: FREE(name_utf16le); @@ -291,6 +294,7 @@ inode_readlink(const struct wim_inode *inode, char *buf, size_t buf_len, { const struct wim_lookup_table_entry *lte; int ret; + u8 *res_buf; wimlib_assert(inode_is_symlink(inode)); @@ -301,12 +305,12 @@ inode_readlink(const struct wim_inode *inode, char *buf, size_t buf_len, if (wim_resource_size(lte) > REPARSE_POINT_MAX_SIZE) return -EIO; - u8 res_buf[wim_resource_size(lte)]; + res_buf = alloca(wim_resource_size(lte)); ret = read_full_resource_into_buf(lte, res_buf, threadsafe); if (ret) return -EIO; - return get_symlink_name(res_buf, wim_resource_size(lte), buf, - buf_len, inode->i_reparse_tag); + return get_symlink_name(res_buf, wim_resource_size(lte), + buf, buf_len, inode->i_reparse_tag); } /* @@ -328,55 +332,28 @@ inode_set_symlink(struct wim_inode *inode, { int ret; - size_t symlink_buf_len; - struct wim_lookup_table_entry *lte = NULL, *existing_lte; - u8 symlink_buf_hash[SHA1_HASH_SIZE]; - void *symlink_buf; - - ret = make_symlink_reparse_data_buf(target, &symlink_buf_len, - &symlink_buf); - if (ret) - return ret; - - DEBUG("Made symlink reparse data buf (len = %zu, name len = %zu)", - symlink_buf_len, symlink_buf_len); - sha1_buffer(symlink_buf, symlink_buf_len, symlink_buf_hash); + /* Buffer for reparse point data */ + u8 rpdata[REPARSE_POINT_MAX_SIZE]; - existing_lte = __lookup_resource(lookup_table, symlink_buf_hash); + /* Actual length of the reparse point data (to be calculated by + * make_symlink_reparse_data_buf()) */ + size_t rplen; - if (existing_lte) { - lte = existing_lte; - FREE(symlink_buf); - symlink_buf = NULL; - } else { - DEBUG("Creating new lookup table entry for symlink buf"); - lte = new_lookup_table_entry(); - if (!lte) { - ret = WIMLIB_ERR_NOMEM; - goto out_free_symlink_buf; - } - lte->resource_location = RESOURCE_IN_ATTACHED_BUFFER; - lte->attached_buffer = symlink_buf; - lte->resource_entry.original_size = symlink_buf_len; - copy_hash(lte->hash, symlink_buf_hash); - } + DEBUG("Creating reparse point data buffer " + "for UNIX symlink target \"%s\"", target); - inode->i_lte = lte; - inode->i_resolved = 1; + ret = make_symlink_reparse_data_buf(target, rpdata, &rplen); + if (ret) + return ret; - DEBUG("Loaded symlink buf"); + ret = inode_set_unnamed_stream(inode, rpdata, rplen, lookup_table); + if (ret) + return ret; - if (existing_lte) - lte->refcnt++; - else - lookup_table_insert(lookup_table, lte); if (lte_ret) - *lte_ret = lte; + *lte_ret = inode->i_lte; return 0; -out_free_symlink_buf: - FREE(symlink_buf); - return ret; } static int @@ -400,24 +377,21 @@ unix_get_ino_and_dev(const char *path, u64 *ino_ret, u64 *dev_ret) #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 #else # define RP_PATH_SEPARATOR '/' +# define is_rp_path_separator(c) ((c) == '/') # define os_get_ino_and_dev unix_get_ino_and_dev #endif -/* Fix up reparse points--- mostly shared between UNIX and Windows */ +/* Fix up absolute symbolic link targets--- mostly shared between UNIX and + * Windows */ tchar * fixup_symlink(tchar *dest, u64 capture_root_ino, u64 capture_root_dev) { tchar *p = dest; -#ifdef __WIN32__ - /* Skip over drive letter */ - if (*p != RP_PATH_SEPARATOR) - p += 2; -#endif - DEBUG("Fixing symlink or junction \"%"TS"\"", dest); for (;;) { tchar save; @@ -425,7 +399,7 @@ fixup_symlink(tchar *dest, u64 capture_root_ino, u64 capture_root_dev) u64 ino; u64 dev; - while (*p == RP_PATH_SEPARATOR) + while (is_rp_path_separator(*p)) p++; save = *p; @@ -433,21 +407,17 @@ fixup_symlink(tchar *dest, u64 capture_root_ino, u64 capture_root_dev) ret = os_get_ino_and_dev(dest, &ino, &dev); *p = save; + if (ret) /* stat() failed before we got to the capture root--- + assume the link points outside it. */ + return NULL; + if (ino == capture_root_ino && dev == capture_root_dev) { /* Link points inside capture root. Return abbreviated * path. */ if (*p == T('\0')) *(p - 1) = RP_PATH_SEPARATOR; - while (p - 1 >= dest && *(p - 1) == RP_PATH_SEPARATOR) + while (p - 1 >= dest && is_rp_path_separator(*(p - 1))) p--; - #ifdef __WIN32__ - /* Add back drive letter */ - if (*dest != RP_PATH_SEPARATOR) { - *--p = *(dest + 1); - *--p = *dest; - } - #endif - wimlib_assert(p >= dest); return p; } @@ -458,7 +428,6 @@ fixup_symlink(tchar *dest, u64 capture_root_ino, u64 capture_root_dev) do { p++; - } while (*p != RP_PATH_SEPARATOR && *p != T('\0')); + } while (!is_rp_path_separator(*p) && *p != T('\0')); } } -