From 3b2b1441ec5feba10700673b815adc8ff385219e Mon Sep 17 00:00:00 2001
From: "Jason T. Greene" <jason.greene@redhat.com>
Date: Mon, 3 Apr 2023 13:25:33 -0500
Subject: [PATCH] Use atomic config writing strategy for podman machine config
 files

Windows: Flush machine config writes before renaming
Windows: Previously this code was changed to improve atomicity by changing
the persitence approach to a two-step process (write + rename).
However, the first-step write operation was not fully flushed,
leading to the possibility of incomplete writes.

[NO NEW TESTS NEEDED]

Signed-off-by: Jason T. Greene <jason.greene@redhat.com>
---
 pkg/machine/qemu/machine.go | 15 ++++++++++++---
 pkg/machine/wsl/machine.go  | 16 +++++++++-------
 2 files changed, 21 insertions(+), 10 deletions(-)

diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go
index fe2e6f1d1c..f39cbd8abc 100644
--- a/pkg/machine/qemu/machine.go
+++ b/pkg/machine/qemu/machine.go
@@ -29,6 +29,7 @@ import (
 	"github.com/containers/podman/v4/pkg/rootless"
 	"github.com/containers/podman/v4/utils"
 	"github.com/containers/storage/pkg/homedir"
+	"github.com/containers/storage/pkg/ioutils"
 	"github.com/digitalocean/go-qemu/qmp"
 	"github.com/docker/go-units"
 	"github.com/sirupsen/logrus"
@@ -1560,14 +1561,22 @@ func (v *MachineVM) writeConfig() error {
 		return err
 	}
 	// Write the JSON file
-	b, err := json.MarshalIndent(v, "", " ")
+	opts := &ioutils.AtomicFileWriterOptions{ExplicitCommit: true}
+	w, err := ioutils.NewAtomicFileWriterWithOpts(v.ConfigPath.GetPath(), 0644, opts)
 	if err != nil {
 		return err
 	}
-	if err := os.WriteFile(v.ConfigPath.GetPath(), b, 0644); err != nil {
+	defer w.Close()
+
+	enc := json.NewEncoder(w)
+	enc.SetIndent("", " ")
+
+	if err := enc.Encode(v); err != nil {
 		return err
 	}
-	return nil
+
+	// Commit the changes to disk if no errors
+	return w.Commit()
 }
 
 // getImageFile wrapper returns the path to the image used
diff --git a/pkg/machine/wsl/machine.go b/pkg/machine/wsl/machine.go
index 760937f160..db14af074a 100644
--- a/pkg/machine/wsl/machine.go
+++ b/pkg/machine/wsl/machine.go
@@ -22,6 +22,7 @@ import (
 	"github.com/containers/podman/v4/pkg/machine"
 	"github.com/containers/podman/v4/utils"
 	"github.com/containers/storage/pkg/homedir"
+	"github.com/containers/storage/pkg/ioutils"
 	"github.com/sirupsen/logrus"
 	"golang.org/x/text/encoding/unicode"
 	"golang.org/x/text/transform"
@@ -450,21 +451,22 @@ func downloadDistro(v *MachineVM, opts machine.InitOptions) error {
 func (v *MachineVM) writeConfig() error {
 	const format = "could not write machine json config: %w"
 	jsonFile := v.ConfigPath
-	tmpFile, err := getConfigPathExt(v.Name, "tmp")
-	if err != nil {
-		return err
-	}
 
-	b, err := json.MarshalIndent(v, "", " ")
+	opts := &ioutils.AtomicFileWriterOptions{ExplicitCommit: true}
+	w, err := ioutils.NewAtomicFileWriterWithOpts(jsonFile, 0644, opts)
 	if err != nil {
 		return fmt.Errorf(format, err)
 	}
+	defer w.Close()
 
-	if err := os.WriteFile(tmpFile, b, 0644); err != nil {
+	enc := json.NewEncoder(w)
+	enc.SetIndent("", " ")
+	if err := enc.Encode(v); err != nil {
 		return fmt.Errorf(format, err)
 	}
 
-	if err := os.Rename(tmpFile, jsonFile); err != nil {
+	// Commit the changes to disk if no error has occurred
+	if err := w.Commit(); err != nil {
 		return fmt.Errorf(format, err)
 	}