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 "preferences_impl.h"
17
18 #include <cinttypes>
19 #include <climits>
20 #include <cstdlib>
21 #include <functional>
22 #include <sstream>
23 #include <thread>
24
25 #include "adaptor.h"
26 #include "logger.h"
27 #include "preferences_errno.h"
28 #include "preferences_xml_utils.h"
29 #include "securec.h"
30 #include "task_executor.h"
31 #include "task_scheduler.h"
32
33 namespace OHOS {
34 namespace NativePreferences {
IsFileExist(const std::string & inputPath)35 static bool IsFileExist(const std::string &inputPath)
36 {
37 char path[PATH_MAX + 1] = { 0x00 };
38
39 if (strlen(inputPath.c_str()) > PATH_MAX || REALPATH(inputPath.c_str(), path, PATH_MAX) == nullptr) {
40 return false;
41 }
42 const char *pathString = path;
43 FILE *file = std::fopen(pathString, "r");
44 if (file != nullptr) {
45 std::fclose(file);
46 return true;
47 }
48 return false;
49 }
50
PreferencesImpl(const std::string & filePath)51 PreferencesImpl::PreferencesImpl(const std::string &filePath)
52 : loaded_(false), filePath_(filePath), backupPath_(MakeBackupPath(filePath_)),
53 brokenPath_(MakeBrokenPath(filePath_))
54 {
55 currentMemoryStateGeneration_ = 0;
56 diskStateGeneration_ = 0;
57 }
58
MakeBackupPath(const std::string & prefPath)59 std::string PreferencesImpl::MakeBackupPath(const std::string &prefPath)
60 {
61 std::string backupPath = prefPath;
62 backupPath += ".bak";
63 return backupPath;
64 }
65
MakeBrokenPath(const std::string & prefPath)66 std::string PreferencesImpl::MakeBrokenPath(const std::string &prefPath)
67 {
68 std::string brokenPath = prefPath;
69 brokenPath += ".bak";
70 return brokenPath;
71 }
72
~PreferencesImpl()73 PreferencesImpl::~PreferencesImpl()
74 {
75 }
76
Init()77 int PreferencesImpl::Init()
78 {
79 if (!StartLoadFromDisk()) {
80 return E_ERROR;
81 }
82 return E_OK;
83 }
84
StartLoadFromDisk()85 bool PreferencesImpl::StartLoadFromDisk()
86 {
87 {
88 std::lock_guard<std::mutex> lock(mutex_);
89 loaded_ = false;
90 }
91 TaskScheduler::Task task = std::bind(PreferencesImpl::LoadFromDisk, shared_from_this());
92 return TaskExecutor::GetInstance().Execute(std::move(task));
93 }
94
CheckKey(const std::string & key)95 int PreferencesImpl::CheckKey(const std::string &key)
96 {
97 if (key.empty()) {
98 LOG_ERROR("The key string is null or empty.");
99 return E_KEY_EMPTY;
100 }
101 if (Preferences::MAX_KEY_LENGTH < key.length()) {
102 LOG_ERROR("The key string length should shorter than 80.");
103 return E_KEY_EXCEED_MAX_LENGTH;
104 }
105 return E_OK;
106 }
107
108 /* static */
LoadFromDisk(std::shared_ptr<PreferencesImpl> pref)109 void PreferencesImpl::LoadFromDisk(std::shared_ptr<PreferencesImpl> pref)
110 {
111 std::lock_guard<std::mutex> lock(pref->mutex_);
112 if (pref->loaded_) {
113 return;
114 }
115
116 if (IsFileExist(pref->backupPath_)) {
117 if (std::remove(pref->filePath_.c_str())) {
118 LOG_ERROR("Couldn't delete file %{private}s when LoadFromDisk and backup exist.", pref->filePath_.c_str());
119 }
120 if (std::rename(pref->backupPath_.c_str(), pref->filePath_.c_str())) {
121 LOG_ERROR("Couldn't rename backup file %{private}s to file %{private}s,when LoadFromDisk and backup "
122 "exist.",
123 pref->backupPath_.c_str(), pref->filePath_.c_str());
124 } else {
125 PreferencesXmlUtils::LimitXmlPermission(pref->filePath_);
126 }
127 }
128
129 if (IsFileExist(pref->filePath_)) {
130 pref->ReadSettingXml(pref->filePath_, pref->map_);
131 }
132
133 pref->loaded_ = true;
134 pref->cond_.notify_all();
135 }
136
AwaitLoadFile()137 void PreferencesImpl::AwaitLoadFile()
138 {
139 std::unique_lock<std::mutex> lock(mutex_);
140 if (!loaded_) {
141 cond_.wait(lock, [this] { return loaded_; });
142 }
143 }
144
WriteToDiskFile(std::shared_ptr<PreferencesImpl> pref,std::shared_ptr<MemoryToDiskRequest> mcr)145 void PreferencesImpl::WriteToDiskFile(std::shared_ptr<PreferencesImpl> pref, std::shared_ptr<MemoryToDiskRequest> mcr)
146 {
147 if (IsFileExist(pref->filePath_)) {
148 bool needWrite = pref->CheckRequestValidForStateGeneration(*mcr);
149 if (!needWrite) {
150 mcr->SetDiskWriteResult(false, E_OK);
151 return;
152 }
153
154 if (IsFileExist(pref->backupPath_)) {
155 if (std::remove(pref->filePath_.c_str())) {
156 LOG_ERROR("Couldn't delete file %{private}s when writeToFile and backup exist.",
157 pref->filePath_.c_str());
158 }
159 } else {
160 if (std::rename(pref->filePath_.c_str(), pref->backupPath_.c_str())) {
161 LOG_ERROR("Couldn't rename file %{private}s to backup file %{private}s", pref->filePath_.c_str(),
162 pref->backupPath_.c_str());
163 mcr->SetDiskWriteResult(false, E_ERROR);
164 return;
165 } else {
166 PreferencesXmlUtils::LimitXmlPermission(pref->backupPath_);
167 }
168 }
169 }
170
171 if (pref->WriteSettingXml(pref->filePath_, mcr->writeToDiskMap_)) {
172 if (IsFileExist(pref->backupPath_) && std::remove(pref->backupPath_.c_str())) {
173 LOG_ERROR("Couldn't delete backup file %{private}s when writeToFile finish.", pref->backupPath_.c_str());
174 }
175 pref->diskStateGeneration_ = mcr->memoryStateGeneration_;
176 mcr->SetDiskWriteResult(true, E_OK);
177 } else {
178 /* Clean up an unsuccessfully written file */
179 if (IsFileExist(pref->filePath_)) {
180 if (std::remove(pref->filePath_.c_str())) {
181 LOG_ERROR("Couldn't clean up partially-written file %{private}s", pref->filePath_.c_str());
182 }
183 }
184 mcr->SetDiskWriteResult(false, E_ERROR);
185 }
186 }
187
CheckRequestValidForStateGeneration(const MemoryToDiskRequest & mcr)188 bool PreferencesImpl::CheckRequestValidForStateGeneration(const MemoryToDiskRequest &mcr)
189 {
190 bool valid = false;
191
192 if (diskStateGeneration_ < mcr.memoryStateGeneration_) {
193 if (mcr.isSyncRequest_) {
194 valid = true;
195 } else {
196 if (currentMemoryStateGeneration_ == mcr.memoryStateGeneration_) {
197 valid = true;
198 }
199 }
200 }
201 return valid;
202 }
203
Get(const std::string & key,const PreferencesValue & defValue)204 PreferencesValue PreferencesImpl::Get(const std::string &key, const PreferencesValue &defValue)
205 {
206 if (CheckKey(key) != E_OK) {
207 return defValue;
208 }
209
210 AwaitLoadFile();
211 std::lock_guard<std::mutex> lock(mutex_);
212 auto iter = map_.find(key);
213 if (iter != map_.end()) {
214 return iter->second;
215 }
216 return defValue;
217 }
218
GetAll()219 std::map<std::string, PreferencesValue> PreferencesImpl::GetAll()
220 {
221 AwaitLoadFile();
222
223 return map_;
224 }
225
ReadXmlArrayElement(const Element & element,std::map<std::string,PreferencesValue> & prefMap)226 void ReadXmlArrayElement(const Element &element, std::map<std::string, PreferencesValue> &prefMap)
227 {
228 if (element.tag_.compare("doubleArray") == 0) {
229 std::vector<double> values;
230 for (auto &child : element.children_) {
231 std::stringstream ss;
232 ss << child.value_;
233 double value = 0.0;
234 ss >> value;
235 values.push_back(value);
236 }
237 prefMap.insert(std::make_pair(element.key_, PreferencesValue(values)));
238 } else if (element.tag_.compare("stringArray") == 0) {
239 std::vector<std::string> values;
240 for (auto &child : element.children_) {
241 values.push_back(child.value_);
242 }
243 prefMap.insert(std::make_pair(element.key_, PreferencesValue(values)));
244 } else if (element.tag_.compare("boolArray") == 0) {
245 std::vector<bool> values;
246 for (auto &child : element.children_) {
247 std::stringstream ss;
248 ss << child.value_;
249 int32_t value = 0;
250 ss >> value;
251 values.push_back(value);
252 }
253 prefMap.insert(std::make_pair(element.key_, PreferencesValue(values)));
254 }
255 }
256
ReadXmlElement(const Element & element,std::map<std::string,PreferencesValue> & prefMap,const std::string & prefPath)257 void ReadXmlElement(
258 const Element &element, std::map<std::string, PreferencesValue> &prefMap, const std::string &prefPath)
259 {
260 if (element.tag_.compare("int") == 0) {
261 std::stringstream ss;
262 ss << element.value_;
263 int value = 0;
264 ss >> value;
265 prefMap.insert(std::make_pair(element.key_, PreferencesValue(value)));
266 } else if (element.tag_.compare("bool") == 0) {
267 bool value = (element.value_.compare("true") == 0) ? true : false;
268 prefMap.insert(std::make_pair(element.key_, PreferencesValue(value)));
269 } else if (element.tag_.compare("long") == 0) {
270 std::stringstream ss;
271 ss << element.value_;
272 int64_t value = 0;
273 ss >> value;
274 prefMap.insert(std::make_pair(element.key_, PreferencesValue(value)));
275 } else if (element.tag_.compare("float") == 0) {
276 std::stringstream ss;
277 ss << element.value_;
278 float value = 0.0;
279 ss >> value;
280 prefMap.insert(std::make_pair(element.key_, PreferencesValue(value)));
281 } else if (element.tag_.compare("double") == 0) {
282 std::stringstream ss;
283 ss << element.value_;
284 double value = 0.0;
285 ss >> value;
286 prefMap.insert(std::make_pair(element.key_, PreferencesValue(value)));
287 } else if (element.tag_.compare("string") == 0) {
288 prefMap.insert(std::make_pair(element.key_, PreferencesValue(element.value_)));
289 } else if (element.tag_.compare("doubleArray") == 0 || element.tag_.compare("stringArray") == 0
290 || element.tag_.compare("boolArray") == 0) {
291 ReadXmlArrayElement(element, prefMap);
292 } else {
293 LOG_WARN("ReadSettingXml:%{private}s, unknown element tag:%{public}s.", prefPath.c_str(), element.tag_.c_str());
294 }
295 }
296
ReadSettingXml(const std::string & prefPath,std::map<std::string,PreferencesValue> & prefMap)297 bool PreferencesImpl::ReadSettingXml(
298 const std::string &prefPath, std::map<std::string, PreferencesValue> &prefMap)
299 {
300 std::vector<Element> settings;
301 if (!PreferencesXmlUtils::ReadSettingXml(prefPath, settings)) {
302 LOG_ERROR("ReadSettingXml:%{private}s failed!", filePath_.c_str());
303 return false;
304 }
305
306 for (auto &element : settings) {
307 ReadXmlElement(element, prefMap, prefPath);
308 }
309 return true;
310 }
311
WriteXmlElement(Element & elem,const PreferencesValue & value,const std::string & filePath)312 void WriteXmlElement(Element &elem, const PreferencesValue &value, const std::string &filePath)
313 {
314 if (value.IsDoubleArray()) {
315 elem.tag_ = std::string("doubleArray");
316 auto values = (std::vector<double>)value;
317 for (double val : values) {
318 Element element;
319 element.tag_ = std::string("double");
320 element.value_ = std::to_string((double)val);
321 elem.children_.push_back(element);
322 }
323 } else if (value.IsBoolArray()) {
324 elem.tag_ = std::string("boolArray");
325 auto values = (std::vector<bool>)value;
326 for (bool val : values) {
327 Element element;
328 element.tag_ = std::string("bool");
329 std::string tmpVal = std::to_string((bool)val);
330 element.value_ = tmpVal == "1" ? "true" : "false";
331 elem.children_.push_back(element);
332 }
333 } else if (value.IsStringArray()) {
334 elem.tag_ = std::string("stringArray");
335 auto values = (std::vector<std::string>)value;
336 for (std::string &val : values) {
337 Element element;
338 element.tag_ = std::string("string");
339 element.value_ = val;
340 elem.children_.push_back(element);
341 }
342 } else if (value.IsInt()) {
343 elem.tag_ = std::string("int");
344 elem.value_ = std::to_string((int)value);
345 } else if (value.IsBool()) {
346 elem.tag_ = std::string("bool");
347 std::string tmpVal = std::to_string((bool)value);
348 elem.value_ = tmpVal == "1" ? "true" : "false";
349 } else if (value.IsLong()) {
350 elem.tag_ = std::string("long");
351 elem.value_ = std::to_string((int64_t)value);
352 } else if (value.IsFloat()) {
353 elem.tag_ = std::string("float");
354 elem.value_ = std::to_string((float)value);
355 } else if (value.IsDouble()) {
356 elem.tag_ = std::string("double");
357 elem.value_ = std::to_string((double)value);
358 } else if (value.IsString()) {
359 elem.tag_ = std::string("string");
360 elem.value_ = std::string(value);
361 } else {
362 LOG_WARN("WriteSettingXml:%{private}s, unknown element type.", filePath.c_str());
363 }
364 }
365
WriteSettingXml(const std::string & prefPath,const std::map<std::string,PreferencesValue> & prefMap)366 bool PreferencesImpl::WriteSettingXml(
367 const std::string &prefPath, const std::map<std::string, PreferencesValue> &prefMap)
368 {
369 std::vector<Element> settings;
370 for (auto it = prefMap.begin(); it != prefMap.end(); it++) {
371 Element elem;
372 elem.key_ = it->first;
373 PreferencesValue value = it->second;
374
375 WriteXmlElement(elem, value, filePath_);
376 settings.push_back(elem);
377 }
378
379 return PreferencesXmlUtils::WriteSettingXml(prefPath, settings);
380 }
381
HasKey(const std::string & key)382 bool PreferencesImpl::HasKey(const std::string &key)
383 {
384 if (CheckKey(key) != E_OK) {
385 return false;
386 }
387
388 AwaitLoadFile();
389
390 std::lock_guard<std::mutex> lock(mutex_);
391 return (map_.find(key) != map_.end());
392 }
393
RegisterObserver(std::shared_ptr<PreferencesObserver> preferencesObserver)394 void PreferencesImpl::RegisterObserver(std::shared_ptr<PreferencesObserver> preferencesObserver)
395 {
396 std::lock_guard<std::mutex> lock(mutex_);
397 std::weak_ptr<PreferencesObserver> weakPreferencesObserver = preferencesObserver;
398 preferencesObservers_.push_back(weakPreferencesObserver);
399 }
400
UnRegisterObserver(std::shared_ptr<PreferencesObserver> preferencesObserver)401 void PreferencesImpl::UnRegisterObserver(std::shared_ptr<PreferencesObserver> preferencesObserver)
402 {
403 std::lock_guard<std::mutex> lock(mutex_);
404 for (auto it = preferencesObservers_.begin(); it != preferencesObservers_.end(); ++it) {
405 std::weak_ptr<PreferencesObserver> weakPreferencesObserver = *it;
406 std::shared_ptr<PreferencesObserver> sharedPreferencesObserver = weakPreferencesObserver.lock();
407 if (!sharedPreferencesObserver || sharedPreferencesObserver == preferencesObserver) {
408 preferencesObservers_.erase(it);
409 break;
410 }
411 }
412 }
413
Put(const std::string & key,const PreferencesValue & value)414 int PreferencesImpl::Put(const std::string &key, const PreferencesValue &value)
415 {
416 int errCode = CheckKey(key);
417 if (errCode != E_OK) {
418 return errCode;
419 }
420 if (value.IsString()) {
421 errCode = CheckStringValue(value);
422 if (errCode != E_OK) {
423 LOG_ERROR("PreferencesImpl::Put string value length should shorter than 8*1024");
424 return errCode;
425 }
426 }
427
428 AwaitLoadFile();
429
430 std::lock_guard<std::mutex> lock(mutex_);
431
432 auto iter = map_.find(key);
433 if (iter != map_.end()) {
434 PreferencesValue &val = iter->second;
435 if (val == value) {
436 return E_OK;
437 }
438 }
439
440 map_.insert_or_assign(key, value);
441 modifiedKeys_.push_back(key);
442 return E_OK;
443 }
444
CheckStringValue(const std::string & value)445 int PreferencesImpl::CheckStringValue(const std::string &value)
446 {
447 if (Preferences::MAX_VALUE_LENGTH < value.length()) {
448 LOG_ERROR("The value string length should shorter than 8 * 1024.");
449 return E_VALUE_EXCEED_MAX_LENGTH;
450 }
451 return E_OK;
452 }
453
Delete(const std::string & key)454 int PreferencesImpl::Delete(const std::string &key)
455 {
456 int errCode = CheckKey(key);
457 if (errCode != E_OK) {
458 return errCode;
459 }
460
461 std::lock_guard<std::mutex> lock(mutex_);
462
463 auto pos = map_.find(key);
464 if (pos != map_.end()) {
465 map_.erase(pos);
466 modifiedKeys_.push_back(key);
467 }
468
469 return E_OK;
470 }
471
Clear()472 int PreferencesImpl::Clear()
473 {
474 std::lock_guard<std::mutex> lock(mutex_);
475
476 if (!map_.empty()) {
477 for (auto &kv : map_) {
478 modifiedKeys_.push_back(kv.first);
479 }
480 map_.clear();
481 }
482 return E_OK;
483 }
484
Flush()485 void PreferencesImpl::Flush()
486 {
487 DISTRIBUTED_DATA_HITRACE(std::string(__FUNCTION__));
488 std::shared_ptr<PreferencesImpl::MemoryToDiskRequest> request = commitToMemory();
489 request->isSyncRequest_ = false;
490 TaskScheduler::Task task = std::bind(PreferencesImpl::WriteToDiskFile, shared_from_this(), request);
491 TaskExecutor::GetInstance().Execute(std::move(task));
492
493 notifyPreferencesObserver(*request);
494 }
495
FlushSync()496 int PreferencesImpl::FlushSync()
497 {
498 std::shared_ptr<PreferencesImpl::MemoryToDiskRequest> request = commitToMemory();
499 request->isSyncRequest_ = true;
500 std::unique_lock<std::mutex> lock(request->reqMutex_);
501 PreferencesImpl::WriteToDiskFile(shared_from_this(), request);
502 if (request->wasWritten_) {
503 LOG_DEBUG("%{private}s:%{public}" PRId64 " written", filePath_.c_str(), request->memoryStateGeneration_);
504 }
505 notifyPreferencesObserver(*request);
506 return request->writeToDiskResult_;
507 }
508
commitToMemory()509 std::shared_ptr<PreferencesImpl::MemoryToDiskRequest> PreferencesImpl::commitToMemory()
510 {
511 std::lock_guard<std::mutex> lock(mutex_);
512 int64_t memoryStateGeneration = -1;
513 std::list<std::string> keysModified;
514 std::vector<std::weak_ptr<PreferencesObserver>> preferencesObservers;
515 std::map<std::string, PreferencesValue> writeToDiskMap;
516 writeToDiskMap = map_;
517
518 if (!modifiedKeys_.empty()) {
519 currentMemoryStateGeneration_++;
520 keysModified = modifiedKeys_;
521 modifiedKeys_.clear();
522 }
523 memoryStateGeneration = currentMemoryStateGeneration_;
524 preferencesObservers = preferencesObservers_;
525 return std::make_shared<MemoryToDiskRequest>(
526 writeToDiskMap, keysModified, preferencesObservers, memoryStateGeneration);
527 }
528
notifyPreferencesObserver(const PreferencesImpl::MemoryToDiskRequest & request)529 void PreferencesImpl::notifyPreferencesObserver(const PreferencesImpl::MemoryToDiskRequest &request)
530 {
531 if ((request.preferencesObservers_.empty()) || (request.keysModified_.empty())) {
532 return;
533 }
534 for (auto key = request.keysModified_.begin(); key != request.keysModified_.end(); ++key) {
535 for (auto it = request.preferencesObservers_.begin(); it != request.preferencesObservers_.end(); ++it) {
536 std::weak_ptr<PreferencesObserver> weakPreferencesObserver = *it;
537 if (std::shared_ptr<PreferencesObserver> sharedPreferencesObserver = weakPreferencesObserver.lock()) {
538 sharedPreferencesObserver->OnChange(*key);
539 }
540 }
541 }
542 }
543
MemoryToDiskRequest(const std::map<std::string,PreferencesValue> & writeToDiskMap,const std::list<std::string> & keysModified,const std::vector<std::weak_ptr<PreferencesObserver>> preferencesObservers,int64_t memStataGeneration)544 PreferencesImpl::MemoryToDiskRequest::MemoryToDiskRequest(
545 const std::map<std::string, PreferencesValue> &writeToDiskMap, const std::list<std::string> &keysModified,
546 const std::vector<std::weak_ptr<PreferencesObserver>> preferencesObservers, int64_t memStataGeneration)
547 {
548 writeToDiskMap_ = writeToDiskMap;
549 keysModified_ = keysModified;
550 preferencesObservers_ = preferencesObservers;
551 memoryStateGeneration_ = memStataGeneration;
552 isSyncRequest_ = false;
553 wasWritten_ = false;
554 writeToDiskResult_ = E_ERROR;
555 }
556
SetDiskWriteResult(bool wasWritten,int result)557 void PreferencesImpl::MemoryToDiskRequest::SetDiskWriteResult(bool wasWritten, int result)
558 {
559 writeToDiskResult_ = result;
560 wasWritten_ = wasWritten;
561 reqCond_.notify_one();
562 }
563 } // End of namespace NativePreferences
564 } // End of namespace OHOS
565