github.com/megatontech/mynoteforgo@v0.0.0-20200507084910-5d0c6ea6e890/源码/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 ) 12 13 func walkSymlinks(path string) (string, error) { 14 volLen := volumeNameLen(path) 15 pathSeparator := string(os.PathSeparator) 16 17 if volLen < len(path) && os.IsPathSeparator(path[volLen]) { 18 volLen++ 19 } 20 vol := path[:volLen] 21 dest := vol 22 linksWalked := 0 23 for start, end := volLen, volLen; start < len(path); start = end { 24 for start < len(path) && os.IsPathSeparator(path[start]) { 25 start++ 26 } 27 end = start 28 for end < len(path) && !os.IsPathSeparator(path[end]) { 29 end++ 30 } 31 32 // On Windows, "." can be a symlink. 33 // We look it up, and use the value if it is absolute. 34 // If not, we just return ".". 35 isWindowsDot := runtime.GOOS == "windows" && path[volumeNameLen(path):] == "." 36 37 // The next path component is in path[start:end]. 38 if end == start { 39 // No more path components. 40 break 41 } else if path[start:end] == "." && !isWindowsDot { 42 // Ignore path component ".". 43 continue 44 } else if path[start:end] == ".." { 45 // Back up to previous component if possible. 46 // Note that volLen includes any leading slash. 47 var r int 48 for r = len(dest) - 1; r >= volLen; r-- { 49 if os.IsPathSeparator(dest[r]) { 50 break 51 } 52 } 53 if r < volLen { 54 if len(dest) > volLen { 55 dest += pathSeparator 56 } 57 dest += ".." 58 } else { 59 dest = dest[:r] 60 } 61 continue 62 } 63 64 // Ordinary path component. Add it to result. 65 66 if len(dest) > volumeNameLen(dest) && !os.IsPathSeparator(dest[len(dest)-1]) { 67 dest += pathSeparator 68 } 69 70 dest += path[start:end] 71 72 // Resolve symlink. 73 74 fi, err := os.Lstat(dest) 75 if err != nil { 76 return "", err 77 } 78 79 if fi.Mode()&os.ModeSymlink == 0 { 80 if !fi.Mode().IsDir() && end < len(path) { 81 return "", slashAfterFilePathError 82 } 83 continue 84 } 85 86 // Found symlink. 87 88 linksWalked++ 89 if linksWalked > 255 { 90 return "", errors.New("EvalSymlinks: too many links") 91 } 92 93 link, err := os.Readlink(dest) 94 if err != nil { 95 return "", err 96 } 97 98 if isWindowsDot && !IsAbs(link) { 99 // On Windows, if "." is a relative symlink, 100 // just return ".". 101 break 102 } 103 104 path = link + path[end:] 105 106 v := volumeNameLen(link) 107 if v > 0 { 108 // Symlink to drive name is an absolute path. 109 if v < len(link) && os.IsPathSeparator(link[v]) { 110 v++ 111 } 112 vol = link[:v] 113 dest = vol 114 end = len(vol) 115 } else if len(link) > 0 && os.IsPathSeparator(link[0]) { 116 // Symlink to absolute path. 117 dest = link[:1] 118 end = 1 119 } else { 120 // Symlink to relative path; replace last 121 // path component in dest. 122 var r int 123 for r = len(dest) - 1; r >= volLen; r-- { 124 if os.IsPathSeparator(dest[r]) { 125 break 126 } 127 } 128 if r < volLen { 129 dest = vol 130 } else { 131 dest = dest[:r] 132 } 133 end = 0 134 } 135 } 136 return Clean(dest), nil 137 }