Skip to content

Commit 3278e3b

Browse files
committed
[Bug #21177] Win32: Allow longer path name
1 parent e51411f commit 3278e3b

File tree

2 files changed

+58
-23
lines changed

2 files changed

+58
-23
lines changed

test/ruby/test_dir.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,19 @@ def test_home_at_startup_windows
641641
assert_equal("C:/ruby/homepath", Dir.home)
642642
end;
643643
end
644+
645+
def test_children_long_name
646+
Dir.mktmpdir do |dirname|
647+
longest_possible_component = "b" * 255
648+
long_path = File.join(dirname, longest_possible_component)
649+
Dir.mkdir(long_path)
650+
File.write("#{long_path}/c", "")
651+
assert_equal(%w[c], Dir.children(long_path))
652+
ensure
653+
File.unlink("#{long_path}/c")
654+
Dir.rmdir(long_path)
655+
end
656+
end
644657
end
645658

646659
def test_home

win32/win32.c

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1967,14 +1967,28 @@ open_special(const WCHAR *path, DWORD access, DWORD flags)
19671967

19681968
static const WCHAR namespace_prefix[] = {L'\\', L'\\', L'?', L'\\'};
19691969

1970-
enum {FINAL_PATH_MAX = PATH_MAX + numberof(namespace_prefix)};
1970+
/* License: Ruby's */
1971+
/* returns 0 on failure, otherwise stores tha path in `*pathptr` and
1972+
* returns the length of that path. The path must be freed. */
1973+
static DWORD
1974+
get_handle_pathname(HANDLE fh, WCHAR **pathptr, DWORD add)
1975+
{
1976+
DWORD len = GetFinalPathNameByHandleW(fh, NULL, 0, 0);
1977+
if (!len) return 0;
1978+
WCHAR *path = malloc((len + add + 1) * sizeof(WCHAR));
1979+
if (!(*pathptr = path)) return 0;
1980+
len = GetFinalPathNameByHandleW(fh, path, len + 1, 0);
1981+
if (!len) free(path);
1982+
return len;
1983+
}
19711984

19721985
/* License: Artistic or GPL */
19731986
static HANDLE
19741987
open_dir_handle(const WCHAR *filename, WIN32_FIND_DATAW *fd)
19751988
{
19761989
HANDLE fh;
1977-
WCHAR fullname[FINAL_PATH_MAX + rb_strlen_lit("\\*")];
1990+
int wildcard_len = rb_strlen_lit("\\*");
1991+
WCHAR *fullname = 0;
19781992
WCHAR *p;
19791993
int len = 0;
19801994

@@ -1984,21 +1998,18 @@ open_dir_handle(const WCHAR *filename, WIN32_FIND_DATAW *fd)
19841998

19851999
fh = open_special(filename, 0, 0);
19862000
if (fh != INVALID_HANDLE_VALUE) {
1987-
len = GetFinalPathNameByHandleW(fh, fullname, FINAL_PATH_MAX, 0);
2001+
len = get_handle_pathname(fh, &fullname, wildcard_len);
19882002
CloseHandle(fh);
1989-
if (len >= FINAL_PATH_MAX) {
1990-
errno = ENAMETOOLONG;
1991-
return INVALID_HANDLE_VALUE;
1992-
}
19932003
}
19942004
if (!len) {
19952005
len = lstrlenW(filename);
1996-
if (len >= PATH_MAX) {
1997-
errno = ENAMETOOLONG;
1998-
return INVALID_HANDLE_VALUE;
1999-
}
2006+
fullname = malloc((len + wildcard_len + 1) * sizeof(WCHAR));
2007+
if (!fullname) return INVALID_HANDLE_VALUE;
20002008
MEMCPY(fullname, filename, WCHAR, len);
20012009
}
2010+
else {
2011+
RUBY_ASSERT(fullname);
2012+
}
20022013
p = &fullname[len-1];
20032014
if (!(isdirsep(*p) || *p == L':')) *++p = L'\\';
20042015
*++p = L'*';
@@ -2011,6 +2022,7 @@ open_dir_handle(const WCHAR *filename, WIN32_FIND_DATAW *fd)
20112022
if (fh == INVALID_HANDLE_VALUE) {
20122023
errno = map_errno(GetLastError());
20132024
}
2025+
free(fullname);
20142026
return fh;
20152027
}
20162028

@@ -5697,7 +5709,6 @@ check_valid_dir(const WCHAR *path)
56975709
WIN32_FIND_DATAW fd;
56985710
HANDLE fh;
56995711
WCHAR full[PATH_MAX];
5700-
WCHAR *dmy;
57015712
WCHAR *p, *q;
57025713

57035714
/* GetFileAttributes() determines "..." as directory. */
@@ -5713,12 +5724,20 @@ check_valid_dir(const WCHAR *path)
57135724

57145725
/* if the specified path is the root of a drive and the drive is empty, */
57155726
/* FindFirstFile() returns INVALID_HANDLE_VALUE. */
5716-
if (!GetFullPathNameW(path, sizeof(full) / sizeof(WCHAR), full, &dmy)) {
5727+
DWORD len = GetFullPathNameW(path, numberof(full), full, NULL);
5728+
if (len >= numberof(full)) {
5729+
WCHAR *fullpath = malloc(len * sizeof(WCHAR));
5730+
if (!fullpath) return -1;
5731+
len = GetFullPathNameW(path, len, fullpath, NULL);
5732+
if (len == 3) MEMCPY(full, fullpath, WCHAR, len+1);
5733+
free(fullpath);
5734+
}
5735+
if (!len) {
57175736
errno = map_errno(GetLastError());
57185737
return -1;
57195738
}
5720-
if (full[1] == L':' && !full[3] && GetDriveTypeW(full) != DRIVE_NO_ROOT_DIR)
5721-
return 0;
5739+
if (len == 3 && full[1] == L':' && GetDriveTypeW(full) != DRIVE_NO_ROOT_DIR)
5740+
return 0; /* x:\ only */
57225741

57235742
fh = open_dir_handle(path, &fd);
57245743
if (fh == INVALID_HANDLE_VALUE)
@@ -5757,8 +5776,11 @@ stat_by_find(const WCHAR *path, struct stati128 *st)
57575776
static int
57585777
path_drive(const WCHAR *path)
57595778
{
5760-
return (iswalpha(path[0]) && path[1] == L':') ?
5761-
towupper(path[0]) - L'A' : _getdrive() - 1;
5779+
if (path[0] && path[1] == L':') {
5780+
if (iswalpha(path[0])) return towupper(path[0]) - L'A';
5781+
return (int)path[0];
5782+
}
5783+
return _getdrive() - 1;
57625784
}
57635785

57645786
/* License: Ruby's */
@@ -5767,7 +5789,7 @@ winnt_stat(const WCHAR *path, struct stati128 *st, BOOL lstat)
57675789
{
57685790
DWORD flags = lstat ? FILE_FLAG_OPEN_REPARSE_POINT : 0;
57695791
HANDLE f;
5770-
WCHAR finalname[PATH_MAX];
5792+
WCHAR *finalname = 0;
57715793
int open_error;
57725794

57735795
memset(st, 0, sizeof(*st));
@@ -5788,7 +5810,6 @@ winnt_stat(const WCHAR *path, struct stati128 *st, BOOL lstat)
57885810
}
57895811
if (f != INVALID_HANDLE_VALUE) {
57905812
DWORD attr = stati128_handle(f, st);
5791-
const DWORD len = GetFinalPathNameByHandleW(f, finalname, numberof(finalname), 0);
57925813
unsigned mode = 0;
57935814
switch (GetFileType(f)) {
57945815
case FILE_TYPE_CHAR:
@@ -5798,6 +5819,9 @@ winnt_stat(const WCHAR *path, struct stati128 *st, BOOL lstat)
57985819
mode = S_IFIFO;
57995820
break;
58005821
default:
5822+
if (attr & FILE_ATTRIBUTE_DIRECTORY) {
5823+
if (check_valid_dir(path)) return -1;
5824+
}
58015825
if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
58025826
FILE_ATTRIBUTE_TAG_INFO attr_info;
58035827
DWORD e;
@@ -5818,13 +5842,10 @@ winnt_stat(const WCHAR *path, struct stati128 *st, BOOL lstat)
58185842
}
58195843
}
58205844
}
5845+
const DWORD len = get_handle_pathname(f, &finalname, 0);
58215846
CloseHandle(f);
5822-
if (attr & FILE_ATTRIBUTE_DIRECTORY) {
5823-
if (check_valid_dir(path)) return -1;
5824-
}
58255847
st->st_mode = fileattr_to_unixmode(attr, path, mode);
58265848
if (len) {
5827-
finalname[min(len, numberof(finalname)-1)] = L'\0';
58285849
path = finalname;
58295850
if (wcsncmp(path, namespace_prefix, numberof(namespace_prefix)) == 0)
58305851
path += numberof(namespace_prefix);
@@ -5841,6 +5862,7 @@ winnt_stat(const WCHAR *path, struct stati128 *st, BOOL lstat)
58415862
}
58425863

58435864
st->st_dev = st->st_rdev = path_drive(path);
5865+
if (finalname) free(finalname);
58445866

58455867
return 0;
58465868
}

0 commit comments

Comments
 (0)