| | |
| | |
| | |
| |
|
| | package filepath |
| |
|
| | import ( |
| | "errors" |
| | "internal/filepathlite" |
| | "io/fs" |
| | "os" |
| | "runtime" |
| | "syscall" |
| | ) |
| |
|
| | func walkSymlinks(path string) (string, error) { |
| | volLen := filepathlite.VolumeNameLen(path) |
| | pathSeparator := string(os.PathSeparator) |
| |
|
| | if volLen < len(path) && os.IsPathSeparator(path[volLen]) { |
| | volLen++ |
| | } |
| | vol := path[:volLen] |
| | dest := vol |
| | linksWalked := 0 |
| | for start, end := volLen, volLen; start < len(path); start = end { |
| | for start < len(path) && os.IsPathSeparator(path[start]) { |
| | start++ |
| | } |
| | end = start |
| | for end < len(path) && !os.IsPathSeparator(path[end]) { |
| | end++ |
| | } |
| |
|
| | |
| | |
| | |
| | isWindowsDot := runtime.GOOS == "windows" && path[filepathlite.VolumeNameLen(path):] == "." |
| |
|
| | |
| | if end == start { |
| | |
| | break |
| | } else if path[start:end] == "." && !isWindowsDot { |
| | |
| | continue |
| | } else if path[start:end] == ".." { |
| | |
| | |
| |
|
| | |
| | |
| | var r int |
| | for r = len(dest) - 1; r >= volLen; r-- { |
| | if os.IsPathSeparator(dest[r]) { |
| | break |
| | } |
| | } |
| | if r < volLen || dest[r+1:] == ".." { |
| | |
| | |
| | |
| | |
| | if len(dest) > volLen { |
| | dest += pathSeparator |
| | } |
| | dest += ".." |
| | } else { |
| | |
| | dest = dest[:r] |
| | } |
| | continue |
| | } |
| |
|
| | |
| |
|
| | if len(dest) > filepathlite.VolumeNameLen(dest) && !os.IsPathSeparator(dest[len(dest)-1]) { |
| | dest += pathSeparator |
| | } |
| |
|
| | dest += path[start:end] |
| |
|
| | |
| |
|
| | fi, err := os.Lstat(dest) |
| | if err != nil { |
| | return "", err |
| | } |
| |
|
| | if fi.Mode()&fs.ModeSymlink == 0 { |
| | if !fi.Mode().IsDir() && end < len(path) { |
| | return "", syscall.ENOTDIR |
| | } |
| | continue |
| | } |
| |
|
| | |
| |
|
| | linksWalked++ |
| | if linksWalked > 255 { |
| | return "", errors.New("EvalSymlinks: too many links") |
| | } |
| |
|
| | link, err := os.Readlink(dest) |
| | if err != nil { |
| | return "", err |
| | } |
| |
|
| | if isWindowsDot && !IsAbs(link) { |
| | |
| | |
| | break |
| | } |
| |
|
| | path = link + path[end:] |
| |
|
| | v := filepathlite.VolumeNameLen(link) |
| | if v > 0 { |
| | |
| | if v < len(link) && os.IsPathSeparator(link[v]) { |
| | v++ |
| | } |
| | vol = link[:v] |
| | dest = vol |
| | end = len(vol) |
| | } else if len(link) > 0 && os.IsPathSeparator(link[0]) { |
| | |
| | dest = link[:1] |
| | end = 1 |
| | vol = link[:1] |
| | volLen = 1 |
| | } else { |
| | |
| | |
| | var r int |
| | for r = len(dest) - 1; r >= volLen; r-- { |
| | if os.IsPathSeparator(dest[r]) { |
| | break |
| | } |
| | } |
| | if r < volLen { |
| | dest = vol |
| | } else { |
| | dest = dest[:r] |
| | } |
| | end = 0 |
| | } |
| | } |
| | return Clean(dest), nil |
| | } |
| |
|