• 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 
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