github.com/guyezi/gofrontend@v0.0.0-20200228202240-7a62a49e62c0/libgo/go/path/filepath/symlink.go (about) 1 // Copyright 2012 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package filepath 6 7 import ( 8 "errors" 9 "os" 10 "runtime" 11 "syscall" 12 ) 13 14 func walkSymlinks(path string) (string, error) { 15 volLen := volumeNameLen(path) 16 pathSeparator := string(os.PathSeparator) 17 18 if volLen < len(path) && os.IsPathSeparator(path[volLen]) { 19 volLen++ 20 } 21 vol := path[:volLen] 22 dest := vol 23 linksWalked := 0 24 for start, end := volLen, volLen; start < len(path); start = end { 25 for start < len(path) && os.IsPathSeparator(path[start]) { 26 start++ 27 } 28 end = start 29 for end < len(path) && !os.IsPathSeparator(path[end]) { 30 end++ 31 } 32 33 // On Windows, "." can be a symlink. 34 // We look it up, and use the value if it is absolute. 35 // If not, we just return ".". 36 isWindowsDot := runtime.GOOS == "windows" && path[volumeNameLen(path):] == "." 37 38 // The next path component is in path[start:end]. 39 if end == start { 40 // No more path components. 41 break 42 } else if path[start:end] == "." && !isWindowsDot { 43 // Ignore path component ".". 44 continue 45 } else if path[start:end] == ".." { 46 // Back up to previous component if possible. 47 // Note that volLen includes any leading slash. 48 49 // Set r to the index of the last slash in dest, 50 // after the volume. 51 var r int 52 for r = len(dest) - 1; r >= volLen; r-- { 53 if os.IsPathSeparator(dest[r]) { 54 break 55 } 56 } 57 if r < volLen || dest[r+1:] == ".." { 58 // Either path has no slashes 59 // (it's empty or just "C:") 60 // or it ends in a ".." we had to keep. 61 // Either way, keep this "..". 62 if len(dest) > volLen { 63 dest += pathSeparator 64 } 65 dest += ".." 66 } else { 67 // Discard everything since the last slash. 68 dest = dest[:r] 69 } 70 continue 71 } 72 73 // Ordinary path component. Add it to result. 74 75 if len(dest) > volumeNameLen(dest) && !os.IsPathSeparator(dest[len(dest)-1]) { 76 dest += pathSeparator 77 } 78 79 dest += path[start:end] 80 81 // Resolve symlink. 82 83 fi, err := os.Lstat(dest) 84 if err != nil { 85 return "", err 86 } 87 88 if fi.Mode()&os.ModeSymlink == 0 { 89 if !fi.Mode().IsDir() && end < len(path) { 90 return "", syscall.ENOTDIR 91 } 92 continue 93 } 94 95 // Found symlink. 96 97 linksWalked++ 98 if linksWalked > 255 { 99 return "", errors.New("EvalSymlinks: too many links") 100 } 101 102 link, err := os.Readlink(dest) 103 if err != nil { 104 return "", err 105 } 106 107 if isWindowsDot && !IsAbs(link) { 108 // On Windows, if "." is a relative symlink, 109 // just return ".". 110 break 111 } 112 113 path = link + path[end:] 114 115 v := volumeNameLen(link) 116 if v > 0 { 117 // Symlink to drive name is an absolute path. 118 if v < len(link) && os.IsPathSeparator(link[v]) { 119 v++ 120 } 121 vol = link[:v] 122 dest = vol 123 end = len(vol) 124 } else if len(link) > 0 && os.IsPathSeparator(link[0]) { 125 // Symlink to absolute path. 126 dest = link[:1] 127 end = 1 128 } else { 129 // Symlink to relative path; replace last 130 // path component in dest. 131 var r int 132 for r = len(dest) - 1; r >= volLen; r-- { 133 if os.IsPathSeparator(dest[r]) { 134 break 135 } 136 } 137 if r < volLen { 138 dest = vol 139 } else { 140 dest = dest[:r] 141 } 142 end = 0 143 } 144 } 145 return Clean(dest), nil 146 }