• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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