]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/gofmt: don't eat source if -w fails
authorRobert Griesemer <gri@golang.org>
Wed, 9 Nov 2016 23:29:41 +0000 (15:29 -0800)
committerRobert Griesemer <gri@golang.org>
Thu, 10 Nov 2016 00:30:18 +0000 (00:30 +0000)
Write output to a temp file first and only upon success
rename that file to source file name.

Fixes #8984.

Change-Id: Ie40e49d2a4eb3c9462fe769ccbf055b4366eceb0
Reviewed-on: https://go-review.googlesource.com/33018
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
src/cmd/gofmt/gofmt.go

index f29b6cb83d995695b9c606c64716553e51bc8e4a..4cf91336a3beea018e95104ac40713ef266f84fe 100644 (file)
@@ -116,7 +116,7 @@ func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error
                        fmt.Fprintln(out, filename)
                }
                if *write {
-                       err = ioutil.WriteFile(filename, res, 0644)
+                       err = writeFile(filename, res, 0644)
                        if err != nil {
                                return err
                        }
@@ -235,3 +235,35 @@ func diff(b1, b2 []byte) (data []byte, err error) {
        return
 
 }
+
+// writeFile is a drop-in replacement for ioutil.WriteFile;
+// but writeFile writes data to a temporary file first and
+// only upon success renames that file to filename.
+// TODO(gri) This can be removed if #17869 is accepted and
+// implemented.
+func writeFile(filename string, data []byte, perm os.FileMode) error {
+       // open temp file
+       f, err := ioutil.TempFile(filepath.Dir(filename), "tmp")
+       if err != nil {
+               return err
+       }
+       err = f.Chmod(perm)
+       if err != nil {
+               return err
+       }
+       tmpname := f.Name()
+
+       // write data to temp file
+       n, err := f.Write(data)
+       if err == nil && n < len(data) {
+               err = io.ErrShortWrite
+       }
+       if err1 := f.Close(); err == nil {
+               err = err1
+       }
+       if err != nil {
+               return err
+       }
+
+       return os.Rename(tmpname, filename)
+}