* See the COPYING file in the top-level directory.
*/
-#include <stdio.h>
+#include "qemu/osdep.h"
#include "vss-common.h"
+#include "vss-debug.h"
#include "requester.h"
-#include "assert.h"
-#include "inc/win2003/vswriter.h"
-#include "inc/win2003/vsbackup.h"
+#include "install.h"
+#include <vswriter.h>
+#include <vsbackup.h>
/* Max wait time for frozen event (VSS can only hold writes for 10 seconds) */
-#define VSS_TIMEOUT_FREEZE_MSEC 10000
+#define VSS_TIMEOUT_FREEZE_MSEC 60000
/* Call QueryStatus every 10 ms while waiting for frozen event */
#define VSS_TIMEOUT_EVENT_MSEC 10
-#define err_set(e, err, fmt, ...) \
- ((e)->error_set((e)->errp, err, (e)->err_class, fmt, ## __VA_ARGS__))
-#define err_is_set(e) ((e)->errp && *(e)->errp)
+#define DEFAULT_VSS_BACKUP_TYPE VSS_BT_FULL
+#define err_set(e, err, fmt, ...) { \
+ (e)->error_setg_win32_wrapper((e)->errp, __FILE__, __LINE__, __func__, \
+ err, fmt, ## __VA_ARGS__); \
+ qga_debug(fmt, ## __VA_ARGS__); \
+}
+/* Bad idea, works only when (e)->errp != NULL: */
+#define err_is_set(e) ((e)->errp && *(e)->errp)
+/* To lift this restriction, error_propagate(), like we do in QEMU code */
/* Handle to VSSAPI.DLL */
static HMODULE hLib;
STDAPI requester_init(void)
{
+ qga_debug_begin;
+
COMInitializer initializer; /* to call CoInitializeSecurity */
HRESULT hr = CoInitializeSecurity(
NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_NONE, NULL);
if (FAILED(hr)) {
- fprintf(stderr, "failed to CoInitializeSecurity (error %lx)\n", hr);
+ qga_debug("failed to CoInitializeSecurity (error %lx)", hr);
return hr;
}
hLib = LoadLibraryA("VSSAPI.DLL");
if (!hLib) {
- fprintf(stderr, "failed to load VSSAPI.DLL\n");
+ qga_debug("failed to load VSSAPI.DLL");
return HRESULT_FROM_WIN32(GetLastError());
}
#endif
);
if (!pCreateVssBackupComponents) {
- fprintf(stderr, "failed to get proc address from VSSAPI.DLL\n");
+ qga_debug("failed to get proc address from VSSAPI.DLL");
return HRESULT_FROM_WIN32(GetLastError());
}
pVssFreeSnapshotProperties = (t_VssFreeSnapshotProperties)
GetProcAddress(hLib, "VssFreeSnapshotProperties");
if (!pVssFreeSnapshotProperties) {
- fprintf(stderr, "failed to get proc address from VSSAPI.DLL\n");
+ qga_debug("failed to get proc address from VSSAPI.DLL");
return HRESULT_FROM_WIN32(GetLastError());
}
+ qga_debug_end;
return S_OK;
}
static void requester_cleanup(void)
{
+ qga_debug_begin;
+
if (vss_ctx.hEventFrozen) {
CloseHandle(vss_ctx.hEventFrozen);
vss_ctx.hEventFrozen = NULL;
vss_ctx.pVssbc = NULL;
}
vss_ctx.cFrozenVols = 0;
+ qga_debug_end;
}
STDAPI requester_deinit(void)
{
+ qga_debug_begin;
+
requester_cleanup();
pCreateVssBackupComponents = NULL;
hLib = NULL;
}
+ qga_debug_end;
return S_OK;
}
static HRESULT WaitForAsync(IVssAsync *pAsync)
{
+ qga_debug_begin;
+
HRESULT ret, hr;
do {
}
} while (ret == VSS_S_ASYNC_PENDING);
+ qga_debug_end;
return ret;
}
static void AddComponents(ErrorSet *errset)
{
+ qga_debug_begin;
+
unsigned int cWriters, i;
VSS_ID id, idInstance, idWriter;
BSTR bstrWriterName = NULL;
if (pComponent && info) {
pComponent->FreeComponentInfo(info);
}
+ qga_debug_end;
}
-void requester_freeze(int *num_vols, ErrorSet *errset)
+DWORD get_reg_dword_value(HKEY baseKey, LPCSTR subKey, LPCSTR valueName,
+ DWORD defaultData)
{
+ qga_debug_begin;
+
+ DWORD regGetValueError;
+ DWORD dwordData;
+ DWORD dataSize = sizeof(DWORD);
+
+ regGetValueError = RegGetValue(baseKey, subKey, valueName, RRF_RT_DWORD,
+ NULL, &dwordData, &dataSize);
+ qga_debug_end;
+ if (regGetValueError != ERROR_SUCCESS) {
+ return defaultData;
+ }
+ return dwordData;
+}
+
+bool is_valid_vss_backup_type(VSS_BACKUP_TYPE vssBT)
+{
+ return (vssBT > VSS_BT_UNDEFINED && vssBT < VSS_BT_OTHER);
+}
+
+VSS_BACKUP_TYPE get_vss_backup_type(
+ VSS_BACKUP_TYPE defaultVssBT = DEFAULT_VSS_BACKUP_TYPE)
+{
+ qga_debug_begin;
+
+ VSS_BACKUP_TYPE vssBackupType;
+
+ vssBackupType = static_cast<VSS_BACKUP_TYPE>(
+ get_reg_dword_value(HKEY_LOCAL_MACHINE,
+ QGA_PROVIDER_REGISTRY_ADDRESS,
+ "VssOption",
+ defaultVssBT));
+ qga_debug_end;
+ if (!is_valid_vss_backup_type(vssBackupType)) {
+ return defaultVssBT;
+ }
+ return vssBackupType;
+}
+
+void requester_freeze(int *num_vols, void *mountpoints, ErrorSet *errset)
+{
+ qga_debug_begin;
+
COMPointer<IVssAsync> pAsync;
HANDLE volume;
HRESULT hr;
WCHAR short_volume_name[64], *display_name = short_volume_name;
DWORD wait_status;
int num_fixed_drives = 0, i;
+ int num_mount_points = 0;
+ VSS_BACKUP_TYPE vss_bt = get_vss_backup_type();
if (vss_ctx.pVssbc) { /* already frozen */
*num_vols = 0;
+ qga_debug("finished, already frozen");
return;
}
goto out;
}
- hr = vss_ctx.pVssbc->SetBackupState(true, true, VSS_BT_FULL, false);
+ hr = vss_ctx.pVssbc->SetBackupState(true, true, vss_bt, false);
if (FAILED(hr)) {
err_set(errset, hr, "failed to set backup state");
goto out;
goto out;
}
- volume = FindFirstVolumeW(short_volume_name, sizeof(short_volume_name));
- if (volume == INVALID_HANDLE_VALUE) {
- err_set(errset, hr, "failed to find first volume");
- goto out;
- }
- for (;;) {
- if (GetDriveTypeW(short_volume_name) == DRIVE_FIXED) {
+ if (mountpoints) {
+ PWCHAR volume_name_wchar;
+ for (volList *list = (volList *)mountpoints; list; list = list->next) {
+ size_t len = strlen(list->value) + 1;
+ size_t converted = 0;
VSS_ID pid;
- hr = vss_ctx.pVssbc->AddToSnapshotSet(short_volume_name,
+
+ volume_name_wchar = new wchar_t[len];
+ mbstowcs_s(&converted, volume_name_wchar, len,
+ list->value, _TRUNCATE);
+
+ hr = vss_ctx.pVssbc->AddToSnapshotSet(volume_name_wchar,
g_gProviderId, &pid);
if (FAILED(hr)) {
- WCHAR volume_path_name[PATH_MAX];
- if (GetVolumePathNamesForVolumeNameW(
- short_volume_name, volume_path_name,
- sizeof(volume_path_name), NULL) && *volume_path_name) {
- display_name = volume_path_name;
- }
err_set(errset, hr, "failed to add %S to snapshot set",
- display_name);
- FindVolumeClose(volume);
+ volume_name_wchar);
+ delete[] volume_name_wchar;
goto out;
}
- num_fixed_drives++;
+ num_mount_points++;
+
+ delete[] volume_name_wchar;
}
- if (!FindNextVolumeW(volume, short_volume_name,
- sizeof(short_volume_name))) {
- FindVolumeClose(volume);
- break;
+
+ if (num_mount_points == 0) {
+ /* If there is no valid mount points, just exit. */
+ goto out;
}
}
- if (num_fixed_drives == 0) {
- goto out; /* If there is no fixed drive, just exit. */
+ if (!mountpoints) {
+ volume = FindFirstVolumeW(short_volume_name, sizeof(short_volume_name));
+ if (volume == INVALID_HANDLE_VALUE) {
+ err_set(errset, hr, "failed to find first volume");
+ goto out;
+ }
+
+ for (;;) {
+ if (GetDriveTypeW(short_volume_name) == DRIVE_FIXED) {
+ VSS_ID pid;
+ hr = vss_ctx.pVssbc->AddToSnapshotSet(short_volume_name,
+ g_gProviderId, &pid);
+ if (FAILED(hr)) {
+ WCHAR volume_path_name[PATH_MAX];
+ if (GetVolumePathNamesForVolumeNameW(
+ short_volume_name, volume_path_name,
+ sizeof(volume_path_name), NULL) &&
+ *volume_path_name) {
+ display_name = volume_path_name;
+ }
+ err_set(errset, hr, "failed to add %S to snapshot set",
+ display_name);
+ FindVolumeClose(volume);
+ goto out;
+ }
+ num_fixed_drives++;
+ }
+ if (!FindNextVolumeW(volume, short_volume_name,
+ sizeof(short_volume_name))) {
+ FindVolumeClose(volume);
+ break;
+ }
+ }
+
+ if (num_fixed_drives == 0) {
+ goto out; /* If there is no fixed drive, just exit. */
+ }
}
+ qga_debug("preparing for backup");
hr = vss_ctx.pVssbc->PrepareForBackup(pAsync.replace());
if (SUCCEEDED(hr)) {
hr = WaitForAsync(pAsync);
* CQGAVssProvider::CommitSnapshots will kick vss_ctx.hEventFrozen
* after the applications and filesystems are frozen.
*/
+ qga_debug("do snapshot set");
hr = vss_ctx.pVssbc->DoSnapshotSet(&vss_ctx.pAsyncSnapshot);
if (FAILED(hr)) {
err_set(errset, hr, "failed to do snapshot set");
break;
}
}
+
+ if (wait_status == WAIT_TIMEOUT) {
+ err_set(errset, E_FAIL,
+ "timeout when try to receive Frozen event from VSS provider");
+ /* If we are here, VSS had timeout.
+ * Don't call AbortBackup, just return directly.
+ */
+ goto out1;
+ }
+
if (wait_status != WAIT_OBJECT_0) {
err_set(errset, E_FAIL,
"couldn't receive Frozen event from VSS provider");
goto out;
}
- *num_vols = vss_ctx.cFrozenVols = num_fixed_drives;
+ if (mountpoints) {
+ *num_vols = vss_ctx.cFrozenVols = num_mount_points;
+ } else {
+ *num_vols = vss_ctx.cFrozenVols = num_fixed_drives;
+ }
+
+ qga_debug("end successful");
return;
out:
if (vss_ctx.pVssbc) {
vss_ctx.pVssbc->AbortBackup();
}
+
+out1:
requester_cleanup();
CoUninitialize();
+
+ qga_debug_end;
}
-void requester_thaw(int *num_vols, ErrorSet *errset)
+void requester_thaw(int *num_vols, void *mountpints, ErrorSet *errset)
{
+ qga_debug_begin;
COMPointer<IVssAsync> pAsync;
if (!vss_ctx.hEventThaw) {
* and no volumes must be frozen. We return without an error.
*/
*num_vols = 0;
+ qga_debug("finished, no volumes were frozen");
+
return;
}
requester_cleanup();
CoUninitialize();
+ StopService();
+
+ qga_debug_end;
}