Skip to content

Commit

Permalink
path: use proper size for win32 api calls
Browse files Browse the repository at this point in the history
  • Loading branch information
ianhattendorf committed May 28, 2020
1 parent 396d076 commit 40a3c13
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 30 deletions.
8 changes: 6 additions & 2 deletions src/path.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include <stdio.h>
#include <ctype.h>

extern bool git_win32_longpaths_support;

static int dos_drive_prefix_length(const char *path)
{
int i;
Expand Down Expand Up @@ -1253,14 +1255,16 @@ int git_path_diriter_init(
static int diriter_update_paths(git_path_diriter *diriter)
{
size_t filename_len, path_len;

size_t max_path_utf16_length = git_win32_longpaths_support
? GIT_WIN_PATH_UTF16
: GIT_WIN_SHORT_PATH_UTF16;
filename_len = wcslen(diriter->current.cFileName);

if (GIT_ADD_SIZET_OVERFLOW(&path_len, diriter->parent_len, filename_len) ||
GIT_ADD_SIZET_OVERFLOW(&path_len, path_len, 2))
return -1;

if (path_len > GIT_WIN_PATH_UTF16) {
if (path_len > max_path_utf16_length) {
git_error_set(GIT_ERROR_FILESYSTEM,
"invalid path '%.*ls\\%ls' (path too long)",
diriter->parent_len, diriter->path, diriter->current.cFileName);
Expand Down
53 changes: 30 additions & 23 deletions src/win32/path_w32.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,6 @@ int git_win32_path_canonicalize(git_win32_path path)
{
wchar_t *base, *from, *to, *next;
size_t len;
size_t max_path_length = git_win32_longpaths_support
? GIT_WIN_PATH_UTF16
: GIT_WIN_SHORT_PATH_UTF16;

base = to = path__skip_prefix(path);

Expand Down Expand Up @@ -147,22 +144,20 @@ int git_win32_path_canonicalize(git_win32_path path)
while (to > base && to[-1] == L'\\') to--;

*to = L'\0';
len = to - path;
if (len >= 8 && wcsncmp(L"\\\\?\\UNC\\", path, 8) != 0) {
/* Not a UNC path, max length shorter by 2 */
max_path_length -= 2;
}
if (len >= max_path_length) {
if ((to - path) > INT_MAX) {
SetLastError(ERROR_FILENAME_EXCED_RANGE);
return -1;
}

return (int)len;
return (int)(to - path);
}

int git_win32_path__cwd(wchar_t *out, size_t len)
{
int cwd_len;
int max_path_length = git_win32_longpaths_support
? WIN_GIT_PATH_MAX
: WIN_GIT_SHORT_PATH_MAX;

if (len > INT_MAX) {
errno = ENAMETOOLONG;
Expand All @@ -179,7 +174,7 @@ int git_win32_path__cwd(wchar_t *out, size_t len)
* '\'s, but we we add a 'UNC' specifier to the path, plus
* a trailing directory separator, plus a NUL.
*/
if (cwd_len > WIN_GIT_PATH_MAX - 4) {
if (cwd_len > max_path_length - 4) {
errno = ENAMETOOLONG;
return -1;
}
Expand All @@ -196,7 +191,7 @@ int git_win32_path__cwd(wchar_t *out, size_t len)
* working directory. (One character for the directory separator,
* one for the null.
*/
else if (cwd_len > WIN_GIT_PATH_MAX - 2) {
else if (cwd_len > max_path_length - 2) {
errno = ENAMETOOLONG;
return -1;
}
Expand All @@ -207,20 +202,23 @@ int git_win32_path__cwd(wchar_t *out, size_t len)
int git_win32_path_from_utf8(git_win32_path out, const char *src)
{
wchar_t *dest = out;
size_t max_path_length = git_win32_longpaths_support
? WIN_GIT_PATH_MAX
: WIN_GIT_SHORT_PATH_MAX;

/* All win32 paths are in NT-prefixed format, beginning with "\\?\". */
memcpy(dest, PATH__NT_NAMESPACE, sizeof(wchar_t) * PATH__NT_NAMESPACE_LEN);
dest += PATH__NT_NAMESPACE_LEN;

/* See if this is an absolute path (beginning with a drive letter) */
if (git_path_is_absolute(src)) {
if (git__utf8_to_16(dest, WIN_GIT_PATH_MAX, src) < 0)
if (git__utf8_to_16(dest, max_path_length, src) < 0)
goto on_error;
}
/* File-prefixed NT-style paths beginning with \\?\ */
else if (path__is_nt_namespace(src)) {
/* Skip the NT prefix, the destination already contains it */
if (git__utf8_to_16(dest, WIN_GIT_PATH_MAX, src + PATH__NT_NAMESPACE_LEN) < 0)
if (git__utf8_to_16(dest, max_path_length, src + PATH__NT_NAMESPACE_LEN) < 0)
goto on_error;
}
/* UNC paths */
Expand All @@ -229,12 +227,12 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src)
dest += 4;

/* Skip the leading "\\" */
if (git__utf8_to_16(dest, WIN_GIT_PATH_MAX - 2, src + 2) < 0)
if (git__utf8_to_16(dest, max_path_length - 2, src + 2) < 0)
goto on_error;
}
/* Absolute paths omitting the drive letter */
else if (path__startswith_slash(src)) {
if (path__cwd(dest, WIN_GIT_MAX_PATH) < 0)
if (path__cwd(dest, (int)max_path_length) < 0)
goto on_error;

if (!git_path_is_absolute(dest)) {
Expand All @@ -243,19 +241,19 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src)
}

/* Skip the drive letter specification ("C:") */
if (git__utf8_to_16(dest + 2, WIN_GIT_PATH_MAX - 2, src) < 0)
if (git__utf8_to_16(dest + 2, max_path_length - 2, src) < 0)
goto on_error;
}
/* Relative paths */
else {
int cwd_len;

if ((cwd_len = git_win32_path__cwd(dest, WIN_GIT_PATH_MAX)) < 0)
if ((cwd_len = git_win32_path__cwd(dest, max_path_length)) < 0)
goto on_error;

dest[cwd_len++] = L'\\';

if (git__utf8_to_16(dest + cwd_len, WIN_GIT_PATH_MAX - cwd_len, src) < 0)
if (git__utf8_to_16(dest + cwd_len, max_path_length - cwd_len, src) < 0)
goto on_error;
}

Expand All @@ -273,6 +271,9 @@ int git_win32_path_relative_from_utf8(git_win32_path out, const char *src)
{
wchar_t *dest = out, *p;
int len;
size_t max_path_length = git_win32_longpaths_support
? WIN_GIT_PATH_MAX
: WIN_GIT_SHORT_PATH_MAX;

/* Handle absolute paths */
if (git_path_is_absolute(src) ||
Expand All @@ -282,7 +283,7 @@ int git_win32_path_relative_from_utf8(git_win32_path out, const char *src)
return git_win32_path_from_utf8(out, src);
}

if ((len = git__utf8_to_16(dest, WIN_GIT_MAX_PATH, src)) < 0)
if ((len = git__utf8_to_16(dest, max_path_length, src)) < 0)
return -1;

for (p = dest; p < (dest + len); p++) {
Expand Down Expand Up @@ -325,16 +326,19 @@ char *git_win32_path_8dot3_name(const char *path)
wchar_t *start;
char *shortname;
int len, namelen = 1;
int max_path_utf16_length = git_win32_longpaths_support
? GIT_WIN_PATH_UTF16
: GIT_WIN_SHORT_PATH_UTF16;

if (git_win32_path_from_utf8(longpath, path) < 0)
return NULL;

len = GetShortPathNameW(longpath, shortpath, GIT_WIN_PATH_UTF16);
len = GetShortPathNameW(longpath, shortpath, max_path_utf16_length);

while (len && shortpath[len-1] == L'\\')
shortpath[--len] = L'\0';

if (len == 0 || len >= GIT_WIN_PATH_UTF16)
if (len == 0 || len >= max_path_utf16_length)
return NULL;

for (start = shortpath + (len - 1);
Expand Down Expand Up @@ -370,6 +374,9 @@ int git_win32_path_readlink_w(git_win32_path dest, const git_win32_path path)
DWORD ioctl_ret;
wchar_t *target;
size_t target_len;
size_t max_path_utf16_length = git_win32_longpaths_support
? GIT_WIN_PATH_UTF16
: GIT_WIN_SHORT_PATH_UTF16;

int error = -1;

Expand Down Expand Up @@ -416,7 +423,7 @@ int git_win32_path_readlink_w(git_win32_path dest, const git_win32_path path)

/* Need one additional character in the target buffer
* for the terminating NULL. */
if (GIT_WIN_PATH_UTF16 > target_len) {
if (max_path_utf16_length > target_len) {
wcscpy(dest, target);
error = (int)target_len;
}
Expand Down
18 changes: 14 additions & 4 deletions src/win32/posix_w32.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
unsigned long git_win32__createfile_sharemode =
FILE_SHARE_READ | FILE_SHARE_WRITE;
int git_win32__retries = 10;
extern bool git_win32_longpaths_support;

GIT_INLINE(void) set_errno(void)
{
Expand Down Expand Up @@ -637,7 +638,10 @@ int p_futimes(int fd, const struct p_timeval times[2])
int p_getcwd(char *buffer_out, size_t size)
{
git_win32_path buf;
wchar_t *cwd = _wgetcwd(buf, GIT_WIN_PATH_UTF16);
int max_path_utf16_length = git_win32_longpaths_support
? GIT_WIN_PATH_UTF16
: GIT_WIN_SHORT_PATH_UTF16;
wchar_t *cwd = _wgetcwd(buf, max_path_utf16_length);

if (!cwd)
return -1;
Expand All @@ -663,6 +667,9 @@ static int getfinalpath_w(
{
HANDLE hFile;
DWORD dwChars;
DWORD max_path_utf16_length = git_win32_longpaths_support
? GIT_WIN_PATH_UTF16
: GIT_WIN_SHORT_PATH_UTF16;

/* Use FILE_FLAG_BACKUP_SEMANTICS so we can open a directory. Do not
* specify FILE_FLAG_OPEN_REPARSE_POINT; we want to open a handle to the
Expand All @@ -674,10 +681,10 @@ static int getfinalpath_w(
return -1;

/* Call GetFinalPathNameByHandle */
dwChars = GetFinalPathNameByHandleW(hFile, dest, GIT_WIN_PATH_UTF16, FILE_NAME_NORMALIZED);
dwChars = GetFinalPathNameByHandleW(hFile, dest, max_path_utf16_length, FILE_NAME_NORMALIZED);
CloseHandle(hFile);

if (!dwChars || dwChars >= GIT_WIN_PATH_UTF16)
if (!dwChars || dwChars >= max_path_utf16_length)
return -1;

/* The path may be delivered to us with a namespace prefix; remove */
Expand Down Expand Up @@ -780,14 +787,17 @@ int p_rmdir(const char* path)
char *p_realpath(const char *orig_path, char *buffer)
{
git_win32_path orig_path_w, buffer_w;
DWORD max_path_utf16_length = git_win32_longpaths_support
? GIT_WIN_PATH_UTF16
: GIT_WIN_SHORT_PATH_UTF16;

if (git_win32_path_from_utf8(orig_path_w, orig_path) < 0)
return NULL;

/* Note that if the path provided is a relative path, then the current directory
* is used to resolve the path -- which is a concurrency issue because the current
* directory is a process-wide variable. */
if (!GetFullPathNameW(orig_path_w, GIT_WIN_PATH_UTF16, buffer_w, NULL)) {
if (!GetFullPathNameW(orig_path_w, max_path_utf16_length, buffer_w, NULL)) {
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
errno = ENAMETOOLONG;
else
Expand Down
7 changes: 6 additions & 1 deletion src/win32/w32_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

#include "w32_util.h"

extern bool git_win32_longpaths_support;

/**
* Creates a FindFirstFile(Ex) filter string from a UTF-8 path.
* The filter string enumerates all items in the directory.
Expand All @@ -19,6 +21,9 @@ bool git_win32__findfirstfile_filter(git_win32_path dest, const char *src)
{
static const wchar_t suffix[] = L"\\*";
int len = git_win32_path_from_utf8(dest, src);
size_t max_path_utf16_length = git_win32_longpaths_support
? GIT_WIN_PATH_UTF16
: GIT_WIN_SHORT_PATH_UTF16;

/* Ensure the path was converted */
if (len < 0)
Expand All @@ -35,7 +40,7 @@ bool git_win32__findfirstfile_filter(git_win32_path dest, const char *src)
}

/* Ensure we have enough room to add the suffix */
if ((size_t)len >= GIT_WIN_PATH_UTF16 - CONST_STRLEN(suffix))
if ((size_t)len >= max_path_utf16_length - CONST_STRLEN(suffix))
return false;

wcscat(dest, suffix);
Expand Down

0 comments on commit 40a3c13

Please sign in to comment.