66 lines
1.3 KiB
Go
66 lines
1.3 KiB
Go
package agent
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
func writeJSONAtomic(path string, value any) error {
|
|
data, err := json.MarshalIndent(value, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
file, err := os.CreateTemp(filepath.Dir(path), ".state-*.tmp")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
tmp := file.Name()
|
|
defer os.Remove(tmp)
|
|
if _, err := file.Write(append(data, '\n')); err != nil {
|
|
_ = file.Close()
|
|
return err
|
|
}
|
|
if err := file.Sync(); err != nil {
|
|
_ = file.Close()
|
|
_ = os.Remove(tmp)
|
|
return err
|
|
}
|
|
if err := file.Close(); err != nil {
|
|
_ = os.Remove(tmp)
|
|
return err
|
|
}
|
|
if err := os.Rename(tmp, path); err != nil {
|
|
_ = os.Remove(tmp)
|
|
return err
|
|
}
|
|
return syncDirectory(filepath.Dir(path))
|
|
}
|
|
|
|
func syncDirectory(path string) error {
|
|
dir, err := os.Open(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
syncErr := dir.Sync()
|
|
return firstError(syncErr, dir.Close())
|
|
}
|
|
|
|
func validateName(name string) error {
|
|
if name == "" || name == "." || name == ".." || strings.ContainsAny(name, `/\\`) || strings.Contains(name, "..") || strings.TrimSpace(name) != name {
|
|
return fmt.Errorf("unsafe file name %q", name)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func firstError(errs ...error) error {
|
|
for _, err := range errs {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|