You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
61 lines
1.7 KiB
61 lines
1.7 KiB
// Package atomicfile persists bounded service snapshots without exposing a
|
|
// partially written destination after a process crash.
|
|
package atomicfile
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
func Write(path string, content []byte, mode os.FileMode) error {
|
|
if path == "" {
|
|
return errors.New("atomic file path must not be empty")
|
|
}
|
|
path = filepath.Clean(path)
|
|
if info, err := os.Lstat(path); err == nil {
|
|
if info.Mode()&os.ModeSymlink != 0 {
|
|
return errors.New("refusing to replace a symbolic-link store path")
|
|
}
|
|
if !info.Mode().IsRegular() {
|
|
return errors.New("refusing to replace a non-regular store path")
|
|
}
|
|
} else if !os.IsNotExist(err) {
|
|
return fmt.Errorf("inspect store path: %w", err)
|
|
}
|
|
|
|
directory := filepath.Dir(path)
|
|
temporary, err := os.CreateTemp(directory, ".tera-store-*")
|
|
if err != nil {
|
|
return fmt.Errorf("create temporary store: %w", err)
|
|
}
|
|
temporaryPath := temporary.Name()
|
|
renamed := false
|
|
defer func() {
|
|
_ = temporary.Close()
|
|
if !renamed {
|
|
_ = os.Remove(temporaryPath)
|
|
}
|
|
}()
|
|
if err := temporary.Chmod(mode.Perm()); err != nil {
|
|
return fmt.Errorf("protect temporary store: %w", err)
|
|
}
|
|
if _, err := temporary.Write(content); err != nil {
|
|
return fmt.Errorf("write temporary store: %w", err)
|
|
}
|
|
if err := temporary.Sync(); err != nil {
|
|
return fmt.Errorf("sync temporary store: %w", err)
|
|
}
|
|
if err := temporary.Close(); err != nil {
|
|
return fmt.Errorf("close temporary store: %w", err)
|
|
}
|
|
if err := os.Rename(temporaryPath, path); err != nil {
|
|
return fmt.Errorf("replace store: %w", err)
|
|
}
|
|
renamed = true
|
|
if err := syncDirectory(directory); err != nil {
|
|
return fmt.Errorf("sync store directory: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|