Skip to content

Commit

Permalink
Fix Windows-specific problem to determine the real path near a drive …
Browse files Browse the repository at this point in the history
…root (#4253)

This PR fixes a pretty fundamental problem where
`win32_strbuf_realpath()` mishandled non-existing paths that are close
to the drive root, e.g. `C:/foo`.

This fixes #4200
  • Loading branch information
dscho authored Jan 28, 2023
2 parents fd21527 + b9457d6 commit 10a93f8
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 8 deletions.
29 changes: 21 additions & 8 deletions compat/mingw.c
Original file line number Diff line number Diff line change
Expand Up @@ -1386,6 +1386,7 @@ char *mingw_strbuf_realpath(struct strbuf *resolved, const char *path)
DWORD ret;
int len;
const char *last_component = NULL;
char *append = NULL;

if (xutftowcs_path(wpath, path) < 0)
return NULL;
Expand All @@ -1408,34 +1409,46 @@ char *mingw_strbuf_realpath(struct strbuf *resolved, const char *path)
break; /* found start of last component */

if (p != wpath && (last_component = find_last_dir_sep(path))) {
last_component++; /* skip directory separator */
*p = L'\0';
append = xstrdup(last_component + 1); /* skip directory separator */
/*
* Do not strip the trailing slash at the drive root, otherwise
* the path would be e.g. `C:` (which resolves to the
* _current_ directory on that drive).
*/
if (p[-1] == L':')
p[1] = L'\0';
else
*p = L'\0';
h = CreateFileW(wpath, 0, FILE_SHARE_READ |
FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, NULL);
}
}

if (h == INVALID_HANDLE_VALUE)
if (h == INVALID_HANDLE_VALUE) {
realpath_failed:
FREE_AND_NULL(append);
return NULL;
}

ret = GetFinalPathNameByHandleW(h, wpath, ARRAY_SIZE(wpath), 0);
CloseHandle(h);
if (!ret || ret >= ARRAY_SIZE(wpath))
return NULL;
goto realpath_failed;

len = wcslen(wpath) * 3;
strbuf_grow(resolved, len);
len = xwcstoutf(resolved->buf, normalize_ntpath(wpath), len);
if (len < 0)
return NULL;
goto realpath_failed;
resolved->len = len;

if (last_component) {
if (append) {
/* Use forward-slash, like `normalize_ntpath()` */
strbuf_addch(resolved, '/');
strbuf_addstr(resolved, last_component);
strbuf_complete(resolved, '/');
strbuf_addstr(resolved, append);
FREE_AND_NULL(append);
}

return resolved->buf;
Expand Down
6 changes: 6 additions & 0 deletions t/t0060-path-utils.sh
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,12 @@ test_expect_success SYMLINKS 'real path works on symlinks' '
test "$sym" = "$(test-tool path-utils real_path "$dir2/syml")"
'

test_expect_success MINGW 'real path works near drive root' '
# we need a non-existing path at the drive root; simply skip if C:/xyz exists
test -e C:/xyz ||
test C:/xyz = $(test-tool path-utils real_path C:/xyz)
'

test_expect_success SYMLINKS 'prefix_path works with absolute paths to work tree symlinks' '
ln -s target symlink &&
test "$(test-tool path-utils prefix_path prefix "$(pwd)/symlink")" = "symlink"
Expand Down

0 comments on commit 10a93f8

Please sign in to comment.