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