Skip to content

Commit 842fca0

Browse files
committed
includes_normalizes: Full fix for long path support.
Use GetFullPathNameW() instead of GetFullPathNameA() to ensure that normalization works for all long paths on Windows. Adjust unit tests accordingly. Fixes #2442
1 parent 4c8cab9 commit 842fca0

File tree

2 files changed

+32
-20
lines changed

2 files changed

+32
-20
lines changed

src/includes_normalize-win32.cc

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,24 +34,35 @@ bool InternalGetFullPathName(const StringPiece& file_name, std::string* path,
3434
// "The filename or extension is too long" even if long path supported is
3535
// enabled. GetFullPathNameW() must be used for this function to work!
3636
path->clear();
37+
// Convert to wide filename first.
3738
std::string filename_str = file_name.AsString();
38-
DWORD full_size = GetFullPathNameA(filename_str.c_str(), 0, NULL, NULL);
39-
if (full_size == 0) {
40-
*err = "GetFullPathNameA(" + filename_str + "): " + GetLastErrorString();
39+
std::wstring wide_filename;
40+
if (!ConvertUTF8ToWin32Unicode(filename_str, &wide_filename, err))
41+
return false;
42+
43+
// Call GetFullPathNameW()
44+
DWORD wide_full_size = GetFullPathNameW(wide_filename.c_str(), 0, NULL, NULL);
45+
if (wide_full_size == 0) {
46+
*err = "GetFullPathNameW(" +
47+
std::string(wide_filename.begin(), wide_filename.end()) +
48+
"): " + GetLastErrorString();
4149
return false;
4250
}
4351

44-
// NOTE: full_size includes the null-terminating character.
45-
path->resize(static_cast<size_t>(full_size - 1));
46-
DWORD result2 = GetFullPathNameA(filename_str.c_str(), full_size,
47-
const_cast<char*>(path->data()), NULL);
48-
if (result2 == 0) {
49-
*err = "GetFullPathNameA(" + filename_str + "): " + GetLastErrorString();
52+
// NOTE: wide_full_size includes the null-terminating character.
53+
std::wstring wide_path;
54+
wide_path.resize(static_cast<size_t>(wide_full_size - 1));
55+
DWORD wide_full_size2 =
56+
GetFullPathNameW(wide_filename.c_str(), wide_full_size,
57+
const_cast<wchar_t*>(wide_path.data()), NULL);
58+
if (wide_full_size2 == 0) {
59+
*err = "GetFullPathNameW(" + filename_str + "): " + GetLastErrorString();
60+
path->clear();
5061
return false;
5162
}
5263

53-
path->resize(static_cast<size_t>(result2));
54-
return true;
64+
// Convert wide_path to Unicode.
65+
return ConvertWin32UnicodeToUTF8(wide_path, path, err);
5566
}
5667

5768
// Get the drive prefix of a given filename. On success set |*drive| then return

src/includes_normalize_test.cc

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -144,26 +144,27 @@ TEST(IncludesNormalize, LongInvalidPath) {
144144
NormalizeAndCheckNoError(kExactlyMaxPath));
145145
}
146146

147-
TEST(IncludesNormalize, ShortRelativeButTooLongAbsolutePath) {
147+
TEST(IncludesNormalize, ShortRelativeButLongAbsolutePath) {
148148
std::string result, err;
149149
IncludesNormalize normalizer(".");
150150
// A short path should work
151151
EXPECT_TRUE(normalizer.Normalize("a", &result, &err));
152152
EXPECT_EQ("", err);
153153

154+
// Make sure a path that's exactly _MAX_PATH long fails does not fail.
154155
// Construct max size path having cwd prefix.
155156
// kExactlyMaxPath = "aaaa\\aaaa...aaaa\0";
156-
char kExactlyMaxPath[_MAX_PATH + 1];
157+
std::string exactly_max_path;
157158
for (int i = 0; i < _MAX_PATH; ++i) {
158159
if (i < _MAX_PATH - 1 && i % 10 == 4)
159-
kExactlyMaxPath[i] = '\\';
160+
exactly_max_path.push_back('\\');
160161
else
161-
kExactlyMaxPath[i] = 'a';
162+
exactly_max_path.push_back('a');
162163
}
163-
kExactlyMaxPath[_MAX_PATH] = '\0';
164-
EXPECT_EQ(strlen(kExactlyMaxPath), static_cast<size_t>(_MAX_PATH));
164+
EXPECT_EQ(exactly_max_path.size(), static_cast<size_t>(_MAX_PATH));
165+
EXPECT_TRUE(normalizer.Normalize(exactly_max_path, &result, &err)) << err;
165166

166-
// Make sure a path that's exactly _MAX_PATH long fails with a proper error.
167-
EXPECT_FALSE(normalizer.Normalize(kExactlyMaxPath, &result, &err)) << err;
168-
EXPECT_NE(err.find("GetFullPathNameA"), std::string::npos);
167+
// Make sue a path of _MAX_PATH + 1 characters also works.
168+
std::string more_than_max_path = exactly_max_path + "\\a";
169+
EXPECT_TRUE(normalizer.Normalize(more_than_max_path, &result, &err)) << err;
169170
}

0 commit comments

Comments
 (0)