...

Source file src/pkg/pkg/os/getwd.go

     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	

View as plain text