Commit 6cf180b2 authored by Sietse Ringers's avatar Sietse Ringers
Browse files

Save storage files atomically

parent 8b00d1cf
...@@ -3,7 +3,6 @@ package irmago ...@@ -3,7 +3,6 @@ package irmago
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"io"
"io/ioutil" "io/ioutil"
"os" "os"
...@@ -49,17 +48,28 @@ func ensureDirectoryExists(path string) error { ...@@ -49,17 +48,28 @@ func ensureDirectoryExists(path string) error {
return os.Mkdir(path, 0700) return os.Mkdir(path, 0700)
} }
// writeFile writes the contents of reader to a new or truncated file at dest. // Save the filecontents at the specified path atomically:
func writeFile(reader io.Reader, dest string) error { // - first save the content in a temp file with a random filename in the same dir
destfile, err := os.Create(dest) // - then rename the temp file to the specified filepath, overwriting the old file
func saveFile(filepath string, content []byte) (err error) {
dir := path.Dir(filepath)
// Read random data for filename and convert to hex
randBytes := make([]byte, 16)
_, err = rand.Read(randBytes)
if err != nil { if err != nil {
return err return
} }
if _, err := io.Copy(destfile, reader); err != nil { tempfilename := hex.EncodeToString(randBytes)
destfile.Close()
return err // Create temp file
err = ioutil.WriteFile(dir+"/"+tempfilename, content, 0600)
if err != nil {
return
} }
return destfile.Close()
// Rename, overwriting old file
return os.Rename(dir+"/"+tempfilename, filepath)
} }
// NewCredentialManager creates a new CredentialManager that uses the directory // NewCredentialManager creates a new CredentialManager that uses the directory
...@@ -174,7 +184,7 @@ func (cm *CredentialManager) update() error { ...@@ -174,7 +184,7 @@ func (cm *CredentialManager) update() error {
if err != nil { if err != nil {
return err return err
} }
cm.saveFile(cm.path(updatesFile), bytes) saveFile(cm.path(updatesFile), bytes)
return nil return nil
} }
...@@ -184,6 +194,11 @@ func (cm *CredentialManager) path(file string) string { ...@@ -184,6 +194,11 @@ func (cm *CredentialManager) path(file string) string {
} }
func (cm *CredentialManager) signatureFilename(attrs *AttributeList) string { func (cm *CredentialManager) signatureFilename(attrs *AttributeList) string {
// We take the SHA256 hash over all attributes as the filename for the signature.
// This means that the signatures of two credentials that have identical attributes
// will be written to the same file, one overwriting the other - but that doesn't
// matter, because either one of the signatures is valid over both attribute lists,
// so keeping one of them suffices.
return cm.path(signaturesDir) + "/" + attrs.hash() return cm.path(signaturesDir) + "/" + attrs.hash()
} }
...@@ -221,31 +236,7 @@ func (cm *CredentialManager) storeSecretKey(sk *secretKey) error { ...@@ -221,31 +236,7 @@ func (cm *CredentialManager) storeSecretKey(sk *secretKey) error {
if err != nil { if err != nil {
return err return err
} }
return ioutil.WriteFile(cm.path(skFile), bytes, 0600) return saveFile(cm.path(skFile), bytes)
}
// Save the filecontents at the specified path atomically:
// - first save the content in a temp file with a random filename in the same dir
// - then rename the temp file to the specified filepath, overwriting the old file
func (cm *CredentialManager) saveFile(filepath string, content []byte) (err error) {
dir := path.Dir(filepath)
// Read random data for filename and convert to hex
randBytes := make([]byte, 16)
_, err = rand.Read(randBytes)
if err != nil {
return
}
tempfilename := hex.EncodeToString(randBytes)
// Create temp file
err = ioutil.WriteFile(dir+"/"+tempfilename, content, 0600)
if err != nil {
return
}
// Rename, overwriting old file
return os.Rename(dir+"/"+tempfilename, filepath)
} }
func (cm *CredentialManager) storeSignature(cred *credential, counter int) (err error) { func (cm *CredentialManager) storeSignature(cred *credential, counter int) (err error) {
...@@ -259,7 +250,7 @@ func (cm *CredentialManager) storeSignature(cred *credential, counter int) (err ...@@ -259,7 +250,7 @@ func (cm *CredentialManager) storeSignature(cred *credential, counter int) (err
} }
filename := cm.signatureFilename(cred.AttributeList()) filename := cm.signatureFilename(cred.AttributeList())
err = ioutil.WriteFile(filename, credbytes, 0600) err = saveFile(filename, credbytes)
return return
} }
...@@ -272,7 +263,7 @@ func (cm *CredentialManager) storeAttributes() error { ...@@ -272,7 +263,7 @@ func (cm *CredentialManager) storeAttributes() error {
} }
if attrbytes, err := json.Marshal(temp); err == nil { if attrbytes, err := json.Marshal(temp); err == nil {
err = ioutil.WriteFile(cm.path(attributesFile), attrbytes, 0600) err = saveFile(cm.path(attributesFile), attrbytes)
return nil return nil
} else { } else {
return err return err
...@@ -284,7 +275,7 @@ func (cm *CredentialManager) storeKeyshareServers() (err error) { ...@@ -284,7 +275,7 @@ func (cm *CredentialManager) storeKeyshareServers() (err error) {
if err != nil { if err != nil {
return return
} }
err = ioutil.WriteFile(cm.path(kssFile), bts, 0600) err = saveFile(cm.path(kssFile), bts)
return return
} }
...@@ -293,7 +284,7 @@ func (cm *CredentialManager) storePaillierKeys() (err error) { ...@@ -293,7 +284,7 @@ func (cm *CredentialManager) storePaillierKeys() (err error) {
if err != nil { if err != nil {
return return
} }
err = ioutil.WriteFile(cm.path(paillierFile), bts, 0600) err = saveFile(cm.path(paillierFile), bts)
return return
} }
......
...@@ -231,7 +231,11 @@ func (store *ConfigurationStore) Copy(source string, parse bool) error { ...@@ -231,7 +231,11 @@ func (store *ConfigurationStore) Copy(source string, parse bool) error {
return err return err
} }
defer srcfile.Close() defer srcfile.Close()
if err := writeFile(srcfile, store.path+subpath); err != nil { bytes, err := ioutil.ReadAll(srcfile)
if err != nil {
return err
}
if err := saveFile(store.path+subpath, bytes); err != nil {
return err return err
} }
} }
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment