From: "Anton Serbulov via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Junio C Hamano <gitster@pobox.com>, Anton Serbulov <aserbulov@plesk.com>
Subject: [PATCH 2/2] mingw: fix getcwd when the parent directory cannot be queried
Date: Tue, 23 Oct 2018 03:52:49 -0700 (PDT) [thread overview]
Message-ID: <3e3b1c3b1ff3363b216574f09d2c9cf22d25ad30.1540291965.git.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.54.git.gitgitgadget@gmail.com>
From: Anton Serbulov <aserbulov@plesk.com>
`GetLongPathName()` function may fail when it is unable to query
the parent directory of a path component to determine the long name
for that component. It happens, because of it uses `FindFirstFile()`
function for each next short part of path. The `FindFirstFile()`
requires `List Directory` and `Synchronize` desired access for a calling
process.
In case of lacking such permission for some part of path,
the `GetLongPathName()` returns 0 as result and `GetLastError()`
returns ERROR_ACCESS_DENIED.
`GetFinalPathNameByHandle()` function can help in such cases, because
it requires `Read Attributes` and `Synchronize` desired access to the
target path only.
The `GetFinalPathNameByHandle()` function was introduced on
`Windows Server 2008/Windows Vista`. So we need to load it dynamically.
`CreateFile()` parameters:
`lpFileName` = path to the current directory
`dwDesiredAccess` = 0 (it means `Read Attributes` and `Synchronize`)
`dwShareMode` = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE
(it prevents `Sharing Violation`)
`lpSecurityAttributes` = NULL (default security attributes)
`dwCreationDisposition` = OPEN_EXISTING
(required to obtain a directory handle)
`dwFlagsAndAttributes` = FILE_FLAG_BACKUP_SEMANTICS
(required to obtain a directory handle)
`hTemplateFile` = NULL (when opening an existing file or directory,
`CreateFile` ignores this parameter)
The string that is returned by `GetFinalPathNameByHandle()` function
uses the \\?\ syntax. To skip the prefix and convert backslashes
to slashes, the `normalize_ntpath()` mingw function will be used.
Note: `GetFinalPathNameByHandle()` function returns a final path.
It is the path that is returned when a path is fully resolved.
For example, for a symbolic link named "C:\tmp\mydir" that points to
"D:\yourdir", the final path would be "D:\yourdir".
Signed-off-by: Anton Serbulov <aserbulov@plesk.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
compat/mingw.c | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/compat/mingw.c b/compat/mingw.c
index 2c3e27ce9..19addfa5d 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -202,6 +202,31 @@ static int ask_yes_no_if_possible(const char *format, ...)
}
}
+/* Normalizes NT paths as returned by some low-level APIs. */
+static wchar_t *normalize_ntpath(wchar_t *wbuf)
+{
+ int i;
+ /* fix absolute path prefixes */
+ if (wbuf[0] == '\\') {
+ /* strip NT namespace prefixes */
+ if (!wcsncmp(wbuf, L"\\??\\", 4) ||
+ !wcsncmp(wbuf, L"\\\\?\\", 4))
+ wbuf += 4;
+ else if (!wcsnicmp(wbuf, L"\\DosDevices\\", 12))
+ wbuf += 12;
+ /* replace remaining '...UNC\' with '\\' */
+ if (!wcsnicmp(wbuf, L"UNC\\", 4)) {
+ wbuf += 2;
+ *wbuf = '\\';
+ }
+ }
+ /* convert backslashes to slashes */
+ for (i = 0; wbuf[i]; i++)
+ if (wbuf[i] == '\\')
+ wbuf[i] = '/';
+ return wbuf;
+}
+
int mingw_unlink(const char *pathname)
{
int ret, tries = 0;
@@ -925,6 +950,20 @@ char *mingw_getcwd(char *pointer, int len)
return NULL;
}
ret = GetLongPathNameW(cwd, wpointer, ARRAY_SIZE(wpointer));
+ if (!ret && GetLastError() == ERROR_ACCESS_DENIED) {
+ HANDLE hnd = CreateFileW(cwd, 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ if (hnd == INVALID_HANDLE_VALUE)
+ return NULL;
+ ret = GetFinalPathNameByHandleW(hnd, wpointer, ARRAY_SIZE(wpointer), 0);
+ CloseHandle(hnd);
+ if (!ret || ret >= ARRAY_SIZE(wpointer))
+ return NULL;
+ if (xwcstoutf(pointer, normalize_ntpath(wpointer), len) < 0)
+ return NULL;
+ return pointer;
+ }
if (!ret || ret >= ARRAY_SIZE(wpointer))
return NULL;
if (xwcstoutf(pointer, wpointer, len) < 0)
--
gitgitgadget
next prev parent reply other threads:[~2018-10-23 10:52 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-10-23 10:52 [PATCH 0/2] Work around case-insensitivity issues with cwd on Windows Johannes Schindelin via GitGitGadget
2018-10-23 10:52 ` [PATCH 1/2] mingw: ensure `getcwd()` reports the correct case Johannes Schindelin via GitGitGadget
2018-10-23 10:52 ` Anton Serbulov via GitGitGadget [this message]
2018-10-23 13:16 ` [PATCH 2/2] mingw: fix getcwd when the parent directory cannot be queried Stephen & Linda Smith
2018-10-23 13:20 ` Stephen Smith
2018-10-24 9:19 ` Johannes Schindelin
2018-10-24 9:19 ` [PATCH v2 0/2] Work around case-insensitivity issues with cwd on Windows Johannes Schindelin via GitGitGadget
2018-10-24 9:19 ` [PATCH v2 1/2] mingw: ensure `getcwd()` reports the correct case Johannes Schindelin via GitGitGadget
2018-10-24 9:19 ` [PATCH v2 2/2] mingw: fix getcwd when the parent directory cannot be queried Anton Serbulov via GitGitGadget
2018-10-24 10:22 ` [PATCH v2 0/2] Work around case-insensitivity issues with cwd on Windows Junio C Hamano
2018-10-24 15:20 ` Johannes Schindelin
2018-10-25 4:11 ` Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=3e3b1c3b1ff3363b216574f09d2c9cf22d25ad30.1540291965.git.gitgitgadget@gmail.com \
--to=gitgitgadget@gmail.com \
--cc=aserbulov@plesk.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).