renameio.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  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.
  4. // Package renameio writes files atomically by renaming temporary files.
  5. package renameio
  6. import (
  7. "bytes"
  8. "io"
  9. "io/ioutil"
  10. "os"
  11. "path/filepath"
  12. "runtime"
  13. "strings"
  14. "time"
  15. )
  16. const patternSuffix = "*.tmp"
  17. // Pattern returns a glob pattern that matches the unrenamed temporary files
  18. // created when writing to filename.
  19. func Pattern(filename string) string {
  20. return filepath.Join(filepath.Dir(filename), filepath.Base(filename)+patternSuffix)
  21. }
  22. // WriteFile is like ioutil.WriteFile, but first writes data to an arbitrary
  23. // file in the same directory as filename, then renames it atomically to the
  24. // final name.
  25. //
  26. // That ensures that the final location, if it exists, is always a complete file.
  27. func WriteFile(filename string, data []byte) (err error) {
  28. return WriteToFile(filename, bytes.NewReader(data))
  29. }
  30. // WriteToFile is a variant of WriteFile that accepts the data as an io.Reader
  31. // instead of a slice.
  32. func WriteToFile(filename string, data io.Reader) (err error) {
  33. f, err := ioutil.TempFile(filepath.Dir(filename), filepath.Base(filename)+patternSuffix)
  34. if err != nil {
  35. return err
  36. }
  37. defer func() {
  38. // Only call os.Remove on f.Name() if we failed to rename it: otherwise,
  39. // some other process may have created a new file with the same name after
  40. // that.
  41. if err != nil {
  42. f.Close()
  43. os.Remove(f.Name())
  44. }
  45. }()
  46. if _, err := io.Copy(f, data); err != nil {
  47. return err
  48. }
  49. // Sync the file before renaming it: otherwise, after a crash the reader may
  50. // observe a 0-length file instead of the actual contents.
  51. // See https://golang.org/issue/22397#issuecomment-380831736.
  52. if err := f.Sync(); err != nil {
  53. return err
  54. }
  55. if err := f.Close(); err != nil {
  56. return err
  57. }
  58. var start time.Time
  59. for {
  60. err := os.Rename(f.Name(), filename)
  61. if err == nil || runtime.GOOS != "windows" || !strings.HasSuffix(err.Error(), "Access is denied.") {
  62. return err
  63. }
  64. // Windows seems to occasionally trigger spurious "Access is denied" errors
  65. // here (see golang.org/issue/31247). We're not sure why. It's probably
  66. // worth a little extra latency to avoid propagating the spurious errors.
  67. if start.IsZero() {
  68. start = time.Now()
  69. } else if time.Since(start) >= 500*time.Millisecond {
  70. return err
  71. }
  72. time.Sleep(5 * time.Millisecond)
  73. }
  74. }