11 "github.com/docker/docker/pkg/longpath"
14 func toShort(path string) (string, error) {
15 p, err := syscall.UTF16FromString(path)
19 b := p // GetShortPathName says we can reuse buffer
20 n, err := syscall.GetShortPathName(&p[0], &b[0], uint32(len(b)))
24 if n > uint32(len(b)) {
26 if _, err = syscall.GetShortPathName(&p[0], &b[0], uint32(len(b))); err != nil {
30 return syscall.UTF16ToString(b), nil
33 func toLong(path string) (string, error) {
34 p, err := syscall.UTF16FromString(path)
38 b := p // GetLongPathName says we can reuse buffer
39 n, err := syscall.GetLongPathName(&p[0], &b[0], uint32(len(b)))
43 if n > uint32(len(b)) {
45 n, err = syscall.GetLongPathName(&p[0], &b[0], uint32(len(b)))
51 return syscall.UTF16ToString(b), nil
54 func evalSymlinks(path string) (string, error) {
55 path, err := walkSymlinks(path)
60 p, err := toShort(path)
68 // syscall.GetLongPathName does not change the case of the drive letter,
69 // but the result of EvalSymlinks must be unique, so we have
70 // EvalSymlinks(`c:\a`) == EvalSymlinks(`C:\a`).
71 // Make drive letter upper case.
72 if len(p) >= 2 && p[1] == ':' && 'a' <= p[0] && p[0] <= 'z' {
73 p = string(p[0]+'A'-'a') + p[1:]
74 } else if len(p) >= 6 && p[5] == ':' && 'a' <= p[4] && p[4] <= 'z' {
75 p = p[:3] + string(p[4]+'A'-'a') + p[5:]
77 return filepath.Clean(p), nil
80 const utf8RuneSelf = 0x80
82 func walkSymlinks(path string) (string, error) {
85 // consume path by taking each frontmost path element,
86 // expanding it if it's a symlink, and appending it to b
88 for n := 0; path != ""; n++ {
90 return "", errors.New("EvalSymlinks: too many links in " + originalPath)
93 // A path beginning with `\\?\` represents the root, so automatically
94 // skip that part and begin processing the next segment.
95 if strings.HasPrefix(path, longpath.Prefix) {
96 b.WriteString(longpath.Prefix)
101 // find next path component, p
103 for j, c := range path {
104 if c < utf8RuneSelf && os.IsPathSeparator(uint8(c)) {
113 p, path = path[:i], path[i+1:]
118 // must be absolute path
119 b.WriteRune(filepath.Separator)
124 // If this is the first segment after the long path prefix, accept the
125 // current segment as a volume root or UNC share and move on to the next.
126 if b.String() == longpath.Prefix {
128 b.WriteRune(filepath.Separator)
132 fi, err := os.Lstat(b.String() + p)
136 if fi.Mode()&os.ModeSymlink == 0 {
138 if path != "" || (b.Len() == 2 && len(p) == 2 && p[1] == ':') {
139 b.WriteRune(filepath.Separator)
144 // it's a symlink, put it at the front of path
145 dest, err := os.Readlink(b.String() + p)
149 if filepath.IsAbs(dest) || os.IsPathSeparator(dest[0]) {
152 path = dest + string(filepath.Separator) + path
154 return filepath.Clean(b.String()), nil
157 func isDriveOrRoot(p string) bool {
158 if p == string(filepath.Separator) {
164 if p[length-1] == ':' && (('a' <= p[length-2] && p[length-2] <= 'z') || ('A' <= p[length-2] && p[length-2] <= 'Z')) {