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) }