]> git.proxmox.com Git - libgit2.git/blobdiff - src/win32/posix_w32.c
New upstream version 1.1.0+dfsg.1
[libgit2.git] / src / win32 / posix_w32.c
index 91e1643489b6b967dc382c9b069b3558018bc076..cacf986e8b56755a4e7e1ac8bf73520ee28f6a12 100644 (file)
@@ -8,7 +8,7 @@
 #include "common.h"
 
 #include "../posix.h"
-#include "../fileops.h"
+#include "../futils.h"
 #include "path.h"
 #include "path_w32.h"
 #include "utf-conv.h"
@@ -210,7 +210,7 @@ on_error:
  * We now take a "git_off_t" rather than "long" because
  * files may be longer than 2Gb.
  */
-int p_ftruncate(int fd, git_off_t size)
+int p_ftruncate(int fd, off64_t size)
 {
        if (size < 0) {
                errno = EINVAL;
@@ -251,9 +251,25 @@ int p_link(const char *old, const char *new)
 
 GIT_INLINE(int) unlink_once(const wchar_t *path)
 {
+       DWORD error;
+
        if (DeleteFileW(path))
                return 0;
 
+       if ((error = GetLastError()) == ERROR_ACCESS_DENIED) {
+               WIN32_FILE_ATTRIBUTE_DATA fdata;
+               if (!GetFileAttributesExW(path, GetFileExInfoStandard, &fdata) ||
+                   !(fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ||
+                   !(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+                       goto out;
+
+               if (RemoveDirectoryW(path))
+                       return 0;
+       }
+
+out:
+       SetLastError(error);
+
        if (last_error_retryable())
                return GIT_RETRY;
 
@@ -398,18 +414,44 @@ int p_readlink(const char *path, char *buf, size_t bufsiz)
        return (int)bufsiz;
 }
 
+static bool target_is_dir(const char *target, const char *path)
+{
+       git_buf resolved = GIT_BUF_INIT;
+       git_win32_path resolved_w;
+       bool isdir = true;
+
+       if (git_path_is_absolute(target))
+               git_win32_path_from_utf8(resolved_w, target);
+       else if (git_path_dirname_r(&resolved, path) < 0 ||
+                git_path_apply_relative(&resolved, target) < 0 ||
+                git_win32_path_from_utf8(resolved_w, resolved.ptr) < 0)
+               goto out;
+
+       isdir = GetFileAttributesW(resolved_w) & FILE_ATTRIBUTE_DIRECTORY;
+
+out:
+       git_buf_dispose(&resolved);
+       return isdir;
+}
+
 int p_symlink(const char *target, const char *path)
 {
        git_win32_path target_w, path_w;
        DWORD dwFlags;
 
+       /*
+        * Convert both target and path to Windows-style paths. Note that we do
+        * not want to use `git_win32_path_from_utf8` for converting the target,
+        * as that function will automatically pre-pend the current working
+        * directory in case the path is not absolute. As Git will instead use
+        * relative symlinks, this is not someting we want.
+        */
        if (git_win32_path_from_utf8(path_w, path) < 0 ||
-               git__utf8_to_16(target_w, MAX_PATH, target) < 0)
+           git_win32_path_relative_from_utf8(target_w, target) < 0)
                return -1;
 
        dwFlags = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
-
-       if (GetFileAttributesW(target_w) & FILE_ATTRIBUTE_DIRECTORY)
+       if (target_is_dir(target, path))
                dwFlags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
 
        if (!CreateSymbolicLinkW(path_w, target_w, dwFlags))