* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
-
-#ifndef _WIN32_WINNT
-# define _WIN32_WINNT 0x0600
-#endif
#include "qemu/osdep.h"
+
#include <wtypes.h>
#include <powrprof.h>
#include <winsock2.h>
#include <winioctl.h>
#include <ntddscsi.h>
#include <setupapi.h>
+#include <cfgmgr32.h>
#include <initguid.h>
#endif
#include <lm.h>
#include <wtsapi32.h>
#include <wininet.h>
-#include "qga/guest-agent-core.h"
-#include "qga/vss-win32.h"
-#include "qga-qmp-commands.h"
+#include "guest-agent-core.h"
+#include "vss-win32.h"
+#include "qga-qapi-commands.h"
+#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "qemu/queue.h"
#include "qemu/host-utils.h"
{"a+b", FILE_GENERIC_APPEND|GENERIC_READ, OPEN_ALWAYS }
};
+#define debug_error(msg) do { \
+ char *suffix = g_win32_error_message(GetLastError()); \
+ g_debug("%s: %s", (msg), suffix); \
+ g_free(suffix); \
+} while (0)
+
static OpenFlags *find_open_flag(const char *mode_str)
{
int mode;
int64_t qmp_guest_file_open(const char *path, bool has_mode,
const char *mode, Error **errp)
{
- int64_t fd;
+ int64_t fd = -1;
HANDLE fh;
HANDLE templ_file = NULL;
DWORD share_mode = FILE_SHARE_READ;
DWORD flags_and_attr = FILE_ATTRIBUTE_NORMAL;
LPSECURITY_ATTRIBUTES sa_attr = NULL;
OpenFlags *guest_flags;
+ GError *gerr = NULL;
+ wchar_t *w_path = NULL;
if (!has_mode) {
mode = "r";
guest_flags = find_open_flag(mode);
if (guest_flags == NULL) {
error_setg(errp, "invalid file open mode");
- return -1;
+ goto done;
}
- fh = CreateFile(path, guest_flags->desired_access, share_mode, sa_attr,
+ w_path = g_utf8_to_utf16(path, -1, NULL, NULL, &gerr);
+ if (!w_path) {
+ goto done;
+ }
+
+ fh = CreateFileW(w_path, guest_flags->desired_access, share_mode, sa_attr,
guest_flags->creation_disposition, flags_and_attr,
templ_file);
if (fh == INVALID_HANDLE_VALUE) {
error_setg_win32(errp, GetLastError(), "failed to open file '%s'",
path);
- return -1;
+ goto done;
}
/* set fd non-blocking to avoid common use cases (like reading from a
if (fd < 0) {
CloseHandle(fh);
error_setg(errp, "failed to add handle to qmp handle table");
- return -1;
+ goto done;
}
slog("guest-file-open, handle: % " PRId64, fd);
+
+done:
+ if (gerr) {
+ error_setg(errp, QERR_QGA_COMMAND_FAILED, gerr->message);
+ g_error_free(gerr);
+ }
+ g_free(w_path);
return fd;
}
}
if (!has_count) {
count = QGA_READ_COUNT_DEFAULT;
- } else if (count < 0) {
+ } else if (count < 0 || count >= UINT32_MAX) {
error_setg(errp, "value '%" PRId64
"' is invalid for argument count", count);
return NULL;
#ifdef CONFIG_QGA_NTDDSCSI
-static STORAGE_BUS_TYPE win2qemu[] = {
+static GuestDiskBusType win2qemu[] = {
[BusTypeUnknown] = GUEST_DISK_BUS_TYPE_UNKNOWN,
[BusTypeScsi] = GUEST_DISK_BUS_TYPE_SCSI,
[BusTypeAtapi] = GUEST_DISK_BUS_TYPE_IDE,
[BusTypeFibre] = GUEST_DISK_BUS_TYPE_SSA,
[BusTypeUsb] = GUEST_DISK_BUS_TYPE_USB,
[BusTypeRAID] = GUEST_DISK_BUS_TYPE_RAID,
-#if (_WIN32_WINNT >= 0x0600)
[BusTypeiScsi] = GUEST_DISK_BUS_TYPE_ISCSI,
[BusTypeSas] = GUEST_DISK_BUS_TYPE_SAS,
[BusTypeSata] = GUEST_DISK_BUS_TYPE_SATA,
[BusTypeSd] = GUEST_DISK_BUS_TYPE_SD,
[BusTypeMmc] = GUEST_DISK_BUS_TYPE_MMC,
-#endif
#if (_WIN32_WINNT >= 0x0601)
[BusTypeVirtual] = GUEST_DISK_BUS_TYPE_VIRTUAL,
[BusTypeFileBackedVirtual] = GUEST_DISK_BUS_TYPE_FILE_BACKED_VIRTUAL,
static GuestDiskBusType find_bus_type(STORAGE_BUS_TYPE bus)
{
- if (bus > ARRAY_SIZE(win2qemu) || (int)bus < 0) {
+ if (bus >= ARRAY_SIZE(win2qemu) || (int)bus < 0) {
return GUEST_DISK_BUS_TYPE_UNKNOWN;
}
return win2qemu[(int)bus];
}
-DEFINE_GUID(GUID_DEVINTERFACE_VOLUME,
- 0x53f5630dL, 0xb6bf, 0x11d0, 0x94, 0xf2,
+DEFINE_GUID(GUID_DEVINTERFACE_DISK,
+ 0x53f56307L, 0xb6bf, 0x11d0, 0x94, 0xf2,
+ 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
+DEFINE_GUID(GUID_DEVINTERFACE_STORAGEPORT,
+ 0x2accfe60L, 0xc130, 0x11d2, 0xb0, 0x82,
0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
-static GuestPCIAddress *get_pci_info(char *guid, Error **errp)
+static GuestPCIAddress *get_pci_info(int number, Error **errp)
{
HDEVINFO dev_info;
SP_DEVINFO_DATA dev_info_data;
- DWORD size = 0;
+ SP_DEVICE_INTERFACE_DATA dev_iface_data;
+ HANDLE dev_file;
int i;
- char dev_name[MAX_PATH];
- char *buffer = NULL;
GuestPCIAddress *pci = NULL;
- char *name = g_strdup(&guid[4]);
+ bool partial_pci = false;
- if (!QueryDosDevice(name, dev_name, ARRAY_SIZE(dev_name))) {
- error_setg_win32(errp, GetLastError(), "failed to get dos device name");
- goto out;
- }
+ pci = g_malloc0(sizeof(*pci));
+ pci->domain = -1;
+ pci->slot = -1;
+ pci->function = -1;
+ pci->bus = -1;
- dev_info = SetupDiGetClassDevs(&GUID_DEVINTERFACE_VOLUME, 0, 0,
+ dev_info = SetupDiGetClassDevs(&GUID_DEVINTERFACE_DISK, 0, 0,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (dev_info == INVALID_HANDLE_VALUE) {
error_setg_win32(errp, GetLastError(), "failed to get devices tree");
goto out;
}
+ g_debug("enumerating devices");
dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
+ dev_iface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
for (i = 0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) {
- DWORD addr, bus, slot, func, dev, data, size2;
- while (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data,
- SPDRP_PHYSICAL_DEVICE_OBJECT_NAME,
- &data, (PBYTE)buffer, size,
- &size2)) {
- size = MAX(size, size2);
- if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
- g_free(buffer);
- /* Double the size to avoid problems on
- * W2k MBCS systems per KB 888609.
- * https://support.microsoft.com/en-us/kb/259695 */
- buffer = g_malloc(size * 2);
- } else {
+ PSP_DEVICE_INTERFACE_DETAIL_DATA pdev_iface_detail_data = NULL;
+ STORAGE_DEVICE_NUMBER sdn;
+ char *parent_dev_id = NULL;
+ HDEVINFO parent_dev_info;
+ SP_DEVINFO_DATA parent_dev_info_data;
+ DWORD j;
+ DWORD size = 0;
+
+ g_debug("getting device path");
+ if (SetupDiEnumDeviceInterfaces(dev_info, &dev_info_data,
+ &GUID_DEVINTERFACE_DISK, 0,
+ &dev_iface_data)) {
+ while (!SetupDiGetDeviceInterfaceDetail(dev_info, &dev_iface_data,
+ pdev_iface_detail_data,
+ size, &size,
+ &dev_info_data)) {
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ pdev_iface_detail_data = g_malloc(size);
+ pdev_iface_detail_data->cbSize =
+ sizeof(*pdev_iface_detail_data);
+ } else {
+ error_setg_win32(errp, GetLastError(),
+ "failed to get device interfaces");
+ goto free_dev_info;
+ }
+ }
+
+ dev_file = CreateFile(pdev_iface_detail_data->DevicePath, 0,
+ FILE_SHARE_READ, NULL, OPEN_EXISTING, 0,
+ NULL);
+ g_free(pdev_iface_detail_data);
+
+ if (!DeviceIoControl(dev_file, IOCTL_STORAGE_GET_DEVICE_NUMBER,
+ NULL, 0, &sdn, sizeof(sdn), &size, NULL)) {
+ CloseHandle(dev_file);
error_setg_win32(errp, GetLastError(),
- "failed to get device name");
+ "failed to get device slot number");
goto free_dev_info;
}
- }
- if (g_strcmp0(buffer, dev_name)) {
- continue;
+ CloseHandle(dev_file);
+ if (sdn.DeviceNumber != number) {
+ continue;
+ }
+ } else {
+ error_setg_win32(errp, GetLastError(),
+ "failed to get device interfaces");
+ goto free_dev_info;
}
- /* There is no need to allocate buffer in the next functions. The size
- * is known and ULONG according to
- * https://support.microsoft.com/en-us/kb/253232
- * https://msdn.microsoft.com/en-us/library/windows/hardware/ff543095(v=vs.85).aspx
- */
- if (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data,
- SPDRP_BUSNUMBER, &data, (PBYTE)&bus, size, NULL)) {
- break;
+ g_debug("found device slot %d. Getting storage controller", number);
+ {
+ CONFIGRET cr;
+ DEVINST dev_inst, parent_dev_inst;
+ ULONG dev_id_size = 0;
+
+ size = 0;
+ while (!SetupDiGetDeviceInstanceId(dev_info, &dev_info_data,
+ parent_dev_id, size, &size)) {
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ parent_dev_id = g_malloc(size);
+ } else {
+ error_setg_win32(errp, GetLastError(),
+ "failed to get device instance ID");
+ goto out;
+ }
+ }
+
+ /*
+ * CM API used here as opposed to
+ * SetupDiGetDeviceProperty(..., DEVPKEY_Device_Parent, ...)
+ * which exports are only available in mingw-w64 6+
+ */
+ cr = CM_Locate_DevInst(&dev_inst, parent_dev_id, 0);
+ if (cr != CR_SUCCESS) {
+ g_error("CM_Locate_DevInst failed with code %lx", cr);
+ error_setg_win32(errp, GetLastError(),
+ "failed to get device instance");
+ goto out;
+ }
+ cr = CM_Get_Parent(&parent_dev_inst, dev_inst, 0);
+ if (cr != CR_SUCCESS) {
+ g_error("CM_Get_Parent failed with code %lx", cr);
+ error_setg_win32(errp, GetLastError(),
+ "failed to get parent device instance");
+ goto out;
+ }
+
+ cr = CM_Get_Device_ID_Size(&dev_id_size, parent_dev_inst, 0);
+ if (cr != CR_SUCCESS) {
+ g_error("CM_Get_Device_ID_Size failed with code %lx", cr);
+ error_setg_win32(errp, GetLastError(),
+ "failed to get parent device ID length");
+ goto out;
+ }
+
+ ++dev_id_size;
+ if (dev_id_size > size) {
+ g_free(parent_dev_id);
+ parent_dev_id = g_malloc(dev_id_size);
+ }
+
+ cr = CM_Get_Device_ID(parent_dev_inst, parent_dev_id, dev_id_size,
+ 0);
+ if (cr != CR_SUCCESS) {
+ g_error("CM_Get_Device_ID failed with code %lx", cr);
+ error_setg_win32(errp, GetLastError(),
+ "failed to get parent device ID");
+ goto out;
+ }
}
- /* The function retrieves the device's address. This value will be
- * transformed into device function and number */
- if (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data,
- SPDRP_ADDRESS, &data, (PBYTE)&addr, size, NULL)) {
- break;
+ g_debug("querying storage controller %s for PCI information",
+ parent_dev_id);
+ parent_dev_info =
+ SetupDiGetClassDevs(&GUID_DEVINTERFACE_STORAGEPORT, parent_dev_id,
+ NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+ g_free(parent_dev_id);
+
+ if (parent_dev_info == INVALID_HANDLE_VALUE) {
+ error_setg_win32(errp, GetLastError(),
+ "failed to get parent device");
+ goto out;
}
- /* This call returns UINumber of DEVICE_CAPABILITIES structure.
- * This number is typically a user-perceived slot number. */
- if (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data,
- SPDRP_UI_NUMBER, &data, (PBYTE)&slot, size, NULL)) {
- break;
+ parent_dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
+ if (!SetupDiEnumDeviceInfo(parent_dev_info, 0, &parent_dev_info_data)) {
+ error_setg_win32(errp, GetLastError(),
+ "failed to get parent device data");
+ goto out;
}
- /* SetupApi gives us the same information as driver with
- * IoGetDeviceProperty. According to Microsoft
- * https://support.microsoft.com/en-us/kb/253232
- * FunctionNumber = (USHORT)((propertyAddress) & 0x0000FFFF);
- * DeviceNumber = (USHORT)(((propertyAddress) >> 16) & 0x0000FFFF);
- * SPDRP_ADDRESS is propertyAddress, so we do the same.*/
-
- func = addr & 0x0000FFFF;
- dev = (addr >> 16) & 0x0000FFFF;
- pci = g_malloc0(sizeof(*pci));
- pci->domain = dev;
- pci->slot = slot;
- pci->function = func;
- pci->bus = bus;
+ for (j = 0;
+ SetupDiEnumDeviceInfo(parent_dev_info, j, &parent_dev_info_data);
+ j++) {
+ DWORD addr, bus, ui_slot, type;
+ int func, slot;
+
+ /*
+ * There is no need to allocate buffer in the next functions. The
+ * size is known and ULONG according to
+ * https://msdn.microsoft.com/en-us/library/windows/hardware/ff543095(v=vs.85).aspx
+ */
+ if (!SetupDiGetDeviceRegistryProperty(
+ parent_dev_info, &parent_dev_info_data, SPDRP_BUSNUMBER,
+ &type, (PBYTE)&bus, size, NULL)) {
+ debug_error("failed to get PCI bus");
+ bus = -1;
+ partial_pci = true;
+ }
+
+ /*
+ * The function retrieves the device's address. This value will be
+ * transformed into device function and number
+ */
+ if (!SetupDiGetDeviceRegistryProperty(
+ parent_dev_info, &parent_dev_info_data, SPDRP_ADDRESS,
+ &type, (PBYTE)&addr, size, NULL)) {
+ debug_error("failed to get PCI address");
+ addr = -1;
+ partial_pci = true;
+ }
+
+ /*
+ * This call returns UINumber of DEVICE_CAPABILITIES structure.
+ * This number is typically a user-perceived slot number.
+ */
+ if (!SetupDiGetDeviceRegistryProperty(
+ parent_dev_info, &parent_dev_info_data, SPDRP_UI_NUMBER,
+ &type, (PBYTE)&ui_slot, size, NULL)) {
+ debug_error("failed to get PCI slot");
+ ui_slot = -1;
+ partial_pci = true;
+ }
+
+ /*
+ * SetupApi gives us the same information as driver with
+ * IoGetDeviceProperty. According to Microsoft:
+ *
+ * FunctionNumber = (USHORT)((propertyAddress) & 0x0000FFFF)
+ * DeviceNumber = (USHORT)(((propertyAddress) >> 16) & 0x0000FFFF)
+ * SPDRP_ADDRESS is propertyAddress, so we do the same.
+ *
+ * https://docs.microsoft.com/en-us/windows/desktop/api/setupapi/nf-setupapi-setupdigetdeviceregistrypropertya
+ */
+ if (partial_pci) {
+ pci->domain = -1;
+ pci->slot = -1;
+ pci->function = -1;
+ pci->bus = -1;
+ continue;
+ } else {
+ func = ((int)addr == -1) ? -1 : addr & 0x0000FFFF;
+ slot = ((int)addr == -1) ? -1 : (addr >> 16) & 0x0000FFFF;
+ if ((int)ui_slot != slot) {
+ g_debug("mismatch with reported slot values: %d vs %d",
+ (int)ui_slot, slot);
+ }
+ pci->domain = 0;
+ pci->slot = (int)ui_slot;
+ pci->function = func;
+ pci->bus = (int)bus;
+ break;
+ }
+ }
+ SetupDiDestroyDeviceInfoList(parent_dev_info);
break;
}
free_dev_info:
SetupDiDestroyDeviceInfoList(dev_info);
out:
- g_free(buffer);
- g_free(name);
return pci;
}
-static int get_disk_bus_type(HANDLE vol_h, Error **errp)
+static void get_disk_properties(HANDLE vol_h, GuestDiskAddress *disk,
+ Error **errp)
{
STORAGE_PROPERTY_QUERY query;
STORAGE_DEVICE_DESCRIPTOR *dev_desc, buf;
DWORD received;
+ ULONG size = sizeof(buf);
dev_desc = &buf;
- dev_desc->Size = sizeof(buf);
query.PropertyId = StorageDeviceProperty;
query.QueryType = PropertyStandardQuery;
if (!DeviceIoControl(vol_h, IOCTL_STORAGE_QUERY_PROPERTY, &query,
sizeof(STORAGE_PROPERTY_QUERY), dev_desc,
- dev_desc->Size, &received, NULL)) {
+ size, &received, NULL)) {
error_setg_win32(errp, GetLastError(), "failed to get bus type");
- return -1;
+ return;
+ }
+ disk->bus_type = find_bus_type(dev_desc->BusType);
+ g_debug("bus type %d", disk->bus_type);
+
+ /* Query once more. Now with long enough buffer. */
+ size = dev_desc->Size;
+ dev_desc = g_malloc0(size);
+ if (!DeviceIoControl(vol_h, IOCTL_STORAGE_QUERY_PROPERTY, &query,
+ sizeof(STORAGE_PROPERTY_QUERY), dev_desc,
+ size, &received, NULL)) {
+ error_setg_win32(errp, GetLastError(), "failed to get serial number");
+ g_debug("failed to get serial number");
+ goto out_free;
}
+ if (dev_desc->SerialNumberOffset > 0) {
+ const char *serial;
+ size_t len;
- return dev_desc->BusType;
+ if (dev_desc->SerialNumberOffset >= received) {
+ error_setg(errp, "failed to get serial number: offset outside the buffer");
+ g_debug("serial number offset outside the buffer");
+ goto out_free;
+ }
+ serial = (char *)dev_desc + dev_desc->SerialNumberOffset;
+ len = received - dev_desc->SerialNumberOffset;
+ g_debug("serial number \"%s\"", serial);
+ if (*serial != 0) {
+ disk->serial = g_strndup(serial, len);
+ disk->has_serial = true;
+ }
+ }
+out_free:
+ g_free(dev_desc);
+
+ return;
}
-/* VSS provider works with volumes, thus there is no difference if
- * the volume consist of spanned disks. Info about the first disk in the
- * volume is returned for the spanned disk group (LVM) */
-static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp)
+static void get_single_disk_info(int disk_number,
+ GuestDiskAddress *disk, Error **errp)
{
- GuestDiskAddressList *list = NULL;
- GuestDiskAddress *disk;
SCSI_ADDRESS addr, *scsi_ad;
DWORD len;
- int bus;
- HANDLE vol_h;
+ HANDLE disk_h;
+ Error *local_err = NULL;
scsi_ad = &addr;
- char *name = g_strndup(guid, strlen(guid)-1);
- vol_h = CreateFile(name, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+ g_debug("getting disk info for: %s", disk->dev);
+ disk_h = CreateFile(disk->dev, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
0, NULL);
- if (vol_h == INVALID_HANDLE_VALUE) {
- error_setg_win32(errp, GetLastError(), "failed to open volume");
- goto out_free;
+ if (disk_h == INVALID_HANDLE_VALUE) {
+ error_setg_win32(errp, GetLastError(), "failed to open disk");
+ return;
}
- bus = get_disk_bus_type(vol_h, errp);
- if (bus < 0) {
- goto out_close;
+ get_disk_properties(disk_h, disk, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto err_close;
}
- disk = g_malloc0(sizeof(*disk));
- disk->bus_type = find_bus_type(bus);
- if (bus == BusTypeScsi || bus == BusTypeAta || bus == BusTypeRAID
-#if (_WIN32_WINNT >= 0x0600)
+ g_debug("bus type %d", disk->bus_type);
+ /* always set pci_controller as required by schema. get_pci_info() should
+ * report -1 values for non-PCI buses rather than fail. fail the command
+ * if that doesn't hold since that suggests some other unexpected
+ * breakage
+ */
+ disk->pci_controller = get_pci_info(disk_number, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto err_close;
+ }
+ if (disk->bus_type == GUEST_DISK_BUS_TYPE_SCSI
+ || disk->bus_type == GUEST_DISK_BUS_TYPE_IDE
+ || disk->bus_type == GUEST_DISK_BUS_TYPE_RAID
/* This bus type is not supported before Windows Server 2003 SP1 */
- || bus == BusTypeSas
-#endif
+ || disk->bus_type == GUEST_DISK_BUS_TYPE_SAS
) {
/* We are able to use the same ioctls for different bus types
* according to Microsoft docs
* https://technet.microsoft.com/en-us/library/ee851589(v=ws.10).aspx */
- if (DeviceIoControl(vol_h, IOCTL_SCSI_GET_ADDRESS, NULL, 0, scsi_ad,
+ g_debug("getting SCSI info");
+ if (DeviceIoControl(disk_h, IOCTL_SCSI_GET_ADDRESS, NULL, 0, scsi_ad,
sizeof(SCSI_ADDRESS), &len, NULL)) {
disk->unit = addr.Lun;
disk->target = addr.TargetId;
disk->bus = addr.PathId;
- disk->pci_controller = get_pci_info(name, errp);
}
/* We do not set error in this case, because we still have enough
* information about volume. */
- } else {
- disk->pci_controller = NULL;
}
- list = g_malloc0(sizeof(*list));
- list->value = disk;
- list->next = NULL;
-out_close:
- CloseHandle(vol_h);
-out_free:
+err_close:
+ CloseHandle(disk_h);
+ return;
+}
+
+/* VSS provider works with volumes, thus there is no difference if
+ * the volume consist of spanned disks. Info about the first disk in the
+ * volume is returned for the spanned disk group (LVM) */
+static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp)
+{
+ Error *local_err = NULL;
+ GuestDiskAddressList *list = NULL, *cur_item = NULL;
+ GuestDiskAddress *disk = NULL;
+ int i;
+ HANDLE vol_h;
+ DWORD size;
+ PVOLUME_DISK_EXTENTS extents = NULL;
+
+ /* strip final backslash */
+ char *name = g_strdup(guid);
+ if (g_str_has_suffix(name, "\\")) {
+ name[strlen(name) - 1] = 0;
+ }
+
+ g_debug("opening %s", name);
+ vol_h = CreateFile(name, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+ 0, NULL);
+ if (vol_h == INVALID_HANDLE_VALUE) {
+ error_setg_win32(errp, GetLastError(), "failed to open volume");
+ goto out;
+ }
+
+ /* Get list of extents */
+ g_debug("getting disk extents");
+ size = sizeof(VOLUME_DISK_EXTENTS);
+ extents = g_malloc0(size);
+ if (!DeviceIoControl(vol_h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL,
+ 0, extents, size, &size, NULL)) {
+ DWORD last_err = GetLastError();
+ if (last_err == ERROR_MORE_DATA) {
+ /* Try once more with big enough buffer */
+ g_free(extents);
+ extents = g_malloc0(size);
+ if (!DeviceIoControl(
+ vol_h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL,
+ 0, extents, size, NULL, NULL)) {
+ error_setg_win32(errp, GetLastError(),
+ "failed to get disk extents");
+ goto out;
+ }
+ } else if (last_err == ERROR_INVALID_FUNCTION) {
+ /* Possibly CD-ROM or a shared drive. Try to pass the volume */
+ g_debug("volume not on disk");
+ disk = g_malloc0(sizeof(GuestDiskAddress));
+ disk->has_dev = true;
+ disk->dev = g_strdup(name);
+ get_single_disk_info(0xffffffff, disk, &local_err);
+ if (local_err) {
+ g_debug("failed to get disk info, ignoring error: %s",
+ error_get_pretty(local_err));
+ error_free(local_err);
+ goto out;
+ }
+ list = g_malloc0(sizeof(*list));
+ list->value = disk;
+ disk = NULL;
+ list->next = NULL;
+ goto out;
+ } else {
+ error_setg_win32(errp, GetLastError(),
+ "failed to get disk extents");
+ goto out;
+ }
+ }
+ g_debug("Number of extents: %lu", extents->NumberOfDiskExtents);
+
+ /* Go through each extent */
+ for (i = 0; i < extents->NumberOfDiskExtents; i++) {
+ disk = g_malloc0(sizeof(GuestDiskAddress));
+
+ /* Disk numbers directly correspond to numbers used in UNCs
+ *
+ * See documentation for DISK_EXTENT:
+ * https://docs.microsoft.com/en-us/windows/desktop/api/winioctl/ns-winioctl-_disk_extent
+ *
+ * See also Naming Files, Paths and Namespaces:
+ * https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#win32-device-namespaces
+ */
+ disk->has_dev = true;
+ disk->dev = g_strdup_printf("\\\\.\\PhysicalDrive%lu",
+ extents->Extents[i].DiskNumber);
+
+ get_single_disk_info(extents->Extents[i].DiskNumber, disk, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto out;
+ }
+ cur_item = g_malloc0(sizeof(*list));
+ cur_item->value = disk;
+ disk = NULL;
+ cur_item->next = list;
+ list = cur_item;
+ }
+
+
+out:
+ if (vol_h != INVALID_HANDLE_VALUE) {
+ CloseHandle(vol_h);
+ }
+ qapi_free_GuestDiskAddress(disk);
+ g_free(extents);
g_free(name);
+
return list;
}
char fs_name[32];
char vol_info[MAX_PATH+1];
size_t len;
+ uint64_t i64FreeBytesToCaller, i64TotalBytes, i64FreeBytes;
GuestFilesystemInfo *fs = NULL;
GetVolumePathNamesForVolumeName(guid, (LPCH)&mnt, 0, &info_size);
fs_name[sizeof(fs_name) - 1] = 0;
fs = g_malloc(sizeof(*fs));
fs->name = g_strdup(guid);
+ fs->has_total_bytes = false;
+ fs->has_used_bytes = false;
if (len == 0) {
fs->mountpoint = g_strdup("System Reserved");
} else {
fs->mountpoint = g_strndup(mnt_point, len);
+ if (GetDiskFreeSpaceEx(fs->mountpoint,
+ (PULARGE_INTEGER) & i64FreeBytesToCaller,
+ (PULARGE_INTEGER) & i64TotalBytes,
+ (PULARGE_INTEGER) & i64FreeBytes)) {
+ fs->used_bytes = i64TotalBytes - i64FreeBytes;
+ fs->total_bytes = i64TotalBytes;
+ fs->has_total_bytes = true;
+ fs->has_used_bytes = true;
+ }
}
fs->type = g_strdup(fs_name);
fs->disk = build_guest_disk_info(guid, errp);
* The frozen state is limited for up to 10 seconds by VSS.
*/
int64_t qmp_guest_fsfreeze_freeze(Error **errp)
+{
+ return qmp_guest_fsfreeze_freeze_list(false, NULL, errp);
+}
+
+int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
+ strList *mountpoints,
+ Error **errp)
{
int i;
Error *local_err = NULL;
/* cannot risk guest agent blocking itself on a write in this state */
ga_set_frozen(ga_state);
- qga_vss_fsfreeze(&i, true, &local_err);
+ qga_vss_fsfreeze(&i, true, mountpoints, &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto error;
return 0;
}
-int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
- strList *mountpoints,
- Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
-
- return 0;
-}
-
/*
* Thaw local file systems using Volume Shadow-copy Service.
*/
return 0;
}
- qga_vss_fsfreeze(&i, false, errp);
+ qga_vss_fsfreeze(&i, false, NULL, errp);
ga_unset_frozen(ga_state);
return i;
GuestFilesystemTrimResponse *resp;
HANDLE handle;
WCHAR guid[MAX_PATH] = L"";
+ OSVERSIONINFO osvi;
+ BOOL win8_or_later;
+
+ ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ GetVersionEx(&osvi);
+ win8_or_later = (osvi.dwMajorVersion > 6 ||
+ ((osvi.dwMajorVersion == 6) &&
+ (osvi.dwMinorVersion >= 2)));
+ if (!win8_or_later) {
+ error_setg(errp, "fstrim is only supported for Win8+");
+ return NULL;
+ }
handle = FindFirstVolumeW(guid, ARRAYSIZE(guid));
if (handle == INVALID_HANDLE_VALUE) {
return NULL;
}
-#if (_WIN32_WINNT >= 0x0600)
static int64_t guest_ip_prefix(IP_ADAPTER_UNICAST_ADDRESS *ip_addr)
{
/* For Windows Vista/2008 and newer, use the OnLinkPrefixLength
*/
return ip_addr->OnLinkPrefixLength;
}
-#else
-/* When using the Windows XP and 2003 build environment, do the best we can to
- * figure out the prefix.
- */
-static IP_ADAPTER_INFO *guest_get_adapters_info(void)
-{
- IP_ADAPTER_INFO *adptr_info = NULL;
- ULONG adptr_info_len = 0;
- DWORD ret;
-
- /* Call the first time to get the adptr_info_len. */
- GetAdaptersInfo(adptr_info, &adptr_info_len);
-
- adptr_info = g_malloc(adptr_info_len);
- ret = GetAdaptersInfo(adptr_info, &adptr_info_len);
- if (ret != ERROR_SUCCESS) {
- g_free(adptr_info);
- adptr_info = NULL;
- }
- return adptr_info;
-}
-
-static int64_t guest_ip_prefix(IP_ADAPTER_UNICAST_ADDRESS *ip_addr)
-{
- int64_t prefix = -1; /* Use for AF_INET6 and unknown/undetermined values. */
- IP_ADAPTER_INFO *adptr_info, *info;
- IP_ADDR_STRING *ip;
- struct in_addr *p;
-
- if (ip_addr->Address.lpSockaddr->sa_family != AF_INET) {
- return prefix;
- }
- adptr_info = guest_get_adapters_info();
- if (adptr_info == NULL) {
- return prefix;
- }
-
- /* Match up the passed in ip_addr with one found in adaptr_info.
- * The matching one in adptr_info will have the netmask.
- */
- p = &((struct sockaddr_in *)ip_addr->Address.lpSockaddr)->sin_addr;
- for (info = adptr_info; info; info = info->Next) {
- for (ip = &info->IpAddressList; ip; ip = ip->Next) {
- if (p->S_un.S_addr == inet_addr(ip->IpAddress.String)) {
- prefix = ctpop32(inet_addr(ip->IpMask.String));
- goto out;
- }
- }
- }
-out:
- g_free(adptr_info);
- return prefix;
-}
-#endif
#define INTERFACE_PATH_BUF_SZ 512
return index;
}
}
+
+typedef NETIOAPI_API (WINAPI *GetIfEntry2Func)(PMIB_IF_ROW2 Row);
+
static int guest_get_network_stats(const char *name,
- GuestNetworkInterfaceStat *stats)
+ GuestNetworkInterfaceStat *stats)
{
- DWORD if_index = 0;
- MIB_IFROW a_mid_ifrow;
- memset(&a_mid_ifrow, 0, sizeof(a_mid_ifrow));
- if_index = get_interface_index(name);
- a_mid_ifrow.dwIndex = if_index;
- if (NO_ERROR == GetIfEntry(&a_mid_ifrow)) {
- stats->rx_bytes = a_mid_ifrow.dwInOctets;
- stats->rx_packets = a_mid_ifrow.dwInUcastPkts;
- stats->rx_errs = a_mid_ifrow.dwInErrors;
- stats->rx_dropped = a_mid_ifrow.dwInDiscards;
- stats->tx_bytes = a_mid_ifrow.dwOutOctets;
- stats->tx_packets = a_mid_ifrow.dwOutUcastPkts;
- stats->tx_errs = a_mid_ifrow.dwOutErrors;
- stats->tx_dropped = a_mid_ifrow.dwOutDiscards;
- return 0;
+ OSVERSIONINFO os_ver;
+
+ os_ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ GetVersionEx(&os_ver);
+ if (os_ver.dwMajorVersion >= 6) {
+ MIB_IF_ROW2 a_mid_ifrow;
+ GetIfEntry2Func getifentry2_ex;
+ DWORD if_index = 0;
+ HMODULE module = GetModuleHandle("iphlpapi");
+ PVOID func = GetProcAddress(module, "GetIfEntry2");
+
+ if (func == NULL) {
+ return -1;
+ }
+
+ getifentry2_ex = (GetIfEntry2Func)func;
+ if_index = get_interface_index(name);
+ if (if_index == (DWORD)~0) {
+ return -1;
+ }
+
+ memset(&a_mid_ifrow, 0, sizeof(a_mid_ifrow));
+ a_mid_ifrow.InterfaceIndex = if_index;
+ if (NO_ERROR == getifentry2_ex(&a_mid_ifrow)) {
+ stats->rx_bytes = a_mid_ifrow.InOctets;
+ stats->rx_packets = a_mid_ifrow.InUcastPkts;
+ stats->rx_errs = a_mid_ifrow.InErrors;
+ stats->rx_dropped = a_mid_ifrow.InDiscards;
+ stats->tx_bytes = a_mid_ifrow.OutOctets;
+ stats->tx_packets = a_mid_ifrow.OutUcastPkts;
+ stats->tx_errs = a_mid_ifrow.OutErrors;
+ stats->tx_dropped = a_mid_ifrow.OutDiscards;
+ return 0;
+ }
}
return -1;
}
"guest-set-vcpus",
"guest-get-memory-blocks", "guest-set-memory-blocks",
"guest-get-memory-block-size",
- "guest-fsfreeze-freeze-list",
NULL};
char **p = (char **)list_unsupported;
GuestUserList *qmp_guest_get_users(Error **err)
{
-#if (_WIN32_WINNT >= 0x0600)
#define QGA_NANOSECONDS 10000000
GHashTable *cache = NULL;
}
g_hash_table_destroy(cache);
return head;
-#else
- error_setg(err, QERR_UNSUPPORTED);
- return NULL;
-#endif
}
typedef struct _ga_matrix_lookup_t {
{ 6, 1, "Microsoft Windows Server 2008 R2", "2008r2"},
{ 6, 2, "Microsoft Windows Server 2012", "2012"},
{ 6, 3, "Microsoft Windows Server 2012 R2", "2012r2"},
- {10, 0, "Microsoft Windows Server 2016", "2016"},
+ { 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0}
}
};
+typedef struct _ga_win_10_0_server_t {
+ int final_build;
+ char const *version;
+ char const *version_id;
+} ga_win_10_0_server_t;
+
+static ga_win_10_0_server_t const WIN_10_0_SERVER_VERSION_MATRIX[3] = {
+ {14393, "Microsoft Windows Server 2016", "2016"},
+ {17763, "Microsoft Windows Server 2019", "2019"},
+ {0, 0}
+};
+
static void ga_get_win_version(RTL_OSVERSIONINFOEXW *info, Error **errp)
{
typedef NTSTATUS(WINAPI * rtl_get_version_t)(
{
DWORD major = os_version->dwMajorVersion;
DWORD minor = os_version->dwMinorVersion;
+ DWORD build = os_version->dwBuildNumber;
int tbl_idx = (os_version->wProductType != VER_NT_WORKSTATION);
ga_matrix_lookup_t const *table = WIN_VERSION_MATRIX[tbl_idx];
+ ga_win_10_0_server_t const *win_10_0_table = WIN_10_0_SERVER_VERSION_MATRIX;
while (table->version != NULL) {
- if (major == table->major && minor == table->minor) {
+ if (major == 10 && minor == 0 && tbl_idx) {
+ while (win_10_0_table->version != NULL) {
+ if (build <= win_10_0_table->final_build) {
+ if (id) {
+ return g_strdup(win_10_0_table->version_id);
+ } else {
+ return g_strdup(win_10_0_table->version);
+ }
+ }
+ win_10_0_table++;
+ }
+ } else if (major == table->major && minor == table->minor) {
if (id) {
return g_strdup(table->version_id);
} else {