From cf99f85f6d18beb34d3213e60ed8c8f47eee27b6 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 30 Jul 2013 21:48:37 -0700 Subject: [PATCH] builder/amazon/chroot: flock so that device searching is safe --- builder/amazon/chroot/builder.go | 1 + builder/amazon/chroot/lockfile.go | 13 ++++++ builder/amazon/chroot/lockfile_unix.go | 32 ++++++++++++++ builder/amazon/chroot/step_flock.go | 59 ++++++++++++++++++++++++++ 4 files changed, 105 insertions(+) create mode 100644 builder/amazon/chroot/lockfile.go create mode 100644 builder/amazon/chroot/lockfile_unix.go create mode 100644 builder/amazon/chroot/step_flock.go diff --git a/builder/amazon/chroot/builder.go b/builder/amazon/chroot/builder.go index 1641cde57..b01a50919 100644 --- a/builder/amazon/chroot/builder.go +++ b/builder/amazon/chroot/builder.go @@ -123,6 +123,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe steps := []multistep.Step{ &StepInstanceInfo{}, &StepSourceAMIInfo{}, + &StepFlock{}, &StepPrepareDevice{}, &StepCreateVolume{}, &StepAttachVolume{}, diff --git a/builder/amazon/chroot/lockfile.go b/builder/amazon/chroot/lockfile.go new file mode 100644 index 000000000..c9495bef4 --- /dev/null +++ b/builder/amazon/chroot/lockfile.go @@ -0,0 +1,13 @@ +// +build windows + +package chroot + +import "errors" + +func lockFile(*os.File) error { + return errors.New("not supported on Windows") +} + +func unlockFile(f *os.File) error { + return nil +} diff --git a/builder/amazon/chroot/lockfile_unix.go b/builder/amazon/chroot/lockfile_unix.go new file mode 100644 index 000000000..f84ea0d57 --- /dev/null +++ b/builder/amazon/chroot/lockfile_unix.go @@ -0,0 +1,32 @@ +// +build !windows + +package chroot + +import ( + "errors" + "os" + "syscall" +) + +// See: http://linux.die.net/include/sys/file.h +const LOCK_EX = 2 +const LOCK_NB = 4 +const LOCK_UN = 8 + +func lockFile(f *os.File) error { + err := syscall.Flock(int(f.Fd()), LOCK_EX|LOCK_NB) + if err != nil { + errno, ok := err.(syscall.Errno) + if ok && errno == syscall.EWOULDBLOCK { + return errors.New("file already locked") + } + + return err + } + + return nil +} + +func unlockFile(f *os.File) error { + return syscall.Flock(int(f.Fd()), LOCK_UN) +} diff --git a/builder/amazon/chroot/step_flock.go b/builder/amazon/chroot/step_flock.go new file mode 100644 index 000000000..57c07ebba --- /dev/null +++ b/builder/amazon/chroot/step_flock.go @@ -0,0 +1,59 @@ +package chroot + +import ( + "fmt" + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" + "log" + "os" + "path/filepath" +) + +// StepFlock provisions the instance within a chroot. +type StepFlock struct { + fh *os.File +} + +func (s *StepFlock) Run(state map[string]interface{}) multistep.StepAction { + ui := state["ui"].(packer.Ui) + + lockfile := "/var/lock/packer-chroot/lock" + if err := os.MkdirAll(filepath.Dir(lockfile), 0755); err != nil { + err := fmt.Errorf("Error creating lock: %s", err) + state["error"] = err + ui.Error(err.Error()) + return multistep.ActionHalt + } + + log.Printf("Obtaining lock: %s", lockfile) + f, err := os.Create(lockfile) + if err != nil { + err := fmt.Errorf("Error creating lock: %s", err) + state["error"] = err + ui.Error(err.Error()) + return multistep.ActionHalt + } + + // LOCK! + if err := lockFile(f); err != nil { + err := fmt.Errorf("Error creating lock: %s", err) + state["error"] = err + ui.Error(err.Error()) + return multistep.ActionHalt + } + + // Set the file handle, we can't close it because we need to hold + // the lock. + s.fh = f + + return multistep.ActionContinue +} + +func (s *StepFlock) Cleanup(state map[string]interface{}) { + if s.fh == nil { + return + } + + log.Printf("Unlocking: %s", s.fh.Name()) + unlockFile(s.fh) +}