Skip to content

Commit 0b743ac

Browse files
authored
fix: don't delete an existing conflist (#2115)
1 parent 1116f7e commit 0b743ac

File tree

2 files changed

+33
-8
lines changed

2 files changed

+33
-8
lines changed

internal/fs/atomic.go

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package fs
22

33
import (
44
"io"
5+
"io/fs"
56
"os"
67
"path"
78

@@ -15,19 +16,39 @@ type AtomicWriter struct {
1516

1617
var _ io.WriteCloser = &AtomicWriter{}
1718

19+
// NewAtomicWriter returns an io.WriteCloser that will write contents to a temp file and move that temp file to the destination filename. If the destination
20+
// filename already exists, this constructor will copy the file to <filename>-old, truncating that file if it already exists.
1821
func NewAtomicWriter(filename string) (*AtomicWriter, error) {
22+
// if a file already exists, copy it to <filname>-old
1923
exists := true
20-
if _, err := os.Stat(filename); err != nil {
21-
if os.IsNotExist(err) {
24+
existingFile, err := os.Open(filename)
25+
if err != nil {
26+
if errors.Is(err, fs.ErrNotExist) {
2227
exists = false
2328
} else {
24-
return nil, errors.Wrap(err, "unable to stat existing file")
29+
return nil, errors.Wrap(err, "error opening existing file")
2530
}
2631
}
2732

2833
if exists {
29-
if err := os.Rename(filename, filename+"-old"); err != nil {
30-
return nil, errors.Wrap(err, "unable to move existing file from destination")
34+
// os.Create truncates existing files so we'll keep overwriting the <filename>-old and not filling up the disc if the
35+
// process calls this over and over again on the same filename (e.g. if CNS uses this for conflist generation and keeps crashing and re-writing)
36+
oldFilename := filename + "-old"
37+
oldFile, createErr := os.Create(oldFilename)
38+
if createErr != nil {
39+
if closeErr := existingFile.Close(); closeErr != nil {
40+
return nil, errors.Wrapf(createErr, "error closing file: [%v] occurred when handling file creation error", closeErr.Error())
41+
}
42+
return nil, errors.Wrapf(createErr, "error creating file %s", oldFilename)
43+
}
44+
45+
// copy the existing file to <filename>-old
46+
if _, err := io.Copy(oldFile, existingFile); err != nil { //nolint:govet // shadowing err is fine here since its encapsulated in the if block
47+
return nil, errors.Wrapf(err, "error copying existing file %s to destination %s", existingFile.Name(), oldFile.Name())
48+
}
49+
50+
if err := existingFile.Close(); err != nil { //nolint:govet // shadowing err is fine here since its encapsulated in the if block
51+
return nil, errors.Wrapf(err, "error closing file %s", existingFile.Name())
3152
}
3253
}
3354

@@ -39,18 +60,20 @@ func NewAtomicWriter(filename string) (*AtomicWriter, error) {
3960
return &AtomicWriter{filename: filename, tempFile: tempFile}, nil
4061
}
4162

63+
// Close closes the temp file handle and moves the temp file to the final destination
4264
func (a *AtomicWriter) Close() error {
4365
if err := a.tempFile.Close(); err != nil {
44-
return errors.Wrap(err, "unable to close temp file")
66+
return errors.Wrapf(err, "unable to close temp file %s", a.tempFile.Name())
4567
}
4668

4769
if err := os.Rename(a.tempFile.Name(), a.filename); err != nil {
48-
return errors.Wrap(err, "unable to move temp file to destination")
70+
return errors.Wrapf(err, "unable to move temp file %s to destination %s", a.tempFile.Name(), a.filename)
4971
}
5072

5173
return nil
5274
}
5375

76+
// Write writes the buffer to the temp file. You must call Close() to complete the move from temp file to dest file
5477
func (a *AtomicWriter) Write(p []byte) (int, error) {
5578
bs, err := a.tempFile.Write(p)
5679
return bs, errors.Wrap(err, "unable to write to temp file")

internal/fs/atmoic_test.go renamed to internal/fs/atomic_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ func TestAtomicWriterFileExists(t *testing.T) {
1414
w, err := fs.NewAtomicWriter(file)
1515
require.NoError(t, err, "error creating atomic writer")
1616

17-
// atomic writer should replace existing file with -old suffix
17+
// atomic writer should copy existing file with -old suffix
18+
_, err = os.Stat(file)
19+
require.NoError(t, err, "error stating existing file")
1820
_, err = os.Stat(file + "-old")
1921
require.NoError(t, err, "error stating old file")
2022

0 commit comments

Comments
 (0)