diff options
Diffstat (limited to 'compat/mingw.c')
| -rw-r--r-- | compat/mingw.c | 64 |
1 files changed, 52 insertions, 12 deletions
diff --git a/compat/mingw.c b/compat/mingw.c index f524c54d06..8a9972a1ca 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -21,6 +21,9 @@ #include "gettext.h" #define SECURITY_WIN32 #include <sspi.h> +#include <winternl.h> + +#define STATUS_DELETE_PENDING ((NTSTATUS) 0xC0000056) #define HCAST(type, handle) ((type)(intptr_t)handle) @@ -302,7 +305,7 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf) return wbuf; } -int mingw_unlink(const char *pathname) +int mingw_unlink(const char *pathname, int handle_in_use_error) { int ret, tries = 0; wchar_t wpathname[MAX_PATH]; @@ -317,6 +320,9 @@ int mingw_unlink(const char *pathname) while ((ret = _wunlink(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) { if (!is_file_in_use_error(GetLastError())) break; + if (!handle_in_use_error) + return ret; + /* * We assume that some other process had the source or * destination file open at the wrong moment and retry. @@ -621,6 +627,8 @@ int mingw_open (const char *filename, int oflags, ...) wchar_t wfilename[MAX_PATH]; open_fn_t open_fn; + DECLARE_PROC_ADDR(ntdll.dll, NTSTATUS, NTAPI, RtlGetLastNtStatus, void); + va_start(args, oflags); mode = va_arg(args, int); va_end(args); @@ -644,6 +652,21 @@ int mingw_open (const char *filename, int oflags, ...) fd = open_fn(wfilename, oflags, mode); + /* + * Internally, `_wopen()` uses the `CreateFile()` API with CREATE_NEW, + * which may error out with ERROR_ACCESS_DENIED and an NtStatus of + * STATUS_DELETE_PENDING when the file is scheduled for deletion via + * `DeleteFileW()`. The file essentially exists, so we map errno to + * EEXIST instead of EACCESS so that callers don't have to special-case + * this. + * + * This fixes issues for example with the lockfile interface when one + * process has a lock that it is about to commit or release while + * another process wants to acquire it. + */ + if (fd < 0 && create && GetLastError() == ERROR_ACCESS_DENIED && + INIT_PROC_ADDR(RtlGetLastNtStatus) && RtlGetLastNtStatus() == STATUS_DELETE_PENDING) + errno = EEXIST; if (fd < 0 && (oflags & O_ACCMODE) != O_RDONLY && errno == EACCES) { DWORD attrs = GetFileAttributesW(wfilename); if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY)) @@ -2826,31 +2849,44 @@ static void setup_windows_environment(void) } } -static PSID get_current_user_sid(void) +static void get_current_user_sid(PSID *sid, HANDLE *linked_token) { HANDLE token; DWORD len = 0; - PSID result = NULL; + TOKEN_ELEVATION_TYPE elevationType; + DWORD size; + + *sid = NULL; + *linked_token = NULL; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) - return NULL; + return; if (!GetTokenInformation(token, TokenUser, NULL, 0, &len)) { TOKEN_USER *info = xmalloc((size_t)len); if (GetTokenInformation(token, TokenUser, info, len, &len)) { len = GetLengthSid(info->User.Sid); - result = xmalloc(len); - if (!CopySid(len, result, info->User.Sid)) { + *sid = xmalloc(len); + if (!CopySid(len, *sid, info->User.Sid)) { error(_("failed to copy SID (%ld)"), GetLastError()); - FREE_AND_NULL(result); + FREE_AND_NULL(*sid); } } FREE_AND_NULL(info); } - CloseHandle(token); - return result; + if (GetTokenInformation(token, TokenElevationType, &elevationType, sizeof(elevationType), &size) && + elevationType == TokenElevationTypeLimited) { + /* + * The current process is run by a member of the Administrators + * group, but is not running elevated. + */ + if (!GetTokenInformation(token, TokenLinkedToken, linked_token, sizeof(*linked_token), &size)) + linked_token = NULL; /* there is no linked token */ + } + + CloseHandle(token); } static BOOL user_sid_to_user_name(PSID sid, LPSTR *str) @@ -2931,18 +2967,22 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report) else if (sid && IsValidSid(sid)) { /* Now, verify that the SID matches the current user's */ static PSID current_user_sid; + static HANDLE linked_token; BOOL is_member; if (!current_user_sid) - current_user_sid = get_current_user_sid(); + get_current_user_sid(¤t_user_sid, &linked_token); if (current_user_sid && IsValidSid(current_user_sid) && EqualSid(sid, current_user_sid)) result = 1; else if (IsWellKnownSid(sid, WinBuiltinAdministratorsSid) && - CheckTokenMembership(NULL, sid, &is_member) && - is_member) + ((CheckTokenMembership(NULL, sid, &is_member) && + is_member) || + (linked_token && + CheckTokenMembership(linked_token, sid, &is_member) && + is_member))) /* * If owned by the Administrators group, and the * current user is an administrator, we consider that |
