package firewall import ( "errors" "fmt" "os" "path/filepath" ) func stageFile(path string, data []byte) (string, error) { f, err := os.CreateTemp(filepath.Dir(path), ".debtik-*") if err != nil { return "", err } name := f.Name() _, err = f.Write(data) if err == nil { err = f.Sync() } closed := f.Close() if err == nil { err = closed } if err != nil { os.Remove(name) return "", err } return name, nil } func writeAtomic(path string, data []byte) error { name, err := stageFile(path, data) if err != nil { return err } defer os.Remove(name) return os.Rename(name, path) } func saveFileGroup(files map[string][]byte) error { staged := map[string]string{} original := map[string][]byte{} exists := map[string]bool{} defer func() { for _, name := range staged { os.Remove(name) } }() for path, data := range files { old, err := os.ReadFile(path) if err != nil && !os.IsNotExist(err) { return err } exists[path] = err == nil original[path] = old name, err := stageFile(path, data) if err != nil { return err } staged[path] = name } var renamed []string for path, name := range staged { if err := os.Rename(name, path); err != nil { failures := []error{err} for _, oldPath := range renamed { var restore error if exists[oldPath] { restore = writeAtomic(oldPath, original[oldPath]) } else { restore = os.Remove(oldPath) } if restore != nil { failures = append(failures, fmt.Errorf("restore %s: %w", oldPath, restore)) } } return errors.Join(failures...) } renamed = append(renamed, path) } return nil }