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 <yong.tang.github@outlook.com>
| ... | ... |
@@ -4243,3 +4243,12 @@ func (s *DockerSuite) TestRunAttachFailedNoLeak(c *check.C) {
|
| 4243 | 4243 |
// NGoroutines is not updated right away, so we need to wait before failing |
| 4244 | 4244 |
c.Assert(waitForGoroutines(nroutines), checker.IsNil) |
| 4245 | 4245 |
} |
| 4246 |
+ |
|
| 4247 |
+// Test for one character directory name case (#20122) |
|
| 4248 |
+func (s *DockerSuite) TestRunVolumeWithOneCharacter(c *check.C) {
|
|
| 4249 |
+ testRequires(c, DaemonIsLinux) |
|
| 4250 |
+ |
|
| 4251 |
+ out, _ := dockerCmd(c, "run", "-v", "/tmp/q:/foo", "busybox", "sh", "-c", "find /foo") |
|
| 4252 |
+ fmt.Printf("OUTPUT: %+v", out)
|
|
| 4253 |
+ c.Assert(strings.TrimSpace(out), checker.Equals, "/foo") |
|
| 4254 |
+} |
| ... | ... |
@@ -695,8 +695,12 @@ func validatePath(val string, validator func(string) bool) (string, error) {
|
| 695 | 695 |
} |
| 696 | 696 |
|
| 697 | 697 |
// volumeSplitN splits raw into a maximum of n parts, separated by a separator colon. |
| 698 |
-// A separator colon is the last `:` character in the regex `[/:\\]?[a-zA-Z]:` (note `\\` is `\` escaped). |
|
| 699 |
-// This allows to correctly split strings such as `C:\foo:D:\:rw`. |
|
| 698 |
+// A separator colon is the last `:` character in the regex `[:\\]?[a-zA-Z]:` (note `\\` is `\` escaped). |
|
| 699 |
+// In Windows driver letter appears in two situations: |
|
| 700 |
+// a. `^[a-zA-Z]:` (A colon followed by `^[a-zA-Z]:` is OK as colon is the separator in volume option) |
|
| 701 |
+// b. A string in the format like `\\?\C:\Windows\...` (UNC). |
|
| 702 |
+// Therefore, a driver letter can only follow either a `:` or `\\` |
|
| 703 |
+// This allows to correctly split strings such as `C:\foo:D:\:rw` or `/tmp/q:/foo`. |
|
| 700 | 704 |
func volumeSplitN(raw string, n int) []string {
|
| 701 | 705 |
var array []string |
| 702 | 706 |
if len(raw) == 0 || raw[0] == ':' {
|
| ... | ... |
@@ -721,7 +725,8 @@ func volumeSplitN(raw string, n int) []string {
|
| 721 | 721 |
if (potentialDriveLetter >= 'A' && potentialDriveLetter <= 'Z') || (potentialDriveLetter >= 'a' && potentialDriveLetter <= 'z') {
|
| 722 | 722 |
if right > 1 {
|
| 723 | 723 |
beforePotentialDriveLetter := raw[right-2] |
| 724 |
- if beforePotentialDriveLetter != ':' && beforePotentialDriveLetter != '/' && beforePotentialDriveLetter != '\\' {
|
|
| 724 |
+ // Only `:` or `\\` are checked (`/` could fall into the case of `/tmp/q:/foo`) |
|
| 725 |
+ if beforePotentialDriveLetter != ':' && beforePotentialDriveLetter != '\\' {
|
|
| 725 | 726 |
// e.g. `C:` is not preceded by any delimiter, therefore it was not a drive letter but a path ending with `C:`. |
| 726 | 727 |
array = append(array, raw[left:right]) |
| 727 | 728 |
left = right + 1 |
| ... | ... |
@@ -801,6 +801,9 @@ func TestVolumeSplitN(t *testing.T) {
|
| 801 | 801 |
{`..\`, -1, []string{`..\`}},
|
| 802 | 802 |
{`c:\:..\`, -1, []string{`c:\`, `..\`}},
|
| 803 | 803 |
{`c:\:d:\:xyzzy`, -1, []string{`c:\`, `d:\`, `xyzzy`}},
|
| 804 |
+ |
|
| 805 |
+ // Cover directories with one-character name |
|
| 806 |
+ {`/tmp/x/y:/foo/x/y`, -1, []string{`/tmp/x/y`, `/foo/x/y`}},
|
|
| 804 | 807 |
} {
|
| 805 | 808 |
res := volumeSplitN(x.input, x.n) |
| 806 | 809 |
if len(res) < len(x.expected) {
|