storage.go 1.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. package firewall
  2. import (
  3. "errors"
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. )
  8. func stageFile(path string, data []byte) (string, error) {
  9. f, err := os.CreateTemp(filepath.Dir(path), ".debtik-*")
  10. if err != nil {
  11. return "", err
  12. }
  13. name := f.Name()
  14. _, err = f.Write(data)
  15. if err == nil {
  16. err = f.Sync()
  17. }
  18. closed := f.Close()
  19. if err == nil {
  20. err = closed
  21. }
  22. if err != nil {
  23. os.Remove(name)
  24. return "", err
  25. }
  26. return name, nil
  27. }
  28. func writeAtomic(path string, data []byte) error {
  29. name, err := stageFile(path, data)
  30. if err != nil {
  31. return err
  32. }
  33. defer os.Remove(name)
  34. return os.Rename(name, path)
  35. }
  36. func saveFileGroup(files map[string][]byte) error {
  37. staged := map[string]string{}
  38. original := map[string][]byte{}
  39. exists := map[string]bool{}
  40. defer func() {
  41. for _, name := range staged {
  42. os.Remove(name)
  43. }
  44. }()
  45. for path, data := range files {
  46. old, err := os.ReadFile(path)
  47. if err != nil && !os.IsNotExist(err) {
  48. return err
  49. }
  50. exists[path] = err == nil
  51. original[path] = old
  52. name, err := stageFile(path, data)
  53. if err != nil {
  54. return err
  55. }
  56. staged[path] = name
  57. }
  58. var renamed []string
  59. for path, name := range staged {
  60. if err := os.Rename(name, path); err != nil {
  61. failures := []error{err}
  62. for _, oldPath := range renamed {
  63. var restore error
  64. if exists[oldPath] {
  65. restore = writeAtomic(oldPath, original[oldPath])
  66. } else {
  67. restore = os.Remove(oldPath)
  68. }
  69. if restore != nil {
  70. failures = append(failures, fmt.Errorf("restore %s: %w", oldPath, restore))
  71. }
  72. }
  73. return errors.Join(failures...)
  74. }
  75. renamed = append(renamed, path)
  76. }
  77. return nil
  78. }