github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/syscall/pwd_plan9.go (about) 1 // Copyright 2015 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 // The working directory in Plan 9 is effectively per P, so different 6 // goroutines and even the same goroutine as it's rescheduled on 7 // different Ps can see different working directories. 8 // 9 // Instead, track a Go process-wide intent of the current working directory, 10 // and switch to it at important points. 11 12 package syscall 13 14 import ( 15 "runtime" 16 "sync" 17 ) 18 19 var ( 20 wdmu sync.Mutex // guards following 21 wdSet bool 22 wdStr string 23 ) 24 25 // Ensure current working directory seen by this goroutine matches 26 // the most recent [Chdir] called in any goroutine. It's called internally 27 // before executing any syscall which uses a relative pathname. Must 28 // be called with the goroutine locked to the OS thread, to prevent 29 // rescheduling on a different thread (potentially with a different 30 // working directory) before the syscall is executed. 31 func Fixwd() { 32 wdmu.Lock() 33 defer wdmu.Unlock() 34 fixwdLocked() 35 } 36 37 func fixwdLocked() { 38 if !wdSet { 39 return 40 } 41 // always call chdir when getwd returns an error 42 wd, _ := getwd() 43 if wd == wdStr { 44 return 45 } 46 if err := chdir(wdStr); err != nil { 47 return 48 } 49 } 50 51 // If any of the paths is relative, call Fixwd and return true 52 // (locked to OS thread). Otherwise return false. 53 func fixwd(paths ...string) bool { 54 for _, path := range paths { 55 if path != "" && path[0] != '/' && path[0] != '#' { 56 runtime.LockOSThread() 57 Fixwd() 58 return true 59 } 60 } 61 return false 62 } 63 64 // goroutine-specific getwd 65 func getwd() (wd string, err error) { 66 fd, err := open(".", O_RDONLY) 67 if err != nil { 68 return "", err 69 } 70 defer Close(fd) 71 return Fd2path(fd) 72 } 73 74 func Getwd() (wd string, err error) { 75 wdmu.Lock() 76 defer wdmu.Unlock() 77 78 if wdSet { 79 return wdStr, nil 80 } 81 wd, err = getwd() 82 if err != nil { 83 return 84 } 85 wdSet = true 86 wdStr = wd 87 return wd, nil 88 } 89 90 func Chdir(path string) error { 91 // If Chdir is to a relative path, sync working dir first 92 if fixwd(path) { 93 defer runtime.UnlockOSThread() 94 } 95 wdmu.Lock() 96 defer wdmu.Unlock() 97 98 runtime.LockOSThread() 99 defer runtime.UnlockOSThread() 100 if err := chdir(path); err != nil { 101 return err 102 } 103 104 wd, err := getwd() 105 if err != nil { 106 return err 107 } 108 wdSet = true 109 wdStr = wd 110 return nil 111 }