X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=qga%2Fcommands-win32.c;h=7a37f5cadd0268ed1c939aef116cf29c526a70a3;hb=8b7acc79b9adb4dda6cc867b90e3a1e873f4f7e8;hp=7e8ecb3b40e84c95c612787181aad132b4235607;hpb=34daffa04886444dafd4a6951167225e824003d0;p=qemu.git diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 7e8ecb3b4..7a37f5cad 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -15,6 +15,7 @@ #include #include #include "qga/guest-agent-core.h" +#include "qga/vss-win32.h" #include "qga-qmp-commands.h" #include "qapi/qmp/qerror.h" @@ -22,6 +23,12 @@ #define SHTDN_REASON_FLAG_PLANNED 0x80000000 #endif +/* multiple of 100 nanoseconds elapsed between windows baseline + * (1/1/1601) and Unix Epoch (1/1/1970), accounting for leap years */ +#define W32_FT_OFFSET (10000000ULL * 60 * 60 * 24 * \ + (365 * (1970 - 1601) + \ + (1970 - 1601) / 4 - 3)) + static void acquire_privilege(const char *name, Error **err) { HANDLE token; @@ -150,27 +157,89 @@ void qmp_guest_file_flush(int64_t handle, Error **err) */ GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) { - error_set(err, QERR_UNSUPPORTED); - return 0; + if (!vss_initialized()) { + error_set(err, QERR_UNSUPPORTED); + return 0; + } + + if (ga_is_frozen(ga_state)) { + return GUEST_FSFREEZE_STATUS_FROZEN; + } + + return GUEST_FSFREEZE_STATUS_THAWED; } /* - * Walk list of mounted file systems in the guest, and freeze the ones which - * are real local file systems. + * Freeze local file systems using Volume Shadow-copy Service. + * The frozen state is limited for up to 10 seconds by VSS. */ int64_t qmp_guest_fsfreeze_freeze(Error **err) { - error_set(err, QERR_UNSUPPORTED); + int i; + Error *local_err = NULL; + + if (!vss_initialized()) { + error_set(err, QERR_UNSUPPORTED); + return 0; + } + + slog("guest-fsfreeze called"); + + /* cannot risk guest agent blocking itself on a write in this state */ + ga_set_frozen(ga_state); + + qga_vss_fsfreeze(&i, err, true); + if (error_is_set(err)) { + goto error; + } + + return i; + +error: + qmp_guest_fsfreeze_thaw(&local_err); + if (error_is_set(&local_err)) { + g_debug("cleanup thaw: %s", error_get_pretty(local_err)); + error_free(local_err); + } return 0; } /* - * Walk list of frozen file systems in the guest, and thaw them. + * Thaw local file systems using Volume Shadow-copy Service. */ int64_t qmp_guest_fsfreeze_thaw(Error **err) { - error_set(err, QERR_UNSUPPORTED); - return 0; + int i; + + if (!vss_initialized()) { + error_set(err, QERR_UNSUPPORTED); + return 0; + } + + qga_vss_fsfreeze(&i, err, false); + + ga_unset_frozen(ga_state); + return i; +} + +static void guest_fsfreeze_cleanup(void) +{ + Error *err = NULL; + + if (!vss_initialized()) { + return; + } + + if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) { + qmp_guest_fsfreeze_thaw(&err); + if (err) { + slog("failed to clean up frozen filesystems: %s", + error_get_pretty(err)); + error_free(err); + } + } + + vss_deinit(true); } /* @@ -278,7 +347,77 @@ GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **err) return NULL; } +int64_t qmp_guest_get_time(Error **errp) +{ + SYSTEMTIME ts = {0}; + int64_t time_ns; + FILETIME tf; + + GetSystemTime(&ts); + if (ts.wYear < 1601 || ts.wYear > 30827) { + error_setg(errp, "Failed to get time"); + return -1; + } + + if (!SystemTimeToFileTime(&ts, &tf)) { + error_setg(errp, "Failed to convert system time: %d", (int)GetLastError()); + return -1; + } + + time_ns = ((((int64_t)tf.dwHighDateTime << 32) | tf.dwLowDateTime) + - W32_FT_OFFSET) * 100; + + return time_ns; +} + +void qmp_guest_set_time(int64_t time_ns, Error **errp) +{ + SYSTEMTIME ts; + FILETIME tf; + LONGLONG time; + + if (time_ns < 0 || time_ns / 100 > INT64_MAX - W32_FT_OFFSET) { + error_setg(errp, "Time %" PRId64 "is invalid", time_ns); + return; + } + + time = time_ns / 100 + W32_FT_OFFSET; + + tf.dwLowDateTime = (DWORD) time; + tf.dwHighDateTime = (DWORD) (time >> 32); + + if (!FileTimeToSystemTime(&tf, &ts)) { + error_setg(errp, "Failed to convert system time %d", (int)GetLastError()); + return; + } + + acquire_privilege(SE_SYSTEMTIME_NAME, errp); + if (error_is_set(errp)) { + return; + } + + if (!SetSystemTime(&ts)) { + error_setg(errp, "Failed to set time to guest: %d", (int)GetLastError()); + return; + } +} + +GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); + return NULL; +} + +int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); + return -1; +} + /* register init/cleanup routines for stateful command groups */ void ga_command_state_init(GAState *s, GACommandState *cs) { + if (vss_init(true)) { + ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup); + } }