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

Windows: support long paths when calling WinAPI functions #2181

Closed
laszlocsomor opened this issue Dec 5, 2016 · 11 comments
Closed

Windows: support long paths when calling WinAPI functions #2181

laszlocsomor opened this issue Dec 5, 2016 · 11 comments
Assignees
Labels
P2 We'll consider working on this in future. (Assignee optional) platform: windows type: feature request
Milestone

Comments

@laszlocsomor
Copy link
Contributor

Description of the problem / feature request / question:

We're hitting path length limitations because we use the ASCII version of Win32 functions and paths are limited to 260 characters (MAX_PATH).

See for example #2178 (comment).

We have to use the widechar versions and prefix all long paths with \\?\.
Caveats: such prefixed paths may not use / as the path separator, only \. Using / only works with the ASCII versions.

If possible, provide a minimal example to reproduce the problem:

$ git checkout c9e363c674e4a9cd2422e80da2636f6620798fcb
$ bazel test //src/test/shell/bazel:bazel_windows_example_test

Environment info

  • Operating System: Windows (every version)

  • If bazel info release returns "development version" or "(@non-git)", please tell us what source tree you compiled Bazel from; git commit hash is appreciated (git rev-parse HEAD):

$ git rev-parse HEAD
c9e363c674e4a9cd2422e80da2636f6620798fcb

Have you found anything relevant by searching the web? (e.g. GitHub issues, email threads in the bazel-discuss@googlegroups.com archive)

Naupe.

Anything else, information or logs or outputs that would be helpful?

(If they are large, please upload as attachment or provide link).

@laszlocsomor laszlocsomor added platform: windows P2 We'll consider working on this in future. (Assignee optional) type: feature request labels Dec 5, 2016
@laszlocsomor laszlocsomor self-assigned this Dec 5, 2016
@laszlocsomor laszlocsomor added P1 I'll work on this now. (Assignee required) and removed P2 We'll consider working on this in future. (Assignee optional) labels Dec 5, 2016
@laszlocsomor
Copy link
Contributor Author

Bumping to P1 because this makes our CI flaky / failing.

bazel-io pushed a commit that referenced this issue Dec 5, 2016
This way it's easier to find the functions we need
to change to widechar version.

See #2181

--
PiperOrigin-RevId: 141043389
MOS_MIGRATED_REVID=141043389
@laszlocsomor laszlocsomor added P2 We'll consider working on this in future. (Assignee optional) and removed P1 I'll work on this now. (Assignee required) labels Dec 6, 2016
@dslomov dslomov added this to the 0.5 milestone Dec 6, 2016
bazel-io pushed a commit that referenced this issue Dec 6, 2016
This change:
- renames windows_error_handling.* to
windows_util.*
- moves most stuff except for the JNI method
implementations into the new windows_util
namespace
- implements a jstring to wchar string converter
- uses GetFileAttributesW in
windows_file_operations.cc

See #2181

--
PiperOrigin-RevId: 141187291
MOS_MIGRATED_REVID=141187291
bazel-io pushed a commit that referenced this issue Dec 14, 2016
Implement conversion methods between char strings
and wchar_t strings. This is necessary to use
widechar Windows functions.

See #2181

--
PiperOrigin-RevId: 142009930
MOS_MIGRATED_REVID=142009930
bazel-io pushed a commit that referenced this issue Dec 15, 2016
In this change:
- implement PrintErrorW (copy of PrintError but
uses FormatMessageW)
- use GetModuleFileNameW in GetSelfPath and do not
worry about UNC paths, we'll automatically handle
them in the future when we use widechar
functions everywhere. Until then, if a path
happens to have the "\\?\" prefix and that causes
an error, we won't be worse off than today; today
we just call `pdie`
- use GetTempPathW in GetOutputRoot; in theory
this might also return an UNC path but again that
shouldn't be a problem
- add comments for the arguments of CreateFileA
and CreateProcessA calls, to make the code a bit
more readable

See #2181

--
PiperOrigin-RevId: 142118854
MOS_MIGRATED_REVID=142118854
bazel-io pushed a commit that referenced this issue Dec 15, 2016
Move GetJvmVersion from blaze_util to
blaze_util_platform, and remove the RunProgram
declaration from blaze_util.h.

Since GetJvmVersion is the only user of RunProgram
this is safe to do, and allows making RunProgram
static as well as simplifying its implementation
on Windows, while also changing it to use
CreateProcessW instead of CreateProcessA.

