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