• 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 
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