From c7616d0c686267ff9556f93e0501940bffd0679c Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 17 Aug 2013 18:36:05 -0500 Subject: [PATCH] Add support for WIMLIB_EXTRACT_FLAG_SYMLINK on Windows Also: on Windows, don't fail to create symlink or hardlink because of an existing file (try deleting it first). --- include/wimlib/win32_common.h | 6 +++ src/extract.c | 11 ++++- src/unix_apply.c | 14 +++---- src/win32_apply.c | 75 ++++++++++++++++++++++++++++------- src/win32_common.c | 7 ++++ 5 files changed, 90 insertions(+), 23 deletions(-) diff --git a/include/wimlib/win32_common.h b/include/wimlib/win32_common.h index 4366c1af..ce23f196 100644 --- a/include/wimlib/win32_common.h +++ b/include/wimlib/win32_common.h @@ -39,6 +39,7 @@ win32_open_existing_file(const wchar_t *path, DWORD dwDesiredAccess); extern HANDLE win32_open_file_data_only(const wchar_t *path); +/* Vista and later */ extern HANDLE (WINAPI *win32func_FindFirstStreamW)(LPCWSTR lpFileName, STREAM_INFO_LEVELS InfoLevel, LPVOID lpFindStreamData, @@ -48,6 +49,11 @@ extern HANDLE (WINAPI *win32func_FindFirstStreamW)(LPCWSTR lpFileName, extern BOOL (WINAPI *win32func_FindNextStreamW)(HANDLE hFindStream, LPVOID lpFindStreamData); +/* Vista and later */ +extern BOOL (WINAPI *win32func_CreateSymbolicLinkW)(const wchar_t *lpSymlinkFileName, + const wchar_t *lpTargetFileName, + DWORD dwFlags); + extern bool windows_version_is_at_least(unsigned major, unsigned minor); diff --git a/src/extract.c b/src/extract.c index 2cc134dc..7ca01220 100644 --- a/src/extract.c +++ b/src/extract.c @@ -898,6 +898,11 @@ static unsigned get_num_path_components(const tchar *path, tchar path_separator) { unsigned num_components = 0; +#ifdef __WIN32__ + /* Ignore drive letter. */ + if (path[0] != L'\0' && path[1] == L':') + path += 2; +#endif while (*path) { while (*path == path_separator) @@ -940,7 +945,11 @@ extract_multiimage_symlink(const tchar *oldpath, const tchar *newpath, num_target_path_components--; } - p_old = oldpath; + p_old = oldpath + ctx->ops->path_prefix_nchars; +#ifdef __WIN32__ + if (p_old[0] != L'\0' && p_old[1] == ':') + p_old += 2; +#endif while (*p_old == ctx->ops->path_separator) p_old++; while (--num_target_path_components) { diff --git a/src/unix_apply.c b/src/unix_apply.c index 3940d33a..86be081f 100644 --- a/src/unix_apply.c +++ b/src/unix_apply.c @@ -85,14 +85,12 @@ unix_makelink(const tchar *oldpath, const tchar *newpath, int (*makelink)(const tchar *oldpath, const tchar *newpath)) { if ((*makelink)(oldpath, newpath)) { - if (errno == EEXIST) { - if (unlink(newpath)) - return WIMLIB_ERR_LINK; - if ((*makelink)(oldpath, newpath)) - return WIMLIB_ERR_LINK; - return 0; - } - return WIMLIB_ERR_LINK; + if (errno != EEXIST) + return WIMLIB_ERR_LINK; + if (unlink(newpath)) + return WIMLIB_ERR_LINK; + if ((*makelink)(oldpath, newpath)) + return WIMLIB_ERR_LINK; } return 0; } diff --git a/src/win32_apply.c b/src/win32_apply.c index ffc4c552..d08ea5d8 100644 --- a/src/win32_apply.c +++ b/src/win32_apply.c @@ -44,19 +44,40 @@ win32_start_extract(const wchar_t *path, struct apply_ctx *ctx) if (ret) return ret; - ctx->supported_features.archive_files = 1; - ctx->supported_features.hidden_files = 1; - ctx->supported_features.system_files = 1; - ctx->supported_features.compressed_files = !!(vol_flags & FILE_FILE_COMPRESSION); - ctx->supported_features.encrypted_files = !!(vol_flags & FILE_SUPPORTS_ENCRYPTION); - ctx->supported_features.encrypted_directories = !!(vol_flags & FILE_SUPPORTS_ENCRYPTION); + ctx->supported_features.archive_files = 1; + ctx->supported_features.hidden_files = 1; + ctx->supported_features.system_files = 1; + + if (vol_flags & FILE_FILE_COMPRESSION) + ctx->supported_features.compressed_files = 1; + + if (vol_flags & FILE_SUPPORTS_ENCRYPTION) { + ctx->supported_features.encrypted_files = 1; + ctx->supported_features.encrypted_directories = 1; + } + ctx->supported_features.not_context_indexed_files = 1; - ctx->supported_features.sparse_files = !!(vol_flags & FILE_SUPPORTS_SPARSE_FILES); - ctx->supported_features.named_data_streams = !!(vol_flags & FILE_NAMED_STREAMS); - ctx->supported_features.hard_links = !!(vol_flags & FILE_SUPPORTS_HARD_LINKS); - ctx->supported_features.reparse_points = !!(vol_flags & FILE_SUPPORTS_REPARSE_POINTS); - ctx->supported_features.security_descriptors = !!(vol_flags & FILE_PERSISTENT_ACLS); - ctx->supported_features.short_names = !!supports_SetFileShortName; + + if (vol_flags & FILE_SUPPORTS_SPARSE_FILES) + ctx->supported_features.sparse_files = 1; + + if (vol_flags & FILE_NAMED_STREAMS) + ctx->supported_features.named_data_streams = 1; + + if (vol_flags & FILE_SUPPORTS_HARD_LINKS) + ctx->supported_features.hard_links = 1; + + if (vol_flags & FILE_SUPPORTS_REPARSE_POINTS) { + ctx->supported_features.reparse_points = 1; + if (win32func_CreateSymbolicLinkW) + ctx->supported_features.symlink_reparse_points = 1; + } + + if (vol_flags & FILE_PERSISTENT_ACLS) + ctx->supported_features.security_descriptors = 1; + + if (supports_SetFileShortName) + ctx->supported_features.short_names = 1; return 0; } @@ -95,8 +116,33 @@ static int win32_create_hardlink(const wchar_t *oldpath, const wchar_t *newpath, struct apply_ctx *ctx) { - if (!CreateHardLink(newpath, oldpath, NULL)) - goto error; + if (!CreateHardLink(newpath, oldpath, NULL)) { + if (GetLastError() != ERROR_ALREADY_EXISTS) + goto error; + if (!DeleteFile(newpath)) + goto error; + if (!CreateHardLink(newpath, oldpath, NULL)) + goto error; + } + return 0; + +error: + set_errno_from_GetLastError(); + return WIMLIB_ERR_LINK; +} + +static int +win32_create_symlink(const wchar_t *oldpath, const wchar_t *newpath, + struct apply_ctx *ctx) +{ + if (!(*win32func_CreateSymbolicLinkW)(newpath, oldpath, 0)) { + if (GetLastError() != ERROR_ALREADY_EXISTS) + goto error; + if (!DeleteFile(newpath)) + goto error; + if (!(*win32func_CreateSymbolicLinkW)(newpath, oldpath, 0)) + goto error; + } return 0; error: @@ -496,6 +542,7 @@ const struct apply_operations win32_apply_ops = { .create_file = win32_create_file, .create_directory = win32_create_directory, .create_hardlink = win32_create_hardlink, + .create_symlink = win32_create_symlink, .extract_unnamed_stream = win32_extract_unnamed_stream, .extract_named_stream = win32_extract_named_stream, .extract_encrypted_stream = win32_extract_encrypted_stream, diff --git a/src/win32_common.c b/src/win32_common.c index 5a2b7948..c347a965 100644 --- a/src/win32_common.c +++ b/src/win32_common.c @@ -534,6 +534,11 @@ HANDLE (WINAPI *win32func_FindFirstStreamW)(LPCWSTR lpFileName, BOOL (WINAPI *win32func_FindNextStreamW)(HANDLE hFindStream, LPVOID lpFindStreamData) = NULL; +/* Vista and later */ +BOOL (WINAPI *win32func_CreateSymbolicLinkW)(const wchar_t *lpSymlinkFileName, + const wchar_t *lpTargetFileName, + DWORD dwFlags) = NULL; + static OSVERSIONINFO windows_version_info = { .dwOSVersionInfoSize = sizeof(OSVERSIONINFO), }; @@ -581,6 +586,8 @@ win32_global_init(int init_flags) if (!win32func_FindNextStreamW) win32func_FindFirstStreamW = NULL; } + win32func_CreateSymbolicLinkW = (void*)GetProcAddress(hKernel32, + "CreateSymbolicLinkW"); } return 0; -- 2.43.0