file_posix.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. /*
  2. Copyright The containerd Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package sysx
  14. import (
  15. "os"
  16. "path/filepath"
  17. "github.com/containerd/continuity/syscallx"
  18. )
  19. // Readlink returns the destination of the named symbolic link.
  20. // If there is an error, it will be of type *PathError.
  21. func Readlink(name string) (string, error) {
  22. for len := 128; ; len *= 2 {
  23. b := make([]byte, len)
  24. n, e := fixCount(syscallx.Readlink(fixLongPath(name), b))
  25. if e != nil {
  26. return "", &os.PathError{Op: "readlink", Path: name, Err: e}
  27. }
  28. if n < len {
  29. return string(b[0:n]), nil
  30. }
  31. }
  32. }
  33. // Many functions in package syscall return a count of -1 instead of 0.
  34. // Using fixCount(call()) instead of call() corrects the count.
  35. func fixCount(n int, err error) (int, error) {
  36. if n < 0 {
  37. n = 0
  38. }
  39. return n, err
  40. }
  41. // fixLongPath returns the extended-length (\\?\-prefixed) form of
  42. // path when needed, in order to avoid the default 260 character file
  43. // path limit imposed by Windows. If path is not easily converted to
  44. // the extended-length form (for example, if path is a relative path
  45. // or contains .. elements), or is short enough, fixLongPath returns
  46. // path unmodified.
  47. //
  48. // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath
  49. func fixLongPath(path string) string {
  50. // Do nothing (and don't allocate) if the path is "short".
  51. // Empirically (at least on the Windows Server 2013 builder),
  52. // the kernel is arbitrarily okay with < 248 bytes. That
  53. // matches what the docs above say:
  54. // "When using an API to create a directory, the specified
  55. // path cannot be so long that you cannot append an 8.3 file
  56. // name (that is, the directory name cannot exceed MAX_PATH
  57. // minus 12)." Since MAX_PATH is 260, 260 - 12 = 248.
  58. //
  59. // The MSDN docs appear to say that a normal path that is 248 bytes long
  60. // will work; empirically the path must be less then 248 bytes long.
  61. if len(path) < 248 {
  62. // Don't fix. (This is how Go 1.7 and earlier worked,
  63. // not automatically generating the \\?\ form)
  64. return path
  65. }
  66. // The extended form begins with \\?\, as in
  67. // \\?\c:\windows\foo.txt or \\?\UNC\server\share\foo.txt.
  68. // The extended form disables evaluation of . and .. path
  69. // elements and disables the interpretation of / as equivalent
  70. // to \. The conversion here rewrites / to \ and elides
  71. // . elements as well as trailing or duplicate separators. For
  72. // simplicity it avoids the conversion entirely for relative
  73. // paths or paths containing .. elements. For now,
  74. // \\server\share paths are not converted to
  75. // \\?\UNC\server\share paths because the rules for doing so
  76. // are less well-specified.
  77. if len(path) >= 2 && path[:2] == `\\` {
  78. // Don't canonicalize UNC paths.
  79. return path
  80. }
  81. if !filepath.IsAbs(path) {
  82. // Relative path
  83. return path
  84. }
  85. const prefix = `\\?`
  86. pathbuf := make([]byte, len(prefix)+len(path)+len(`\`))
  87. copy(pathbuf, prefix)
  88. n := len(path)
  89. r, w := 0, len(prefix)
  90. for r < n {
  91. switch {
  92. case os.IsPathSeparator(path[r]):
  93. // empty block
  94. r++
  95. case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])):
  96. // /./
  97. r++
  98. case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])):
  99. // /../ is currently unhandled
  100. return path
  101. default:
  102. pathbuf[w] = '\\'
  103. w++
  104. for ; r < n && !os.IsPathSeparator(path[r]); r++ {
  105. pathbuf[w] = path[r]
  106. w++
  107. }
  108. }
  109. }
  110. // A drive's root directory needs a trailing \
  111. if w == len(`\\?\c:`) {
  112. pathbuf[w] = '\\'
  113. w++
  114. }
  115. return string(pathbuf[:w])
  116. }