LeechCraft  0.6.70-18450-gabe19ee3b0
Modular cross-platform feature rich live environment.
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 
9 #include "consistencychecker.h"
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 
84  Task<RecoverResult_t> Recover (QString dbPath)
85  {
86  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 }
Task< CheckResult_t > Check(QString dbPath)
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
SpaceInfo GetSpaceInfo(const QString &path)
Returns the disk space info of the partition containing path.
Definition: paths.cpp:155
requires std::invocable< F, const L & > detail::EitherAwaiter< L, R, F > WithHandler(const Either< L, R > &either, F &&errorHandler)
Definition: either.h:82
std::variant< RecoverNoSpace, RecoverTargetExists, RecoverOtherFailure > RecoverFailed
Task< Either< QString, Void > > DumpSqlite(QString from, QString to)
Definition: dumper.cpp:58
QString MakePrettySize(qint64 sourcesize)
Makes a formatted size from number.
Definition: util.cpp:52
auto Visit(const Either< Left, Right > &either, Args &&... args)
Definition: either.h:180
Task< RecoverResult_t > Recover(QString dbPath)
Task< RecoverResult_t > RecoverWithUserInteraction(QString dbPath, QString diaTitle)
detail::ScopeGuard< F > MakeScopeGuard(const F &f)
Returns an object performing passed function on scope exit.
Definition: util.h:155
quint64 Available_
How much space is available to the current user.
Definition: paths.h:202
QString GenConnectionName(const QString &base)
Generates an unique thread-safe connection name.
Definition: util.cpp:55
A proper void type, akin to unit (or ()) type in functional languages.
Definition: void.h:20