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 "bytes" 9 "errors" 10 "os" 11 ) 12 13 const utf8RuneSelf = 0x80 14 15 func walkSymlinks(path string) (string, error) { 16 const maxIter = 255 17 originalPath := path 18 // consume path by taking each frontmost path element, 19 // expanding it if it's a symlink, and appending it to b 20 var b bytes.Buffer 21 for n := 0; path != ""; n++ { 22 if n > maxIter { 23 return "", errors.New("EvalSymlinks: too many links in " + originalPath) 24 } 25 26 // find next path component, p 27 var i = -1 28 for j, c := range path { 29 if c < utf8RuneSelf && os.IsPathSeparator(uint8(c)) { 30 i = j 31 break 32 } 33 } 34 var p string 35 if i == -1 { 36 p, path = path, "" 37 } else { 38 p, path = path[:i], path[i+1:] 39 } 40 41 if p == "" { 42 if b.Len() == 0 { 43 // must be absolute path 44 b.WriteRune(Separator) 45 } 46 continue 47 } 48 49 fi, err := os.Lstat(b.String() + p) 50 if err != nil { 51 return "", err 52 } 53 if fi.Mode()&os.ModeSymlink == 0 { 54 b.WriteString(p) 55 if path != "" || (b.Len() == 2 && len(p) == 2 && p[1] == ':') { 56 b.WriteRune(Separator) 57 } 58 continue 59 } 60 61 // it's a symlink, put it at the front of path 62 dest, err := os.Readlink(b.String() + p) 63 if err != nil { 64 return "", err 65 } 66 if IsAbs(dest) || os.IsPathSeparator(dest[0]) { 67 b.Reset() 68 } 69 path = dest + string(Separator) + path 70 } 71 return Clean(b.String()), nil 72 } 73