mingw: optionally use legacy (non-POSIX) delete semantics
At some point between Windows 10 Build 17134.1304 and Build 18363.657, the default behavior of `DeleteFileW()` was changed to use POSIX semantics (https://stackoverflow.com/a/60512798). Under those semantics, a file can be deleted even when another process holds an active `MapViewOfFile` view on it: the directory entry is removed immediately, but the underlying data persists until the last handle is closed. On older Windows versions (and Windows 10 builds before that change), `DeleteFileW()` uses legacy semantics where deletion fails outright if any process holds a file mapping. To allow testing code paths that depend on the legacy behavior, introduce a `GIT_TEST_LEGACY_DELETE` environment variable. When set, `mingw_unlink()` uses `SetFileInformationByHandle()` with `FileDispositionInfo` (the non-POSIX variant) instead of `DeleteFileW()`, forcing legacy delete semantics regardless of the Windows version. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>main
parent
94f057755b
commit
69ed0e35a7
|
|
@ -449,20 +449,63 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
|
|||
return wbuf;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use SetFileInformationByHandle(FileDispositionInfo) to force legacy
|
||||
* (non-POSIX) delete semantics. On Windows 11, DeleteFileW() uses POSIX
|
||||
* delete semantics internally, allowing deletion even with active
|
||||
* MapViewOfFile views. This helper simulates Windows 10 behavior where
|
||||
* deletion fails if a file mapping exists.
|
||||
*
|
||||
* Returns nonzero on success (like DeleteFileW), 0 on failure.
|
||||
*/
|
||||
static int legacy_delete_file(const wchar_t *wpathname)
|
||||
{
|
||||
FILE_DISPOSITION_INFO fdi = { TRUE };
|
||||
DWORD gle;
|
||||
HANDLE h = CreateFileW(wpathname, DELETE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE |
|
||||
FILE_SHARE_DELETE,
|
||||
NULL, OPEN_EXISTING,
|
||||
FILE_FLAG_OPEN_REPARSE_POINT, NULL);
|
||||
if (h == INVALID_HANDLE_VALUE)
|
||||
return 0;
|
||||
|
||||
if (SetFileInformationByHandle(h, FileDispositionInfo,
|
||||
&fdi, sizeof(fdi))) {
|
||||
CloseHandle(h);
|
||||
return 1;
|
||||
}
|
||||
gle = GetLastError();
|
||||
CloseHandle(h);
|
||||
SetLastError(gle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int try_delete_file(const wchar_t *wpathname, int use_legacy)
|
||||
{
|
||||
if (use_legacy)
|
||||
return legacy_delete_file(wpathname);
|
||||
return DeleteFileW(wpathname);
|
||||
}
|
||||
|
||||
int mingw_unlink(const char *pathname, int handle_in_use_error)
|
||||
{
|
||||
static int use_legacy_delete = -1;
|
||||
int tries = 0;
|
||||
wchar_t wpathname[MAX_PATH];
|
||||
if (xutftowcs_path(wpathname, pathname) < 0)
|
||||
return -1;
|
||||
|
||||
if (DeleteFileW(wpathname))
|
||||
if (use_legacy_delete < 0)
|
||||
use_legacy_delete = git_env_bool("GIT_TEST_LEGACY_DELETE", 0);
|
||||
|
||||
if (try_delete_file(wpathname, use_legacy_delete))
|
||||
return 0;
|
||||
|
||||
do {
|
||||
/* read-only files cannot be removed */
|
||||
_wchmod(wpathname, 0666);
|
||||
if (!_wunlink(wpathname))
|
||||
if (try_delete_file(wpathname, use_legacy_delete))
|
||||
return 0;
|
||||
if (!is_file_in_use_error(GetLastError()))
|
||||
break;
|
||||
|
|
|
|||
Loading…
Reference in New Issue