atomicfile.go 1.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. // Package atomicfile contains code related to writing to filesystems
  4. // atomically.
  5. //
  6. // This package should be considered internal; its API is not stable.
  7. package atomicfile // import "tailscale.com/atomicfile"
  8. import (
  9. "fmt"
  10. "os"
  11. "path/filepath"
  12. "runtime"
  13. )
  14. // WriteFile writes data to filename+some suffix, then renames it into filename.
  15. // The perm argument is ignored on Windows. If the target filename already
  16. // exists but is not a regular file, WriteFile returns an error.
  17. func WriteFile(filename string, data []byte, perm os.FileMode) (err error) {
  18. fi, err := os.Stat(filename)
  19. if err == nil && !fi.Mode().IsRegular() {
  20. return fmt.Errorf("%s already exists and is not a regular file", filename)
  21. }
  22. f, err := os.CreateTemp(filepath.Dir(filename), filepath.Base(filename)+".tmp")
  23. if err != nil {
  24. return err
  25. }
  26. tmpName := f.Name()
  27. defer func() {
  28. if err != nil {
  29. f.Close()
  30. os.Remove(tmpName)
  31. }
  32. }()
  33. if _, err := f.Write(data); err != nil {
  34. return err
  35. }
  36. if runtime.GOOS != "windows" {
  37. if err := f.Chmod(perm); err != nil {
  38. return err
  39. }
  40. }
  41. if err := f.Sync(); err != nil {
  42. return err
  43. }
  44. if err := f.Close(); err != nil {
  45. return err
  46. }
  47. return os.Rename(tmpName, filename)
  48. }