getwd.go 2.5 KB

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