LeechCraft  0.6.70-15082-g543737046d
Modular cross-platform feature rich live environment.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules
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 <QFile>
12 #include <QSqlDatabase>
13 #include <QMessageBox>
14 #include <QtConcurrentRun>
15 #include <util/sll/visitor.h>
16 #include <util/sys/paths.h>
17 #include <util/threads/futures.h>
18 #include <util/util.h>
19 #include "dumper.h"
20 #include "util.h"
21 
22 namespace LC::Util
23 {
24  class FailedImpl final : public ConsistencyChecker::IFailed
25  {
26  const std::shared_ptr<ConsistencyChecker> Checker_;
27  public:
28  explicit FailedImpl (std::shared_ptr<ConsistencyChecker> checker)
29  : Checker_ {std::move (checker)}
30  {
31  }
32  private:
33  QFuture<ConsistencyChecker::DumpResult_t> DumpReinit () override
34  {
35  return Checker_->DumpReinit ();
36  }
37  };
38 
39  ConsistencyChecker::ConsistencyChecker (QString dbPath, QString dialogContext, QObject *parent)
40  : QObject { parent }
41  , DBPath_ { std::move (dbPath) }
42  , DialogContext_ { std::move (dialogContext) }
43  {
44  }
45 
46  std::shared_ptr<ConsistencyChecker> ConsistencyChecker::Create (QString dbPath, QString dialogContext)
47  {
48  return std::shared_ptr<ConsistencyChecker> { new ConsistencyChecker { std::move (dbPath), std::move (dialogContext) } };
49  }
50 
52  {
53  return QtConcurrent::run ([pThis = shared_from_this ()] { return pThis->CheckDB (); });
54  }
55 
56  ConsistencyChecker::CheckResult_t ConsistencyChecker::CheckDB ()
57  {
58  qDebug () << Q_FUNC_INFO
59  << "checking"
60  << DBPath_;
61  const auto& connName = Util::GenConnectionName ("ConsistencyChecker_" + DBPath_);
62 
63  std::shared_ptr<QSqlDatabase> db
64  {
65  new QSqlDatabase { QSqlDatabase::addDatabase ("QSQLITE", connName) },
66  [connName] (QSqlDatabase *db)
67  {
68  delete db;
69  QSqlDatabase::removeDatabase (connName);
70  }
71  };
72 
73  db->setDatabaseName (DBPath_);
74  if (!db->open ())
75  {
76  qWarning () << Q_FUNC_INFO
77  << "cannot open the DB, but that's not the kind of errors we're solving.";
78  return Succeeded {};
79  }
80 
81  QSqlQuery pragma { *db };
82  static const QString checkQuery = qgetenv ("LC_THOROUGH_SQLITE_CHECK") == "1" ?
83  QStringLiteral ("PRAGMA integrity_check;") :
84  QStringLiteral ("PRAGMA quick_check;");
85  const auto isGood = pragma.exec (checkQuery) &&
86  pragma.next () &&
87  pragma.value (0) == "ok";
88  qDebug () << Q_FUNC_INFO
89  << "done checking"
90  << DBPath_
91  << "; result is:"
92  << isGood;
93  if (isGood)
94  return Succeeded {};
95  else
96  return std::make_shared<FailedImpl> (shared_from_this ());
97  }
98 
99  QFuture<ConsistencyChecker::DumpResult_t> ConsistencyChecker::DumpReinit ()
100  {
102  iface.reportStarted ();
103 
104  DumpReinitImpl (iface);
105 
106  return iface.future ();
107  }
108 
109  namespace
110  {
112  const ConsistencyChecker::DumpResult_t& result)
113  {
114  iface.reportFinished (&result);
115  }
116  }
117 
118  void ConsistencyChecker::DumpReinitImpl (QFutureInterface<DumpResult_t> iface)
119  {
120  const QFileInfo fi { DBPath_ };
121  const auto filesize = fi.size ();
122 
123  while (true)
124  {
125  const auto available = GetSpaceInfo (DBPath_).Available_;
126 
127  qDebug () << Q_FUNC_INFO
128  << "db size:" << filesize
129  << "free space:" << available;
130  if (available >= static_cast<quint64> (filesize))
131  break;
132 
133  if (QMessageBox::question (nullptr,
134  DialogContext_,
135  tr ("Not enough available space on partition with file %1: "
136  "%2 while the restored file is expected to be around %3. "
137  "Please either free some disk space on this partition "
138  "and retry or cancel the restore process.")
139  .arg ("<em>" + DBPath_ + "</em>",
140  Util::MakePrettySize (available),
141  Util::MakePrettySize (filesize)),
142  QMessageBox::Retry | QMessageBox::Cancel) == QMessageBox::Cancel)
143  {
144  ReportResult (iface, DumpError { tr ("Not enough available disk space.") });
145  return;
146  }
147  }
148 
149  const auto& newPath = DBPath_ + ".new";
150 
151  while (true)
152  {
153  if (!QFile::exists (newPath))
154  break;
155 
156  if (QMessageBox::question (nullptr,
157  DialogContext_,
158  tr ("%1 already exists. Please either remove the file manually "
159  "and retry or cancel the restore process.")
160  .arg ("<em>" + newPath + "</em>"),
161  QMessageBox::Retry | QMessageBox::Cancel) == QMessageBox::Cancel)
162  {
163  ReportResult (iface, DumpError { tr ("Backup file already exists.") });
164  return;
165  }
166  }
167 
168  const auto dumper = new Dumper { DBPath_, newPath };
169 
170  const auto managed = shared_from_this ();
171  Util::Sequence (nullptr, dumper->GetFuture ()) >>
172  [iface, newPath, managed] (const Dumper::Result_t& result)
173  {
174  Util::Visit (result,
175  [iface] (const Dumper::Error& error)
176  {
177  ReportResult (iface,
178  DumpError { tr ("Unable to restore the database.") + " " + error.What_ });
179  },
180  [iface, newPath, managed] (Dumper::Finished) { managed->HandleDumperFinished (iface, newPath); });
181  };
182  }
183 
184  void ConsistencyChecker::HandleDumperFinished (QFutureInterface<DumpResult_t> iface, const QString& to)
185  {
186  const auto oldSize = QFileInfo { DBPath_ }.size ();
187  const auto newSize = QFileInfo { to }.size ();
188 
189  const auto& backup = DBPath_ + ".bak";
190  while (!QFile::rename (DBPath_, backup))
191  QMessageBox::critical (nullptr,
192  DialogContext_,
193  tr ("Unable to backup %1 to %2. Please remove %2 and hit OK.")
194  .arg (DBPath_,
195  backup));
196 
197  QFile::rename (to, DBPath_);
198 
199  ReportResult (iface, DumpFinished { oldSize, newSize });
200  }
201 }
visitor.h
LC::Util::ConsistencyChecker::CheckResult_t
std::variant< Succeeded, Failed > CheckResult_t
Definition: consistencychecker.h:58
LC::Util::ConsistencyChecker::DumpResult_t
std::variant< DumpFinished, DumpError > DumpResult_t
Definition: consistencychecker.h:45
LC::Util::MakePrettySize
QString MakePrettySize(qint64 sourcesize)
Makes a formatted size from number.
Definition: util.cpp:59
LC::Util
Definition: icoreproxy.h:33
util.h
futures.h
LC::Util::ConsistencyChecker::Succeeded
Definition: consistencychecker.h:47
LC::Util::Visit
auto Visit(const Either< Left, Right > &either, Args &&... args)
Definition: either.h:212
LC::Util::ConsistencyChecker::StartCheck
QFuture< CheckResult_t > StartCheck()
Definition: consistencychecker.cpp:57
paths.h
LC::Util::ConsistencyChecker::Create
static std::shared_ptr< ConsistencyChecker > Create(QString dbPath, QString dialogContext)
Definition: consistencychecker.cpp:52
consistencychecker.h
LC::Util::FailedImpl::FailedImpl
FailedImpl(std::shared_ptr< ConsistencyChecker > checker)
Definition: consistencychecker.cpp:40
QFuture
Definition: idownload.h:17
LC::Util::SpaceInfo::Available_
quint64 Available_
How much space is available to the current user.
Definition: paths.h:202
QFutureInterface
Definition: consistencychecker.h:20
LC::Util::GenConnectionName
QString GenConnectionName(const QString &base)
Generates an unique thread-safe connection name.
Definition: util.cpp:57
util.h
dumper.h
LC::Util::Dumper::Result_t
std::variant< Finished, Error > Result_t
Definition: dumper.h:49
LC::Util::GetSpaceInfo
SpaceInfo GetSpaceInfo(const QString &path)
Returns the disk space info of the partition containing path.
Definition: paths.cpp:160