aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorebiggers <ebiggers@google.com>2019-09-09 13:41:32 -0700
committerJoseph Richey <joerichey@google.com>2019-09-09 13:41:32 -0700
commit237308a671bd2bbea2059bea9e75cb1272edbdbf (patch)
tree031a6383af9d0d6936c5ae85b998194b060a8c0e
parent6445dad7d66fa6a1867090fcd9602c98863649f6 (diff)
writeDataAtomic() fixes (#140)
* filesystem: ensure data is persisted before returning success Sync the temporary file before renaming it, to ensure that after a crash, the destination file isn't zero-length or otherwise incomplete. Also sync the directory after the rename, to ensure the rename has been persisted before returning success. * filesystem: don't use fixed temporary file name Using a fixed temporary file name in a world-writable sticky directory is problematic since another user can create the file first. Use ioutil.TempFile() to do it properly. It uses O_EXCL under the hood to ensure the file is newly created.
-rw-r--r--filesystem/filesystem.go37
1 files changed, 31 insertions, 6 deletions
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index f4f9201..66b3804 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -250,27 +250,52 @@ func (m *Mount) RemoveAllMetadata() error {
return m.err(os.Rename(m.BaseDir(), temp.BaseDir()))
}
+func syncDirectory(dirPath string) error {
+ dirFile, err := os.Open(dirPath)
+ if err != nil {
+ return err
+ }
+ if err = dirFile.Sync(); err != nil {
+ dirFile.Close()
+ return err
+ }
+ return dirFile.Close()
+}
+
// writeDataAtomic writes the data to the path such that the data is either
// written to stable storage or an error is returned.
func (m *Mount) writeDataAtomic(path string, data []byte) error {
- // Write the file to a temporary file then move into place so that the
- // operation will be atomic.
- tempPath := filepath.Join(filepath.Dir(path), tempPrefix+filepath.Base(path))
- tempFile, err := os.OpenFile(tempPath, os.O_WRONLY|os.O_CREATE, filePermissions)
+ // Write the data to a temporary file, sync it, then rename into place
+ // so that the operation will be atomic.
+ dirPath := filepath.Dir(path)
+ tempFile, err := ioutil.TempFile(dirPath, tempPrefix)
if err != nil {
return err
}
- defer os.Remove(tempPath)
+ defer os.Remove(tempFile.Name())
+ // TempFile() creates the file with mode 0600. Change it to 0644.
+ if err = tempFile.Chmod(filePermissions); err != nil {
+ tempFile.Close()
+ return err
+ }
if _, err = tempFile.Write(data); err != nil {
tempFile.Close()
return err
}
+ if err = tempFile.Sync(); err != nil {
+ tempFile.Close()
+ return err
+ }
if err = tempFile.Close(); err != nil {
return err
}
- return os.Rename(tempPath, path)
+ if err = os.Rename(tempFile.Name(), path); err != nil {
+ return err
+ }
+ // Ensure the rename has been persisted before returning success.
+ return syncDirectory(dirPath)
}
// addMetadata writes the metadata structure to the file with the specified