diff options
| author | Patrick Steinhardt <ps@pks.im> | 2019-06-29 09:59:14 +0200 |
|---|---|---|
| committer | Patrick Steinhardt <ps@pks.im> | 2019-07-20 19:11:20 +0200 |
| commit | a00842c40a808d193e72bbd7bab1d8030b564447 (patch) | |
| tree | b9a287f7417669ed88680bea947e6845264b3df0 /src/win32 | |
| parent | ded77bb1f18c6cb7a0371b3f66c92387413a161d (diff) | |
| download | libgit2-a00842c40a808d193e72bbd7bab1d8030b564447.tar.gz | |
win32: correctly unlink symlinks to directories
When deleting a symlink on Windows, then the way to delete it depends on
whether it is a directory symlink or a file symlink. In the first case,
we need to use `DeleteFile`, in the second `RemoveDirectory`. Right now,
`p_unlink` will only ever try to use `DeleteFile`, though, and thus fail
to remove directory symlinks. This mismatches how unlink(3P) is expected
to behave, though, as it shall remove any symlink disregarding whether
it is a file or directory symlink.
In order to correctly unlink a symlink, we thus need to check what kind
of file this is. If we were to first query file attributes of every file
upon calling `p_unlink`, then this would penalize the common case
though. Instead, we can try to first delete the file with `DeleteFile`
and only if the error returned is `ERROR_ACCESS_DENIED` will we query
file attributes and determine whether it is a directory symlink to use
`RemoveDirectory` instead.
Diffstat (limited to 'src/win32')
| -rw-r--r-- | src/win32/posix_w32.c | 16 |
1 files changed, 16 insertions, 0 deletions
diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index 2d1fa9050..4de321796 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -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; |
