| // Copyright 2015 The Go Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style | 
 | // license that can be found in the LICENSE file. | 
 |  | 
 | // The working directory in Plan 9 is effectively per P, so different | 
 | // goroutines and even the same goroutine as it's rescheduled on | 
 | // different Ps can see different working directories. | 
 | // | 
 | // Instead, track a Go process-wide intent of the current working directory, | 
 | // and switch to it at important points. | 
 |  | 
 | package syscall | 
 |  | 
 | import "sync" | 
 |  | 
 | var ( | 
 | 	wdmu  sync.Mutex // guards following | 
 | 	wdSet bool | 
 | 	wdStr string | 
 | ) | 
 |  | 
 | func Fixwd() { | 
 | 	wdmu.Lock() | 
 | 	defer wdmu.Unlock() | 
 | 	fixwdLocked() | 
 | } | 
 |  | 
 | func fixwdLocked() { | 
 | 	if !wdSet { | 
 | 		return | 
 | 	} | 
 | 	// always call chdir when getwd returns an error | 
 | 	wd, _ := getwd() | 
 | 	if wd == wdStr { | 
 | 		return | 
 | 	} | 
 | 	if err := chdir(wdStr); err != nil { | 
 | 		return | 
 | 	} | 
 | } | 
 |  | 
 | // goroutine-specific getwd | 
 | func getwd() (wd string, err error) { | 
 | 	fd, err := open(".", O_RDONLY) | 
 | 	if err != nil { | 
 | 		return "", err | 
 | 	} | 
 | 	defer Close(fd) | 
 | 	return Fd2path(fd) | 
 | } | 
 |  | 
 | func Getwd() (wd string, err error) { | 
 | 	wdmu.Lock() | 
 | 	defer wdmu.Unlock() | 
 |  | 
 | 	if wdSet { | 
 | 		return wdStr, nil | 
 | 	} | 
 | 	wd, err = getwd() | 
 | 	if err != nil { | 
 | 		return | 
 | 	} | 
 | 	wdSet = true | 
 | 	wdStr = wd | 
 | 	return wd, nil | 
 | } | 
 |  | 
 | func Chdir(path string) error { | 
 | 	wdmu.Lock() | 
 | 	defer wdmu.Unlock() | 
 |  | 
 | 	if err := chdir(path); err != nil { | 
 | 		return err | 
 | 	} | 
 |  | 
 | 	wd, err := getwd() | 
 | 	if err != nil { | 
 | 		return err | 
 | 	} | 
 | 	wdSet = true | 
 | 	wdStr = wd | 
 | 	return nil | 
 | } |