Commit a00842c4 by Patrick Steinhardt

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.
parent ded77bb1
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment