Source file src/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  // 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  }
   127  

View as plain text