1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <gtest/gtest.h>
17
18 #include <fstream>
19 #include <string>
20 #include <unistd.h>
21 #include <vector>
22
23 #include "common.h"
24 #include "logger.h"
25 #include "rdb_errno.h"
26 #include "rdb_helper.h"
27 #include "rdb_open_callback.h"
28
29 using namespace testing::ext;
30 using namespace OHOS::Rdb;
31 using namespace OHOS::NativeRdb;
32
33 namespace Test {
34
35 class RdbCorruptTest : public testing::Test {
36 public:
SetUpTestCase(void)37 static void SetUpTestCase(void) {}
TearDownTestCase(void)38 static void TearDownTestCase(void) {}
39 void SetUp();
40 void TearDown();
41 void GenerateData(int count);
42 static void DestroyDbFile(const std::string &filePath, size_t offset, size_t len, unsigned char ch);
43 static constexpr const char *DATABASE_NAME = "corrupt_test.db";
44 static constexpr int DB_FILE_HEADER_LENGTH = 100;
45 std::shared_ptr<RdbStore> store_;
46 };
47
48 class CorruptTestOpenCallback : public RdbOpenCallback {
49 public:
50 int OnCreate(RdbStore &store) override;
51 int OnUpgrade(RdbStore &store, int oldVersion, int newVersion) override;
52 static constexpr const char *CREATE_TABLE_TEST = "CREATE TABLE IF NOT EXISTS test "
53 "(id INTEGER PRIMARY KEY AUTOINCREMENT, "
54 "name TEXT NOT NULL, age INTEGER, salary "
55 "REAL, blobType BLOB)";
56 };
57
OnCreate(RdbStore & store)58 int CorruptTestOpenCallback::OnCreate(RdbStore &store)
59 {
60 return store.ExecuteSql(CREATE_TABLE_TEST);
61 }
62
OnUpgrade(RdbStore & store,int oldVersion,int newVersion)63 int CorruptTestOpenCallback::OnUpgrade(RdbStore &store, int oldVersion, int newVersion)
64 {
65 return E_OK;
66 }
67
SetUp(void)68 void RdbCorruptTest::SetUp(void)
69 {
70 RdbHelper::ClearCache();
71 RdbHelper::DeleteRdbStore(RDB_TEST_PATH + DATABASE_NAME);
72 RdbStoreConfig sqliteSharedRstConfig(RDB_TEST_PATH + DATABASE_NAME);
73 CorruptTestOpenCallback openCallback;
74 int errCode = E_OK;
75 store_ = RdbHelper::GetRdbStore(sqliteSharedRstConfig, 1, openCallback, errCode);
76 EXPECT_NE(store_, nullptr);
77 EXPECT_EQ(errCode, E_OK);
78 // Preset 1000 entries into database file
79 GenerateData(1000);
80 store_ = nullptr;
81 RdbHelper::ClearCache();
82 }
83
TearDown(void)84 void RdbCorruptTest::TearDown(void)
85 {
86 RdbHelper::ClearCache();
87 RdbHelper::DeleteRdbStore(RDB_TEST_PATH + DATABASE_NAME);
88 }
89
GenerateData(int count)90 void RdbCorruptTest::GenerateData(int count)
91 {
92 for (int64_t i = 0; i < count; i++) {
93 // Preset data into database
94 RowData rowData = {1, "test", 18, 100.5, std::vector<uint8_t>{ 1, 2, 3 }};
95 rowData.id += i;
96 rowData.name += std::to_string(i + 1);
97 rowData.salary += i;
98 int64_t rowId = 0;
99 auto ret = store_->Insert(rowId, "test", UTUtils::SetRowData(rowData));
100 EXPECT_EQ(E_OK, ret);
101 EXPECT_EQ(i + 1, rowId);
102 }
103 }
104
DestroyDbFile(const std::string & filePath,size_t offset,size_t len,unsigned char ch)105 void RdbCorruptTest::DestroyDbFile(const std::string &filePath, size_t offset, size_t len, unsigned char ch)
106 {
107 std::fstream f;
108 f.open(filePath.c_str());
109
110 f.seekp(offset, std::ios::beg);
111 std::vector<char> buf(len, ch);
112 f.write(buf.data(), len);
113 f.close();
114 }
115
116 /**
117 * @tc.name: RdbCorruptTest001
118 * @tc.desc: test Rdb corruption
119 * @tc.type: FUNC
120 */
121 HWTEST_F(RdbCorruptTest, RdbCorruptTest001, TestSize.Level2)
122 {
123 // Destroy database file, set 1st byte of 3rd page into undefined flag, which indicate the btree page type
124 RdbCorruptTest::DestroyDbFile(RDB_TEST_PATH + DATABASE_NAME, 8192, 1, 0xFF);
125
126 // Get RDB store failed as database corrupted
127 CorruptTestOpenCallback sqliteCallback;
128 RdbStoreConfig sqliteConfig(RDB_TEST_PATH + DATABASE_NAME);
129 int errCode = E_OK;
130 store_ = RdbHelper::GetRdbStore(sqliteConfig, 1, sqliteCallback, errCode);
131 EXPECT_NE(store_, nullptr);
132 EXPECT_EQ(errCode, E_OK);
133
134 std::shared_ptr<ResultSet> resultSet = store_->QueryByStep("SELECT * FROM test");
135 EXPECT_NE(resultSet, nullptr);
136
137 while ((errCode = resultSet->GoToNextRow()) == E_OK) {
138 }
139 EXPECT_EQ(errCode, E_SQLITE_CORRUPT);
140 }
141
142 /**
143 * @tc.name: RdbCorruptTest002
144 * @tc.desc: test Rdb verify db file header's reserved bytes
145 * @tc.type: FUNC
146 */
147 HWTEST_F(RdbCorruptTest, RdbCorruptTest002, TestSize.Level2)
148 {
149 std::fstream f;
150 f.open(RDB_TEST_PATH + DATABASE_NAME);
151 f.seekp(0, std::ios::beg);
152 char buf[DB_FILE_HEADER_LENGTH] = {0};
153 f.read(buf, sizeof(buf));
154 f.close();
155 // 20 is the offset of reserved bytes field
156 EXPECT_EQ((unsigned int)buf[20], 0);
157 }
158 } // namespace Test