...

Source file src/os/getwd.go

Documentation: 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  // 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