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

Problematic use of EvalSymlinks on Windows #2648

Open
ericwj opened this issue Jul 23, 2020 · 3 comments
Open

Problematic use of EvalSymlinks on Windows #2648

ericwj opened this issue Jul 23, 2020 · 3 comments
Assignees

Comments

@ericwj
Copy link

ericwj commented Jul 23, 2020

Docker is using go's path/filepath.EvalSymlinks by default and remembers resolved symlink paths instead of the original path provided through the CLI by the user.

On Windows, this is a severe problem which can lead to data loss, bypassing security and simply fails on current versions of go in all but the most trivial system configurations. See /golang/go/issues/40180 and the list of errors created by testing this API.

The most important use of EvalSymlinks is through the use of the API's listed below - I have only searched through /docker/cli, there may be other uses of either EvalSymlinks directory or indirectly in this repo or others:

The comment in ResolveAndValidateContextPath reads:

The context dir might be a symbolic link, so follow it to the actual target directory.

There is not one good reason for this on Windows. The whole code section can simply be deleted and nothing will start failing - on the contrary, a long list of issues and potential issues will be fixed.

The more difficult issue to fix is that, also on Linux, the configured path is replaced by the path returned by EvalSymlinks, which is a bad practice. Docker should not preempt operational administrative actions from having effects by remembering only the resolved path, forgetting where it was configured to run from, or by caching the result of EvalSymlinks for any length of time.

For Users: How to use Docker today, avoiding problems with EvalSymlinks

The very best thing to do is to give Docker only paths on volumes that are rooted at a drive letter, such as C:\ or Q:\. Obviously this severely drastically limits where Docker can run and gives no options to (re)configure the storage system if the need arises.

isUNC is linked above, not quite for being correct at all, but it checks that paths do not start with \\ - wrongfully conluding it must be a UNC path of the form \\server\share.

But any (absolute) path on Windows can be written to start with \\, hence it is possible to at least have Docker avoid the use of EvalSymlinks by changing the path given to the docker cli to include \\:

  • \\?\C:\ContextDir
  • \\?\Volume{00000000-0000-0000-0000-000000000000}\ContextDir
  • \\?\UNC\server\share\ContextDir

Note that this might or might not introduce other issues with go's very poorly ported path/filepath on Windows which is full of problems very much like the ones hinted at by isUnc.

Also, this does not fix issues with Docker when Docker is invoked by other tools such as Visual Studio that aren't so easy to configure to use any \\ prefix consistently - and shouldn't have to be.

For more information see File path formats on Windows systems | Microsoft Docs, amongst others. The prefix \\.\ is also described. More technical information will also reveal \??\ is sometimes valid.

Issues with Docker on Windows suspected to be caused by this issue

These are some of the existing issues with the use of this API - some are at least suspected to be caused by the use of EvalSymlinks in Docker:

There are no issues that indicate any of the additional problems with the use of EvalSymlinks on Windows. Docker simply does not work unless the path it is given is to a volume that is mounted with a drive letter, unless the path starts with \\:

  • Using Docker using a UNC path that contains links or junctions on the local or a remote server.
  • Using Docker on a drive letter obtained by mapping a UNC local or remote share.
  • Volumes that have an access path such as C:\FileServer\Share\BigFolders\Docker in addition to \\?\Volume{00000000-0000-0000-0000-000000000000}\
  • Using Docker through a directory junction that points to a volume root or UNC path \\?\UNC\....
  • Using Docker through a hard link that points to a volume root or UNC path \\?\UNC\...

These situations may be desired by administrators using Docker on storage spaces VirtualDisk or Hyper-V VHD virtual disks as well as on systems with more than one physical partition if they use GPT partitioning such as all 64-bit Windows editions.

ericwj added a commit to ericwj/cli that referenced this issue Jul 23, 2020
ericwj added a commit to ericwj/cli that referenced this issue Jul 23, 2020
Signed-off-by: Eric Jonker <ericnl@live.com>
ericwj added a commit to ericwj/cli that referenced this issue Jul 23, 2020
Signed-off-by: Eric <ericwj@users.noreply.github.com>
@mbs-c
Copy link

mbs-c commented Feb 24, 2025

Since the behavior of EvalSymlinks was changed in go 1.23, I hoped docker v28 would finally allow me to finally redirect the data-root to a dev drive mounted in C:\DevDrive. However, the previous error message:

Unable to get the full path to root (C:\DevDrive\docker): failed to canonicalise path for C:\DevDrive\docker: EvalSymlinks: too many links []

simply turned into (first attempt):

failed to start daemon: Unable to get the full path to the TempDir (C:\DevDrive\docker\tmp): failed to canonicalise path for C:\DevDrive\docker\tmp: The system cannot find the path specified.

which is strange, since it actually successfully created C:\DevDrive\docker\tmp.

On subsequent attempts, the error morphs into:

Unable to get the full path to root (C:\DevDrive\docker): failed to canonicalise path for C:\DevDrive\docker: The system cannot find the path specified. []
Unable to get the full path to root (C:\DevDrive\docker): failed to canonicalise path for C:\DevDrive\docker: The system cannot find the path specified.

It looks like the workaround from golangci/golangci-lint#5245 might help here as well.

@thaJeztah
Copy link
Member

@mbs-c that error is produced by the docker daemon, not the CLI; if you suspect there's a bug, it's better to open a ticket in https://github.com/moby/moby/issues with steps to reproduce.

Does this setup work if you specify the actual location of the storage directory you want to use in the daemon's configuration ("data-root" property in daemon.json or --data-root flag) instead of a symlinked path?

@mbs-c
Copy link

mbs-c commented Feb 26, 2025

@thaJeztah Sorry, I thought it would be relevant to mention it here, since this looked like a meta-issue for the root cause. I can file an additional bug report in the correct repository, if desired.

Does this setup work if you specify the actual location of the storage directory you want to use in the daemon's configuration ("data-root" property in daemon.json or --data-root flag) instead of a symlinked path?

First of all, let me clarify that there is no symlink in the path, only a mount point. As I understand it, this is now (as of go 1.23 / docker v28) handled "correctly" by the go standard library in that it's no longer treated as a symbolic link. However, accessing paths containing mount points can still lead to ENOTDIR, as described in the PR I linked above. In the PR, a wrapper function for filepath.EvalSymlinks was introduced, which works around this API issue.

Since my dev drive does not have a drive letter, there is no "actual location" I can specify other than the mount point C:\DevDrive (see above) or the volume path \\?\Volume{<guid>}, which leads to a different error from the daemon:

failed to start daemon: error initializing graphdriver: the path provided to GetFileSystemType must start with a drive letter: windowsfilter

So there's currently no way for me to specify the "data-root" property in any way that docker will accept.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants