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