aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fileutil/fileutil.go15
-rw-r--r--fileutil/fileutil_test.go80
2 files changed, 93 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 {
diff --git a/fileutil/fileutil_test.go b/fileutil/fileutil_test.go
new file mode 100644
index 000000000..1084cc98d
--- /dev/null
+++ b/fileutil/fileutil_test.go
@@ -0,0 +1,80 @@
+// Copyright 2015 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package fileutil
+
+import (
+ "bytes"
+ "io/ioutil"
+ "os"
+ "strconv"
+ "sync"
+ "testing"
+ "path/filepath"
+)
+
+func TestProcessTempDir(t *testing.T) {
+ for try := 0; try < 10; try++ {
+ func() {
+ tmp, err := ioutil.TempDir("", "syz")
+ if err != nil {
+ t.Fatalf("failed to create a temp dir: %v", err)
+ }
+ defer os.RemoveAll(tmp)
+ const P = 16
+ // Pre-create half of the instances with stale pid.
+ var dirs []string
+ for i := 0; i < P/2; i++ {
+ dir, idx, err := ProcessTempDir(tmp)
+ if err != nil {
+ t.Fatalf("failed to create process temp dir")
+ }
+ if idx != i {
+ t.Fatalf("unexpected index: want %v, got %v", i, idx)
+ }
+ dirs = append(dirs, dir)
+ }
+ for _, dir := range dirs {
+ if err := ioutil.WriteFile(filepath.Join(dir, ".pid"), []byte(strconv.Itoa(999999999)), 0600); err != nil {
+ t.Fatalf("failed to write pid file: %v", err)
+ }
+ }
+ // Now request a bunch of instances concurrently.
+ done := make(chan bool)
+ indices := make(map[int]bool)
+ var mu sync.Mutex
+ for p := 0; p < P; p++ {
+ go func() {
+ defer func() {
+ done <- true
+ }()
+ dir, idx, err := ProcessTempDir(tmp)
+ if err != nil {
+ t.Fatalf("failed to create process temp dir")
+ }
+ mu.Lock()
+ present := indices[idx]
+ indices[idx] = true
+ mu.Unlock()
+ if present {
+ t.Fatalf("duplicate index %v", idx)
+ }
+ data := []byte(strconv.Itoa(idx))
+ if err := ioutil.WriteFile(filepath.Join(dir, "data"), data, 0600); err != nil {
+ t.Fatalf("failed to write data file: %v", err)
+ }
+ data1, err := ioutil.ReadFile(filepath.Join(dir, "data"))
+ if err != nil {
+ t.Fatalf("failed to read data file: %v", err)
+ }
+ if bytes.Compare(data, data1) != 0 {
+ t.Fatalf("corrupted data file")
+ }
+ }()
+ }
+ for p := 0; p < P; p++ {
+ <-done
+ }
+ }()
+ }
+}