Skip to content

Commit 10a93f8

Browse files
authored
Fix Windows-specific problem to determine the real path near a drive 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
2 parents fd21527 + b9457d6 commit 10a93f8

File tree

2 files changed

+27
-8
lines changed

2 files changed

+27
-8
lines changed

compat/mingw.c

+21-8
Original file line numberDiff line numberDiff line change
@@ -1386,6 +1386,7 @@ char *mingw_strbuf_realpath(struct strbuf *resolved, const char *path)
13861386
DWORD ret;
13871387
int len;
13881388
const char *last_component = NULL;
1389+
char *append = NULL;
13891390

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

14101411
if (p != wpath && (last_component = find_last_dir_sep(path))) {
1411-
last_component++; /* skip directory separator */
1412-
*p = L'\0';
1412+
append = xstrdup(last_component + 1); /* skip directory separator */
1413+
/*
1414+
* Do not strip the trailing slash at the drive root, otherwise
1415+
* the path would be e.g. `C:` (which resolves to the
1416+
* _current_ directory on that drive).
1417+
*/
1418+
if (p[-1] == L':')
1419+
p[1] = L'\0';
1420+
else
1421+
*p = L'\0';
14131422
h = CreateFileW(wpath, 0, FILE_SHARE_READ |
14141423
FILE_SHARE_WRITE | FILE_SHARE_DELETE,
14151424
NULL, OPEN_EXISTING,
14161425
FILE_FLAG_BACKUP_SEMANTICS, NULL);
14171426
}
14181427
}
14191428

1420-
if (h == INVALID_HANDLE_VALUE)
1429+
if (h == INVALID_HANDLE_VALUE) {
1430+
realpath_failed:
1431+
FREE_AND_NULL(append);
14211432
return NULL;
1433+
}
14221434

14231435
ret = GetFinalPathNameByHandleW(h, wpath, ARRAY_SIZE(wpath), 0);
14241436
CloseHandle(h);
14251437
if (!ret || ret >= ARRAY_SIZE(wpath))
1426-
return NULL;
1438+
goto realpath_failed;
14271439

14281440
len = wcslen(wpath) * 3;
14291441
strbuf_grow(resolved, len);
14301442
len = xwcstoutf(resolved->buf, normalize_ntpath(wpath), len);
14311443
if (len < 0)
1432-
return NULL;
1444+
goto realpath_failed;
14331445
resolved->len = len;
14341446

1435-
if (last_component) {
1447+
if (append) {
14361448
/* Use forward-slash, like `normalize_ntpath()` */
1437-
strbuf_addch(resolved, '/');
1438-
strbuf_addstr(resolved, last_component);
1449+
strbuf_complete(resolved, '/');
1450+
strbuf_addstr(resolved, append);
1451+
FREE_AND_NULL(append);
14391452
}
14401453

14411454
return resolved->buf;

t/t0060-path-utils.sh

+6
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,12 @@ test_expect_success SYMLINKS 'real path works on symlinks' '
237237
test "$sym" = "$(test-tool path-utils real_path "$dir2/syml")"
238238
'
239239

240+
test_expect_success MINGW 'real path works near drive root' '
241+
# we need a non-existing path at the drive root; simply skip if C:/xyz exists
242+
test -e C:/xyz ||
243+
test C:/xyz = $(test-tool path-utils real_path C:/xyz)
244+
'
245+
240246
test_expect_success SYMLINKS 'prefix_path works with absolute paths to work tree symlinks' '
241247
ln -s target symlink &&
242248
test "$(test-tool path-utils prefix_path prefix "$(pwd)/symlink")" = "symlink"

0 commit comments

Comments
 (0)