Add fsync after creating backup flag
[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   file.FSync();
337
338   return 0;
339 }
340
341 int Upgrader::CheckBackupFlag(const string& path) {
342   string flag = path + ".bck.flag";
343
344   if (access(flag.c_str(), F_OK) != 0)
345     return -1;
346
347   return 0;
348 }
349
350 int Upgrader::RemoveBackupFlag(const string& path) {
351   string flag = path + ".bck.flag";
352
353   if (remove(flag.c_str())) {
354     LOG(ERROR) << "cannot remove flag file(" << flag << ") " << errno;
355     return -1;
356   }
357
358   return 0;
359 }
360
361 int Upgrader::BackupFile(const string& src_path, const string& dest_path) {
362   int ret;
363   sqlite3* src_db;
364   sqlite3* dest_db;
365   sqlite3_backup* p_backup;
366
367   if (access(src_path.c_str(), F_OK) != 0) {
368     LOG(ERROR) << "File(" << src_path << ") is not exist";
369     return -1;
370   }
371
372   if (CreateBackupFlag(src_path)) {
373     LOG(ERROR) << "failed to create backup flag";
374     return -1;
375   }
376
377   if (sqlite3_open_v2(src_path.c_str(), &src_db,
378       SQLITE_OPEN_READONLY, nullptr) != SQLITE_OK) {
379     LOG(ERROR) << "Failed to open ( "
380         << src_path << ") db handle : " << src_path;
381     return -1;
382   }
383   unique_ptr<sqlite3, decltype(sqlite3_close_v2)*>
384       src_db_auto(src_db, sqlite3_close_v2);
385
386   if (sqlite3_open_v2(dest_path.c_str(), &dest_db,
387       SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr) != SQLITE_OK) {
388     LOG(ERROR) << "Failed to open ( "
389         << dest_path << ") db handle : " << dest_path;
390     return -1;
391   }
392   unique_ptr<sqlite3, decltype(sqlite3_close_v2)*>
393       dest_db_auto(dest_db, sqlite3_close_v2);
394
395   p_backup = sqlite3_backup_init(dest_db, "main", src_db, "main");
396   if (p_backup) {
397     do {
398       ret = sqlite3_backup_step(p_backup, -1);
399       if (ret == SQLITE_BUSY || ret == SQLITE_LOCKED)
400         sqlite3_sleep(250);
401     } while (ret == SQLITE_OK || ret == SQLITE_BUSY
402         || ret == SQLITE_LOCKED);
403
404     sqlite3_backup_finish(p_backup);
405   }
406
407   ret = sqlite3_errcode(dest_db);
408   if (ret != SQLITE_OK) {
409     LOG(ERROR) << "Fail to backup db val(" << ret << ")";
410     return -1;
411   }
412
413   if (SetDbPermission(dest_path) != 0)
414     return -1;
415
416   RemoveBackupFlag(src_path);
417
418   return 0;
419 }
420
421 int Upgrader::BackupDb(const string& src_path, const string& dest_path) {
422   if (BackupFile(src_path, dest_path) != 0)
423     return -1;
424
425   return 0;
426 }
427
428 int Upgrader::MakeBackupDbs() {
429   string parser_db_bck = parser_db_ + ".bck";
430   string cert_db_bck = cert_db_ + ".bck";
431
432   if (BackupDb(parser_db_, parser_db_bck) == -1) {
433     LOG(ERROR) << "Fail to backup [" << parser_db_ <<
434         "] to [" << parser_db_bck << "]";
435     RemoveBackupDbs();
436     return -1;
437   }
438
439   if (BackupDb(cert_db_, cert_db_bck) == -1) {
440     LOG(ERROR) << "Fail to backup [" << cert_db_ << "] to [" <<
441         cert_db_bck << "]";
442     RemoveBackupDbs();
443     return -1;
444   }
445
446   return 0;
447 }
448
449 void Upgrader::RemoveBackupPath(const string& origin_path) {
450   string backup_path = origin_path + ".bck";
451   string journal_backup_path = origin_path + ".bck-journal";
452
453   if (access(backup_path.c_str(), F_OK) == 0 && remove(backup_path.c_str()))
454     LOG(ERROR) << "cannot remove backup file(" << backup_path << ") " << errno;
455
456   if (access(journal_backup_path.c_str(), F_OK) == 0
457       && remove(journal_backup_path.c_str()))
458     LOG(ERROR) << "cannot remove backup file("
459         << journal_backup_path << ") " << errno;
460 }
461
462 void Upgrader::RemoveBackupDbs() {
463   RemoveBackupPath(parser_db_);
464   RemoveBackupPath(cert_db_);
465 }
466
467 }  // namespace common_fota