mirror of
https://github.com/containers/podman.git
synced 2025-11-29 09:37:38 +08:00
hyperv should reuse hvsock registry entries when possible
Previously, each new HyperV Podman machine required creating new hvsock registry entries, necessitating administrator privileges. This change modifies the HyperV provider to reuse existing hvsock entries if found. This is possible due to Podman's current limitation of running only one HyperV machine at a time. As a result, administrator privileges are only needed for the first initial machine setup (when the registry is empty). Subsequent machines can be created by users in the "Hyper-V Administrators" group without being Admin. Hvsock entries are no longer deleted on each machine removal; cleanup is handled when the last machine gets removed. Signed-off-by: lstocchi <lstocchi@redhat.com>
This commit is contained in:
38
pkg/machine/hyperv/hutil.go
Normal file
38
pkg/machine/hyperv/hutil.go
Normal file
@@ -0,0 +1,38 @@
|
||||
//go:build windows
|
||||
|
||||
package hyperv
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrHypervUserNotInAdminGroup = errors.New("Hyper-V machines require Hyper-V admin rights to be managed. Please add the current user to the Hyper-V Administrators group or run Podman as an administrator")
|
||||
ErrHypervRegistryInitRequiresElevation = errors.New("the first time Podman initializes a Hyper-V machine, it requires admin rights. Please run Podman as an administrator")
|
||||
ErrHypervRegistryRemoveRequiresElevation = errors.New("removing this Hyper-V machine requires admin rights to clean up the Windows Registry. Please run Podman as an administrator")
|
||||
ErrHypervRegistryUpdateRequiresElevation = errors.New("this machine's configuration requires additional Hyper-V networking (hvsock) entries in the Windows Registry. Please run Podman as an administrator")
|
||||
)
|
||||
|
||||
func HasHyperVAdminRights() bool {
|
||||
sid, err := windows.CreateWellKnownSid(windows.WinBuiltinHyperVAdminsSid)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// From MS docs:
|
||||
// "If TokenHandle is NULL, CheckTokenMembership uses the impersonation
|
||||
// token of the calling thread. If the thread is not impersonating,
|
||||
// the function duplicates the thread's primary token to create an
|
||||
// impersonation token."
|
||||
token := windows.Token(0)
|
||||
member, err := token.IsMember(sid)
|
||||
if err != nil {
|
||||
logrus.Warnf("Token Membership Error: %s", err)
|
||||
return false
|
||||
}
|
||||
|
||||
return member
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/containers/podman/v6/pkg/machine/hyperv/vsock"
|
||||
"github.com/containers/podman/v6/pkg/machine/ignition"
|
||||
"github.com/containers/podman/v6/pkg/machine/vmconfigs"
|
||||
"github.com/containers/podman/v6/pkg/machine/windows"
|
||||
"github.com/containers/podman/v6/pkg/systemd/parser"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.podman.io/common/pkg/strongunits"
|
||||
@@ -53,11 +54,56 @@ func (h HyperVStubber) CreateVM(_ define.CreateVMOpts, mc *vmconfigs.MachineConf
|
||||
Memory: uint64(mc.Resources.Memory),
|
||||
}
|
||||
|
||||
networkHVSock, err := vsock.NewHVSockRegistryEntry(mc.Name, vsock.Network)
|
||||
// Allow creation in these two cases:
|
||||
// 1. if the user is Admin
|
||||
// 2. if the user has Hyper-V admin rights and there is at least one *NEW* machine.
|
||||
// *NEW* machines are those created with the vsock entry having the ToolName field.
|
||||
//
|
||||
// This is to prevent a non-admin user from creating the first machine
|
||||
// which would require adding vsock entries into the Windows Registry.
|
||||
if err := h.canCreate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// count number of existing machines, used later to determine if Registry should be cleaned over a failure
|
||||
machines, err := h.countMachinesWithToolname()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Callback to remove any created vsock entries in the Windows Registry if the creation fails
|
||||
removeRegistryEntriesCallBack := func() error {
|
||||
// Allow removal only if user is Admin and this is the first machine created.
|
||||
// If there are already existing machines, the vsock entries should remain.
|
||||
//
|
||||
// There is no need to check for admin rights here as this is already a requirement
|
||||
// to create the first machine and so it would have failed earlier.
|
||||
if machines > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := vsock.RemoveAllHVSockRegistryEntries(); err != nil {
|
||||
return fmt.Errorf("unable to remove hvsock registry entries: %q", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
callbackFuncs.Add(removeRegistryEntriesCallBack)
|
||||
|
||||
// Attempt to load an existing HVSock registry entry for networking.
|
||||
// If no existing entry is found, create a new one.
|
||||
// Creating a new entry requires administrative rights.
|
||||
networkHVSock, err := vsock.LoadHVSockRegistryEntryByPurpose(vsock.Network)
|
||||
if err != nil {
|
||||
if !windows.HasAdminRights() {
|
||||
return ErrHypervRegistryInitRequiresElevation
|
||||
}
|
||||
networkHVSock, err = vsock.NewHVSockRegistryEntry(vsock.Network)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
mc.HyperVHypervisor.NetworkVSock = *networkHVSock
|
||||
|
||||
// Add vsock port numbers to mounts
|
||||
@@ -66,17 +112,6 @@ func (h HyperVStubber) CreateVM(_ define.CreateVMOpts, mc *vmconfigs.MachineConf
|
||||
return err
|
||||
}
|
||||
|
||||
removeShareCallBack := func() error {
|
||||
return removeShares(mc)
|
||||
}
|
||||
callbackFuncs.Add(removeShareCallBack)
|
||||
|
||||
removeRegistrySockets := func() error {
|
||||
removeNetworkAndReadySocketsFromRegistry(mc)
|
||||
return nil
|
||||
}
|
||||
callbackFuncs.Add(removeRegistrySockets)
|
||||
|
||||
netUnitFile, err := createNetworkUnit(mc.HyperVHypervisor.NetworkVSock.Port)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -135,15 +170,23 @@ func (h HyperVStubber) MountVolumesToVM(_ *vmconfigs.MachineConfig, _ bool) erro
|
||||
}
|
||||
|
||||
func (h HyperVStubber) Remove(mc *vmconfigs.MachineConfig) ([]string, func() error, error) {
|
||||
// Allow removal in these two cases:
|
||||
// 1. if the user is Admin
|
||||
// 2. if the user has Hyper-V admin rights and there are 2+ *NEW* machines.
|
||||
// *NEW* machines are those created with the vsock entry having the ToolName field.
|
||||
//
|
||||
// This is to prevent a non-admin user from deleting the last machine
|
||||
// which would require removal of vsock entries from the Windows Registry.
|
||||
if err := h.canRemove(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
_, vm, err := GetVMFromMC(mc)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
rmFunc := func() error {
|
||||
// Tear down vsocks
|
||||
removeNetworkAndReadySocketsFromRegistry(mc)
|
||||
|
||||
// Remove ignition registry entries - not a fatal error
|
||||
// for vm removal
|
||||
// TODO we could improve this by recommending an action be done
|
||||
@@ -152,11 +195,93 @@ func (h HyperVStubber) Remove(mc *vmconfigs.MachineConfig) ([]string, func() err
|
||||
}
|
||||
|
||||
// disk path removal is done by generic remove
|
||||
return vm.Remove("")
|
||||
if err = vm.Remove(""); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// remove vsock registry entries
|
||||
if err := h.removeHvSockFromRegistry(); err != nil {
|
||||
logrus.Errorf("unable to remove hvsock registry entries: %q", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
return []string{}, rmFunc, nil
|
||||
}
|
||||
|
||||
func (h HyperVStubber) canCreate() error {
|
||||
// if admin, can always create
|
||||
if windows.HasAdminRights() {
|
||||
return nil
|
||||
}
|
||||
// not admin, check if there is at least one existing machine
|
||||
// if so, user could create more machines just by being member of the hyperv admin group
|
||||
machines, err := h.countMachinesWithToolname()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// no existing machines, require to be admin
|
||||
if machines == 0 {
|
||||
return ErrHypervRegistryInitRequiresElevation
|
||||
}
|
||||
// at least 1 machine exists
|
||||
// if user is member of the hyperv admin group, allow creation
|
||||
if HasHyperVAdminRights() {
|
||||
return nil
|
||||
}
|
||||
return ErrHypervUserNotInAdminGroup
|
||||
}
|
||||
|
||||
func (h HyperVStubber) canRemove() error {
|
||||
// if admin, can always remove
|
||||
if windows.HasAdminRights() {
|
||||
return nil
|
||||
}
|
||||
// not admin, check if there are multiple machines
|
||||
// if so, user could remove the machine just by being member of the hyperv admin group
|
||||
// (only the last machine removal requires Registry changes)
|
||||
machines, err := h.countMachinesWithToolname()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// more than 1 machine exists, allow removal if user has hyperv admin rights
|
||||
if machines > 1 && HasHyperVAdminRights() {
|
||||
return nil
|
||||
}
|
||||
return ErrHypervRegistryRemoveRequiresElevation
|
||||
}
|
||||
|
||||
// countMachinesWithToolname counts only machines that have a toolname field with value "podman".
|
||||
func (h HyperVStubber) countMachinesWithToolname() (int, error) {
|
||||
dirs, err := env.GetMachineDirs(h.VMType())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
mcs, err := vmconfigs.LoadMachinesInDir(dirs)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
count := 0
|
||||
for _, mc := range mcs {
|
||||
if mc.HyperVHypervisor != nil && mc.HyperVHypervisor.ReadyVsock.ToolName == "podman" {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (h HyperVStubber) removeHvSockFromRegistry() error {
|
||||
// Remove hvsock registry entries only if this is the last machine
|
||||
machines, err := h.countMachinesWithToolname()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if machines > 1 {
|
||||
return nil
|
||||
}
|
||||
return vsock.RemoveAllHVSockRegistryEntries()
|
||||
}
|
||||
|
||||
func (h HyperVStubber) RemoveAndCleanMachines(_ *define.MachineDirs) error {
|
||||
return nil
|
||||
}
|
||||
@@ -347,9 +472,19 @@ func (h HyperVStubber) PrepareIgnition(mc *vmconfigs.MachineConfig, _ *ignition.
|
||||
// simply be derived. So we create the HyperVConfig here.
|
||||
mc.HyperVHypervisor = new(vmconfigs.HyperVConfig)
|
||||
var ignOpts ignition.ReadyUnitOpts
|
||||
readySock, err := vsock.NewHVSockRegistryEntry(mc.Name, vsock.Events)
|
||||
|
||||
// Attempt to load an existing HVSock registry entry for events.
|
||||
// If no existing entry is found, create a new one.
|
||||
// Creating a new entry requires administrative rights.
|
||||
readySock, err := vsock.LoadHVSockRegistryEntryByPurpose(vsock.Events)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if !windows.HasAdminRights() {
|
||||
return nil, ErrHypervRegistryInitRequiresElevation
|
||||
}
|
||||
readySock, err = vsock.NewHVSockRegistryEntry(vsock.Events)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Stopped here ... fails bc mc.Hypervisor is nil ... this can be nil checked prior and created
|
||||
@@ -451,20 +586,6 @@ func resizeDisk(newSize strongunits.GiB, imagePath *define.VMFile) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// removeNetworkAndReadySocketsFromRegistry removes the Network and Ready sockets
|
||||
// from the Windows Registry
|
||||
func removeNetworkAndReadySocketsFromRegistry(mc *vmconfigs.MachineConfig) {
|
||||
// Remove the HVSOCK for networking
|
||||
if err := mc.HyperVHypervisor.NetworkVSock.Remove(); err != nil {
|
||||
logrus.Errorf("unable to remove registry entry for %s: %q", mc.HyperVHypervisor.NetworkVSock.KeyName, err)
|
||||
}
|
||||
|
||||
// Remove the HVSOCK for events
|
||||
if err := mc.HyperVHypervisor.ReadyVsock.Remove(); err != nil {
|
||||
logrus.Errorf("unable to remove registry entry for %s: %q", mc.HyperVHypervisor.ReadyVsock.KeyName, err)
|
||||
}
|
||||
}
|
||||
|
||||
// readAndSplitIgnition reads the ignition file and splits it into key:value pairs
|
||||
func readAndSplitIgnition(mc *vmconfigs.MachineConfig, vm *hypervctl.VirtualMachine) error {
|
||||
ignFile, err := mc.IgnitionFile()
|
||||
|
||||
@@ -12,35 +12,10 @@ import (
|
||||
"github.com/containers/podman/v6/pkg/machine"
|
||||
"github.com/containers/podman/v6/pkg/machine/hyperv/vsock"
|
||||
"github.com/containers/podman/v6/pkg/machine/vmconfigs"
|
||||
"github.com/containers/podman/v6/pkg/machine/windows"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func removeShares(mc *vmconfigs.MachineConfig) error {
|
||||
var removalErr error
|
||||
|
||||
for _, mount := range mc.Mounts {
|
||||
if mount.VSockNumber == nil {
|
||||
// nothing to do if the vsock number was never defined
|
||||
continue
|
||||
}
|
||||
|
||||
vsockReg, err := vsock.LoadHVSockRegistryEntry(*mount.VSockNumber)
|
||||
if err != nil {
|
||||
logrus.Debugf("Vsock %d for mountpoint %s does not have a valid registry entry, skipping removal", *mount.VSockNumber, mount.Target)
|
||||
continue
|
||||
}
|
||||
|
||||
if err := vsockReg.Remove(); err != nil {
|
||||
if removalErr != nil {
|
||||
logrus.Errorf("Error removing vsock: %v", removalErr)
|
||||
}
|
||||
removalErr = fmt.Errorf("removing vsock %d for mountpoint %s: %w", *mount.VSockNumber, mount.Target, err)
|
||||
}
|
||||
}
|
||||
|
||||
return removalErr
|
||||
}
|
||||
|
||||
func startShares(mc *vmconfigs.MachineConfig) error {
|
||||
for _, mount := range mc.Mounts {
|
||||
var args []string
|
||||
@@ -72,11 +47,31 @@ func startShares(mc *vmconfigs.MachineConfig) error {
|
||||
}
|
||||
|
||||
func createShares(mc *vmconfigs.MachineConfig) (err error) {
|
||||
for _, mount := range mc.Mounts {
|
||||
testVsock, err := vsock.NewHVSockRegistryEntry(mc.Name, vsock.Fileserver)
|
||||
if err != nil {
|
||||
return err
|
||||
fileServerVsocks, err := vsock.LoadAllHVSockRegistryEntriesByPurpose(vsock.Fileserver)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load existing file server vsock registry entries: %w", err)
|
||||
}
|
||||
for i, mount := range mc.Mounts {
|
||||
var testVsock *vsock.HVSockRegistryEntry
|
||||
|
||||
// Check if there's an existing file server vsock entry that can be reused for the current mount.
|
||||
if i < len(fileServerVsocks) {
|
||||
testVsock = fileServerVsocks[i]
|
||||
} else {
|
||||
// If no existing vsock entry can be reused, a new one must be created.
|
||||
// Creating a new HVSockRegistryEntry requires administrator privileges.
|
||||
if !windows.HasAdminRights() {
|
||||
if i == 0 {
|
||||
return ErrHypervRegistryInitRequiresElevation
|
||||
}
|
||||
return ErrHypervRegistryUpdateRequiresElevation
|
||||
}
|
||||
testVsock, err = vsock.NewHVSockRegistryEntry(vsock.Fileserver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
mount.VSockNumber = &testVsock.Port
|
||||
logrus.Debugf("Going to share directory %s via 9p on vsock %d", mount.Source, testVsock.Port)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Microsoft/go-winio"
|
||||
@@ -21,6 +22,9 @@ var ErrVSockRegistryEntryExists = errors.New("registry entry already exists")
|
||||
const (
|
||||
// HvsockMachineName is the string identifier for the machine name in a registry entry
|
||||
HvsockMachineName = "MachineName"
|
||||
// HvsockToolName is the string identifier for the tool name in a registry entry
|
||||
HvsockToolName = "ToolName"
|
||||
PodmanToolName = "podman"
|
||||
// HvsockPurpose is the string identifier for the sock purpose in a registry entry
|
||||
HvsockPurpose = "Purpose"
|
||||
// VsockRegistryPath describes the registry path to where the hvsock registry entries live
|
||||
@@ -75,11 +79,19 @@ func openVSockRegistryEntry(entry string) (registry.Key, error) {
|
||||
|
||||
// HVSockRegistryEntry describes a registry entry used in Windows for HVSOCK implementations
|
||||
type HVSockRegistryEntry struct {
|
||||
KeyName string `json:"key_name"`
|
||||
Purpose HVSockPurpose `json:"purpose"`
|
||||
Port uint64 `json:"port"`
|
||||
MachineName string `json:"machineName"`
|
||||
Key registry.Key `json:"key,omitempty"`
|
||||
KeyName string `json:"key_name"`
|
||||
Purpose HVSockPurpose `json:"purpose"`
|
||||
Port uint64 `json:"port"`
|
||||
|
||||
// MachineName is deprecated.
|
||||
// Registry entries are now shared across machines, so a machine-specific identifier isn't appropriate here.
|
||||
MachineName string `json:"machineName,omitempty"`
|
||||
|
||||
// ToolName identifies the application that created this registry entry (e.g., Podman).
|
||||
// This provides information about the entry's origin and can be used for filter entries
|
||||
// if purpose is not enough.
|
||||
ToolName string `json:"creator_tool,omitempty"`
|
||||
Key registry.Key `json:"key,omitempty"`
|
||||
}
|
||||
|
||||
// Add creates a new Windows registry entry with string values from the
|
||||
@@ -117,7 +129,7 @@ func (hv *HVSockRegistryEntry) Add() error {
|
||||
if err := newKey.SetStringValue(HvsockPurpose, hv.Purpose.string()); err != nil {
|
||||
return err
|
||||
}
|
||||
return newKey.SetStringValue(HvsockMachineName, hv.MachineName)
|
||||
return newKey.SetStringValue(HvsockToolName, hv.ToolName)
|
||||
}
|
||||
|
||||
// Remove deletes the registry key and its string values
|
||||
@@ -136,8 +148,8 @@ func (hv *HVSockRegistryEntry) validate() error {
|
||||
if len(hv.Purpose.string()) < 1 {
|
||||
return errors.New("required field purpose is empty")
|
||||
}
|
||||
if len(hv.MachineName) < 1 {
|
||||
return errors.New("required field machinename is empty")
|
||||
if len(hv.ToolName) < 1 {
|
||||
return errors.New("required field toolName is empty")
|
||||
}
|
||||
if len(hv.KeyName) < 1 {
|
||||
return errors.New("required field keypath is empty")
|
||||
@@ -188,7 +200,7 @@ func findOpenHVSockPort() (uint64, error) {
|
||||
|
||||
// NewHVSockRegistryEntry is a constructor to make a new registry entry in Windows. After making the new
|
||||
// object, you must call the add() method to *actually* add it to the Windows registry.
|
||||
func NewHVSockRegistryEntry(machineName string, purpose HVSockPurpose) (*HVSockRegistryEntry, error) {
|
||||
func NewHVSockRegistryEntry(purpose HVSockPurpose) (*HVSockRegistryEntry, error) {
|
||||
// a so-called wildcard entry ... everything from FACB -> 6D3 is MS special sauce
|
||||
// for a " linux vm". this first segment is hexi for the hvsock port number
|
||||
// 00000400-FACB-11E6-BD58-64006A7986D3
|
||||
@@ -197,10 +209,10 @@ func NewHVSockRegistryEntry(machineName string, purpose HVSockPurpose) (*HVSockR
|
||||
return nil, err
|
||||
}
|
||||
r := HVSockRegistryEntry{
|
||||
KeyName: portToKeyName(port),
|
||||
Purpose: purpose,
|
||||
Port: port,
|
||||
MachineName: machineName,
|
||||
KeyName: portToKeyName(port),
|
||||
Purpose: purpose,
|
||||
Port: port,
|
||||
ToolName: PodmanToolName,
|
||||
}
|
||||
if err := r.Add(); err != nil {
|
||||
return nil, err
|
||||
@@ -232,16 +244,11 @@ func LoadHVSockRegistryEntry(port uint64) (*HVSockRegistryEntry, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
machineName, _, err := k.GetStringValue(HvsockMachineName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &HVSockRegistryEntry{
|
||||
KeyName: keyName,
|
||||
Purpose: purpose,
|
||||
Port: port,
|
||||
MachineName: machineName,
|
||||
Key: k,
|
||||
KeyName: keyName,
|
||||
Purpose: purpose,
|
||||
Port: port,
|
||||
Key: k,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -274,3 +281,157 @@ func (hv *HVSockRegistryEntry) ListenSetupWait() (func() error, io.Closer, error
|
||||
return <-errChan
|
||||
}, listener, nil
|
||||
}
|
||||
|
||||
// loadAllHVSockRegistryEntries loads HVSock registry entries, filtered by purpose and optionally limited by size.
|
||||
// If limit is -1, it returns all matching entries. Otherwise, it returns up to 'limit' entries.
|
||||
// The caller is responsible for closing the registry.Key in each returned HVSockRegistryEntry.
|
||||
// Non-matching or excess keys are closed within this function.
|
||||
func loadHVSockRegistryEntries(purpose HVSockPurpose, limit int) ([]*HVSockRegistryEntry, error) {
|
||||
parentKey, err := registry.OpenKey(registry.LOCAL_MACHINE, VsockRegistryPath, registry.ENUMERATE_SUB_KEYS)
|
||||
if err != nil {
|
||||
logrus.Errorf("failed to open registry key: %s: %v", VsockRegistryPath, err)
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err := parentKey.Close(); err != nil {
|
||||
logrus.Errorf("failed to close registry key: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
subKeyNames, err := parentKey.ReadSubKeyNames(-1)
|
||||
if err != nil {
|
||||
logrus.Errorf("failed to read subkey names from %s: %v", VsockRegistryPath, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
allEntries := []*HVSockRegistryEntry{}
|
||||
for _, subKeyName := range subKeyNames {
|
||||
if limit != -1 && len(allEntries) >= limit {
|
||||
break
|
||||
}
|
||||
|
||||
fqPath := fmt.Sprintf("%s\\%s", VsockRegistryPath, subKeyName)
|
||||
k, err := openVSockRegistryEntry(fqPath)
|
||||
if err != nil {
|
||||
logrus.Debugf("Could not open registry entry %s: %v", fqPath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
p, _, err := k.GetStringValue(HvsockPurpose)
|
||||
if err != nil {
|
||||
logrus.Debugf("Could not read purpose from registry entry %s: %v", fqPath, err)
|
||||
k.Close()
|
||||
continue
|
||||
}
|
||||
|
||||
toolName, _, err := k.GetStringValue(HvsockToolName)
|
||||
if err != nil {
|
||||
logrus.Debugf("Could not read tool name from registry entry %s: %v", fqPath, err)
|
||||
k.Close()
|
||||
continue
|
||||
}
|
||||
|
||||
k.Close()
|
||||
|
||||
entryPurpose, err := toHVSockPurpose(p)
|
||||
if err != nil {
|
||||
logrus.Debugf("Could not convert purpose string %q for entry %s: %v", p, fqPath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if !entryPurpose.Equal(purpose.string()) {
|
||||
continue
|
||||
}
|
||||
|
||||
if toolName != PodmanToolName {
|
||||
continue
|
||||
}
|
||||
|
||||
parts := strings.Split(subKeyName, "-")
|
||||
if len(parts) == 0 {
|
||||
logrus.Debugf("Malformed key name %s: cannot extract port", subKeyName)
|
||||
continue
|
||||
}
|
||||
|
||||
portHex := parts[0]
|
||||
port, err := parseHexToUint64(portHex)
|
||||
if err != nil {
|
||||
logrus.Debugf("Could not parse port from key name %s: %v", subKeyName, err)
|
||||
continue
|
||||
}
|
||||
|
||||
allEntries = append(allEntries, &HVSockRegistryEntry{
|
||||
KeyName: subKeyName,
|
||||
Purpose: entryPurpose,
|
||||
Port: port,
|
||||
Key: k,
|
||||
ToolName: PodmanToolName,
|
||||
})
|
||||
}
|
||||
|
||||
return allEntries, nil
|
||||
}
|
||||
|
||||
func LoadHVSockRegistryEntryByPurpose(purpose HVSockPurpose) (*HVSockRegistryEntry, error) {
|
||||
entries, err := loadHVSockRegistryEntries(purpose, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(entries) != 1 {
|
||||
return nil, fmt.Errorf("no hvsock registry entry found for purpose: %s", purpose.string())
|
||||
}
|
||||
|
||||
return entries[0], nil
|
||||
}
|
||||
|
||||
func LoadAllHVSockRegistryEntriesByPurpose(purpose HVSockPurpose) ([]*HVSockRegistryEntry, error) {
|
||||
entries, err := loadHVSockRegistryEntries(purpose, -1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
func parseHexToUint64(hex string) (uint64, error) {
|
||||
return strconv.ParseUint(hex, 16, 64)
|
||||
}
|
||||
|
||||
// It removes HVSock registry entries for Network, Events, and Fileserver.
|
||||
// It returns loading errors immediately. For removals, it attempts all, logs individual failures,
|
||||
// and returns a joined error (via errors.Join) if any occur.
|
||||
// Returns nil only if all entries are loaded and removed successfully.
|
||||
func RemoveAllHVSockRegistryEntries() error {
|
||||
// Tear down vsocks
|
||||
networkSocks, err := LoadAllHVSockRegistryEntriesByPurpose(Network)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
eventsSocks, err := LoadAllHVSockRegistryEntriesByPurpose(Events)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fileserverSocks, err := LoadAllHVSockRegistryEntriesByPurpose(Fileserver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
allSocks := []*HVSockRegistryEntry{}
|
||||
allSocks = append(allSocks, networkSocks...)
|
||||
allSocks = append(allSocks, eventsSocks...)
|
||||
allSocks = append(allSocks, fileserverSocks...)
|
||||
|
||||
var removalErrors []error
|
||||
for _, sock := range allSocks {
|
||||
if err := sock.Remove(); err != nil {
|
||||
logrus.Errorf("unable to remove registry entry for %s: %q", sock.KeyName, err)
|
||||
removalErrors = append(removalErrors, fmt.Errorf("failed to remove sock %s: %w", sock.KeyName, err))
|
||||
}
|
||||
}
|
||||
|
||||
if len(removalErrors) > 0 {
|
||||
return errors.Join(removalErrors...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -39,9 +39,6 @@ func GetByVMType(resolvedVMType define.VMType) (vmconfigs.VMProvider, error) {
|
||||
case define.WSLVirt:
|
||||
return new(wsl.WSLStubber), nil
|
||||
case define.HyperVVirt:
|
||||
if !windows.HasAdminRights() {
|
||||
return nil, fmt.Errorf("hyperv machines require admin authority")
|
||||
}
|
||||
return new(hyperv.HyperVStubber), nil
|
||||
default:
|
||||
}
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
package windows
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrHypervRequiresAdmin = errors.New("Hyper-V machines require admin rights to run. Please run Podman as an administrator")
|
||||
)
|
||||
|
||||
func HasAdminRights() bool {
|
||||
var sid *windows.SID
|
||||
|
||||
|
||||
Reference in New Issue
Block a user