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 #ifndef OMIT_MULTI_VER
17 #include "multi_ver_natural_store_commit_storage.h"
18 
19 #include <stack>
20 
21 #include "db_errno.h"
22 #include "db_constant.h"
23 #include "log_print.h"
24 #include "multi_ver_commit.h"
25 #include "ikvdb_factory.h"
26 #include "parcel.h"
27 #include "db_common.h"
28 #include "sqlite_local_kvdb.h"
29 #include "kvdb_utils.h"
30 
31 namespace DistributedDB {
32 using std::string;
33 using std::vector;
34 using std::list;
35 using std::map;
36 using std::make_pair;
37 using std::stack;
38 
39 namespace {
40     const size_t MAX_COMMIT_ST_LENGTH = 4096;
41     const Version VERSION_MAX = 0xFFFFFFFFFFFFFFFF;
42     const string MULTI_VER_COMMIT_DB_NAME = "commit_logs.db";
43 }
44 
45 const string MultiVerNaturalStoreCommitStorage::HEADER_KEY = "header commit";
46 
MultiVerNaturalStoreCommitStorage()47 MultiVerNaturalStoreCommitStorage::MultiVerNaturalStoreCommitStorage()
48     : commitStorageDatabase_(nullptr),
49       commitStorageDBConnection_(nullptr)
50 {}
51 
~MultiVerNaturalStoreCommitStorage()52 MultiVerNaturalStoreCommitStorage::~MultiVerNaturalStoreCommitStorage()
53 {
54     Close();
55 }
56 
CheckVersion(const Property & property,bool & isDbExist) const57 int MultiVerNaturalStoreCommitStorage::CheckVersion(const Property &property, bool &isDbExist) const
58 {
59     int dbVer = 0;
60     int errCode = GetVersion(property, dbVer, isDbExist);
61     if (errCode != E_OK) {
62         LOGE("[CommitStorage][CheckVer] GetVersion failed, errCode=%d.", errCode);
63         return errCode;
64     }
65     if (!isDbExist) {
66         return E_OK;
67     }
68     LOGD("[CommitStorage][CheckVer] DbVersion=%d, CurVersion=%d.", dbVer, MULTI_VER_COMMIT_STORAGE_VERSION_CURRENT);
69     if (dbVer > MULTI_VER_COMMIT_STORAGE_VERSION_CURRENT) {
70         LOGE("[CommitStorage][CheckVer] Version Not Support!");
71         return -E_VERSION_NOT_SUPPORT;
72     }
73     return E_OK;
74 }
75 
GetVersion(const IKvDBCommitStorage::Property & property,int & version,bool & isDbExisted)76 int MultiVerNaturalStoreCommitStorage::GetVersion(const IKvDBCommitStorage::Property &property,
77     int &version, bool &isDbExisted)
78 {
79     SQLiteLocalKvDB *localKvdb = new (std::nothrow) SQLiteLocalKvDB();
80     if (localKvdb == nullptr) {
81         return -E_INVALID_DB;
82     }
83 
84     KvDBProperties dbProperties;
85     dbProperties.SetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, property.isNeedCreate);
86     dbProperties.SetStringProp(KvDBProperties::DATA_DIR, property.path);
87     dbProperties.SetStringProp(KvDBProperties::FILE_NAME, DBConstant::MULTI_VER_COMMIT_STORE);
88     dbProperties.SetStringProp(KvDBProperties::IDENTIFIER_DIR, property.identifierName);
89     dbProperties.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::MULTI_VER_TYPE_SQLITE);
90     dbProperties.SetPassword(property.cipherType, property.passwd);
91 
92     int errCode = localKvdb->GetVersion(dbProperties, version, isDbExisted);
93     RefObject::DecObjRef(localKvdb);
94     localKvdb = nullptr;
95     return errCode;
96 }
97 
Open(const IKvDBCommitStorage::Property & property)98 int MultiVerNaturalStoreCommitStorage::Open(const IKvDBCommitStorage::Property &property)
99 {
100     if (commitStorageDatabase_ != nullptr && commitStorageDBConnection_ != nullptr) {
101         return E_OK;
102     }
103     IKvDBFactory *factory = IKvDBFactory::GetCurrent();
104     if (factory == nullptr) {
105         LOGE("Failed to open IKvDB! Get factory failed.");
106         return -E_INVALID_DB;
107     }
108     int errCode = E_OK;
109     commitStorageDatabase_ = factory->CreateCommitStorageDB(errCode);
110     if (commitStorageDatabase_ == nullptr) {
111         LOGE("Failed to create commit storage database:%d", errCode);
112         return -E_INVALID_DB;
113     }
114 
115     KvDBProperties dbProperties;
116     dbProperties.SetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, property.isNeedCreate);
117     dbProperties.SetStringProp(KvDBProperties::DATA_DIR, property.path);
118     dbProperties.SetStringProp(KvDBProperties::FILE_NAME, DBConstant::MULTI_VER_COMMIT_STORE);
119     dbProperties.SetStringProp(KvDBProperties::IDENTIFIER_DIR, property.identifierName);
120     dbProperties.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::MULTI_VER_TYPE_SQLITE);
121     dbProperties.SetPassword(property.cipherType, property.passwd);
122 
123     errCode = commitStorageDatabase_->Open(dbProperties);
124     if (errCode != E_OK) {
125         LOGE("Failed to open commit storage database! err:%d", errCode);
126         RefObject::KillAndDecObjRef(commitStorageDatabase_);
127         commitStorageDatabase_ = nullptr;
128         return errCode;
129     }
130     commitStorageDBConnection_ = commitStorageDatabase_->GetDBConnection(errCode);
131     if (commitStorageDBConnection_ == nullptr) {
132         LOGE("Failed to get connection for commit storage! err:%d", errCode);
133         RefObject::KillAndDecObjRef(commitStorageDatabase_);
134         commitStorageDatabase_ = nullptr;
135         return errCode;
136     }
137     // Need to refactor in the future
138     errCode = static_cast<SQLiteLocalKvDB *>(commitStorageDatabase_)->SetVersion(dbProperties,
139         MULTI_VER_COMMIT_STORAGE_VERSION_CURRENT);
140     if (errCode != E_OK) {
141         LOGE("[CommitStorage][Open] SetVersion fail, errCode=%d.", errCode);
142         Close();
143         return errCode;
144     }
145     return E_OK;
146 }
147 
Close()148 void MultiVerNaturalStoreCommitStorage::Close()
149 {
150     if (commitStorageDatabase_ != nullptr && commitStorageDBConnection_ != nullptr) {
151         commitStorageDBConnection_->Close();
152         commitStorageDBConnection_ = nullptr;
153     }
154     if (commitStorageDatabase_ != nullptr) {
155         IKvDB::DecObjRef(commitStorageDatabase_);
156         commitStorageDatabase_ = nullptr;
157     }
158 }
159 
Remove(const IKvDBCommitStorage::Property & property)160 int MultiVerNaturalStoreCommitStorage::Remove(const IKvDBCommitStorage::Property &property)
161 {
162     if (commitStorageDatabase_ != nullptr && commitStorageDBConnection_ != nullptr) {
163         commitStorageDBConnection_->Close();
164         commitStorageDBConnection_ = nullptr;
165         RefObject::DecObjRef(commitStorageDatabase_);
166         commitStorageDatabase_ = nullptr;
167     }
168 
169     std::string dataDir = property.path + ("/" + property.identifierName + "/" + DBConstant::MULTI_SUB_DIR + "/");
170     int errCode = KvDBUtils::RemoveKvDB(dataDir, DBConstant::MULTI_VER_COMMIT_STORE);
171     if (errCode != E_OK) {
172         LOGE("Failed to remove commit storage database! err:%d", errCode);
173         return errCode;
174     }
175     return E_OK;
176 }
177 
AllocCommit(int & errCode) const178 IKvDBCommit *MultiVerNaturalStoreCommitStorage::AllocCommit(int &errCode) const
179 {
180     auto commit = new (std::nothrow) MultiVerCommit();
181     if (commit != nullptr) {
182         errCode = E_OK;
183     } else {
184         errCode = -E_OUT_OF_MEMORY;
185         LOGE("Failed to alloc commit! Bad alloc.");
186     }
187     return commit;
188 }
189 
GetCommit(const CommitID & commitId,int & errCode) const190 IKvDBCommit *MultiVerNaturalStoreCommitStorage::GetCommit(const CommitID &commitId, int &errCode) const
191 {
192     if (commitStorageDatabase_ == nullptr || commitStorageDBConnection_ == nullptr) {
193         LOGE("Failed to get commit! Commit storage do not open.");
194         errCode = -E_INVALID_DB;
195         return nullptr;
196     }
197     Key key;
198     TransferCommitIDToKey(commitId, key);
199     IOption option;
200     Value value;
201     errCode = commitStorageDBConnection_->Get(option, key, value);
202     if (errCode != E_OK) {
203         if (errCode != -E_NOT_FOUND) {
204             LOGE("Failed to get the commit:%d", errCode);
205         }
206         return nullptr;
207     }
208 
209     IKvDBCommit *commit = AllocCommit(errCode);
210     if (commit == nullptr) {
211         return nullptr;
212     }
213 
214     errCode = TransferValueToCommit(value, *commit);
215     if (errCode != E_OK) {
216         delete commit;
217         commit = nullptr;
218     }
219     return commit;
220 }
221 
StartVacuum()222 int MultiVerNaturalStoreCommitStorage::StartVacuum()
223 {
224     if (commitStorageDBConnection_ == nullptr) {
225         LOGE("commitStorage Connection not existed!");
226         return -E_INVALID_CONNECTION;
227     }
228     return commitStorageDBConnection_->StartTransaction();
229 }
230 
CancelVacuum()231 int MultiVerNaturalStoreCommitStorage::CancelVacuum()
232 {
233     if (commitStorageDBConnection_ == nullptr) {
234         LOGE("commitStorage Connection not existed!");
235         return -E_INVALID_CONNECTION;
236     }
237     return commitStorageDBConnection_->RollBack();
238 }
239 
FinishVacuum()240 int MultiVerNaturalStoreCommitStorage::FinishVacuum()
241 {
242     if (commitStorageDBConnection_ == nullptr) {
243         LOGE("commitStorage Connection not existed!");
244         return -E_INVALID_CONNECTION;
245     }
246     return commitStorageDBConnection_->Commit();
247 }
248 
GetAllCommitsInTree(std::list<MultiVerCommitNode> & commits) const249 int MultiVerNaturalStoreCommitStorage::GetAllCommitsInTree(std::list<MultiVerCommitNode> &commits) const
250 {
251     std::map<CommitID, IKvDBCommit *> commitsTable;
252     CommitID headerId;
253     int errCode = GetAllCommits(commitsTable, headerId);
254     if (errCode != E_OK || commitsTable.empty()) { // error or no commit.
255         return errCode;
256     }
257 
258     std::stack<CommitID> commitStack;
259     commitStack.push(headerId);
260     while (!commitStack.empty()) {
261         auto currentCommitIter = commitsTable.find(commitStack.top());
262         if (currentCommitIter == commitsTable.end()) {
263             // not found the node in the commit tree.
264             commits.clear();
265             errCode = -E_UNEXPECTED_DATA;
266             break;
267         }
268 
269         commitStack.pop();
270         if (currentCommitIter->second == nullptr) {
271             // if the node has been released or traveled.
272             continue;
273         }
274 
275         AddParentsToStack(currentCommitIter->second, commitsTable, commitStack);
276         MultiVerCommitNode commitNode;
277         // Get the current commit info
278         commitNode.commitId = currentCommitIter->first;
279         commitNode.deviceInfo = currentCommitIter->second->GetDeviceInfo();
280         commitNode.isLocal = (currentCommitIter->second->GetLocalFlag() ?
281             MultiVerCommitNode::LOCAL_FLAG : MultiVerCommitNode::NON_LOCAL_FLAG);
282         commitNode.leftParent = currentCommitIter->second->GetLeftParentId();
283         commitNode.rightParent = currentCommitIter->second->GetRightParentId();
284         commitNode.timestamp = currentCommitIter->second->GetTimestamp();
285         commitNode.version = currentCommitIter->second->GetCommitVersion();
286         commits.push_back(commitNode);
287 
288         ReleaseCommit(currentCommitIter->second);
289         currentCommitIter->second = nullptr; // has been traveled, set to nullptr.
290     }
291 
292     commits.sort([] (const MultiVerCommitNode &thisNode, const MultiVerCommitNode &thatNode) {
293         return (thisNode.version > thatNode.version);
294     });
295     ReleaseUnusedCommits(commitsTable);
296 
297     return errCode;
298 }
299 
AddCommit(const IKvDBCommit & commitEntry,bool isHeader)300 int MultiVerNaturalStoreCommitStorage::AddCommit(const IKvDBCommit &commitEntry, bool isHeader)
301 {
302     int errCode = CheckAddedCommit(commitEntry);
303     if (errCode != E_OK) {
304         return errCode;
305     }
306 
307     Key key;
308     TransferCommitIDToKey(commitEntry.GetCommitId(), key);
309     Value value;
310     errCode = TransferCommitToValue(commitEntry, value);
311     if (errCode != E_OK) {
312         return errCode;
313     }
314     IOption option;
315     errCode = commitStorageDBConnection_->StartTransaction();
316     if (errCode != E_OK) {
317         return errCode;
318     }
319 
320     errCode = commitStorageDBConnection_->Put(option, key, value);
321     if (errCode != E_OK) {
322         goto END;
323     }
324 
325     if (isHeader) {
326         errCode = SetHeaderInner(commitEntry.GetCommitId());
327     }
328 END:
329     if (errCode != E_OK) {
330         commitStorageDBConnection_->RollBack();
331     } else {
332         errCode = commitStorageDBConnection_->Commit();
333     }
334 
335     return errCode;
336 }
337 
RemoveCommit(const CommitID & commitId)338 int MultiVerNaturalStoreCommitStorage::RemoveCommit(const CommitID &commitId)
339 {
340     if (commitStorageDatabase_ == nullptr || commitStorageDBConnection_ == nullptr) {
341         LOGE("Failed to get commit! Commit storage do not open.");
342         return -E_INVALID_DB;
343     }
344     int errCode = commitStorageDBConnection_->StartTransaction();
345     if (errCode != E_OK) {
346         LOGE("Failed to remove commit when start transaction! err:%d", errCode);
347         return errCode;
348     }
349     Key key;
350     IOption option;
351     CommitID header = GetHeader(errCode);
352     if (header == commitId) {
353         IKvDBCommit *commit = GetCommit(commitId, errCode);
354         if (commit == nullptr) {
355             LOGE("Failed to remove commit when get header commit! err:%d", errCode);
356             goto ERROR;
357         }
358         errCode = SetHeader(commit->GetLeftParentId());
359         ReleaseCommit(commit);
360         commit = nullptr;
361         if (errCode != E_OK) {
362             LOGE("Failed to remove commit when set header commit! err:%d", errCode);
363             goto ERROR;
364         }
365     } else {
366         LOGE("Failed to remove commit! The commit is not the header.");
367         errCode = -E_UNEXPECTED_DATA;
368         goto ERROR;
369     }
370     TransferCommitIDToKey(commitId, key);
371     errCode = commitStorageDBConnection_->Delete(option, key);
372     if (errCode != E_OK) {
373         LOGI("Failed to remove commit when remove commit! err:%d", errCode);
374         goto ERROR;
375     }
376     errCode = commitStorageDBConnection_->Commit();
377     if (errCode != E_OK) {
378         LOGE("Failed to remove commit when commit! err:%d", errCode);
379         goto ERROR;
380     }
381     return E_OK;
382 ERROR:
383     (void)commitStorageDBConnection_->RollBack();
384     return errCode;
385 }
386 
ReleaseCommit(const IKvDBCommit * commit) const387 void MultiVerNaturalStoreCommitStorage::ReleaseCommit(const IKvDBCommit *commit) const
388 {
389     if (commit != nullptr) {
390         delete commit;
391         commit = nullptr;
392     }
393 }
394 
SetHeader(const CommitID & commitId)395 int MultiVerNaturalStoreCommitStorage::SetHeader(const CommitID &commitId)
396 {
397     if (commitStorageDatabase_ == nullptr || commitStorageDBConnection_ == nullptr) {
398         LOGE("Failed to get commit! Commit storage do not open.");
399         return -E_INVALID_DB;
400     }
401 
402     if (commitId.size() != 0) {
403         int errCode = E_OK;
404         if (!CommitExist(commitId, errCode)) {
405             LOGE("Failed to set header! The commit does not exist.");
406             return errCode;
407         }
408     }
409 
410     return SetHeaderInner(commitId);
411 }
412 
GetHeader(int & errCode) const413 CommitID MultiVerNaturalStoreCommitStorage::GetHeader(int &errCode) const
414 {
415     CommitID headerCommitID;
416     if (commitStorageDatabase_ == nullptr || commitStorageDBConnection_ == nullptr) {
417         LOGE("Failed to get commit for uninitialized store");
418         errCode = -E_INVALID_DB;
419         return headerCommitID;
420     }
421     Key key;
422     TransferStringToKey(HEADER_KEY, key);
423     IOption option;
424     Value value;
425     errCode = commitStorageDBConnection_->Get(option, key, value);
426     if (errCode != E_OK) {
427         if (errCode == -E_NOT_FOUND) { // not find the header, means no header.
428             LOGI("Not find the header.");
429             errCode = E_OK;
430         } else {
431             LOGE("Get the commit header failed:%d", errCode);
432             return headerCommitID;
433         }
434     }
435     TransferValueToCommitID(value, headerCommitID);
436     return headerCommitID;
437 }
438 
CommitExist(const CommitID & commitId,int & errCode) const439 bool MultiVerNaturalStoreCommitStorage::CommitExist(const CommitID &commitId, int &errCode) const
440 {
441     IKvDBCommit *commit = GetCommit(commitId, errCode);
442     if (commit == nullptr) {
443         return false;
444     } else {
445         ReleaseCommit(commit);
446         commit = nullptr;
447         return true;
448     }
449 }
450 
ReleaseUnusedCommits(std::map<CommitID,IKvDBCommit * > & commits) const451 void MultiVerNaturalStoreCommitStorage::ReleaseUnusedCommits(
452     std::map<CommitID, IKvDBCommit *> &commits) const
453 {
454     // need release the unmerged commits
455     for (auto &item : commits) {
456         if (item.second != nullptr) {
457             ReleaseCommit(item.second);
458             item.second = nullptr;
459         }
460     }
461     commits.clear();
462 }
463 
ReleaseLatestCommits(std::map<DeviceID,IKvDBCommit * > & latestCommits) const464 void MultiVerNaturalStoreCommitStorage::ReleaseLatestCommits(
465     std::map<DeviceID, IKvDBCommit *> &latestCommits) const
466 {
467     // need release the commits for exception.
468     for (auto &item : latestCommits) {
469         if (item.second != nullptr) {
470             ReleaseCommit(item.second);
471             item.second = nullptr;
472         }
473     }
474     latestCommits.clear();
475 }
476 
ReleaseCommitList(list<IKvDBCommit * > & commits) const477 void MultiVerNaturalStoreCommitStorage::ReleaseCommitList(list<IKvDBCommit *> &commits) const
478 {
479     for (auto &item : commits) {
480         if (item != nullptr) {
481             ReleaseCommit(item);
482             item = nullptr;
483         }
484     }
485     commits.clear();
486 }
487 
GetLatestCommits(std::map<DeviceID,IKvDBCommit * > & latestCommits) const488 int MultiVerNaturalStoreCommitStorage::GetLatestCommits(std::map<DeviceID, IKvDBCommit *> &latestCommits) const
489 {
490     latestCommits.clear();
491     map<CommitID, IKvDBCommit *> commits;
492     CommitID headerId;
493     int errCode = GetAllCommits(commits, headerId);
494     if (errCode != E_OK || commits.empty()) { // error or no commit.
495         return errCode;
496     }
497 
498     std::stack<CommitID> commitStack;
499     commitStack.push(headerId);
500     while (!commitStack.empty()) {
501         CommitID frontId = commitStack.top();
502         auto currentCommitIter = commits.find(frontId);
503         if (currentCommitIter == commits.end()) {
504             // not found the node in the commit tree.
505             LOGE("Not found the commit for the latest commits!");
506             ReleaseLatestCommits(latestCommits);
507             errCode = -E_UNEXPECTED_DATA;
508             break;
509         }
510 
511         commitStack.pop();
512         if (currentCommitIter->second == nullptr) {
513             // if the node has been released or traveled.
514             continue;
515         }
516 
517         AddParentsToStack(currentCommitIter->second, commits, commitStack);
518 
519         // Get the current commit info
520         DeviceID deviceInfo = currentCommitIter->second->GetDeviceInfo();
521         auto latestCommit = latestCommits.find(deviceInfo);
522         if (latestCommit == latestCommits.end()) {
523             // not found any node of the device in the commit tree.
524             latestCommits.insert(make_pair(deviceInfo, currentCommitIter->second));
525         } else if (CompareCommit(latestCommit->second, currentCommitIter->second)) {
526             // if the current commit version is bigger than the stored.
527             ReleaseCommit(latestCommit->second);
528             latestCommit->second = currentCommitIter->second;
529         } else {
530             ReleaseCommit(currentCommitIter->second);
531         }
532         currentCommitIter->second = nullptr; // has been traveled, set to nullptr.
533     }
534 
535     ReleaseUnusedCommits(commits);
536     return errCode;
537 }
538 
GetLocalVersionThredForLatestCommits(const map<CommitID,IKvDBCommit * > & allCommits,const map<DeviceID,CommitID> & latestCommits,map<DeviceID,Version> & latestCommitVersions)539 void MultiVerNaturalStoreCommitStorage::GetLocalVersionThredForLatestCommits(
540     const map<CommitID, IKvDBCommit *> &allCommits, const map<DeviceID, CommitID> &latestCommits,
541     map<DeviceID, Version> &latestCommitVersions)
542 {
543     for (const auto &latestCommit : latestCommits) {
544         auto commitIter = allCommits.find(latestCommit.second);
545         if (commitIter != allCommits.end()) {
546             // found in the local store, just set the threshold.
547             latestCommitVersions.insert(make_pair(latestCommit.first, commitIter->second->GetCommitVersion()));
548         } else {
549             // not found in the local store, means that newer than local.
550             latestCommitVersions.insert(make_pair(latestCommit.first, VERSION_MAX));
551         }
552     }
553 }
554 
AddParentsToStack(const IKvDBCommit * commit,const std::map<CommitID,IKvDBCommit * > & allCommits,std::stack<CommitID> & commitStack)555 void MultiVerNaturalStoreCommitStorage::AddParentsToStack(const IKvDBCommit *commit,
556     const std::map<CommitID, IKvDBCommit *> &allCommits, std::stack<CommitID> &commitStack)
557 {
558     if (commit == nullptr) {
559         return;
560     }
561 
562     auto leftParentId = commit->GetLeftParentId();
563     auto rightParentId = commit->GetRightParentId();
564     if (!rightParentId.empty()) {
565         auto iter = allCommits.find(rightParentId);
566         if (iter != allCommits.end() && iter->second != nullptr) {
567             // if the right parent has not been traveled, just push into the stack.
568             commitStack.push(rightParentId);
569         }
570     }
571     if (!leftParentId.empty()) {
572         auto iter = allCommits.find(leftParentId);
573         if (iter != allCommits.end() && iter->second != nullptr) {
574             // if the left parent has not been traveled, just push into the stack.
575             commitStack.push(leftParentId);
576         }
577     }
578 }
579 
GetCommitTree(const map<DeviceID,CommitID> & latestCommits,list<IKvDBCommit * > & commits) const580 int MultiVerNaturalStoreCommitStorage::GetCommitTree(const map<DeviceID, CommitID> &latestCommits,
581     list<IKvDBCommit *> &commits) const
582 {
583     commits.clear();
584     CommitID header;
585     map<CommitID, IKvDBCommit *> allCommits;
586     int errCode = GetAllCommits(allCommits, header);
587     // error or no commit.
588     if (errCode != E_OK || allCommits.empty()) {
589         return errCode;
590     }
591     map<DeviceID, Version> latestCommitVersions;
592     GetLocalVersionThredForLatestCommits(allCommits, latestCommits, latestCommitVersions);
593     std::stack<CommitID> commitStack;
594     commitStack.push(header);
595     while (!commitStack.empty()) {
596         CommitID frontId = commitStack.top();
597         auto currentCommitIter = allCommits.find(frontId);
598         if (currentCommitIter == allCommits.end()) {
599             // not found the node in the commit tree.
600             LOGE("Not found the commit in the local tree!");
601             ReleaseCommitList(commits);
602             errCode = -E_UNEXPECTED_DATA;
603             break;
604         }
605         commitStack.pop();
606         if (currentCommitIter->second == nullptr) {
607             // if the commit has been traveled.
608             continue;
609         }
610         AddParentsToStack(currentCommitIter->second, allCommits, commitStack);
611         // Get the current commit info
612         DeviceID deviceInfo = currentCommitIter->second->GetDeviceInfo();
613         auto latestCommit = latestCommitVersions.find(deviceInfo);
614         if (latestCommit == latestCommitVersions.end() ||
615             latestCommit->second < currentCommitIter->second->GetCommitVersion()) {
616             // not found in the latest commits of the other device,
617             // or the current commit version is bigger than the threshold.
618             commits.push_back(currentCommitIter->second);
619         } else {
620             // means that the commit existed in the other device.
621             ReleaseCommit(currentCommitIter->second);
622         }
623         currentCommitIter->second = nullptr;
624     }
625 
626     ReleaseUnusedCommits(allCommits);
627     RefreshCommitTree(commits); // for version ascend.
628     return errCode;
629 }
630 
RunRekeyLogic(CipherType type,const CipherPassword & passwd)631 int MultiVerNaturalStoreCommitStorage::RunRekeyLogic(CipherType type, const CipherPassword &passwd)
632 {
633     int errCode = static_cast<SQLiteLocalKvDB *>(commitStorageDatabase_)->RunRekeyLogic(type, passwd);
634     if (errCode != E_OK) {
635         LOGE("commit logs rekey failed:%d", errCode);
636     }
637     return errCode;
638 }
639 
RunExportLogic(CipherType type,const CipherPassword & passwd,const std::string & dbDir)640 int MultiVerNaturalStoreCommitStorage::RunExportLogic(CipherType type, const CipherPassword &passwd,
641     const std::string &dbDir)
642 {
643     // execute export
644     std::string newDbName = dbDir + "/" + MULTI_VER_COMMIT_DB_NAME;
645     int errCode = static_cast<SQLiteLocalKvDB *>(commitStorageDatabase_)->RunExportLogic(type, passwd, newDbName);
646     if (errCode != E_OK) {
647         LOGE("commit logs export failed:%d", errCode);
648     }
649     return errCode;
650 }
651 
TransferCommitIDToKey(const CommitID & commitID,Key & key)652 void MultiVerNaturalStoreCommitStorage::TransferCommitIDToKey(const CommitID &commitID, Key &key)
653 {
654     key = commitID;
655 }
656 
TransferCommitToValue(const IKvDBCommit & commit,Value & value)657 int MultiVerNaturalStoreCommitStorage::TransferCommitToValue(const IKvDBCommit &commit, Value &value)
658 {
659     // 3 uint64_t members.
660     uint32_t totalLength = Parcel::GetUInt64Len() * 3 + Parcel::GetVectorCharLen(commit.GetCommitId()) +
661         Parcel::GetVectorCharLen(commit.GetLeftParentId()) + Parcel::GetVectorCharLen(commit.GetRightParentId()) +
662         Parcel::GetStringLen(commit.GetDeviceInfo());
663     if (totalLength > MAX_COMMIT_ST_LENGTH) {
664         LOGE("The commit length is over the max threshold");
665         return -E_UNEXPECTED_DATA;
666     }
667 
668     value.resize(totalLength);
669     Parcel parcel(const_cast<uint8_t *>(value.data()), totalLength);
670 
671     int errCode = parcel.WriteUInt64(commit.GetTimestamp());
672     if (errCode != E_OK) {
673         return errCode;
674     }
675 
676     uint64_t localFlag = static_cast<uint32_t>((commit.GetLocalFlag() == true) ? 1 : 0);
677     errCode = parcel.WriteUInt64(localFlag);
678     if (errCode != E_OK) {
679         return errCode;
680     }
681 
682     errCode = parcel.WriteUInt64(commit.GetCommitVersion());
683     if (errCode != E_OK) {
684         return errCode;
685     }
686 
687     errCode = parcel.WriteVectorChar(commit.GetCommitId());
688     if (errCode != E_OK) {
689         return errCode;
690     }
691 
692     errCode = parcel.WriteVectorChar(commit.GetLeftParentId());
693     if (errCode != E_OK) {
694         return errCode;
695     }
696 
697     errCode = parcel.WriteVectorChar(commit.GetRightParentId());
698     if (errCode != E_OK) {
699         return errCode;
700     }
701 
702     return parcel.WriteString(commit.GetDeviceInfo());
703 }
704 
TransferValueToCommit(const Value & value,IKvDBCommit & commit)705 int MultiVerNaturalStoreCommitStorage::TransferValueToCommit(const Value &value, IKvDBCommit &commit)
706 {
707     size_t valueLength = value.size();
708     if (valueLength == 0 || valueLength >= MAX_COMMIT_ST_LENGTH) {
709         LOGE("Failed to transfer value to commit struct! invalid value length:%zu.", valueLength);
710         return -E_UNEXPECTED_DATA;
711     }
712 
713     Timestamp timestamp = 0;
714     uint64_t localFlag = 1;
715     Version versionInfo;
716 
717     CommitID commitID;
718     CommitID leftParentID;
719     CommitID rightParentID;
720     DeviceID deviceInfo;
721 
722     Parcel parcel(const_cast<uint8_t *>(value.data()), valueLength);
723     parcel.ReadUInt64(timestamp);
724     parcel.ReadUInt64(localFlag);
725     parcel.ReadUInt64(versionInfo);
726     parcel.ReadVectorChar(commitID);
727     parcel.ReadVectorChar(leftParentID);
728     parcel.ReadVectorChar(rightParentID);
729     parcel.ReadString(deviceInfo);
730     if (parcel.IsError()) {
731         return -E_PARSE_FAIL;
732     }
733 
734     // set commit value
735     commit.SetCommitVersion(versionInfo);
736     commit.SetCommitId(commitID);
737     commit.SetLeftParentId(leftParentID);
738     commit.SetRightParentId(rightParentID);
739     commit.SetTimestamp(timestamp);
740     commit.SetLocalFlag((localFlag == 1) ? true : false);
741     commit.SetDeviceInfo(deviceInfo);
742     return E_OK;
743 }
744 
TransferStringToKey(const string & str,Key & key)745 void MultiVerNaturalStoreCommitStorage::TransferStringToKey(const string &str, Key &key)
746 {
747     key.assign(str.begin(), str.end());
748 }
749 
TransferCommitIDToValue(const CommitID & commitID,Value & value)750 void MultiVerNaturalStoreCommitStorage::TransferCommitIDToValue(const CommitID &commitID, Value &value)
751 {
752     value = commitID;
753 }
754 
TransferValueToCommitID(const Value & value,CommitID & commitID)755 void MultiVerNaturalStoreCommitStorage::TransferValueToCommitID(const Value &value, CommitID &commitID)
756 {
757     commitID = value;
758 }
759 
CompareCommit(const IKvDBCommit * first,const IKvDBCommit * second)760 bool MultiVerNaturalStoreCommitStorage::CompareCommit(const IKvDBCommit *first,
761     const IKvDBCommit *second)
762 {
763     if (first == nullptr || second == nullptr) {
764         return false;
765     }
766     return first->GetCommitVersion() < second->GetCommitVersion();
767 }
768 
GetAllCommits(map<CommitID,IKvDBCommit * > & commits,CommitID & headerId) const769 int MultiVerNaturalStoreCommitStorage::GetAllCommits(map<CommitID, IKvDBCommit *> &commits,
770     CommitID &headerId) const
771 {
772     if (commitStorageDatabase_ == nullptr || commitStorageDBConnection_ == nullptr) {
773         LOGE("Failed to get all commits for uninitialized store");
774         return -E_INVALID_DB;
775     }
776     IOption option;
777     Key keyPrefix;
778     vector<Entry> entries;
779     int errCode = commitStorageDBConnection_->GetEntries(option, keyPrefix, entries);
780     if (errCode != E_OK) {
781         if (errCode == -E_NOT_FOUND) {
782             errCode = E_OK;
783         } else {
784             LOGE("Failed to get commit entries from DB:%d", errCode);
785         }
786 
787         return errCode;
788     }
789 
790     Key header;
791     TransferStringToKey(HEADER_KEY, header);
792 
793     for (const auto &entry : entries) {
794         if (entry.key == header) {
795             headerId = entry.value; // get the header.
796             continue;
797         }
798         IKvDBCommit *commit = new (std::nothrow) MultiVerCommit();
799         if (commit == nullptr) {
800             ReleaseUnusedCommits(commits);
801             LOGE("Failed to alloc commit! Bad alloc.");
802             return -E_OUT_OF_MEMORY;
803         }
804         errCode = TransferValueToCommit(entry.value, *commit);
805         if (errCode != E_OK) {
806             delete commit;
807             commit = nullptr;
808             ReleaseUnusedCommits(commits);
809             return errCode;
810         }
811         commits.insert(make_pair(commit->GetCommitId(), commit));
812     }
813     return E_OK;
814 }
815 
SetHeaderInner(const CommitID & commitId)816 int MultiVerNaturalStoreCommitStorage::SetHeaderInner(const CommitID &commitId)
817 {
818     Key key;
819     Value value;
820     TransferStringToKey(HEADER_KEY, key);
821     TransferCommitIDToValue(commitId, value);
822     IOption option;
823     int errCode = commitStorageDBConnection_->Put(option, key, value);
824     if (errCode != E_OK) {
825         LOGE("Failed to set header! err:%d", errCode);
826     }
827     return errCode;
828 }
829 
CheckAddedCommit(const IKvDBCommit & commitEntry) const830 int MultiVerNaturalStoreCommitStorage::CheckAddedCommit(const IKvDBCommit &commitEntry) const
831 {
832     if (commitStorageDatabase_ == nullptr || commitStorageDBConnection_ == nullptr) {
833         LOGE("Failed to get commit! Commit storage do not open.");
834         return -E_INVALID_DB;
835     }
836     // Parameter check
837     if (!((static_cast<const MultiVerCommit&>(commitEntry)).CheckCommit())) {
838         LOGE("Failed to add commit! Commit is invalid.");
839         return -E_UNEXPECTED_DATA;
840     }
841     int errCode = E_OK;
842     if (commitEntry.GetLeftParentId().size() != 0) {
843         if (!CommitExist(commitEntry.GetLeftParentId(), errCode)) {
844             LOGE("Failed to add commit! The left parent commit does not exist.");
845             return errCode;
846         }
847     }
848     if (commitEntry.GetRightParentId().size() != 0) {
849         if (!CommitExist(commitEntry.GetRightParentId(), errCode)) {
850             LOGE("Failed to add commit! The right parent commit does not exist.");
851             return errCode;
852         }
853     }
854 
855     return E_OK;
856 }
857 
RefreshCommitTree(std::list<IKvDBCommit * > & commits)858 void MultiVerNaturalStoreCommitStorage::RefreshCommitTree(std::list<IKvDBCommit *> &commits)
859 {
860     if (commits.empty()) {
861         return;
862     }
863     commits.sort(CompareCommit);
864 }
865 
GetMaxCommitVersion(int & errCode) const866 Version MultiVerNaturalStoreCommitStorage::GetMaxCommitVersion(int &errCode) const
867 {
868     std::map<CommitID, IKvDBCommit *> commits;
869     CommitID headerId;
870     errCode = GetAllCommits(commits, headerId);
871     if (errCode != E_OK || commits.empty()) { // means no commit or error.
872         return 0;
873     }
874 
875     Version maxVersion = 0;
876     for (const auto &item : commits) {
877         if (item.second != nullptr) {
878             Version itemVersion = item.second->GetCommitVersion();
879             maxVersion = (maxVersion < itemVersion) ? itemVersion : maxVersion;
880         }
881     }
882     ReleaseUnusedCommits(commits);
883     errCode = E_OK;
884     return maxVersion;
885 }
886 
BackupCurrentDatabase(const Property & property,const std::string & dir)887 int MultiVerNaturalStoreCommitStorage::BackupCurrentDatabase(const Property &property, const std::string &dir)
888 {
889     KvDBProperties dbProperties;
890     dbProperties.SetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, true);
891     dbProperties.SetStringProp(KvDBProperties::DATA_DIR, property.path);
892     dbProperties.SetStringProp(KvDBProperties::FILE_NAME, DBConstant::MULTI_VER_COMMIT_STORE);
893     dbProperties.SetStringProp(KvDBProperties::IDENTIFIER_DIR, property.identifierName);
894     dbProperties.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::MULTI_VER_TYPE_SQLITE);
895     dbProperties.SetPassword(property.cipherType, property.passwd);
896     int errCode = SQLiteLocalKvDB::BackupCurrentDatabase(dbProperties, dir);
897     return errCode;
898 }
899 
ImportDatabase(const Property & property,const std::string & dir,const CipherPassword & passwd)900 int MultiVerNaturalStoreCommitStorage::ImportDatabase(const Property &property, const std::string &dir,
901     const CipherPassword &passwd)
902 {
903     KvDBProperties dbProperties;
904     dbProperties.SetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, true);
905     dbProperties.SetStringProp(KvDBProperties::DATA_DIR, property.path);
906     dbProperties.SetStringProp(KvDBProperties::FILE_NAME, DBConstant::MULTI_VER_COMMIT_STORE);
907     dbProperties.SetStringProp(KvDBProperties::IDENTIFIER_DIR, property.identifierName);
908     dbProperties.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::MULTI_VER_TYPE_SQLITE);
909     dbProperties.SetPassword(property.cipherType, property.passwd);
910     int errCode = SQLiteLocalKvDB::ImportDatabase(dbProperties, dir, passwd);
911     return errCode;
912 }
913 }  // namespace DistributedDB
914 #endif
915