summaryrefslogtreecommitdiff
path: root/compat/mingw.c
diff options
context:
space:
mode:
Diffstat (limited to 'compat/mingw.c')
-rw-r--r--compat/mingw.c64
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(&current_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