Create DB file backup in case it was discovered to be corrupted 49/297649/16
authorTomasz Swierczek <t.swierczek@samsung.com>
Tue, 22 Aug 2023 07:54:48 +0000 (09:54 +0200)
committerTomasz Swierczek <t.swierczek@samsung.com>
Wed, 30 Aug 2023 17:53:08 +0000 (19:53 +0200)
This way, the DB can be later analyzed in detail.
Unit tests were modified to check if the backup
is properly created.

This change was requested by VD division.

Change-Id: I151113f793a2d87ea5060931ffeef72f511558cf

src/common/include/db-config.h
src/server/rules-loader/security-manager-rules-loader.cpp
test/privilege_db_fixture.cpp

index 4a77266..f375627 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019-2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2019-2023 Samsung Electronics Co., Ltd. All rights reserved.
  *
  * This file is licensed under the terms of MIT License or the Apache License
  * Version 2.0 of your choice. See the LICENSE.MIT file for MIT license details.
@@ -41,5 +41,6 @@
 // For database placed in "$f" the filename is ("$f" DB_RECOVERED_SUFFIX).
 #define DB_RECOVERED_SUFFIX "-recovered"
 #define DB_JOURNAL_SUFFIX "-journal"
+#define DB_FAILED_COPY_SUFFIX "-failed"
 
 #define DB_OK_MARKER "/tmp/.security-manager.db.ok"
index 8761ba7..e569ab8 100644 (file)
@@ -993,8 +993,36 @@ inl void overwriteDbFileWithFallback(size_t dbPathLen) {
     if (unlikely(SQLITE_OK != sqlite3_close(db)))
         fail("closing db failed");
 
+    // WARNING: dbPath memory has been made invalid due to tzplatform_mkpath* using a shared scratch buffer
+    // restore dbPath (now points to the copy stored in pkgsInfo)
+    pkgsInfo.t[dbPathLen] = '\0';
+    dbPath = pkgsInfo.t;
+
+    // copy the failed DB to proper path for further investigation
+    // this operation can fail for any reason - but it should not stop
+    // rules-loader from continuing the operation to fix the db
+    // hence "strange" silence in case of any error below
+    int oldFd = open(dbPath, O_RDONLY);
+    acpy(pkgsInfo.t + dbPathLen, DB_FAILED_COPY_SUFFIX);
+    int newFd = open(pkgsInfo.t, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR);
+    if (oldFd >=0 && newFd >= 0) {
+        // get the size of failed DB
+        struct stat failedSt;
+        if (!fstat(oldFd, &failedSt)) {
+            // perform actual copy
+            while (failedSt.st_size) {
+                auto r = sendfile(newFd, oldFd, nullptr, failedSt.st_size);
+                if (unlikely(r <= 0))
+                    break;
+                failedSt.st_size -= r;
+            }
+        }
+        // we can safely close the descriptors
+        close(oldFd);
+        close(newFd);
+    }
+
     // retrieve fallback database file path
-    // WARNING: dbPath memory has been made invalid (will restore later) due to tzplatform_mkpath* using a shared scratch buffer
     char const *fallbackPath = getFallbackPath();
 
     // open the fallback database regular file and get its size
@@ -1050,9 +1078,10 @@ int main(int argc, char *argv[]) {
     // dbPath : to open the database
     // dbPath + DB_RECOVERED_SUFFIX : to remove/create the "database recovery attempted" marker file
     // dbPath + DB_JOURNAL_SUFFIX : to potentially truncate the journal file when overwriting database with fallback
+    // dbPath + DB_FAILED_COPY_SUFFIX : to create DB file backup in case it was discovered to be corrupted
     //
     // the pkgsInfo tape memory is used to store these names (the tape is otherwise unused during database bringup)
-    const auto maxFilenameSize = dbPathLen + max(sizeof DB_RECOVERED_SUFFIX, sizeof DB_JOURNAL_SUFFIX);
+    const auto maxFilenameSize = dbPathLen + max(sizeof DB_RECOVERED_SUFFIX, max(sizeof DB_JOURNAL_SUFFIX, sizeof DB_FAILED_COPY_SUFFIX));
     // adjust the pkgsInfo tape size if needed to accommodate filename size
     if (unlikely(maxFilenameSize > pkgsInfo.reserved))
         pkgsInfo.reserved = align(maxFilenameSize, underlying(pageSize));
index 75be0e1..27f893b 100644 (file)
@@ -57,7 +57,7 @@ std::string genName(const std::string &prefix, int i)
     std::string caseName(boost::unit_test::framework::current_test_case().p_name);
     return prefix + std::to_string(i) + "_" + caseName;
 }
-void testMarkerFile(const char *f, bool present) {
+void testMarkerFile(const char *f, bool present, bool check_empty = true) {
     struct stat st;
     if (lstat(f, &st)) {
         if (ENOENT != errno) BOOST_FAIL("marker file (" << f << ") lstat failed");
@@ -65,13 +65,14 @@ void testMarkerFile(const char *f, bool present) {
     } else {
         if (!present) BOOST_FAIL("marker file (" << f << ") exists");
         if (!S_ISREG(st.st_mode)) BOOST_FAIL("marker file (" << f << ") not a regular file");
-        if (st.st_size) BOOST_FAIL("marker file (" << f << ") not empty");
+        if (check_empty && st.st_size) BOOST_FAIL("marker file (" << f << ") not empty");
     }
 }
 void checkMarker(PrivilegeDBFixture::Marker marker) {
     const auto broken = marker != PrivilegeDBFixture::Marker::standard;
     testMarkerFile(TEST_DB_OK_MARKER, !broken);
     testMarkerFile(TEST_DB_PATH DB_RECOVERED_SUFFIX, broken);
+    testMarkerFile(TEST_DB_PATH DB_FAILED_COPY_SUFFIX, broken, !broken);
     if (underlying(marker)) {
         struct stat st;
         BOOST_REQUIRE(!lstat(TEST_DB_PATH, &st));
@@ -85,6 +86,7 @@ constexpr const char *dbFiles[] = {
     TEST_DB_PATH,
     TEST_DB_PATH DB_JOURNAL_SUFFIX,
     TEST_DB_PATH DB_RECOVERED_SUFFIX,
+    TEST_DB_PATH DB_FAILED_COPY_SUFFIX,
     TEST_PRIVILEGE_FALLBACK_DB_PATH,
     TEST_DB_OK_MARKER,
 };
@@ -114,6 +116,7 @@ PrivilegeDBFixture::PrivilegeDBFixture(const std::string &src, const std::string
     checkMarker(PostMgrMarker::unchanged == postMgr ? preMgr : Marker::fallback);
 }
 
+
 PrivilegeDBFixture::~PrivilegeDBFixture()
 {
     delete testPrivDb;