diff options
Diffstat (limited to 'libgo/go/path/filepath/symlink.go')
-rw-r--r-- | libgo/go/path/filepath/symlink.go | 135 |
1 files changed, 90 insertions, 45 deletions
diff --git a/libgo/go/path/filepath/symlink.go b/libgo/go/path/filepath/symlink.go index df0a9e0c2ba..bc287c5ecb3 100644 --- a/libgo/go/path/filepath/symlink.go +++ b/libgo/go/path/filepath/symlink.go @@ -5,68 +5,113 @@ package filepath import ( - "bytes" "errors" "os" + "runtime" ) -const utf8RuneSelf = 0x80 +// isRoot returns true if path is root of file system +// (`/` on unix and `/`, `\`, `c:\` or `c:/` on windows). +func isRoot(path string) bool { + if runtime.GOOS != "windows" { + return path == "/" + } + switch len(path) { + case 1: + return os.IsPathSeparator(path[0]) + case 3: + return path[1] == ':' && os.IsPathSeparator(path[2]) + } + return false +} -func walkSymlinks(path string) (string, error) { - const maxIter = 255 - originalPath := path - // consume path by taking each frontmost path element, - // expanding it if it's a symlink, and appending it to b - var b bytes.Buffer - for n := 0; path != ""; n++ { - if n > maxIter { - return "", errors.New("EvalSymlinks: too many links in " + originalPath) - } +// isDriveLetter returns true if path is Windows drive letter (like "c:"). +func isDriveLetter(path string) bool { + if runtime.GOOS != "windows" { + return false + } + return len(path) == 2 && path[1] == ':' +} - // find next path component, p - var i = -1 - for j, c := range path { - if c < utf8RuneSelf && os.IsPathSeparator(uint8(c)) { - i = j - break - } - } - var p string - if i == -1 { - p, path = path, "" - } else { - p, path = path[:i], path[i+1:] - } +func walkLink(path string, linksWalked *int) (newpath string, islink bool, err error) { + if *linksWalked > 255 { + return "", false, errors.New("EvalSymlinks: too many links") + } + fi, err := os.Lstat(path) + if err != nil { + return "", false, err + } + if fi.Mode()&os.ModeSymlink == 0 { + return path, false, nil + } + newpath, err = os.Readlink(path) + if err != nil { + return "", false, err + } + *linksWalked++ + return newpath, true, nil +} - if p == "" { - if b.Len() == 0 { - // must be absolute path - b.WriteRune(Separator) +func walkLinks(path string, linksWalked *int) (string, error) { + switch dir, file := Split(path); { + case dir == "": + newpath, _, err := walkLink(file, linksWalked) + return newpath, err + case file == "": + if isDriveLetter(dir) { + return dir, nil + } + if os.IsPathSeparator(dir[len(dir)-1]) { + if isRoot(dir) { + return dir, nil } - continue + return walkLinks(dir[:len(dir)-1], linksWalked) } - - fi, err := os.Lstat(b.String() + p) + newpath, _, err := walkLink(dir, linksWalked) + return newpath, err + default: + newdir, err := walkLinks(dir, linksWalked) if err != nil { return "", err } - if fi.Mode()&os.ModeSymlink == 0 { - b.WriteString(p) - if path != "" || (b.Len() == 2 && len(p) == 2 && p[1] == ':') { - b.WriteRune(Separator) - } - continue + newpath, islink, err := walkLink(Join(newdir, file), linksWalked) + if err != nil { + return "", err + } + if !islink { + return newpath, nil } + if IsAbs(newpath) || os.IsPathSeparator(newpath[0]) { + return newpath, nil + } + return Join(newdir, newpath), nil + } +} - // it's a symlink, put it at the front of path - dest, err := os.Readlink(b.String() + p) +func walkSymlinks(path string) (string, error) { + if path == "" { + return path, nil + } + var linksWalked int // to protect against cycles + for { + i := linksWalked + newpath, err := walkLinks(path, &linksWalked) if err != nil { return "", err } - if IsAbs(dest) || os.IsPathSeparator(dest[0]) { - b.Reset() + if runtime.GOOS == "windows" { + // walkLinks(".", ...) always retuns "." on unix. + // But on windows it returns symlink target, if current + // directory is a symlink. Stop the walk, if symlink + // target is not absolute path, and return "." + // to the caller (just like unix does). + if path == "." && !IsAbs(newpath) { + return ".", nil + } + } + if i == linksWalked { + return Clean(newpath), nil } - path = dest + string(Separator) + path + path = newpath } - return Clean(b.String()), nil } |