76 lines
2.1 KiB
Go
76 lines
2.1 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
type configTempFile interface {
|
|
io.Writer
|
|
Name() string
|
|
Chmod(os.FileMode) error
|
|
Sync() error
|
|
Close() error
|
|
}
|
|
|
|
type configSaveOperations struct {
|
|
createTemp func(string, string) (configTempFile, error)
|
|
ownership func(string, string) error
|
|
replace func(string, string) error
|
|
}
|
|
|
|
func defaultConfigSaveOperations() configSaveOperations {
|
|
return configSaveOperations{
|
|
createTemp: func(directory, pattern string) (configTempFile, error) {
|
|
return os.CreateTemp(directory, pattern)
|
|
},
|
|
ownership: applyLinuxServiceOwnership,
|
|
replace: os.Rename,
|
|
}
|
|
}
|
|
|
|
// saveConfigFile 以同目录文件替换作为提交点,提交之后不再执行可能报错的操作。
|
|
// 目录权限和归属调整仍可能留下元数据变化;此处不保证断电后的目录项持久性。
|
|
func saveConfigFile(path string, data []byte, operations configSaveOperations) error {
|
|
directory := filepath.Dir(path)
|
|
temporary, err := operations.createTemp(directory, "."+filepath.Base(path)+"-*.tmp")
|
|
if err != nil {
|
|
return fmt.Errorf("创建临时配置文件失败:%w", err)
|
|
}
|
|
closed, committed := false, false
|
|
defer func() {
|
|
if !closed {
|
|
_ = temporary.Close()
|
|
}
|
|
if !committed {
|
|
_ = os.Remove(temporary.Name())
|
|
}
|
|
}()
|
|
if written, err := temporary.Write(data); err != nil {
|
|
return fmt.Errorf("写入临时配置文件失败:%w", err)
|
|
} else if written != len(data) {
|
|
return fmt.Errorf("写入临时配置文件失败:%w", io.ErrShortWrite)
|
|
}
|
|
if err := temporary.Chmod(0o600); err != nil {
|
|
return fmt.Errorf("设置配置文件权限失败:%w", err)
|
|
}
|
|
if err := operations.ownership(directory, temporary.Name()); err != nil {
|
|
return fmt.Errorf("设置配置文件归属失败:%w", err)
|
|
}
|
|
if err := temporary.Sync(); err != nil {
|
|
return fmt.Errorf("同步临时配置文件失败:%w", err)
|
|
}
|
|
err = temporary.Close()
|
|
closed = true
|
|
if err != nil {
|
|
return fmt.Errorf("关闭临时配置文件失败:%w", err)
|
|
}
|
|
if err := operations.replace(temporary.Name(), path); err != nil {
|
|
return fmt.Errorf("替换配置文件失败:%w", err)
|
|
}
|
|
committed = true
|
|
return nil
|
|
}
|