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