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 14 #include <glib.h> 15 #include <wtypes.h> 16 #include <powrprof.h> 17 #include <stdio.h> 18 #include <string.h> 19 #include <winsock2.h> 20 #include <ws2tcpip.h> 21 #include <iptypes.h> 22 #include <iphlpapi.h> 23 #ifdef CONFIG_QGA_NTDDSCSI 24 #include <winioctl.h> 25 #include <ntddscsi.h> 26 #include <setupapi.h> 27 #include <initguid.h> 28 #endif 29 #include <lm.h> 30 31 #include "qga/guest-agent-core.h" 32 #include "qga/vss-win32.h" 33 #include "qga-qmp-commands.h" 34 #include "qapi/qmp/qerror.h" 35 #include "qemu/queue.h" 36 #include "qemu/host-utils.h" 37 38 #ifndef SHTDN_REASON_FLAG_PLANNED 39 #define SHTDN_REASON_FLAG_PLANNED 0x80000000 40 #endif 41 42 /* multiple of 100 nanoseconds elapsed between windows baseline 43 * (1/1/1601) and Unix Epoch (1/1/1970), accounting for leap years */ 44 #define W32_FT_OFFSET (10000000ULL * 60 * 60 * 24 * \ 45 (365 * (1970 - 1601) + \ 46 (1970 - 1601) / 4 - 3)) 47 48 #define INVALID_SET_FILE_POINTER ((DWORD)-1) 49 50 typedef struct GuestFileHandle { 51 int64_t id; 52 HANDLE fh; 53 QTAILQ_ENTRY(GuestFileHandle) next; 54 } GuestFileHandle; 55 56 static struct { 57 QTAILQ_HEAD(, GuestFileHandle) filehandles; 58 } guest_file_state = { 59 .filehandles = QTAILQ_HEAD_INITIALIZER(guest_file_state.filehandles), 60 }; 61 62 #define FILE_GENERIC_APPEND (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA) 63 64 typedef struct OpenFlags { 65 const char *forms; 66 DWORD desired_access; 67 DWORD creation_disposition; 68 } OpenFlags; 69 static OpenFlags guest_file_open_modes[] = { 70 {"r", GENERIC_READ, OPEN_EXISTING}, 71 {"rb", GENERIC_READ, OPEN_EXISTING}, 72 {"w", GENERIC_WRITE, CREATE_ALWAYS}, 73 {"wb", GENERIC_WRITE, CREATE_ALWAYS}, 74 {"a", FILE_GENERIC_APPEND, OPEN_ALWAYS }, 75 {"r+", GENERIC_WRITE|GENERIC_READ, OPEN_EXISTING}, 76 {"rb+", GENERIC_WRITE|GENERIC_READ, OPEN_EXISTING}, 77 {"r+b", GENERIC_WRITE|GENERIC_READ, OPEN_EXISTING}, 78 {"w+", GENERIC_WRITE|GENERIC_READ, CREATE_ALWAYS}, 79 {"wb+", GENERIC_WRITE|GENERIC_READ, CREATE_ALWAYS}, 80 {"w+b", GENERIC_WRITE|GENERIC_READ, CREATE_ALWAYS}, 81 {"a+", FILE_GENERIC_APPEND|GENERIC_READ, OPEN_ALWAYS }, 82 {"ab+", FILE_GENERIC_APPEND|GENERIC_READ, OPEN_ALWAYS }, 83 {"a+b", FILE_GENERIC_APPEND|GENERIC_READ, OPEN_ALWAYS } 84 }; 85 86 static OpenFlags *find_open_flag(const char *mode_str) 87 { 88 int mode; 89 Error **errp = NULL; 90 91 for (mode = 0; mode < ARRAY_SIZE(guest_file_open_modes); ++mode) { 92 OpenFlags *flags = guest_file_open_modes + mode; 93 94 if (strcmp(flags->forms, mode_str) == 0) { 95 return flags; 96 } 97 } 98 99 error_setg(errp, "invalid file open mode '%s'", mode_str); 100 return NULL; 101 } 102 103 static int64_t guest_file_handle_add(HANDLE fh, Error **errp) 104 { 105 GuestFileHandle *gfh; 106 int64_t handle; 107 108 handle = ga_get_fd_handle(ga_state, errp); 109 if (handle < 0) { 110 return -1; 111 } 112 gfh = g_new0(GuestFileHandle, 1); 113 gfh->id = handle; 114 gfh->fh = fh; 115 QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next); 116 117 return handle; 118 } 119 120 static GuestFileHandle *guest_file_handle_find(int64_t id, Error **errp) 121 { 122 GuestFileHandle *gfh; 123 QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next) { 124 if (gfh->id == id) { 125 return gfh; 126 } 127 } 128 error_setg(errp, "handle '%" PRId64 "' has not been found", id); 129 return NULL; 130 } 131 132 static void handle_set_nonblocking(HANDLE fh) 133 { 134 DWORD file_type, pipe_state; 135 file_type = GetFileType(fh); 136 if (file_type != FILE_TYPE_PIPE) { 137 return; 138 } 139 /* If file_type == FILE_TYPE_PIPE, according to MSDN 140 * the specified file is socket or named pipe */ 141 if (!GetNamedPipeHandleState(fh, &pipe_state, NULL, 142 NULL, NULL, NULL, 0)) { 143 return; 144 } 145 /* The fd is named pipe fd */ 146 if (pipe_state & PIPE_NOWAIT) { 147 return; 148 } 149 150 pipe_state |= PIPE_NOWAIT; 151 SetNamedPipeHandleState(fh, &pipe_state, NULL, NULL); 152 } 153 154 int64_t qmp_guest_file_open(const char *path, bool has_mode, 155 const char *mode, Error **errp) 156 { 157 int64_t fd; 158 HANDLE fh; 159 HANDLE templ_file = NULL; 160 DWORD share_mode = FILE_SHARE_READ; 161 DWORD flags_and_attr = FILE_ATTRIBUTE_NORMAL; 162 LPSECURITY_ATTRIBUTES sa_attr = NULL; 163 OpenFlags *guest_flags; 164 165 if (!has_mode) { 166 mode = "r"; 167 } 168 slog("guest-file-open called, filepath: %s, mode: %s", path, mode); 169 guest_flags = find_open_flag(mode); 170 if (guest_flags == NULL) { 171 error_setg(errp, "invalid file open mode"); 172 return -1; 173 } 174 175 fh = CreateFile(path, guest_flags->desired_access, share_mode, sa_attr, 176 guest_flags->creation_disposition, flags_and_attr, 177 templ_file); 178 if (fh == INVALID_HANDLE_VALUE) { 179 error_setg_win32(errp, GetLastError(), "failed to open file '%s'", 180 path); 181 return -1; 182 } 183 184 /* set fd non-blocking to avoid common use cases (like reading from a 185 * named pipe) from hanging the agent 186 */ 187 handle_set_nonblocking(fh); 188 189 fd = guest_file_handle_add(fh, errp); 190 if (fd < 0) { 191 CloseHandle(fh); 192 error_setg(errp, "failed to add handle to qmp handle table"); 193 return -1; 194 } 195 196 slog("guest-file-open, handle: % " PRId64, fd); 197 return fd; 198 } 199 200 void qmp_guest_file_close(int64_t handle, Error **errp) 201 { 202 bool ret; 203 GuestFileHandle *gfh = guest_file_handle_find(handle, errp); 204 slog("guest-file-close called, handle: %" PRId64, handle); 205 if (gfh == NULL) { 206 return; 207 } 208 ret = CloseHandle(gfh->fh); 209 if (!ret) { 210 error_setg_win32(errp, GetLastError(), "failed close handle"); 211 return; 212 } 213 214 QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next); 215 g_free(gfh); 216 } 217 218 static void acquire_privilege(const char *name, Error **errp) 219 { 220 HANDLE token = NULL; 221 TOKEN_PRIVILEGES priv; 222 Error *local_err = NULL; 223 224 if (OpenProcessToken(GetCurrentProcess(), 225 TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &token)) 226 { 227 if (!LookupPrivilegeValue(NULL, name, &priv.Privileges[0].Luid)) { 228 error_setg(&local_err, QERR_QGA_COMMAND_FAILED, 229 "no luid for requested privilege"); 230 goto out; 231 } 232 233 priv.PrivilegeCount = 1; 234 priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 235 236 if (!AdjustTokenPrivileges(token, FALSE, &priv, 0, NULL, 0)) { 237 error_setg(&local_err, QERR_QGA_COMMAND_FAILED, 238 "unable to acquire requested privilege"); 239 goto out; 240 } 241 242 } else { 243 error_setg(&local_err, QERR_QGA_COMMAND_FAILED, 244 "failed to open privilege token"); 245 } 246 247 out: 248 if (token) { 249 CloseHandle(token); 250 } 251 if (local_err) { 252 error_propagate(errp, local_err); 253 } 254 } 255 256 static void execute_async(DWORD WINAPI (*func)(LPVOID), LPVOID opaque, 257 Error **errp) 258 { 259 Error *local_err = NULL; 260 261 HANDLE thread = CreateThread(NULL, 0, func, opaque, 0, NULL); 262 if (!thread) { 263 error_setg(&local_err, QERR_QGA_COMMAND_FAILED, 264 "failed to dispatch asynchronous command"); 265 error_propagate(errp, local_err); 266 } 267 } 268 269 void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp) 270 { 271 Error *local_err = NULL; 272 UINT shutdown_flag = EWX_FORCE; 273 274 slog("guest-shutdown called, mode: %s", mode); 275 276 if (!has_mode || strcmp(mode, "powerdown") == 0) { 277 shutdown_flag |= EWX_POWEROFF; 278 } else if (strcmp(mode, "halt") == 0) { 279 shutdown_flag |= EWX_SHUTDOWN; 280 } else if (strcmp(mode, "reboot") == 0) { 281 shutdown_flag |= EWX_REBOOT; 282 } else { 283 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "mode", 284 "halt|powerdown|reboot"); 285 return; 286 } 287 288 /* Request a shutdown privilege, but try to shut down the system 289 anyway. */ 290 acquire_privilege(SE_SHUTDOWN_NAME, &local_err); 291 if (local_err) { 292 error_propagate(errp, local_err); 293 return; 294 } 295 296 if (!ExitWindowsEx(shutdown_flag, SHTDN_REASON_FLAG_PLANNED)) { 297 slog("guest-shutdown failed: %lu", GetLastError()); 298 error_setg(errp, QERR_UNDEFINED_ERROR); 299 } 300 } 301 302 GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, 303 int64_t count, Error **errp) 304 { 305 GuestFileRead *read_data = NULL; 306 guchar *buf; 307 HANDLE fh; 308 bool is_ok; 309 DWORD read_count; 310 GuestFileHandle *gfh = guest_file_handle_find(handle, errp); 311 312 if (!gfh) { 313 return NULL; 314 } 315 if (!has_count) { 316 count = QGA_READ_COUNT_DEFAULT; 317 } else if (count < 0) { 318 error_setg(errp, "value '%" PRId64 319 "' is invalid for argument count", count); 320 return NULL; 321 } 322 323 fh = gfh->fh; 324 buf = g_malloc0(count+1); 325 is_ok = ReadFile(fh, buf, count, &read_count, NULL); 326 if (!is_ok) { 327 error_setg_win32(errp, GetLastError(), "failed to read file"); 328 slog("guest-file-read failed, handle %" PRId64, handle); 329 } else { 330 buf[read_count] = 0; 331 read_data = g_new0(GuestFileRead, 1); 332 read_data->count = (size_t)read_count; 333 read_data->eof = read_count == 0; 334 335 if (read_count != 0) { 336 read_data->buf_b64 = g_base64_encode(buf, read_count); 337 } 338 } 339 g_free(buf); 340 341 return read_data; 342 } 343 344 GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64, 345 bool has_count, int64_t count, 346 Error **errp) 347 { 348 GuestFileWrite *write_data = NULL; 349 guchar *buf; 350 gsize buf_len; 351 bool is_ok; 352 DWORD write_count; 353 GuestFileHandle *gfh = guest_file_handle_find(handle, errp); 354 HANDLE fh; 355 356 if (!gfh) { 357 return NULL; 358 } 359 fh = gfh->fh; 360 buf = g_base64_decode(buf_b64, &buf_len); 361 362 if (!has_count) { 363 count = buf_len; 364 } else if (count < 0 || count > buf_len) { 365 error_setg(errp, "value '%" PRId64 366 "' is invalid for argument count", count); 367 goto done; 368 } 369 370 is_ok = WriteFile(fh, buf, count, &write_count, NULL); 371 if (!is_ok) { 372 error_setg_win32(errp, GetLastError(), "failed to write to file"); 373 slog("guest-file-write-failed, handle: %" PRId64, handle); 374 } else { 375 write_data = g_new0(GuestFileWrite, 1); 376 write_data->count = (size_t) write_count; 377 } 378 379 done: 380 g_free(buf); 381 return write_data; 382 } 383 384 GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset, 385 int64_t whence_code, Error **errp) 386 { 387 GuestFileHandle *gfh; 388 GuestFileSeek *seek_data; 389 HANDLE fh; 390 LARGE_INTEGER new_pos, off_pos; 391 off_pos.QuadPart = offset; 392 BOOL res; 393 int whence; 394 395 gfh = guest_file_handle_find(handle, errp); 396 if (!gfh) { 397 return NULL; 398 } 399 400 /* We stupidly exposed 'whence':'int' in our qapi */ 401 switch (whence_code) { 402 case QGA_SEEK_SET: 403 whence = SEEK_SET; 404 break; 405 case QGA_SEEK_CUR: 406 whence = SEEK_CUR; 407 break; 408 case QGA_SEEK_END: 409 whence = SEEK_END; 410 break; 411 default: 412 error_setg(errp, "invalid whence code %"PRId64, whence_code); 413 return NULL; 414 } 415 416 fh = gfh->fh; 417 res = SetFilePointerEx(fh, off_pos, &new_pos, whence); 418 if (!res) { 419 error_setg_win32(errp, GetLastError(), "failed to seek file"); 420 return NULL; 421 } 422 seek_data = g_new0(GuestFileSeek, 1); 423 seek_data->position = new_pos.QuadPart; 424 return seek_data; 425 } 426 427 void qmp_guest_file_flush(int64_t handle, Error **errp) 428 { 429 HANDLE fh; 430 GuestFileHandle *gfh = guest_file_handle_find(handle, errp); 431 if (!gfh) { 432 return; 433 } 434 435 fh = gfh->fh; 436 if (!FlushFileBuffers(fh)) { 437 error_setg_win32(errp, GetLastError(), "failed to flush file"); 438 } 439 } 440 441 #ifdef CONFIG_QGA_NTDDSCSI 442 443 static STORAGE_BUS_TYPE win2qemu[] = { 444 [BusTypeUnknown] = GUEST_DISK_BUS_TYPE_UNKNOWN, 445 [BusTypeScsi] = GUEST_DISK_BUS_TYPE_SCSI, 446 [BusTypeAtapi] = GUEST_DISK_BUS_TYPE_IDE, 447 [BusTypeAta] = GUEST_DISK_BUS_TYPE_IDE, 448 [BusType1394] = GUEST_DISK_BUS_TYPE_IEEE1394, 449 [BusTypeSsa] = GUEST_DISK_BUS_TYPE_SSA, 450 [BusTypeFibre] = GUEST_DISK_BUS_TYPE_SSA, 451 [BusTypeUsb] = GUEST_DISK_BUS_TYPE_USB, 452 [BusTypeRAID] = GUEST_DISK_BUS_TYPE_RAID, 453 #if (_WIN32_WINNT >= 0x0600) 454 [BusTypeiScsi] = GUEST_DISK_BUS_TYPE_ISCSI, 455 [BusTypeSas] = GUEST_DISK_BUS_TYPE_SAS, 456 [BusTypeSata] = GUEST_DISK_BUS_TYPE_SATA, 457 [BusTypeSd] = GUEST_DISK_BUS_TYPE_SD, 458 [BusTypeMmc] = GUEST_DISK_BUS_TYPE_MMC, 459 #endif 460 #if (_WIN32_WINNT >= 0x0601) 461 [BusTypeVirtual] = GUEST_DISK_BUS_TYPE_VIRTUAL, 462 [BusTypeFileBackedVirtual] = GUEST_DISK_BUS_TYPE_FILE_BACKED_VIRTUAL, 463 #endif 464 }; 465 466 static GuestDiskBusType find_bus_type(STORAGE_BUS_TYPE bus) 467 { 468 if (bus > ARRAY_SIZE(win2qemu) || (int)bus < 0) { 469 return GUEST_DISK_BUS_TYPE_UNKNOWN; 470 } 471 return win2qemu[(int)bus]; 472 } 473 474 DEFINE_GUID(GUID_DEVINTERFACE_VOLUME, 475 0x53f5630dL, 0xb6bf, 0x11d0, 0x94, 0xf2, 476 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); 477 478 static GuestPCIAddress *get_pci_info(char *guid, Error **errp) 479 { 480 HDEVINFO dev_info; 481 SP_DEVINFO_DATA dev_info_data; 482 DWORD size = 0; 483 int i; 484 char dev_name[MAX_PATH]; 485 char *buffer = NULL; 486 GuestPCIAddress *pci = NULL; 487 char *name = g_strdup(&guid[4]); 488 489 if (!QueryDosDevice(name, dev_name, ARRAY_SIZE(dev_name))) { 490 error_setg_win32(errp, GetLastError(), "failed to get dos device name"); 491 goto out; 492 } 493 494 dev_info = SetupDiGetClassDevs(&GUID_DEVINTERFACE_VOLUME, 0, 0, 495 DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); 496 if (dev_info == INVALID_HANDLE_VALUE) { 497 error_setg_win32(errp, GetLastError(), "failed to get devices tree"); 498 goto out; 499 } 500 501 dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA); 502 for (i = 0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) { 503 DWORD addr, bus, slot, func, dev, data, size2; 504 while (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, 505 SPDRP_PHYSICAL_DEVICE_OBJECT_NAME, 506 &data, (PBYTE)buffer, size, 507 &size2)) { 508 size = MAX(size, size2); 509 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { 510 g_free(buffer); 511 /* Double the size to avoid problems on 512 * W2k MBCS systems per KB 888609. 513 * https://support.microsoft.com/en-us/kb/259695 */ 514 buffer = g_malloc(size * 2); 515 } else { 516 error_setg_win32(errp, GetLastError(), 517 "failed to get device name"); 518 goto out; 519 } 520 } 521 522 if (g_strcmp0(buffer, dev_name)) { 523 continue; 524 } 525 526 /* There is no need to allocate buffer in the next functions. The size 527 * is known and ULONG according to 528 * https://support.microsoft.com/en-us/kb/253232 529 * https://msdn.microsoft.com/en-us/library/windows/hardware/ff543095(v=vs.85).aspx 530 */ 531 if (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, 532 SPDRP_BUSNUMBER, &data, (PBYTE)&bus, size, NULL)) { 533 break; 534 } 535 536 /* The function retrieves the device's address. This value will be 537 * transformed into device function and number */ 538 if (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, 539 SPDRP_ADDRESS, &data, (PBYTE)&addr, size, NULL)) { 540 break; 541 } 542 543 /* This call returns UINumber of DEVICE_CAPABILITIES structure. 544 * This number is typically a user-perceived slot number. */ 545 if (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, 546 SPDRP_UI_NUMBER, &data, (PBYTE)&slot, size, NULL)) { 547 break; 548 } 549 550 /* SetupApi gives us the same information as driver with 551 * IoGetDeviceProperty. According to Microsoft 552 * https://support.microsoft.com/en-us/kb/253232 553 * FunctionNumber = (USHORT)((propertyAddress) & 0x0000FFFF); 554 * DeviceNumber = (USHORT)(((propertyAddress) >> 16) & 0x0000FFFF); 555 * SPDRP_ADDRESS is propertyAddress, so we do the same.*/ 556 557 func = addr & 0x0000FFFF; 558 dev = (addr >> 16) & 0x0000FFFF; 559 pci = g_malloc0(sizeof(*pci)); 560 pci->domain = dev; 561 pci->slot = slot; 562 pci->function = func; 563 pci->bus = bus; 564 break; 565 } 566 out: 567 g_free(buffer); 568 g_free(name); 569 return pci; 570 } 571 572 static int get_disk_bus_type(HANDLE vol_h, Error **errp) 573 { 574 STORAGE_PROPERTY_QUERY query; 575 STORAGE_DEVICE_DESCRIPTOR *dev_desc, buf; 576 DWORD received; 577 578 dev_desc = &buf; 579 dev_desc->Size = sizeof(buf); 580 query.PropertyId = StorageDeviceProperty; 581 query.QueryType = PropertyStandardQuery; 582 583 if (!DeviceIoControl(vol_h, IOCTL_STORAGE_QUERY_PROPERTY, &query, 584 sizeof(STORAGE_PROPERTY_QUERY), dev_desc, 585 dev_desc->Size, &received, NULL)) { 586 error_setg_win32(errp, GetLastError(), "failed to get bus type"); 587 return -1; 588 } 589 590 return dev_desc->BusType; 591 } 592 593 /* VSS provider works with volumes, thus there is no difference if 594 * the volume consist of spanned disks. Info about the first disk in the 595 * volume is returned for the spanned disk group (LVM) */ 596 static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp) 597 { 598 GuestDiskAddressList *list = NULL; 599 GuestDiskAddress *disk; 600 SCSI_ADDRESS addr, *scsi_ad; 601 DWORD len; 602 int bus; 603 HANDLE vol_h; 604 605 scsi_ad = &addr; 606 char *name = g_strndup(guid, strlen(guid)-1); 607 608 vol_h = CreateFile(name, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, 609 0, NULL); 610 if (vol_h == INVALID_HANDLE_VALUE) { 611 error_setg_win32(errp, GetLastError(), "failed to open volume"); 612 goto out_free; 613 } 614 615 bus = get_disk_bus_type(vol_h, errp); 616 if (bus < 0) { 617 goto out_close; 618 } 619 620 disk = g_malloc0(sizeof(*disk)); 621 disk->bus_type = find_bus_type(bus); 622 if (bus == BusTypeScsi || bus == BusTypeAta || bus == BusTypeRAID 623 #if (_WIN32_WINNT >= 0x0600) 624 /* This bus type is not supported before Windows Server 2003 SP1 */ 625 || bus == BusTypeSas 626 #endif 627 ) { 628 /* We are able to use the same ioctls for different bus types 629 * according to Microsoft docs 630 * https://technet.microsoft.com/en-us/library/ee851589(v=ws.10).aspx */ 631 if (DeviceIoControl(vol_h, IOCTL_SCSI_GET_ADDRESS, NULL, 0, scsi_ad, 632 sizeof(SCSI_ADDRESS), &len, NULL)) { 633 disk->unit = addr.Lun; 634 disk->target = addr.TargetId; 635 disk->bus = addr.PathId; 636 disk->pci_controller = get_pci_info(name, errp); 637 } 638 /* We do not set error in this case, because we still have enough 639 * information about volume. */ 640 } else { 641 disk->pci_controller = NULL; 642 } 643 644 list = g_malloc0(sizeof(*list)); 645 list->value = disk; 646 list->next = NULL; 647 out_close: 648 CloseHandle(vol_h); 649 out_free: 650 g_free(name); 651 return list; 652 } 653 654 #else 655 656 static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp) 657 { 658 return NULL; 659 } 660 661 #endif /* CONFIG_QGA_NTDDSCSI */ 662 663 static GuestFilesystemInfo *build_guest_fsinfo(char *guid, Error **errp) 664 { 665 DWORD info_size; 666 char mnt, *mnt_point; 667 char fs_name[32]; 668 char vol_info[MAX_PATH+1]; 669 size_t len; 670 GuestFilesystemInfo *fs = NULL; 671 672 GetVolumePathNamesForVolumeName(guid, (LPCH)&mnt, 0, &info_size); 673 if (GetLastError() != ERROR_MORE_DATA) { 674 error_setg_win32(errp, GetLastError(), "failed to get volume name"); 675 return NULL; 676 } 677 678 mnt_point = g_malloc(info_size + 1); 679 if (!GetVolumePathNamesForVolumeName(guid, mnt_point, info_size, 680 &info_size)) { 681 error_setg_win32(errp, GetLastError(), "failed to get volume name"); 682 goto free; 683 } 684 685 len = strlen(mnt_point); 686 mnt_point[len] = '\\'; 687 mnt_point[len+1] = 0; 688 if (!GetVolumeInformation(mnt_point, vol_info, sizeof(vol_info), NULL, NULL, 689 NULL, (LPSTR)&fs_name, sizeof(fs_name))) { 690 if (GetLastError() != ERROR_NOT_READY) { 691 error_setg_win32(errp, GetLastError(), "failed to get volume info"); 692 } 693 goto free; 694 } 695 696 fs_name[sizeof(fs_name) - 1] = 0; 697 fs = g_malloc(sizeof(*fs)); 698 fs->name = g_strdup(guid); 699 if (len == 0) { 700 fs->mountpoint = g_strdup("System Reserved"); 701 } else { 702 fs->mountpoint = g_strndup(mnt_point, len); 703 } 704 fs->type = g_strdup(fs_name); 705 fs->disk = build_guest_disk_info(guid, errp); 706 free: 707 g_free(mnt_point); 708 return fs; 709 } 710 711 GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp) 712 { 713 HANDLE vol_h; 714 GuestFilesystemInfoList *new, *ret = NULL; 715 char guid[256]; 716 717 vol_h = FindFirstVolume(guid, sizeof(guid)); 718 if (vol_h == INVALID_HANDLE_VALUE) { 719 error_setg_win32(errp, GetLastError(), "failed to find any volume"); 720 return NULL; 721 } 722 723 do { 724 GuestFilesystemInfo *info = build_guest_fsinfo(guid, errp); 725 if (info == NULL) { 726 continue; 727 } 728 new = g_malloc(sizeof(*ret)); 729 new->value = info; 730 new->next = ret; 731 ret = new; 732 } while (FindNextVolume(vol_h, guid, sizeof(guid))); 733 734 if (GetLastError() != ERROR_NO_MORE_FILES) { 735 error_setg_win32(errp, GetLastError(), "failed to find next volume"); 736 } 737 738 FindVolumeClose(vol_h); 739 return ret; 740 } 741 742 /* 743 * Return status of freeze/thaw 744 */ 745 GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp) 746 { 747 if (!vss_initialized()) { 748 error_setg(errp, QERR_UNSUPPORTED); 749 return 0; 750 } 751 752 if (ga_is_frozen(ga_state)) { 753 return GUEST_FSFREEZE_STATUS_FROZEN; 754 } 755 756 return GUEST_FSFREEZE_STATUS_THAWED; 757 } 758 759 /* 760 * Freeze local file systems using Volume Shadow-copy Service. 761 * The frozen state is limited for up to 10 seconds by VSS. 762 */ 763 int64_t qmp_guest_fsfreeze_freeze(Error **errp) 764 { 765 int i; 766 Error *local_err = NULL; 767 768 if (!vss_initialized()) { 769 error_setg(errp, QERR_UNSUPPORTED); 770 return 0; 771 } 772 773 slog("guest-fsfreeze called"); 774 775 /* cannot risk guest agent blocking itself on a write in this state */ 776 ga_set_frozen(ga_state); 777 778 qga_vss_fsfreeze(&i, &local_err, true); 779 if (local_err) { 780 error_propagate(errp, local_err); 781 goto error; 782 } 783 784 return i; 785 786 error: 787 local_err = NULL; 788 qmp_guest_fsfreeze_thaw(&local_err); 789 if (local_err) { 790 g_debug("cleanup thaw: %s", error_get_pretty(local_err)); 791 error_free(local_err); 792 } 793 return 0; 794 } 795 796 int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints, 797 strList *mountpoints, 798 Error **errp) 799 { 800 error_setg(errp, QERR_UNSUPPORTED); 801 802 return 0; 803 } 804 805 /* 806 * Thaw local file systems using Volume Shadow-copy Service. 807 */ 808 int64_t qmp_guest_fsfreeze_thaw(Error **errp) 809 { 810 int i; 811 812 if (!vss_initialized()) { 813 error_setg(errp, QERR_UNSUPPORTED); 814 return 0; 815 } 816 817 qga_vss_fsfreeze(&i, errp, false); 818 819 ga_unset_frozen(ga_state); 820 return i; 821 } 822 823 static void guest_fsfreeze_cleanup(void) 824 { 825 Error *err = NULL; 826 827 if (!vss_initialized()) { 828 return; 829 } 830 831 if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) { 832 qmp_guest_fsfreeze_thaw(&err); 833 if (err) { 834 slog("failed to clean up frozen filesystems: %s", 835 error_get_pretty(err)); 836 error_free(err); 837 } 838 } 839 840 vss_deinit(true); 841 } 842 843 /* 844 * Walk list of mounted file systems in the guest, and discard unused 845 * areas. 846 */ 847 GuestFilesystemTrimResponse * 848 qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) 849 { 850 error_setg(errp, QERR_UNSUPPORTED); 851 return NULL; 852 } 853 854 typedef enum { 855 GUEST_SUSPEND_MODE_DISK, 856 GUEST_SUSPEND_MODE_RAM 857 } GuestSuspendMode; 858 859 static void check_suspend_mode(GuestSuspendMode mode, Error **errp) 860 { 861 SYSTEM_POWER_CAPABILITIES sys_pwr_caps; 862 Error *local_err = NULL; 863 864 ZeroMemory(&sys_pwr_caps, sizeof(sys_pwr_caps)); 865 if (!GetPwrCapabilities(&sys_pwr_caps)) { 866 error_setg(&local_err, QERR_QGA_COMMAND_FAILED, 867 "failed to determine guest suspend capabilities"); 868 goto out; 869 } 870 871 switch (mode) { 872 case GUEST_SUSPEND_MODE_DISK: 873 if (!sys_pwr_caps.SystemS4) { 874 error_setg(&local_err, QERR_QGA_COMMAND_FAILED, 875 "suspend-to-disk not supported by OS"); 876 } 877 break; 878 case GUEST_SUSPEND_MODE_RAM: 879 if (!sys_pwr_caps.SystemS3) { 880 error_setg(&local_err, QERR_QGA_COMMAND_FAILED, 881 "suspend-to-ram not supported by OS"); 882 } 883 break; 884 default: 885 error_setg(&local_err, QERR_INVALID_PARAMETER_VALUE, "mode", 886 "GuestSuspendMode"); 887 } 888 889 out: 890 if (local_err) { 891 error_propagate(errp, local_err); 892 } 893 } 894 895 static DWORD WINAPI do_suspend(LPVOID opaque) 896 { 897 GuestSuspendMode *mode = opaque; 898 DWORD ret = 0; 899 900 if (!SetSuspendState(*mode == GUEST_SUSPEND_MODE_DISK, TRUE, TRUE)) { 901 slog("failed to suspend guest, %lu", GetLastError()); 902 ret = -1; 903 } 904 g_free(mode); 905 return ret; 906 } 907 908 void qmp_guest_suspend_disk(Error **errp) 909 { 910 Error *local_err = NULL; 911 GuestSuspendMode *mode = g_new(GuestSuspendMode, 1); 912 913 *mode = GUEST_SUSPEND_MODE_DISK; 914 check_suspend_mode(*mode, &local_err); 915 acquire_privilege(SE_SHUTDOWN_NAME, &local_err); 916 execute_async(do_suspend, mode, &local_err); 917 918 if (local_err) { 919 error_propagate(errp, local_err); 920 g_free(mode); 921 } 922 } 923 924 void qmp_guest_suspend_ram(Error **errp) 925 { 926 Error *local_err = NULL; 927 GuestSuspendMode *mode = g_new(GuestSuspendMode, 1); 928 929 *mode = GUEST_SUSPEND_MODE_RAM; 930 check_suspend_mode(*mode, &local_err); 931 acquire_privilege(SE_SHUTDOWN_NAME, &local_err); 932 execute_async(do_suspend, mode, &local_err); 933 934 if (local_err) { 935 error_propagate(errp, local_err); 936 g_free(mode); 937 } 938 } 939 940 void qmp_guest_suspend_hybrid(Error **errp) 941 { 942 error_setg(errp, QERR_UNSUPPORTED); 943 } 944 945 static IP_ADAPTER_ADDRESSES *guest_get_adapters_addresses(Error **errp) 946 { 947 IP_ADAPTER_ADDRESSES *adptr_addrs = NULL; 948 ULONG adptr_addrs_len = 0; 949 DWORD ret; 950 951 /* Call the first time to get the adptr_addrs_len. */ 952 GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, 953 NULL, adptr_addrs, &adptr_addrs_len); 954 955 adptr_addrs = g_malloc(adptr_addrs_len); 956 ret = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, 957 NULL, adptr_addrs, &adptr_addrs_len); 958 if (ret != ERROR_SUCCESS) { 959 error_setg_win32(errp, ret, "failed to get adapters addresses"); 960 g_free(adptr_addrs); 961 adptr_addrs = NULL; 962 } 963 return adptr_addrs; 964 } 965 966 static char *guest_wctomb_dup(WCHAR *wstr) 967 { 968 char *str; 969 size_t i; 970 971 i = wcslen(wstr) + 1; 972 str = g_malloc(i); 973 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, 974 wstr, -1, str, i, NULL, NULL); 975 return str; 976 } 977 978 static char *guest_addr_to_str(IP_ADAPTER_UNICAST_ADDRESS *ip_addr, 979 Error **errp) 980 { 981 char addr_str[INET6_ADDRSTRLEN + INET_ADDRSTRLEN]; 982 DWORD len; 983 int ret; 984 985 if (ip_addr->Address.lpSockaddr->sa_family == AF_INET || 986 ip_addr->Address.lpSockaddr->sa_family == AF_INET6) { 987 len = sizeof(addr_str); 988 ret = WSAAddressToString(ip_addr->Address.lpSockaddr, 989 ip_addr->Address.iSockaddrLength, 990 NULL, 991 addr_str, 992 &len); 993 if (ret != 0) { 994 error_setg_win32(errp, WSAGetLastError(), 995 "failed address presentation form conversion"); 996 return NULL; 997 } 998 return g_strdup(addr_str); 999 } 1000 return NULL; 1001 } 1002 1003 #if (_WIN32_WINNT >= 0x0600) 1004 static int64_t guest_ip_prefix(IP_ADAPTER_UNICAST_ADDRESS *ip_addr) 1005 { 1006 /* For Windows Vista/2008 and newer, use the OnLinkPrefixLength 1007 * field to obtain the prefix. 1008 */ 1009 return ip_addr->OnLinkPrefixLength; 1010 } 1011 #else 1012 /* When using the Windows XP and 2003 build environment, do the best we can to 1013 * figure out the prefix. 1014 */ 1015 static IP_ADAPTER_INFO *guest_get_adapters_info(void) 1016 { 1017 IP_ADAPTER_INFO *adptr_info = NULL; 1018 ULONG adptr_info_len = 0; 1019 DWORD ret; 1020 1021 /* Call the first time to get the adptr_info_len. */ 1022 GetAdaptersInfo(adptr_info, &adptr_info_len); 1023 1024 adptr_info = g_malloc(adptr_info_len); 1025 ret = GetAdaptersInfo(adptr_info, &adptr_info_len); 1026 if (ret != ERROR_SUCCESS) { 1027 g_free(adptr_info); 1028 adptr_info = NULL; 1029 } 1030 return adptr_info; 1031 } 1032 1033 static int64_t guest_ip_prefix(IP_ADAPTER_UNICAST_ADDRESS *ip_addr) 1034 { 1035 int64_t prefix = -1; /* Use for AF_INET6 and unknown/undetermined values. */ 1036 IP_ADAPTER_INFO *adptr_info, *info; 1037 IP_ADDR_STRING *ip; 1038 struct in_addr *p; 1039 1040 if (ip_addr->Address.lpSockaddr->sa_family != AF_INET) { 1041 return prefix; 1042 } 1043 adptr_info = guest_get_adapters_info(); 1044 if (adptr_info == NULL) { 1045 return prefix; 1046 } 1047 1048 /* Match up the passed in ip_addr with one found in adaptr_info. 1049 * The matching one in adptr_info will have the netmask. 1050 */ 1051 p = &((struct sockaddr_in *)ip_addr->Address.lpSockaddr)->sin_addr; 1052 for (info = adptr_info; info; info = info->Next) { 1053 for (ip = &info->IpAddressList; ip; ip = ip->Next) { 1054 if (p->S_un.S_addr == inet_addr(ip->IpAddress.String)) { 1055 prefix = ctpop32(inet_addr(ip->IpMask.String)); 1056 goto out; 1057 } 1058 } 1059 } 1060 out: 1061 g_free(adptr_info); 1062 return prefix; 1063 } 1064 #endif 1065 1066 GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) 1067 { 1068 IP_ADAPTER_ADDRESSES *adptr_addrs, *addr; 1069 IP_ADAPTER_UNICAST_ADDRESS *ip_addr = NULL; 1070 GuestNetworkInterfaceList *head = NULL, *cur_item = NULL; 1071 GuestIpAddressList *head_addr, *cur_addr; 1072 GuestNetworkInterfaceList *info; 1073 GuestIpAddressList *address_item = NULL; 1074 unsigned char *mac_addr; 1075 char *addr_str; 1076 WORD wsa_version; 1077 WSADATA wsa_data; 1078 int ret; 1079 1080 adptr_addrs = guest_get_adapters_addresses(errp); 1081 if (adptr_addrs == NULL) { 1082 return NULL; 1083 } 1084 1085 /* Make WSA APIs available. */ 1086 wsa_version = MAKEWORD(2, 2); 1087 ret = WSAStartup(wsa_version, &wsa_data); 1088 if (ret != 0) { 1089 error_setg_win32(errp, ret, "failed socket startup"); 1090 goto out; 1091 } 1092 1093 for (addr = adptr_addrs; addr; addr = addr->Next) { 1094 info = g_malloc0(sizeof(*info)); 1095 1096 if (cur_item == NULL) { 1097 head = cur_item = info; 1098 } else { 1099 cur_item->next = info; 1100 cur_item = info; 1101 } 1102 1103 info->value = g_malloc0(sizeof(*info->value)); 1104 info->value->name = guest_wctomb_dup(addr->FriendlyName); 1105 1106 if (addr->PhysicalAddressLength != 0) { 1107 mac_addr = addr->PhysicalAddress; 1108 1109 info->value->hardware_address = 1110 g_strdup_printf("%02x:%02x:%02x:%02x:%02x:%02x", 1111 (int) mac_addr[0], (int) mac_addr[1], 1112 (int) mac_addr[2], (int) mac_addr[3], 1113 (int) mac_addr[4], (int) mac_addr[5]); 1114 1115 info->value->has_hardware_address = true; 1116 } 1117 1118 head_addr = NULL; 1119 cur_addr = NULL; 1120 for (ip_addr = addr->FirstUnicastAddress; 1121 ip_addr; 1122 ip_addr = ip_addr->Next) { 1123 addr_str = guest_addr_to_str(ip_addr, errp); 1124 if (addr_str == NULL) { 1125 continue; 1126 } 1127 1128 address_item = g_malloc0(sizeof(*address_item)); 1129 1130 if (!cur_addr) { 1131 head_addr = cur_addr = address_item; 1132 } else { 1133 cur_addr->next = address_item; 1134 cur_addr = address_item; 1135 } 1136 1137 address_item->value = g_malloc0(sizeof(*address_item->value)); 1138 address_item->value->ip_address = addr_str; 1139 address_item->value->prefix = guest_ip_prefix(ip_addr); 1140 if (ip_addr->Address.lpSockaddr->sa_family == AF_INET) { 1141 address_item->value->ip_address_type = 1142 GUEST_IP_ADDRESS_TYPE_IPV4; 1143 } else if (ip_addr->Address.lpSockaddr->sa_family == AF_INET6) { 1144 address_item->value->ip_address_type = 1145 GUEST_IP_ADDRESS_TYPE_IPV6; 1146 } 1147 } 1148 if (head_addr) { 1149 info->value->has_ip_addresses = true; 1150 info->value->ip_addresses = head_addr; 1151 } 1152 } 1153 WSACleanup(); 1154 out: 1155 g_free(adptr_addrs); 1156 return head; 1157 } 1158 1159 int64_t qmp_guest_get_time(Error **errp) 1160 { 1161 SYSTEMTIME ts = {0}; 1162 int64_t time_ns; 1163 FILETIME tf; 1164 1165 GetSystemTime(&ts); 1166 if (ts.wYear < 1601 || ts.wYear > 30827) { 1167 error_setg(errp, "Failed to get time"); 1168 return -1; 1169 } 1170 1171 if (!SystemTimeToFileTime(&ts, &tf)) { 1172 error_setg(errp, "Failed to convert system time: %d", (int)GetLastError()); 1173 return -1; 1174 } 1175 1176 time_ns = ((((int64_t)tf.dwHighDateTime << 32) | tf.dwLowDateTime) 1177 - W32_FT_OFFSET) * 100; 1178 1179 return time_ns; 1180 } 1181 1182 void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp) 1183 { 1184 Error *local_err = NULL; 1185 SYSTEMTIME ts; 1186 FILETIME tf; 1187 LONGLONG time; 1188 1189 if (!has_time) { 1190 /* Unfortunately, Windows libraries don't provide an easy way to access 1191 * RTC yet: 1192 * 1193 * https://msdn.microsoft.com/en-us/library/aa908981.aspx 1194 */ 1195 error_setg(errp, "Time argument is required on this platform"); 1196 return; 1197 } 1198 1199 /* Validate time passed by user. */ 1200 if (time_ns < 0 || time_ns / 100 > INT64_MAX - W32_FT_OFFSET) { 1201 error_setg(errp, "Time %" PRId64 "is invalid", time_ns); 1202 return; 1203 } 1204 1205 time = time_ns / 100 + W32_FT_OFFSET; 1206 1207 tf.dwLowDateTime = (DWORD) time; 1208 tf.dwHighDateTime = (DWORD) (time >> 32); 1209 1210 if (!FileTimeToSystemTime(&tf, &ts)) { 1211 error_setg(errp, "Failed to convert system time %d", 1212 (int)GetLastError()); 1213 return; 1214 } 1215 1216 acquire_privilege(SE_SYSTEMTIME_NAME, &local_err); 1217 if (local_err) { 1218 error_propagate(errp, local_err); 1219 return; 1220 } 1221 1222 if (!SetSystemTime(&ts)) { 1223 error_setg(errp, "Failed to set time to guest: %d", (int)GetLastError()); 1224 return; 1225 } 1226 } 1227 1228 GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp) 1229 { 1230 error_setg(errp, QERR_UNSUPPORTED); 1231 return NULL; 1232 } 1233 1234 int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) 1235 { 1236 error_setg(errp, QERR_UNSUPPORTED); 1237 return -1; 1238 } 1239 1240 static gchar * 1241 get_net_error_message(gint error) 1242 { 1243 HMODULE module = NULL; 1244 gchar *retval = NULL; 1245 wchar_t *msg = NULL; 1246 int flags, nchars; 1247 1248 flags = FORMAT_MESSAGE_ALLOCATE_BUFFER 1249 |FORMAT_MESSAGE_IGNORE_INSERTS 1250 |FORMAT_MESSAGE_FROM_SYSTEM; 1251 1252 if (error >= NERR_BASE && error <= MAX_NERR) { 1253 module = LoadLibraryExW(L"netmsg.dll", NULL, LOAD_LIBRARY_AS_DATAFILE); 1254 1255 if (module != NULL) { 1256 flags |= FORMAT_MESSAGE_FROM_HMODULE; 1257 } 1258 } 1259 1260 FormatMessageW(flags, module, error, 0, (LPWSTR)&msg, 0, NULL); 1261 1262 if (msg != NULL) { 1263 nchars = wcslen(msg); 1264 1265 if (nchars > 2 && msg[nchars-1] == '\n' && msg[nchars-2] == '\r') { 1266 msg[nchars-2] = '\0'; 1267 } 1268 1269 retval = g_utf16_to_utf8(msg, -1, NULL, NULL, NULL); 1270 1271 LocalFree(msg); 1272 } 1273 1274 if (module != NULL) { 1275 FreeLibrary(module); 1276 } 1277 1278 return retval; 1279 } 1280 1281 void qmp_guest_set_user_password(const char *username, 1282 const char *password, 1283 bool crypted, 1284 Error **errp) 1285 { 1286 NET_API_STATUS nas; 1287 char *rawpasswddata = NULL; 1288 size_t rawpasswdlen; 1289 wchar_t *user, *wpass; 1290 USER_INFO_1003 pi1003 = { 0, }; 1291 1292 if (crypted) { 1293 error_setg(errp, QERR_UNSUPPORTED); 1294 return; 1295 } 1296 1297 rawpasswddata = (char *)g_base64_decode(password, &rawpasswdlen); 1298 rawpasswddata = g_renew(char, rawpasswddata, rawpasswdlen + 1); 1299 rawpasswddata[rawpasswdlen] = '\0'; 1300 1301 user = g_utf8_to_utf16(username, -1, NULL, NULL, NULL); 1302 wpass = g_utf8_to_utf16(rawpasswddata, -1, NULL, NULL, NULL); 1303 1304 pi1003.usri1003_password = wpass; 1305 nas = NetUserSetInfo(NULL, user, 1306 1003, (LPBYTE)&pi1003, 1307 NULL); 1308 1309 if (nas != NERR_Success) { 1310 gchar *msg = get_net_error_message(nas); 1311 error_setg(errp, "failed to set password: %s", msg); 1312 g_free(msg); 1313 } 1314 1315 g_free(user); 1316 g_free(wpass); 1317 g_free(rawpasswddata); 1318 } 1319 1320 GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp) 1321 { 1322 error_setg(errp, QERR_UNSUPPORTED); 1323 return NULL; 1324 } 1325 1326 GuestMemoryBlockResponseList * 1327 qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp) 1328 { 1329 error_setg(errp, QERR_UNSUPPORTED); 1330 return NULL; 1331 } 1332 1333 GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp) 1334 { 1335 error_setg(errp, QERR_UNSUPPORTED); 1336 return NULL; 1337 } 1338 1339 /* add unsupported commands to the blacklist */ 1340 GList *ga_command_blacklist_init(GList *blacklist) 1341 { 1342 const char *list_unsupported[] = { 1343 "guest-suspend-hybrid", 1344 "guest-get-vcpus", "guest-set-vcpus", 1345 "guest-get-memory-blocks", "guest-set-memory-blocks", 1346 "guest-get-memory-block-size", 1347 "guest-fsfreeze-freeze-list", 1348 "guest-fstrim", NULL}; 1349 char **p = (char **)list_unsupported; 1350 1351 while (*p) { 1352 blacklist = g_list_append(blacklist, g_strdup(*p++)); 1353 } 1354 1355 if (!vss_init(true)) { 1356 g_debug("vss_init failed, vss commands are going to be disabled"); 1357 const char *list[] = { 1358 "guest-get-fsinfo", "guest-fsfreeze-status", 1359 "guest-fsfreeze-freeze", "guest-fsfreeze-thaw", NULL}; 1360 p = (char **)list; 1361 1362 while (*p) { 1363 blacklist = g_list_append(blacklist, g_strdup(*p++)); 1364 } 1365 } 1366 1367 return blacklist; 1368 } 1369 1370 /* register init/cleanup routines for stateful command groups */ 1371 void ga_command_state_init(GAState *s, GACommandState *cs) 1372 { 1373 if (!vss_initialized()) { 1374 ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup); 1375 } 1376 } 1377