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