1 // Copyright 2018 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // Package renameio writes files atomically by renaming temporary files.
16 "honnef.co/go/tools/internal/robustio"
19 const patternSuffix = ".tmp"
21 // Pattern returns a glob pattern that matches the unrenamed temporary files
22 // created when writing to filename.
23 func Pattern(filename string) string {
24 return filepath.Join(filepath.Dir(filename), filepath.Base(filename)+patternSuffix)
27 // WriteFile is like ioutil.WriteFile, but first writes data to an arbitrary
28 // file in the same directory as filename, then renames it atomically to the
31 // That ensures that the final location, if it exists, is always a complete file.
32 func WriteFile(filename string, data []byte, perm os.FileMode) (err error) {
33 return WriteToFile(filename, bytes.NewReader(data), perm)
36 // WriteToFile is a variant of WriteFile that accepts the data as an io.Reader
37 // instead of a slice.
38 func WriteToFile(filename string, data io.Reader, perm os.FileMode) (err error) {
39 f, err := tempFile(filepath.Dir(filename), filepath.Base(filename), perm)
44 // Only call os.Remove on f.Name() if we failed to rename it: otherwise,
45 // some other process may have created a new file with the same name after
53 if _, err := io.Copy(f, data); err != nil {
56 // Sync the file before renaming it: otherwise, after a crash the reader may
57 // observe a 0-length file instead of the actual contents.
58 // See https://golang.org/issue/22397#issuecomment-380831736.
59 if err := f.Sync(); err != nil {
62 if err := f.Close(); err != nil {
66 return robustio.Rename(f.Name(), filename)
69 // tempFile creates a new temporary file with given permission bits.
70 func tempFile(dir, prefix string, perm os.FileMode) (f *os.File, err error) {
71 for i := 0; i < 10000; i++ {
72 name := filepath.Join(dir, prefix+strconv.Itoa(rand.Intn(1000000000))+patternSuffix)
73 f, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, perm)
82 // ReadFile is like ioutil.ReadFile, but on Windows retries spurious errors that
83 // may occur if the file is concurrently replaced.
85 // Errors are classified heuristically and retries are bounded, so even this
86 // function may occasionally return a spurious error on Windows.
87 // If so, the error will likely wrap one of:
88 // - syscall.ERROR_ACCESS_DENIED
89 // - syscall.ERROR_FILE_NOT_FOUND
90 // - internal/syscall/windows.ERROR_SHARING_VIOLATION
91 func ReadFile(filename string) ([]byte, error) {
92 return robustio.ReadFile(filename)