syzkaller/fileutil/fileutil.go
Dmitry Vyukov 9d672cd451 config: use dense indices for VMs
CreateVMConfig uses fileutil.ProcessTempDir to assign indices to VMs.
fileutil.ProcessTempDir generates unique indices globally across several processes.
This was required for old vm/qemu code that used the index to choose unique ssh port for the VM.
Now vm/qemu does not use index as port and this global index assignment started
causing problems for adb and gce. Adb really needs indexes to be dense --
index is used to choose adb device id (if we have 2 devices, index 3 causes
out of bounds panic). For gce it leads to creation of unnecessary VM instances
(if I set count=4, I want at most 4 VMs created).

Don't use fileutil.ProcessTempDir-generated index in CreateVMConfig
and instead just use the dense indices passed by caller.
2016-11-25 17:11:56 +01:00

120 lines
2.9 KiB
Go

// 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 (
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"sync"
"syscall"
"unsafe"
)
var copyMu sync.Mutex
// CopyFile copies oldFile to newFile, potentially serializing with other
// file copies (for large files).
func CopyFile(oldFile, newFile string, serial bool) error {
if serial {
copyMu.Lock()
defer copyMu.Unlock()
}
oldf, err := os.Open(oldFile)
if err != nil {
return err
}
defer oldf.Close()
newf, err := os.Create(newFile)
if err != nil {
return err
}
defer newf.Close()
_, err = io.Copy(newf, oldf)
if err != nil {
return err
}
return nil
}
// WriteTempFile writes data to a temp file and returns its name.
func WriteTempFile(data []byte) (string, error) {
f, err := ioutil.TempFile("", "syzkaller")
if err != nil {
return "", fmt.Errorf("failed to create a temp file: %v", err)
}
if _, err := f.Write(data); err != nil {
f.Close()
os.Remove(f.Name())
return "", fmt.Errorf("failed to write a temp file: %v", err)
}
f.Close()
return f.Name(), nil
}
// 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, error) {
lk := filepath.Join(where, "instance-lock")
lkf, err := syscall.Open(lk, syscall.O_RDWR|syscall.O_CREAT, 0600)
if err != nil {
return "", err
}
defer syscall.Close(lkf)
if err := syscall.Flock(lkf, syscall.LOCK_EX); err != nil {
return "", 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 && len(data) > 0 {
pid, err := strconv.Atoi(string(data))
if err == nil && pid > 1 {
if err := syscall.Kill(pid, 0); err == syscall.ESRCH {
if os.Remove(pidfile) == nil {
if os.RemoveAll(path) == nil {
i--
continue
}
}
}
}
}
// If err != nil, assume that the pid file is not created yet.
continue
}
if err != nil {
return "", err
}
if err := ioutil.WriteFile(pidfile, []byte(strconv.Itoa(syscall.Getpid())), 0600); err != nil {
return "", err
}
return path, nil
}
return "", fmt.Errorf("too many live instances")
}
// UmountAll recurusively unmounts all mounts in dir.
func UmountAll(dir string) {
files, _ := ioutil.ReadDir(dir)
for _, f := range files {
name := filepath.Join(dir, f.Name())
if f.IsDir() {
UmountAll(name)
}
fn := []byte(name + "\x00")
syscall.Syscall(syscall.SYS_UMOUNT2, uintptr(unsafe.Pointer(&fn[0])), syscall.MNT_FORCE, 0)
}
}