...

Source file src/github.com/Microsoft/hcsshim/internal/windevice/devicequery.go

Documentation: github.com/Microsoft/hcsshim/internal/windevice

     1  //go:build windows
     2  
     3  package windevice
     4  
     5  import (
     6  	"fmt"
     7  	"strings"
     8  	"unicode/utf16"
     9  
    10  	"github.com/Microsoft/go-winio/pkg/guid"
    11  	"github.com/Microsoft/hcsshim/internal/winapi"
    12  	"github.com/pkg/errors"
    13  )
    14  
    15  const (
    16  	_CM_GETIDLIST_FILTER_BUSRELATIONS uint32 = 0x00000020
    17  
    18  	_CM_LOCATE_DEVNODE_NORMAL uint32 = 0x00000000
    19  
    20  	_DEVPROP_TYPE_STRING      uint32 = 0x00000012
    21  	_DEVPROP_TYPEMOD_LIST     uint32 = 0x00002000
    22  	_DEVPROP_TYPE_STRING_LIST uint32 = (_DEVPROP_TYPE_STRING | _DEVPROP_TYPEMOD_LIST)
    23  
    24  	_DEVPKEY_LOCATIONPATHS_GUID = "a45c254e-df1c-4efd-8020-67d146a850e0"
    25  )
    26  
    27  // getDevPKeyDeviceLocationPaths creates a DEVPROPKEY struct for the
    28  // DEVPKEY_Device_LocationPaths property as defined in devpkey.h
    29  func getDevPKeyDeviceLocationPaths() (*winapi.DevPropKey, error) {
    30  	guid, err := guid.FromString(_DEVPKEY_LOCATIONPATHS_GUID)
    31  	if err != nil {
    32  		return nil, err
    33  	}
    34  	return &winapi.DevPropKey{
    35  		Fmtid: guid,
    36  		// pid value is defined in devpkey.h
    37  		Pid: 37,
    38  	}, nil
    39  }
    40  
    41  func GetDeviceLocationPathsFromIDs(ids []string) ([]string, error) {
    42  	result := []string{}
    43  	devPKeyDeviceLocationPaths, err := getDevPKeyDeviceLocationPaths()
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  	for _, id := range ids {
    48  		var devNodeInst uint32
    49  		err = winapi.CMLocateDevNode(&devNodeInst, id, _CM_LOCATE_DEVNODE_NORMAL)
    50  		if err != nil {
    51  			return nil, errors.Wrapf(err, "failed to locate device node for %s", id)
    52  		}
    53  		propertyType := uint32(0)
    54  		propertyBufferSize := uint32(0)
    55  
    56  		// get the size of the property buffer by querying with a nil buffer and zeroed propertyBufferSize
    57  		err = winapi.CMGetDevNodeProperty(devNodeInst, devPKeyDeviceLocationPaths, &propertyType, nil, &propertyBufferSize, 0)
    58  		if err != nil {
    59  			return nil, errors.Wrapf(err, "failed to get property buffer size of devnode query for %s with", id)
    60  		}
    61  
    62  		// get the property with the resulting propertyBufferSize
    63  		propertyBuffer := make([]uint16, propertyBufferSize/2)
    64  		err = winapi.CMGetDevNodeProperty(devNodeInst, devPKeyDeviceLocationPaths, &propertyType, &propertyBuffer[0], &propertyBufferSize, 0)
    65  		if err != nil {
    66  			return nil, errors.Wrapf(err, "failed to get location path property from device node for %s with", id)
    67  		}
    68  		if propertyType != _DEVPROP_TYPE_STRING_LIST {
    69  			return nil, fmt.Errorf("expected to return property type DEVPROP_TYPE_STRING_LIST %d, instead got %d", _DEVPROP_TYPE_STRING_LIST, propertyType)
    70  		}
    71  		if int(propertyBufferSize/2) > len(propertyBuffer) {
    72  			return nil, fmt.Errorf("location path is too large for the buffer, size in bytes %d", propertyBufferSize)
    73  		}
    74  
    75  		formattedResult, err := convertFirstNullTerminatedValueToString(propertyBuffer[:propertyBufferSize/2])
    76  		if err != nil {
    77  			return nil, err
    78  		}
    79  		result = append(result, formattedResult)
    80  	}
    81  
    82  	return result, nil
    83  }
    84  
    85  // helper function that finds the first \u0000 rune and returns the wide string as a regular go string
    86  func convertFirstNullTerminatedValueToString(buf []uint16) (string, error) {
    87  	r := utf16.Decode(buf)
    88  	converted := string(r)
    89  	zerosIndex := strings.IndexRune(converted, '\u0000')
    90  	if zerosIndex == -1 {
    91  		return "", errors.New("cannot convert value to string, malformed data passed")
    92  	}
    93  	return converted[:zerosIndex], nil
    94  }
    95  
    96  func GetChildrenFromInstanceIDs(parentIDs []string) ([]string, error) {
    97  	var result []string
    98  	for _, id := range parentIDs {
    99  		pszFilterParentID := []byte(id)
   100  		children, err := getDeviceIDList(&pszFilterParentID[0], _CM_GETIDLIST_FILTER_BUSRELATIONS)
   101  		if err != nil {
   102  			return nil, err
   103  		}
   104  		result = append(result, children...)
   105  	}
   106  	return result, nil
   107  }
   108  
   109  func getDeviceIDList(pszFilter *byte, ulFlags uint32) ([]string, error) {
   110  	listLength := uint32(0)
   111  	if err := winapi.CMGetDeviceIDListSize(&listLength, pszFilter, ulFlags); err != nil {
   112  		return nil, err
   113  	}
   114  	if listLength == 0 {
   115  		return []string{}, nil
   116  	}
   117  	buf := make([]byte, listLength)
   118  	if err := winapi.CMGetDeviceIDList(pszFilter, &buf[0], uint32(listLength), ulFlags); err != nil {
   119  		return nil, err
   120  	}
   121  
   122  	return winapi.ConvertStringSetToSlice(buf)
   123  }
   124  

View as plain text