#include <wtsapi32.h>
#include <wininet.h>
-#include "qga/guest-agent-core.h"
-#include "qga/vss-win32.h"
+#include "guest-agent-core.h"
+#include "vss-win32.h"
#include "qga-qapi-commands.h"
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
{"a+b", FILE_GENERIC_APPEND|GENERIC_READ, OPEN_ALWAYS }
};
+#define debug_error(msg) do { \
+ char *suffix = g_win32_error_message(GetLastError()); \
+ g_debug("%s: %s", (msg), suffix); \
+ g_free(suffix); \
+} while (0)
+
static OpenFlags *find_open_flag(const char *mode_str)
{
int mode;
int64_t qmp_guest_file_open(const char *path, bool has_mode,
const char *mode, Error **errp)
{
- int64_t fd;
+ int64_t fd = -1;
HANDLE fh;
HANDLE templ_file = NULL;
DWORD share_mode = FILE_SHARE_READ;
DWORD flags_and_attr = FILE_ATTRIBUTE_NORMAL;
LPSECURITY_ATTRIBUTES sa_attr = NULL;
OpenFlags *guest_flags;
+ GError *gerr = NULL;
+ wchar_t *w_path = NULL;
if (!has_mode) {
mode = "r";
guest_flags = find_open_flag(mode);
if (guest_flags == NULL) {
error_setg(errp, "invalid file open mode");
- return -1;
+ goto done;
}
- fh = CreateFile(path, guest_flags->desired_access, share_mode, sa_attr,
+ w_path = g_utf8_to_utf16(path, -1, NULL, NULL, &gerr);
+ if (!w_path) {
+ goto done;
+ }
+
+ fh = CreateFileW(w_path, guest_flags->desired_access, share_mode, sa_attr,
guest_flags->creation_disposition, flags_and_attr,
templ_file);
if (fh == INVALID_HANDLE_VALUE) {
error_setg_win32(errp, GetLastError(), "failed to open file '%s'",
path);
- return -1;
+ goto done;
}
/* set fd non-blocking to avoid common use cases (like reading from a
if (fd < 0) {
CloseHandle(fh);
error_setg(errp, "failed to add handle to qmp handle table");
- return -1;
+ goto done;
}
slog("guest-file-open, handle: % " PRId64, fd);
+
+done:
+ if (gerr) {
+ error_setg(errp, QERR_QGA_COMMAND_FAILED, gerr->message);
+ g_error_free(gerr);
+ }
+ g_free(w_path);
return fd;
}
}
if (!has_count) {
count = QGA_READ_COUNT_DEFAULT;
- } else if (count < 0) {
+ } else if (count < 0 || count >= UINT32_MAX) {
error_setg(errp, "value '%" PRId64
"' is invalid for argument count", count);
return NULL;
char *buffer = NULL;
GuestPCIAddress *pci = NULL;
char *name = g_strdup(&guid[4]);
+ bool partial_pci = false;
+ pci = g_malloc0(sizeof(*pci));
+ pci->domain = -1;
+ pci->slot = -1;
+ pci->function = -1;
+ pci->bus = -1;
if (!QueryDosDevice(name, dev_name, ARRAY_SIZE(dev_name))) {
error_setg_win32(errp, GetLastError(), "failed to get dos device name");
goto out;
}
+ g_debug("enumerating devices");
dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
for (i = 0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) {
- DWORD addr, bus, slot, func, dev, data, size2;
+ DWORD addr, bus, slot, data, size2;
+ int func, dev;
while (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data,
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME,
&data, (PBYTE)buffer, size,
if (g_strcmp0(buffer, dev_name)) {
continue;
}
+ g_debug("found device %s", dev_name);
/* There is no need to allocate buffer in the next functions. The size
* is known and ULONG according to
*/
if (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data,
SPDRP_BUSNUMBER, &data, (PBYTE)&bus, size, NULL)) {
- break;
+ debug_error("failed to get bus");
+ bus = -1;
+ partial_pci = true;
}
/* The function retrieves the device's address. This value will be
* transformed into device function and number */
if (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data,
SPDRP_ADDRESS, &data, (PBYTE)&addr, size, NULL)) {
- break;
+ debug_error("failed to get address");
+ addr = -1;
+ partial_pci = true;
}
/* This call returns UINumber of DEVICE_CAPABILITIES structure.
* This number is typically a user-perceived slot number. */
if (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data,
SPDRP_UI_NUMBER, &data, (PBYTE)&slot, size, NULL)) {
- break;
+ debug_error("failed to get slot");
+ slot = -1;
+ partial_pci = true;
}
/* SetupApi gives us the same information as driver with
* DeviceNumber = (USHORT)(((propertyAddress) >> 16) & 0x0000FFFF);
* SPDRP_ADDRESS is propertyAddress, so we do the same.*/
- func = addr & 0x0000FFFF;
- dev = (addr >> 16) & 0x0000FFFF;
- pci = g_malloc0(sizeof(*pci));
- pci->domain = dev;
- pci->slot = slot;
- pci->function = func;
- pci->bus = bus;
+ if (partial_pci) {
+ pci->domain = -1;
+ pci->slot = -1;
+ pci->function = -1;
+ pci->bus = -1;
+ } else {
+ func = ((int) addr == -1) ? -1 : addr & 0x0000FFFF;
+ dev = ((int) addr == -1) ? -1 : (addr >> 16) & 0x0000FFFF;
+ pci->domain = dev;
+ pci->slot = (int) slot;
+ pci->function = func;
+ pci->bus = (int) bus;
+ }
break;
}
return pci;
}
-static int get_disk_bus_type(HANDLE vol_h, Error **errp)
+static void get_disk_properties(HANDLE vol_h, GuestDiskAddress *disk,
+ Error **errp)
{
STORAGE_PROPERTY_QUERY query;
STORAGE_DEVICE_DESCRIPTOR *dev_desc, buf;
DWORD received;
+ ULONG size = sizeof(buf);
dev_desc = &buf;
- dev_desc->Size = sizeof(buf);
query.PropertyId = StorageDeviceProperty;
query.QueryType = PropertyStandardQuery;
if (!DeviceIoControl(vol_h, IOCTL_STORAGE_QUERY_PROPERTY, &query,
sizeof(STORAGE_PROPERTY_QUERY), dev_desc,
- dev_desc->Size, &received, NULL)) {
+ size, &received, NULL)) {
error_setg_win32(errp, GetLastError(), "failed to get bus type");
- return -1;
+ return;
+ }
+ disk->bus_type = find_bus_type(dev_desc->BusType);
+ g_debug("bus type %d", disk->bus_type);
+
+ /* Query once more. Now with long enough buffer. */
+ size = dev_desc->Size;
+ dev_desc = g_malloc0(size);
+ if (!DeviceIoControl(vol_h, IOCTL_STORAGE_QUERY_PROPERTY, &query,
+ sizeof(STORAGE_PROPERTY_QUERY), dev_desc,
+ size, &received, NULL)) {
+ error_setg_win32(errp, GetLastError(), "failed to get serial number");
+ g_debug("failed to get serial number");
+ goto out_free;
+ }
+ if (dev_desc->SerialNumberOffset > 0) {
+ const char *serial;
+ size_t len;
+
+ if (dev_desc->SerialNumberOffset >= received) {
+ error_setg(errp, "failed to get serial number: offset outside the buffer");
+ g_debug("serial number offset outside the buffer");
+ goto out_free;
+ }
+ serial = (char *)dev_desc + dev_desc->SerialNumberOffset;
+ len = received - dev_desc->SerialNumberOffset;
+ g_debug("serial number \"%s\"", serial);
+ if (*serial != 0) {
+ disk->serial = g_strndup(serial, len);
+ disk->has_serial = true;
+ }
}
+out_free:
+ g_free(dev_desc);
- return dev_desc->BusType;
+ return;
}
-/* VSS provider works with volumes, thus there is no difference if
- * the volume consist of spanned disks. Info about the first disk in the
- * volume is returned for the spanned disk group (LVM) */
-static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp)
+static void get_single_disk_info(char *name, GuestDiskAddress *disk,
+ Error **errp)
{
- GuestDiskAddressList *list = NULL;
- GuestDiskAddress *disk;
SCSI_ADDRESS addr, *scsi_ad;
DWORD len;
- int bus;
HANDLE vol_h;
+ Error *local_err = NULL;
scsi_ad = &addr;
- char *name = g_strndup(guid, strlen(guid)-1);
+ g_debug("getting disk info for: %s", name);
vol_h = CreateFile(name, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
0, NULL);
if (vol_h == INVALID_HANDLE_VALUE) {
error_setg_win32(errp, GetLastError(), "failed to open volume");
- goto out_free;
+ goto err;
}
- bus = get_disk_bus_type(vol_h, errp);
- if (bus < 0) {
- goto out_close;
+ get_disk_properties(vol_h, disk, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto err_close;
}
- disk = g_malloc0(sizeof(*disk));
- disk->bus_type = find_bus_type(bus);
- if (bus == BusTypeScsi || bus == BusTypeAta || bus == BusTypeRAID
+ g_debug("bus type %d", disk->bus_type);
+ /* always set pci_controller as required by schema. get_pci_info() should
+ * report -1 values for non-PCI buses rather than fail. fail the command
+ * if that doesn't hold since that suggests some other unexpected
+ * breakage
+ */
+ disk->pci_controller = get_pci_info(name, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto err_close;
+ }
+ if (disk->bus_type == GUEST_DISK_BUS_TYPE_SCSI
+ || disk->bus_type == GUEST_DISK_BUS_TYPE_IDE
+ || disk->bus_type == GUEST_DISK_BUS_TYPE_RAID
#if (_WIN32_WINNT >= 0x0600)
/* This bus type is not supported before Windows Server 2003 SP1 */
- || bus == BusTypeSas
+ || disk->bus_type == GUEST_DISK_BUS_TYPE_SAS
#endif
) {
/* We are able to use the same ioctls for different bus types
* according to Microsoft docs
* https://technet.microsoft.com/en-us/library/ee851589(v=ws.10).aspx */
+ g_debug("getting pci-controller info");
if (DeviceIoControl(vol_h, IOCTL_SCSI_GET_ADDRESS, NULL, 0, scsi_ad,
sizeof(SCSI_ADDRESS), &len, NULL)) {
disk->unit = addr.Lun;
disk->target = addr.TargetId;
disk->bus = addr.PathId;
- disk->pci_controller = get_pci_info(name, errp);
}
/* We do not set error in this case, because we still have enough
* information about volume. */
- } else {
- disk->pci_controller = NULL;
}
- list = g_malloc0(sizeof(*list));
- list->value = disk;
- list->next = NULL;
-out_close:
+err_close:
CloseHandle(vol_h);
-out_free:
+err:
+ return;
+}
+
+/* VSS provider works with volumes, thus there is no difference if
+ * the volume consist of spanned disks. Info about the first disk in the
+ * volume is returned for the spanned disk group (LVM) */
+static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp)
+{
+ Error *local_err = NULL;
+ GuestDiskAddressList *list = NULL, *cur_item = NULL;
+ GuestDiskAddress *disk = NULL;
+
+ /* strip final backslash */
+ char *name = g_strdup(guid);
+ if (g_str_has_suffix(name, "\\")) {
+ name[strlen(name) - 1] = 0;
+ }
+
+ disk = g_malloc0(sizeof(GuestDiskAddress));
+ get_single_disk_info(name, disk, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto out;
+ }
+
+ cur_item = g_malloc0(sizeof(*list));
+ cur_item->value = disk;
+ disk = NULL;
+ list = cur_item;
+
+out:
+ qapi_free_GuestDiskAddress(disk);
g_free(name);
+
return list;
}
char fs_name[32];
char vol_info[MAX_PATH+1];
size_t len;
+ uint64_t i64FreeBytesToCaller, i64TotalBytes, i64FreeBytes;
GuestFilesystemInfo *fs = NULL;
GetVolumePathNamesForVolumeName(guid, (LPCH)&mnt, 0, &info_size);
fs_name[sizeof(fs_name) - 1] = 0;
fs = g_malloc(sizeof(*fs));
fs->name = g_strdup(guid);
+ fs->has_total_bytes = false;
+ fs->has_used_bytes = false;
if (len == 0) {
fs->mountpoint = g_strdup("System Reserved");
} else {
fs->mountpoint = g_strndup(mnt_point, len);
+ if (GetDiskFreeSpaceEx(fs->mountpoint,
+ (PULARGE_INTEGER) & i64FreeBytesToCaller,
+ (PULARGE_INTEGER) & i64TotalBytes,
+ (PULARGE_INTEGER) & i64FreeBytes)) {
+ fs->used_bytes = i64TotalBytes - i64FreeBytes;
+ fs->total_bytes = i64TotalBytes;
+ fs->has_total_bytes = true;
+ fs->has_used_bytes = true;
+ }
}
fs->type = g_strdup(fs_name);
fs->disk = build_guest_disk_info(guid, errp);
* The frozen state is limited for up to 10 seconds by VSS.
*/
int64_t qmp_guest_fsfreeze_freeze(Error **errp)
+{
+ return qmp_guest_fsfreeze_freeze_list(false, NULL, errp);
+}
+
+int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
+ strList *mountpoints,
+ Error **errp)
{
int i;
Error *local_err = NULL;
/* cannot risk guest agent blocking itself on a write in this state */
ga_set_frozen(ga_state);
- qga_vss_fsfreeze(&i, true, &local_err);
+ qga_vss_fsfreeze(&i, true, mountpoints, &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto error;
return 0;
}
-int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
- strList *mountpoints,
- Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
-
- return 0;
-}
-
/*
* Thaw local file systems using Volume Shadow-copy Service.
*/
return 0;
}
- qga_vss_fsfreeze(&i, false, errp);
+ qga_vss_fsfreeze(&i, false, NULL, errp);
ga_unset_frozen(ga_state);
return i;
GuestFilesystemTrimResponse *resp;
HANDLE handle;
WCHAR guid[MAX_PATH] = L"";
+ OSVERSIONINFO osvi;
+ BOOL win8_or_later;
+
+ ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ GetVersionEx(&osvi);
+ win8_or_later = (osvi.dwMajorVersion > 6 ||
+ ((osvi.dwMajorVersion == 6) &&
+ (osvi.dwMinorVersion >= 2)));
+ if (!win8_or_later) {
+ error_setg(errp, "fstrim is only supported for Win8+");
+ return NULL;
+ }
handle = FindFirstVolumeW(guid, ARRAYSIZE(guid));
if (handle == INVALID_HANDLE_VALUE) {
"guest-set-vcpus",
"guest-get-memory-blocks", "guest-set-memory-blocks",
"guest-get-memory-block-size",
- "guest-fsfreeze-freeze-list",
NULL};
char **p = (char **)list_unsupported;