Mining/pkg/mining/file_utils.go

58 lines
1.4 KiB
Go
Raw Permalink Normal View History

package mining
import (
"fmt"
"os"
"path/filepath"
)
// AtomicWriteFile writes data to a file atomically by writing to a temp file
// first, syncing to disk, then renaming to the target path. This prevents
// corruption if the process is interrupted during write.
func AtomicWriteFile(path string, data []byte, perm os.FileMode) error {
dir := filepath.Dir(path)
// Create temp file in the same directory for atomic rename
tmpFile, err := os.CreateTemp(dir, ".tmp-*")
if err != nil {
return fmt.Errorf("failed to create temp file: %w", err)
}
tmpPath := tmpFile.Name()
// Clean up temp file on error
success := false
defer func() {
if !success {
os.Remove(tmpPath)
}
}()
if _, err := tmpFile.Write(data); err != nil {
tmpFile.Close()
return fmt.Errorf("failed to write temp file: %w", err)
}
// Sync to ensure data is flushed to disk before rename
if err := tmpFile.Sync(); err != nil {
tmpFile.Close()
return fmt.Errorf("failed to sync temp file: %w", err)
}
if err := tmpFile.Close(); err != nil {
return fmt.Errorf("failed to close temp file: %w", err)
}
// Set permissions before rename
if err := os.Chmod(tmpPath, perm); err != nil {
return fmt.Errorf("failed to set file permissions: %w", err)
}
// Atomic rename (on POSIX systems)
if err := os.Rename(tmpPath, path); err != nil {
return fmt.Errorf("failed to rename temp file: %w", err)
}
success = true
return nil
}