Add support for WIMLIB_EXTRACT_FLAG_SYMLINK on Windows
authorEric Biggers <ebiggers3@gmail.com>
Sat, 17 Aug 2013 23:36:05 +0000 (18:36 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Sat, 17 Aug 2013 23:36:59 +0000 (18:36 -0500)
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
src/extract.c
src/unix_apply.c
src/win32_apply.c
src/win32_common.c

index 4366c1a..ce23f19 100644 (file)
@@ -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);
 
index 2cc134d..7ca0122 100644 (file)
@@ -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) {
index 3940d33..86be081 100644 (file)
@@ -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;
 }
index ffc4c55..d08ea5d 100644 (file)
@@ -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,
index 5a2b794..c347a96 100644 (file)
@@ -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;