1 /*
2 * Copyright (c) 2021-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 "access_token_db.h"
17
18 #include <algorithm>
19 #include <cinttypes>
20 #include <mutex>
21
22 #include "accesstoken_common_log.h"
23 #include "access_token_error.h"
24 #include "access_token_open_callback.h"
25 #include "hisysevent_adapter.h"
26 #include "rdb_helper.h"
27 #include "time_util.h"
28 #include "token_field_const.h"
29
30 namespace OHOS {
31 namespace Security {
32 namespace AccessToken {
33 namespace {
34 constexpr const char* DATABASE_NAME = "access_token.db";
35 constexpr const char* ACCESSTOKEN_SERVICE_NAME = "accesstoken_service";
36 static constexpr int32_t ACCESSTOKEN_CLEAR_MEMORY_SIZE = 4;
37 }
38
AccessTokenDb()39 AccessTokenDb::AccessTokenDb()
40 {
41 InitRdb();
42 }
43
~AccessTokenDb()44 AccessTokenDb::~AccessTokenDb()
45 {
46 db_ = nullptr;
47 LOGI(ATM_DOMAIN, ATM_TAG, "~AccessTokenDb");
48 }
49
RestoreAndInsertIfCorrupt(const int32_t resultCode,int64_t & outInsertNum,const std::string & tableName,const std::vector<NativeRdb::ValuesBucket> & buckets,const std::shared_ptr<NativeRdb::RdbStore> & db)50 int32_t AccessTokenDb::RestoreAndInsertIfCorrupt(const int32_t resultCode, int64_t& outInsertNum,
51 const std::string& tableName, const std::vector<NativeRdb::ValuesBucket>& buckets,
52 const std::shared_ptr<NativeRdb::RdbStore>& db)
53 {
54 if (resultCode != NativeRdb::E_SQLITE_CORRUPT) {
55 return resultCode;
56 }
57
58 LOGW(ATM_DOMAIN, ATM_TAG, "Detech database corrupt, restore from backup!");
59 int32_t res = db->Restore("");
60 if (res != NativeRdb::E_OK) {
61 LOGC(ATM_DOMAIN, ATM_TAG, "Db restore failed, res is %{public}d.", res);
62 return res;
63 }
64 LOGI(ATM_DOMAIN, ATM_TAG, "Database restore success, try insert again!");
65
66 res = db->BatchInsert(outInsertNum, tableName, buckets);
67 if (res != NativeRdb::E_OK) {
68 LOGC(ATM_DOMAIN, ATM_TAG, "Failed to batch insert into table %{public}s again, res is %{public}d.",
69 tableName.c_str(), res);
70 return res;
71 }
72
73 return 0;
74 }
75
InitRdb()76 void AccessTokenDb::InitRdb()
77 {
78 std::string dbPath = std::string(DATABASE_PATH) + std::string(DATABASE_NAME);
79 NativeRdb::RdbStoreConfig config(dbPath);
80 config.SetSecurityLevel(NativeRdb::SecurityLevel::S3);
81 config.SetAllowRebuild(true);
82 config.SetHaMode(NativeRdb::HAMode::MAIN_REPLICA); // Real-time dual-write backup database
83 config.SetServiceName(std::string(ACCESSTOKEN_SERVICE_NAME));
84 config.SetClearMemorySize(ACCESSTOKEN_CLEAR_MEMORY_SIZE);
85 AccessTokenOpenCallback callback;
86 int32_t res = NativeRdb::E_OK;
87 // pragma user_version will done by rdb, they store path and db_ as pair in RdbStoreManager
88 db_ = NativeRdb::RdbHelper::GetRdbStore(config, DATABASE_VERSION_6, callback, res);
89 if ((res != NativeRdb::E_OK) || (db_ == nullptr)) {
90 LOGE(ATM_DOMAIN, ATM_TAG, "Failed to init rdb, res is %{public}d.", res);
91 }
92 }
93
GetRdb()94 std::shared_ptr<NativeRdb::RdbStore> AccessTokenDb::GetRdb()
95 {
96 std::lock_guard<std::mutex> lock(dbLock_);
97 if (db_ == nullptr) {
98 InitRdb();
99 }
100 return db_;
101 }
102
AddValues(const AtmDataType type,const std::vector<GenericValues> & addValues)103 int32_t AccessTokenDb::AddValues(const AtmDataType type, const std::vector<GenericValues>& addValues)
104 {
105 std::string tableName;
106 AccessTokenDbUtil::GetTableNameByType(type, tableName);
107 if (tableName.empty()) {
108 LOGE(ATM_DOMAIN, ATM_TAG, "Table name is empty.");
109 return AccessTokenError::ERR_PARAM_INVALID;
110 }
111
112 // if nothing to insert, no need to call BatchInsert
113 if (addValues.empty()) {
114 return 0;
115 }
116
117 std::shared_ptr<NativeRdb::RdbStore> db = GetRdb();
118 if (db == nullptr) {
119 LOGC(ATM_DOMAIN, ATM_TAG, "Db is nullptr.");
120 return AccessTokenError::ERR_DATABASE_OPERATE_FAILED;
121 }
122
123 // fill buckets with addValues
124 int64_t outInsertNum = 0;
125 std::vector<NativeRdb::ValuesBucket> buckets;
126 AccessTokenDbUtil::ToRdbValueBuckets(addValues, buckets);
127
128 int32_t res = db->BatchInsert(outInsertNum, tableName, buckets);
129 if (res != NativeRdb::E_OK) {
130 LOGE(ATM_DOMAIN, ATM_TAG, "Failed to batch insert into table %{public}s, res is %{public}d.",
131 tableName.c_str(), res);
132 ReportSysEventDbException(AccessTokenDbSceneCode::AT_DB_INSERT_RESTORE, res, tableName);
133 int32_t result = RestoreAndInsertIfCorrupt(res, outInsertNum, tableName, buckets, db);
134 if (result != NativeRdb::E_OK) {
135 return result;
136 }
137 }
138 if (outInsertNum <= 0) { // rdb bug, adapt it
139 LOGC(ATM_DOMAIN, ATM_TAG, "Insert count %{public}" PRId64 " abnormal.", outInsertNum);
140 return AccessTokenError::ERR_DATABASE_OPERATE_FAILED;
141 }
142
143 LOGI(ATM_DOMAIN, ATM_TAG, "Batch insert %{public}" PRId64 " records to table %{public}s.", outInsertNum,
144 tableName.c_str());
145
146 return 0;
147 }
148
RestoreAndDeleteIfCorrupt(const int32_t resultCode,int32_t & deletedRows,const NativeRdb::RdbPredicates & predicates,const std::shared_ptr<NativeRdb::RdbStore> & db)149 int32_t AccessTokenDb::RestoreAndDeleteIfCorrupt(const int32_t resultCode, int32_t& deletedRows,
150 const NativeRdb::RdbPredicates& predicates, const std::shared_ptr<NativeRdb::RdbStore>& db)
151 {
152 if (resultCode != NativeRdb::E_SQLITE_CORRUPT) {
153 return resultCode;
154 }
155
156 LOGW(ATM_DOMAIN, ATM_TAG, "Detech database corrupt, restore from backup!");
157 int32_t res = db->Restore("");
158 if (res != NativeRdb::E_OK) {
159 LOGC(ATM_DOMAIN, ATM_TAG, "Db restore failed, res is %{public}d.", res);
160 return res;
161 }
162 LOGI(ATM_DOMAIN, ATM_TAG, "Database restore success, try delete again!");
163
164 res = db->Delete(deletedRows, predicates);
165 if (res != NativeRdb::E_OK) {
166 LOGC(ATM_DOMAIN, ATM_TAG, "Failed to delete record from table %{public}s again, res is %{public}d.",
167 predicates.GetTableName().c_str(), res);
168 return res;
169 }
170
171 return 0;
172 }
173
RemoveValues(const AtmDataType type,const GenericValues & conditionValue)174 int32_t AccessTokenDb::RemoveValues(const AtmDataType type, const GenericValues& conditionValue)
175 {
176 std::string tableName;
177 AccessTokenDbUtil::GetTableNameByType(type, tableName);
178 if (tableName.empty()) {
179 LOGE(ATM_DOMAIN, ATM_TAG, "Table name is empty.");
180 return AccessTokenError::ERR_PARAM_INVALID;
181 }
182
183 std::shared_ptr<NativeRdb::RdbStore> db = GetRdb();
184 if (db == nullptr) {
185 LOGC(ATM_DOMAIN, ATM_TAG, "Db is nullptr.");
186 return AccessTokenError::ERR_DATABASE_OPERATE_FAILED;
187 }
188
189 int32_t deletedRows = 0;
190 NativeRdb::RdbPredicates predicates(tableName);
191 AccessTokenDbUtil::ToRdbPredicates(conditionValue, predicates);
192
193 int32_t res = db->Delete(deletedRows, predicates);
194 if (res != NativeRdb::E_OK) {
195 LOGE(ATM_DOMAIN, ATM_TAG, "Failed to delete record from table %{public}s, res is %{public}d.",
196 tableName.c_str(), res);
197 ReportSysEventDbException(AccessTokenDbSceneCode::AT_DB_DELETE_RESTORE, res, tableName);
198 int32_t result = RestoreAndDeleteIfCorrupt(res, deletedRows, predicates, db);
199 if (result != NativeRdb::E_OK) {
200 return result;
201 }
202 }
203
204 LOGI(ATM_DOMAIN, ATM_TAG, "Delete %{public}d records from table %{public}s.", deletedRows, tableName.c_str());
205
206 return 0;
207 }
208
RestoreAndUpdateIfCorrupt(const int32_t resultCode,int32_t & changedRows,const NativeRdb::ValuesBucket & bucket,const NativeRdb::RdbPredicates & predicates,const std::shared_ptr<NativeRdb::RdbStore> & db)209 int32_t AccessTokenDb::RestoreAndUpdateIfCorrupt(const int32_t resultCode, int32_t& changedRows,
210 const NativeRdb::ValuesBucket& bucket, const NativeRdb::RdbPredicates& predicates,
211 const std::shared_ptr<NativeRdb::RdbStore>& db)
212 {
213 if (resultCode != NativeRdb::E_SQLITE_CORRUPT) {
214 return resultCode;
215 }
216
217 LOGW(ATM_DOMAIN, ATM_TAG, "Detech database corrupt, restore from backup!");
218 int32_t res = db->Restore("");
219 if (res != NativeRdb::E_OK) {
220 LOGC(ATM_DOMAIN, ATM_TAG, "Db restore failed, res is %{public}d.", res);
221 return res;
222 }
223 LOGI(ATM_DOMAIN, ATM_TAG, "Database restore success, try update again!");
224
225 res = db->Update(changedRows, bucket, predicates);
226 if (res != NativeRdb::E_OK) {
227 LOGC(ATM_DOMAIN, ATM_TAG, "Failed to update record from table %{public}s again, res is %{public}d.",
228 predicates.GetTableName().c_str(), res);
229 return res;
230 }
231
232 return 0;
233 }
234
Modify(const AtmDataType type,const GenericValues & modifyValue,const GenericValues & conditionValue)235 int32_t AccessTokenDb::Modify(const AtmDataType type, const GenericValues& modifyValue,
236 const GenericValues& conditionValue)
237 {
238 int64_t beginTime = TimeUtil::GetCurrentTimestamp();
239 std::string tableName;
240 AccessTokenDbUtil::GetTableNameByType(type, tableName);
241 if (tableName.empty()) {
242 LOGC(ATM_DOMAIN, ATM_TAG, "Get table name failed, type=%{public}d!", static_cast<int32_t>(type));
243 return AccessTokenError::ERR_PARAM_INVALID;
244 }
245
246 NativeRdb::ValuesBucket bucket;
247
248 AccessTokenDbUtil::ToRdbValueBucket(modifyValue, bucket);
249 if (bucket.IsEmpty()) {
250 LOGC(ATM_DOMAIN, ATM_TAG, "To rdb value bucket failed!");
251 return AccessTokenError::ERR_PARAM_INVALID;
252 }
253
254 NativeRdb::RdbPredicates predicates(tableName);
255 AccessTokenDbUtil::ToRdbPredicates(conditionValue, predicates);
256
257 int32_t changedRows = 0;
258 {
259 OHOS::Utils::UniqueWriteGuard<OHOS::Utils::RWLock> lock(this->rwLock_);
260 auto db = GetRdb();
261 if (db == nullptr) {
262 LOGC(ATM_DOMAIN, ATM_TAG, "Db is nullptr.");
263 return AccessTokenError::ERR_DATABASE_OPERATE_FAILED;
264 }
265
266 int32_t res = db->Update(changedRows, bucket, predicates);
267 if (res != NativeRdb::E_OK) {
268 LOGE(ATM_DOMAIN, ATM_TAG, "Failed to update record from table %{public}s, res is %{public}d.",
269 tableName.c_str(), res);
270 ReportSysEventDbException(AccessTokenDbSceneCode::AT_DB_UPDATE_RESTORE, res, tableName);
271 int32_t result = RestoreAndUpdateIfCorrupt(res, changedRows, bucket, predicates, db);
272 if (result != NativeRdb::E_OK) {
273 LOGC(ATM_DOMAIN, ATM_TAG, "Failed to restore and update, result is %{public}d.", result);
274 return result;
275 }
276 }
277 }
278
279 int64_t endTime = TimeUtil::GetCurrentTimestamp();
280 LOGI(ATM_DOMAIN, ATM_TAG, "Modify cost %{public}" PRId64
281 ", update %{public}d records from table %{public}s.", endTime - beginTime, changedRows, tableName.c_str());
282
283 return 0;
284 }
285
RestoreAndQueryIfCorrupt(const NativeRdb::RdbPredicates & predicates,const std::vector<std::string> & columns,std::shared_ptr<NativeRdb::AbsSharedResultSet> & queryResultSet,const std::shared_ptr<NativeRdb::RdbStore> & db)286 int32_t AccessTokenDb::RestoreAndQueryIfCorrupt(const NativeRdb::RdbPredicates& predicates,
287 const std::vector<std::string>& columns, std::shared_ptr<NativeRdb::AbsSharedResultSet>& queryResultSet,
288 const std::shared_ptr<NativeRdb::RdbStore>& db)
289 {
290 int32_t count = 0;
291 int32_t res = queryResultSet->GetRowCount(count);
292 if (res != NativeRdb::E_OK) {
293 if (res == NativeRdb::E_SQLITE_CORRUPT) {
294 queryResultSet->Close();
295 queryResultSet = nullptr;
296
297 LOGW(ATM_DOMAIN, ATM_TAG, "Detech database corrupt, restore from backup!");
298 ReportSysEventDbException(AccessTokenDbSceneCode::AT_DB_QUERY_RESTORE, res, predicates.GetTableName());
299 res = db->Restore("");
300 if (res != NativeRdb::E_OK) {
301 LOGC(ATM_DOMAIN, ATM_TAG, "Db restore failed, res is %{public}d.", res);
302 return res;
303 }
304 LOGI(ATM_DOMAIN, ATM_TAG, "Database restore success, try query again!");
305
306 queryResultSet = db->Query(predicates, columns);
307 if (queryResultSet == nullptr) {
308 LOGC(ATM_DOMAIN, ATM_TAG, "Failed to find records from table %{public}s again.",
309 predicates.GetTableName().c_str());
310 return AccessTokenError::ERR_DATABASE_OPERATE_FAILED;
311 }
312 } else {
313 LOGC(ATM_DOMAIN, ATM_TAG, "Failed to get result count.");
314 return AccessTokenError::ERR_DATABASE_OPERATE_FAILED;
315 }
316 }
317
318 return 0;
319 }
320
Find(AtmDataType type,const GenericValues & conditionValue,std::vector<GenericValues> & results)321 int32_t AccessTokenDb::Find(AtmDataType type, const GenericValues& conditionValue,
322 std::vector<GenericValues>& results)
323 {
324 int64_t beginTime = TimeUtil::GetCurrentTimestamp();
325 std::string tableName;
326 AccessTokenDbUtil::GetTableNameByType(type, tableName);
327 if (tableName.empty()) {
328 return AccessTokenError::ERR_PARAM_INVALID;
329 }
330
331 NativeRdb::RdbPredicates predicates(tableName);
332 AccessTokenDbUtil::ToRdbPredicates(conditionValue, predicates);
333
334 std::vector<std::string> columns; // empty columns means query all columns
335 int count = 0;
336 {
337 OHOS::Utils::UniqueReadGuard<OHOS::Utils::RWLock> lock(this->rwLock_);
338 auto db = GetRdb();
339 if (db == nullptr) {
340 LOGC(ATM_DOMAIN, ATM_TAG, "Db is nullptr.");
341 return AccessTokenError::ERR_DATABASE_OPERATE_FAILED;
342 }
343
344 auto queryResultSet = db->Query(predicates, columns);
345 if (queryResultSet == nullptr) {
346 LOGC(ATM_DOMAIN, ATM_TAG, "Failed to find records from table %{public}s.",
347 tableName.c_str());
348 return AccessTokenError::ERR_DATABASE_OPERATE_FAILED;
349 }
350
351 int32_t res = RestoreAndQueryIfCorrupt(predicates, columns, queryResultSet, db);
352 if (res != 0) {
353 LOGC(ATM_DOMAIN, ATM_TAG, "Restore and query failed!");
354 return res;
355 }
356
357 while (queryResultSet->GoToNextRow() == NativeRdb::E_OK) {
358 GenericValues value;
359 AccessTokenDbUtil::ResultToGenericValues(queryResultSet, value);
360 if (value.GetAllKeys().empty()) {
361 continue;
362 }
363
364 results.emplace_back(value);
365 count++;
366 }
367 }
368
369 int64_t endTime = TimeUtil::GetCurrentTimestamp();
370 LOGI(ATM_DOMAIN, ATM_TAG, "Find cost %{public}" PRId64
371 ", query %{public}d records from table %{public}s.", endTime - beginTime, count, tableName.c_str());
372
373 return 0;
374 }
375
RestoreAndCommitIfCorrupt(const int32_t resultCode,const std::shared_ptr<NativeRdb::RdbStore> & db)376 int32_t AccessTokenDb::RestoreAndCommitIfCorrupt(const int32_t resultCode,
377 const std::shared_ptr<NativeRdb::RdbStore>& db)
378 {
379 if (resultCode != NativeRdb::E_SQLITE_CORRUPT) {
380 return resultCode;
381 }
382
383 LOGW(ATM_DOMAIN, ATM_TAG, "Detech database corrupt, restore from backup!");
384 int32_t res = db->Restore("");
385 if (res != NativeRdb::E_OK) {
386 LOGC(ATM_DOMAIN, ATM_TAG, "Db restore failed, res is %{public}d.", res);
387 return res;
388 }
389 LOGI(ATM_DOMAIN, ATM_TAG, "Database restore success, try commit again!");
390
391 res = db->Commit();
392 if (res != NativeRdb::E_OK) {
393 LOGC(ATM_DOMAIN, ATM_TAG, "Failed to Commit again, res is %{public}d.", res);
394 return res;
395 }
396
397 return NativeRdb::E_OK;
398 }
399
DeleteAndInsertValues(const std::vector<DelInfo> & delInfoVec,const std::vector<AddInfo> & addInfoVec)400 int32_t AccessTokenDb::DeleteAndInsertValues(const std::vector<DelInfo>& delInfoVec,
401 const std::vector<AddInfo>& addInfoVec)
402 {
403 int64_t beginTime = TimeUtil::GetCurrentTimestamp();
404
405 {
406 OHOS::Utils::UniqueWriteGuard<OHOS::Utils::RWLock> lock(this->rwLock_);
407 std::shared_ptr<NativeRdb::RdbStore> db = GetRdb();
408 if (db == nullptr) {
409 LOGC(ATM_DOMAIN, ATM_TAG, "Db is nullptr.");
410 return AccessTokenError::ERR_DATABASE_OPERATE_FAILED;
411 }
412
413 db->BeginTransaction();
414
415 int32_t res = 0;
416 size_t count = delInfoVec.size();
417 for (size_t i = 0; i < count; ++i) {
418 res = RemoveValues(delInfoVec[i].delType, delInfoVec[i].delValue);
419 if (res != 0) {
420 db->RollBack();
421 LOGC(ATM_DOMAIN, ATM_TAG, "Remove values failed, res is %{public}d.", res);
422 return res;
423 }
424 }
425
426 count = addInfoVec.size();
427 for (size_t i = 0; i < count; ++i) {
428 res = AddValues(addInfoVec[i].addType, addInfoVec[i].addValues);
429 if (res != 0) {
430 db->RollBack();
431 LOGC(ATM_DOMAIN, ATM_TAG, "Add values failed, res is %{public}d.", res);
432 return res;
433 }
434 }
435
436 res = db->Commit();
437 if (res != NativeRdb::E_OK) {
438 LOGE(ATM_DOMAIN, ATM_TAG, "Failed to commit, res is %{public}d.", res);
439 ReportSysEventDbException(AccessTokenDbSceneCode::AT_DB_COMMIT_RESTORE, res, "");
440 int32_t result = RestoreAndCommitIfCorrupt(res, db);
441 if (result != NativeRdb::E_OK) {
442 LOGC(ATM_DOMAIN, ATM_TAG, "Failed to restore and commit, result is %{public}d.", result);
443 return result;
444 }
445 }
446 }
447
448 int64_t endTime = TimeUtil::GetCurrentTimestamp();
449 LOGI(ATM_DOMAIN, ATM_TAG, "DeleteAndInsertValues cost %{public}" PRId64 ".", endTime - beginTime);
450
451 return 0;
452 }
453 } // namespace AccessToken
454 } // namespace Security
455 } // namespace OHOS
456