Modify database backup sequence
[platform/core/appfw/pkgmgr-tool.git] / src / pkg_upgrade / src / upgrader.cc
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "upgrader.hh"
18
19 #include <ctype.h>
20 #include <dirent.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <pwd.h>
24 #include <sqlite3.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <sys/smack.h>
29 #include <sys/stat.h>
30 #include <sys/time.h>
31 #include <sys/types.h>
32 #include <sys/wait.h>
33 #include <unistd.h>
34
35 #include <tzplatform_config.h>
36
37 #include "logging.hh"
38 #include "pkg_upgrader_factory.hh"
39
40 using std::list;
41 using std::shared_ptr;
42 using std::string;
43 using std::unique_ptr;
44
45 namespace {
46 constexpr char kLogFileName[] = "/var/log/appfw/app-installers/fota.log";
47 constexpr int kLogRotationSize = 1024 * 1024;  // 1MB
48 constexpr int kLogMaximumRotation = 3;
49 constexpr int kBufSize = 1024;
50 constexpr char kDbLabel[] = "User::Home";
51 constexpr char kAppfwUser[] = "app_fw";
52
53 class File {
54  public:
55   explicit File(const string& path) {
56     fd_ = open(path.c_str(), O_RDONLY);
57   }
58
59   File(const string& path, int flags, mode_t mode) {
60     fd_ = open(path.c_str(), flags, mode);
61   }
62
63   ~File() {
64     if (fd_ != -1)
65       close(fd_);
66   }
67
68   int GetFd() const {
69     return fd_;
70   }
71
72   int FStat(struct stat* statbuf) {
73     return fstat(fd_, statbuf);
74   }
75
76   int FChOwn(uid_t owner, gid_t group) {
77     return fchown(fd_, owner, group);
78   }
79
80   int FChMod(mode_t mode) {
81     return fchmod(fd_, mode);
82   }
83
84   int FSync() {
85     return fsync(fd_);
86   }
87
88  private:
89   int fd_ = -1;
90 };
91
92 }  // namespace
93
94 namespace common_fota {
95
96 Upgrader::Upgrader() {
97   logger_ = shared_ptr<utils::FileLogBackend>(new utils::FileLogBackend(
98       ::kLogFileName, ::kLogRotationSize, ::kLogMaximumRotation));
99   ::utils::LogCore::GetCore().AddLogBackend(logger_);
100   string path = tzplatform_getenv(TZ_SYS_DB);
101   SetDbPath(path);
102 }
103
104 void Upgrader::SetDbPath(const string& path) {
105   parser_db_ = path + "/.pkgmgr_parser.db";
106   cert_db_ = path + "/.pkgmgr_cert.db";
107 }
108
109 bool Upgrader::Process(PkgFinder* finder) {
110   if (CheckAndRestoreBackupDbs() != 0) {
111     LOG(ERROR) << "CheckAndRestoreBackupDbs failed";
112     return false;
113   }
114
115   if (MakeBackupDbs() != 0) {
116     LOG(ERROR) << "MakeBackupDbs failed";
117     return false;
118   }
119
120   // set the database permission to solve the case of using the backup database
121   // which the smack label is not applied properly
122   if (SetDbPermission(parser_db_) != 0) {
123     LOG(ERROR) << "SetDbPermission at parser_db failed";
124     return -1;
125   }
126
127   if (SetDbPermission(cert_db_) != 0) {
128     LOG(ERROR) << "SetDbPermission at cert_db failed";
129     return -1;
130   }
131
132   PkgUpgraderFactory factory;
133   auto list = factory.MakeList(finder);
134
135   for (auto& pkg : list) {
136     if (pkg->Upgrade()) {
137       LOG(DEBUG) << "upgrade success (" << pkg->GetId() << ")";
138       success_list_.push_back(move(pkg));
139     } else {
140       LOG(ERROR) << "upgrade failed (" << pkg->GetId() << ")";
141       failure_list_.push_back(move(pkg));
142     }
143   }
144
145   RemoveBackupDbs();
146
147   logger_->WriteLog(::utils::LogLevel::LOG_INFO, "", "Upgrade Done");
148   logger_->WriteLogToFile();
149   return true;
150 }
151
152 const list<unique_ptr<PkgUpgrader>>& Upgrader::GetSuccessList() const {
153   return success_list_;
154 }
155
156 const list<unique_ptr<PkgUpgrader>>& Upgrader::GetFailureList() const {
157   return failure_list_;
158 }
159
160 int Upgrader::CheckAndRestoreBackupDbs() {
161   // if backup flag exists, it means the previous backup process aborted.
162   if (CheckBackupFlag(parser_db_) == 0 || CheckBackupFlag(cert_db_) == 0) {
163     RemoveBackupDbs();
164     RemoveBackupFlag(parser_db_);
165     RemoveBackupFlag(cert_db_);
166     return 0;
167   }
168
169   if (IntegrityCheckBackupDbs()) {
170     RemoveBackupDbs();
171     return 0;
172   }
173
174   if (CheckAndRestoreBackup(parser_db_))
175     return -1;
176
177   if (CheckAndRestoreBackup(cert_db_))
178     return -1;
179
180   return 0;
181 }
182
183 int Upgrader::CheckAndRestoreBackup(const string& origin_path) {
184   string backup_path = origin_path + ".bck";
185   string journal_path = origin_path + "-journal";
186   string journal_backup_path = origin_path + ".bck-journal";
187
188   if (access(backup_path.c_str(), F_OK))
189     return 0;
190
191   if (access(journal_backup_path.c_str(), F_OK) == 0) {
192     if (rename(journal_backup_path.c_str(), journal_path.c_str())) {
193       LOG(ERROR) << "fail to rename " << journal_backup_path
194           << " to " << journal_path << " " << errno;
195       return -1;
196     }
197   }
198
199   if (rename(backup_path.c_str(), origin_path.c_str())) {
200     LOG(ERROR) << "fail to rename " << backup_path
201         << " to " << origin_path << " " << errno;
202     return -1;
203   }
204
205   return 0;
206 }
207
208 int Upgrader::IntegrityCheckBackupDbs() {
209   std::string parser_db_bck = parser_db_ + ".bck";
210   std::string cert_db_bck = cert_db_ + ".bck";
211
212   if (IntegrityCheck(parser_db_bck) != 0) {
213     LOG(ERROR) << "Fail to integrity check db(" << parser_db_bck << "%s)";
214     return -1;
215   }
216
217   if (IntegrityCheck(cert_db_bck) != 0) {
218     LOG(ERROR) << "Fail to integrity check db(" << cert_db_bck << "%s)";
219     return -1;
220   }
221
222   return 0;
223 }
224
225 int Upgrader::IntegrityCheck(const std::string& path) {
226   constexpr const char query[] = "PRAGMA integrity_check";
227   sqlite3* db;
228   sqlite3_stmt* stmt;
229
230   if (access(path.c_str(), F_OK))
231     return 0;
232
233   if (sqlite3_open_v2(path.c_str(), &db,
234       SQLITE_OPEN_READONLY, nullptr) != SQLITE_OK) {
235     LOG(ERROR) << "Fail to open (" << path << ") db handle";
236     return -1;
237   }
238   unique_ptr<sqlite3, decltype(sqlite3_close_v2)*>
239       db_auto(db, sqlite3_close_v2);
240
241   if (sqlite3_prepare_v2(db, query, strlen(query),
242       &stmt, nullptr) != SQLITE_OK) {
243     LOG(ERROR) << "prepare failed : " << sqlite3_errmsg(db);
244     return -1;
245   }
246   unique_ptr<sqlite3_stmt, decltype(sqlite3_finalize)*>
247       stmt_auto(stmt, sqlite3_finalize);
248
249   if (sqlite3_step(stmt) != SQLITE_ROW) {
250     LOG(ERROR) << "sqlite3_step fail";
251     return -1;
252   }
253
254   std::string val = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
255   if (val != "ok") {
256     LOG(ERROR) << "Fail to integrity check, db("
257         << path << "), val(" << val << ")";
258     return -1;
259   }
260
261   return 0;
262 }
263
264 int Upgrader::SetDbPermission(const string& path) {
265   struct stat sb;
266   struct passwd pwd;
267   struct passwd* result;
268   char buf[kBufSize];
269
270   int ret = getpwnam_r(kAppfwUser, &pwd, buf, sizeof(buf), &result);
271   if (result == nullptr) {
272     LOG(ERROR) << "getpwnam_r failed: " << errno;
273     return -1;
274   }
275   uid_t uid = pwd.pw_uid;
276
277   ret = getpwuid_r(uid, &pwd, buf, sizeof(buf), &result);
278   if (result == nullptr) {
279     LOG(ERROR) << "getpwuid_r failed: " << errno;
280     return -1;
281   }
282
283   for (const auto& p : std::vector<std::string>{path, path + "-journal"}) {
284     ::File file(p);
285
286     if (file.GetFd() == -1) {
287       LOG(ERROR) << "open(" << p << "failed: " << errno;
288       return -1;
289     }
290
291     ret = file.FStat(&sb);
292     if (ret == -1) {
293       LOG(ERROR) << "stat " << p << "failed: " << errno;
294       return -1;
295     }
296     if (S_ISLNK(sb.st_mode)) {
297       LOG(ERROR) << p << " is symlink!";
298       return -1;
299     }
300     ret = file.FChOwn(uid, pwd.pw_gid);
301     if (ret == -1) {
302       LOG(ERROR) << "fchown " << p << " failed: " << errno;
303       return -1;
304     }
305
306     mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
307     if (p.find(cert_db_) != string::npos)
308       mode |= S_IWOTH;
309     ret = file.FChMod(mode);
310     if (ret == -1) {
311       LOG(ERROR) << "fchmod " << p << " failed: " << errno;
312       return -1;
313     }
314     file.FSync();
315     if (smack_setlabel(p.c_str(), kDbLabel, SMACK_LABEL_ACCESS))
316       LOG(ERROR) << "failed chsmack -a " << kDbLabel << " " << path;
317   }
318
319   return 0;
320 }
321
322 int Upgrader::CreateBackupFlag(const string& path) {
323   string flag = path + ".bck.flag";
324
325   ::File file(flag, O_CREAT | O_WRONLY, 0644);
326   if (file.GetFd() == -1) {
327     LOG(ERROR) << "failed to create flag file " << flag << ", " << errno;
328     return -1;
329   }
330
331   return 0;
332 }
333
334 int Upgrader::CheckBackupFlag(const string& path) {
335   string flag = path + ".bck.flag";
336
337   if (access(flag.c_str(), F_OK) != 0)
338     return -1;
339
340   return 0;
341 }
342
343 int Upgrader::RemoveBackupFlag(const string& path) {
344   string flag = path + ".bck.flag";
345
346   if (remove(flag.c_str())) {
347     LOG(ERROR) << "cannot remove flag file(" << flag << ") " << errno;
348     return -1;
349   }
350
351   return 0;
352 }
353
354 int Upgrader::BackupFile(const string& src_path, const string& dest_path) {
355   int ret;
356   sqlite3* src_db;
357   sqlite3* dest_db;
358   sqlite3_backup* p_backup;
359
360   if (access(src_path.c_str(), F_OK) != 0) {
361     LOG(ERROR) << "File(" << src_path << ") is not exist";
362     return -1;
363   }
364
365   if (CreateBackupFlag(src_path)) {
366     LOG(ERROR) << "failed to create backup flag";
367     return -1;
368   }
369
370   if (sqlite3_open_v2(src_path.c_str(), &src_db,
371       SQLITE_OPEN_READONLY, nullptr) != SQLITE_OK) {
372     LOG(ERROR) << "Failed to open ( "
373         << src_path << ") db handle : " << src_path;
374     return -1;
375   }
376   unique_ptr<sqlite3, decltype(sqlite3_close_v2)*>
377       src_db_auto(src_db, sqlite3_close_v2);
378
379   if (sqlite3_open_v2(dest_path.c_str(), &dest_db,
380       SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr) != SQLITE_OK) {
381     LOG(ERROR) << "Failed to open ( "
382         << dest_path << ") db handle : " << dest_path;
383     return -1;
384   }
385   unique_ptr<sqlite3, decltype(sqlite3_close_v2)*>
386       dest_db_auto(dest_db, sqlite3_close_v2);
387
388   p_backup = sqlite3_backup_init(dest_db, "main", src_db, "main");
389   if (p_backup) {
390     do {
391       ret = sqlite3_backup_step(p_backup, -1);
392       if (ret == SQLITE_BUSY || ret == SQLITE_LOCKED)
393         sqlite3_sleep(250);
394     } while (ret == SQLITE_OK || ret == SQLITE_BUSY
395         || ret == SQLITE_LOCKED);
396
397     sqlite3_backup_finish(p_backup);
398   }
399
400   ret = sqlite3_errcode(dest_db);
401   if (ret != SQLITE_OK) {
402     LOG(ERROR) << "Fail to backup db val(" << ret << ")";
403     return -1;
404   }
405
406   if (SetDbPermission(dest_path) != 0)
407     return -1;
408
409   RemoveBackupFlag(src_path);
410
411   return 0;
412 }
413
414 int Upgrader::BackupDb(const string& src_path, const string& dest_path) {
415   if (BackupFile(src_path, dest_path) != 0)
416     return -1;
417
418   return 0;
419 }
420
421 int Upgrader::MakeBackupDbs() {
422   string parser_db_bck = parser_db_ + ".bck";
423   string cert_db_bck = cert_db_ + ".bck";
424
425   if (BackupDb(parser_db_, parser_db_bck) == -1) {
426     LOG(ERROR) << "Fail to backup [" << parser_db_ <<
427         "] to [" << parser_db_bck << "]";
428     RemoveBackupDbs();
429     return -1;
430   }
431
432   if (BackupDb(cert_db_, cert_db_bck) == -1) {
433     LOG(ERROR) << "Fail to backup [" << cert_db_ << "] to [" <<
434         cert_db_bck << "]";
435     RemoveBackupDbs();
436     return -1;
437   }
438
439   return 0;
440 }
441
442 void Upgrader::RemoveBackupPath(const string& origin_path) {
443   string backup_path = origin_path + ".bck";
444   string journal_backup_path = origin_path + ".bck-journal";
445
446   if (access(backup_path.c_str(), F_OK) == 0 && remove(backup_path.c_str()))
447     LOG(ERROR) << "cannot remove backup file(" << backup_path << ") " << errno;
448
449   if (access(journal_backup_path.c_str(), F_OK) == 0
450       && remove(journal_backup_path.c_str()))
451     LOG(ERROR) << "cannot remove backup file("
452         << journal_backup_path << ") " << errno;
453 }
454
455 void Upgrader::RemoveBackupDbs() {
456   RemoveBackupPath(parser_db_);
457   RemoveBackupPath(cert_db_);
458 }
459
460 }  // namespace common_fota