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 "intercepted_data_impl.h"
17 #include "db_common.h"
18 #include "db_constant.h"
19 #include "generic_single_ver_kv_entry.h"
20 #include "parcel.h"
21 #include "version.h"
22 
23 namespace DistributedDB {
24 namespace {
CheckKey(const Key & key)25 bool CheckKey(const Key &key)
26 {
27     if (key.empty() || key.size() > DBConstant::MAX_KEY_SIZE) {
28         LOGE("Key is too large:%zu.", key.size());
29         return false;
30     }
31     return true;
32 }
33 
CheckValue(const Value & value,const std::function<int (const Value &)> & checkSchema)34 bool CheckValue(const Value &value, const std::function<int(const Value &)> &checkSchema)
35 {
36     if (value.size() > DBConstant::MAX_VALUE_SIZE) {
37         LOGE("Value is too large:%zu.", value.size());
38         return false;
39     }
40 
41     if (checkSchema == nullptr) {
42         LOGE("Check schema failed, no check func.");
43         return false;
44     }
45 
46     int errCode = checkSchema(value);
47     if (errCode != E_OK) {
48         LOGE("Check schema failed, value is invalid:%d.", errCode);
49         return false;
50     }
51     return true;
52 }
53 
CheckLength(size_t len,size_t maxPacketSize)54 bool CheckLength(size_t len, size_t maxPacketSize)
55 {
56     if (len > maxPacketSize) {
57         LOGE("Packet is too large:%zu.", len);
58         return false;
59     }
60     return true;
61 }
62 } // anonymous namespace
63 
InterceptedDataImpl(std::vector<SingleVerKvEntry * > dataItems,const std::function<int (const Value &)> & checkSchema)64 InterceptedDataImpl::InterceptedDataImpl(std::vector<SingleVerKvEntry *> dataItems,
65     const std::function<int(const Value&)> &checkSchema)
66     : kvEntriesReady_(false),
67       isError_(false),
68       totalLength_(),
69       maxPacketSize_(),
70       checkSchema_(checkSchema),
71       dataItems_(dataItems),
72       kvEntries_(),
73       indexes_()
74 {
75     totalLength_ = GenericSingleVerKvEntry::CalculateLens(dataItems, SOFTWARE_VERSION_CURRENT);
76     // New packet cannot exceed both twice the MTU and twice the original size.
77     // Besides, it cannot exceed 30 MB.
78     maxPacketSize_ = std::min(DBConstant::MAX_SYNC_BLOCK_SIZE,
79         std::max(totalLength_, static_cast<size_t>(DBConstant::MAX_MTU_SIZE)) * 2); // 2 times MAX_MTU size
80 }
81 
~InterceptedDataImpl()82 InterceptedDataImpl::~InterceptedDataImpl()
83 {}
84 
GetEntries()85 std::vector<KVEntry> InterceptedDataImpl::GetEntries()
86 {
87     if (!kvEntriesReady_) {
88         GetKvEntries();
89     }
90     return kvEntries_;
91 }
92 
CheckIndex(size_t index)93 bool InterceptedDataImpl::CheckIndex(size_t index)
94 {
95     if (!kvEntriesReady_) {
96         GetKvEntries();
97     }
98 
99     if (index >= kvEntries_.size()) {
100         LOGE("Index is too large:%zu, size:%zu.", index, kvEntries_.size());
101         return false;
102     }
103     return true;
104 }
105 
ModifyKey(size_t index,const Key & newKey)106 DBStatus InterceptedDataImpl::ModifyKey(size_t index, const Key &newKey)
107 {
108     // Check index.
109     if (!CheckIndex(index)) {
110         isError_ = true;
111         return INVALID_ARGS;
112     }
113 
114     // Check key.
115     if (!CheckKey(newKey)) {
116         isError_ = true;
117         return INVALID_ARGS;
118     }
119 
120     // Check length.
121     const auto &oldKey = dataItems_[indexes_[index]]->GetKey();
122     size_t newLength = totalLength_ - Parcel::GetVectorCharLen(oldKey) + Parcel::GetVectorCharLen(newKey);
123     if (!CheckLength(newLength, maxPacketSize_)) {
124         isError_ = true;
125         return INVALID_ARGS;
126     }
127     totalLength_ = newLength;
128 
129     // Modify data
130     auto entry = dataItems_[indexes_[index]];
131     entry->SetKey(newKey);
132     Key hashKey;
133     int errCode = DBCommon::CalcValueHash(newKey, hashKey);
134     if (errCode != E_OK) {
135         LOGE("Calc hashkey failed.");
136         isError_ = true;
137         return INVALID_ARGS;
138     }
139     entry->SetHashKey(hashKey);
140     return OK;
141 }
142 
ModifyValue(size_t index,const Value & newValue)143 DBStatus InterceptedDataImpl::ModifyValue(size_t index, const Value &newValue)
144 {
145     // Check index.
146     if (!CheckIndex(index)) {
147         isError_ = true;
148         return INVALID_ARGS;
149     }
150 
151     // Check value.
152     if (!CheckValue(newValue, checkSchema_)) {
153         isError_ = true;
154         return INVALID_ARGS;
155     }
156 
157     // Check length.
158     const auto &oldValue = dataItems_[indexes_[index]]->GetValue();
159     size_t newLength = totalLength_ - Parcel::GetVectorCharLen(oldValue) + Parcel::GetVectorCharLen(newValue);
160     if (!CheckLength(newLength, maxPacketSize_)) {
161         isError_ = true;
162         return INVALID_ARGS;
163     }
164     totalLength_ = newLength;
165 
166     // Modify data
167     auto entry = dataItems_[indexes_[index]];
168     entry->SetValue(newValue);
169     return OK;
170 }
171 
IsError() const172 bool InterceptedDataImpl::IsError() const
173 {
174     return isError_;
175 }
176 
GetKvEntries()177 void InterceptedDataImpl::GetKvEntries()
178 {
179     for (size_t i = 0; i < dataItems_.size(); ++i) {
180         const auto &kvEntry = dataItems_[i];
181         if (kvEntry == nullptr) {
182             continue;
183         }
184         if ((kvEntry->GetFlag() & DataItem::DELETE_FLAG) == 0) { // For deleted data, do not modify.
185             kvEntries_.push_back({ kvEntry->GetKey(), kvEntry->GetValue() });
186             indexes_.push_back(i);
187         }
188     }
189     kvEntriesReady_ = true;
190 }
191 } // namespace DistributedDB
192 
193