From 1a3c3898c12d4a3d315d8460bb48617e8ef59c2c Mon Sep 17 00:00:00 2001
From: Anil Belur <askb23@gmail.com>
Date: Sun, 29 Nov 2015 09:08:03 +0530
Subject: [PATCH] Fixes #1931

Now this change ensures `config.json` is first written into a temp-file and
rename into the original file, if no errors from the FS. If ENOSPC occurs on write,
when the machine is restarted, the previous `config.json` is still available on startup.

Extended `TestStoreSave()` to check for any residual config files in the
machine directory.

Signed-off-by: Anil Belur <askb23@gmail.com>
---
 libmachine/persist/filestore.go      | 26 +++++++++++++++++++++++++-
 libmachine/persist/filestore_test.go | 12 ++++++++++++
 2 files changed, 37 insertions(+), 1 deletion(-)

diff --git a/libmachine/persist/filestore.go b/libmachine/persist/filestore.go
index 739e9b5fc8..62263dfe8a 100644
--- a/libmachine/persist/filestore.go
+++ b/libmachine/persist/filestore.go
@@ -31,7 +31,31 @@ func (s Filestore) GetMachinesDir() string {
 }
 
 func (s Filestore) saveToFile(data []byte, file string) error {
-	return ioutil.WriteFile(file, data, 0600)
+	if _, err := os.Stat(file); os.IsNotExist(err) {
+		return ioutil.WriteFile(file, data, 0600)
+	}
+
+	tmpfi, err := ioutil.TempFile(filepath.Dir(file), "config.json.tmp")
+	if err != nil {
+		return err
+	}
+	defer os.Remove(tmpfi.Name())
+
+	err = ioutil.WriteFile(tmpfi.Name(), data, 0600)
+	if err != nil {
+		return err
+	}
+
+	err = os.Remove(file)
+	if err != nil {
+		return err
+	}
+
+	err = os.Rename(tmpfi.Name(), file)
+	if err != nil {
+		return err
+	}
+	return nil
 }
 
 func (s Filestore) Save(host *host.Host) error {
diff --git a/libmachine/persist/filestore_test.go b/libmachine/persist/filestore_test.go
index 9928f89a3b..874ecda43b 100644
--- a/libmachine/persist/filestore_test.go
+++ b/libmachine/persist/filestore_test.go
@@ -6,6 +6,7 @@ import (
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"regexp"
 	"testing"
 
 	"github.com/docker/machine/commands/mcndirs"
@@ -52,6 +53,17 @@ func TestStoreSave(t *testing.T) {
 	if _, err := os.Stat(path); os.IsNotExist(err) {
 		t.Fatalf("Host path doesn't exist: %s", path)
 	}
+
+	files, _ := ioutil.ReadDir(path)
+	for _, f := range files {
+		r, err := regexp.Compile("config.json.tmp*")
+		if err != nil {
+			t.Fatalf("Failed to compile regexp string")
+		}
+		if r.MatchString(f.Name()) {
+			t.Fatalf("Failed to remove temp filestore:%s", f.Name())
+		}
+	}
 }
 
 func TestStoreSaveOmitRawDriver(t *testing.T) {