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