See #2181

--
PiperOrigin-RevId: 142122045
MOS_MIGRATED_REVID=142122045
bazel-io pushed a commit that referenced this issue Dec 21, 2016
This method can normalize paths with "." and ".."
and multiple "/" characters.

E.g. normalize("../foo/./bar/../baz") = "foo/baz"

This method enables us implementing PathExists on
Windows. If the path to check is too long, we need
to prefix it with "\\?\" for the Windows API
functions to work, but then the path must be
fully normalized and in Windows format. We already
have functions to convert a path to Windows format
but that doesn't normalize; with this function we
can finally convert paths like "/c/foo/../bar" to
L"\\?\c:\foo" and check if it exists.

See #2107
See #2181

--
PiperOrigin-RevId: 142648194
MOS_MIGRATED_REVID=142648194
@petemounce
Copy link
Contributor

Anything I can do to assist with this? It's one of the things that blocks our Windows adoption.

@laszlocsomor
Copy link
Contributor Author

Thanks, I think we're close to fixing this. I'm about to update the CreateFileA calls to CreateFileW in blaze_util_windows, then update the CreateProcessA ones to W, and that should do it.

bazel-io pushed a commit that referenced this issue Jan 5, 2017
Checking if a path exists is surprisingly hard on
Windows. The most convenient API functions are
PathFileExists and GetFileAttributes but neither
of them follows junctions. To check if a junction
is dangling, we have to resolve it all the way.

This change adds a JunctionResolver class to
file_windows, which can resolve junctions (if they
aren't dangling) and non-junctions (in this case
just checks their existence).

See #2107
See #2181

--
PiperOrigin-RevId: 143645274
MOS_MIGRATED_REVID=143645274
bazel-io pushed a commit that referenced this issue Jan 5, 2017
See #2107
See #2181

--
PiperOrigin-RevId: 143655738
MOS_MIGRATED_REVID=143655738
@laszlocsomor
Copy link
Contributor Author

laszlocsomor commented Jan 5, 2017

TIL: CreateProcessW doesn't support long paths, not even with "\\?\".

(..a moment of silence..)

Luckily, there's GetShortPathName so we can shorten those long paths and CreateProcess can spawn a process with them.

@laszlocsomor
Copy link
Contributor Author

@petemounce
Copy link
Contributor

Happy New Year...?

bazel-io pushed a commit that referenced this issue Jan 9, 2017
See #2107
See #2181

--
PiperOrigin-RevId: 143947877
MOS_MIGRATED_REVID=143947877
bazel-io pushed a commit that referenced this issue Jan 10, 2017
Because CreateProcessW doesn't support long paths,
not even with the "\\?\" prefix [1], we need to
convert long paths to short ones to spawn
processes. This change implements the
corresponding function and uses it in
blaze_util_windows.

[1] #2181 (comment)

See #2107
See #2181

--
PiperOrigin-RevId: 144062404
MOS_MIGRATED_REVID=144062404
bazel-io pushed a commit that referenced this issue Jan 12, 2017
Add a separate argument to nativeCreateProcess for
argv[0] specifically, and another for the rest of
the args.

In a subsequent change I'll add code to compute
the 8dot3 style short name of the argv[0] so we
can use longer paths for executables in
CreateProcessA than we normally could. This is the
same approach as used in commit 44ecf9a

See #2107
See #2181

--
PiperOrigin-RevId: 144311562
MOS_MIGRATED_REVID=144311562
bazel-io pushed a commit that referenced this issue Jan 12, 2017
Use CreateFileW to create the output files for
redirected stdout/stderr in nativeCreateProcess.
This way we can support long paths for these
files.

See #2107
See #2181

--
PiperOrigin-RevId: 144311729
MOS_MIGRATED_REVID=144311729
bazel-io pushed a commit that referenced this issue Jan 16, 2017
When spawning a new process with CreateProcessA,
convert argv0 to a 8dot3 style short path so we
can support longer paths than MAX_PATH. This is
the same approach we did in commit 44ecf9a.

See #2107
See #2181

--
PiperOrigin-RevId: 144613589
MOS_MIGRATED_REVID=144613589
bazel-io pushed a commit that referenced this issue Jan 18, 2017
Use CreateFileW in blaze_util_windows.cc when
opening the "jvm.out" file. This allows supporting
long paths.

