diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2015-12-29 13:29:00 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2015-12-29 13:29:00 +0100 |
| commit | d40104b8a35f01d31cad1f11e312e76e034ffc4a (patch) | |
| tree | 7670efd663a3c1a140b985f9bf01a7146603fd44 /fileutil/fileutil.go | |
| parent | b17c5726f6dc911ac0d4c9be02c2a0d9a7dee393 (diff) | |
fileutil: fix race in ProcessTempDir
One goroutine decides that it needs to clean up an instance,
but before it tries to delete pid file it is preempted.
Then another goroutine cleans up this instances and creates
a new instances in the same dir.
Then first goroutine removes already new pid file and removes
the used dir.
Fix this by using flock on a lock file.
Add a test.
Diffstat (limited to 'fileutil/fileutil.go')
| -rw-r--r-- | fileutil/fileutil.go | 15 |
1 files changed, 13 insertions, 2 deletions
diff --git a/fileutil/fileutil.go b/fileutil/fileutil.go index 34e2562fa..a019a14ea 100644 --- a/fileutil/fileutil.go +++ b/fileutil/fileutil.go @@ -59,14 +59,25 @@ func WriteTempFile(data []byte) (string, error) { // ProcessTempDir creates a new temp dir in where and returns its path and an unique index. // It also cleans up old, unused temp dirs after dead processes. func ProcessTempDir(where string) (string, int, error) { - for i := 0; i < 1e4; i++ { + lk := filepath.Join(where, "instance-lock") + lkf, err := syscall.Open(lk, syscall.O_RDWR|syscall.O_CREAT, 0600) + if err != nil { + return "", 0, err + } + defer syscall.Close(lkf) + if err := syscall.Flock(lkf, syscall.LOCK_EX); err != nil { + return "", 0, err + } + defer syscall.Flock(lkf, syscall.LOCK_UN) + + for i := 0; i < 1e3; i++ { path := filepath.Join(where, fmt.Sprintf("instance-%v", i)) pidfile := filepath.Join(path, ".pid") err := os.Mkdir(path, 0700) if os.IsExist(err) { // Try to clean up. data, err := ioutil.ReadFile(pidfile) - if err == nil { + if err == nil && len(data) > 0 { pid, err := strconv.Atoi(string(data)) if err == nil && pid > 1 { if err := syscall.Kill(pid, 0); err == syscall.ESRCH { |
