Browse code

Use `filepath.Clean` in `normaliseWorkdir` for windows

As is seem in the comment of `normaliseWorkdir` for windows:
```
...
// WORKDIR c:\\foo --> C:\foo
// WORKDIR \\foo --> C:\foo
...
```

However, this is not the case in the current implementation because
`filepath.FromSlash` is used and `FromSlash` does not replace multiple
separator with a single one (`file.Clean` does).
So `normaliseWorkdir` does not truly normalize workdir.

This fix changes the implementation of `normaliseWorkdir` and use
`filepath.Clean` instead of `filepath.FromSlash`.

Additional test cases have been added to the unit test.

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>

Yong Tang authored on 2016/09/21 13:46:22
Showing 2 changed files
... ...
@@ -10,6 +10,8 @@ import (
10 10
 	"github.com/docker/docker/pkg/system"
11 11
 )
12 12
 
13
+var pattern = regexp.MustCompile(`^[a-zA-Z]:\.$`)
14
+
13 15
 // normaliseWorkdir normalises a user requested working directory in a
14 16
 // platform sematically consistent way.
15 17
 func normaliseWorkdir(current string, requested string) (string, error) {
... ...
@@ -17,8 +19,27 @@ func normaliseWorkdir(current string, requested string) (string, error) {
17 17
 		return "", fmt.Errorf("cannot normalise nothing")
18 18
 	}
19 19
 
20
-	current = filepath.FromSlash(current)
21
-	requested = filepath.FromSlash(requested)
20
+	// `filepath.Clean` will replace "" with "." so skip in that case
21
+	if current != "" {
22
+		current = filepath.Clean(current)
23
+	}
24
+	if requested != "" {
25
+		requested = filepath.Clean(requested)
26
+	}
27
+
28
+	// If either current or requested in Windows is:
29
+	// C:
30
+	// C:.
31
+	// then an error will be thrown as the definition for the above
32
+	// refers to `current directory on drive C:`
33
+	// Since filepath.Clean() will automatically normalize the above
34
+	// to `C:.`, we only need to check the last format
35
+	if pattern.MatchString(current) {
36
+		return "", fmt.Errorf("%s is not a directory. If you are specifying a drive letter, please add a trailing '\\'", current)
37
+	}
38
+	if pattern.MatchString(requested) {
39
+		return "", fmt.Errorf("%s is not a directory. If you are specifying a drive letter, please add a trailing '\\'", requested)
40
+	}
22 41
 
23 42
 	// Target semantics is C:\somefolder, specifically in the format:
24 43
 	// UPPERCASEDriveLetter-Colon-Backslash-FolderName. We are already
... ...
@@ -7,9 +7,15 @@ import "testing"
7 7
 func TestNormaliseWorkdir(t *testing.T) {
8 8
 	tests := []struct{ current, requested, expected, etext string }{
9 9
 		{``, ``, ``, `cannot normalise nothing`},
10
+		{``, `C:`, ``, `C:. is not a directory. If you are specifying a drive letter, please add a trailing '\'`},
11
+		{``, `C:.`, ``, `C:. is not a directory. If you are specifying a drive letter, please add a trailing '\'`},
12
+		{`c:`, `\a`, ``, `c:. is not a directory. If you are specifying a drive letter, please add a trailing '\'`},
13
+		{`c:.`, `\a`, ``, `c:. is not a directory. If you are specifying a drive letter, please add a trailing '\'`},
10 14
 		{``, `a`, `C:\a`, ``},
11 15
 		{``, `c:\foo`, `C:\foo`, ``},
16
+		{``, `c:\\foo`, `C:\foo`, ``},
12 17
 		{``, `\foo`, `C:\foo`, ``},
18
+		{``, `\\foo`, `C:\foo`, ``},
13 19
 		{``, `/foo`, `C:\foo`, ``},
14 20
 		{``, `C:/foo`, `C:\foo`, ``},
15 21
 		{`C:\foo`, `bar`, `C:\foo\bar`, ``},
... ...
@@ -20,15 +26,15 @@ func TestNormaliseWorkdir(t *testing.T) {
20 20
 		r, e := normaliseWorkdir(i.current, i.requested)
21 21
 
22 22
 		if i.etext != "" && e == nil {
23
-			t.Fatalf("TestNormaliseWorkingDir Expected error %s", i.etext)
23
+			t.Fatalf("TestNormaliseWorkingDir Expected error %s for '%s' '%s', got no error", i.etext, i.current, i.requested)
24 24
 		}
25 25
 
26 26
 		if i.etext != "" && e.Error() != i.etext {
27
-			t.Fatalf("TestNormaliseWorkingDir Expected error %s, got %s", i.etext, e.Error())
27
+			t.Fatalf("TestNormaliseWorkingDir Expected error %s for '%s' '%s', got %s", i.etext, i.current, i.requested, e.Error())
28 28
 		}
29 29
 
30 30
 		if r != i.expected {
31
-			t.Fatalf("TestNormaliseWorkingDir Expected %s for %s %s", i.expected, i.current, i.requested)
31
+			t.Fatalf("TestNormaliseWorkingDir Expected '%s' for '%s' '%s', got '%s'", i.expected, i.current, i.requested, r)
32 32
 		}
33 33
 	}
34 34
 }