boxdd: Add custom working directory support

This commit is contained in:
世界
2026-08-30 17:41:43 +08:00
parent c66dd4c8ad
commit 5ba8abd0fd
3 changed files with 47 additions and 14 deletions
+5 -5
View File
@@ -2,9 +2,7 @@ package main
import ( import (
"os" "os"
"path/filepath"
"runtime" "runtime"
"strings"
"time" "time"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
@@ -29,10 +27,12 @@ func preparePlatformWorkingDirectory() error {
if listenAddress != "" { if listenAddress != "" {
return os.MkdirAll(workingDirectory, 0o700) return os.MkdirAll(workingDirectory, 0o700)
} }
if !strings.EqualFold(filepath.Clean(workingDirectory), filepath.Clean(defaultServiceWorkingDirectory)) { serviceWorkingDirectory, err := resolveWindowsServiceWorkingDirectory(workingDirectory)
return E.New("the Windows service working directory must be ", defaultServiceWorkingDirectory) if err != nil {
return err
} }
return ensureWindowsWorkingDirectory(workingDirectory) workingDirectory = serviceWorkingDirectory
return ensureWindowsWorkingDirectory(serviceWorkingDirectory)
} }
type windowsService struct{} type windowsService struct{}
+8 -9
View File
@@ -3,7 +3,6 @@ package main
import ( import (
"errors" "errors"
"os" "os"
"path/filepath"
"strings" "strings"
"time" "time"
@@ -65,19 +64,24 @@ func serviceInstall() error {
if err != nil { if err != nil {
return E.Cause(err, "get executable path") return E.Cause(err, "get executable path")
} }
if !strings.EqualFold(filepath.Clean(commandServiceFlagWorkingDirectory), filepath.Clean(defaultServiceWorkingDirectory)) { serviceWorkingDirectory, err := resolveWindowsServiceWorkingDirectory(commandServiceFlagWorkingDirectory)
return E.New("the Windows service working directory must be ", defaultServiceWorkingDirectory) if err != nil {
return E.Cause(err, "validate working directory")
} }
executablePath, err = secureWindowsInstallation(executablePath, commandServiceFlagAllowUnsafeInstallation) executablePath, err = secureWindowsInstallation(executablePath, commandServiceFlagAllowUnsafeInstallation)
if err != nil { if err != nil {
return E.Cause(err, "secure installation") return E.Cause(err, "secure installation")
} }
err = ensureWindowsWorkingDirectory(serviceWorkingDirectory)
if err != nil {
return E.Cause(err, "secure working directory")
}
manager, err := mgr.Connect() manager, err := mgr.Connect()
if err != nil { if err != nil {
return E.Cause(err, "connect to service manager") return E.Cause(err, "connect to service manager")
} }
defer manager.Disconnect() defer manager.Disconnect()
arguments := []string{"run", "--working-directory", defaultServiceWorkingDirectory} arguments := []string{"run", "--working-directory", serviceWorkingDirectory}
config := mgr.Config{ config := mgr.Config{
DisplayName: serviceDisplayName, DisplayName: serviceDisplayName,
Description: serviceDescriptionText, Description: serviceDescriptionText,
@@ -132,11 +136,6 @@ func serviceInstall() error {
rollback() rollback()
return E.Cause(err, "secure service") return E.Cause(err, "secure service")
} }
err = ensureWindowsWorkingDirectory(defaultServiceWorkingDirectory)
if err != nil {
rollback()
return E.Cause(err, "secure working directory")
}
err = eventlog.InstallAsEventCreate(serviceName, eventlog.Error|eventlog.Warning|eventlog.Info) err = eventlog.InstallAsEventCreate(serviceName, eventlog.Error|eventlog.Warning|eventlog.Info)
if err != nil && !strings.Contains(err.Error(), "already exists") { if err != nil && !strings.Contains(err.Error(), "already exists") {
rollback() rollback()
+34
View File
@@ -336,6 +336,40 @@ func validateFixedNTFSVolume(path string) (string, error) {
return filepath.Clean(volumePath), nil return filepath.Clean(volumePath), nil
} }
func resolveWindowsServiceWorkingDirectory(path string) (string, error) {
if path == "" {
return "", E.New("missing daemon working directory")
}
absolutePath, err := filepath.Abs(path)
if err != nil {
return "", E.Cause(err, "resolve daemon working directory")
}
cleanPath := filepath.Clean(absolutePath)
parentPath := filepath.Dir(cleanPath)
parentAttributes, err := windowsFileAttributes(parentPath)
if err != nil {
return "", E.Cause(err, "query daemon working directory parent")
}
if parentAttributes&windows.FILE_ATTRIBUTE_DIRECTORY == 0 {
return "", E.New("daemon working directory parent is not a directory")
}
if parentAttributes&windows.FILE_ATTRIBUTE_REPARSE_POINT != 0 {
return "", E.New("daemon working directory parent is a reparse point")
}
volumeRoot, err := validateFixedNTFSVolume(parentPath)
if err != nil {
return "", E.Cause(err, "validate daemon working directory volume")
}
if strings.EqualFold(cleanPath, filepath.Clean(volumeRoot)) {
return "", E.New("daemon working directory must not be a volume root")
}
err = validateInstallationAncestors(parentPath, volumeRoot, true)
if err != nil {
return "", E.Cause(err, "validate daemon working directory ancestors")
}
return cleanPath, nil
}
func validateInstallationAncestors(path string, volumeRoot string, validatePermissions bool) error { func validateInstallationAncestors(path string, volumeRoot string, validatePermissions bool) error {
currentPath := filepath.Clean(path) currentPath := filepath.Clean(path)
cleanVolumeRoot := filepath.Clean(volumeRoot) cleanVolumeRoot := filepath.Clean(volumeRoot)