writeFileAtomic writes data to path via a same-directory temp file that is permissioned, synced, and renamed into place, so a crash can never leave a partial config; the config holds credentials, hence the 0600 perm. After the rename the parent directory is fsynced to persist the directory entry. Th
(path string, data []byte, perm os.FileMode)
| 544 | // final step is skipped on Windows, where directory fsync is unsupported and |
| 545 | // os.Rename already uses replace-existing semantics. |
| 546 | func writeFileAtomic(path string, data []byte, perm os.FileMode) (err error) { |
| 547 | dir := filepath.Dir(path) |
| 548 | tmp, err := os.CreateTemp(dir, ".config-*.tmp") |
| 549 | if err != nil { |
| 550 | return err |
| 551 | } |
| 552 | tmpPath := tmp.Name() |
| 553 | defer func() { |
| 554 | _ = tmp.Close() |
| 555 | if err != nil { |
| 556 | _ = os.Remove(tmpPath) |
| 557 | } |
| 558 | }() |
| 559 | if err = tmp.Chmod(perm); err != nil { |
| 560 | return err |
| 561 | } |
| 562 | if _, err = tmp.Write(data); err != nil { |
| 563 | return err |
| 564 | } |
| 565 | if err = tmp.Sync(); err != nil { |
| 566 | return err |
| 567 | } |
| 568 | if err = tmp.Close(); err != nil { |
| 569 | return err |
| 570 | } |
| 571 | if err = renameFile(tmpPath, path); err != nil { |
| 572 | return err |
| 573 | } |
| 574 | if runtime.GOOS == "windows" { |
| 575 | return nil |
| 576 | } |
| 577 | dirHandle, err := os.Open(dir) |
| 578 | if err != nil { |
| 579 | return err |
| 580 | } |
| 581 | err = dirHandle.Sync() |
| 582 | _ = dirHandle.Close() |
| 583 | return err |
| 584 | } |
| 585 | |
| 586 | var renameFile = os.Rename |
| 587 |