]> ruderich.org/simon Gitweb - nsscash/nsscash.git/commitdiff
nsscash: convert: create file atomically
authorSimon Ruderich <simon@ruderich.org>
Fri, 14 Jun 2019 18:28:29 +0000 (20:28 +0200)
committerSimon Ruderich <simon@ruderich.org>
Fri, 14 Jun 2019 18:28:29 +0000 (20:28 +0200)
Previously the file was first truncated or created and then written to.
Although "convert" is not designed to be used for deployment, it is a
unexpected behavior.

main.go

diff --git a/main.go b/main.go
index a563b511355273983d0a0ea3060a288ad64e456d..5455dfe0b367cc8bf92f2ba20979273a97dce086 100644 (file)
--- a/main.go
+++ b/main.go
@@ -24,6 +24,7 @@ import (
        "io/ioutil"
        "log"
        "os"
+       "path/filepath"
 )
 
 func main() {
@@ -129,21 +130,29 @@ func mainConvert(typ, srcPath, dstPath string) error {
                return fmt.Errorf("unsupported file type %v", t)
        }
 
-       // We must create the file first or deployFile() will abort
-       f, err := os.Create(dstPath)
+       dstDir := filepath.Dir(dstPath)
+       dstName := filepath.Base(dstPath)
+
+       // We must create the file first or deployFile() will abort; this is
+       // ugly because deployFile() already performs an atomic replacement
+       // but the simplest solution with the least duplicate code
+       f, err := ioutil.TempFile(dstDir, "tmp-"+dstName+"-")
        if err != nil {
                return err
        }
+       tmpPath := f.Name()
+       defer os.Remove(tmpPath)
        f.Close()
 
        err = deployFile(&File{
                Type: t,
                Url:  srcPath,
-               Path: dstPath,
+               Path: tmpPath,
                body: x.Bytes(),
        })
        if err != nil {
                return err
        }
-       return nil
+
+       return os.Rename(tmpPath, dstPath)
 }