Skip to content

Commit 9efc7c1

Browse files
committed
Minor.
1 parent b577860 commit 9efc7c1

File tree

1 file changed

+90
-3
lines changed

1 file changed

+90
-3
lines changed

keystore/file.go

Lines changed: 90 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package keystore
22

33
import (
4+
"bytes"
45
"context"
6+
"fmt"
7+
"io"
58
"os"
9+
"path/filepath"
610
)
711

8-
const readWritePerms = os.FileMode(0600)
9-
1012
var _ Storage = &FileStorage{}
1113

1214
// FileStorage implements Storage using a file
@@ -25,5 +27,90 @@ func (f *FileStorage) GetEncryptedKeystore(ctx context.Context) ([]byte, error)
2527
}
2628

2729
func (f *FileStorage) PutEncryptedKeystore(ctx context.Context, encryptedKeystore []byte) error {
28-
return os.WriteFile(f.name, encryptedKeystore, readWritePerms)
30+
return writeFile(f.name, bytes.NewReader(encryptedKeystore))
31+
}
32+
33+
// The atomic write function below is from https://github.com/natefinch/atomic/blob/master/atomic.go under license:
34+
// The MIT License (MIT)
35+
//
36+
// # Copyright (c) 2015 Nate Finch
37+
//
38+
// Permission is hereby granted, free of charge, to any person obtaining a copy
39+
// of this software and associated documentation files (the "Software"), to deal
40+
// in the Software without restriction, including without limitation the rights
41+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
42+
// copies of the Software, and to permit persons to whom the Software is
43+
// furnished to do so, subject to the following conditions:
44+
//
45+
// The above copyright notice and this permission notice shall be included in all
46+
// copies or substantial portions of the Software.
47+
//
48+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
49+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
50+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
51+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
52+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
53+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
54+
// SOFTWARE.
55+
//
56+
// writeFile atomically writes the contents of r to the specified filepath. If
57+
// an error occurs, the target file is guaranteed to be either fully written, or
58+
// not written at all. writeFile overwrites any file that exists at the
59+
// location (but only if the write fully succeeds, otherwise the existing file
60+
// is unmodified).
61+
func writeFile(filename string, r io.Reader) (err error) {
62+
// write to a temp file first, then we'll atomically replace the target file
63+
// with the temp file.
64+
dir, file := filepath.Split(filename)
65+
if dir == "" {
66+
dir = "."
67+
}
68+
69+
f, err := os.CreateTemp(dir, file)
70+
if err != nil {
71+
return fmt.Errorf("cannot create temp file: %v", err)
72+
}
73+
defer func() {
74+
if err != nil {
75+
// Don't leave the temp file lying around on error.
76+
_ = os.Remove(f.Name()) // yes, ignore the error, not much we can do about it.
77+
}
78+
}()
79+
// ensure we always close f. Note that this does not conflict with the
80+
// close below, as close is idempotent.
81+
defer f.Close()
82+
name := f.Name()
83+
if _, err := io.Copy(f, r); err != nil {
84+
return fmt.Errorf("cannot write data to tempfile %q: %v", name, err)
85+
}
86+
// fsync is important, otherwise os.Rename could rename a zero-length file
87+
if err := f.Sync(); err != nil {
88+
return fmt.Errorf("can't flush tempfile %q: %v", name, err)
89+
}
90+
if err := f.Close(); err != nil {
91+
return fmt.Errorf("can't close tempfile %q: %v", name, err)
92+
}
93+
94+
// get the file mode from the original file and use that for the replacement
95+
// file, too.
96+
destInfo, err := os.Stat(filename)
97+
if os.IsNotExist(err) {
98+
// no original file
99+
} else if err != nil {
100+
return err
101+
} else {
102+
sourceInfo, err := os.Stat(name)
103+
if err != nil {
104+
return err
105+
}
106+
if sourceInfo.Mode() != destInfo.Mode() {
107+
if err := os.Chmod(name, destInfo.Mode()); err != nil {
108+
return fmt.Errorf("can't set filemode on tempfile %q: %v", name, err)
109+
}
110+
}
111+
}
112+
if err := os.Rename(name, filename); err != nil {
113+
return fmt.Errorf("cannot replace %q with tempfile %q: %v", filename, name, err)
114+
}
115+
return nil
29116
}

0 commit comments

Comments
 (0)