github.com/MangoDowner/go-gm@v0.0.0-20180818020936-8baa2bd4408c/src/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 // isRoot returns true if path is root of file system 14 // (`/` on unix and `/`, `\`, `c:\` or `c:/` on windows). 15 func isRoot(path string) bool { 16 if runtime.GOOS != "windows" { 17 return path == "/" 18 } 19 switch len(path) { 20 case 1: 21 return os.IsPathSeparator(path[0]) 22 case 3: 23 return path[1] == ':' && os.IsPathSeparator(path[2]) 24 } 25 return false 26 } 27 28 // isDriveLetter returns true if path is Windows drive letter (like "c:"). 29 func isDriveLetter(path string) bool { 30 if runtime.GOOS != "windows" { 31 return false 32 } 33 return len(path) == 2 && path[1] == ':' 34 } 35 36 func walkLink(path string, linksWalked *int) (newpath string, islink bool, err error) { 37 if *linksWalked > 255 { 38 return "", false, errors.New("EvalSymlinks: too many links") 39 } 40 fi, err := os.Lstat(path) 41 if err != nil { 42 return "", false, err 43 } 44 if fi.Mode()&os.ModeSymlink == 0 { 45 return path, false, nil 46 } 47 newpath, err = os.Readlink(path) 48 if err != nil { 49 return "", false, err 50 } 51 *linksWalked++ 52 return newpath, true, nil 53 } 54 55 func walkLinks(path string, linksWalked *int) (string, error) { 56 switch dir, file := Split(path); { 57 case dir == "": 58 newpath, _, err := walkLink(file, linksWalked) 59 return newpath, err 60 case file == "": 61 if isDriveLetter(dir) { 62 return dir, nil 63 } 64 if os.IsPathSeparator(dir[len(dir)-1]) { 65 if isRoot(dir) { 66 return dir, nil 67 } 68 return walkLinks(dir[:len(dir)-1], linksWalked) 69 } 70 newpath, _, err := walkLink(dir, linksWalked) 71 return newpath, err 72 default: 73 newdir, err := walkLinks(dir, linksWalked) 74 if err != nil { 75 return "", err 76 } 77 newpath, islink, err := walkLink(Join(newdir, file), linksWalked) 78 if err != nil { 79 return "", err 80 } 81 if !islink { 82 return newpath, nil 83 } 84 if IsAbs(newpath) || os.IsPathSeparator(newpath[0]) { 85 return newpath, nil 86 } 87 return Join(newdir, newpath), nil 88 } 89 } 90 91 func walkSymlinks(path string) (string, error) { 92 if path == "" { 93 return path, nil 94 } 95 var linksWalked int // to protect against cycles 96 for { 97 i := linksWalked 98 newpath, err := walkLinks(path, &linksWalked) 99 if err != nil { 100 return "", err 101 } 102 if runtime.GOOS == "windows" { 103 // walkLinks(".", ...) always returns "." on unix. 104 // But on windows it returns symlink target, if current 105 // directory is a symlink. Stop the walk, if symlink 106 // target is not absolute path, and return "." 107 // to the caller (just like unix does). 108 // Same for "C:.". 109 if path[volumeNameLen(path):] == "." && !IsAbs(newpath) { 110 return path, nil 111 } 112 } 113 if i == linksWalked { 114 return Clean(newpath), nil 115 } 116 path = newpath 117 } 118 }