1 /*
2 * This file is part of signon
3 *
4 * Copyright (C) 2009-2010 Nokia Corporation.
5 * Copyright (C) 2013-2016 Canonical Ltd.
6 *
7 * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * version 2.1 as published by the Free Software Foundation.
12 *
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 * 02110-1301 USA
22 */
23
24 #include <QCoreApplication>
25 #include <QDebug>
26 #include <QDir>
27 #include <QFile>
28 #include <QDBusConnection>
29
30 #include "SignOn/misc.h"
31
32 #include "backup.h"
33 #include "backup_adaptor.h"
34 #include "credentialsaccessmanager.h"
35 #include "signond-common.h"
36
37 #define BACKUP_DIR_NAME() \
38 (QDir::separator() + QLatin1String("backup"))
39
40 using namespace SignOn;
41
42 namespace SignonDaemonNS {
43
Backup(CredentialsAccessManager * cam,bool backupMode,QObject * parent)44 Backup::Backup(CredentialsAccessManager *cam, bool backupMode,
45 QObject *parent):
46 QObject(parent),
47 m_cam(cam),
48 m_backup(backupMode)
49 {
50 QDBusConnection sessionConnection = QDBusConnection::sessionBus();
51
52 QDBusConnection::RegisterOptions registerSessionOptions =
53 QDBusConnection::ExportAdaptors;
54
55 (void)new BackupclientAdaptor(this);
56
57 if (!sessionConnection.registerObject(SIGNOND_DAEMON_OBJECTPATH
58 + QLatin1String("/Backup"),
59 this, registerSessionOptions)) {
60 TRACE() << "Object cannot be registered";
61
62 qFatal("SignonDaemon requires to register backup object");
63 }
64
65 if (!sessionConnection.registerService(SIGNOND_SERVICE +
66 QLatin1String(".Backup"))) {
67 QDBusError err = sessionConnection.lastError();
68 TRACE() << "Service cannot be registered: " <<
69 err.errorString(err.type());
70
71 qFatal("SignonDaemon requires to register backup service");
72 }
73 }
74
~Backup()75 Backup::~Backup()
76 {
77 QDBusConnection sessionConnection = QDBusConnection::sessionBus();
78 sessionConnection.unregisterObject(SIGNOND_DAEMON_OBJECTPATH
79 + QLatin1String("/Backup"));
80 sessionConnection.unregisterService(SIGNOND_SERVICE
81 + QLatin1String(".Backup"));
82 }
83
eraseBackupDir() const84 void Backup::eraseBackupDir() const
85 {
86 const CAMConfiguration &config = m_cam->configuration();
87 QString backupRoot = config.m_storagePath + BACKUP_DIR_NAME();
88
89 QDir target(backupRoot);
90 if (!target.exists()) return;
91
92 QStringList targetEntries = target.entryList(QDir::Files);
93 foreach (QString entry, targetEntries) {
94 target.remove(entry);
95 }
96
97 target.rmdir(backupRoot);
98 }
99
copyToBackupDir(const QStringList & fileNames) const100 bool Backup::copyToBackupDir(const QStringList &fileNames) const
101 {
102 const CAMConfiguration &config = m_cam->configuration();
103 QString backupRoot = config.m_storagePath + BACKUP_DIR_NAME();
104
105 QDir target(backupRoot);
106 if (!target.exists() && !target.mkpath(backupRoot)) {
107 qCritical() << "Cannot create target directory";
108 return false;
109 }
110
111 setUserOwnership(backupRoot);
112
113 /* Now copy the files to be backed up */
114 bool ok = true;
115 foreach (const QString &fileName, fileNames) {
116 /* Remove the target file, if it exists */
117 if (target.exists(fileName))
118 target.remove(fileName);
119
120 /* Copy the source into the target directory */
121 QString source = config.m_storagePath + QDir::separator() + fileName;
122 if (!QFile::exists(source)) continue;
123
124 QString destination = backupRoot + QDir::separator() + fileName;
125 ok = QFile::copy(source, destination);
126 if (!ok) {
127 BLAME() << "Copying" << source << "to" << destination << "failed";
128 break;
129 }
130
131 setUserOwnership(destination);
132 }
133
134 return ok;
135 }
136
copyFromBackupDir(const QStringList & fileNames) const137 bool Backup::copyFromBackupDir(const QStringList &fileNames) const
138 {
139 const CAMConfiguration &config = m_cam->configuration();
140 QString backupRoot = config.m_storagePath + BACKUP_DIR_NAME();
141
142 QDir sourceDir(backupRoot);
143 if (!sourceDir.exists()) {
144 TRACE() << "Backup directory does not exist!";
145 }
146
147 if (!sourceDir.exists(config.m_dbName)) {
148 TRACE() << "Backup does not contain DB:" << config.m_dbName;
149 }
150
151 /* Now restore the files from the backup */
152 bool ok = true;
153 QDir target(config.m_storagePath);
154 QStringList movedFiles, copiedFiles;
155 foreach (const QString &fileName, fileNames) {
156 /* Rename the target file, if it exists */
157 if (target.exists(fileName)) {
158 if (target.rename(fileName, fileName + QLatin1String(".bak")))
159 movedFiles += fileName;
160 }
161
162 /* Copy the source into the target directory */
163 QString source = backupRoot + QDir::separator() + fileName;
164 if (!QFile::exists(source)) {
165 TRACE() << "Ignoring file not present in backup:" << source;
166 continue;
167 }
168
169 QString destination =
170 config.m_storagePath + QDir::separator() + fileName;
171
172 ok = QFile::copy(source, destination);
173 if (ok) {
174 copiedFiles << fileName;
175 } else {
176 qWarning() << "Copy failed for:" << source;
177 break;
178 }
179 }
180
181 if (!ok) {
182 qWarning() << "Restore failed, recovering previous DB";
183
184 foreach (const QString &fileName, copiedFiles) {
185 target.remove(fileName);
186 }
187
188 foreach (const QString &fileName, movedFiles) {
189 if (!target.rename(fileName + QLatin1String(".bak"), fileName)) {
190 qCritical() << "Could not recover:" << fileName;
191 }
192 }
193 } else {
194 /* delete ".bak" files */
195 foreach (const QString &fileName, movedFiles) {
196 target.remove(fileName + QLatin1String(".bak"));
197 }
198
199 }
200 return ok;
201 }
202
createStorageFileTree(const QStringList & backupFiles) const203 bool Backup::createStorageFileTree(const QStringList &backupFiles) const
204 {
205 const CAMConfiguration &config = m_cam->configuration();
206 QDir storageDir(config.m_storagePath);
207
208 if (!storageDir.exists()) {
209 if (!storageDir.mkpath(config.m_storagePath)) {
210 qCritical() << "Could not create storage dir for backup.";
211 return false;
212 }
213 }
214
215 foreach (const QString &fileName, backupFiles) {
216 if (storageDir.exists(fileName)) continue;
217
218 QString filePath = storageDir.path() + QDir::separator() + fileName;
219 QFile file(filePath);
220 if (!file.open(QIODevice::WriteOnly)) {
221 qCritical() << "Failed to create empty file for backup:" << filePath;
222 return false;
223 } else {
224 file.close();
225 }
226 }
227
228 return true;
229 }
230
backupStarts()231 uchar Backup::backupStarts()
232 {
233 TRACE() << "backup";
234 if (!m_backup && m_cam->credentialsSystemOpened())
235 {
236 m_cam->closeCredentialsSystem();
237 if (m_cam->credentialsSystemOpened())
238 {
239 qCritical() << "Cannot close credentials database";
240 return 2;
241 }
242 }
243
244 const CAMConfiguration &config = m_cam->configuration();
245
246 /* do backup copy: prepare the list of files to be backed up */
247 QStringList backupFiles;
248 backupFiles << config.m_dbName;
249 backupFiles << m_cam->backupFiles();
250
251 /* make sure that all the backup files and storage directory exist:
252 create storage dir and empty files if not so, as backup/restore
253 operations must be consistent */
254 if (!createStorageFileTree(backupFiles)) {
255 qCritical() << "Cannot create backup file tree.";
256 return 2;
257 }
258
259 /* perform the copy */
260 eraseBackupDir();
261 if (!copyToBackupDir(backupFiles)) {
262 qCritical() << "Cannot copy database";
263 if (!m_backup)
264 m_cam->openCredentialsSystem();
265 return 2;
266 }
267
268 if (!m_backup)
269 {
270 //mount file system back
271 if (!m_cam->openCredentialsSystem()) {
272 qCritical() << "Cannot reopen database";
273 }
274 }
275 return 0;
276 }
277
backupFinished()278 uchar Backup::backupFinished()
279 {
280 TRACE() << "close";
281
282 eraseBackupDir();
283
284 if (m_backup)
285 {
286 //close daemon
287 TRACE() << "close daemon";
288 QCoreApplication::quit();
289 }
290
291 return 0;
292 }
293
294 /*
295 * Does nothing but start-on-demand
296 * */
restoreStarts()297 uchar Backup::restoreStarts()
298 {
299 TRACE();
300 return 0;
301 }
302
restoreFinished()303 uchar Backup::restoreFinished()
304 {
305 TRACE() << "restore";
306 //restore requested
307 if (m_cam->credentialsSystemOpened())
308 {
309 //umount file system
310 if (!m_cam->closeCredentialsSystem())
311 {
312 qCritical() << "database cannot be closed";
313 return 2;
314 }
315 }
316
317 const CAMConfiguration &config = m_cam->configuration();
318
319 QStringList backupFiles;
320 backupFiles << config.m_dbName;
321 backupFiles << m_cam->backupFiles();
322
323 /* perform the copy */
324 if (!copyFromBackupDir(backupFiles)) {
325 qCritical() << "Cannot copy database";
326 m_cam->openCredentialsSystem();
327 return 2;
328 }
329
330 eraseBackupDir();
331
332 //TODO check database integrity
333 if (!m_backup)
334 {
335 //mount file system back
336 if (!m_cam->openCredentialsSystem())
337 return 2;
338 }
339
340 return 0;
341 }
342
343 } //namespace SignonDaemonNS
344