1 /*
2 * Copyright (c) 2023-2024 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 #define LOG_TAG "KvAdaptor"
16
17 #include <cstddef>
18 #include <filesystem>
19 #include <fstream>
20 #include <iostream>
21
22 #include "kv_delegate.h"
23
24 #include "datashare_errno.h"
25 #include "directory/directory_manager.h"
26 #include "grd_base/grd_error.h"
27 #include "grd_document/grd_document_api.h"
28 #include "ipc_skeleton.h"
29 #include "log_print.h"
30
31 namespace OHOS::DataShare {
32 constexpr int WAIT_TIME = 30;
33
34 // If using multi-process access, back up dataShare.db.map as well. Check config file to see whether
35 // using multi-process maccess
36 const char* g_backupFiles[] = {
37 "dataShare.db",
38 "dataShare.db.redo",
39 "dataShare.db.safe",
40 "dataShare.db.undo",
41 };
42 const char* BACKUP_SUFFIX = ".backup";
43
44 // If isBackUp is true, remove db backup files. Otherwise remove source db files.
RemoveDbFile(bool isBackUp)45 void KvDelegate::RemoveDbFile(bool isBackUp)
46 {
47 for (auto &fileName: g_backupFiles) {
48 std::string dbPath = path_ + "/" + fileName;
49 if (isBackUp) {
50 dbPath += BACKUP_SUFFIX;
51 }
52 if (std::filesystem::exists(dbPath)) {
53 std::error_code ec;
54 bool success = std::filesystem::remove(dbPath, ec);
55 if (!success) {
56 ZLOGE("failed to remove file %{public}s, err: %{public}s", fileName, ec.message().c_str());
57 }
58 }
59 }
60 }
61
CopyFile(bool isBackup)62 bool KvDelegate::CopyFile(bool isBackup)
63 {
64 std::filesystem::copy_options options = std::filesystem::copy_options::overwrite_existing;
65 std::error_code code;
66 bool ret = true;
67 for (auto &fileName : g_backupFiles) {
68 std::string src = path_ + "/" + fileName;
69 std::string dst = src;
70 isBackup ? dst.append(BACKUP_SUFFIX) : src.append(BACKUP_SUFFIX);
71 // If src doesn't exist, error will be returned through `std::error_code`
72 bool copyRet = std::filesystem::copy_file(src, dst, options, code);
73 if (!copyRet) {
74 ZLOGE("failed to copy file %{public}s, isBackup %{public}d, err: %{public}s",
75 fileName, isBackup, code.message().c_str());
76 ret = false;
77 RemoveDbFile(isBackup);
78 break;
79 }
80 }
81 return ret;
82 }
83
84 // Restore database data when it is broken somehow. Some failure of insertion / deletion / updates will be considered
85 // as database files damage, and therefore trigger the process of restoration.
Restore()86 void KvDelegate::Restore()
87 {
88 // No need to lock because this inner method will only be called when upper methods lock up
89 CopyFile(false);
90 ZLOGD("finish restoring kv");
91 }
92
93 // Backup database data by copying its key files. This mechanism might be costly, but acceptable when updating
94 // contents of KV database happens not so frequently now.
Backup()95 void KvDelegate::Backup()
96 {
97 // No need to lock because this inner method will only be called when upper methods lock up
98 ZLOGD("backup kv");
99 if (hasChange_) {
100 CopyFile(true);
101 hasChange_ = false;
102 }
103 ZLOGD("finish backing up kv");
104 }
105
106 // Set hasChange_ to true. Caller can use this to control when to back up db.
NotifyBackup()107 void OHOS::DataShare::KvDelegate::NotifyBackup()
108 {
109 std::lock_guard<decltype(mutex_)> lock(mutex_);
110 hasChange_ = true;
111 }
112
113 // The return val indicates whether the database has been restored
RestoreIfNeed(int32_t dbStatus)114 bool KvDelegate::RestoreIfNeed(int32_t dbStatus)
115 {
116 // No need to lock because this inner method will only be called when upper methods lock up
117 if (dbStatus == GRD_INVALID_FILE_FORMAT || dbStatus == GRD_REBUILD_DATABASE) {
118 if (db_ != NULL) {
119 GRD_DBClose(db_, GRD_DB_CLOSE);
120 db_ = nullptr;
121 isInitDone_ = false;
122 }
123 // If db is NULL, it has been closed.
124 Restore();
125 return true;
126 }
127 return false;
128 }
129
Upsert(const std::string & collectionName,const std::string & filter,const std::string & value)130 int64_t KvDelegate::Upsert(const std::string &collectionName, const std::string &filter, const std::string &value)
131 {
132 std::lock_guard<decltype(mutex_)> lock(mutex_);
133 if (!Init()) {
134 ZLOGE("init failed, %{public}s", collectionName.c_str());
135 return E_ERROR;
136 }
137 int count = GRD_UpsertDoc(db_, collectionName.c_str(), filter.c_str(), value.c_str(), 0);
138 if (count <= 0) {
139 ZLOGE("GRD_UpSertDoc failed,status %{public}d", count);
140 RestoreIfNeed(count);
141 return count;
142 }
143 Flush();
144 return E_OK;
145 }
146
Delete(const std::string & collectionName,const std::string & filter)147 int32_t KvDelegate::Delete(const std::string &collectionName, const std::string &filter)
148 {
149 std::lock_guard<decltype(mutex_)> lock(mutex_);
150 if (!Init()) {
151 ZLOGE("init failed, %{public}s", collectionName.c_str());
152 return E_ERROR;
153 }
154 std::vector<std::string> queryResults;
155
156 int32_t status = GetBatch(collectionName, filter, "{\"id_\": true}", queryResults);
157 if (status != E_OK) {
158 ZLOGE("db GetBatch failed, %{public}s %{public}d", filter.c_str(), status);
159 // `GetBatch` should decide whether to restore before errors are returned, so skip restoration here.
160 return status;
161 }
162 for (auto &result : queryResults) {
163 auto count = GRD_DeleteDoc(db_, collectionName.c_str(), result.c_str(), 0);
164 if (count < 0) {
165 ZLOGE("GRD_DeleteDoc failed,status %{public}d %{public}s", count, result.c_str());
166 if (RestoreIfNeed(count)) {
167 return count;
168 }
169 continue;
170 }
171 }
172 Flush();
173 if (queryResults.size() > 0) {
174 ZLOGI("Delete, %{public}s, count %{public}zu", collectionName.c_str(), queryResults.size());
175 }
176 return E_OK;
177 }
178
Init()179 bool KvDelegate::Init()
180 {
181 if (isInitDone_) {
182 if (executors_ != nullptr) {
183 executors_->Reset(taskId_, std::chrono::seconds(WAIT_TIME));
184 }
185 return true;
186 }
187 int status = GRD_DBOpen(
188 (path_ + "/dataShare.db").c_str(), nullptr, GRD_DB_OPEN_CREATE | GRD_DB_OPEN_CHECK_FOR_ABNORMAL, &db_);
189 if (status != GRD_OK || db_ == nullptr) {
190 ZLOGE("GRD_DBOpen failed,status %{public}d", status);
191 RestoreIfNeed(status);
192 return false;
193 }
194 if (executors_ != nullptr) {
195 taskId_ = executors_->Schedule(std::chrono::seconds(WAIT_TIME), [this]() {
196 std::lock_guard<decltype(mutex_)> lock(mutex_);
197 GRD_DBClose(db_, GRD_DB_CLOSE);
198 db_ = nullptr;
199 isInitDone_ = false;
200 taskId_ = ExecutorPool::INVALID_TASK_ID;
201 Backup();
202 });
203 }
204 status = GRD_CreateCollection(db_, TEMPLATE_TABLE, nullptr, 0);
205 if (status != GRD_OK) {
206 // If opeaning db succeeds, it is rare to fail to create tables
207 ZLOGE("GRD_CreateCollection template table failed,status %{public}d", status);
208 RestoreIfNeed(status);
209 return false;
210 }
211
212 status = GRD_CreateCollection(db_, DATA_TABLE, nullptr, 0);
213 if (status != GRD_OK) {
214 // If opeaning db succeeds, it is rare to fail to create tables
215 ZLOGE("GRD_CreateCollection data table failed,status %{public}d", status);
216 RestoreIfNeed(status);
217 return false;
218 }
219 isInitDone_ = true;
220 return true;
221 }
222
~KvDelegate()223 KvDelegate::~KvDelegate()
224 {
225 std::lock_guard<decltype(mutex_)> lock(mutex_);
226 if (isInitDone_) {
227 int status = GRD_DBClose(db_, 0);
228 if (status != GRD_OK) {
229 ZLOGE("GRD_DBClose failed,status %{public}d", status);
230 }
231 }
232 }
233
Upsert(const std::string & collectionName,const KvData & value)234 int32_t KvDelegate::Upsert(const std::string &collectionName, const KvData &value)
235 {
236 std::string id = value.GetId();
237 if (value.HasVersion() && value.GetVersion() != 0) {
238 int version = -1;
239 if (GetVersion(collectionName, id, version)) {
240 if (value.GetVersion() <= version) {
241 ZLOGE("GetVersion failed,%{public}s id %{private}s %{public}d %{public}d", collectionName.c_str(),
242 id.c_str(), value.GetVersion(), version);
243 return E_VERSION_NOT_NEWER;
244 }
245 }
246 }
247 return Upsert(collectionName, id, value.GetValue());
248 }
249
Get(const std::string & collectionName,const Id & id,std::string & value)250 int32_t KvDelegate::Get(const std::string &collectionName, const Id &id, std::string &value)
251 {
252 std::string filter = DistributedData::Serializable::Marshall(id);
253 if (Get(collectionName, filter, "{}", value) != E_OK) {
254 ZLOGE("Get failed, %{public}s %{public}s", collectionName.c_str(), filter.c_str());
255 return E_ERROR;
256 }
257 return E_OK;
258 }
259
GetVersion(const std::string & collectionName,const std::string & filter,int & version)260 bool KvDelegate::GetVersion(const std::string &collectionName, const std::string &filter, int &version)
261 {
262 std::string value;
263 if (Get(collectionName, filter, "{}", value) != E_OK) {
264 ZLOGE("Get failed, %{public}s %{public}s", collectionName.c_str(), filter.c_str());
265 return false;
266 }
267 VersionData data(-1);
268 if (!DistributedData::Serializable::Unmarshall(value, data)) {
269 ZLOGE("Unmarshall failed,data %{public}s", value.c_str());
270 return false;
271 }
272 version = data.GetVersion();
273 return true;
274 }
275
Get(const std::string & collectionName,const std::string & filter,const std::string & projection,std::string & result)276 int32_t KvDelegate::Get(
277 const std::string &collectionName, const std::string &filter, const std::string &projection, std::string &result)
278 {
279 std::lock_guard<decltype(mutex_)> lock(mutex_);
280 if (!Init()) {
281 ZLOGE("init failed, %{public}s", collectionName.c_str());
282 return E_ERROR;
283 }
284 Query query;
285 query.filter = filter.c_str();
286 query.projection = projection.c_str();
287 GRD_ResultSet *resultSet = nullptr;
288 int status = GRD_FindDoc(db_, collectionName.c_str(), query, 0, &resultSet);
289 if (status != GRD_OK || resultSet == nullptr) {
290 ZLOGE("GRD_FindDoc failed,status %{public}d", status);
291 RestoreIfNeed(status);
292 return status;
293 }
294 status = GRD_Next(resultSet);
295 if (status != GRD_OK) {
296 GRD_FreeResultSet(resultSet);
297 ZLOGE("GRD_Next failed,status %{public}d", status);
298 RestoreIfNeed(status);
299 return status;
300 }
301 char *value = nullptr;
302 status = GRD_GetValue(resultSet, &value);
303 if (status != GRD_OK || value == nullptr) {
304 GRD_FreeResultSet(resultSet);
305 ZLOGE("GRD_GetValue failed,status %{public}d", status);
306 RestoreIfNeed(status);
307 return status;
308 }
309 result = value;
310 GRD_FreeValue(value);
311 GRD_FreeResultSet(resultSet);
312 return E_OK;
313 }
314
Flush()315 void KvDelegate::Flush()
316 {
317 int status = GRD_Flush(db_, GRD_DB_FLUSH_ASYNC);
318 if (status != GRD_OK) {
319 ZLOGE("GRD_Flush failed,status %{public}d", status);
320 RestoreIfNeed(status);
321 }
322 }
323
GetBatch(const std::string & collectionName,const std::string & filter,const std::string & projection,std::vector<std::string> & result)324 int32_t KvDelegate::GetBatch(const std::string &collectionName, const std::string &filter,
325 const std::string &projection, std::vector<std::string> &result)
326 {
327 std::lock_guard<decltype(mutex_)> lock(mutex_);
328 if (!Init()) {
329 ZLOGE("init failed, %{public}s", collectionName.c_str());
330 return E_ERROR;
331 }
332 Query query;
333 query.filter = filter.c_str();
334 query.projection = projection.c_str();
335 GRD_ResultSet *resultSet;
336 int status = GRD_FindDoc(db_, collectionName.c_str(), query, GRD_DOC_ID_DISPLAY, &resultSet);
337 if (status != GRD_OK || resultSet == nullptr) {
338 ZLOGE("GRD_FindDoc failed,status %{public}d", status);
339 RestoreIfNeed(status);
340 return status;
341 }
342 char *value = nullptr;
343 while (GRD_Next(resultSet) == GRD_OK) {
344 status = GRD_GetValue(resultSet, &value);
345 if (status != GRD_OK || value == nullptr) {
346 GRD_FreeResultSet(resultSet);
347 ZLOGE("GRD_GetValue failed,status %{public}d", status);
348 RestoreIfNeed(status);
349 return status;
350 }
351 result.emplace_back(value);
352 GRD_FreeValue(value);
353 }
354 GRD_FreeResultSet(resultSet);
355 return E_OK;
356 }
357
KvDelegate(const std::string & path,const std::shared_ptr<ExecutorPool> & executors)358 KvDelegate::KvDelegate(const std::string &path, const std::shared_ptr<ExecutorPool> &executors)
359 : path_(path), executors_(executors)
360 {
361 }
362 } // namespace OHOS::DataShare