72d2599be4c5fb834fe9a98085722c2b81e5e63f
[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 false;
125   }
126
127   if (SetDbPermission(cert_db_) != 0) {
128     LOG(ERROR) << "SetDbPermission at cert_db failed";
129     return false;
130   }
131
132   PkgUpgraderFactory factory;
133   auto list = factory.MakeList(finder);
134   if (factory.IsFailed()) {
135     RemoveBackupDbs();
136     return false;
137   }
138
139   for (auto& pkg : list) {
140     if (pkg->Upgrade()) {
141       LOG(DEBUG) << "upgrade success (" << pkg->GetId() << ")";
142       success_list_.push_back(move(pkg));
143     } else {
144       LOG(ERROR) << "upgrade failed (" << pkg->GetId() << ")";
145       failure_list_.push_back(move(pkg));
146     }
147   }
148
149   RemoveBackupDbs();
150
151   logger_->WriteLog(::utils::LogLevel::LOG_INFO, "", "Upgrade Done");
152   logger_->WriteLogToFile();
153   return true;
154 }
155
156 const list<unique_ptr<PkgUpgrader>>& Upgrader::GetSuccessList() const {
157   return success_list_;
158 }
159
160 const list<unique_ptr<PkgUpgrader>>& Upgrader::GetFailureList() const {
161   return failure_list_;
162 }
163
164 int Upgrader::CheckAndRestoreBackupDbs() {
165   // if backup flag exists, it means the previous backup process aborted.
166   if (CheckBackupFlag(parser_db_) == 0 || CheckBackupFlag(cert_db_) == 0) {
167     RemoveBackupDbs();
168     RemoveBackupFlag(parser_db_);
169     RemoveBackupFlag(cert_db_);
170     return 0;
171   }
172
173   if (IntegrityCheckBackupDbs()) {
174     RemoveBackupDbs();
175     return 0;
176   }
177
178   if (CheckAndRestoreBackup(parser_db_))
179     return -1;
180
181   if (CheckAndRestoreBackup(cert_db_))
182     return -1;
183
184   return 0;
185 }
186
187 int Upgrader::CheckAndRestoreBackup(const string& origin_path) {
188   string backup_path = origin_path + ".bck";
189   string journal_path = origin_path + "-journal";
190   string journal_backup_path = origin_path + ".bck-journal";
191
192   if (access(backup_path.c_str(), F_OK))
193     return 0;
194
195   if (access(journal_backup_path.c_str(), F_OK) == 0) {
196     if (rename(journal_backup_path.c_str(), journal_path.c_str())) {
197       LOG(ERROR) << "fail to rename " << journal_backup_path
198           << " to " << journal_path << " " << errno;
199       return -1;
200     }
201   }
202
203   if (rename(backup_path.c_str(), origin_path.c_str())) {
204     LOG(ERROR) << "fail to rename " << backup_path
205         << " to " << origin_path << " " << errno;
206     return -1;
207   }
208
209   return 0;
210 }
211
212 int Upgrader::IntegrityCheckBackupDbs() {
213   std::string parser_db_bck = parser_db_ + ".bck";
214   std::string cert_db_bck = cert_db_ + ".bck";
215
216   if (IntegrityCheck(parser_db_bck) != 0) {
217     LOG(ERROR) << "Fail to integrity check db(" << parser_db_bck << "%s)";
218     return -1;
219   }
220
221   if (IntegrityCheck(cert_db_bck) != 0) {
222     LOG(ERROR) << "Fail to integrity check db(" << cert_db_bck << "%s)";
223     return -1;
224   }
225
226   return 0;
227 }
228
229 int Upgrader::IntegrityCheck(const std::string& path) {
230   constexpr const char query[] = "PRAGMA integrity_check";
231   sqlite3* db;
232   sqlite3_stmt* stmt;
233
234   if (access(path.c_str(), F_OK))
235     return 0;
236
237   if (sqlite3_open_v2(path.c_str(), &db,
238       SQLITE_OPEN_READONLY, nullptr) != SQLITE_OK) {
239     LOG(ERROR) << "Fail to open (" << path << ") db handle";
240     return -1;
241   }
242   unique_ptr<sqlite3, decltype(sqlite3_close_v2)*>
243       db_auto(db, sqlite3_close_v2);
244
245   if (sqlite3_prepare_v2(db, query, strlen(query),
246       &stmt, nullptr) != SQLITE_OK) {
247     LOG(ERROR) << "prepare failed : " << sqlite3_errmsg(db);
248     return -1;
249   }
250   unique_ptr<sqlite3_stmt, decltype(sqlite3_finalize)*>
251       stmt_auto(stmt, sqlite3_finalize);
252
253   if (sqlite3_step(stmt) != SQLITE_ROW) {
254     LOG(ERROR) << "sqlite3_step fail";
255     return -1;
256   }
257
258   std::string val = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
259   if (val != "ok") {
260     LOG(ERROR) << "Fail to integrity check, db("
261         << path << "), val(" << val << ")";
262     return -1;
263   }
264
265   return 0;
266 }
267
268 int Upgrader::SetDbPermission(const string& path) {
269   struct stat sb;
270   struct passwd pwd;
271   struct passwd* result;
272   char buf[kBufSize];
273
274   int ret = getpwnam_r(kAppfwUser, &pwd, buf, sizeof(buf), &result);
275   if (result == nullptr) {
276     LOG(ERROR) << "getpwnam_r failed: " << errno;
277     return -1;
278   }
279   uid_t uid = pwd.pw_uid;
280
281   ret = getpwuid_r(uid, &pwd, buf, sizeof(buf), &result);
282   if (result == nullptr) {
283     LOG(ERROR) << "getpwuid_r failed: " << errno;
284     return -1;
285   }
286
287   for (const auto& p : std::vector<std::string>{path, path + "-journal"}) {
288     ::File file(p);
289
290     if (file.GetFd() == -1) {
291       LOG(ERROR) << "open(" << p << "failed: " << errno;
292       return -1;
293     }
294
295     ret = file.FStat(&sb);
296     if (ret == -1) {
297       LOG(ERROR) << "stat " << p << "failed: " << errno;
298       return -1;
299     }
300     if (S_ISLNK(sb.st_mode)) {
301       LOG(ERROR) << p << " is symlink!";
302       return -1;
303     }
304     ret = file.FChOwn(uid, pwd.pw_gid);
305     if (ret == -1) {
306       LOG(ERROR) << "fchown " << p << " failed: " << errno;
307       return -1;
308     }
309
310     mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
311     if (p.find(cert_db_) != string::npos)
312       mode |= S_IWOTH;
313     ret = file.FChMod(mode);
314     if (ret == -1) {
315       LOG(ERROR) << "fchmod " << p << " failed: " << errno;
316       return -1;
317     }
318     file.FSync();
319     if (smack_setlabel(p.c_str(), kDbLabel, SMACK_LABEL_ACCESS)) {
320       LOG(ERROR) << "failed chsmack -a " << kDbLabel << " " << path;
321       return -1;
322     }
323   }
324
325   return 0;
326 }
327
328 int Upgrader::CreateBackupFlag(const string& path) {
329   string flag = path + ".bck.flag";
330
331   ::File file(flag, O_CREAT | O_WRONLY, 0644);
332   if (file.GetFd() == -1) {
333     LOG(ERROR) << "failed to create flag file " << flag << ", " << errno;
334     return -1;
335   }
336
337   return 0;
338 }
339
340 int Upgrader::CheckBackupFlag(const string& path) {
341   string flag = path + ".bck.flag";
342
343   if (access(flag.c_str(), F_OK) != 0)
344     return -1;
345
346   return 0;
347 }
348
349 int Upgrader::RemoveBackupFlag(const string& path) {
350   string flag = path + ".bck.flag";
351
352   if (remove(flag.c_str())) {
353     LOG(ERROR) << "cannot remove flag file(" << flag << ") " << errno;
354     return -1;
355   }
356
357   return 0;
358 }
359
360 int Upgrader::BackupFile(const string& src_path, const string& dest_path) {
361   int ret;
362   sqlite3* src_db;
363   sqlite3* dest_db;
364   sqlite3_backup* p_backup;
365
366   if (access(src_path.c_str(), F_OK) != 0) {
367     LOG(ERROR) << "File(" << src_path << ") is not exist";
368     return -1;
369   }
370
371   if (CreateBackupFlag(src_path)) {
372     LOG(ERROR) << "failed to create backup flag";
373     return -1;
374   }
375
376   if (sqlite3_open_v2(src_path.c_str(), &src_db,
377       SQLITE_OPEN_READONLY, nullptr) != SQLITE_OK) {
378     LOG(ERROR) << "Failed to open ( "
379         << src_path << ") db handle : " << src_path;
380     return -1;
381   }
382   unique_ptr<sqlite3, decltype(sqlite3_close_v2)*>
383       src_db_auto(src_db, sqlite3_close_v2);
384
385   if (sqlite3_open_v2(dest_path.c_str(), &dest_db,
386       SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr) != SQLITE_OK) {
387     LOG(ERROR) << "Failed to open ( "
388         << dest_path << ") db handle : " << dest_path;
389     return -1;
390   }
391   unique_ptr<sqlite3, decltype(sqlite3_close_v2)*>
392       dest_db_auto(dest_db, sqlite3_close_v2);
393
394   p_backup = sqlite3_backup_init(dest_db, "main", src_db, "main");
395   if (p_backup) {
396     do {
397       ret = sqlite3_backup_step(p_backup, -1);
398       if (ret == SQLITE_BUSY || ret == SQLITE_LOCKED)
399         sqlite3_sleep(250);
400     } while (ret == SQLITE_OK || ret == SQLITE_BUSY
401         || ret == SQLITE_LOCKED);
402
403     sqlite3_backup_finish(p_backup);
404   }
405
406   ret = sqlite3_errcode(dest_db);
407   if (ret != SQLITE_OK) {
408     LOG(ERROR) << "Fail to backup db val(" << ret << ")";
409     return -1;
410   }
411
412   if (SetDbPermission(dest_path) != 0)
413     return -1;
414
415   RemoveBackupFlag(src_path);
416
417   return 0;
418 }
419
420 int Upgrader::BackupDb(const string& src_path, const string& dest_path) {
421   if (BackupFile(src_path, dest_path) != 0)
422     return -1;
423
424   return 0;
425 }
426
427 int Upgrader::MakeBackupDbs() {
428   string parser_db_bck = parser_db_ + ".bck";
429   string cert_db_bck = cert_db_ + ".bck";
430
431   if (BackupDb(parser_db_, parser_db_bck) == -1) {
432     LOG(ERROR) << "Fail to backup [" << parser_db_ <<
433         "] to [" << parser_db_bck << "]";
434     RemoveBackupDbs();
435     return -1;
436   }
437
438   if (BackupDb(cert_db_, cert_db_bck) == -1) {
439     LOG(ERROR) << "Fail to backup [" << cert_db_ << "] to [" <<
440         cert_db_bck << "]";
441     RemoveBackupDbs();
442     return -1;
443   }
444
445   return 0;
446 }
447
448 void Upgrader::RemoveBackupPath(const string& origin_path) {
449   string backup_path = origin_path + ".bck";
450   string journal_backup_path = origin_path + ".bck-journal";
451
452   if (access(backup_path.c_str(), F_OK) == 0 && remove(backup_path.c_str()))
453     LOG(ERROR) << "cannot remove backup file(" << backup_path << ") " << errno;
454
455   if (access(journal_backup_path.c_str(), F_OK) == 0
456       && remove(journal_backup_path.c_str()))
457     LOG(ERROR) << "cannot remove backup file("
458         << journal_backup_path << ") " << errno;
459 }
460
461 void Upgrader::RemoveBackupDbs() {
462   RemoveBackupPath(parser_db_);
463   RemoveBackupPath(cert_db_);
464 }
465
466 }  // namespace common_fota