1 /*
2 * Copyright (c) 2021 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 "kvdb_manager.h"
17 #include "log_print.h"
18 #include "db_common.h"
19 #include "runtime_context.h"
20 #include "schema_object.h"
21 #include "default_factory.h"
22 #include "generic_kvdb.h"
23 #include "db_constant.h"
24 #include "res_finalizer.h"
25
26 namespace DistributedDB {
27 const std::string KvDBManager::PROCESS_LABEL_CONNECTOR = "-";
28 std::atomic<KvDBManager *> KvDBManager::instance_{nullptr};
29 std::mutex KvDBManager::kvDBLock_;
30 std::mutex KvDBManager::instanceLock_;
31 std::map<std::string, OS::FileHandle *> KvDBManager::locks_;
32 std::mutex KvDBManager::fileHandleMutex_;
33
34 namespace {
35 DefaultFactory g_defaultFactory;
36
37 static const KvDBType g_dbTypeArr[] = {
38 #ifndef OMIT_MULTI_VER
39 LOCAL_KVDB,
40 #endif // OMIT_MULTI_VER
41 SINGER_VER_KVDB,
42 #ifndef OMIT_MULTI_VER
43 MULTI_VER_KVDB
44 #endif // OMIT_MULTI_VER
45 };
46
CreateDataBaseInstance(const KvDBProperties & property,IKvDB * & kvDB)47 int CreateDataBaseInstance(const KvDBProperties &property, IKvDB *&kvDB)
48 {
49 IKvDBFactory *factory = IKvDBFactory::GetCurrent();
50 if (factory == nullptr) {
51 return -E_OUT_OF_MEMORY;
52 }
53 int errCode = E_OK;
54 int databaseType = property.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE);
55 if (databaseType == KvDBProperties::LOCAL_TYPE) {
56 #ifndef OMIT_MULTI_VER
57 kvDB = factory->CreateKvDb(LOCAL_KVDB, errCode);
58 if (kvDB != nullptr) {
59 kvDB->EnableAutonomicUpgrade();
60 }
61 #else
62 return -E_NOT_SUPPORT;
63 #endif // OMIT_MULTI_VER
64 } else if (databaseType == KvDBProperties::SINGLE_VER_TYPE) {
65 kvDB = factory->CreateKvDb(SINGER_VER_KVDB, errCode);
66 } else {
67 #ifndef OMIT_MULTI_VER
68 kvDB = factory->CreateKvDb(MULTI_VER_KVDB, errCode);
69 #else
70 return -E_NOT_SUPPORT;
71 #endif // OMIT_MULTI_VER
72 }
73 return errCode;
74 }
75
CreateRemoveStateFlagFile(const KvDBProperties & properties)76 int CreateRemoveStateFlagFile(const KvDBProperties &properties)
77 {
78 std::string dataDir = properties.GetStringProp(KvDBProperties::DATA_DIR, "");
79 std::string identifier = properties.GetStringProp(KvDBProperties::IDENTIFIER_DATA, "");
80 std::string identifierName = DBCommon::TransferStringToHex(identifier);
81 std::string storeDir = dataDir + "/" + identifierName + DBConstant::DELETE_KVSTORE_REMOVING;
82 if (OS::CheckPathExistence(storeDir)) {
83 return E_OK;
84 }
85 // create the pre flag file.
86 int errCode = OS::CreateFileByFileName(storeDir);
87 if (errCode != E_OK) {
88 LOGE("Create remove state flag file failed:%d.", errCode);
89 }
90 return errCode;
91 }
92 }
93
CheckRemoveStateAndRetry(const KvDBProperties & property)94 int KvDBManager::CheckRemoveStateAndRetry(const KvDBProperties &property)
95 {
96 std::string dataDir = property.GetStringProp(KvDBProperties::DATA_DIR, "");
97 std::string identifier = property.GetStringProp(KvDBProperties::IDENTIFIER_DATA, "");
98 std::string identifierName = DBCommon::TransferStringToHex(identifier);
99 std::string storeDir = dataDir + "/" + identifierName + DBConstant::DELETE_KVSTORE_REMOVING;
100
101 if (OS::CheckPathExistence(storeDir)) {
102 KvDBManager::ExecuteRemoveDatabase(property);
103 }
104 // Re-detection deleted had been finish
105 if (OS::CheckPathExistence(storeDir)) {
106 LOGD("Deletekvstore unfinished, can not create new same identifier kvstore!");
107 return -E_REMOVE_FILE;
108 }
109 return E_OK;
110 }
111
ExecuteRemoveDatabase(const KvDBProperties & properties)112 int KvDBManager::ExecuteRemoveDatabase(const KvDBProperties &properties)
113 {
114 int errCode = CheckDatabaseFileStatus(properties);
115 if (errCode != E_OK) {
116 return errCode;
117 }
118 IKvDBFactory *factory = IKvDBFactory::GetCurrent();
119 if (factory == nullptr) {
120 return -E_INVALID_DB;
121 }
122
123 errCode = CreateRemoveStateFlagFile(properties);
124 if (errCode != E_OK) {
125 LOGE("create ctrl file failed:%d.", errCode);
126 return errCode;
127 }
128
129 errCode = -E_NOT_FOUND;
130 for (KvDBType kvDbType : g_dbTypeArr) {
131 int innerErrCode = E_OK;
132 IKvDB *kvdb = factory->CreateKvDb(kvDbType, innerErrCode);
133 if (innerErrCode != E_OK) {
134 return innerErrCode;
135 }
136 innerErrCode = kvdb->RemoveKvDB(properties);
137 RefObject::DecObjRef(kvdb);
138 if (innerErrCode != -E_NOT_FOUND) {
139 if (innerErrCode != E_OK) {
140 return innerErrCode;
141 }
142 errCode = E_OK;
143 }
144 }
145
146 if (errCode == -E_NOT_FOUND) {
147 LOGE("DataBase file Not exist! return NOT_FOUND.");
148 }
149
150 RemoveDBDirectory(properties);
151 return errCode;
152 }
153
RemoveDBDirectory(const KvDBProperties & properties)154 void KvDBManager::RemoveDBDirectory(const KvDBProperties &properties)
155 {
156 std::string dataDir = properties.GetStringProp(KvDBProperties::DATA_DIR, "");
157 std::string identifier = properties.GetStringProp(KvDBProperties::IDENTIFIER_DATA, "");
158 std::string identifierName = DBCommon::TransferStringToHex(identifier);
159 std::string storeDir = dataDir + "/" + identifierName;
160 std::string removingFlag = dataDir + "/" + identifierName + DBConstant::DELETE_KVSTORE_REMOVING;
161 (void)OS::RemoveDBDirectory(storeDir);
162
163 std::string storeId = properties.GetStringProp(KvDBProperties::STORE_ID, "");
164 identifier = DBCommon::TransferHashString(storeId);
165 identifierName = DBCommon::TransferStringToHex(identifier);
166 storeDir = dataDir + "/" + identifierName;
167 (void)OS::RemoveDBDirectory(storeDir);
168
169 (void)OS::RemoveFile(removingFlag);
170 }
171
172 // Used to open a kvdb with the given property
OpenDatabase(const KvDBProperties & property,int & errCode)173 IKvDB *KvDBManager::OpenDatabase(const KvDBProperties &property, int &errCode)
174 {
175 KvDBManager *manager = GetInstance();
176 if (manager == nullptr) {
177 errCode = -E_OUT_OF_MEMORY;
178 return nullptr;
179 }
180 return manager->GetDataBase(property, errCode, true);
181 }
182
EnterDBOpenCloseProcess(const std::string & identifier)183 void KvDBManager::EnterDBOpenCloseProcess(const std::string &identifier)
184 {
185 std::unique_lock<std::mutex> lock(kvDBOpenMutex_);
186 kvDBOpenCondition_.wait(lock, [this, &identifier]() {
187 return this->kvDBOpenSet_.count(identifier) == 0;
188 });
189 (void)kvDBOpenSet_.insert(identifier);
190 }
191
ExitDBOpenCloseProcess(const std::string & identifier)192 void KvDBManager::ExitDBOpenCloseProcess(const std::string &identifier)
193 {
194 std::unique_lock<std::mutex> lock(kvDBOpenMutex_);
195 (void)kvDBOpenSet_.erase(identifier);
196 kvDBOpenCondition_.notify_all();
197 }
198
199 // one time 100ms
200 // In order to prevent long-term blocking of the process, a retry method is used
201 // The dimensions of the lock by appid-userid-storeid
TryLockDB(const KvDBProperties & kvDBProp,int retryTimes)202 int KvDBManager::TryLockDB(const KvDBProperties &kvDBProp, int retryTimes)
203 {
204 std::string dataDir = kvDBProp.GetStringProp(KvDBProperties::DATA_DIR, "");
205 bool isMemoryDb = kvDBProp.GetBoolProp(KvDBProperties::MEMORY_MODE, false);
206 std::string id = KvDBManager::GenerateKvDBIdentifier(kvDBProp);
207 if (dataDir.back() != '/') {
208 dataDir += "/";
209 }
210
211 if (isMemoryDb) {
212 LOGI("MemoryDb not need lock!");
213 return E_OK;
214 }
215
216 {
217 std::lock_guard<std::mutex> autoLock(fileHandleMutex_);
218 if (locks_.count(id) != 0) {
219 LOGI("db has been locked!");
220 return E_OK;
221 }
222 }
223
224 std::string hexHashId = DBCommon::TransferStringToHex((id));
225 OS::FileHandle *handle = nullptr;
226 int errCode = OS::OpenFile(dataDir + hexHashId + DBConstant::DB_LOCK_POSTFIX, handle);
227 if (errCode != E_OK) {
228 LOGE("Open lock file fail errCode = [%d], errno:%d", errCode, errno);
229 return errCode;
230 }
231
232 while (retryTimes-- > 0) {
233 errCode = OS::FileLock(handle, false); // not block process
234 if (errCode == E_OK) {
235 LOGI("[%s]locked!", STR_MASK(DBCommon::TransferStringToHex(KvDBManager::GenerateKvDBIdentifier(kvDBProp))));
236 std::lock_guard<std::mutex> autoLock(fileHandleMutex_);
237 locks_[id] = handle;
238 return errCode;
239 } else if (errCode == -E_BUSY) {
240 LOGD("DB already held by process lock!");
241 std::this_thread::sleep_for(std::chrono::milliseconds(100)); // wait for 100ms
242 continue;
243 } else {
244 LOGE("Try lock db failed, errCode = [%d] errno:%d", errCode, errno);
245 OS::CloseFile(handle);
246 return errCode;
247 }
248 }
249 OS::CloseFile(handle);
250 return -E_BUSY;
251 }
252
UnlockDB(const KvDBProperties & kvDBProp)253 int KvDBManager::UnlockDB(const KvDBProperties &kvDBProp)
254 {
255 bool isMemoryDb = kvDBProp.GetBoolProp(KvDBProperties::MEMORY_MODE, false);
256 if (isMemoryDb) {
257 return E_OK;
258 }
259 std::string identifierDir = KvDBManager::GenerateKvDBIdentifier(kvDBProp);
260 OS::FileHandle *handle = nullptr;
261 {
262 std::lock_guard<std::mutex> autoLock(fileHandleMutex_);
263 if (locks_.count(identifierDir) == 0) {
264 return E_OK;
265 }
266 handle = locks_[identifierDir];
267 }
268 int errCode = OS::FileUnlock(handle);
269 if (errCode != E_OK) {
270 LOGE("DB unlocked! errCode = [%d]", errCode);
271 return errCode;
272 }
273 errCode = OS::CloseFile(handle);
274 if (errCode != E_OK) {
275 LOGE("DB closed! errCode = [%d]", errCode);
276 return errCode;
277 }
278 std::lock_guard<std::mutex> autoLock(fileHandleMutex_);
279 locks_.erase(identifierDir);
280 return E_OK;
281 }
282
CheckOpenDBOptionWithCached(const KvDBProperties & properties,IKvDB * kvDB)283 bool KvDBManager::CheckOpenDBOptionWithCached(const KvDBProperties &properties, IKvDB *kvDB)
284 {
285 bool isMemoryDb = properties.GetBoolProp(KvDBProperties::MEMORY_MODE, false);
286 std::string canonicalDir = properties.GetStringProp(KvDBProperties::DATA_DIR, "");
287 if (!isMemoryDb && (canonicalDir.empty() || canonicalDir != kvDB->GetStorePath())) {
288 LOGE("Failed to check store path, the input path does not match with cached store.");
289 return false;
290 }
291
292 bool compressOnSyncUser = properties.GetBoolProp(KvDBProperties::COMPRESS_ON_SYNC, false);
293 bool compressOnSyncGet = kvDB->GetMyProperties().GetBoolProp(KvDBProperties::COMPRESS_ON_SYNC, false);
294 if (compressOnSyncUser != compressOnSyncGet) {
295 LOGE("Failed to check compress option, the input %d not match with cached %d.", compressOnSyncUser,
296 compressOnSyncGet);
297 return false;
298 }
299 if (compressOnSyncUser) {
300 int compressRateUser = properties.GetIntProp(KvDBProperties::COMPRESSION_RATE, 0);
301 int compressRateGet = kvDB->GetMyProperties().GetIntProp(KvDBProperties::COMPRESSION_RATE, 0);
302 if (compressRateUser != compressRateGet) {
303 LOGE("Failed to check compress rate, the input %d not match with cached %d.", compressRateUser,
304 compressRateGet);
305 return false;
306 }
307 }
308 return true;
309 }
310
311 // Used to open a kvdb with the given property
GetDatabaseConnection(const KvDBProperties & properties,int & errCode,bool isNeedIfOpened)312 IKvDBConnection *KvDBManager::GetDatabaseConnection(const KvDBProperties &properties, int &errCode,
313 bool isNeedIfOpened)
314 {
315 auto manager = GetInstance();
316 if (manager == nullptr) {
317 errCode = -E_OUT_OF_MEMORY;
318 return nullptr;
319 }
320 IKvDBConnection *connection = nullptr;
321 std::string identifier = properties.GetStringProp(KvDBProperties::IDENTIFIER_DATA, "");
322 LOGD("Begin to get [%s] database connection.", STR_MASK(DBCommon::TransferStringToHex(identifier)));
323 manager->EnterDBOpenCloseProcess(identifier);
324
325 IKvDB *kvDB = manager->GetDataBase(properties, errCode, isNeedIfOpened);
326 if (kvDB == nullptr) {
327 if (isNeedIfOpened) {
328 LOGE("Failed to open the db:%d", errCode);
329 }
330 } else {
331 if (!CheckOpenDBOptionWithCached(properties, kvDB)) {
332 LOGE("Failed to check open db option");
333 errCode = -E_INVALID_ARGS;
334 } else {
335 connection = kvDB->GetDBConnection(errCode);
336 if (connection == nullptr) { // not kill kvdb, Other operations like import may be used concurrently
337 LOGE("Failed to get the db connect for delegate:%d", errCode);
338 }
339 }
340 RefObject::DecObjRef(kvDB); // restore the reference increased by the cache.
341 kvDB = nullptr;
342 }
343
344 manager->ExitDBOpenCloseProcess(identifier);
345 if (errCode == -E_INVALID_PASSWD_OR_CORRUPTED_DB) {
346 std::string appId = properties.GetStringProp(KvDBProperties::APP_ID, "");
347 std::string userId = properties.GetStringProp(KvDBProperties::USER_ID, "");
348 std::string storeId = properties.GetStringProp(KvDBProperties::STORE_ID, "");
349 manager->DataBaseCorruptNotify(appId, userId, storeId);
350 LOGE("Database [%s] is corrupted or invalid passwd:%d", STR_MASK(DBCommon::TransferStringToHex(identifier)),
351 errCode);
352 }
353
354 return connection;
355 }
356
ReleaseDatabaseConnection(IKvDBConnection * connection)357 int KvDBManager::ReleaseDatabaseConnection(IKvDBConnection *connection)
358 {
359 if (connection == nullptr) {
360 return -E_INVALID_DB;
361 }
362
363 std::string identifier = connection->GetIdentifier();
364 auto manager = GetInstance();
365 if (manager == nullptr) {
366 return -E_OUT_OF_MEMORY;
367 }
368 manager->EnterDBOpenCloseProcess(identifier);
369 int errCode = connection->Close();
370 manager->ExitDBOpenCloseProcess(identifier);
371
372 if (errCode != E_OK) {
373 LOGE("[KvDBManager] Release db connection:%d", errCode);
374 }
375 LOGI("[Connection] db[%s] conn Close", STR_MASK(DBCommon::TransferStringToHex(identifier)));
376 return errCode;
377 }
378
CreateDataBase(const KvDBProperties & property,int & errCode)379 IKvDB *KvDBManager::CreateDataBase(const KvDBProperties &property, int &errCode)
380 {
381 IKvDB *kvDB = OpenNewDatabase(property, errCode);
382 if (kvDB == nullptr) {
383 LOGE("Failed to open the new database.");
384 if (errCode == -E_INVALID_PASSWD_OR_CORRUPTED_DB &&
385 property.GetBoolProp(KvDBProperties::RM_CORRUPTED_DB, false)) {
386 LOGI("Remove the corrupted database while open");
387 ExecuteRemoveDatabase(property);
388 kvDB = OpenNewDatabase(property, errCode);
389 }
390 return kvDB;
391 }
392
393 if (property.GetBoolProp(KvDBProperties::CHECK_INTEGRITY, false)) {
394 int integrityStatus = kvDB->CheckIntegrity();
395 if (integrityStatus == -E_INVALID_PASSWD_OR_CORRUPTED_DB) {
396 RemoveKvDBFromCache(kvDB);
397 RefObject::KillAndDecObjRef(kvDB);
398 kvDB = nullptr;
399 errCode = -E_INVALID_PASSWD_OR_CORRUPTED_DB;
400 if (property.GetBoolProp(KvDBProperties::RM_CORRUPTED_DB, false)) {
401 LOGI("Remove the corrupted database for the integrity check");
402 ExecuteRemoveDatabase(property);
403 kvDB = OpenNewDatabase(property, errCode);
404 }
405 }
406 }
407 return kvDB;
408 }
409
GetDataBase(const KvDBProperties & property,int & errCode,bool isNeedIfOpened)410 IKvDB *KvDBManager::GetDataBase(const KvDBProperties &property, int &errCode, bool isNeedIfOpened)
411 {
412 bool isMemoryDb = property.GetBoolProp(KvDBProperties::MEMORY_MODE, false);
413 bool isCreateNecessary = property.GetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, true);
414 IKvDB *kvDB = FindAndGetKvDBFromCache(property, errCode);
415 if (kvDB != nullptr) {
416 if (!isNeedIfOpened) {
417 LOGI("[KvDBManager] Database has already been opened.");
418 RefObject::DecObjRef(kvDB);
419 errCode = -E_ALREADY_OPENED;
420 kvDB = nullptr;
421 }
422 return kvDB;
423 }
424 if (isMemoryDb && !isCreateNecessary) {
425 LOGI("IsCreateNecessary is false, Not need create database");
426 return nullptr;
427 }
428 if (errCode != -E_NOT_FOUND) {
429 return nullptr;
430 }
431
432 // Taking into account the compatibility of version delivery,
433 // temporarily use isNeedIntegrityCheck this field to avoid multi-process concurrency
434 bool isNeedIntegrityCheck = property.GetBoolProp(KvDBProperties::CHECK_INTEGRITY, false);
435 if (isNeedIntegrityCheck) {
436 LOGI("db need lock, need check integrity is [%d]", isNeedIntegrityCheck);
437 errCode = KvDBManager::TryLockDB(property, 10); // default 10 times retry
438 if (errCode != E_OK) {
439 return nullptr;
440 }
441 }
442
443 ResFinalizer unlock([&errCode, &property, &kvDB]() {
444 int err = KvDBManager::UnlockDB(property);
445 if (err != E_OK) {
446 LOGE("GetDataBase unlock failed! err [%d] errCode [%d]", err, errCode);
447 errCode = err;
448 RefObject::KillAndDecObjRef(kvDB);
449 kvDB = nullptr;
450 }
451 });
452
453 kvDB = CreateDataBase(property, errCode);
454 if (errCode != E_OK) {
455 LOGE("Create database failed, errCode = [%d]", errCode);
456 }
457 return kvDB;
458 }
459
IsOpenMemoryDb(const KvDBProperties & properties,const std::map<std::string,IKvDB * > & cache) const460 bool KvDBManager::IsOpenMemoryDb(const KvDBProperties &properties, const std::map<std::string, IKvDB *> &cache) const
461 {
462 std::string identifier = GenerateKvDBIdentifier(properties);
463 auto iter = cache.find(identifier);
464 if (iter != cache.end()) {
465 IKvDB *kvDB = iter->second;
466 if (kvDB != nullptr && kvDB->GetMyProperties().GetBoolProp(KvDBProperties::MEMORY_MODE, false)) {
467 return true;
468 }
469 }
470 return false;
471 }
472
473 // used to get kvdb size with the given property.
CalculateKvStoreSize(const KvDBProperties & properties,uint64_t & size)474 int KvDBManager::CalculateKvStoreSize(const KvDBProperties &properties, uint64_t &size)
475 {
476 KvDBManager *manager = GetInstance();
477 if (manager == nullptr) {
478 LOGE("Failed to get KvDBManager instance!");
479 return -E_OUT_OF_MEMORY;
480 }
481
482 std::lock_guard<std::mutex> lockGuard(kvDBLock_);
483 if (manager->IsOpenMemoryDb(properties, manager->singleVerNaturalStores_)) {
484 size = 0;
485 return E_OK;
486 }
487
488 IKvDBFactory *factory = IKvDBFactory::GetCurrent();
489 if (factory == nullptr) {
490 return -E_INVALID_DB;
491 }
492
493 uint64_t totalSize = 0;
494 for (KvDBType kvDbType : g_dbTypeArr) {
495 int innerErrCode = E_OK;
496 IKvDB *kvDB = factory->CreateKvDb(kvDbType, innerErrCode);
497 if (innerErrCode != E_OK) {
498 return innerErrCode;
499 }
500 uint64_t dbSize = 0;
501 innerErrCode = kvDB->GetKvDBSize(properties, dbSize);
502 RefObject::DecObjRef(kvDB);
503 if (innerErrCode != E_OK && innerErrCode != -E_NOT_FOUND) {
504 return innerErrCode;
505 }
506 LOGD("DB type [%u], size[%" PRIu64 "]", static_cast<unsigned>(kvDbType), dbSize);
507 totalSize = totalSize + dbSize;
508 }
509 // This represent Db file size(Unit is byte), It is small than max size(max uint64_t represent 2^64B)
510 if (totalSize != 0ULL) {
511 size = totalSize;
512 return E_OK;
513 }
514 return -E_NOT_FOUND;
515 }
516
GetKvDBFromCacheByIdentify(const std::string & identifier,const std::map<std::string,IKvDB * > & cache) const517 IKvDB *KvDBManager::GetKvDBFromCacheByIdentify(const std::string &identifier,
518 const std::map<std::string, IKvDB *> &cache) const
519 {
520 auto iter = cache.find(identifier);
521 if (iter != cache.end()) {
522 IKvDB *kvDB = iter->second;
523 if (kvDB == nullptr) {
524 LOGE("Kvstore cache is nullptr, there may be a logic error");
525 return nullptr;
526 }
527 return kvDB;
528 }
529 return nullptr;
530 }
531
CheckDatabaseFileStatus(const KvDBProperties & properties)532 int KvDBManager::CheckDatabaseFileStatus(const KvDBProperties &properties)
533 {
534 KvDBManager *manager = GetInstance();
535 if (manager == nullptr) {
536 LOGE("Failed to get KvDBManager instance!");
537 return -E_OUT_OF_MEMORY;
538 }
539
540 std::string identifier = GenerateKvDBIdentifier(properties);
541 std::lock_guard<std::mutex> lockGuard(kvDBLock_);
542 IKvDB *kvDB = manager->GetKvDBFromCacheByIdentify(identifier, manager->localKvDBs_);
543 if (kvDB != nullptr) {
544 LOGE("The local KvDB is busy!");
545 return -E_BUSY;
546 }
547
548 kvDB = manager->GetKvDBFromCacheByIdentify(identifier, manager->multiVerNaturalStores_);
549 if (kvDB != nullptr) {
550 LOGE("The multi ver natural store is busy!");
551 return -E_BUSY;
552 }
553
554 kvDB = manager->GetKvDBFromCacheByIdentify(identifier, manager->singleVerNaturalStores_);
555 if (kvDB != nullptr) {
556 LOGE("The single version natural store is busy!");
557 return -E_BUSY;
558 }
559 return E_OK;
560 }
561
OpenNewDatabase(const KvDBProperties & property,int & errCode)562 IKvDB *KvDBManager::OpenNewDatabase(const KvDBProperties &property, int &errCode)
563 {
564 errCode = CheckRemoveStateAndRetry(property);
565 if (errCode != E_OK) {
566 LOGE("Failed to open IKvDB! Because delete kvstore unfinished err:%d", errCode);
567 return nullptr;
568 }
569
570 IKvDB *kvDB = nullptr;
571 errCode = CreateDataBaseInstance(property, kvDB);
572 if (errCode != E_OK) {
573 LOGE("Failed to get IKvDB! err:%d", errCode);
574 return nullptr;
575 }
576
577 errCode = kvDB->Open(property);
578 if (errCode != E_OK) {
579 LOGE("Failed to open IKvDB! err:%d", errCode);
580 RefObject::KillAndDecObjRef(kvDB);
581 kvDB = nullptr;
582 return nullptr;
583 }
584 auto identifier = DBCommon::TransferStringToHex(property.GetStringProp(KvDBProperties::IDENTIFIER_DATA, ""));
585 auto dbDir = property.GetStringProp(KvDBProperties::IDENTIFIER_DIR, "");
586 LOGI("Database identifier:%.6s, dir:%.6s", identifier.c_str(), dbDir.c_str());
587 // Register the callback function when the database is closed, triggered when kvdb free
588 kvDB->OnClose([kvDB, this]() {
589 LOGI("Remove from the cache");
590 this->RemoveKvDBFromCache(kvDB);
591 });
592
593 IKvDB *kvDBTmp = SaveKvDBToCache(kvDB);
594 if (kvDBTmp != kvDB) {
595 RefObject::KillAndDecObjRef(kvDB);
596 kvDB = nullptr;
597 return kvDBTmp;
598 }
599 return kvDB;
600 }
601
602 // used to delete a kvdb with the given property.
603 // return BUSY if in use
RemoveDatabase(const KvDBProperties & properties)604 int KvDBManager::RemoveDatabase(const KvDBProperties &properties)
605 {
606 KvDBManager *manager = GetInstance();
607 if (manager == nullptr) {
608 LOGE("Failed to get kvdb manager while removing the db!");
609 return -E_OUT_OF_MEMORY;
610 }
611 std::string identifier = GenerateKvDBIdentifier(properties);
612 manager->EnterDBOpenCloseProcess(identifier);
613
614 LOGI("KvDBManager::RemoveDatabase begin try lock the database!");
615 std::string lockFile = properties.GetStringProp(KvDBProperties::DATA_DIR, "") + "/" +
616 DBCommon::TransferStringToHex(identifier) + DBConstant::DB_LOCK_POSTFIX;
617 int errCode = E_OK;
618 if (OS::CheckPathExistence(lockFile)) {
619 errCode = KvDBManager::TryLockDB(properties, 10); // default 10 times retry
620 if (errCode != E_OK) {
621 manager->ExitDBOpenCloseProcess(identifier);
622 return errCode;
623 }
624 }
625
626 errCode = ExecuteRemoveDatabase(properties);
627 if (errCode != E_OK) {
628 LOGE("[KvDBManager] Remove database failed:%d", errCode);
629 }
630 int err = KvDBManager::UnlockDB(properties); // unlock and delete lock file before delete dir
631 if (err != E_OK) {
632 LOGE("[KvDBManager][RemoveDatabase] UnlockDB failed:%d, errno:%d", err, errno);
633 errCode = err;
634 }
635
636 manager->ExitDBOpenCloseProcess(identifier);
637 return errCode;
638 }
639
GenerateKvDBIdentifier(const KvDBProperties & property)640 std::string KvDBManager::GenerateKvDBIdentifier(const KvDBProperties &property)
641 {
642 return property.GetStringProp(KvDBProperties::IDENTIFIER_DATA, "");
643 }
644
GetInstance()645 KvDBManager *KvDBManager::GetInstance()
646 {
647 // For Double-Checked Locking, we need check instance_ twice
648 if (instance_ == nullptr) {
649 std::lock_guard<std::mutex> lockGuard(instanceLock_);
650 if (instance_ == nullptr) {
651 instance_ = new (std::nothrow) KvDBManager();
652 if (instance_ == nullptr) {
653 LOGE("failed to new KvDBManager!");
654 return nullptr;
655 }
656 }
657 }
658 if (IKvDBFactory::GetCurrent() == nullptr) {
659 IKvDBFactory::Register(&g_defaultFactory);
660 }
661 return instance_;
662 }
663
664 // Save to IKvDB to the global map
SaveKvDBToCache(IKvDB * kvDB)665 IKvDB *KvDBManager::SaveKvDBToCache(IKvDB *kvDB)
666 {
667 if (kvDB == nullptr) {
668 return nullptr;
669 }
670
671 {
672 KvDBProperties properties = kvDB->GetMyProperties();
673 std::string identifier = GenerateKvDBIdentifier(properties);
674 int databaseType = properties.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE);
675 std::lock_guard<std::mutex> lockGuard(kvDBLock_);
676 int errCode = E_OK;
677 if (databaseType == KvDBProperties::LOCAL_TYPE) {
678 IKvDB *kvDBTmp = FindKvDBFromCache(properties, localKvDBs_, true, errCode);
679 if (kvDBTmp != nullptr) {
680 kvDBTmp->IncObjRef(kvDBTmp);
681 return kvDBTmp;
682 }
683 localKvDBs_.insert(std::pair<std::string, IKvDB *>(identifier, kvDB));
684 } else if (databaseType == KvDBProperties::MULTI_VER_TYPE) {
685 IKvDB *kvDBTmp = FindKvDBFromCache(properties, multiVerNaturalStores_, true, errCode);
686 if (kvDBTmp != nullptr) {
687 kvDBTmp->IncObjRef(kvDBTmp);
688 return kvDBTmp;
689 }
690 kvDB->WakeUpSyncer();
691 multiVerNaturalStores_.insert(std::pair<std::string, IKvDB *>(identifier, kvDB));
692 } else {
693 IKvDB *kvDBTmp = FindKvDBFromCache(properties, singleVerNaturalStores_, true, errCode);
694 if (kvDBTmp != nullptr) {
695 kvDBTmp->IncObjRef(kvDBTmp);
696 return kvDBTmp;
697 }
698 kvDB->WakeUpSyncer();
699 singleVerNaturalStores_.insert(std::pair<std::string, IKvDB *>(identifier, kvDB));
700 }
701 }
702 kvDB->SetCorruptHandler([kvDB, this]() {
703 std::string appId = kvDB->GetMyProperties().GetStringProp(KvDBProperties::APP_ID, "");
704 std::string userId = kvDB->GetMyProperties().GetStringProp(KvDBProperties::USER_ID, "");
705 std::string storeId = kvDB->GetMyProperties().GetStringProp(KvDBProperties::STORE_ID, "");
706 this->DataBaseCorruptNotifyAsync(appId, userId, storeId);
707 });
708 return kvDB;
709 }
710
711 // Save to IKvDB to the global map
RemoveKvDBFromCache(const IKvDB * kvDB)712 void KvDBManager::RemoveKvDBFromCache(const IKvDB *kvDB)
713 {
714 const KvDBProperties &properties = kvDB->GetMyProperties();
715 std::string identifier = GenerateKvDBIdentifier(properties);
716 int databaseType = properties.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE);
717 std::lock_guard<std::mutex> lockGuard(kvDBLock_);
718 if (databaseType == KvDBProperties::LOCAL_TYPE) {
719 localKvDBs_.erase(identifier);
720 } else if (databaseType == KvDBProperties::MULTI_VER_TYPE) {
721 multiVerNaturalStores_.erase(identifier);
722 } else {
723 singleVerNaturalStores_.erase(identifier);
724 }
725 }
726
727 // Get IKvDB from the global map
FindAndGetKvDBFromCache(const KvDBProperties & properties,int & errCode) const728 IKvDB *KvDBManager::FindAndGetKvDBFromCache(const KvDBProperties &properties, int &errCode) const
729 {
730 std::lock_guard<std::mutex> lockGuard(kvDBLock_);
731
732 IKvDB *kvDB = FindKvDBFromCache(properties, localKvDBs_, true, errCode);
733 if (kvDB != nullptr) {
734 kvDB->IncObjRef(kvDB);
735 return kvDB;
736 }
737 if (errCode != -E_NOT_FOUND) {
738 return nullptr;
739 }
740
741 kvDB = FindKvDBFromCache(properties, multiVerNaturalStores_, true, errCode);
742 if (kvDB != nullptr) {
743 kvDB->IncObjRef(kvDB);
744 return kvDB;
745 }
746 if (errCode != -E_NOT_FOUND) {
747 return nullptr;
748 }
749
750 kvDB = FindKvDBFromCache(properties, singleVerNaturalStores_, true, errCode);
751 if (kvDB != nullptr) {
752 kvDB->IncObjRef(kvDB);
753 return kvDB;
754 }
755 return nullptr;
756 }
757
FindKvDBFromCache(const KvDBProperties & properties,const std::map<std::string,IKvDB * > & cache,bool isNeedCheckPasswd,int & errCode) const758 IKvDB *KvDBManager::FindKvDBFromCache(const KvDBProperties &properties, const std::map<std::string, IKvDB *> &cache,
759 bool isNeedCheckPasswd, int &errCode) const
760 {
761 errCode = E_OK;
762 std::string identifier = GenerateKvDBIdentifier(properties);
763 auto iter = cache.find(identifier);
764 if (iter != cache.end()) {
765 IKvDB *kvDB = iter->second;
766 if (kvDB == nullptr) {
767 LOGE("KVSTORE cache is nullptr, there may be a logic error");
768 errCode = -E_INTERNAL_ERROR;
769 return nullptr;
770 }
771 int newType = properties.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE);
772 int oldType = kvDB->GetMyProperties().GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE);
773 if (oldType == newType) {
774 errCode = CheckKvDBProperties(kvDB, properties, isNeedCheckPasswd);
775 if (errCode != E_OK) {
776 return nullptr;
777 }
778 return kvDB;
779 } else {
780 errCode = -E_INVALID_ARGS;
781 LOGE("Database [%s] type not matched, type [%d] vs [%d]",
782 STR_MASK(DBCommon::TransferStringToHex(identifier)), newType, oldType);
783 return nullptr;
784 }
785 }
786
787 errCode = -E_NOT_FOUND;
788 return nullptr;
789 }
790
SetProcessLabel(const std::string & appId,const std::string & userId)791 int KvDBManager::SetProcessLabel(const std::string &appId, const std::string &userId)
792 {
793 std::string label = appId + PROCESS_LABEL_CONNECTOR + userId;
794 RuntimeContext::GetInstance()->SetProcessLabel(label);
795 return E_OK;
796 }
797
RestoreSyncableKvStore()798 void KvDBManager::RestoreSyncableKvStore()
799 {
800 KvDBManager *manager = GetInstance();
801 if (manager == nullptr) {
802 return;
803 }
804
805 manager->RestoreSyncerOfAllKvStore();
806 }
807
SetDatabaseCorruptionHandler(const KvStoreCorruptionHandler & handler)808 void KvDBManager::SetDatabaseCorruptionHandler(const KvStoreCorruptionHandler &handler)
809 {
810 KvDBManager *manager = GetInstance();
811 if (manager == nullptr) {
812 return;
813 }
814
815 manager->SetAllDatabaseCorruptionHander(handler);
816 }
817
SetAllDatabaseCorruptionHander(const KvStoreCorruptionHandler & handler)818 void KvDBManager::SetAllDatabaseCorruptionHander(const KvStoreCorruptionHandler &handler)
819 {
820 {
821 std::lock_guard<std::mutex> lock(corruptMutex_);
822 corruptHandler_ = handler;
823 }
824 std::lock_guard<std::mutex> lockGuard(kvDBLock_);
825 SetCorruptHandlerForDatabases(singleVerNaturalStores_);
826 SetCorruptHandlerForDatabases(localKvDBs_);
827 SetCorruptHandlerForDatabases(multiVerNaturalStores_);
828 }
829
DataBaseCorruptNotify(const std::string & appId,const std::string & userId,const std::string & storeId)830 void KvDBManager::DataBaseCorruptNotify(const std::string &appId, const std::string &userId, const std::string &storeId)
831 {
832 KvStoreCorruptionHandler corruptHandler = nullptr;
833 {
834 std::lock_guard<std::mutex> lock(corruptMutex_);
835 corruptHandler = corruptHandler_;
836 }
837
838 if (corruptHandler != nullptr) {
839 corruptHandler(appId, userId, storeId);
840 }
841 }
842
DataBaseCorruptNotifyAsync(const std::string & appId,const std::string & userId,const std::string & storeId)843 void KvDBManager::DataBaseCorruptNotifyAsync(const std::string &appId, const std::string &userId,
844 const std::string &storeId)
845 {
846 int errCode = RuntimeContext::GetInstance()->ScheduleTask(
847 std::bind(&KvDBManager::DataBaseCorruptNotify, this, appId, userId, storeId));
848 if (errCode != E_OK) {
849 LOGE("[KvDBManager][CorruptNotify] ScheduleTask failed, errCode = %d.", errCode);
850 return;
851 }
852 }
853
SetCorruptHandlerForDatabases(const std::map<std::string,IKvDB * > & dbMaps)854 void KvDBManager::SetCorruptHandlerForDatabases(const std::map<std::string, IKvDB *> &dbMaps)
855 {
856 for (const auto &item : dbMaps) {
857 if (item.second == nullptr) {
858 continue;
859 }
860
861 item.second->SetCorruptHandler([item, this]() {
862 std::string appId = item.second->GetMyProperties().GetStringProp(KvDBProperties::APP_ID, "");
863 std::string userId = item.second->GetMyProperties().GetStringProp(KvDBProperties::USER_ID, "");
864 std::string storeId = item.second->GetMyProperties().GetStringProp(KvDBProperties::STORE_ID, "");
865 this->DataBaseCorruptNotifyAsync(appId, userId, storeId);
866 });
867 }
868 }
869
RestoreSyncerOfAllKvStore()870 void KvDBManager::RestoreSyncerOfAllKvStore()
871 {
872 std::lock_guard<std::mutex> lockGuard(kvDBLock_);
873 for (auto &item : singleVerNaturalStores_) {
874 if (item.second != nullptr) {
875 item.second->WakeUpSyncer();
876 }
877 }
878
879 for (auto &item : multiVerNaturalStores_) {
880 if (item.second != nullptr) {
881 item.second->WakeUpSyncer();
882 }
883 }
884 }
885
CompareSchemaObject(const SchemaObject & newSchema,const SchemaObject & oldSchema)886 bool KvDBManager::CompareSchemaObject(const SchemaObject &newSchema, const SchemaObject &oldSchema)
887 {
888 if (!newSchema.IsSchemaValid() && !oldSchema.IsSchemaValid()) {
889 return true;
890 }
891 if (!newSchema.IsSchemaValid() || !oldSchema.IsSchemaValid()) {
892 return false;
893 }
894 return (oldSchema.CompareAgainstSchemaObject(newSchema) == -E_SCHEMA_EQUAL_EXACTLY);
895 }
896
CheckSchema(const IKvDB * kvDB,const KvDBProperties & properties)897 int KvDBManager::CheckSchema(const IKvDB *kvDB, const KvDBProperties &properties)
898 {
899 if (kvDB == nullptr) {
900 LOGE("input kvdb is nullptr");
901 return -E_INVALID_ARGS;
902 }
903 SchemaObject inputSchema = properties.GetSchema();
904 SchemaObject cacheSchema = kvDB->GetMyProperties().GetSchema();
905 bool isFirstOpenReadOnly =
906 kvDB->GetMyProperties().GetBoolProp(KvDBProperties::FIRST_OPEN_IS_READ_ONLY, false);
907 if (isFirstOpenReadOnly) {
908 if (inputSchema.IsSchemaValid()) {
909 LOGE("schema not matched");
910 return -E_SCHEMA_MISMATCH;
911 } else {
912 return E_OK;
913 }
914 }
915 if (!CompareSchemaObject(inputSchema, cacheSchema)) {
916 LOGE("schema not matched");
917 return -E_SCHEMA_MISMATCH;
918 }
919 return E_OK;
920 }
921
922 namespace {
CheckSecOptions(const KvDBProperties & input,const KvDBProperties & existed)923 bool CheckSecOptions(const KvDBProperties &input, const KvDBProperties &existed)
924 {
925 // If any has NO_SET, skip the check and using the existed option.
926 if (input.GetIntProp(KvDBProperties::SECURITY_LABEL, 0) != 0 &&
927 existed.GetIntProp(KvDBProperties::SECURITY_LABEL, 0) != 0) {
928 if (existed.GetIntProp(KvDBProperties::SECURITY_LABEL, 0) !=
929 input.GetIntProp(KvDBProperties::SECURITY_LABEL, 0)) {
930 LOGE("Security label mismatch: existed[%d] vs input[%d]",
931 existed.GetIntProp(KvDBProperties::SECURITY_LABEL, 0),
932 input.GetIntProp(KvDBProperties::SECURITY_LABEL, 0));
933 return false;
934 }
935 if (existed.GetIntProp(KvDBProperties::SECURITY_FLAG, 0) !=
936 input.GetIntProp(KvDBProperties::SECURITY_FLAG, 0)) {
937 LOGE("Security flag mismatch: existed[%d] vs input[%d]",
938 existed.GetIntProp(KvDBProperties::SECURITY_FLAG, 0),
939 input.GetIntProp(KvDBProperties::SECURITY_FLAG, 0));
940 return false;
941 }
942 }
943 return true;
944 }
945 }
946
CheckKvDBProperties(const IKvDB * kvDB,const KvDBProperties & properties,bool isNeedCheckPasswd) const947 int KvDBManager::CheckKvDBProperties(const IKvDB *kvDB, const KvDBProperties &properties,
948 bool isNeedCheckPasswd) const
949 {
950 // if get from cache is not memoryDb, do not support open or create memory DB
951 bool isMemoryDb = properties.GetBoolProp(KvDBProperties::MEMORY_MODE, false);
952 if (isMemoryDb != kvDB->GetMyProperties().GetBoolProp(KvDBProperties::MEMORY_MODE, false)) {
953 LOGE("Already open same id physical DB, so do not support open or create memory DB");
954 return -E_INVALID_ARGS;
955 }
956
957 if (kvDB->GetMyProperties().GetBoolProp(KvDBProperties::CREATE_DIR_BY_STORE_ID_ONLY, false) !=
958 properties.GetBoolProp(KvDBProperties::CREATE_DIR_BY_STORE_ID_ONLY, false)) {
959 LOGE("Different ways to create dir.");
960 return -E_INVALID_ARGS;
961 }
962
963 if (kvDB->GetMyProperties().GetIntProp(KvDBProperties::CONFLICT_RESOLVE_POLICY, 0) !=
964 properties.GetIntProp(KvDBProperties::CONFLICT_RESOLVE_POLICY, 0)) {
965 LOGE("Different conflict resolve policy.");
966 return -E_INVALID_ARGS;
967 }
968
969 if (kvDB->GetMyProperties().GetBoolProp(KvDBProperties::SYNC_DUAL_TUPLE_MODE, false) !=
970 properties.GetBoolProp(KvDBProperties::SYNC_DUAL_TUPLE_MODE, false)) {
971 LOGE("Different dual tuple sync mode");
972 return -E_MODE_MISMATCH;
973 }
974
975 if (kvDB->GetMyProperties().GetBoolProp(KvDBProperties::LOCAL_ONLY, false) !=
976 properties.GetBoolProp(KvDBProperties::LOCAL_ONLY, false)) {
977 LOGE("Different local only mode");
978 return -E_INVALID_ARGS;
979 }
980
981 if (!CheckSecOptions(properties, kvDB->GetMyProperties())) {
982 return -E_INVALID_ARGS;
983 }
984
985 CipherType cacheType;
986 CipherType inputType;
987 CipherPassword cachePasswd;
988 CipherPassword inputPasswd;
989 kvDB->GetMyProperties().GetPassword(cacheType, cachePasswd);
990 properties.GetPassword(inputType, inputPasswd);
991 if (isNeedCheckPasswd && (cachePasswd != inputPasswd || !DBCommon::IsSameCipher(cacheType, inputType))) {
992 LOGE("Identification not matched");
993 return -E_INVALID_PASSWD_OR_CORRUPTED_DB;
994 }
995
996 return CheckSchema(kvDB, properties);
997 }
998
999 // Attention. After call FindKvDB and kvdb is not null, you need to call DecObjRef.
FindKvDB(const std::string & identifier) const1000 IKvDB* KvDBManager::FindKvDB(const std::string &identifier) const
1001 {
1002 std::lock_guard<std::mutex> lockGuard(kvDBLock_);
1003 auto kvdb = singleVerNaturalStores_.find(identifier);
1004 if (kvdb != singleVerNaturalStores_.end()) {
1005 // Increase ref counter here.
1006 RefObject::IncObjRef(kvdb->second);
1007 return kvdb->second;
1008 }
1009 return nullptr;
1010 }
1011
Dump(int fd)1012 void KvDBManager::Dump(int fd)
1013 {
1014 std::lock_guard<std::mutex> lockGuard(kvDBLock_);
1015 for (const auto &entry : singleVerNaturalStores_) {
1016 RefObject::IncObjRef(entry.second);
1017 entry.second->Dump(fd);
1018 RefObject::DecObjRef(entry.second);
1019 }
1020 }
1021 } // namespace DistributedDB
1022