Also use AsWindowsPathWithUncPrefix instead of
just AsWindowsPath plus manually adding the UNC
prefix.

Also fix a compilation error in
file_windows_test.cc, I'm surprised the CI system
didn't catch this, maybe we aren't running this
test there.

See #2107
See #2181

--
PiperOrigin-RevId: 144813245
MOS_MIGRATED_REVID=144813245
bazel-io pushed a commit that referenced this issue Jan 18, 2017
Add tests for the AsExecutablePathForCreateProcess
method, since its logic is pretty complex.

Unfortunately testing it also requires complex
logic, as we need to test what exactly happens
when the input path is shorter than MAX_PATH or
when it's longer than it. To test that reliably,
we need a base path that we know will not get
shortened. Creating that base path under the temp
directory is a nightmare, we need to:
(1) retrieve the temp dir, shorten it so we know
that it won't be shortened further
(2) keep creating subdirectories that have a short
name so they also won't get shortened, but keep
the entire path below MAX_PATH while leaving
enough space for a file name in the end
(3) append a file name such that the path is just
below MAX_PATH, or is exactly that long, or is
longer than it. Because of steps (1) and (2) we
can be sure that no other component in the path
will get shortened, so we can test exactly what's
going on with the shortener logic and its error
handling. But oh boy is it complicated.

Side note, we need to use the Widechar WinAPI
functions to create/delete the directories and
files, because the POSIX API on Windows appears to
be backed by the ASCII API functions, so
attempting to `mkdir` with a path longer than
CreateDirectoryA's limit is going to fail.

But on the positive side, adding tests caught two
bugs in the method, so we have that going for us
which is nice.

See #2107
See #2181

--
PiperOrigin-RevId: 144823029
MOS_MIGRATED_REVID=144823029
bazel-io pushed a commit that referenced this issue Jan 20, 2017
Implement ForEachDirectoryEntry on Windows using
FindFirstFileW / FindNextFileW.

Supports long paths and traversing junctions.

See #2107
See #2181

--
PiperOrigin-RevId: 145062749
MOS_MIGRATED_REVID=145062749
bazel-io pushed a commit that referenced this issue Jan 23, 2017
*** Reason for rollback ***

Breaks Bazel-Install-Trigger on CI.

*** Original change description ***

Bazel client, Windows: impl. ForEachDirectoryEntry

Implement ForEachDirectoryEntry on Windows using
FindFirstFileW / FindNextFileW.

Supports long paths and traversing junctions.

See #2107
See #2181

--
PiperOrigin-RevId: 145282158
MOS_MIGRATED_REVID=145282158
@laszlocsomor
Copy link
Contributor Author

I have a change that fixes about 5 long path related bugs. Now I can build bazel with a long output_user_root. I'll break it down and start sending out.

bazel-io pushed a commit that referenced this issue Mar 6, 2017
The hard limit for SetCurrentDirectory{A,W} is
MAX_PATH-1, even with UNC prefix, therefore a
process' cwd may also not be longer than that.

See #2107
See #2406
See #2181

--
PiperOrigin-RevId: 149290147
MOS_MIGRATED_REVID=149290147
bazel-io pushed a commit that referenced this issue Mar 7, 2017
Fix blaze_util_windows.ConvertPath: in the MSVC
version this is using the actual %PATH% value, we
don't need to convert it.

Fix blaze_util_windows.PathAsJvmFlag: shorten the
path so we can pass it to the JVM process (long
paths aren't understood by the shell), but also
converrt backslashes to forward slashes so the JVM
won't believe we are passing paths with escaped
characters.

See #2107
See #2181

--
PiperOrigin-RevId: 149396971
MOS_MIGRATED_REVID=149396971
@dslomov
Copy link
Contributor

dslomov commented Apr 4, 2017

Is there still work to do here?

@laszlocsomor
Copy link
Contributor Author

Not that I'm aware of. It's my great pleasure to hit "Close and comment".

@petemounce
Copy link
Contributor

We're going to eagerly try this out this week as it's one of the things that blocks our use of Bazel on Windows. Thanks and we'll keep you posted!

@laszlocsomor
Copy link
Contributor Author

We're also going to start dogfooding the MSYS-less version with 0.5.0, stay tuned!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
P2 We'll consider working on this in future. (Assignee optional) platform: windows type: feature request
Projects
None yet
Development

No branches or pull requests

3 participants