pkg/private/fpath: subsume AtomicWriteFile
AtomicWriteFile is useful primitive to use throughout the codebase Change-Id: I338fc4505ba20d5aece09ddc257286f46298e083
This commit is contained in:
parent
4d2881b711
commit
2461ccd469
@ -6,9 +6,6 @@ package process
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
@ -18,6 +15,7 @@ import (
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
|
||||
"storj.io/storj/pkg/cfgstruct"
|
||||
"storj.io/storj/private/fpath"
|
||||
)
|
||||
|
||||
// SaveConfigOption is a function that updates the options for SaveConfig.
|
||||
@ -201,7 +199,7 @@ func SaveConfig(cmd *cobra.Command, outfile string, opts ...SaveConfigOption) er
|
||||
lines = append(lines, nil)
|
||||
}
|
||||
|
||||
return errs.Wrap(atomicWrite(outfile, 0600, bytes.Join(lines, nl)))
|
||||
return errs.Wrap(fpath.AtomicWriteFile(outfile, bytes.Join(lines, nl), 0600))
|
||||
}
|
||||
|
||||
// readSourceAnnotation is a helper to return the source annotation or cfgstruct.AnySource if unset.
|
||||
@ -218,30 +216,3 @@ func readBoolAnnotation(flag *pflag.Flag, key string) bool {
|
||||
annotation := flag.Annotations[key]
|
||||
return len(annotation) > 0 && annotation[0] == "true"
|
||||
}
|
||||
|
||||
// atomicWrite is a helper to atomically write the data to the outfile.
|
||||
func atomicWrite(outfile string, mode os.FileMode, data []byte) (err error) {
|
||||
fh, err := ioutil.TempFile(filepath.Dir(outfile), filepath.Base(outfile))
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
err = errs.Combine(err, fh.Close())
|
||||
err = errs.Combine(err, os.Remove(fh.Name()))
|
||||
}
|
||||
}()
|
||||
if _, err := fh.Write(data); err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
if err := fh.Sync(); err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
if err := fh.Close(); err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
if err := os.Rename(fh.Name(), outfile); err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
42
private/fpath/atomic.go
Normal file
42
private/fpath/atomic.go
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package fpath
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/zeebo/errs"
|
||||
)
|
||||
|
||||
// AtomicWriteFile is a helper to atomically write the data to the outfile.
|
||||
func AtomicWriteFile(outfile string, data []byte, mode os.FileMode) (err error) {
|
||||
// TODO: provide better atomicity guarantees, like fsyncing the parent
|
||||
// directory and, on windows, using MoveFileEx with MOVEFILE_WRITE_THROUGH.
|
||||
|
||||
fh, err := ioutil.TempFile(filepath.Dir(outfile), filepath.Base(outfile))
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
err = errs.Combine(err, fh.Close())
|
||||
err = errs.Combine(err, os.Remove(fh.Name()))
|
||||
}
|
||||
}()
|
||||
if _, err := fh.Write(data); err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
if err := fh.Sync(); err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
if err := fh.Close(); err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
if err := os.Rename(fh.Name(), outfile); err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user