Source file src/cmd/go/internal/base/path.go
1 // Copyright 2017 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 base 6 7 import ( 8 "errors" 9 "os" 10 "path/filepath" 11 "runtime" 12 "strings" 13 "sync" 14 15 "cmd/go/internal/str" 16 ) 17 18 // UncachedCwd returns the current working directory. 19 // Most callers should use Cwd, which caches the result for future use. 20 // UncachedCwd is appropriate to call early in program startup before flag parsing, 21 // because the -C flag may change the current directory. 22 func UncachedCwd() string { 23 wd, err := os.Getwd() 24 if err != nil { 25 Fatalf("cannot determine current directory: %v", err) 26 } 27 return wd 28 } 29 30 var cwdOnce = sync.OnceValue(UncachedCwd) 31 32 // Cwd returns the current working directory at the time of the first call. 33 func Cwd() string { 34 return cwdOnce() 35 } 36 37 // ShortPath returns an absolute or relative name for path, whatever is shorter. 38 // There are rare cases where the path produced by ShortPath could be incorrect 39 // so it should only be used when formatting paths for error messages, not to read 40 // a file. 41 func ShortPath(path string) string { 42 if rel, err := filepath.Rel(Cwd(), path); err == nil && len(rel) < len(path) { 43 return rel 44 } 45 return path 46 } 47 48 // ShortPathConservative is similar to ShortPath, but returns the input if the result of ShortPath 49 // would meet conditions that could make it invalid. If the short path would reach into a 50 // parent directory and the base path contains a symlink, a ".." component can 51 // cross a symlink boundary. That could be a problem because the symlinks could be evaluated, 52 // changing the relative location of the boundary, before the ".." terms are applied to 53 // go to parents. The check here is a little more conservative: it checks 54 // whether the path starts with a ../ or ..\ component, and if any of the parent directories 55 // of the working directory are symlinks. 56 // See #68383 for a case where this could happen. 57 func ShortPathConservative(path string) string { 58 if rel, err := relConservative(Cwd(), path); err == nil && len(rel) < len(path) { 59 return rel 60 } 61 return path 62 } 63 64 func relConservative(basepath, targpath string) (string, error) { 65 relpath, err := filepath.Rel(basepath, targpath) 66 if err != nil { 67 return "", err 68 } 69 if strings.HasPrefix(relpath, str.WithFilePathSeparator("..")) { 70 expanded, err := filepath.EvalSymlinks(basepath) 71 if err != nil || expanded != basepath { // The basepath contains a symlink. Be conservative and reject it. 72 return "", errors.New("conservatively rejecting relative path that may be invalid") 73 } 74 } 75 return relpath, nil 76 } 77 78 // RelPaths returns a copy of paths with absolute paths 79 // made relative to the current directory if they would be shorter. 80 func RelPaths(paths []string) []string { 81 out := make([]string, 0, len(paths)) 82 for _, p := range paths { 83 rel, err := relConservative(Cwd(), p) 84 if err == nil && len(rel) < len(p) { 85 p = rel 86 } 87 out = append(out, p) 88 } 89 return out 90 } 91 92 // IsTestFile reports whether the source file is a set of tests and should therefore 93 // be excluded from coverage analysis. 94 func IsTestFile(file string) bool { 95 // We don't cover tests, only the code they test. 96 return strings.HasSuffix(file, "_test.go") 97 } 98 99 // IsNull reports whether the path is a common name for the null device. 100 // It returns true for /dev/null on Unix, or NUL (case-insensitive) on Windows. 101 func IsNull(path string) bool { 102 if path == os.DevNull { 103 return true 104 } 105 if runtime.GOOS == "windows" { 106 if strings.EqualFold(path, "NUL") { 107 return true 108 } 109 } 110 return false 111 } 112