github.com/lovishpuri/go-40569/src@v0.0.0-20230519171745-f8623e7c56cf/os/getwd.go (about) 1 // Copyright 2009 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 os 6 7 import ( 8 "runtime" 9 "sync" 10 "syscall" 11 ) 12 13 var getwdCache struct { 14 sync.Mutex 15 dir string 16 } 17 18 // Getwd returns a rooted path name corresponding to the 19 // current directory. If the current directory can be 20 // reached via multiple paths (due to symbolic links), 21 // Getwd may return any one of them. 22 func Getwd() (dir string, err error) { 23 if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { 24 return syscall.Getwd() 25 } 26 27 // Clumsy but widespread kludge: 28 // if $PWD is set and matches ".", use it. 29 dot, err := statNolog(".") 30 if err != nil { 31 return "", err 32 } 33 dir = Getenv("PWD") 34 if len(dir) > 0 && dir[0] == '/' { 35 d, err := statNolog(dir) 36 if err == nil && SameFile(dot, d) { 37 return dir, nil 38 } 39 } 40 41 // If the operating system provides a Getwd call, use it. 42 // Otherwise, we're trying to find our way back to ".". 43 if syscall.ImplementsGetwd { 44 var ( 45 s string 46 e error 47 ) 48 for { 49 s, e = syscall.Getwd() 50 if e != syscall.EINTR { 51 break 52 } 53 } 54 return s, NewSyscallError("getwd", e) 55 } 56 57 // Apply same kludge but to cached dir instead of $PWD. 58 getwdCache.Lock() 59 dir = getwdCache.dir 60 getwdCache.Unlock() 61 if len(dir) > 0 { 62 d, err := statNolog(dir) 63 if err == nil && SameFile(dot, d) { 64 return dir, nil 65 } 66 } 67 68 // Root is a special case because it has no parent 69 // and ends in a slash. 70 root, err := statNolog("/") 71 if err != nil { 72 // Can't stat root - no hope. 73 return "", err 74 } 75 if SameFile(root, dot) { 76 return "/", nil 77 } 78 79 // General algorithm: find name in parent 80 // and then find name of parent. Each iteration 81 // adds /name to the beginning of dir. 82 dir = "" 83 for parent := ".."; ; parent = "../" + parent { 84 if len(parent) >= 1024 { // Sanity check 85 return "", syscall.ENAMETOOLONG 86 } 87 fd, err := openFileNolog(parent, O_RDONLY, 0) 88 if err != nil { 89 return "", err 90 } 91 92 for { 93 names, err := fd.Readdirnames(100) 94 if err != nil { 95 fd.Close() 96 return "", err 97 } 98 for _, name := range names { 99 d, _ := lstatNolog(parent + "/" + name) 100 if SameFile(d, dot) { 101 dir = "/" + name + dir 102 goto Found 103 } 104 } 105 } 106 107 Found: 108 pd, err := fd.Stat() 109 fd.Close() 110 if err != nil { 111 return "", err 112 } 113 if SameFile(pd, root) { 114 break 115 } 116 // Set up for next round. 117 dot = pd 118 } 119 120 // Save answer as hint to avoid the expensive path next time. 121 getwdCache.Lock() 122 getwdCache.dir = dir 123 getwdCache.Unlock() 124 125 return dir, nil 126 }