From 800a7d513d3b80478a7996cb2c357b72f65e0b09 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Fri, 11 Mar 2016 01:06:53 +0000 Subject: [PATCH] Fix one-character directory issue in the volume option (#20122). The issue comes from the implementation of volumeSplitN() where a driver letter (`[a-zA-Z]:`) was assumed to follow either `:`, `/`, or `\\`. In Windows driver letter appears in two situations: a. `^[a-zA-Z]:` (A colon followed by `^[a-zA-Z]:` is OK as colon is the separator in volume option) b. A string in the format like `\\?\C:\Windows\...` (UNC). Therefore, a driver letter can only follow either a `:` or `\\` This PR removes the condition of `/` before the driver letter so that options like `-v /tmp/q:/foo` could be handled correctly. A couple of tests has also been added. This PR fixes #20122. Signed-off-by: Yong Tang --- integration-cli/docker_cli_run_test.go | 9 +++++++++ runconfig/opts/parse.go | 11 ++++++++--- runconfig/opts/parse_test.go | 3 +++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index 309912bb70..b7c776f912 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -4243,3 +4243,12 @@ func (s *DockerSuite) TestRunAttachFailedNoLeak(c *check.C) { // NGoroutines is not updated right away, so we need to wait before failing c.Assert(waitForGoroutines(nroutines), checker.IsNil) } + +// Test for one character directory name case (#20122) +func (s *DockerSuite) TestRunVolumeWithOneCharacter(c *check.C) { + testRequires(c, DaemonIsLinux) + + out, _ := dockerCmd(c, "run", "-v", "/tmp/q:/foo", "busybox", "sh", "-c", "find /foo") + fmt.Printf("OUTPUT: %+v", out) + c.Assert(strings.TrimSpace(out), checker.Equals, "/foo") +} diff --git a/runconfig/opts/parse.go b/runconfig/opts/parse.go index 79a1492a00..d6abcc956e 100644 --- a/runconfig/opts/parse.go +++ b/runconfig/opts/parse.go @@ -695,8 +695,12 @@ func validatePath(val string, validator func(string) bool) (string, error) { } // volumeSplitN splits raw into a maximum of n parts, separated by a separator colon. -// A separator colon is the last `:` character in the regex `[/:\\]?[a-zA-Z]:` (note `\\` is `\` escaped). -// This allows to correctly split strings such as `C:\foo:D:\:rw`. +// A separator colon is the last `:` character in the regex `[:\\]?[a-zA-Z]:` (note `\\` is `\` escaped). +// In Windows driver letter appears in two situations: +// a. `^[a-zA-Z]:` (A colon followed by `^[a-zA-Z]:` is OK as colon is the separator in volume option) +// b. A string in the format like `\\?\C:\Windows\...` (UNC). +// Therefore, a driver letter can only follow either a `:` or `\\` +// This allows to correctly split strings such as `C:\foo:D:\:rw` or `/tmp/q:/foo`. func volumeSplitN(raw string, n int) []string { var array []string if len(raw) == 0 || raw[0] == ':' { @@ -721,7 +725,8 @@ func volumeSplitN(raw string, n int) []string { if (potentialDriveLetter >= 'A' && potentialDriveLetter <= 'Z') || (potentialDriveLetter >= 'a' && potentialDriveLetter <= 'z') { if right > 1 { beforePotentialDriveLetter := raw[right-2] - if beforePotentialDriveLetter != ':' && beforePotentialDriveLetter != '/' && beforePotentialDriveLetter != '\\' { + // Only `:` or `\\` are checked (`/` could fall into the case of `/tmp/q:/foo`) + if beforePotentialDriveLetter != ':' && beforePotentialDriveLetter != '\\' { // e.g. `C:` is not preceded by any delimiter, therefore it was not a drive letter but a path ending with `C:`. array = append(array, raw[left:right]) left = right + 1 diff --git a/runconfig/opts/parse_test.go b/runconfig/opts/parse_test.go index 93db97821b..4fe2c6aa69 100644 --- a/runconfig/opts/parse_test.go +++ b/runconfig/opts/parse_test.go @@ -801,6 +801,9 @@ func TestVolumeSplitN(t *testing.T) { {`..\`, -1, []string{`..\`}}, {`c:\:..\`, -1, []string{`c:\`, `..\`}}, {`c:\:d:\:xyzzy`, -1, []string{`c:\`, `d:\`, `xyzzy`}}, + + // Cover directories with one-character name + {`/tmp/x/y:/foo/x/y`, -1, []string{`/tmp/x/y`, `/foo/x/y`}}, } { res := volumeSplitN(x.input, x.n) if len(res) < len(x.expected) {