Open paescuj opened 4 years ago
running a debug-enabled sepiola through gdb (installed via msys2) on Windows gives the following backtrace:
Thread 1 received signal ?, Unknown signal.
0x00007fff0606033f in RaiseFailFastException ()
from C:\Windows\System32\KernelBase.dll
(gdb) bt
#0 0x00007fff0606033f in RaiseFailFastException ()
from C:\Windows\System32\KernelBase.dll
#1 0x000000006674e901 in qt_message_fatal(QtMsgType, QMessageLogContext const&, QString const&) [clone .isra.0] () from C:\sepiola\bin\Qt5Core.dll
#2 0x0000000066a0ea98 in QMessageLogger::fatal(char const*, ...) const ()
from C:\sepiola\bin\Qt5Core.dll
#3 0x0000000066752a86 in QThread::~QThread() ()
from C:\sepiola\bin\Qt5Core.dll
#4 0x0000000066752ace in QThread::~QThread() ()
from C:\sepiola\bin\Qt5Core.dll
#5 0x00000000004210fc in FilesystemSnapshot::~FilesystemSnapshot (this=
0x7ce9440, __in_chrg=<optimized out>)
at /app/src/tools/filesystem_snapshot.cc:167
#6 0x0000000000421157 in FilesystemSnapshot::~FilesystemSnapshot (
this=0x7ce9440, __in_chrg=<optimized out>)
at /app/src/tools/filesystem_snapshot.cc:171
#7 0x0000000000436ba4 in MainModel::uploadFiles (this=0x2a2fcd0, result=4)
at /app/src/model/main_model.cc:593
#8 0x00000000004403d8 in MainModel::qt_static_metacall (_o=0x2a2fcd0,
_c=QMetaObject::InvokeMetaMethod, _id=23, _a=0x2a2be70)
at /build/src/model/moc_main_model.cpp:185
#9 0x000000006690000a in QMetaObject::activate(QObject*, int, int, void**) ()
from C:\sepiola\bin\Qt5Core.dll
#10 0x000000000043e3b9 in FilesystemSnapshot::sendSnapshotDone (
this=0x7ce9440, _t1=4) at /build/src/tools/moc_filesystem_snapshot.cpp:275
#11 0x0000000000421984 in FilesystemSnapshot::snapshotTaken (this=0x7ce9440,
result=4) at /app/src/tools/filesystem_snapshot.cc:251
#12 0x000000000043dd6a in FilesystemSnapshot::qt_static_metacall (
_o=0x7ce9440, _c=QMetaObject::InvokeMetaMethod, _id=11, _a=0x7ce9ba0)
at /build/src/tools/moc_filesystem_snapshot.cpp:131
#13 0x0000000066901057 in QObject::event(QEvent*) ()
from C:\sepiola\bin\Qt5Core.dll
#14 0x0000000003237c16 in QApplicationPrivate::notify_helper(QObject*, QEvent*) () from C:\sepiola\bin\Qt5Widgets.dll
#15 0x0000000003240dce in QApplication::notify(QObject*, QEvent*) ()
from C:\sepiola\bin\Qt5Widgets.dll
#16 0x00000000668dc210 in QCoreApplication::notifyInternal2(QObject*, QEvent*)
() from C:\sepiola\bin\Qt5Core.dll
#17 0x00000000668e2209 in QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) () from C:\sepiola\bin\Qt5Core.dll
#18 0x000000006dd42d4d in QWindowsGuiEventDispatcher::sendPostedEvents() ()
from C:\sepiola\bin\platforms\qwindows.dll
#19 0x000000006692c019 in qt_internal_proc(HWND__*, unsigned int, unsigned long long, long long) () from C:\sepiola\bin\Qt5Core.dll
#20 0x00007fff08865c7d in USER32!CallWindowProcW ()
from C:\Windows\System32\user32.dll
#21 0x00007fff08865672 in USER32!DispatchMessageW ()
from C:\Windows\System32\user32.dll
#22 0x000000006692b71b in QEventDispatcherWin32::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from C:\sepiola\bin\Qt5Core.dll
#23 0x000000006dd42d34 in QWindowsGuiEventDispatcher::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from C:\sepiola\bin\platforms\qwindows.dll
#24 0x00000000668db23a in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () from C:\sepiola\bin\Qt5Core.dll
#25 0x00000000668e2e43 in QCoreApplication::exec() ()
from C:\sepiola\bin\Qt5Core.dll
#26 0x00000000004517f7 in main (argc=1, argv=0x2a53fd0)
at /app/src/main.cc:344
Further investigation suggests that we're doing something wrong by explicitly deleting the QThread
. Deleting the snapshot object on the next line is then probably also wrong, since that was moved to the thread and should be cleaned after the thread (which is probably why we do an explicit cleanup in the first place). Since the snapshot is a Q_OBJECT
as well, we could extend the factory to take a parent, pass along a reference to the QThread
to make that QThread
its parent.
Besides all that, recent versions of MinGW actually contain the vssapi, the only thing one has to do is to set -D_WIN32_WINNT=0x601
(or 0x600
) to signal MinGW to enable the corresponding Windows API definitions. This allows to rely on proper function/interface definitions and clean linking instead of the dynamic LoadLibrary
approach.
The Dockerfile
for a mingw64 build environment I've been using:
FROM fedora:latest
RUN set -ex ; \
dnf -y update ; \
# make sure we get the required translations when install qttranslations:
echo "%_install_langs all" > /etc/rpm/macros.image-language-conf ; \
dnf -y install \
cmake make \
mingw64-gcc mingw64-gcc-c++ \
mingw64-qt5-qtbase-devel mingw64-qt5-qttools mingw64-qt5-qttools-tools mingw64-qt5-qttranslations \
mingw64-boost \
mingw32-nsiswrapper \
; \
dnf clean all
... and the patch for the Windows API should I not find the time to submit a PR:
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 7556fcf..15cd7ea 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -80,16 +80,14 @@ if(WIN32)
COMMAND ${CMAKE_RC_COMPILER} ${PROJECT_SOURCE_DIR}/${WINDOWS_RESSOURCE_FILE} -o
${CMAKE_CURRENT_BINARY_DIR}/resource.o)
set(sepiola_RC ${sepiola_RC} ${CMAKE_CURRENT_BINARY_DIR}/resource.o)
+
+ add_definitions(-D_WIN32_WINNT=0x601) # Windows 7
endif(WIN32)
generate_qt_resource_from_files(
"${CMAKE_CURRENT_BINARY_DIR}/logos_images.qrc" "/main"
"${PROJECT_SOURCE_DIR}/${ABOUT_IMAGE_FILE};${PROJECT_SOURCE_DIR}/${WINDOW_ICON_FILE}")
-if(WIN32)
- set(OS_HEADER_DIRECTORIES /usr/local/inc/win2003/)
-endif(WIN32)
-
include_directories(. ${CMAKE_CURRENT_BINARY_DIR} ${OS_HEADER_DIRECTORIES})
include_directories(SYSTEM ${OS_HEADER_DIRECTORIES})
@@ -314,6 +312,8 @@ if(WIN32)
# for second binary
target_link_libraries(${SSBACKUP_EXECUTABLE_NAME}.com Qt5::Core Qt5::Widgets Qt5::Network)
target_link_libraries(${SSBACKUP_EXECUTABLE_NAME}.com debug Qt5::Core Qt5::Widgets Qt5::Network)
+ target_link_libraries(${SSBACKUP_EXECUTABLE_NAME} vssapi)
+ target_link_libraries(${SSBACKUP_EXECUTABLE_NAME}.com vssapi)
endif(WIN32)
target_link_libraries(${SSBACKUP_EXECUTABLE_NAME} Qt5::Core Qt5::Widgets Qt5::Network)
diff --git a/src/tools/shadow_copy.cc b/src/tools/shadow_copy.cc
index 497bf7f..7fb169c 100644
--- a/src/tools/shadow_copy.cc
+++ b/src/tools/shadow_copy.cc
@@ -29,23 +29,13 @@
#include <QStringList>
#include <QWaitCondition>
+#include <vsbackup.h>
+
#ifndef __GNUC__
// only valid for Visual C++ linker (either with Visual C++ or clang frontend)
#pragma comment(lib, "VssApi.lib")
#endif
-/* Functions in VSSAPI.DLL */
-typedef HRESULT(STDAPICALLTYPE *_CreateVssBackupComponentsInternal)(
- OUT IVssBackupComponents **ppBackup);
-typedef void(APIENTRY *_VssFreeSnapshotPropertiesInternal)(IN VSS_SNAPSHOT_PROP *pProp);
-static _CreateVssBackupComponentsInternal CreateVssBackupComponentsInternal_I;
-static _VssFreeSnapshotPropertiesInternal VssFreeSnapshotPropertiesInternal_I;
-
-/* Funktions in kernel32.dll */
-typedef BOOL(WINAPI *CreateSymbolicLinkProc)(LPCSTR, LPCSTR, DWORD);
-typedef BOOL(WINAPI *RemoveDirectoryProc)(LPCSTR);
-typedef BOOL(WINAPI *PathFileExistsProc)(LPCSTR);
-
const QString ShadowCopy::MOUNT_PREFIX = "mount_shadow_copy_";
ShadowCopy::ShadowCopy()
@@ -76,15 +66,8 @@ void ShadowCopy::createSnapshotObject()
{
CoInitialize(NULL);
- // Load the vssapi library
- this->vssapiBase = LoadLibrary("vssapi.dll");
-
- // Get the
- CreateVssBackupComponentsInternal_I = (_CreateVssBackupComponentsInternal)
- GetProcAddress(this->vssapiBase, "CreateVssBackupComponentsInternal");
-
// Create the shadow copy backup component (VSSBackupComponent)
- HRESULT result = CreateVssBackupComponentsInternal_I(&(this->pBackup));
+ HRESULT result = CreateVssBackupComponents(&(this->pBackup));
// Check if the operation succeeded
if (result != S_OK) {
@@ -108,7 +91,7 @@ void ShadowCopy::initializeSnapshot()
return;
}
- HRESULT result = this->pBackup->InitializeForBackup();
+ HRESULT result = this->pBackup->InitializeForBackup(NULL);
// Check if the operation succeeded
if (result != S_OK) {
@@ -143,7 +126,7 @@ void ShadowCopy::initializeSnapshot()
}
// Wait until all writers collected the metadata
- this->pAsync->Wait();
+ this->pAsync->Wait(INFINITE);
// Check if the operation succeeded
if (result != S_OK) {
@@ -254,7 +237,8 @@ void ShadowCopy::takeSnapshot()
// Set the backup state
HRESULT result = this->pBackup->SetBackupState(this->SC_SNAPSHOT_SELECT_COMPONENTS,
this->SC_SNAPSHOT_BOOTABLE_STATE,
- this->SC_SNAPSHOT_TYPE);
+ this->SC_SNAPSHOT_TYPE,
+ this->SC_PARTIAL_FILE_SUPPORT);
// Check if the operation succeeded
if (result != S_OK) {
@@ -274,7 +258,7 @@ void ShadowCopy::takeSnapshot()
}
// Wait for everyone to be ready
- result = this->pPrepare->Wait();
+ result = this->pPrepare->Wait(INFINITE);
// Check if the operation succeeded
if (result != S_OK) {
@@ -296,7 +280,7 @@ void ShadowCopy::takeSnapshot()
}
// Wait until the shadow copy is created
- result = this->pDoShadowCopy->Wait();
+ result = this->pDoShadowCopy->Wait(INFINITE);
// Check if the operation succeeded
if (result != S_OK) {
@@ -339,35 +323,16 @@ void ShadowCopy::takeSnapshot()
linkname.append(partition);
snapshotPath.append("\\");
- HMODULE lib;
- CreateSymbolicLinkProc CreateSymbolicLink_func;
- DWORD flags = 1;
-
- lib = LoadLibrary("kernel32");
- CreateSymbolicLink_func = (CreateSymbolicLinkProc) GetProcAddress(lib,
- "CreateSymbolicLinkW");
-
QString fullLinkname = linkname;
fullLinkname.prepend("\\\\?\\");
LPCSTR link = (LPCSTR) fullLinkname.utf16();
LPCSTR target = (LPCSTR) snapshotPathForCygwin.utf16();
- // Check if the link already (what would be wrong) exists, if yes,
- // remove it
- PathFileExistsProc PathFileExists_func;
- PathFileExists_func = (PathFileExistsProc) GetProcAddress(lib, "PathFileExistsW");
-
- if (CreateSymbolicLink_func == NULL) {
- qDebug() << "WinAPI function CreateSymbolicLinkW not available";
+ if (CreateSymbolicLink(link, target, 1) == 0) {
+ qDebug() << "WinAPI call CreateSymbolicLink failed" << GetLastError();
emit sendSnapshotTaken(SNAPSHOR_CANNOT_MOUNT_SNAPSHOT);
return;
- } else {
- if ((*CreateSymbolicLink_func)(link, target, flags) == 0) {
- qDebug() << "WinAPI call CreateSymbolicLink failed" << GetLastError();
- emit sendSnapshotTaken(SNAPSHOR_CANNOT_MOUNT_SNAPSHOT);
- return;
- }
}
mapper.setSnapshotPath(linkname);
@@ -450,17 +415,8 @@ void ShadowCopy::checkCleanup()
bool ShadowCopy::removeWindowsSymlink(QString linkname)
{
// Take the linkname and prepend the necessary Windows UNC path
- linkname.prepend("\\\\?\\");
-
- HMODULE lib;
- RemoveDirectoryProc RemoveDirectory_func;
-
- lib = LoadLibrary("kernel32");
- RemoveDirectory_func = (RemoveDirectoryProc) GetProcAddress(lib, "RemoveDirectoryW");
-
- LPCSTR link = (LPCSTR) linkname.utf16();
-
- return (*RemoveDirectory_func)(link);
+ auto linkname_wchar = linkname.prepend("\\\\?\\").toStdWString();
+ return RemoveDirectoryW(linkname_wchar.c_str());
}
const SnapshotMapper &ShadowCopy::getSnapshotPathMappers() const
diff --git a/src/tools/shadow_copy.hh b/src/tools/shadow_copy.hh
index b2cf671..4555168 100644
--- a/src/tools/shadow_copy.hh
+++ b/src/tools/shadow_copy.hh
@@ -22,17 +22,15 @@
#include "abstract_snapshot.hh"
#include "error.h"
+
#include <iostream>
-#include <shlwapi.h>
-#include <stdio.h>
#include <string>
-#include <tchar.h>
-#include <vss.h>
-#include <vswriter.h>
-#include <vsbackup.h>
#include <QHash>
#include <QString>
+#include <windows.h>
+#include <vss.h>
+
/* Define some vss snapshot errors code which are 100 < code < 200*/
#define SNAPSHOT_CANNOT_SET_BACKUP_CONTEXT 101
#define SNAPSHOT_WRITER_GATHERING_METADATA_FAILED 102
@@ -107,6 +105,7 @@ private:
static const VSS_BACKUP_TYPE SC_SNAPSHOT_TYPE = VSS_BT_COPY;
static const bool SC_SNAPSHOT_BOOTABLE_STATE = false;
static const bool SC_SNAPSHOT_SELECT_COMPONENTS = false;
+ static const bool SC_PARTIAL_FILE_SUPPORT = false;
static const QString MOUNT_PREFIX;
HMODULE vssapiBase;
and more evidence that we're doing it wrong:
Warning: Deleting a QObject while pending events are waiting
to be delivered can cause a crash. You must not delete the
QObject directly if it exists in a different thread than the one
currently executing. Use deleteLater() instead, which will cause
the event loop to delete the object after all pending events have
been delivered to it.
Describe the bug The application crashes during the creation of the filesystem snapshot on Windows. It doesn't crash when the filesystem snapshot is deactivated.
To Reproduce Steps to reproduce the behavior:
Expected behavior The application should not crash when creating the filesystem snapshot.
Version (please complete the following information):
Additional context This bug must have been introduced when switching to Qt 5 (https://github.com/stepping-stone/sepiola/compare/a8e8cdc558928bf409127edd655b7931996a19f6...4a80f7bfbbe25bcf30d07d801021c10ce927449e). I've tested it by leaving out any newer commits with the exception of https://github.com/stepping-stone/sepiola/commit/e7621f0070a3c014d2eb2d60088b09a87c1af121 - the problem still persists.
Quick debugging reveals that there happens an "Access violation". Output from "API Monitor":
This issue is a blocker for v2.8.