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 an absolute 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  //
    23  // On Unix platforms, if the environment variable PWD
    24  // provides an absolute name, and it is a name of the
    25  // current directory, it is returned.
    26  func Getwd() (dir string, err error) {
    27  	if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
    28  		// Use syscall.Getwd directly for
    29  		//   - plan9: see reasons in CL 89575;
    30  		//   - windows: syscall implementation is sufficient,
    31  		//     and we should not rely on $PWD.
    32  		dir, err = syscall.Getwd()
    33  		return dir, NewSyscallError("getwd", err)
    34  	}
    35  
    36  	// Clumsy but widespread kludge:
    37  	// if $PWD is set and matches ".", use it.
    38  	var dot FileInfo
    39  	dir = Getenv("PWD")
    40  	if len(dir) > 0 && dir[0] == '/' {
    41  		dot, err = statNolog(".")
    42  		if err != nil {
    43  			return "", err
    44  		}
    45  		d, err := statNolog(dir)
    46  		if err == nil && SameFile(dot, d) {
    47  			return dir, nil
    48  		}
    49  		// If err is ENAMETOOLONG here, the syscall.Getwd below will
    50  		// fail with the same error, too, but let's give it a try
    51  		// anyway as the fallback code is much slower.
    52  	}
    53  
    54  	// If the operating system provides a Getwd call, use it.
    55  	if syscall.ImplementsGetwd {
    56  		dir, err = ignoringEINTR2(syscall.Getwd)
    57  		// Linux returns ENAMETOOLONG if the result is too long.
    58  		// Some BSD systems appear to return EINVAL.
    59  		// FreeBSD systems appear to use ENOMEM
    60  		// Solaris appears to use ERANGE.
    61  		if err != syscall.ENAMETOOLONG && err != syscall.EINVAL && err != errERANGE && err != errENOMEM {
    62  			return dir, NewSyscallError("getwd", err)
    63  		}
    64  	}
    65  
    66  	// We're trying to find our way back to ".".
    67  	if dot == nil {
    68  		dot, err = statNolog(".")
    69  		if err != nil {
    70  			return "", err
    71  		}
    72  	}
    73  	// Apply same kludge but to cached dir instead of $PWD.
    74  	getwdCache.Lock()
    75  	dir = getwdCache.dir
    76  	getwdCache.Unlock()
    77  	if len(dir) > 0 {
    78  		d, err := statNolog(dir)
    79  		if err == nil && SameFile(dot, d) {
    80  			return dir, nil
    81  		}
    82  	}
    83  
    84  	// Root is a special case because it has no parent
    85  	// and ends in a slash.
    86  	root, err := statNolog("/")
    87  	if err != nil {
    88  		// Can't stat root - no hope.
    89  		return "", err
    90  	}
    91  	if SameFile(root, dot) {
    92  		return "/", nil
    93  	}
    94  
    95  	// General algorithm: find name in parent
    96  	// and then find name of parent. Each iteration
    97  	// adds /name to the beginning of dir.
    98  	dir = ""
    99  	for parent := ".."; ; parent = "../" + parent {
   100  		if len(parent) >= 1024 { // Sanity check
   101  			return "", NewSyscallError("getwd", syscall.ENAMETOOLONG)
   102  		}
   103  		fd, err := openDirNolog(parent)
   104  		if err != nil {
   105  			return "", err
   106  		}
   107  
   108  		for {
   109  			names, err := fd.Readdirnames(100)
   110  			if err != nil {
   111  				fd.Close()
   112  				// Readdirnames can return io.EOF or other error.
   113  				// In any case, we're here because syscall.Getwd
   114  				// is not implemented or failed with ENAMETOOLONG,
   115  				// so return the most sensible error.
   116  				if syscall.ImplementsGetwd {
   117  					return "", NewSyscallError("getwd", syscall.ENAMETOOLONG)
   118  				}
   119  				return "", NewSyscallError("getwd", errENOSYS)
   120  			}
   121  			for _, name := range names {
   122  				d, _ := lstatNolog(parent + "/" + name)
   123  				if SameFile(d, dot) {
   124  					dir = "/" + name + dir
   125  					goto Found
   126  				}
   127  			}
   128  		}
   129  
   130  	Found:
   131  		pd, err := fd.Stat()
   132  		fd.Close()
   133  		if err != nil {
   134  			return "", err
   135  		}
   136  		if SameFile(pd, root) {
   137  			break
   138  		}
   139  		// Set up for next round.
   140  		dot = pd
   141  	}
   142  
   143  	// Save answer as hint to avoid the expensive path next time.
   144  	getwdCache.Lock()
   145  	getwdCache.dir = dir
   146  	getwdCache.Unlock()
   147  
   148  	return dir, nil
   149  }
   150  

View as plain text