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