fs_windows.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. package symlink
  2. import (
  3. "bytes"
  4. "errors"
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. "syscall"
  9. "github.com/docker/docker/pkg/longpath"
  10. )
  11. func toShort(path string) (string, error) {
  12. p, err := syscall.UTF16FromString(path)
  13. if err != nil {
  14. return "", err
  15. }
  16. b := p // GetShortPathName says we can reuse buffer
  17. n, err := syscall.GetShortPathName(&p[0], &b[0], uint32(len(b)))
  18. if err != nil {
  19. return "", err
  20. }
  21. if n > uint32(len(b)) {
  22. b = make([]uint16, n)
  23. if _, err = syscall.GetShortPathName(&p[0], &b[0], uint32(len(b))); err != nil {
  24. return "", err
  25. }
  26. }
  27. return syscall.UTF16ToString(b), nil
  28. }
  29. func toLong(path string) (string, error) {
  30. p, err := syscall.UTF16FromString(path)
  31. if err != nil {
  32. return "", err
  33. }
  34. b := p // GetLongPathName says we can reuse buffer
  35. n, err := syscall.GetLongPathName(&p[0], &b[0], uint32(len(b)))
  36. if err != nil {
  37. return "", err
  38. }
  39. if n > uint32(len(b)) {
  40. b = make([]uint16, n)
  41. n, err = syscall.GetLongPathName(&p[0], &b[0], uint32(len(b)))
  42. if err != nil {
  43. return "", err
  44. }
  45. }
  46. b = b[:n]
  47. return syscall.UTF16ToString(b), nil
  48. }
  49. func evalSymlinks(path string) (string, error) {
  50. path, err := walkSymlinks(path)
  51. if err != nil {
  52. return "", err
  53. }
  54. p, err := toShort(path)
  55. if err != nil {
  56. return "", err
  57. }
  58. p, err = toLong(p)
  59. if err != nil {
  60. return "", err
  61. }
  62. // syscall.GetLongPathName does not change the case of the drive letter,
  63. // but the result of EvalSymlinks must be unique, so we have
  64. // EvalSymlinks(`c:\a`) == EvalSymlinks(`C:\a`).
  65. // Make drive letter upper case.
  66. if len(p) >= 2 && p[1] == ':' && 'a' <= p[0] && p[0] <= 'z' {
  67. p = string(p[0]+'A'-'a') + p[1:]
  68. } else if len(p) >= 6 && p[5] == ':' && 'a' <= p[4] && p[4] <= 'z' {
  69. p = p[:3] + string(p[4]+'A'-'a') + p[5:]
  70. }
  71. return filepath.Clean(p), nil
  72. }
  73. const utf8RuneSelf = 0x80
  74. func walkSymlinks(path string) (string, error) {
  75. const maxIter = 255
  76. originalPath := path
  77. // consume path by taking each frontmost path element,
  78. // expanding it if it's a symlink, and appending it to b
  79. var b bytes.Buffer
  80. for n := 0; path != ""; n++ {
  81. if n > maxIter {
  82. return "", errors.New("EvalSymlinks: too many links in " + originalPath)
  83. }
  84. // A path beginning with `\\?\` represents the root, so automatically
  85. // skip that part and begin processing the next segment.
  86. if strings.HasPrefix(path, longpath.Prefix) {
  87. b.WriteString(longpath.Prefix)
  88. path = path[4:]
  89. continue
  90. }
  91. // find next path component, p
  92. var i = -1
  93. for j, c := range path {
  94. if c < utf8RuneSelf && os.IsPathSeparator(uint8(c)) {
  95. i = j
  96. break
  97. }
  98. }
  99. var p string
  100. if i == -1 {
  101. p, path = path, ""
  102. } else {
  103. p, path = path[:i], path[i+1:]
  104. }
  105. if p == "" {
  106. if b.Len() == 0 {
  107. // must be absolute path
  108. b.WriteRune(filepath.Separator)
  109. }
  110. continue
  111. }
  112. // If this is the first segment after the long path prefix, accept the
  113. // current segment as a volume root or UNC share and move on to the next.
  114. if b.String() == longpath.Prefix {
  115. b.WriteString(p)
  116. b.WriteRune(filepath.Separator)
  117. continue
  118. }
  119. fi, err := os.Lstat(b.String() + p)
  120. if err != nil {
  121. return "", err
  122. }
  123. if fi.Mode()&os.ModeSymlink == 0 {
  124. b.WriteString(p)
  125. if path != "" || (b.Len() == 2 && len(p) == 2 && p[1] == ':') {
  126. b.WriteRune(filepath.Separator)
  127. }
  128. continue
  129. }
  130. // it's a symlink, put it at the front of path
  131. dest, err := os.Readlink(b.String() + p)
  132. if err != nil {
  133. return "", err
  134. }
  135. if filepath.IsAbs(dest) || os.IsPathSeparator(dest[0]) {
  136. b.Reset()
  137. }
  138. path = dest + string(filepath.Separator) + path
  139. }
  140. return filepath.Clean(b.String()), nil
  141. }
  142. func isDriveOrRoot(p string) bool {
  143. if p == string(filepath.Separator) {
  144. return true
  145. }
  146. length := len(p)
  147. if length >= 2 {
  148. if p[length-1] == ':' && (('a' <= p[length-2] && p[length-2] <= 'z') || ('A' <= p[length-2] && p[length-2] <= 'Z')) {
  149. return true
  150. }
  151. }
  152. return false
  153. }