Merge pull request #22922 from BlackHole1/improve-windows

refactor(machine,wsl): improve operations of Windows API
This commit is contained in:
openshift-merge-bot[bot]
2024-06-27 12:49:35 +00:00
committed by GitHub

View File

@ -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)
} }