Skip to content

Conversation

StefanScherer
Copy link

Checklist
  • make -j8 test (UNIX), or vcbuild test nosign (Windows) passes
  • tests and/or benchmarks are included
  • commit message follows commit guidelines
Affected core subsystem(s)

fs

Description of change

This is my proposal fixing the problem #8897 using fs.realpathSync() on Docker shared volumes in Windows containers. The additional check if a symlink target is \ContainerMappedDirectories than just ignore that link.

Without that change:

PS C:\test> docker run -v "$(pwd):C:\test" nodeexe:4.6.1 node -p "const fs=require('fs'); fs.realpathSync('c:/test')"
fs.js:839
  return binding.lstat(pathModule._makeLong(path));
                 ^

Error: ENOENT: no such file or directory, lstat 'c:\ContainerMappedDirectories'

After applying the change:

PS C:\test> docker run -v "$(pwd):C:\test" nodecheck node -p "const fs=require('fs'); fs.realpathSync('c:/test')"
c:\test

Any ideas how to add a test for this as it only occurs in Windows containers?

Signed-off-by: Stefan Scherer <scherer_stefan@icloud.com>
@nodejs-github-bot nodejs-github-bot added the fs Issues and PRs related to the fs subsystem / file system. label Nov 5, 2016
if (linkTarget === null) {
fs.statSync(base);
linkTarget = fs.readlinkSync(base);
if (linkTarget.startsWith('\\ContainerMappedDirectories')) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems a bit hacky. Wouldn't this prevent any application from using \ContainerMappedDirectories? I imagine an isWindows check should be added too.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cjihrig is being too kind. This is not an acceptable solution.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took the effort and had a look where things go wrong. I don't have expected that this PR gets merged in. It's just tried to get started understand what Node.js is doing here and start a discussion and giving a first idea to find a fix for Node.js in Windows containers.

The idea is to just stop the symlink expansion at the container volume shares.

I believe the work should be done by some Node.js core maintainer with more knowledge of the code base. I just forked the node repo for the first time, tried to compile the sources and find a fix and not just filing issues.

As a user of Node.js I just wonder why Node.js does so many symlink expansion for such simple tasks like node.exe c:\mount\hello.js and not finding the source code. I just wonder why this is all needed ( just thinking "code that does not exist could not have bugs" ). I'm sure there is a good reason why this is done in node.exe and npm at a lot of places, but it seems that it complicate things at some point.

@addaleax addaleax added the windows Issues and PRs related to the Windows platform. label Nov 5, 2016
@silverwind
Copy link
Contributor

Maybe a more general approach to paths starting with \\?\ could be applied? Note that this kind of fix usually should go into libuv.

@StefanScherer
Copy link
Author

Yes, I also thought of libuv. Just reading through the source code of libuv, planning to build a Dockerfile to have a Windows build and test environment for it.

@jasnell
Copy link
Member

jasnell commented Mar 24, 2017

Any status updates on this?

@jasnell jasnell added the stalled Issues and PRs that are stalled. label Mar 24, 2017
@StefanScherer
Copy link
Author

@jasnell Well, over the last couple of months I learned that other languages have similar problems retrieving the real path of a file in such volume mount points.

See moby/moby#27537 for a discussion.
I'm using a workaround StefanScherer/dockerfiles-windows@9fd8ce4 to map the volume to a drive letter in the container. Node.js stops retrieving the real path at this drive letter and it works. But this depends on you application if you have other files and folders that should be mapped to volume. So maybe you have to use multiple drives, one for each volume mount point.

  • This is a workaround to run Node.js sources mounted from the host
# escape=`
FROM stefanscherer/node-windows:7.7.4-nano
RUN npm install -g nodemon
VOLUME C:\code
RUN set-itemproperty -path `
    'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\DOS Devices' `
    -Name 'G:' -Value '\??\C:\code' -Type String
WORKDIR G:\
CMD ["nodemon.cmd", "--debug=5858", "app.js"]
  • The Node.js app is running in G:\, you still use C:\code for your volume.

@StefanScherer
Copy link
Author

Closing this PR

@joshgav
Copy link
Contributor

joshgav commented Mar 28, 2017

cc @digitalinfinity @nodejs/platform-windows

@StefanScherer
Copy link
Author

As of moby/moby#27537 (comment) it seems we have two options. Live with the drive mapping workaround or make node/libuv aware of these ContainerMappedDirectories links.

@StefanScherer
Copy link
Author

A interesting comment in Golang's fix for a similar issue inside a Windows container: https://go-review.googlesource.com/c/41834/

Currently windows Stat uses combination of Lstat and Readlink to
walk symlinks until it reaches file or directory. Windows Readlink
is implemented via Windows DeviceIoControl(FSCTL_GET_REPARSE_POINT, ...)
call, but that call does not work on network shares or inside of
Docker container (see issues #18555 ad #19922 for details).

But Raymond Chen suggests different approach:
https://blogs.msdn.microsoft.com/oldnewthing/20100212-00/?p=14963/

  • he suggests to use Windows I/O manager to dereferences the
    symbolic link.

This appears to work for all normal symlinks, but also for network
shares and inside of Docker container.

@refack
Copy link
Contributor

refack commented Apr 30, 2017

@StefanScherer that interesting info. Since node uses libuv for platform abstraction, this should go there. I'll start a PR for it.
Ref: https://github.com/libuv/libuv/blob/v1.x/src/win/fs.c#L1173

@StefanScherer
Copy link
Author

@refack Do you have any updates on libuv? In the meantime I had the chance to try out the first Windows Server Insider build and it seems that Node.js is running fine in a mounted folder (there's no C:\ContainerMappedDirectories symlink). And nanoserver base image got much smaller, so a Node.js image on Windows is 89 ... 92 MB ( https://hub.docker.com/r/stefanscherer/node-windows/tags/ ).

@StefanScherer StefanScherer deleted the dont-resolve-container-mapped-dirs branch July 14, 2017 06:50
@refack
Copy link
Contributor

refack commented Jul 14, 2017

@refack Do you have any updates on libuv? In the meantime I had the chance to try out the first Windows Server Insider build and it seems that Node.js is running fine in a mounted folder (there's no C:\ContainerMappedDirectories symlink). And nanoserver base image got much smaller, so a Node.js image on Windows is 89 ... 92 MB ( https://hub.docker.com/r/stefanscherer/node-windows/tags/ ).

@StefanScherer I've been playing with the "using IO manager" trick a bit locally... It has allot of other advantages (huge maximal path length, UNC compatible, Direct device access) but it needs some massaging and I need more than 24h in a day...

@refack
Copy link
Contributor

refack commented Jul 14, 2017

I had the chance to try out the first Windows Server Insider build

Windows Server on the outside or the inside (i.e. host OS or contained OS)?

@StefanScherer
Copy link
Author

There isa new ISO file to build a VM with the Windows Server 2016 Insider preview. I'm using this Vagrant environment https://github.com/StefanScherer/insider-docker-machine for these tests.

@StefanScherer
Copy link
Author

@refack The symlink ContainerMappedDirectories is still there in HyperV containers. This means in all Windows containers running on a Windows 10 machine where the typical developer wants to run code and live debug code in a container. I just verified it with docker run -v c:\code:c:\code --isolation=hyperv -it stefanscherer/node-windows:8.1.4-insider cmd on a Windows Server 2016 Core with the Insider build. The more lightweight containers (without the --isolation=hyperv) have a real mapped folder c:\code where node.exe finds all js files in it.
Maybe this will improve in the future as well, but I don't have any more details yet.

@refack
Copy link
Contributor

refack commented Jul 18, 2017

@refack The symlink ContainerMappedDirectories is still there in HyperV containers. This means in all Windows containers running on a Windows 10 machine where the typical developer wants to run code and live debug code in a container. I just verified it with docker run -v c:\code:c:\code --isolation=hyperv -it stefanscherer/node-windows:8.1.4-insider cmd on a Windows Server 2016 Core with the Insider build. The more lightweight containers (without the --isolation=hyperv) have a real mapped folder c:\code where node.exe finds all js files in it.
Maybe this will improve in the future as well, but I don't have any more details yet.

So there's a slight complication with using the "Windows I/O manager". AFAICT it requires all paths to use the "proper" syntax that includes only backslashes, but currently libuv supports mixed forward slash paths on Windows... I have to make sure all code paths normalize the path before doing the system call...

@dunnock
Copy link

dunnock commented Jul 26, 2017

Just got into this issue with official https://hub.docker.com/r/microsoft/windowsservercore/ container. Unfortunately kubernetes cannot mount emptyDir into driver letter, therefore I end up setting my temp dir to C:\Users\ContainerAdministrator\Downloads within container. In the case of kubernetes Docker container has to be built before disk is mounted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fs Issues and PRs related to the fs subsystem / file system. stalled Issues and PRs that are stalled. windows Issues and PRs related to the Windows platform.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants