LeechCraft 0.6.70-17609-g3dde4097dd
Modular cross-platform feature rich live environment.
Loading...
Searching...
No Matches
consistencychecker.cpp
Go to the documentation of this file.
1/**********************************************************************
2 * LeechCraft - modular cross-platform feature rich internet client.
3 * Copyright (C) 2006-2014 Georg Rudoy
4 *
5 * Distributed under the Boost Software License, Version 1.0.
6 * (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt)
7 **********************************************************************/
8
10#include <memory>
11#include <QCoreApplication>
12#include <QFile>
13#include <QMessageBox>
14#include <QSqlDatabase>
15#include <QtConcurrentRun>
16#include <util/gui/util.h>
17#include <util/sll/qtutil.h>
18#include <util/sll/visitor.h>
19#include <util/sys/paths.h>
20#include <util/threads/coro.h>
21#include <util/util.h>
22#include "dumper.h"
23#include "util.h"
24
26{
27 namespace
28 {
29 struct Tr
30 {
31 Q_DECLARE_TR_FUNCTIONS (LC::Util::ConsistencyChecker)
32 };
33
34 CheckResult_t CheckSync (const QString& dbPath)
35 {
36 qDebug () << "checking" << dbPath;
37 const auto& connName = GenConnectionName ("ConsistencyChecker_" + dbPath);
38
39 auto db = QSqlDatabase::addDatabase ("QSQLITE"_qs, connName);
40 const auto remGuard = MakeScopeGuard ([connName] { QSqlDatabase::removeDatabase (connName); });
41
42 db.setDatabaseName (dbPath);
43 if (!db.open ())
44 {
45 qWarning () << "cannot open the DB, but that's not the kind of errors we're solving.";
46 return Succeeded {};
47 }
48
49 QSqlQuery pragma { db };
50 static const auto checkQuery = qgetenv ("LC_THOROUGH_SQLITE_CHECK") == "1" ?
51 "PRAGMA integrity_check;"_qs :
52 "PRAGMA quick_check;"_qs;
53 const auto isGood = pragma.exec (checkQuery) &&
54 pragma.next () &&
55 pragma.value (0) == "ok";
56 qDebug () << "done checking" << dbPath << "; db is good?" << isGood;
57 if (isGood)
58 return Succeeded {};
59
60 return Left { Failed {} };
61 }
62
63 Either<RecoverFailed, Void> CheckRecoverSpace (const QString& dbPath)
64 {
65 const QFileInfo fi { dbPath };
66 const auto filesize = fi.size ();
67
68 const auto available = static_cast<qint64> (GetSpaceInfo (dbPath).Available_);
69
70 qDebug () << "db size:" << filesize
71 << "free space:" << available;
72 if (available >= filesize)
73 return Void {};
74
75 return Left { RecoverNoSpace { .Available_ = available, .Expected_ = filesize } };
76 }
77 }
78
79 Task<CheckResult_t> Check (QString dbPath)
80 {
81 co_return co_await QtConcurrent::run (CheckSync, dbPath);
82 }
83
85 {
86 [[maybe_unused]] const auto hasEnoughSpace = co_await CheckRecoverSpace (dbPath);
87 const auto& newPath = dbPath + ".new";
88 if (QFile::exists (newPath))
89 co_return Left { RecoverTargetExists { newPath } };
90
91 const auto dumpProcResult = co_await DumpSqlite (dbPath, newPath);
92 [[maybe_unused]] const auto dumpProcSuccess = co_await WithHandler (dumpProcResult,
93 [] (const QString& msg) { return RecoverOtherFailure { msg }; });
94
95 const auto oldSize = QFileInfo { dbPath }.size ();
96 const auto newSize = QFileInfo { newPath }.size ();
97
98 const auto& backupPath = dbPath + ".bak";
99 if (!QFile::rename (dbPath, backupPath))
100 co_return Left { RecoverTargetExists { backupPath } };
101
102 // extremely unlikely if the previous rename succeeded
103 while (!QFile::rename (newPath, dbPath))
104 {
105 qCritical () << "unable to rename" << newPath << "→" << dbPath;
106 const auto& msg = Tr::tr ("Unable to rename %1 to %2. Please check %2 does not exist, and hit OK.")
107 .arg (newPath, dbPath);
108 QMessageBox::critical (nullptr, "LeechCraft"_qs, msg);
109 }
110
111 co_return RecoverFinished { .OldFileSize_ = oldSize, .NewFileSize_ = newSize };
112 }
113
114 namespace
115 {
116 QString GetRecoverFailureMessage (const RecoverFailed& failure)
117 {
118 return Visit (failure,
119 [&] (RecoverNoSpace space)
120 {
121 return Tr::tr ("Not enough space available: "
122 "%1 free while the restored file is expected to be around %2. "
123 "Please either free some disk space on this partition "
124 "and retry or cancel the restore process.")
125 .arg (MakePrettySize (space.Available_), MakePrettySize (space.Expected_));
126 },
127 [&] (const RecoverTargetExists& exists)
128 {
129 return Tr::tr ("Target file %1 already exists, please remove it manually and retry.")
130 .arg (FormatName (exists.Target_));
131 },
132 [&] (const RecoverOtherFailure& other)
133 {
134 return other.Message_;
135 });
136 }
137 }
138
139 Task<RecoverResult_t> RecoverWithUserInteraction (QString dbPath, QString diaTitle)
140 {
141 while (true)
142 {
143 const auto result = co_await Recover (dbPath);
144 if (result.IsRight ())
145 co_return result;
146
147 const auto& question = Tr::tr ("Unable to dump corrupted SQLite database %1.").arg (FormatName (dbPath)) +
148 "<br/><br/>"_qs +
149 GetRecoverFailureMessage (result.GetLeft ());
150 if (QMessageBox::question (nullptr, diaTitle, question, QMessageBox::Retry | QMessageBox::Cancel) == QMessageBox::Cancel)
151 co_return result;
152 }
153 }
154}
QString GenConnectionName(const QString &base)
Generates an unique thread-safe connection name.
Definition util.cpp:55
Task< RecoverResult_t > Recover(QString dbPath)
Task< RecoverResult_t > RecoverWithUserInteraction(QString dbPath, QString diaTitle)
Either< Failed, Succeeded > CheckResult_t
std::variant< RecoverNoSpace, RecoverTargetExists, RecoverOtherFailure > RecoverFailed
Task< CheckResult_t > Check(QString dbPath)
detail::ScopeGuard< F > MakeScopeGuard(const F &f)
Returns an object performing passed function on scope exit.
Definition util.h:155
Task< Either< QString, Void > > DumpSqlite(QString from, QString to)
Definition dumper.cpp:58
detail::EitherAwaiter< L, R, F > WithHandler(const Either< L, R > &either, F &&errorHandler)
Definition either.h:72
auto Visit(const Either< Left, Right > &either, Args &&... args)
Definition either.h:207
SpaceInfo GetSpaceInfo(const QString &path)
Returns the disk space info of the partition containing path.
Definition paths.cpp:154
QString FormatName(const QString &name)
HTML-formats the name to let the user know it is not a part of the fixed dialog text.
Definition util.cpp:114
QString MakePrettySize(qint64 sourcesize)
Makes a formatted size from number.
Definition util.cpp:52
quint64 Available_
How much space is available to the current user.
Definition paths.h:202
A proper void type, akin to unit (or ()) type in functional languages.
Definition void.h:21