Some checks are pending
Security Checks / security (push) Waiting to run
统一 frontend、backend、test、test-log 和 out 目录,更新构建、部署及测试入口;纳入配置管理兼容与首次安全引导修复;排除本机配置、密钥和测试产物。
99 lines
2.1 KiB
Go
99 lines
2.1 KiB
Go
package atomicfile
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
)
|
|
|
|
func Write(path string, data []byte, perm os.FileMode) error {
|
|
directory := filepath.Dir(path)
|
|
root, err := os.OpenRoot(directory)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer root.Close()
|
|
return WriteRoot(root, filepath.Base(path), data, perm)
|
|
}
|
|
|
|
func WriteRoot(root *os.Root, name string, data []byte, perm os.FileMode) error {
|
|
if root == nil || name == "" || name == "." || filepath.IsAbs(name) {
|
|
return fmt.Errorf("invalid atomic file path")
|
|
}
|
|
directory := filepath.Dir(name)
|
|
base := filepath.Base(name)
|
|
token := make([]byte, 16)
|
|
if _, err := rand.Read(token); err != nil {
|
|
return err
|
|
}
|
|
temporaryName := filepath.Join(directory, "."+base+"-"+hex.EncodeToString(token))
|
|
temporary, err := root.OpenFile(temporaryName, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
closed := false
|
|
committed := false
|
|
defer func() {
|
|
if !closed {
|
|
_ = temporary.Close()
|
|
}
|
|
if !committed {
|
|
_ = root.Remove(temporaryName)
|
|
}
|
|
}()
|
|
|
|
if err = temporary.Chmod(perm); err != nil {
|
|
return err
|
|
}
|
|
written, err := temporary.Write(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if written != len(data) {
|
|
return fmt.Errorf("short atomic file write: wrote %d of %d bytes", written, len(data))
|
|
}
|
|
if err = temporary.Sync(); err != nil {
|
|
return err
|
|
}
|
|
if err = temporary.Close(); err != nil {
|
|
closed = true
|
|
return err
|
|
}
|
|
closed = true
|
|
if err = root.Rename(temporaryName, name); err != nil {
|
|
return err
|
|
}
|
|
committed = true
|
|
if err = root.Chmod(name, perm); err != nil {
|
|
return err
|
|
}
|
|
return syncRootDirectory(root, directory)
|
|
}
|
|
|
|
func SyncDirectory(path string) error {
|
|
if runtime.GOOS == "windows" {
|
|
return nil
|
|
}
|
|
root, err := os.OpenRoot(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer root.Close()
|
|
return syncRootDirectory(root, ".")
|
|
}
|
|
|
|
func syncRootDirectory(root *os.Root, name string) error {
|
|
if runtime.GOOS == "windows" {
|
|
return nil
|
|
}
|
|
directory, err := root.Open(name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer directory.Close()
|
|
return directory.Sync()
|
|
}
|