• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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