|  | // 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 | 
|  | } | 
|  | } | 
|  |  | 
|  | func fixwd(paths ...string) { | 
|  | for _, path := range paths { | 
|  | if path != "" && path[0] != '/' && path[0] != '#' { | 
|  | Fixwd() | 
|  | 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 { | 
|  | fixwd(path) | 
|  | 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 | 
|  | } |