aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--qga/commands-win32.c126
1 files changed, 108 insertions, 18 deletions
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 21a88d31e1..90432bbfce 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -491,9 +491,26 @@ static GuestDiskBusType find_bus_type(STORAGE_BUS_TYPE bus)
return win2qemu[(int)bus];
}
+/* XXX: The following function is BROKEN!
+ *
+ * It does not work and probably has never worked. When we query for list of
+ * disks we get cryptic names like "\Device\0000001d" instead of
+ * "\PhysicalDriveX" or "\HarddiskX". Whether the names can be translated one
+ * way or the other for comparison is an open question.
+ *
+ * When we query volume names (the original version) we are able to match those
+ * but then the property queries report error "Invalid function". (duh!)
+ */
+
+/*
DEFINE_GUID(GUID_DEVINTERFACE_VOLUME,
0x53f5630dL, 0xb6bf, 0x11d0, 0x94, 0xf2,
0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
+*/
+DEFINE_GUID(GUID_DEVINTERFACE_DISK,
+ 0x53f56307L, 0xb6bf, 0x11d0, 0x94, 0xf2,
+ 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
+
static GuestPCIAddress *get_pci_info(char *guid, Error **errp)
{
@@ -517,7 +534,7 @@ static GuestPCIAddress *get_pci_info(char *guid, Error **errp)
goto out;
}
- 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");
@@ -672,20 +689,20 @@ static void get_single_disk_info(char *name, GuestDiskAddress *disk,
{
SCSI_ADDRESS addr, *scsi_ad;
DWORD len;
- HANDLE vol_h;
+ HANDLE disk_h;
Error *local_err = NULL;
scsi_ad = &addr;
g_debug("getting disk info for: %s", name);
- vol_h = CreateFile(name, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+ disk_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 err;
+ if (disk_h == INVALID_HANDLE_VALUE) {
+ error_setg_win32(errp, GetLastError(), "failed to open disk");
+ return;
}
- get_disk_properties(vol_h, disk, &local_err);
+ get_disk_properties(disk_h, disk, &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto err_close;
@@ -714,7 +731,7 @@ static void get_single_disk_info(char *name, GuestDiskAddress *disk,
* according to Microsoft docs
* https://technet.microsoft.com/en-us/library/ee851589(v=ws.10).aspx */
g_debug("getting pci-controller info");
- if (DeviceIoControl(vol_h, IOCTL_SCSI_GET_ADDRESS, NULL, 0, scsi_ad,
+ 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;
@@ -725,8 +742,7 @@ static void get_single_disk_info(char *name, GuestDiskAddress *disk,
}
err_close:
- CloseHandle(vol_h);
-err:
+ CloseHandle(disk_h);
return;
}
@@ -738,6 +754,10 @@ 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);
@@ -745,20 +765,90 @@ static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp)
name[strlen(name) - 1] = 0;
}
- disk = g_malloc0(sizeof(GuestDiskAddress));
- get_single_disk_info(name, disk, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ 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;
}
- cur_item = g_malloc0(sizeof(*list));
- cur_item->value = disk;
- disk = NULL;
- list = cur_item;
+ /* 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, NULL, NULL)) {
+ DWORD last_err = GetLastError();
+ if (last_err == ERROR_MORE_DATA) {
+ /* Try once more with big enough buffer */
+ size = sizeof(VOLUME_DISK_EXTENTS)
+ + extents->NumberOfDiskExtents*sizeof(DISK_EXTENT);
+ 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");
+ return NULL;
+ }
+ } 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));
+ get_single_disk_info(name, 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++) {
+ char *disk_name = NULL;
+ 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_name = g_strdup_printf("\\\\.\\PhysicalDrive%lu",
+ extents->Extents[i].DiskNumber);
+ get_single_disk_info(disk_name, disk, &local_err);
+ g_free(disk_name);
+ 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:
qapi_free_GuestDiskAddress(disk);
+ g_free(extents);
g_free(name);
return list;