qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

commands-win32.c (79847B)


      1 /*
      2  * QEMU Guest Agent win32-specific command implementations
      3  *
      4  * Copyright IBM Corp. 2012
      5  *
      6  * Authors:
      7  *  Michael Roth      <mdroth@linux.vnet.ibm.com>
      8  *  Gal Hammer        <ghammer@redhat.com>
      9  *
     10  * This work is licensed under the terms of the GNU GPL, version 2 or later.
     11  * See the COPYING file in the top-level directory.
     12  */
     13 #include "qemu/osdep.h"
     14 
     15 #include <wtypes.h>
     16 #include <powrprof.h>
     17 #include <winsock2.h>
     18 #include <ws2tcpip.h>
     19 #include <iptypes.h>
     20 #include <iphlpapi.h>
     21 #include <winioctl.h>
     22 #include <ntddscsi.h>
     23 #include <setupapi.h>
     24 #include <cfgmgr32.h>
     25 #include <initguid.h>
     26 #include <devpropdef.h>
     27 #include <lm.h>
     28 #include <wtsapi32.h>
     29 #include <wininet.h>
     30 
     31 #include "guest-agent-core.h"
     32 #include "vss-win32.h"
     33 #include "qga-qapi-commands.h"
     34 #include "qapi/error.h"
     35 #include "qapi/qmp/qerror.h"
     36 #include "qemu/queue.h"
     37 #include "qemu/host-utils.h"
     38 #include "qemu/base64.h"
     39 #include "commands-common.h"
     40 
     41 /*
     42  * The following should be in devpkey.h, but it isn't. The key names were
     43  * prefixed to avoid (future) name clashes. Once the definitions get into
     44  * mingw the following lines can be removed.
     45  */
     46 DEFINE_DEVPROPKEY(qga_DEVPKEY_NAME, 0xb725f130, 0x47ef, 0x101a, 0xa5,
     47     0xf1, 0x02, 0x60, 0x8c, 0x9e, 0xeb, 0xac, 10);
     48     /* DEVPROP_TYPE_STRING */
     49 DEFINE_DEVPROPKEY(qga_DEVPKEY_Device_HardwareIds, 0xa45c254e, 0xdf1c,
     50     0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 3);
     51     /* DEVPROP_TYPE_STRING_LIST */
     52 DEFINE_DEVPROPKEY(qga_DEVPKEY_Device_DriverDate, 0xa8b865dd, 0x2e3d,
     53     0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 2);
     54     /* DEVPROP_TYPE_FILETIME */
     55 DEFINE_DEVPROPKEY(qga_DEVPKEY_Device_DriverVersion, 0xa8b865dd, 0x2e3d,
     56     0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 3);
     57     /* DEVPROP_TYPE_STRING */
     58 /* The CM_Get_DevNode_PropertyW prototype is only sometimes in cfgmgr32.h */
     59 #ifndef CM_Get_DevNode_Property
     60 #pragma GCC diagnostic push
     61 #pragma GCC diagnostic ignored "-Wredundant-decls"
     62 CMAPI CONFIGRET WINAPI CM_Get_DevNode_PropertyW(
     63     DEVINST          dnDevInst,
     64     CONST DEVPROPKEY * PropertyKey,
     65     DEVPROPTYPE      * PropertyType,
     66     PBYTE            PropertyBuffer,
     67     PULONG           PropertyBufferSize,
     68     ULONG            ulFlags
     69 );
     70 #define CM_Get_DevNode_Property CM_Get_DevNode_PropertyW
     71 #pragma GCC diagnostic pop
     72 #endif
     73 
     74 #ifndef SHTDN_REASON_FLAG_PLANNED
     75 #define SHTDN_REASON_FLAG_PLANNED 0x80000000
     76 #endif
     77 
     78 /* multiple of 100 nanoseconds elapsed between windows baseline
     79  *    (1/1/1601) and Unix Epoch (1/1/1970), accounting for leap years */
     80 #define W32_FT_OFFSET (10000000ULL * 60 * 60 * 24 * \
     81                        (365 * (1970 - 1601) +       \
     82                         (1970 - 1601) / 4 - 3))
     83 
     84 #define INVALID_SET_FILE_POINTER ((DWORD)-1)
     85 
     86 struct GuestFileHandle {
     87     int64_t id;
     88     HANDLE fh;
     89     QTAILQ_ENTRY(GuestFileHandle) next;
     90 };
     91 
     92 static struct {
     93     QTAILQ_HEAD(, GuestFileHandle) filehandles;
     94 } guest_file_state = {
     95     .filehandles = QTAILQ_HEAD_INITIALIZER(guest_file_state.filehandles),
     96 };
     97 
     98 #define FILE_GENERIC_APPEND (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA)
     99 
    100 typedef struct OpenFlags {
    101     const char *forms;
    102     DWORD desired_access;
    103     DWORD creation_disposition;
    104 } OpenFlags;
    105 static OpenFlags guest_file_open_modes[] = {
    106     {"r",   GENERIC_READ,                     OPEN_EXISTING},
    107     {"rb",  GENERIC_READ,                     OPEN_EXISTING},
    108     {"w",   GENERIC_WRITE,                    CREATE_ALWAYS},
    109     {"wb",  GENERIC_WRITE,                    CREATE_ALWAYS},
    110     {"a",   FILE_GENERIC_APPEND,              OPEN_ALWAYS  },
    111     {"r+",  GENERIC_WRITE | GENERIC_READ,       OPEN_EXISTING},
    112     {"rb+", GENERIC_WRITE | GENERIC_READ,       OPEN_EXISTING},
    113     {"r+b", GENERIC_WRITE | GENERIC_READ,       OPEN_EXISTING},
    114     {"w+",  GENERIC_WRITE | GENERIC_READ,       CREATE_ALWAYS},
    115     {"wb+", GENERIC_WRITE | GENERIC_READ,       CREATE_ALWAYS},
    116     {"w+b", GENERIC_WRITE | GENERIC_READ,       CREATE_ALWAYS},
    117     {"a+",  FILE_GENERIC_APPEND | GENERIC_READ, OPEN_ALWAYS  },
    118     {"ab+", FILE_GENERIC_APPEND | GENERIC_READ, OPEN_ALWAYS  },
    119     {"a+b", FILE_GENERIC_APPEND | GENERIC_READ, OPEN_ALWAYS  }
    120 };
    121 
    122 #define debug_error(msg) do { \
    123     char *suffix = g_win32_error_message(GetLastError()); \
    124     g_debug("%s: %s", (msg), suffix); \
    125     g_free(suffix); \
    126 } while (0)
    127 
    128 static OpenFlags *find_open_flag(const char *mode_str)
    129 {
    130     int mode;
    131     Error **errp = NULL;
    132 
    133     for (mode = 0; mode < ARRAY_SIZE(guest_file_open_modes); ++mode) {
    134         OpenFlags *flags = guest_file_open_modes + mode;
    135 
    136         if (strcmp(flags->forms, mode_str) == 0) {
    137             return flags;
    138         }
    139     }
    140 
    141     error_setg(errp, "invalid file open mode '%s'", mode_str);
    142     return NULL;
    143 }
    144 
    145 static int64_t guest_file_handle_add(HANDLE fh, Error **errp)
    146 {
    147     GuestFileHandle *gfh;
    148     int64_t handle;
    149 
    150     handle = ga_get_fd_handle(ga_state, errp);
    151     if (handle < 0) {
    152         return -1;
    153     }
    154     gfh = g_new0(GuestFileHandle, 1);
    155     gfh->id = handle;
    156     gfh->fh = fh;
    157     QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next);
    158 
    159     return handle;
    160 }
    161 
    162 GuestFileHandle *guest_file_handle_find(int64_t id, Error **errp)
    163 {
    164     GuestFileHandle *gfh;
    165     QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next) {
    166         if (gfh->id == id) {
    167             return gfh;
    168         }
    169     }
    170     error_setg(errp, "handle '%" PRId64 "' has not been found", id);
    171     return NULL;
    172 }
    173 
    174 static void handle_set_nonblocking(HANDLE fh)
    175 {
    176     DWORD file_type, pipe_state;
    177     file_type = GetFileType(fh);
    178     if (file_type != FILE_TYPE_PIPE) {
    179         return;
    180     }
    181     /* If file_type == FILE_TYPE_PIPE, according to MSDN
    182      * the specified file is socket or named pipe */
    183     if (!GetNamedPipeHandleState(fh, &pipe_state, NULL,
    184                                  NULL, NULL, NULL, 0)) {
    185         return;
    186     }
    187     /* The fd is named pipe fd */
    188     if (pipe_state & PIPE_NOWAIT) {
    189         return;
    190     }
    191 
    192     pipe_state |= PIPE_NOWAIT;
    193     SetNamedPipeHandleState(fh, &pipe_state, NULL, NULL);
    194 }
    195 
    196 int64_t qmp_guest_file_open(const char *path, bool has_mode,
    197                             const char *mode, Error **errp)
    198 {
    199     int64_t fd = -1;
    200     HANDLE fh;
    201     HANDLE templ_file = NULL;
    202     DWORD share_mode = FILE_SHARE_READ;
    203     DWORD flags_and_attr = FILE_ATTRIBUTE_NORMAL;
    204     LPSECURITY_ATTRIBUTES sa_attr = NULL;
    205     OpenFlags *guest_flags;
    206     GError *gerr = NULL;
    207     wchar_t *w_path = NULL;
    208 
    209     if (!has_mode) {
    210         mode = "r";
    211     }
    212     slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
    213     guest_flags = find_open_flag(mode);
    214     if (guest_flags == NULL) {
    215         error_setg(errp, "invalid file open mode");
    216         goto done;
    217     }
    218 
    219     w_path = g_utf8_to_utf16(path, -1, NULL, NULL, &gerr);
    220     if (!w_path) {
    221         goto done;
    222     }
    223 
    224     fh = CreateFileW(w_path, guest_flags->desired_access, share_mode, sa_attr,
    225                     guest_flags->creation_disposition, flags_and_attr,
    226                     templ_file);
    227     if (fh == INVALID_HANDLE_VALUE) {
    228         error_setg_win32(errp, GetLastError(), "failed to open file '%s'",
    229                          path);
    230         goto done;
    231     }
    232 
    233     /* set fd non-blocking to avoid common use cases (like reading from a
    234      * named pipe) from hanging the agent
    235      */
    236     handle_set_nonblocking(fh);
    237 
    238     fd = guest_file_handle_add(fh, errp);
    239     if (fd < 0) {
    240         CloseHandle(fh);
    241         error_setg(errp, "failed to add handle to qmp handle table");
    242         goto done;
    243     }
    244 
    245     slog("guest-file-open, handle: % " PRId64, fd);
    246 
    247 done:
    248     if (gerr) {
    249         error_setg(errp, QERR_QGA_COMMAND_FAILED, gerr->message);
    250         g_error_free(gerr);
    251     }
    252     g_free(w_path);
    253     return fd;
    254 }
    255 
    256 void qmp_guest_file_close(int64_t handle, Error **errp)
    257 {
    258     bool ret;
    259     GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
    260     slog("guest-file-close called, handle: %" PRId64, handle);
    261     if (gfh == NULL) {
    262         return;
    263     }
    264     ret = CloseHandle(gfh->fh);
    265     if (!ret) {
    266         error_setg_win32(errp, GetLastError(), "failed close handle");
    267         return;
    268     }
    269 
    270     QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next);
    271     g_free(gfh);
    272 }
    273 
    274 static void acquire_privilege(const char *name, Error **errp)
    275 {
    276     HANDLE token = NULL;
    277     TOKEN_PRIVILEGES priv;
    278     Error *local_err = NULL;
    279 
    280     if (OpenProcessToken(GetCurrentProcess(),
    281         TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
    282     {
    283         if (!LookupPrivilegeValue(NULL, name, &priv.Privileges[0].Luid)) {
    284             error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
    285                        "no luid for requested privilege");
    286             goto out;
    287         }
    288 
    289         priv.PrivilegeCount = 1;
    290         priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    291 
    292         if (!AdjustTokenPrivileges(token, FALSE, &priv, 0, NULL, 0)) {
    293             error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
    294                        "unable to acquire requested privilege");
    295             goto out;
    296         }
    297 
    298     } else {
    299         error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
    300                    "failed to open privilege token");
    301     }
    302 
    303 out:
    304     if (token) {
    305         CloseHandle(token);
    306     }
    307     error_propagate(errp, local_err);
    308 }
    309 
    310 static void execute_async(DWORD WINAPI (*func)(LPVOID), LPVOID opaque,
    311                           Error **errp)
    312 {
    313     HANDLE thread = CreateThread(NULL, 0, func, opaque, 0, NULL);
    314     if (!thread) {
    315         error_setg(errp, QERR_QGA_COMMAND_FAILED,
    316                    "failed to dispatch asynchronous command");
    317     }
    318 }
    319 
    320 void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp)
    321 {
    322     Error *local_err = NULL;
    323     UINT shutdown_flag = EWX_FORCE;
    324 
    325     slog("guest-shutdown called, mode: %s", mode);
    326 
    327     if (!has_mode || strcmp(mode, "powerdown") == 0) {
    328         shutdown_flag |= EWX_POWEROFF;
    329     } else if (strcmp(mode, "halt") == 0) {
    330         shutdown_flag |= EWX_SHUTDOWN;
    331     } else if (strcmp(mode, "reboot") == 0) {
    332         shutdown_flag |= EWX_REBOOT;
    333     } else {
    334         error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "mode",
    335                    "'halt', 'powerdown', or 'reboot'");
    336         return;
    337     }
    338 
    339     /* Request a shutdown privilege, but try to shut down the system
    340        anyway. */
    341     acquire_privilege(SE_SHUTDOWN_NAME, &local_err);
    342     if (local_err) {
    343         error_propagate(errp, local_err);
    344         return;
    345     }
    346 
    347     if (!ExitWindowsEx(shutdown_flag, SHTDN_REASON_FLAG_PLANNED)) {
    348         g_autofree gchar *emsg = g_win32_error_message(GetLastError());
    349         slog("guest-shutdown failed: %s", emsg);
    350         error_setg_win32(errp, GetLastError(), "guest-shutdown failed");
    351     }
    352 }
    353 
    354 GuestFileRead *guest_file_read_unsafe(GuestFileHandle *gfh,
    355                                       int64_t count, Error **errp)
    356 {
    357     GuestFileRead *read_data = NULL;
    358     guchar *buf;
    359     HANDLE fh = gfh->fh;
    360     bool is_ok;
    361     DWORD read_count;
    362 
    363     buf = g_malloc0(count + 1);
    364     is_ok = ReadFile(fh, buf, count, &read_count, NULL);
    365     if (!is_ok) {
    366         error_setg_win32(errp, GetLastError(), "failed to read file");
    367     } else {
    368         buf[read_count] = 0;
    369         read_data = g_new0(GuestFileRead, 1);
    370         read_data->count = (size_t)read_count;
    371         read_data->eof = read_count == 0;
    372 
    373         if (read_count != 0) {
    374             read_data->buf_b64 = g_base64_encode(buf, read_count);
    375         }
    376     }
    377     g_free(buf);
    378 
    379     return read_data;
    380 }
    381 
    382 GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
    383                                      bool has_count, int64_t count,
    384                                      Error **errp)
    385 {
    386     GuestFileWrite *write_data = NULL;
    387     guchar *buf;
    388     gsize buf_len;
    389     bool is_ok;
    390     DWORD write_count;
    391     GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
    392     HANDLE fh;
    393 
    394     if (!gfh) {
    395         return NULL;
    396     }
    397     fh = gfh->fh;
    398     buf = qbase64_decode(buf_b64, -1, &buf_len, errp);
    399     if (!buf) {
    400         return NULL;
    401     }
    402 
    403     if (!has_count) {
    404         count = buf_len;
    405     } else if (count < 0 || count > buf_len) {
    406         error_setg(errp, "value '%" PRId64
    407                    "' is invalid for argument count", count);
    408         goto done;
    409     }
    410 
    411     is_ok = WriteFile(fh, buf, count, &write_count, NULL);
    412     if (!is_ok) {
    413         error_setg_win32(errp, GetLastError(), "failed to write to file");
    414         slog("guest-file-write-failed, handle: %" PRId64, handle);
    415     } else {
    416         write_data = g_new0(GuestFileWrite, 1);
    417         write_data->count = (size_t) write_count;
    418     }
    419 
    420 done:
    421     g_free(buf);
    422     return write_data;
    423 }
    424 
    425 GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
    426                                    GuestFileWhence *whence_code,
    427                                    Error **errp)
    428 {
    429     GuestFileHandle *gfh;
    430     GuestFileSeek *seek_data;
    431     HANDLE fh;
    432     LARGE_INTEGER new_pos, off_pos;
    433     off_pos.QuadPart = offset;
    434     BOOL res;
    435     int whence;
    436     Error *err = NULL;
    437 
    438     gfh = guest_file_handle_find(handle, errp);
    439     if (!gfh) {
    440         return NULL;
    441     }
    442 
    443     /* We stupidly exposed 'whence':'int' in our qapi */
    444     whence = ga_parse_whence(whence_code, &err);
    445     if (err) {
    446         error_propagate(errp, err);
    447         return NULL;
    448     }
    449 
    450     fh = gfh->fh;
    451     res = SetFilePointerEx(fh, off_pos, &new_pos, whence);
    452     if (!res) {
    453         error_setg_win32(errp, GetLastError(), "failed to seek file");
    454         return NULL;
    455     }
    456     seek_data = g_new0(GuestFileSeek, 1);
    457     seek_data->position = new_pos.QuadPart;
    458     return seek_data;
    459 }
    460 
    461 void qmp_guest_file_flush(int64_t handle, Error **errp)
    462 {
    463     HANDLE fh;
    464     GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
    465     if (!gfh) {
    466         return;
    467     }
    468 
    469     fh = gfh->fh;
    470     if (!FlushFileBuffers(fh)) {
    471         error_setg_win32(errp, GetLastError(), "failed to flush file");
    472     }
    473 }
    474 
    475 static GuestDiskBusType win2qemu[] = {
    476     [BusTypeUnknown] = GUEST_DISK_BUS_TYPE_UNKNOWN,
    477     [BusTypeScsi] = GUEST_DISK_BUS_TYPE_SCSI,
    478     [BusTypeAtapi] = GUEST_DISK_BUS_TYPE_IDE,
    479     [BusTypeAta] = GUEST_DISK_BUS_TYPE_IDE,
    480     [BusType1394] = GUEST_DISK_BUS_TYPE_IEEE1394,
    481     [BusTypeSsa] = GUEST_DISK_BUS_TYPE_SSA,
    482     [BusTypeFibre] = GUEST_DISK_BUS_TYPE_SSA,
    483     [BusTypeUsb] = GUEST_DISK_BUS_TYPE_USB,
    484     [BusTypeRAID] = GUEST_DISK_BUS_TYPE_RAID,
    485     [BusTypeiScsi] = GUEST_DISK_BUS_TYPE_ISCSI,
    486     [BusTypeSas] = GUEST_DISK_BUS_TYPE_SAS,
    487     [BusTypeSata] = GUEST_DISK_BUS_TYPE_SATA,
    488     [BusTypeSd] =  GUEST_DISK_BUS_TYPE_SD,
    489     [BusTypeMmc] = GUEST_DISK_BUS_TYPE_MMC,
    490 #if (_WIN32_WINNT >= 0x0601)
    491     [BusTypeVirtual] = GUEST_DISK_BUS_TYPE_VIRTUAL,
    492     [BusTypeFileBackedVirtual] = GUEST_DISK_BUS_TYPE_FILE_BACKED_VIRTUAL,
    493     /*
    494      * BusTypeSpaces currently is not suported
    495      */
    496     [BusTypeSpaces] = GUEST_DISK_BUS_TYPE_UNKNOWN,
    497     [BusTypeNvme] = GUEST_DISK_BUS_TYPE_NVME,
    498 #endif
    499 };
    500 
    501 static GuestDiskBusType find_bus_type(STORAGE_BUS_TYPE bus)
    502 {
    503     if (bus >= ARRAY_SIZE(win2qemu) || (int)bus < 0) {
    504         return GUEST_DISK_BUS_TYPE_UNKNOWN;
    505     }
    506     return win2qemu[(int)bus];
    507 }
    508 
    509 DEFINE_GUID(GUID_DEVINTERFACE_DISK,
    510         0x53f56307L, 0xb6bf, 0x11d0, 0x94, 0xf2,
    511         0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
    512 DEFINE_GUID(GUID_DEVINTERFACE_STORAGEPORT,
    513         0x2accfe60L, 0xc130, 0x11d2, 0xb0, 0x82,
    514         0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
    515 
    516 static void get_pci_address_for_device(GuestPCIAddress *pci,
    517                                        HDEVINFO dev_info)
    518 {
    519     SP_DEVINFO_DATA dev_info_data;
    520     DWORD j;
    521     DWORD size;
    522     bool partial_pci = false;
    523 
    524     dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
    525 
    526     for (j = 0;
    527          SetupDiEnumDeviceInfo(dev_info, j, &dev_info_data);
    528          j++) {
    529         DWORD addr, bus, ui_slot, type;
    530         int func, slot;
    531         size = sizeof(DWORD);
    532 
    533         /*
    534         * There is no need to allocate buffer in the next functions. The
    535         * size is known and ULONG according to
    536         * https://msdn.microsoft.com/en-us/library/windows/hardware/ff543095(v=vs.85).aspx
    537         */
    538         if (!SetupDiGetDeviceRegistryProperty(
    539                 dev_info, &dev_info_data, SPDRP_BUSNUMBER,
    540                 &type, (PBYTE)&bus, size, NULL)) {
    541             debug_error("failed to get PCI bus");
    542             bus = -1;
    543             partial_pci = true;
    544         }
    545 
    546         /*
    547         * The function retrieves the device's address. This value will be
    548         * transformed into device function and number
    549         */
    550         if (!SetupDiGetDeviceRegistryProperty(
    551                 dev_info, &dev_info_data, SPDRP_ADDRESS,
    552                 &type, (PBYTE)&addr, size, NULL)) {
    553             debug_error("failed to get PCI address");
    554             addr = -1;
    555             partial_pci = true;
    556         }
    557 
    558         /*
    559         * This call returns UINumber of DEVICE_CAPABILITIES structure.
    560         * This number is typically a user-perceived slot number.
    561         */
    562         if (!SetupDiGetDeviceRegistryProperty(
    563                 dev_info, &dev_info_data, SPDRP_UI_NUMBER,
    564                 &type, (PBYTE)&ui_slot, size, NULL)) {
    565             debug_error("failed to get PCI slot");
    566             ui_slot = -1;
    567             partial_pci = true;
    568         }
    569 
    570         /*
    571         * SetupApi gives us the same information as driver with
    572         * IoGetDeviceProperty. According to Microsoft:
    573         *
    574         *   FunctionNumber = (USHORT)((propertyAddress) & 0x0000FFFF)
    575         *   DeviceNumber = (USHORT)(((propertyAddress) >> 16) & 0x0000FFFF)
    576         *   SPDRP_ADDRESS is propertyAddress, so we do the same.
    577         *
    578         * https://docs.microsoft.com/en-us/windows/desktop/api/setupapi/nf-setupapi-setupdigetdeviceregistrypropertya
    579         */
    580         if (partial_pci) {
    581             pci->domain = -1;
    582             pci->slot = -1;
    583             pci->function = -1;
    584             pci->bus = -1;
    585             continue;
    586         } else {
    587             func = ((int)addr == -1) ? -1 : addr & 0x0000FFFF;
    588             slot = ((int)addr == -1) ? -1 : (addr >> 16) & 0x0000FFFF;
    589             if ((int)ui_slot != slot) {
    590                 g_debug("mismatch with reported slot values: %d vs %d",
    591                         (int)ui_slot, slot);
    592             }
    593             pci->domain = 0;
    594             pci->slot = (int)ui_slot;
    595             pci->function = func;
    596             pci->bus = (int)bus;
    597             return;
    598         }
    599     }
    600 }
    601 
    602 static GuestPCIAddress *get_pci_info(int number, Error **errp)
    603 {
    604     HDEVINFO dev_info = INVALID_HANDLE_VALUE;
    605     HDEVINFO parent_dev_info = INVALID_HANDLE_VALUE;
    606 
    607     SP_DEVINFO_DATA dev_info_data;
    608     SP_DEVICE_INTERFACE_DATA dev_iface_data;
    609     HANDLE dev_file;
    610     int i;
    611     GuestPCIAddress *pci = NULL;
    612 
    613     pci = g_malloc0(sizeof(*pci));
    614     pci->domain = -1;
    615     pci->slot = -1;
    616     pci->function = -1;
    617     pci->bus = -1;
    618 
    619     dev_info = SetupDiGetClassDevs(&GUID_DEVINTERFACE_DISK, 0, 0,
    620                                    DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
    621     if (dev_info == INVALID_HANDLE_VALUE) {
    622         error_setg_win32(errp, GetLastError(), "failed to get devices tree");
    623         goto end;
    624     }
    625 
    626     g_debug("enumerating devices");
    627     dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
    628     dev_iface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
    629     for (i = 0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) {
    630         g_autofree PSP_DEVICE_INTERFACE_DETAIL_DATA pdev_iface_detail_data = NULL;
    631         STORAGE_DEVICE_NUMBER sdn;
    632         g_autofree char *parent_dev_id = NULL;
    633         SP_DEVINFO_DATA parent_dev_info_data;
    634         DWORD size = 0;
    635 
    636         g_debug("getting device path");
    637         if (SetupDiEnumDeviceInterfaces(dev_info, &dev_info_data,
    638                                         &GUID_DEVINTERFACE_DISK, 0,
    639                                         &dev_iface_data)) {
    640             if (!SetupDiGetDeviceInterfaceDetail(dev_info, &dev_iface_data,
    641                                                  pdev_iface_detail_data,
    642                                                  size, &size,
    643                                                  &dev_info_data)) {
    644                 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
    645                     pdev_iface_detail_data = g_malloc(size);
    646                     pdev_iface_detail_data->cbSize =
    647                         sizeof(*pdev_iface_detail_data);
    648                 } else {
    649                     error_setg_win32(errp, GetLastError(),
    650                                      "failed to get device interfaces");
    651                     goto end;
    652                 }
    653             }
    654 
    655             if (!SetupDiGetDeviceInterfaceDetail(dev_info, &dev_iface_data,
    656                                                  pdev_iface_detail_data,
    657                                                  size, &size,
    658                                                  &dev_info_data)) {
    659                 // pdev_iface_detail_data already is allocated
    660                 error_setg_win32(errp, GetLastError(),
    661                                     "failed to get device interfaces");
    662                 goto end;
    663             }
    664 
    665             dev_file = CreateFile(pdev_iface_detail_data->DevicePath, 0,
    666                                   FILE_SHARE_READ, NULL, OPEN_EXISTING, 0,
    667                                   NULL);
    668 
    669             if (!DeviceIoControl(dev_file, IOCTL_STORAGE_GET_DEVICE_NUMBER,
    670                                  NULL, 0, &sdn, sizeof(sdn), &size, NULL)) {
    671                 CloseHandle(dev_file);
    672                 error_setg_win32(errp, GetLastError(),
    673                                  "failed to get device slot number");
    674                 goto end;
    675             }
    676 
    677             CloseHandle(dev_file);
    678             if (sdn.DeviceNumber != number) {
    679                 continue;
    680             }
    681         } else {
    682             error_setg_win32(errp, GetLastError(),
    683                              "failed to get device interfaces");
    684             goto end;
    685         }
    686 
    687         g_debug("found device slot %d. Getting storage controller", number);
    688         {
    689             CONFIGRET cr;
    690             DEVINST dev_inst, parent_dev_inst;
    691             ULONG dev_id_size = 0;
    692 
    693             size = 0;
    694             if (!SetupDiGetDeviceInstanceId(dev_info, &dev_info_data,
    695                                             parent_dev_id, size, &size)) {
    696                 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
    697                     parent_dev_id = g_malloc(size);
    698                 } else {
    699                     error_setg_win32(errp, GetLastError(),
    700                                      "failed to get device instance ID");
    701                     goto end;
    702                 }
    703             }
    704 
    705             if (!SetupDiGetDeviceInstanceId(dev_info, &dev_info_data,
    706                                             parent_dev_id, size, &size)) {
    707                 // parent_dev_id already is allocated
    708                 error_setg_win32(errp, GetLastError(),
    709                                     "failed to get device instance ID");
    710                 goto end;
    711             }
    712 
    713             /*
    714              * CM API used here as opposed to
    715              * SetupDiGetDeviceProperty(..., DEVPKEY_Device_Parent, ...)
    716              * which exports are only available in mingw-w64 6+
    717              */
    718             cr = CM_Locate_DevInst(&dev_inst, parent_dev_id, 0);
    719             if (cr != CR_SUCCESS) {
    720                 g_error("CM_Locate_DevInst failed with code %lx", cr);
    721                 error_setg_win32(errp, GetLastError(),
    722                                  "failed to get device instance");
    723                 goto end;
    724             }
    725             cr = CM_Get_Parent(&parent_dev_inst, dev_inst, 0);
    726             if (cr != CR_SUCCESS) {
    727                 g_error("CM_Get_Parent failed with code %lx", cr);
    728                 error_setg_win32(errp, GetLastError(),
    729                                  "failed to get parent device instance");
    730                 goto end;
    731             }
    732 
    733             cr = CM_Get_Device_ID_Size(&dev_id_size, parent_dev_inst, 0);
    734             if (cr != CR_SUCCESS) {
    735                 g_error("CM_Get_Device_ID_Size failed with code %lx", cr);
    736                 error_setg_win32(errp, GetLastError(),
    737                                  "failed to get parent device ID length");
    738                 goto end;
    739             }
    740 
    741             ++dev_id_size;
    742             if (dev_id_size > size) {
    743                 g_free(parent_dev_id);
    744                 parent_dev_id = g_malloc(dev_id_size);
    745             }
    746 
    747             cr = CM_Get_Device_ID(parent_dev_inst, parent_dev_id, dev_id_size,
    748                                   0);
    749             if (cr != CR_SUCCESS) {
    750                 g_error("CM_Get_Device_ID failed with code %lx", cr);
    751                 error_setg_win32(errp, GetLastError(),
    752                                  "failed to get parent device ID");
    753                 goto end;
    754             }
    755         }
    756 
    757         g_debug("querying storage controller %s for PCI information",
    758                 parent_dev_id);
    759         parent_dev_info =
    760             SetupDiGetClassDevs(&GUID_DEVINTERFACE_STORAGEPORT, parent_dev_id,
    761                                 NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
    762 
    763         if (parent_dev_info == INVALID_HANDLE_VALUE) {
    764             error_setg_win32(errp, GetLastError(),
    765                              "failed to get parent device");
    766             goto end;
    767         }
    768 
    769         parent_dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
    770         if (!SetupDiEnumDeviceInfo(parent_dev_info, 0, &parent_dev_info_data)) {
    771             error_setg_win32(errp, GetLastError(),
    772                            "failed to get parent device data");
    773             goto end;
    774         }
    775 
    776         get_pci_address_for_device(pci, parent_dev_info);
    777 
    778         break;
    779     }
    780 
    781 end:
    782     if (parent_dev_info != INVALID_HANDLE_VALUE) {
    783         SetupDiDestroyDeviceInfoList(parent_dev_info);
    784     }
    785     if (dev_info != INVALID_HANDLE_VALUE) {
    786         SetupDiDestroyDeviceInfoList(dev_info);
    787     }
    788     return pci;
    789 }
    790 
    791 static void get_disk_properties(HANDLE vol_h, GuestDiskAddress *disk,
    792     Error **errp)
    793 {
    794     STORAGE_PROPERTY_QUERY query;
    795     STORAGE_DEVICE_DESCRIPTOR *dev_desc, buf;
    796     DWORD received;
    797     ULONG size = sizeof(buf);
    798 
    799     dev_desc = &buf;
    800     query.PropertyId = StorageDeviceProperty;
    801     query.QueryType = PropertyStandardQuery;
    802 
    803     if (!DeviceIoControl(vol_h, IOCTL_STORAGE_QUERY_PROPERTY, &query,
    804                          sizeof(STORAGE_PROPERTY_QUERY), dev_desc,
    805                          size, &received, NULL)) {
    806         error_setg_win32(errp, GetLastError(), "failed to get bus type");
    807         return;
    808     }
    809     disk->bus_type = find_bus_type(dev_desc->BusType);
    810     g_debug("bus type %d", disk->bus_type);
    811 
    812     /* Query once more. Now with long enough buffer. */
    813     size = dev_desc->Size;
    814     dev_desc = g_malloc0(size);
    815     if (!DeviceIoControl(vol_h, IOCTL_STORAGE_QUERY_PROPERTY, &query,
    816                          sizeof(STORAGE_PROPERTY_QUERY), dev_desc,
    817                          size, &received, NULL)) {
    818         error_setg_win32(errp, GetLastError(), "failed to get serial number");
    819         g_debug("failed to get serial number");
    820         goto out_free;
    821     }
    822     if (dev_desc->SerialNumberOffset > 0) {
    823         const char *serial;
    824         size_t len;
    825 
    826         if (dev_desc->SerialNumberOffset >= received) {
    827             error_setg(errp, "failed to get serial number: offset outside the buffer");
    828             g_debug("serial number offset outside the buffer");
    829             goto out_free;
    830         }
    831         serial = (char *)dev_desc + dev_desc->SerialNumberOffset;
    832         len = received - dev_desc->SerialNumberOffset;
    833         g_debug("serial number \"%s\"", serial);
    834         if (*serial != 0) {
    835             disk->serial = g_strndup(serial, len);
    836             disk->has_serial = true;
    837         }
    838     }
    839 out_free:
    840     g_free(dev_desc);
    841 
    842     return;
    843 }
    844 
    845 static void get_single_disk_info(int disk_number,
    846                                  GuestDiskAddress *disk, Error **errp)
    847 {
    848     SCSI_ADDRESS addr, *scsi_ad;
    849     DWORD len;
    850     HANDLE disk_h;
    851     Error *local_err = NULL;
    852 
    853     scsi_ad = &addr;
    854 
    855     g_debug("getting disk info for: %s", disk->dev);
    856     disk_h = CreateFile(disk->dev, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
    857                        0, NULL);
    858     if (disk_h == INVALID_HANDLE_VALUE) {
    859         error_setg_win32(errp, GetLastError(), "failed to open disk");
    860         return;
    861     }
    862 
    863     get_disk_properties(disk_h, disk, &local_err);
    864     if (local_err) {
    865         error_propagate(errp, local_err);
    866         goto err_close;
    867     }
    868 
    869     g_debug("bus type %d", disk->bus_type);
    870     /* always set pci_controller as required by schema. get_pci_info() should
    871      * report -1 values for non-PCI buses rather than fail. fail the command
    872      * if that doesn't hold since that suggests some other unexpected
    873      * breakage
    874      */
    875     disk->pci_controller = get_pci_info(disk_number, &local_err);
    876     if (local_err) {
    877         error_propagate(errp, local_err);
    878         goto err_close;
    879     }
    880     if (disk->bus_type == GUEST_DISK_BUS_TYPE_SCSI
    881             || disk->bus_type == GUEST_DISK_BUS_TYPE_IDE
    882             || disk->bus_type == GUEST_DISK_BUS_TYPE_RAID
    883             /* This bus type is not supported before Windows Server 2003 SP1 */
    884             || disk->bus_type == GUEST_DISK_BUS_TYPE_SAS
    885         ) {
    886         /* We are able to use the same ioctls for different bus types
    887          * according to Microsoft docs
    888          * https://technet.microsoft.com/en-us/library/ee851589(v=ws.10).aspx */
    889         g_debug("getting SCSI info");
    890         if (DeviceIoControl(disk_h, IOCTL_SCSI_GET_ADDRESS, NULL, 0, scsi_ad,
    891                             sizeof(SCSI_ADDRESS), &len, NULL)) {
    892             disk->unit = addr.Lun;
    893             disk->target = addr.TargetId;
    894             disk->bus = addr.PathId;
    895         }
    896         /* We do not set error in this case, because we still have enough
    897          * information about volume. */
    898     }
    899 
    900 err_close:
    901     CloseHandle(disk_h);
    902     return;
    903 }
    904 
    905 /* VSS provider works with volumes, thus there is no difference if
    906  * the volume consist of spanned disks. Info about the first disk in the
    907  * volume is returned for the spanned disk group (LVM) */
    908 static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp)
    909 {
    910     Error *local_err = NULL;
    911     GuestDiskAddressList *list = NULL;
    912     GuestDiskAddress *disk = NULL;
    913     int i;
    914     HANDLE vol_h;
    915     DWORD size;
    916     PVOLUME_DISK_EXTENTS extents = NULL;
    917 
    918     /* strip final backslash */
    919     char *name = g_strdup(guid);
    920     if (g_str_has_suffix(name, "\\")) {
    921         name[strlen(name) - 1] = 0;
    922     }
    923 
    924     g_debug("opening %s", name);
    925     vol_h = CreateFile(name, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
    926                        0, NULL);
    927     if (vol_h == INVALID_HANDLE_VALUE) {
    928         error_setg_win32(errp, GetLastError(), "failed to open volume");
    929         goto out;
    930     }
    931 
    932     /* Get list of extents */
    933     g_debug("getting disk extents");
    934     size = sizeof(VOLUME_DISK_EXTENTS);
    935     extents = g_malloc0(size);
    936     if (!DeviceIoControl(vol_h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL,
    937                          0, extents, size, &size, NULL)) {
    938         DWORD last_err = GetLastError();
    939         if (last_err == ERROR_MORE_DATA) {
    940             /* Try once more with big enough buffer */
    941             g_free(extents);
    942             extents = g_malloc0(size);
    943             if (!DeviceIoControl(
    944                     vol_h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL,
    945                     0, extents, size, NULL, NULL)) {
    946                 error_setg_win32(errp, GetLastError(),
    947                     "failed to get disk extents");
    948                 goto out;
    949             }
    950         } else if (last_err == ERROR_INVALID_FUNCTION) {
    951             /* Possibly CD-ROM or a shared drive. Try to pass the volume */
    952             g_debug("volume not on disk");
    953             disk = g_new0(GuestDiskAddress, 1);
    954             disk->has_dev = true;
    955             disk->dev = g_strdup(name);
    956             get_single_disk_info(0xffffffff, disk, &local_err);
    957             if (local_err) {
    958                 g_debug("failed to get disk info, ignoring error: %s",
    959                     error_get_pretty(local_err));
    960                 error_free(local_err);
    961                 goto out;
    962             }
    963             QAPI_LIST_PREPEND(list, disk);
    964             disk = NULL;
    965             goto out;
    966         } else {
    967             error_setg_win32(errp, GetLastError(),
    968                 "failed to get disk extents");
    969             goto out;
    970         }
    971     }
    972     g_debug("Number of extents: %lu", extents->NumberOfDiskExtents);
    973 
    974     /* Go through each extent */
    975     for (i = 0; i < extents->NumberOfDiskExtents; i++) {
    976         disk = g_new0(GuestDiskAddress, 1);
    977 
    978         /* Disk numbers directly correspond to numbers used in UNCs
    979          *
    980          * See documentation for DISK_EXTENT:
    981          * https://docs.microsoft.com/en-us/windows/desktop/api/winioctl/ns-winioctl-_disk_extent
    982          *
    983          * See also Naming Files, Paths and Namespaces:
    984          * https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#win32-device-namespaces
    985          */
    986         disk->has_dev = true;
    987         disk->dev = g_strdup_printf("\\\\.\\PhysicalDrive%lu",
    988                                     extents->Extents[i].DiskNumber);
    989 
    990         get_single_disk_info(extents->Extents[i].DiskNumber, disk, &local_err);
    991         if (local_err) {
    992             error_propagate(errp, local_err);
    993             goto out;
    994         }
    995         QAPI_LIST_PREPEND(list, disk);
    996         disk = NULL;
    997     }
    998 
    999 
   1000 out:
   1001     if (vol_h != INVALID_HANDLE_VALUE) {
   1002         CloseHandle(vol_h);
   1003     }
   1004     qapi_free_GuestDiskAddress(disk);
   1005     g_free(extents);
   1006     g_free(name);
   1007 
   1008     return list;
   1009 }
   1010 
   1011 GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
   1012 {
   1013     GuestDiskInfoList *ret = NULL;
   1014     HDEVINFO dev_info;
   1015     SP_DEVICE_INTERFACE_DATA dev_iface_data;
   1016     int i;
   1017 
   1018     dev_info = SetupDiGetClassDevs(&GUID_DEVINTERFACE_DISK, 0, 0,
   1019         DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
   1020     if (dev_info == INVALID_HANDLE_VALUE) {
   1021         error_setg_win32(errp, GetLastError(), "failed to get device tree");
   1022         return NULL;
   1023     }
   1024 
   1025     g_debug("enumerating devices");
   1026     dev_iface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
   1027     for (i = 0;
   1028         SetupDiEnumDeviceInterfaces(dev_info, NULL, &GUID_DEVINTERFACE_DISK,
   1029             i, &dev_iface_data);
   1030         i++) {
   1031         GuestDiskAddress *address = NULL;
   1032         GuestDiskInfo *disk = NULL;
   1033         Error *local_err = NULL;
   1034         g_autofree PSP_DEVICE_INTERFACE_DETAIL_DATA
   1035             pdev_iface_detail_data = NULL;
   1036         STORAGE_DEVICE_NUMBER sdn;
   1037         HANDLE dev_file;
   1038         DWORD size = 0;
   1039         BOOL result;
   1040         int attempt;
   1041 
   1042         g_debug("  getting device path");
   1043         for (attempt = 0, result = FALSE; attempt < 2 && !result; attempt++) {
   1044             result = SetupDiGetDeviceInterfaceDetail(dev_info,
   1045                 &dev_iface_data, pdev_iface_detail_data, size, &size, NULL);
   1046             if (result) {
   1047                 break;
   1048             }
   1049             if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
   1050                 pdev_iface_detail_data = g_realloc(pdev_iface_detail_data,
   1051                     size);
   1052                 pdev_iface_detail_data->cbSize =
   1053                     sizeof(*pdev_iface_detail_data);
   1054             } else {
   1055                 g_debug("failed to get device interface details");
   1056                 break;
   1057             }
   1058         }
   1059         if (!result) {
   1060             g_debug("skipping device");
   1061             continue;
   1062         }
   1063 
   1064         g_debug("  device: %s", pdev_iface_detail_data->DevicePath);
   1065         dev_file = CreateFile(pdev_iface_detail_data->DevicePath, 0,
   1066             FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
   1067         if (!DeviceIoControl(dev_file, IOCTL_STORAGE_GET_DEVICE_NUMBER,
   1068                 NULL, 0, &sdn, sizeof(sdn), &size, NULL)) {
   1069             CloseHandle(dev_file);
   1070             debug_error("failed to get storage device number");
   1071             continue;
   1072         }
   1073         CloseHandle(dev_file);
   1074 
   1075         disk = g_new0(GuestDiskInfo, 1);
   1076         disk->name = g_strdup_printf("\\\\.\\PhysicalDrive%lu",
   1077             sdn.DeviceNumber);
   1078 
   1079         g_debug("  number: %lu", sdn.DeviceNumber);
   1080         address = g_new0(GuestDiskAddress, 1);
   1081         address->has_dev = true;
   1082         address->dev = g_strdup(disk->name);
   1083         get_single_disk_info(sdn.DeviceNumber, address, &local_err);
   1084         if (local_err) {
   1085             g_debug("failed to get disk info: %s",
   1086                 error_get_pretty(local_err));
   1087             error_free(local_err);
   1088             qapi_free_GuestDiskAddress(address);
   1089             address = NULL;
   1090         } else {
   1091             disk->address = address;
   1092             disk->has_address = true;
   1093         }
   1094 
   1095         QAPI_LIST_PREPEND(ret, disk);
   1096     }
   1097 
   1098     SetupDiDestroyDeviceInfoList(dev_info);
   1099     return ret;
   1100 }
   1101 
   1102 static GuestFilesystemInfo *build_guest_fsinfo(char *guid, Error **errp)
   1103 {
   1104     DWORD info_size;
   1105     char mnt, *mnt_point;
   1106     wchar_t wfs_name[32];
   1107     char fs_name[32];
   1108     wchar_t vol_info[MAX_PATH + 1];
   1109     size_t len;
   1110     uint64_t i64FreeBytesToCaller, i64TotalBytes, i64FreeBytes;
   1111     GuestFilesystemInfo *fs = NULL;
   1112     HANDLE hLocalDiskHandle = INVALID_HANDLE_VALUE;
   1113 
   1114     GetVolumePathNamesForVolumeName(guid, (LPCH)&mnt, 0, &info_size);
   1115     if (GetLastError() != ERROR_MORE_DATA) {
   1116         error_setg_win32(errp, GetLastError(), "failed to get volume name");
   1117         return NULL;
   1118     }
   1119 
   1120     mnt_point = g_malloc(info_size + 1);
   1121     if (!GetVolumePathNamesForVolumeName(guid, mnt_point, info_size,
   1122                                          &info_size)) {
   1123         error_setg_win32(errp, GetLastError(), "failed to get volume name");
   1124         goto free;
   1125     }
   1126 
   1127     hLocalDiskHandle = CreateFile(guid, 0 , 0, NULL, OPEN_EXISTING,
   1128                                   FILE_ATTRIBUTE_NORMAL |
   1129                                   FILE_FLAG_BACKUP_SEMANTICS, NULL);
   1130     if (INVALID_HANDLE_VALUE == hLocalDiskHandle) {
   1131         error_setg_win32(errp, GetLastError(), "failed to get handle for volume");
   1132         goto free;
   1133     }
   1134 
   1135     len = strlen(mnt_point);
   1136     mnt_point[len] = '\\';
   1137     mnt_point[len + 1] = 0;
   1138 
   1139     if (!GetVolumeInformationByHandleW(hLocalDiskHandle, vol_info,
   1140                                        sizeof(vol_info), NULL, NULL, NULL,
   1141                                        (LPWSTR) & wfs_name, sizeof(wfs_name))) {
   1142         if (GetLastError() != ERROR_NOT_READY) {
   1143             error_setg_win32(errp, GetLastError(), "failed to get volume info");
   1144         }
   1145         goto free;
   1146     }
   1147 
   1148     fs = g_malloc(sizeof(*fs));
   1149     fs->name = g_strdup(guid);
   1150     fs->has_total_bytes = false;
   1151     fs->has_used_bytes = false;
   1152     if (len == 0) {
   1153         fs->mountpoint = g_strdup("System Reserved");
   1154     } else {
   1155         fs->mountpoint = g_strndup(mnt_point, len);
   1156         if (GetDiskFreeSpaceEx(fs->mountpoint,
   1157                                (PULARGE_INTEGER) & i64FreeBytesToCaller,
   1158                                (PULARGE_INTEGER) & i64TotalBytes,
   1159                                (PULARGE_INTEGER) & i64FreeBytes)) {
   1160             fs->used_bytes = i64TotalBytes - i64FreeBytes;
   1161             fs->total_bytes = i64TotalBytes;
   1162             fs->has_total_bytes = true;
   1163             fs->has_used_bytes = true;
   1164         }
   1165     }
   1166     wcstombs(fs_name, wfs_name, sizeof(wfs_name));
   1167     fs->type = g_strdup(fs_name);
   1168     fs->disk = build_guest_disk_info(guid, errp);
   1169 free:
   1170     if (hLocalDiskHandle != INVALID_HANDLE_VALUE) {
   1171         CloseHandle(hLocalDiskHandle);
   1172     }
   1173     g_free(mnt_point);
   1174     return fs;
   1175 }
   1176 
   1177 GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
   1178 {
   1179     HANDLE vol_h;
   1180     GuestFilesystemInfoList *ret = NULL;
   1181     char guid[256];
   1182 
   1183     vol_h = FindFirstVolume(guid, sizeof(guid));
   1184     if (vol_h == INVALID_HANDLE_VALUE) {
   1185         error_setg_win32(errp, GetLastError(), "failed to find any volume");
   1186         return NULL;
   1187     }
   1188 
   1189     do {
   1190         Error *local_err = NULL;
   1191         GuestFilesystemInfo *info = build_guest_fsinfo(guid, &local_err);
   1192         if (local_err) {
   1193             g_debug("failed to get filesystem info, ignoring error: %s",
   1194                     error_get_pretty(local_err));
   1195             error_free(local_err);
   1196             continue;
   1197         }
   1198         QAPI_LIST_PREPEND(ret, info);
   1199     } while (FindNextVolume(vol_h, guid, sizeof(guid)));
   1200 
   1201     if (GetLastError() != ERROR_NO_MORE_FILES) {
   1202         error_setg_win32(errp, GetLastError(), "failed to find next volume");
   1203     }
   1204 
   1205     FindVolumeClose(vol_h);
   1206     return ret;
   1207 }
   1208 
   1209 /*
   1210  * Return status of freeze/thaw
   1211  */
   1212 GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
   1213 {
   1214     if (!vss_initialized()) {
   1215         error_setg(errp, QERR_UNSUPPORTED);
   1216         return 0;
   1217     }
   1218 
   1219     if (ga_is_frozen(ga_state)) {
   1220         return GUEST_FSFREEZE_STATUS_FROZEN;
   1221     }
   1222 
   1223     return GUEST_FSFREEZE_STATUS_THAWED;
   1224 }
   1225 
   1226 /*
   1227  * Freeze local file systems using Volume Shadow-copy Service.
   1228  * The frozen state is limited for up to 10 seconds by VSS.
   1229  */
   1230 int64_t qmp_guest_fsfreeze_freeze(Error **errp)
   1231 {
   1232     return qmp_guest_fsfreeze_freeze_list(false, NULL, errp);
   1233 }
   1234 
   1235 int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
   1236                                        strList *mountpoints,
   1237                                        Error **errp)
   1238 {
   1239     int i;
   1240     Error *local_err = NULL;
   1241 
   1242     if (!vss_initialized()) {
   1243         error_setg(errp, QERR_UNSUPPORTED);
   1244         return 0;
   1245     }
   1246 
   1247     slog("guest-fsfreeze called");
   1248 
   1249     /* cannot risk guest agent blocking itself on a write in this state */
   1250     ga_set_frozen(ga_state);
   1251 
   1252     qga_vss_fsfreeze(&i, true, mountpoints, &local_err);
   1253     if (local_err) {
   1254         error_propagate(errp, local_err);
   1255         goto error;
   1256     }
   1257 
   1258     return i;
   1259 
   1260 error:
   1261     local_err = NULL;
   1262     qmp_guest_fsfreeze_thaw(&local_err);
   1263     if (local_err) {
   1264         g_debug("cleanup thaw: %s", error_get_pretty(local_err));
   1265         error_free(local_err);
   1266     }
   1267     return 0;
   1268 }
   1269 
   1270 /*
   1271  * Thaw local file systems using Volume Shadow-copy Service.
   1272  */
   1273 int64_t qmp_guest_fsfreeze_thaw(Error **errp)
   1274 {
   1275     int i;
   1276 
   1277     if (!vss_initialized()) {
   1278         error_setg(errp, QERR_UNSUPPORTED);
   1279         return 0;
   1280     }
   1281 
   1282     qga_vss_fsfreeze(&i, false, NULL, errp);
   1283 
   1284     ga_unset_frozen(ga_state);
   1285     return i;
   1286 }
   1287 
   1288 static void guest_fsfreeze_cleanup(void)
   1289 {
   1290     Error *err = NULL;
   1291 
   1292     if (!vss_initialized()) {
   1293         return;
   1294     }
   1295 
   1296     if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) {
   1297         qmp_guest_fsfreeze_thaw(&err);
   1298         if (err) {
   1299             slog("failed to clean up frozen filesystems: %s",
   1300                  error_get_pretty(err));
   1301             error_free(err);
   1302         }
   1303     }
   1304 
   1305     vss_deinit(true);
   1306 }
   1307 
   1308 /*
   1309  * Walk list of mounted file systems in the guest, and discard unused
   1310  * areas.
   1311  */
   1312 GuestFilesystemTrimResponse *
   1313 qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
   1314 {
   1315     GuestFilesystemTrimResponse *resp;
   1316     HANDLE handle;
   1317     WCHAR guid[MAX_PATH] = L"";
   1318     OSVERSIONINFO osvi;
   1319     BOOL win8_or_later;
   1320 
   1321     ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
   1322     osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
   1323     GetVersionEx(&osvi);
   1324     win8_or_later = (osvi.dwMajorVersion > 6 ||
   1325                           ((osvi.dwMajorVersion == 6) &&
   1326                            (osvi.dwMinorVersion >= 2)));
   1327     if (!win8_or_later) {
   1328         error_setg(errp, "fstrim is only supported for Win8+");
   1329         return NULL;
   1330     }
   1331 
   1332     handle = FindFirstVolumeW(guid, ARRAYSIZE(guid));
   1333     if (handle == INVALID_HANDLE_VALUE) {
   1334         error_setg_win32(errp, GetLastError(), "failed to find any volume");
   1335         return NULL;
   1336     }
   1337 
   1338     resp = g_new0(GuestFilesystemTrimResponse, 1);
   1339 
   1340     do {
   1341         GuestFilesystemTrimResult *res;
   1342         PWCHAR uc_path;
   1343         DWORD char_count = 0;
   1344         char *path, *out;
   1345         GError *gerr = NULL;
   1346         gchar *argv[4];
   1347 
   1348         GetVolumePathNamesForVolumeNameW(guid, NULL, 0, &char_count);
   1349 
   1350         if (GetLastError() != ERROR_MORE_DATA) {
   1351             continue;
   1352         }
   1353         if (GetDriveTypeW(guid) != DRIVE_FIXED) {
   1354             continue;
   1355         }
   1356 
   1357         uc_path = g_new(WCHAR, char_count);
   1358         if (!GetVolumePathNamesForVolumeNameW(guid, uc_path, char_count,
   1359                                               &char_count) || !*uc_path) {
   1360             /* strange, but this condition could be faced even with size == 2 */
   1361             g_free(uc_path);
   1362             continue;
   1363         }
   1364 
   1365         res = g_new0(GuestFilesystemTrimResult, 1);
   1366 
   1367         path = g_utf16_to_utf8(uc_path, char_count, NULL, NULL, &gerr);
   1368 
   1369         g_free(uc_path);
   1370 
   1371         if (!path) {
   1372             res->has_error = true;
   1373             res->error = g_strdup(gerr->message);
   1374             g_error_free(gerr);
   1375             break;
   1376         }
   1377 
   1378         res->path = path;
   1379 
   1380         QAPI_LIST_PREPEND(resp->paths, res);
   1381 
   1382         memset(argv, 0, sizeof(argv));
   1383         argv[0] = (gchar *)"defrag.exe";
   1384         argv[1] = (gchar *)"/L";
   1385         argv[2] = path;
   1386 
   1387         if (!g_spawn_sync(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
   1388                           &out /* stdout */, NULL /* stdin */,
   1389                           NULL, &gerr)) {
   1390             res->has_error = true;
   1391             res->error = g_strdup(gerr->message);
   1392             g_error_free(gerr);
   1393         } else {
   1394             /* defrag.exe is UGLY. Exit code is ALWAYS zero.
   1395                Error is reported in the output with something like
   1396                (x89000020) etc code in the stdout */
   1397 
   1398             int i;
   1399             gchar **lines = g_strsplit(out, "\r\n", 0);
   1400             g_free(out);
   1401 
   1402             for (i = 0; lines[i] != NULL; i++) {
   1403                 if (g_strstr_len(lines[i], -1, "(0x") == NULL) {
   1404                     continue;
   1405                 }
   1406                 res->has_error = true;
   1407                 res->error = g_strdup(lines[i]);
   1408                 break;
   1409             }
   1410             g_strfreev(lines);
   1411         }
   1412     } while (FindNextVolumeW(handle, guid, ARRAYSIZE(guid)));
   1413 
   1414     FindVolumeClose(handle);
   1415     return resp;
   1416 }
   1417 
   1418 typedef enum {
   1419     GUEST_SUSPEND_MODE_DISK,
   1420     GUEST_SUSPEND_MODE_RAM
   1421 } GuestSuspendMode;
   1422 
   1423 static void check_suspend_mode(GuestSuspendMode mode, Error **errp)
   1424 {
   1425     SYSTEM_POWER_CAPABILITIES sys_pwr_caps;
   1426 
   1427     ZeroMemory(&sys_pwr_caps, sizeof(sys_pwr_caps));
   1428     if (!GetPwrCapabilities(&sys_pwr_caps)) {
   1429         error_setg(errp, QERR_QGA_COMMAND_FAILED,
   1430                    "failed to determine guest suspend capabilities");
   1431         return;
   1432     }
   1433 
   1434     switch (mode) {
   1435     case GUEST_SUSPEND_MODE_DISK:
   1436         if (!sys_pwr_caps.SystemS4) {
   1437             error_setg(errp, QERR_QGA_COMMAND_FAILED,
   1438                        "suspend-to-disk not supported by OS");
   1439         }
   1440         break;
   1441     case GUEST_SUSPEND_MODE_RAM:
   1442         if (!sys_pwr_caps.SystemS3) {
   1443             error_setg(errp, QERR_QGA_COMMAND_FAILED,
   1444                        "suspend-to-ram not supported by OS");
   1445         }
   1446         break;
   1447     default:
   1448         abort();
   1449     }
   1450 }
   1451 
   1452 static DWORD WINAPI do_suspend(LPVOID opaque)
   1453 {
   1454     GuestSuspendMode *mode = opaque;
   1455     DWORD ret = 0;
   1456 
   1457     if (!SetSuspendState(*mode == GUEST_SUSPEND_MODE_DISK, TRUE, TRUE)) {
   1458         g_autofree gchar *emsg = g_win32_error_message(GetLastError());
   1459         slog("failed to suspend guest: %s", emsg);
   1460         ret = -1;
   1461     }
   1462     g_free(mode);
   1463     return ret;
   1464 }
   1465 
   1466 void qmp_guest_suspend_disk(Error **errp)
   1467 {
   1468     Error *local_err = NULL;
   1469     GuestSuspendMode *mode = g_new(GuestSuspendMode, 1);
   1470 
   1471     *mode = GUEST_SUSPEND_MODE_DISK;
   1472     check_suspend_mode(*mode, &local_err);
   1473     if (local_err) {
   1474         goto out;
   1475     }
   1476     acquire_privilege(SE_SHUTDOWN_NAME, &local_err);
   1477     if (local_err) {
   1478         goto out;
   1479     }
   1480     execute_async(do_suspend, mode, &local_err);
   1481 
   1482 out:
   1483     if (local_err) {
   1484         error_propagate(errp, local_err);
   1485         g_free(mode);
   1486     }
   1487 }
   1488 
   1489 void qmp_guest_suspend_ram(Error **errp)
   1490 {
   1491     Error *local_err = NULL;
   1492     GuestSuspendMode *mode = g_new(GuestSuspendMode, 1);
   1493 
   1494     *mode = GUEST_SUSPEND_MODE_RAM;
   1495     check_suspend_mode(*mode, &local_err);
   1496     if (local_err) {
   1497         goto out;
   1498     }
   1499     acquire_privilege(SE_SHUTDOWN_NAME, &local_err);
   1500     if (local_err) {
   1501         goto out;
   1502     }
   1503     execute_async(do_suspend, mode, &local_err);
   1504 
   1505 out:
   1506     if (local_err) {
   1507         error_propagate(errp, local_err);
   1508         g_free(mode);
   1509     }
   1510 }
   1511 
   1512 void qmp_guest_suspend_hybrid(Error **errp)
   1513 {
   1514     error_setg(errp, QERR_UNSUPPORTED);
   1515 }
   1516 
   1517 static IP_ADAPTER_ADDRESSES *guest_get_adapters_addresses(Error **errp)
   1518 {
   1519     IP_ADAPTER_ADDRESSES *adptr_addrs = NULL;
   1520     ULONG adptr_addrs_len = 0;
   1521     DWORD ret;
   1522 
   1523     /* Call the first time to get the adptr_addrs_len. */
   1524     GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX,
   1525                          NULL, adptr_addrs, &adptr_addrs_len);
   1526 
   1527     adptr_addrs = g_malloc(adptr_addrs_len);
   1528     ret = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX,
   1529                                NULL, adptr_addrs, &adptr_addrs_len);
   1530     if (ret != ERROR_SUCCESS) {
   1531         error_setg_win32(errp, ret, "failed to get adapters addresses");
   1532         g_free(adptr_addrs);
   1533         adptr_addrs = NULL;
   1534     }
   1535     return adptr_addrs;
   1536 }
   1537 
   1538 static char *guest_wctomb_dup(WCHAR *wstr)
   1539 {
   1540     char *str;
   1541     size_t str_size;
   1542 
   1543     str_size = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
   1544     /* add 1 to str_size for NULL terminator */
   1545     str = g_malloc(str_size + 1);
   1546     WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, str_size, NULL, NULL);
   1547     return str;
   1548 }
   1549 
   1550 static char *guest_addr_to_str(IP_ADAPTER_UNICAST_ADDRESS *ip_addr,
   1551                                Error **errp)
   1552 {
   1553     char addr_str[INET6_ADDRSTRLEN + INET_ADDRSTRLEN];
   1554     DWORD len;
   1555     int ret;
   1556 
   1557     if (ip_addr->Address.lpSockaddr->sa_family == AF_INET ||
   1558             ip_addr->Address.lpSockaddr->sa_family == AF_INET6) {
   1559         len = sizeof(addr_str);
   1560         ret = WSAAddressToString(ip_addr->Address.lpSockaddr,
   1561                                  ip_addr->Address.iSockaddrLength,
   1562                                  NULL,
   1563                                  addr_str,
   1564                                  &len);
   1565         if (ret != 0) {
   1566             error_setg_win32(errp, WSAGetLastError(),
   1567                 "failed address presentation form conversion");
   1568             return NULL;
   1569         }
   1570         return g_strdup(addr_str);
   1571     }
   1572     return NULL;
   1573 }
   1574 
   1575 static int64_t guest_ip_prefix(IP_ADAPTER_UNICAST_ADDRESS *ip_addr)
   1576 {
   1577     /* For Windows Vista/2008 and newer, use the OnLinkPrefixLength
   1578      * field to obtain the prefix.
   1579      */
   1580     return ip_addr->OnLinkPrefixLength;
   1581 }
   1582 
   1583 #define INTERFACE_PATH_BUF_SZ 512
   1584 
   1585 static DWORD get_interface_index(const char *guid)
   1586 {
   1587     ULONG index;
   1588     DWORD status;
   1589     wchar_t wbuf[INTERFACE_PATH_BUF_SZ];
   1590     snwprintf(wbuf, INTERFACE_PATH_BUF_SZ, L"\\device\\tcpip_%s", guid);
   1591     wbuf[INTERFACE_PATH_BUF_SZ - 1] = 0;
   1592     status = GetAdapterIndex (wbuf, &index);
   1593     if (status != NO_ERROR) {
   1594         return (DWORD)~0;
   1595     } else {
   1596         return index;
   1597     }
   1598 }
   1599 
   1600 typedef NETIOAPI_API (WINAPI *GetIfEntry2Func)(PMIB_IF_ROW2 Row);
   1601 
   1602 static int guest_get_network_stats(const char *name,
   1603                                    GuestNetworkInterfaceStat *stats)
   1604 {
   1605     OSVERSIONINFO os_ver;
   1606 
   1607     os_ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
   1608     GetVersionEx(&os_ver);
   1609     if (os_ver.dwMajorVersion >= 6) {
   1610         MIB_IF_ROW2 a_mid_ifrow;
   1611         GetIfEntry2Func getifentry2_ex;
   1612         DWORD if_index = 0;
   1613         HMODULE module = GetModuleHandle("iphlpapi");
   1614         PVOID func = GetProcAddress(module, "GetIfEntry2");
   1615 
   1616         if (func == NULL) {
   1617             return -1;
   1618         }
   1619 
   1620         getifentry2_ex = (GetIfEntry2Func)func;
   1621         if_index = get_interface_index(name);
   1622         if (if_index == (DWORD)~0) {
   1623             return -1;
   1624         }
   1625 
   1626         memset(&a_mid_ifrow, 0, sizeof(a_mid_ifrow));
   1627         a_mid_ifrow.InterfaceIndex = if_index;
   1628         if (NO_ERROR == getifentry2_ex(&a_mid_ifrow)) {
   1629             stats->rx_bytes = a_mid_ifrow.InOctets;
   1630             stats->rx_packets = a_mid_ifrow.InUcastPkts;
   1631             stats->rx_errs = a_mid_ifrow.InErrors;
   1632             stats->rx_dropped = a_mid_ifrow.InDiscards;
   1633             stats->tx_bytes = a_mid_ifrow.OutOctets;
   1634             stats->tx_packets = a_mid_ifrow.OutUcastPkts;
   1635             stats->tx_errs = a_mid_ifrow.OutErrors;
   1636             stats->tx_dropped = a_mid_ifrow.OutDiscards;
   1637             return 0;
   1638         }
   1639     }
   1640     return -1;
   1641 }
   1642 
   1643 GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
   1644 {
   1645     IP_ADAPTER_ADDRESSES *adptr_addrs, *addr;
   1646     IP_ADAPTER_UNICAST_ADDRESS *ip_addr = NULL;
   1647     GuestNetworkInterfaceList *head = NULL, **tail = &head;
   1648     GuestIpAddressList *head_addr, **tail_addr;
   1649     GuestNetworkInterface *info;
   1650     GuestNetworkInterfaceStat *interface_stat = NULL;
   1651     GuestIpAddress *address_item = NULL;
   1652     unsigned char *mac_addr;
   1653     char *addr_str;
   1654     WORD wsa_version;
   1655     WSADATA wsa_data;
   1656     int ret;
   1657 
   1658     adptr_addrs = guest_get_adapters_addresses(errp);
   1659     if (adptr_addrs == NULL) {
   1660         return NULL;
   1661     }
   1662 
   1663     /* Make WSA APIs available. */
   1664     wsa_version = MAKEWORD(2, 2);
   1665     ret = WSAStartup(wsa_version, &wsa_data);
   1666     if (ret != 0) {
   1667         error_setg_win32(errp, ret, "failed socket startup");
   1668         goto out;
   1669     }
   1670 
   1671     for (addr = adptr_addrs; addr; addr = addr->Next) {
   1672         info = g_malloc0(sizeof(*info));
   1673 
   1674         QAPI_LIST_APPEND(tail, info);
   1675 
   1676         info->name = guest_wctomb_dup(addr->FriendlyName);
   1677 
   1678         if (addr->PhysicalAddressLength != 0) {
   1679             mac_addr = addr->PhysicalAddress;
   1680 
   1681             info->hardware_address =
   1682                 g_strdup_printf("%02x:%02x:%02x:%02x:%02x:%02x",
   1683                                 (int) mac_addr[0], (int) mac_addr[1],
   1684                                 (int) mac_addr[2], (int) mac_addr[3],
   1685                                 (int) mac_addr[4], (int) mac_addr[5]);
   1686 
   1687             info->has_hardware_address = true;
   1688         }
   1689 
   1690         head_addr = NULL;
   1691         tail_addr = &head_addr;
   1692         for (ip_addr = addr->FirstUnicastAddress;
   1693                 ip_addr;
   1694                 ip_addr = ip_addr->Next) {
   1695             addr_str = guest_addr_to_str(ip_addr, errp);
   1696             if (addr_str == NULL) {
   1697                 continue;
   1698             }
   1699 
   1700             address_item = g_malloc0(sizeof(*address_item));
   1701 
   1702             QAPI_LIST_APPEND(tail_addr, address_item);
   1703 
   1704             address_item->ip_address = addr_str;
   1705             address_item->prefix = guest_ip_prefix(ip_addr);
   1706             if (ip_addr->Address.lpSockaddr->sa_family == AF_INET) {
   1707                 address_item->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4;
   1708             } else if (ip_addr->Address.lpSockaddr->sa_family == AF_INET6) {
   1709                 address_item->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6;
   1710             }
   1711         }
   1712         if (head_addr) {
   1713             info->has_ip_addresses = true;
   1714             info->ip_addresses = head_addr;
   1715         }
   1716         if (!info->has_statistics) {
   1717             interface_stat = g_malloc0(sizeof(*interface_stat));
   1718             if (guest_get_network_stats(addr->AdapterName,
   1719                 interface_stat) == -1) {
   1720                 info->has_statistics = false;
   1721                 g_free(interface_stat);
   1722             } else {
   1723                 info->statistics = interface_stat;
   1724                 info->has_statistics = true;
   1725             }
   1726         }
   1727     }
   1728     WSACleanup();
   1729 out:
   1730     g_free(adptr_addrs);
   1731     return head;
   1732 }
   1733 
   1734 static int64_t filetime_to_ns(const FILETIME *tf)
   1735 {
   1736     return ((((int64_t)tf->dwHighDateTime << 32) | tf->dwLowDateTime)
   1737             - W32_FT_OFFSET) * 100;
   1738 }
   1739 
   1740 void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp)
   1741 {
   1742     Error *local_err = NULL;
   1743     SYSTEMTIME ts;
   1744     FILETIME tf;
   1745     LONGLONG time;
   1746 
   1747     if (!has_time) {
   1748         /* Unfortunately, Windows libraries don't provide an easy way to access
   1749          * RTC yet:
   1750          *
   1751          * https://msdn.microsoft.com/en-us/library/aa908981.aspx
   1752          *
   1753          * Instead, a workaround is to use the Windows win32tm command to
   1754          * resync the time using the Windows Time service.
   1755          */
   1756         LPVOID msg_buffer;
   1757         DWORD ret_flags;
   1758 
   1759         HRESULT hr = system("w32tm /resync /nowait");
   1760 
   1761         if (GetLastError() != 0) {
   1762             strerror_s((LPTSTR) & msg_buffer, 0, errno);
   1763             error_setg(errp, "system(...) failed: %s", (LPCTSTR)msg_buffer);
   1764         } else if (hr != 0) {
   1765             if (hr == HRESULT_FROM_WIN32(ERROR_SERVICE_NOT_ACTIVE)) {
   1766                 error_setg(errp, "Windows Time service not running on the "
   1767                                  "guest");
   1768             } else {
   1769                 if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
   1770                                    FORMAT_MESSAGE_FROM_SYSTEM |
   1771                                    FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
   1772                                    (DWORD)hr, MAKELANGID(LANG_NEUTRAL,
   1773                                    SUBLANG_DEFAULT), (LPTSTR) & msg_buffer, 0,
   1774                                    NULL)) {
   1775                     error_setg(errp, "w32tm failed with error (0x%lx), couldn'"
   1776                                      "t retrieve error message", hr);
   1777                 } else {
   1778                     error_setg(errp, "w32tm failed with error (0x%lx): %s", hr,
   1779                                (LPCTSTR)msg_buffer);
   1780                     LocalFree(msg_buffer);
   1781                 }
   1782             }
   1783         } else if (!InternetGetConnectedState(&ret_flags, 0)) {
   1784             error_setg(errp, "No internet connection on guest, sync not "
   1785                              "accurate");
   1786         }
   1787         return;
   1788     }
   1789 
   1790     /* Validate time passed by user. */
   1791     if (time_ns < 0 || time_ns / 100 > INT64_MAX - W32_FT_OFFSET) {
   1792         error_setg(errp, "Time %" PRId64 "is invalid", time_ns);
   1793         return;
   1794     }
   1795 
   1796     time = time_ns / 100 + W32_FT_OFFSET;
   1797 
   1798     tf.dwLowDateTime = (DWORD) time;
   1799     tf.dwHighDateTime = (DWORD) (time >> 32);
   1800 
   1801     if (!FileTimeToSystemTime(&tf, &ts)) {
   1802         error_setg(errp, "Failed to convert system time %d",
   1803                    (int)GetLastError());
   1804         return;
   1805     }
   1806 
   1807     acquire_privilege(SE_SYSTEMTIME_NAME, &local_err);
   1808     if (local_err) {
   1809         error_propagate(errp, local_err);
   1810         return;
   1811     }
   1812 
   1813     if (!SetSystemTime(&ts)) {
   1814         error_setg(errp, "Failed to set time to guest: %d", (int)GetLastError());
   1815         return;
   1816     }
   1817 }
   1818 
   1819 GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
   1820 {
   1821     PSYSTEM_LOGICAL_PROCESSOR_INFORMATION pslpi, ptr;
   1822     DWORD length;
   1823     GuestLogicalProcessorList *head, **tail;
   1824     Error *local_err = NULL;
   1825     int64_t current;
   1826 
   1827     ptr = pslpi = NULL;
   1828     length = 0;
   1829     current = 0;
   1830     head = NULL;
   1831     tail = &head;
   1832 
   1833     if ((GetLogicalProcessorInformation(pslpi, &length) == FALSE) &&
   1834         (GetLastError() == ERROR_INSUFFICIENT_BUFFER) &&
   1835         (length > sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION))) {
   1836         ptr = pslpi = g_malloc0(length);
   1837         if (GetLogicalProcessorInformation(pslpi, &length) == FALSE) {
   1838             error_setg(&local_err, "Failed to get processor information: %d",
   1839                        (int)GetLastError());
   1840         }
   1841     } else {
   1842         error_setg(&local_err,
   1843                    "Failed to get processor information buffer length: %d",
   1844                    (int)GetLastError());
   1845     }
   1846 
   1847     while ((local_err == NULL) && (length > 0)) {
   1848         if (pslpi->Relationship == RelationProcessorCore) {
   1849             ULONG_PTR cpu_bits = pslpi->ProcessorMask;
   1850 
   1851             while (cpu_bits > 0) {
   1852                 if (!!(cpu_bits & 1)) {
   1853                     GuestLogicalProcessor *vcpu;
   1854 
   1855                     vcpu = g_malloc0(sizeof *vcpu);
   1856                     vcpu->logical_id = current++;
   1857                     vcpu->online = true;
   1858                     vcpu->has_can_offline = true;
   1859 
   1860                     QAPI_LIST_APPEND(tail, vcpu);
   1861                 }
   1862                 cpu_bits >>= 1;
   1863             }
   1864         }
   1865         length -= sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
   1866         pslpi++; /* next entry */
   1867     }
   1868 
   1869     g_free(ptr);
   1870 
   1871     if (local_err == NULL) {
   1872         if (head != NULL) {
   1873             return head;
   1874         }
   1875         /* there's no guest with zero VCPUs */
   1876         error_setg(&local_err, "Guest reported zero VCPUs");
   1877     }
   1878 
   1879     qapi_free_GuestLogicalProcessorList(head);
   1880     error_propagate(errp, local_err);
   1881     return NULL;
   1882 }
   1883 
   1884 int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
   1885 {
   1886     error_setg(errp, QERR_UNSUPPORTED);
   1887     return -1;
   1888 }
   1889 
   1890 static gchar *
   1891 get_net_error_message(gint error)
   1892 {
   1893     HMODULE module = NULL;
   1894     gchar *retval = NULL;
   1895     wchar_t *msg = NULL;
   1896     int flags;
   1897     size_t nchars;
   1898 
   1899     flags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
   1900         FORMAT_MESSAGE_IGNORE_INSERTS |
   1901         FORMAT_MESSAGE_FROM_SYSTEM;
   1902 
   1903     if (error >= NERR_BASE && error <= MAX_NERR) {
   1904         module = LoadLibraryExW(L"netmsg.dll", NULL, LOAD_LIBRARY_AS_DATAFILE);
   1905 
   1906         if (module != NULL) {
   1907             flags |= FORMAT_MESSAGE_FROM_HMODULE;
   1908         }
   1909     }
   1910 
   1911     FormatMessageW(flags, module, error, 0, (LPWSTR)&msg, 0, NULL);
   1912 
   1913     if (msg != NULL) {
   1914         nchars = wcslen(msg);
   1915 
   1916         if (nchars >= 2 &&
   1917             msg[nchars - 1] == L'\n' &&
   1918             msg[nchars - 2] == L'\r') {
   1919             msg[nchars - 2] = L'\0';
   1920         }
   1921 
   1922         retval = g_utf16_to_utf8(msg, -1, NULL, NULL, NULL);
   1923 
   1924         LocalFree(msg);
   1925     }
   1926 
   1927     if (module != NULL) {
   1928         FreeLibrary(module);
   1929     }
   1930 
   1931     return retval;
   1932 }
   1933 
   1934 void qmp_guest_set_user_password(const char *username,
   1935                                  const char *password,
   1936                                  bool crypted,
   1937                                  Error **errp)
   1938 {
   1939     NET_API_STATUS nas;
   1940     char *rawpasswddata = NULL;
   1941     size_t rawpasswdlen;
   1942     wchar_t *user = NULL, *wpass = NULL;
   1943     USER_INFO_1003 pi1003 = { 0, };
   1944     GError *gerr = NULL;
   1945 
   1946     if (crypted) {
   1947         error_setg(errp, QERR_UNSUPPORTED);
   1948         return;
   1949     }
   1950 
   1951     rawpasswddata = (char *)qbase64_decode(password, -1, &rawpasswdlen, errp);
   1952     if (!rawpasswddata) {
   1953         return;
   1954     }
   1955     rawpasswddata = g_renew(char, rawpasswddata, rawpasswdlen + 1);
   1956     rawpasswddata[rawpasswdlen] = '\0';
   1957 
   1958     user = g_utf8_to_utf16(username, -1, NULL, NULL, &gerr);
   1959     if (!user) {
   1960         goto done;
   1961     }
   1962 
   1963     wpass = g_utf8_to_utf16(rawpasswddata, -1, NULL, NULL, &gerr);
   1964     if (!wpass) {
   1965         goto done;
   1966     }
   1967 
   1968     pi1003.usri1003_password = wpass;
   1969     nas = NetUserSetInfo(NULL, user,
   1970                          1003, (LPBYTE)&pi1003,
   1971                          NULL);
   1972 
   1973     if (nas != NERR_Success) {
   1974         gchar *msg = get_net_error_message(nas);
   1975         error_setg(errp, "failed to set password: %s", msg);
   1976         g_free(msg);
   1977     }
   1978 
   1979 done:
   1980     if (gerr) {
   1981         error_setg(errp, QERR_QGA_COMMAND_FAILED, gerr->message);
   1982         g_error_free(gerr);
   1983     }
   1984     g_free(user);
   1985     g_free(wpass);
   1986     g_free(rawpasswddata);
   1987 }
   1988 
   1989 GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
   1990 {
   1991     error_setg(errp, QERR_UNSUPPORTED);
   1992     return NULL;
   1993 }
   1994 
   1995 GuestMemoryBlockResponseList *
   1996 qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
   1997 {
   1998     error_setg(errp, QERR_UNSUPPORTED);
   1999     return NULL;
   2000 }
   2001 
   2002 GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
   2003 {
   2004     error_setg(errp, QERR_UNSUPPORTED);
   2005     return NULL;
   2006 }
   2007 
   2008 /* add unsupported commands to the list of blocked RPCs */
   2009 GList *ga_command_init_blockedrpcs(GList *blockedrpcs)
   2010 {
   2011     const char *list_unsupported[] = {
   2012         "guest-suspend-hybrid",
   2013         "guest-set-vcpus",
   2014         "guest-get-memory-blocks", "guest-set-memory-blocks",
   2015         "guest-get-memory-block-size", "guest-get-memory-block-info",
   2016         NULL};
   2017     char **p = (char **)list_unsupported;
   2018 
   2019     while (*p) {
   2020         blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++));
   2021     }
   2022 
   2023     if (!vss_init(true)) {
   2024         g_debug("vss_init failed, vss commands are going to be disabled");
   2025         const char *list[] = {
   2026             "guest-get-fsinfo", "guest-fsfreeze-status",
   2027             "guest-fsfreeze-freeze", "guest-fsfreeze-thaw", NULL};
   2028         p = (char **)list;
   2029 
   2030         while (*p) {
   2031             blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++));
   2032         }
   2033     }
   2034 
   2035     return blockedrpcs;
   2036 }
   2037 
   2038 /* register init/cleanup routines for stateful command groups */
   2039 void ga_command_state_init(GAState *s, GACommandState *cs)
   2040 {
   2041     if (!vss_initialized()) {
   2042         ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
   2043     }
   2044 }
   2045 
   2046 /* MINGW is missing two fields: IncomingFrames & OutgoingFrames */
   2047 typedef struct _GA_WTSINFOA {
   2048     WTS_CONNECTSTATE_CLASS State;
   2049     DWORD SessionId;
   2050     DWORD IncomingBytes;
   2051     DWORD OutgoingBytes;
   2052     DWORD IncomingFrames;
   2053     DWORD OutgoingFrames;
   2054     DWORD IncomingCompressedBytes;
   2055     DWORD OutgoingCompressedBy;
   2056     CHAR WinStationName[WINSTATIONNAME_LENGTH];
   2057     CHAR Domain[DOMAIN_LENGTH];
   2058     CHAR UserName[USERNAME_LENGTH + 1];
   2059     LARGE_INTEGER ConnectTime;
   2060     LARGE_INTEGER DisconnectTime;
   2061     LARGE_INTEGER LastInputTime;
   2062     LARGE_INTEGER LogonTime;
   2063     LARGE_INTEGER CurrentTime;
   2064 
   2065 } GA_WTSINFOA;
   2066 
   2067 GuestUserList *qmp_guest_get_users(Error **errp)
   2068 {
   2069 #define QGA_NANOSECONDS 10000000
   2070 
   2071     GHashTable *cache = NULL;
   2072     GuestUserList *head = NULL, **tail = &head;
   2073 
   2074     DWORD buffer_size = 0, count = 0, i = 0;
   2075     GA_WTSINFOA *info = NULL;
   2076     WTS_SESSION_INFOA *entries = NULL;
   2077     GuestUser *user = NULL;
   2078     gpointer value = NULL;
   2079     INT64 login = 0;
   2080     double login_time = 0;
   2081 
   2082     cache = g_hash_table_new(g_str_hash, g_str_equal);
   2083 
   2084     if (WTSEnumerateSessionsA(NULL, 0, 1, &entries, &count)) {
   2085         for (i = 0; i < count; ++i) {
   2086             buffer_size = 0;
   2087             info = NULL;
   2088             if (WTSQuerySessionInformationA(
   2089                 NULL,
   2090                 entries[i].SessionId,
   2091                 WTSSessionInfo,
   2092                 (LPSTR *)&info,
   2093                 &buffer_size
   2094             )) {
   2095 
   2096                 if (strlen(info->UserName) == 0) {
   2097                     WTSFreeMemory(info);
   2098                     continue;
   2099                 }
   2100 
   2101                 login = info->LogonTime.QuadPart;
   2102                 login -= W32_FT_OFFSET;
   2103                 login_time = ((double)login) / QGA_NANOSECONDS;
   2104 
   2105                 if (g_hash_table_contains(cache, info->UserName)) {
   2106                     value = g_hash_table_lookup(cache, info->UserName);
   2107                     user = (GuestUser *)value;
   2108                     if (user->login_time > login_time) {
   2109                         user->login_time = login_time;
   2110                     }
   2111                 } else {
   2112                     user = g_new0(GuestUser, 1);
   2113 
   2114                     user->user = g_strdup(info->UserName);
   2115                     user->domain = g_strdup(info->Domain);
   2116                     user->has_domain = true;
   2117 
   2118                     user->login_time = login_time;
   2119 
   2120                     g_hash_table_add(cache, user->user);
   2121 
   2122                     QAPI_LIST_APPEND(tail, user);
   2123                 }
   2124             }
   2125             WTSFreeMemory(info);
   2126         }
   2127         WTSFreeMemory(entries);
   2128     }
   2129     g_hash_table_destroy(cache);
   2130     return head;
   2131 }
   2132 
   2133 typedef struct _ga_matrix_lookup_t {
   2134     int major;
   2135     int minor;
   2136     char const *version;
   2137     char const *version_id;
   2138 } ga_matrix_lookup_t;
   2139 
   2140 static ga_matrix_lookup_t const WIN_VERSION_MATRIX[2][7] = {
   2141     {
   2142         /* Desktop editions */
   2143         { 5, 0, "Microsoft Windows 2000",   "2000"},
   2144         { 5, 1, "Microsoft Windows XP",     "xp"},
   2145         { 6, 0, "Microsoft Windows Vista",  "vista"},
   2146         { 6, 1, "Microsoft Windows 7"       "7"},
   2147         { 6, 2, "Microsoft Windows 8",      "8"},
   2148         { 6, 3, "Microsoft Windows 8.1",    "8.1"},
   2149         { 0, 0, 0}
   2150     },{
   2151         /* Server editions */
   2152         { 5, 2, "Microsoft Windows Server 2003",        "2003"},
   2153         { 6, 0, "Microsoft Windows Server 2008",        "2008"},
   2154         { 6, 1, "Microsoft Windows Server 2008 R2",     "2008r2"},
   2155         { 6, 2, "Microsoft Windows Server 2012",        "2012"},
   2156         { 6, 3, "Microsoft Windows Server 2012 R2",     "2012r2"},
   2157         { 0, 0, 0},
   2158         { 0, 0, 0}
   2159     }
   2160 };
   2161 
   2162 typedef struct _ga_win_10_0_t {
   2163     int first_build;
   2164     char const *version;
   2165     char const *version_id;
   2166 } ga_win_10_0_t;
   2167 
   2168 static ga_win_10_0_t const WIN_10_0_SERVER_VERSION_MATRIX[4] = {
   2169     {14393, "Microsoft Windows Server 2016",    "2016"},
   2170     {17763, "Microsoft Windows Server 2019",    "2019"},
   2171     {20344, "Microsoft Windows Server 2022",    "2022"},
   2172     {0, 0}
   2173 };
   2174 
   2175 static ga_win_10_0_t const WIN_10_0_CLIENT_VERSION_MATRIX[3] = {
   2176     {10240, "Microsoft Windows 10",    "10"},
   2177     {22000, "Microsoft Windows 11",    "11"},
   2178     {0, 0}
   2179 };
   2180 
   2181 static void ga_get_win_version(RTL_OSVERSIONINFOEXW *info, Error **errp)
   2182 {
   2183     typedef NTSTATUS(WINAPI *rtl_get_version_t)(
   2184         RTL_OSVERSIONINFOEXW *os_version_info_ex);
   2185 
   2186     info->dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
   2187 
   2188     HMODULE module = GetModuleHandle("ntdll");
   2189     PVOID fun = GetProcAddress(module, "RtlGetVersion");
   2190     if (fun == NULL) {
   2191         error_setg(errp, QERR_QGA_COMMAND_FAILED,
   2192             "Failed to get address of RtlGetVersion");
   2193         return;
   2194     }
   2195 
   2196     rtl_get_version_t rtl_get_version = (rtl_get_version_t)fun;
   2197     rtl_get_version(info);
   2198     return;
   2199 }
   2200 
   2201 static char *ga_get_win_name(OSVERSIONINFOEXW const *os_version, bool id)
   2202 {
   2203     DWORD major = os_version->dwMajorVersion;
   2204     DWORD minor = os_version->dwMinorVersion;
   2205     DWORD build = os_version->dwBuildNumber;
   2206     int tbl_idx = (os_version->wProductType != VER_NT_WORKSTATION);
   2207     ga_matrix_lookup_t const *table = WIN_VERSION_MATRIX[tbl_idx];
   2208     ga_win_10_0_t const *win_10_0_table = tbl_idx ?
   2209         WIN_10_0_SERVER_VERSION_MATRIX : WIN_10_0_CLIENT_VERSION_MATRIX;
   2210     ga_win_10_0_t const *win_10_0_version = NULL;
   2211     while (table->version != NULL) {
   2212         if (major == 10 && minor == 0) {
   2213             while (win_10_0_table->version != NULL) {
   2214                 if (build >= win_10_0_table->first_build) {
   2215                     win_10_0_version = win_10_0_table;
   2216                 }
   2217                 win_10_0_table++;
   2218             }
   2219             if (win_10_0_table) {
   2220                 if (id) {
   2221                     return g_strdup(win_10_0_version->version_id);
   2222                 } else {
   2223                     return g_strdup(win_10_0_version->version);
   2224                 }
   2225             }
   2226         } else if (major == table->major && minor == table->minor) {
   2227             if (id) {
   2228                 return g_strdup(table->version_id);
   2229             } else {
   2230                 return g_strdup(table->version);
   2231             }
   2232         }
   2233         ++table;
   2234     }
   2235     slog("failed to lookup Windows version: major=%lu, minor=%lu",
   2236         major, minor);
   2237     return g_strdup("N/A");
   2238 }
   2239 
   2240 static char *ga_get_win_product_name(Error **errp)
   2241 {
   2242     HKEY key = INVALID_HANDLE_VALUE;
   2243     DWORD size = 128;
   2244     char *result = g_malloc0(size);
   2245     LONG err = ERROR_SUCCESS;
   2246 
   2247     err = RegOpenKeyA(HKEY_LOCAL_MACHINE,
   2248                       "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
   2249                       &key);
   2250     if (err != ERROR_SUCCESS) {
   2251         error_setg_win32(errp, err, "failed to open registry key");
   2252         g_free(result);
   2253         return NULL;
   2254     }
   2255 
   2256     err = RegQueryValueExA(key, "ProductName", NULL, NULL,
   2257                             (LPBYTE)result, &size);
   2258     if (err == ERROR_MORE_DATA) {
   2259         slog("ProductName longer than expected (%lu bytes), retrying",
   2260                 size);
   2261         g_free(result);
   2262         result = NULL;
   2263         if (size > 0) {
   2264             result = g_malloc0(size);
   2265             err = RegQueryValueExA(key, "ProductName", NULL, NULL,
   2266                                     (LPBYTE)result, &size);
   2267         }
   2268     }
   2269     if (err != ERROR_SUCCESS) {
   2270         error_setg_win32(errp, err, "failed to retrive ProductName");
   2271         goto fail;
   2272     }
   2273 
   2274     RegCloseKey(key);
   2275     return result;
   2276 
   2277 fail:
   2278     if (key != INVALID_HANDLE_VALUE) {
   2279         RegCloseKey(key);
   2280     }
   2281     g_free(result);
   2282     return NULL;
   2283 }
   2284 
   2285 static char *ga_get_current_arch(void)
   2286 {
   2287     SYSTEM_INFO info;
   2288     GetNativeSystemInfo(&info);
   2289     char *result = NULL;
   2290     switch (info.wProcessorArchitecture) {
   2291     case PROCESSOR_ARCHITECTURE_AMD64:
   2292         result = g_strdup("x86_64");
   2293         break;
   2294     case PROCESSOR_ARCHITECTURE_ARM:
   2295         result = g_strdup("arm");
   2296         break;
   2297     case PROCESSOR_ARCHITECTURE_IA64:
   2298         result = g_strdup("ia64");
   2299         break;
   2300     case PROCESSOR_ARCHITECTURE_INTEL:
   2301         result = g_strdup("x86");
   2302         break;
   2303     case PROCESSOR_ARCHITECTURE_UNKNOWN:
   2304     default:
   2305         slog("unknown processor architecture 0x%0x",
   2306             info.wProcessorArchitecture);
   2307         result = g_strdup("unknown");
   2308         break;
   2309     }
   2310     return result;
   2311 }
   2312 
   2313 GuestOSInfo *qmp_guest_get_osinfo(Error **errp)
   2314 {
   2315     Error *local_err = NULL;
   2316     OSVERSIONINFOEXW os_version = {0};
   2317     bool server;
   2318     char *product_name;
   2319     GuestOSInfo *info;
   2320 
   2321     ga_get_win_version(&os_version, &local_err);
   2322     if (local_err) {
   2323         error_propagate(errp, local_err);
   2324         return NULL;
   2325     }
   2326 
   2327     server = os_version.wProductType != VER_NT_WORKSTATION;
   2328     product_name = ga_get_win_product_name(errp);
   2329     if (product_name == NULL) {
   2330         return NULL;
   2331     }
   2332 
   2333     info = g_new0(GuestOSInfo, 1);
   2334 
   2335     info->has_kernel_version = true;
   2336     info->kernel_version = g_strdup_printf("%lu.%lu",
   2337         os_version.dwMajorVersion,
   2338         os_version.dwMinorVersion);
   2339     info->has_kernel_release = true;
   2340     info->kernel_release = g_strdup_printf("%lu",
   2341         os_version.dwBuildNumber);
   2342     info->has_machine = true;
   2343     info->machine = ga_get_current_arch();
   2344 
   2345     info->has_id = true;
   2346     info->id = g_strdup("mswindows");
   2347     info->has_name = true;
   2348     info->name = g_strdup("Microsoft Windows");
   2349     info->has_pretty_name = true;
   2350     info->pretty_name = product_name;
   2351     info->has_version = true;
   2352     info->version = ga_get_win_name(&os_version, false);
   2353     info->has_version_id = true;
   2354     info->version_id = ga_get_win_name(&os_version, true);
   2355     info->has_variant = true;
   2356     info->variant = g_strdup(server ? "server" : "client");
   2357     info->has_variant_id = true;
   2358     info->variant_id = g_strdup(server ? "server" : "client");
   2359 
   2360     return info;
   2361 }
   2362 
   2363 /*
   2364  * Safely get device property. Returned strings are using wide characters.
   2365  * Caller is responsible for freeing the buffer.
   2366  */
   2367 static LPBYTE cm_get_property(DEVINST devInst, const DEVPROPKEY *propName,
   2368     PDEVPROPTYPE propType)
   2369 {
   2370     CONFIGRET cr;
   2371     g_autofree LPBYTE buffer = NULL;
   2372     ULONG buffer_len = 0;
   2373 
   2374     /* First query for needed space */
   2375     cr = CM_Get_DevNode_PropertyW(devInst, propName, propType,
   2376         buffer, &buffer_len, 0);
   2377     if (cr != CR_SUCCESS && cr != CR_BUFFER_SMALL) {
   2378 
   2379         slog("failed to get property size, error=0x%lx", cr);
   2380         return NULL;
   2381     }
   2382     buffer = g_new0(BYTE, buffer_len + 1);
   2383     cr = CM_Get_DevNode_PropertyW(devInst, propName, propType,
   2384         buffer, &buffer_len, 0);
   2385     if (cr != CR_SUCCESS) {
   2386         slog("failed to get device property, error=0x%lx", cr);
   2387         return NULL;
   2388     }
   2389     return g_steal_pointer(&buffer);
   2390 }
   2391 
   2392 static GStrv ga_get_hardware_ids(DEVINST devInstance)
   2393 {
   2394     GArray *values = NULL;
   2395     DEVPROPTYPE cm_type;
   2396     LPWSTR id;
   2397     g_autofree LPWSTR property = (LPWSTR)cm_get_property(devInstance,
   2398         &qga_DEVPKEY_Device_HardwareIds, &cm_type);
   2399     if (property == NULL) {
   2400         slog("failed to get hardware IDs");
   2401         return NULL;
   2402     }
   2403     if (*property == '\0') {
   2404         /* empty list */
   2405         return NULL;
   2406     }
   2407     values = g_array_new(TRUE, TRUE, sizeof(gchar *));
   2408     for (id = property; '\0' != *id; id += lstrlenW(id) + 1) {
   2409         gchar *id8 = g_utf16_to_utf8(id, -1, NULL, NULL, NULL);
   2410         g_array_append_val(values, id8);
   2411     }
   2412     return (GStrv)g_array_free(values, FALSE);
   2413 }
   2414 
   2415 /*
   2416  * https://docs.microsoft.com/en-us/windows-hardware/drivers/install/identifiers-for-pci-devices
   2417  */
   2418 #define DEVICE_PCI_RE "PCI\\\\VEN_(1AF4|1B36)&DEV_([0-9A-B]{4})(&|$)"
   2419 
   2420 GuestDeviceInfoList *qmp_guest_get_devices(Error **errp)
   2421 {
   2422     GuestDeviceInfoList *head = NULL, **tail = &head;
   2423     HDEVINFO dev_info = INVALID_HANDLE_VALUE;
   2424     SP_DEVINFO_DATA dev_info_data;
   2425     int i, j;
   2426     GError *gerr = NULL;
   2427     g_autoptr(GRegex) device_pci_re = NULL;
   2428     DEVPROPTYPE cm_type;
   2429 
   2430     device_pci_re = g_regex_new(DEVICE_PCI_RE,
   2431         G_REGEX_ANCHORED | G_REGEX_OPTIMIZE, 0,
   2432         &gerr);
   2433     g_assert(device_pci_re != NULL);
   2434 
   2435     dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
   2436     dev_info = SetupDiGetClassDevs(0, 0, 0, DIGCF_PRESENT | DIGCF_ALLCLASSES);
   2437     if (dev_info == INVALID_HANDLE_VALUE) {
   2438         error_setg(errp, "failed to get device tree");
   2439         return NULL;
   2440     }
   2441 
   2442     slog("enumerating devices");
   2443     for (i = 0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) {
   2444         bool skip = true;
   2445         g_autofree LPWSTR name = NULL;
   2446         g_autofree LPFILETIME date = NULL;
   2447         g_autofree LPWSTR version = NULL;
   2448         g_auto(GStrv) hw_ids = NULL;
   2449         g_autoptr(GuestDeviceInfo) device = g_new0(GuestDeviceInfo, 1);
   2450         g_autofree char *vendor_id = NULL;
   2451         g_autofree char *device_id = NULL;
   2452 
   2453         name = (LPWSTR)cm_get_property(dev_info_data.DevInst,
   2454             &qga_DEVPKEY_NAME, &cm_type);
   2455         if (name == NULL) {
   2456             slog("failed to get device description");
   2457             continue;
   2458         }
   2459         device->driver_name = g_utf16_to_utf8(name, -1, NULL, NULL, NULL);
   2460         if (device->driver_name == NULL) {
   2461             error_setg(errp, "conversion to utf8 failed (driver name)");
   2462             return NULL;
   2463         }
   2464         slog("querying device: %s", device->driver_name);
   2465         hw_ids = ga_get_hardware_ids(dev_info_data.DevInst);
   2466         if (hw_ids == NULL) {
   2467             continue;
   2468         }
   2469         for (j = 0; hw_ids[j] != NULL; j++) {
   2470             g_autoptr(GMatchInfo) match_info;
   2471             GuestDeviceIdPCI *id;
   2472             if (!g_regex_match(device_pci_re, hw_ids[j], 0, &match_info)) {
   2473                 continue;
   2474             }
   2475             skip = false;
   2476 
   2477             vendor_id = g_match_info_fetch(match_info, 1);
   2478             device_id = g_match_info_fetch(match_info, 2);
   2479 
   2480             device->id = g_new0(GuestDeviceId, 1);
   2481             device->has_id = true;
   2482             device->id->type = GUEST_DEVICE_TYPE_PCI;
   2483             id = &device->id->u.pci;
   2484             id->vendor_id = g_ascii_strtoull(vendor_id, NULL, 16);
   2485             id->device_id = g_ascii_strtoull(device_id, NULL, 16);
   2486 
   2487             break;
   2488         }
   2489         if (skip) {
   2490             continue;
   2491         }
   2492 
   2493         version = (LPWSTR)cm_get_property(dev_info_data.DevInst,
   2494             &qga_DEVPKEY_Device_DriverVersion, &cm_type);
   2495         if (version == NULL) {
   2496             slog("failed to get driver version");
   2497             continue;
   2498         }
   2499         device->driver_version = g_utf16_to_utf8(version, -1, NULL,
   2500             NULL, NULL);
   2501         if (device->driver_version == NULL) {
   2502             error_setg(errp, "conversion to utf8 failed (driver version)");
   2503             return NULL;
   2504         }
   2505         device->has_driver_version = true;
   2506 
   2507         date = (LPFILETIME)cm_get_property(dev_info_data.DevInst,
   2508             &qga_DEVPKEY_Device_DriverDate, &cm_type);
   2509         if (date == NULL) {
   2510             slog("failed to get driver date");
   2511             continue;
   2512         }
   2513         device->driver_date = filetime_to_ns(date);
   2514         device->has_driver_date = true;
   2515 
   2516         slog("driver: %s\ndriver version: %" PRId64 ",%s\n",
   2517              device->driver_name, device->driver_date,
   2518              device->driver_version);
   2519         QAPI_LIST_APPEND(tail, g_steal_pointer(&device));
   2520     }
   2521 
   2522     if (dev_info != INVALID_HANDLE_VALUE) {
   2523         SetupDiDestroyDeviceInfoList(dev_info);
   2524     }
   2525     return head;
   2526 }
   2527 
   2528 char *qga_get_host_name(Error **errp)
   2529 {
   2530     wchar_t tmp[MAX_COMPUTERNAME_LENGTH + 1];
   2531     DWORD size = G_N_ELEMENTS(tmp);
   2532 
   2533     if (GetComputerNameW(tmp, &size) == 0) {
   2534         error_setg_win32(errp, GetLastError(), "failed close handle");
   2535         return NULL;
   2536     }
   2537 
   2538     return g_utf16_to_utf8(tmp, size, NULL, NULL, NULL);
   2539 }
   2540 
   2541 GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp)
   2542 {
   2543     error_setg(errp, QERR_UNSUPPORTED);
   2544     return NULL;
   2545 }
   2546 
   2547 GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp)
   2548 {
   2549     error_setg(errp, QERR_UNSUPPORTED);
   2550     return NULL;
   2551 }