Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update threshold for long path shortening to be MAX_PATH - 4 #12941

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions src/main/native/windows/util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ static bool Contains(const wstring& s, const WCHAR* substr) {
}

wstring AsShortPath(wstring path, wstring* result) {
// Using MAX_PATH - 4 (256) instead of MAX_PATH to fix https://github.com/bazelbuild/bazel/issues/12310
static const size_t kMaxPath = MAX_PATH - 4;

if (path.empty()) {
result->clear();
return L"";
Expand All @@ -212,7 +215,7 @@ wstring AsShortPath(wstring path, wstring* result) {
return MakeErrorMessage(WSTR(__FILE__), __LINE__, L"AsShortPath", path,
L"path is not normalized");
}
if (path.size() >= MAX_PATH && !HasSeparator(path)) {
if (path.size() >= kMaxPath && !HasSeparator(path)) {
return MakeErrorMessage(WSTR(__FILE__), __LINE__, L"AsShortPath", path,
L"path is just a file name but too long");
}
Expand All @@ -221,15 +224,15 @@ wstring AsShortPath(wstring path, wstring* result) {
L"path is not absolute");
}
// At this point we know the path is either just a file name (shorter than
// MAX_PATH), or an absolute, normalized, Windows-style path (of any length).
// kMaxPath), or an absolute, normalized, Windows-style path (of any length).

std::replace(path.begin(), path.end(), '/', '\\');
// Fast-track: the path is already short.
if (path.size() < MAX_PATH) {
if (path.size() < kMaxPath) {
*result = path;
return L"";
}
// At this point we know that the path is at least MAX_PATH long and that it's
// At this point we know that the path is at least kMaxPath long and that it's
// absolute, normalized, and Windows-style.

wstring wlong = wstring(L"\\\\?\\") + path;
Expand All @@ -239,10 +242,10 @@ wstring AsShortPath(wstring path, wstring* result) {
// did too (though this behavior is not documented on MSDN)
// - CreateProcess{A,W} only accept an executable of MAX_PATH - 1 length
// Therefore for our purposes the acceptable shortened length is
// MAX_PATH + 4 (null-terminated). That is, MAX_PATH - 1 for the shortened
// kMaxPath + 4 (null-terminated). That is, kMaxPath - 1 for the shortened
// path, plus a potential "\\?\" prefix that's only there if `wlong` also had
// it and which we'll omit from `result`, plus a null terminator.
static const size_t kMaxShortPath = MAX_PATH + 4;
static const size_t kMaxShortPath = kMaxPath + 4;

WCHAR wshort[kMaxShortPath];
DWORD wshort_size = ::GetShortPathNameW(wlong.c_str(), NULL, 0);
Expand Down
36 changes: 19 additions & 17 deletions src/test/native/windows/util_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ using std::unique_ptr;
using std::wstring;

static const wstring kUncPrefix = wstring(L"\\\\?\\");
// Using MAX_PATH - 4 instead of MAX_PATH to fix https://github.com/bazelbuild/bazel/issues/12310
static const size_t kMaxPath = MAX_PATH - 4;

// Retrieves TEST_TMPDIR as a shortened path. Result won't have a "\\?\" prefix.
static void GetShortTempDir(wstring* result) {
Expand Down Expand Up @@ -66,11 +68,11 @@ static void GetShortTempDir(wstring* result) {
::GetShortPathNameW(tmpdir.c_str(), buf.get(), size);

// Set the result, omit the "\\?\" prefix.
// Ensure that the result is shorter than MAX_PATH and also has room for a
// Ensure that the result is shorter than kMaxPath and also has room for a
// backslash (1 wchar) and a single-letter executable name with .bat
// extension (5 wchars).
*result = wstring(buf.get() + 4);
ASSERT_LT(result->size(), MAX_PATH - 6);
ASSERT_LT(result->size(), kMaxPath - 6);
}

// If `success` is true, returns an empty string, otherwise an error message.
Expand Down Expand Up @@ -165,14 +167,14 @@ static wstring DeleteDir(wstring path) {
// `result_path` will be also a short path under `basedir`.
//
// Every directory in `result_path` will be created. The entire length of this
// path will be exactly MAX_PATH - 7 (not including null-terminator).
// path will be exactly kMaxPath - 7 (not including null-terminator).
// Just by appending a file name segment between 6 and 8 characters long (i.e.
// "\a.bat", "\ab.bat", or "\abc.bat") the caller can obtain a path that is
// MAX_PATH - 1 long, or MAX_PATH long, or MAX_PATH + 1 long, respectively,
// kMaxPath - 1 long, or kMaxPath long, or kMaxPath + 1 long, respectively,
// and cannot be shortened further.
static void CreateShortDirsUnder(wstring basedir, wstring* result_path) {
ASSERT_LT(basedir.size(), MAX_PATH);
size_t remaining_len = MAX_PATH - 1 - basedir.size();
ASSERT_LT(basedir.size(), kMaxPath);
size_t remaining_len = kMaxPath - 1 - basedir.size();
ASSERT_GE(remaining_len, 6); // has room for suffix "\a.bat"

// If `remaining_len` is odd, make it even.
Expand All @@ -188,7 +190,7 @@ static void CreateShortDirsUnder(wstring basedir, wstring* result_path) {
basedir += wstring(L"\\a");
CREATE_DIR(basedir);
}
ASSERT_EQ(basedir.size(), MAX_PATH - 1 - 6);
ASSERT_EQ(basedir.size(), kMaxPath - 1 - 6);
*result_path = basedir;
}

Expand Down Expand Up @@ -260,7 +262,7 @@ TEST(WindowsUtilTest, TestAsExecutablePathForCreateProcessBadInputs) {
ASSERT_SHORTENING_FAILS(L"\\bar.exe", L"path is absolute");

wstring dummy = L"hello";
while (dummy.size() < MAX_PATH) {
while (dummy.size() < kMaxPath) {
dummy += dummy;
}
dummy += L".exe";
Expand All @@ -281,24 +283,24 @@ TEST(WindowsUtilTest, TestAsExecutablePathForCreateProcessConversions) {
CreateShortDirsUnder(tmpdir, &short_root);

// Assert that we have enough room to append a file name that is just short
// enough to fit into MAX_PATH - 1, or one that's just long enough to make
// the whole path MAX_PATH long or longer.
ASSERT_EQ(short_root.size(), MAX_PATH - 1 - 6);
// enough to fit into kMaxPath - 1, or one that's just long enough to make
// the whole path kMaxPath long or longer.
ASSERT_EQ(short_root.size(), kMaxPath - 1 - 6);

wstring actual;
wstring error;
for (size_t i = 0; i < 3; ++i) {
wstring wfilename = short_root;

APPEND_FILE_SEGMENT(6 + i, &wfilename);
ASSERT_EQ(wfilename.size(), MAX_PATH - 1 + i);
ASSERT_EQ(wfilename.size(), kMaxPath - 1 + i);

// When i=0 then `wfilename` is MAX_PATH - 1 long, so
// When i=0 then `wfilename` is kMaxPath - 1 long, so
// `AsExecutablePathForCreateProcess` will not attempt to shorten it, and
// so it also won't notice that the file doesn't exist. If however we pass
// a non-existent path to CreateProcessA, then it'll fail, so we'll find out
// about this error in production code.
// When i>0 then `wfilename` is at least MAX_PATH long, so
// When i>0 then `wfilename` is at least kMaxPath long, so
// `AsExecutablePathForCreateProcess` will attempt to shorten it, but
// because the file doesn't yet exist, the shortening attempt will fail.
if (i > 0) {
Expand Down Expand Up @@ -326,14 +328,14 @@ TEST(WindowsUtilTest, TestAsExecutablePathForCreateProcessConversions) {
// Finally construct a path that can and will be shortened. Just walk up a few
// levels in `short_root` and create a long file name that can be shortened.
wstring wshortenable_root = short_root;
while (wshortenable_root.size() > MAX_PATH - 1 - 13) {
while (wshortenable_root.size() > kMaxPath - 1 - 13) {
wshortenable_root =
wshortenable_root.substr(0, wshortenable_root.find_last_of(L'\\'));
}
wstring wshortenable = wshortenable_root + wstring(L"\\") +
wstring(MAX_PATH - wshortenable_root.size(), L'a') +
wstring(kMaxPath - wshortenable_root.size(), L'a') +
wstring(L".bat");
ASSERT_GT(wshortenable.size(), MAX_PATH);
ASSERT_GT(wshortenable.size(), kMaxPath);

// Attempt to shorten. It will fail because the file doesn't exist yet.
ASSERT_SHORTENING_FAILS(wshortenable, L"GetShortPathNameW");
Expand Down