dispatchers_windows.go 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. package dockerfile
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "regexp"
  7. "strings"
  8. "github.com/docker/docker/pkg/system"
  9. )
  10. var pattern = regexp.MustCompile(`^[a-zA-Z]:\.$`)
  11. // normaliseWorkdir normalises a user requested working directory in a
  12. // platform sematically consistent way.
  13. func normaliseWorkdir(current string, requested string) (string, error) {
  14. if requested == "" {
  15. return "", fmt.Errorf("cannot normalise nothing")
  16. }
  17. // `filepath.Clean` will replace "" with "." so skip in that case
  18. if current != "" {
  19. current = filepath.Clean(current)
  20. }
  21. if requested != "" {
  22. requested = filepath.Clean(requested)
  23. }
  24. // If either current or requested in Windows is:
  25. // C:
  26. // C:.
  27. // then an error will be thrown as the definition for the above
  28. // refers to `current directory on drive C:`
  29. // Since filepath.Clean() will automatically normalize the above
  30. // to `C:.`, we only need to check the last format
  31. if pattern.MatchString(current) {
  32. return "", fmt.Errorf("%s is not a directory. If you are specifying a drive letter, please add a trailing '\\'", current)
  33. }
  34. if pattern.MatchString(requested) {
  35. return "", fmt.Errorf("%s is not a directory. If you are specifying a drive letter, please add a trailing '\\'", requested)
  36. }
  37. // Target semantics is C:\somefolder, specifically in the format:
  38. // UPPERCASEDriveLetter-Colon-Backslash-FolderName. We are already
  39. // guaranteed that `current`, if set, is consistent. This allows us to
  40. // cope correctly with any of the following in a Dockerfile:
  41. // WORKDIR a --> C:\a
  42. // WORKDIR c:\\foo --> C:\foo
  43. // WORKDIR \\foo --> C:\foo
  44. // WORKDIR /foo --> C:\foo
  45. // WORKDIR c:\\foo \ WORKDIR bar --> C:\foo --> C:\foo\bar
  46. // WORKDIR C:/foo \ WORKDIR bar --> C:\foo --> C:\foo\bar
  47. // WORKDIR C:/foo \ WORKDIR \\bar --> C:\foo --> C:\bar
  48. // WORKDIR /foo \ WORKDIR c:/bar --> C:\foo --> C:\bar
  49. if len(current) == 0 || system.IsAbs(requested) {
  50. if (requested[0] == os.PathSeparator) ||
  51. (len(requested) > 1 && string(requested[1]) != ":") ||
  52. (len(requested) == 1) {
  53. requested = filepath.Join(`C:\`, requested)
  54. }
  55. } else {
  56. requested = filepath.Join(current, requested)
  57. }
  58. // Upper-case drive letter
  59. return (strings.ToUpper(string(requested[0])) + requested[1:]), nil
  60. }
  61. func errNotJSON(command, original string) error {
  62. // For Windows users, give a hint if it looks like it might contain
  63. // a path which hasn't been escaped such as ["c:\windows\system32\prog.exe", "-param"],
  64. // as JSON must be escaped. Unfortunate...
  65. //
  66. // Specifically looking for quote-driveletter-colon-backslash, there's no
  67. // double backslash and a [] pair. No, this is not perfect, but it doesn't
  68. // have to be. It's simply a hint to make life a little easier.
  69. extra := ""
  70. original = filepath.FromSlash(strings.ToLower(strings.Replace(strings.ToLower(original), strings.ToLower(command)+" ", "", -1)))
  71. if len(regexp.MustCompile(`"[a-z]:\\.*`).FindStringSubmatch(original)) > 0 &&
  72. !strings.Contains(original, `\\`) &&
  73. strings.Contains(original, "[") &&
  74. strings.Contains(original, "]") {
  75. extra = fmt.Sprintf(`. It looks like '%s' includes a file path without an escaped back-slash. JSON requires back-slashes to be escaped such as ["c:\\path\\to\\file.exe", "/parameter"]`, original)
  76. }
  77. return fmt.Errorf("%s requires the arguments to be in JSON form%s", command, extra)
  78. }