Skip to content

Commit

Permalink
win: remove MAX_PATH limitations
Browse files Browse the repository at this point in the history
Since Windows 10 1607 some WinApi functions no longer have a MAX_PATH
limit on the filenames length. This removes this hard-coded path length
limit from various places in libuv, switching to dynamically allocating
string buffers.

Fixes: libuv#2331

PR-URL: libuv#2788
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
  • Loading branch information
bzoz committed Apr 28, 2020
1 parent 4d3779c commit da7e50b
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 43 deletions.
33 changes: 26 additions & 7 deletions src/win/fs-event.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ static void uv_relative_path(const WCHAR* filename,
static int uv_split_path(const WCHAR* filename, WCHAR** dir,
WCHAR** file) {
size_t len, i;
DWORD dir_len;

if (filename == NULL) {
if (dir != NULL)
Expand All @@ -97,12 +98,16 @@ static int uv_split_path(const WCHAR* filename, WCHAR** dir,

if (i == 0) {
if (dir) {
*dir = (WCHAR*)uv__malloc((MAX_PATH + 1) * sizeof(WCHAR));
dir_len = GetCurrentDirectoryW(0, NULL);
if (dir_len == 0) {
return -1;
}
*dir = (WCHAR*)uv__malloc(dir_len * sizeof(WCHAR));
if (!*dir) {
uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
}

if (!GetCurrentDirectoryW(MAX_PATH, *dir)) {
if (!GetCurrentDirectoryW(dir_len, *dir)) {
uv__free(*dir);
*dir = NULL;
return -1;
Expand Down Expand Up @@ -155,9 +160,11 @@ int uv_fs_event_start(uv_fs_event_t* handle,
int name_size, is_path_dir, size;
DWORD attr, last_error;
WCHAR* dir = NULL, *dir_to_watch, *pathw = NULL;
WCHAR short_path_buffer[MAX_PATH];
DWORD short_path_buffer_len;
WCHAR *short_path_buffer;
WCHAR* short_path, *long_path;

short_path = NULL;
if (uv__is_active(handle))
return UV_EINVAL;

Expand Down Expand Up @@ -230,13 +237,23 @@ int uv_fs_event_start(uv_fs_event_t* handle,
*/

/* Convert to short path. */
short_path_buffer = NULL;
short_path_buffer_len = GetShortPathNameW(pathw, NULL, 0);
if (short_path_buffer_len == 0) {
goto short_path_done;
}
short_path_buffer = uv__malloc(short_path_buffer_len * sizeof(WCHAR));
if (short_path_buffer == NULL) {
goto short_path_done;
}
if (GetShortPathNameW(pathw,
short_path_buffer,
ARRAY_SIZE(short_path_buffer))) {
short_path = short_path_buffer;
} else {
short_path = NULL;
short_path_buffer_len) == 0) {
uv__free(short_path_buffer);
short_path_buffer = NULL;
}
short_path_done:
short_path = short_path_buffer;

if (uv_split_path(pathw, &dir, &handle->filew) != 0) {
last_error = GetLastError();
Expand Down Expand Up @@ -346,6 +363,8 @@ int uv_fs_event_start(uv_fs_event_t* handle,
if (uv__is_active(handle))
uv__handle_stop(handle);

uv__free(short_path);

return uv_translate_sys_error(last_error);
}

Expand Down
122 changes: 86 additions & 36 deletions src/win/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,20 +154,26 @@ int uv_exepath(char* buffer, size_t* size_ptr) {

int uv_cwd(char* buffer, size_t* size) {
DWORD utf16_len;
WCHAR utf16_buffer[MAX_PATH];
WCHAR *utf16_buffer;
int r;

if (buffer == NULL || size == NULL) {
return UV_EINVAL;
}

utf16_len = GetCurrentDirectoryW(MAX_PATH, utf16_buffer);
utf16_len = GetCurrentDirectoryW(0, NULL);
if (utf16_len == 0) {
return uv_translate_sys_error(GetLastError());
} else if (utf16_len > MAX_PATH) {
/* This should be impossible; however the CRT has a code path to deal with
* this scenario, so I added a check anyway. */
return UV_EIO;
}
utf16_buffer = uv__malloc(utf16_len * sizeof(WCHAR));
if (utf16_buffer == NULL) {
return UV_ENOMEM;
}

utf16_len = GetCurrentDirectoryW(utf16_len, utf16_buffer);
if (utf16_len == 0) {
uv__free(utf16_buffer);
return uv_translate_sys_error(GetLastError());
}

/* utf16_len contains the length, *not* including the terminating null. */
Expand All @@ -191,8 +197,10 @@ int uv_cwd(char* buffer, size_t* size) {
NULL,
NULL);
if (r == 0) {
uv__free(utf16_buffer);
return uv_translate_sys_error(GetLastError());
} else if (r > (int) *size) {
uv__free(utf16_buffer);
*size = r;
return UV_ENOBUFS;
}
Expand All @@ -206,6 +214,8 @@ int uv_cwd(char* buffer, size_t* size) {
*size > INT_MAX ? INT_MAX : (int) *size,
NULL,
NULL);
uv__free(utf16_buffer);

if (r == 0) {
return uv_translate_sys_error(GetLastError());
}
Expand All @@ -216,43 +226,61 @@ int uv_cwd(char* buffer, size_t* size) {


int uv_chdir(const char* dir) {
WCHAR utf16_buffer[MAX_PATH];
size_t utf16_len;
WCHAR *utf16_buffer;
size_t utf16_len, new_utf16_len;
WCHAR drive_letter, env_var[4];

if (dir == NULL) {
return UV_EINVAL;
}

utf16_len = MultiByteToWideChar(CP_UTF8,
0,
dir,
-1,
NULL,
0);
if (utf16_len == 0) {
return uv_translate_sys_error(GetLastError());
}
utf16_buffer = uv__malloc(utf16_len * sizeof(WCHAR));
if (utf16_buffer == NULL) {
return UV_ENOMEM;
}

if (MultiByteToWideChar(CP_UTF8,
0,
dir,
-1,
utf16_buffer,
MAX_PATH) == 0) {
DWORD error = GetLastError();
/* The maximum length of the current working directory is 260 chars,
* including terminating null. If it doesn't fit, the path name must be too
* long. */
if (error == ERROR_INSUFFICIENT_BUFFER) {
return UV_ENAMETOOLONG;
} else {
return uv_translate_sys_error(error);
}
utf16_len) == 0) {
uv__free(utf16_buffer);
return uv_translate_sys_error(GetLastError());
}

if (!SetCurrentDirectoryW(utf16_buffer)) {
uv__free(utf16_buffer);
return uv_translate_sys_error(GetLastError());
}

/* Windows stores the drive-local path in an "hidden" environment variable,
* which has the form "=C:=C:\Windows". SetCurrentDirectory does not update
* this, so we'll have to do it. */
utf16_len = GetCurrentDirectoryW(MAX_PATH, utf16_buffer);
new_utf16_len = GetCurrentDirectoryW(utf16_len, utf16_buffer);
if (new_utf16_len > utf16_len ) {
uv__free(utf16_buffer);
utf16_buffer = uv__malloc(new_utf16_len * sizeof(WCHAR));
if (utf16_buffer == NULL) {
/* When updating the environment variable fails, return UV_OK anyway.
* We did successfully change current working directory, only updating
* hidden env variable failed. */
return 0;
}
new_utf16_len = GetCurrentDirectoryW(new_utf16_len, utf16_buffer);
}
if (utf16_len == 0) {
return uv_translate_sys_error(GetLastError());
} else if (utf16_len > MAX_PATH) {
return UV_EIO;
uv__free(utf16_buffer);
return 0;
}

/* The returned directory should not have a trailing slash, unless it points
Expand Down Expand Up @@ -284,11 +312,10 @@ int uv_chdir(const char* dir) {
env_var[2] = L':';
env_var[3] = L'\0';

if (!SetEnvironmentVariableW(env_var, utf16_buffer)) {
return uv_translate_sys_error(GetLastError());
}
SetEnvironmentVariableW(env_var, utf16_buffer);
}

uv__free(utf16_buffer);
return 0;
}

Expand Down Expand Up @@ -1167,20 +1194,29 @@ int uv_os_homedir(char* buffer, size_t* size) {


int uv_os_tmpdir(char* buffer, size_t* size) {
wchar_t path[MAX_PATH + 2];
wchar_t *path;
DWORD bufsize;
size_t len;

if (buffer == NULL || size == NULL || *size == 0)
return UV_EINVAL;

len = GetTempPathW(ARRAY_SIZE(path), path);
len = 0;
len = GetTempPathW(0, NULL);
if (len == 0) {
return uv_translate_sys_error(GetLastError());
}
/* Include space for terminating null char. */
len += 1;
path = uv__malloc(len * sizeof(wchar_t));
if (path == NULL) {
return UV_ENOMEM;
}
len = GetTempPathW(len, path);

if (len == 0) {
uv__free(path);
return uv_translate_sys_error(GetLastError());
} else if (len > ARRAY_SIZE(path)) {
/* This should not be possible */
return UV_EIO;
}

/* The returned directory should not have a trailing slash, unless it points
Expand All @@ -1195,8 +1231,10 @@ int uv_os_tmpdir(char* buffer, size_t* size) {
bufsize = WideCharToMultiByte(CP_UTF8, 0, path, -1, NULL, 0, NULL, NULL);

if (bufsize == 0) {
uv__free(path);
return uv_translate_sys_error(GetLastError());
} else if (bufsize > *size) {
uv__free(path);
*size = bufsize;
return UV_ENOBUFS;
}
Expand All @@ -1210,6 +1248,7 @@ int uv_os_tmpdir(char* buffer, size_t* size) {
*size,
NULL,
NULL);
uv__free(path);

if (bufsize == 0)
return uv_translate_sys_error(GetLastError());
Expand Down Expand Up @@ -1329,7 +1368,7 @@ int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16) {
int uv__getpwuid_r(uv_passwd_t* pwd) {
HANDLE token;
wchar_t username[UNLEN + 1];
wchar_t path[MAX_PATH];
wchar_t *path;
DWORD bufsize;
int r;

Expand All @@ -1340,15 +1379,24 @@ int uv__getpwuid_r(uv_passwd_t* pwd) {
if (OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &token) == 0)
return uv_translate_sys_error(GetLastError());

bufsize = ARRAY_SIZE(path);
if (!GetUserProfileDirectoryW(token, path, &bufsize)) {
bufsize = 0;
GetUserProfileDirectoryW(token, NULL, &bufsize);
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
r = GetLastError();
CloseHandle(token);
return uv_translate_sys_error(r);
}

/* This should not be possible */
if (r == ERROR_INSUFFICIENT_BUFFER)
return UV_ENOMEM;
path = uv__malloc(bufsize * sizeof(wchar_t));
if (path == NULL) {
CloseHandle(token);
return UV_ENOMEM;
}

if (!GetUserProfileDirectoryW(token, path, &bufsize)) {
r = GetLastError();
CloseHandle(token);
uv__free(path);
return uv_translate_sys_error(r);
}

Expand All @@ -1358,6 +1406,7 @@ int uv__getpwuid_r(uv_passwd_t* pwd) {
bufsize = ARRAY_SIZE(username);
if (!GetUserNameW(username, &bufsize)) {
r = GetLastError();
uv__free(path);

/* This should not be possible */
if (r == ERROR_INSUFFICIENT_BUFFER)
Expand All @@ -1368,6 +1417,7 @@ int uv__getpwuid_r(uv_passwd_t* pwd) {

pwd->homedir = NULL;
r = uv__convert_utf16_to_utf8(path, -1, &pwd->homedir);
uv__free(path);

if (r != 0)
return r;
Expand Down

0 comments on commit da7e50b

Please sign in to comment.