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