Files
sing-box-extended-mirror/common/windivert/driver_asset_windows.go
T

92 lines
2.1 KiB
Go

//go:build windows
package windivert
import (
"bytes"
"io"
"os"
"path/filepath"
"runtime"
"strconv"
E "github.com/sagernet/sing/common/exceptions"
)
func driverFilePath() (string, error) {
if driverAssetName == "" {
return "", E.New("windivert: unsupported architecture ", runtime.GOARCH)
}
base, err := os.UserCacheDir()
if err != nil {
return "", E.Cause(err, "windivert: locate user cache dir")
}
return filepath.Join(base, "sing-box", "windivert", "v"+AssetVersion, driverAssetName), nil
}
func openVerifiedDriver() (string, *os.File, error) {
target, err := driverFilePath()
if err != nil {
return "", nil, err
}
assetContent, err := driverAsset()
if err != nil {
return "", nil, err
}
err = os.MkdirAll(filepath.Dir(target), 0o755)
if err != nil {
return "", nil, E.Cause(err, "windivert: mkdir ", filepath.Dir(target))
}
var (
sysFile *os.File
content []byte
)
for attempt := 0; ; attempt++ {
sysFile, err = openDriverFile(target)
if err != nil {
if !os.IsNotExist(err) {
return "", nil, E.Cause(err, "windivert: open ", target)
}
err = writeDriverFile(target, assetContent)
if err != nil {
return "", nil, err
}
sysFile, err = openDriverFile(target)
if err != nil {
return "", nil, E.Cause(err, "windivert: open ", target)
}
}
content, err = io.ReadAll(sysFile)
if err != nil {
sysFile.Close()
return "", nil, E.Cause(err, "windivert: read ", target)
}
if bytes.Equal(content, assetContent) {
return target, sysFile, nil
}
sysFile.Close()
if attempt > 0 {
return "", nil, E.New("windivert: driver file ", target, " is being concurrently modified")
}
err = writeDriverFile(target, assetContent)
if err != nil {
return "", nil, err
}
}
}
func writeDriverFile(target string, content []byte) error {
temporaryPath := target + ".tmp-" + strconv.Itoa(os.Getpid())
err := os.WriteFile(temporaryPath, content, 0o644)
if err != nil {
return E.Cause(err, "windivert: write ", filepath.Base(target))
}
err = os.Rename(temporaryPath, target)
if err != nil {
os.Remove(temporaryPath)
return E.Cause(err, "windivert: rename ", filepath.Base(target))
}
return nil
}