mirror of
https://github.com/containers/podman.git
synced 2025-07-04 01:48:28 +08:00
Merge pull request #22922 from BlackHole1/improve-windows
refactor(machine,wsl): improve operations of Windows API
This commit is contained in:
@ -3,7 +3,9 @@
|
|||||||
package wsl
|
package wsl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@ -13,6 +15,7 @@ import (
|
|||||||
"unicode/utf16"
|
"unicode/utf16"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/Microsoft/go-winio"
|
||||||
"github.com/containers/storage/pkg/fileutils"
|
"github.com/containers/storage/pkg/fileutils"
|
||||||
"github.com/containers/storage/pkg/homedir"
|
"github.com/containers/storage/pkg/homedir"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -54,31 +57,26 @@ type TokenPrivileges struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Cleaner to refer to the official OS constant names, and consistent with syscall
|
// Cleaner to refer to the official OS constant names, and consistent with syscall
|
||||||
|
// Ref: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-shellexecuteinfow#members
|
||||||
const (
|
const (
|
||||||
//nolint:stylecheck
|
//nolint:stylecheck
|
||||||
SEE_MASK_NOCLOSEPROCESS = 0x40
|
SEE_MASK_NOCLOSEPROCESS = 0x40
|
||||||
//nolint:stylecheck
|
//nolint:stylecheck
|
||||||
EWX_FORCEIFHUNG = 0x10
|
|
||||||
//nolint:stylecheck
|
|
||||||
EWX_REBOOT = 0x02
|
|
||||||
//nolint:stylecheck
|
|
||||||
EWX_RESTARTAPPS = 0x40
|
|
||||||
//nolint:stylecheck
|
|
||||||
SHTDN_REASON_MAJOR_APPLICATION = 0x00040000
|
|
||||||
//nolint:stylecheck
|
|
||||||
SHTDN_REASON_MINOR_INSTALLATION = 0x00000002
|
|
||||||
//nolint:stylecheck
|
|
||||||
SHTDN_REASON_FLAG_PLANNED = 0x80000000
|
|
||||||
//nolint:stylecheck
|
|
||||||
TOKEN_ADJUST_PRIVILEGES = 0x0020
|
|
||||||
//nolint:stylecheck
|
|
||||||
TOKEN_QUERY = 0x0008
|
|
||||||
//nolint:stylecheck
|
|
||||||
SE_PRIVILEGE_ENABLED = 0x00000002
|
|
||||||
//nolint:stylecheck
|
|
||||||
SE_ERR_ACCESSDENIED = 0x05
|
SE_ERR_ACCESSDENIED = 0x05
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ref: https://learn.microsoft.com/en-us/windows/win32/secauthz/privilege-constants#constants
|
||||||
|
rebootPrivilege = "SeShutdownPrivilege"
|
||||||
|
|
||||||
|
// "Application: Installation (Planned)" A planned restart or shutdown to perform application installation.
|
||||||
|
// ref: https://learn.microsoft.com/en-us/windows/win32/shutdown/system-shutdown-reason-codes
|
||||||
|
rebootReason = windows.SHTDN_REASON_MAJOR_APPLICATION | windows.SHTDN_REASON_MINOR_INSTALLATION | windows.SHTDN_REASON_FLAG_PLANNED
|
||||||
|
|
||||||
|
// ref: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-exitwindowsex#parameters
|
||||||
|
rebootFlags = windows.EWX_REBOOT | windows.EWX_RESTARTAPPS | windows.EWX_FORCEIFHUNG
|
||||||
|
)
|
||||||
|
|
||||||
func winVersionAtLeast(major uint, minor uint, build uint) bool {
|
func winVersionAtLeast(major uint, minor uint, build uint) bool {
|
||||||
var out [3]uint32
|
var out [3]uint32
|
||||||
|
|
||||||
@ -148,7 +146,7 @@ func relaunchElevatedWait() error {
|
|||||||
lpFile: uintptr(unsafe.Pointer(exe)),
|
lpFile: uintptr(unsafe.Pointer(exe)),
|
||||||
lpParameters: uintptr(unsafe.Pointer(arg)),
|
lpParameters: uintptr(unsafe.Pointer(arg)),
|
||||||
lpDirectory: uintptr(unsafe.Pointer(cwd)),
|
lpDirectory: uintptr(unsafe.Pointer(cwd)),
|
||||||
nShow: 1,
|
nShow: syscall.SW_SHOWNORMAL,
|
||||||
}
|
}
|
||||||
info.cbSize = uint32(unsafe.Sizeof(*info))
|
info.cbSize = uint32(unsafe.Sizeof(*info))
|
||||||
procShellExecuteEx := shell32.NewProc("ShellExecuteExW")
|
procShellExecuteEx := shell32.NewProc("ShellExecuteExW")
|
||||||
@ -172,7 +170,7 @@ func relaunchElevatedWait() error {
|
|||||||
case syscall.WAIT_FAILED:
|
case syscall.WAIT_FAILED:
|
||||||
return fmt.Errorf("could not wait for process, failed: %w", err)
|
return fmt.Errorf("could not wait for process, failed: %w", err)
|
||||||
default:
|
default:
|
||||||
return errors.New("could not wait for process, unknown error")
|
return fmt.Errorf("could not wait for process, unknown error. event: %X, err: %v", w, err)
|
||||||
}
|
}
|
||||||
var code uint32
|
var code uint32
|
||||||
if err := syscall.GetExitCodeProcess(handle, &code); err != nil {
|
if err := syscall.GetExitCodeProcess(handle, &code); err != nil {
|
||||||
@ -235,14 +233,6 @@ func reboot() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := addRunOnceRegistryEntry(command); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := obtainShutdownPrivilege(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
message := "To continue the process of enabling WSL, the system needs to reboot. " +
|
message := "To continue the process of enabling WSL, the system needs to reboot. " +
|
||||||
"Alternatively, you can cancel and reboot manually\n\n" +
|
"Alternatively, you can cancel and reboot manually\n\n" +
|
||||||
"After rebooting, please wait a minute or two for podman machine to relaunch and continue installing."
|
"After rebooting, please wait a minute or two for podman machine to relaunch and continue installing."
|
||||||
@ -253,42 +243,17 @@ func reboot() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
user32 := syscall.NewLazyDLL("user32")
|
if err := addRunOnceRegistryEntry(command); err != nil {
|
||||||
procExit := user32.NewProc("ExitWindowsEx")
|
return err
|
||||||
if ret, _, err := procExit.Call(EWX_REBOOT|EWX_RESTARTAPPS|EWX_FORCEIFHUNG,
|
|
||||||
SHTDN_REASON_MAJOR_APPLICATION|SHTDN_REASON_MINOR_INSTALLATION|SHTDN_REASON_FLAG_PLANNED); ret != 1 {
|
|
||||||
return fmt.Errorf("reboot failed: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
if err := winio.RunWithPrivilege(rebootPrivilege, func() error {
|
||||||
}
|
if err := windows.ExitWindowsEx(rebootFlags, rebootReason); err != nil {
|
||||||
|
return fmt.Errorf("execute ExitWindowsEx to reboot system failed: %w", err)
|
||||||
func obtainShutdownPrivilege() error {
|
}
|
||||||
const SeShutdownName = "SeShutdownPrivilege"
|
return nil
|
||||||
|
}); err != nil {
|
||||||
advapi32 := syscall.NewLazyDLL("advapi32")
|
return fmt.Errorf("cannot reboot system: %w", err)
|
||||||
OpenProcessToken := advapi32.NewProc("OpenProcessToken")
|
|
||||||
LookupPrivilegeValue := advapi32.NewProc("LookupPrivilegeValueW")
|
|
||||||
AdjustTokenPrivileges := advapi32.NewProc("AdjustTokenPrivileges")
|
|
||||||
|
|
||||||
proc, _ := syscall.GetCurrentProcess()
|
|
||||||
|
|
||||||
var hToken uintptr
|
|
||||||
if ret, _, err := OpenProcessToken.Call(uintptr(proc), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, uintptr(unsafe.Pointer(&hToken))); ret != 1 {
|
|
||||||
return fmt.Errorf("opening process token: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var privs TokenPrivileges
|
|
||||||
//nolint:staticcheck
|
|
||||||
if ret, _, err := LookupPrivilegeValue.Call(uintptr(0), uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(SeShutdownName))), uintptr(unsafe.Pointer(&(privs.privileges[0].luid)))); ret != 1 {
|
|
||||||
return fmt.Errorf("looking up shutdown privilege: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
privs.privilegeCount = 1
|
|
||||||
privs.privileges[0].attributes = SE_PRIVILEGE_ENABLED
|
|
||||||
|
|
||||||
if ret, _, err := AdjustTokenPrivileges.Call(hToken, 0, uintptr(unsafe.Pointer(&privs)), 0, uintptr(0), 0); ret != 1 {
|
|
||||||
return fmt.Errorf("enabling shutdown privilege on token: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -311,30 +276,25 @@ func addRunOnceRegistryEntry(command string) error {
|
|||||||
|
|
||||||
func encodeUTF16Bytes(s string) []byte {
|
func encodeUTF16Bytes(s string) []byte {
|
||||||
u16 := utf16.Encode([]rune(s))
|
u16 := utf16.Encode([]rune(s))
|
||||||
u16le := make([]byte, len(u16)*2)
|
buf := new(bytes.Buffer)
|
||||||
for i := 0; i < len(u16); i++ {
|
for _, r := range u16 {
|
||||||
u16le[i<<1] = byte(u16[i])
|
_ = binary.Write(buf, binary.LittleEndian, r)
|
||||||
u16le[(i<<1)+1] = byte(u16[i] >> 8)
|
|
||||||
}
|
}
|
||||||
return u16le
|
return buf.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
func MessageBox(caption, title string, fail bool) int {
|
func MessageBox(caption, title string, fail bool) int {
|
||||||
var format int
|
var format uint32
|
||||||
if fail {
|
if fail {
|
||||||
format = 0x10
|
format = windows.MB_ICONERROR
|
||||||
} else {
|
} else {
|
||||||
format = 0x41
|
format = windows.MB_OKCANCEL | windows.MB_ICONINFORMATION
|
||||||
}
|
}
|
||||||
|
|
||||||
user32 := syscall.NewLazyDLL("user32.dll")
|
|
||||||
captionPtr, _ := syscall.UTF16PtrFromString(caption)
|
captionPtr, _ := syscall.UTF16PtrFromString(caption)
|
||||||
titlePtr, _ := syscall.UTF16PtrFromString(title)
|
titlePtr, _ := syscall.UTF16PtrFromString(title)
|
||||||
ret, _, _ := user32.NewProc("MessageBoxW").Call(
|
|
||||||
uintptr(0),
|
ret, _ := windows.MessageBox(0, captionPtr, titlePtr, format)
|
||||||
uintptr(unsafe.Pointer(captionPtr)),
|
|
||||||
uintptr(unsafe.Pointer(titlePtr)),
|
|
||||||
uintptr(format))
|
|
||||||
|
|
||||||
return int(ret)
|
return int(ret)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user