• 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_xml_utils.h"
17 #include "base64_helper.h"
18 #include <sys/stat.h>
19 
20 #include <cerrno>
21 #include <cstring>
22 #include <sstream>
23 
24 #include "libxml/parser.h"
25 #include <libxml/xmlwriter.h>
26 #include "log_print.h"
27 #include "preferences_dfx_adapter.h"
28 #include "preferences_file_lock.h"
29 #include "preferences_file_operation.h"
30 #include "preferences_utils.h"
31 
32 namespace OHOS {
33 namespace NativePreferences {
34 constexpr int TOO_MANY_OPEN_FILES = 24;
35 constexpr int PRE_ALLOCATE_BUFFER_SIZE = 128;
36 constexpr int NO_SPACE_LEFT_ON_DEVICE = 28;
37 constexpr int DISK_QUOTA_EXCEEDED = 122;
38 constexpr int REQUIRED_KEY_NOT_AVAILABLE = 126;
39 constexpr int REQUIRED_KEY_REVOKED = 128;
40 
41 constexpr const char *TAG_PREFERENCES = "preferences";
42 constexpr const char *TAG_VERSION = "version";
43 constexpr const char *VERSION_VALUE = "1.0";
44 constexpr const char *ATTR_KEY = "key";
45 constexpr const char *ATTR_VALUE = "value";
46 
47 static bool ParseNodeElement(const xmlNode *node, Element &element);
48 static bool ParsePrimitiveNodeElement(const xmlNode *node, Element &element);
49 static bool ParseStringNodeElement(const xmlNode *node, Element &element);
50 static bool ParseArrayNodeElement(const xmlNode *node, Element &element);
51 
52 struct ValueVisitor {
53     std::string_view typeTag;
54     std::string valueStr;
55     std::string_view childrenTag;
56     std::vector<std::string> arrayValues;
57 
operator ()OHOS::NativePreferences::ValueVisitor58     void operator()(int val)
59     {
60         typeTag = "int";
61         valueStr = std::to_string(val);
62     }
63 
operator ()OHOS::NativePreferences::ValueVisitor64     void operator()(bool val)
65     {
66         typeTag = "bool";
67         valueStr = val ? "true" : "false";
68     }
69 
operator ()OHOS::NativePreferences::ValueVisitor70     void operator()(int64_t val)
71     {
72         typeTag = "long";
73         valueStr = std::to_string(val);
74     }
75 
operator ()OHOS::NativePreferences::ValueVisitor76     void operator()(uint64_t val)
77     {
78         typeTag = "uint64_t";
79         valueStr = std::to_string(val);
80     }
81 
operator ()OHOS::NativePreferences::ValueVisitor82     void operator()(float val)
83     {
84         typeTag = "float";
85         valueStr = std::to_string(val);
86     }
87 
operator ()OHOS::NativePreferences::ValueVisitor88     void operator()(double val)
89     {
90         typeTag = "double";
91         valueStr = std::to_string(val);
92     }
93 
operator ()OHOS::NativePreferences::ValueVisitor94     void operator()(const std::string& val)
95     {
96         typeTag = "string";
97         valueStr = val;
98     }
99 
operator ()OHOS::NativePreferences::ValueVisitor100     void operator()(const std::vector<std::string>& val)
101     {
102         typeTag = "stringArray";
103         childrenTag = "string";
104         arrayValues.reserve(val.size());
105         for (const auto& s : val) {
106             arrayValues.push_back(s);
107         }
108     }
109 
operator ()OHOS::NativePreferences::ValueVisitor110     void operator()(const std::vector<double>& val)
111     {
112         typeTag = "doubleArray";
113         childrenTag = "double";
114         arrayValues.reserve(val.size());
115         for (const auto& d : val) {
116             arrayValues.push_back(std::to_string(d));
117         }
118     }
119 
operator ()OHOS::NativePreferences::ValueVisitor120     void operator()(const std::vector<bool>& val)
121     {
122         typeTag = "boolArray";
123         childrenTag = "bool";
124         arrayValues.reserve(val.size());
125         for (const auto& b : val) {
126             arrayValues.push_back(b ? "true" : "false");
127         }
128     }
129 
operator ()OHOS::NativePreferences::ValueVisitor130     void operator()(const std::vector<uint8_t>& val)
131     {
132         typeTag = "uint8Array";
133         valueStr = Base64Helper::Encode(val);
134     }
135 
operator ()OHOS::NativePreferences::ValueVisitor136     void operator()(const Object& val)
137     {
138         typeTag = "object";
139         valueStr = val.valueStr;
140     }
141 
operator ()OHOS::NativePreferences::ValueVisitor142     void operator()(const BigInt& val)
143     {
144         typeTag = "BigInt";
145         childrenTag = "uint64_t";
146         arrayValues.reserve(val.words_.size() + 1);
147         for (const auto& word : val.words_) {
148             arrayValues.push_back(std::to_string(word));
149         }
150         arrayValues.push_back(std::to_string(static_cast<uint64_t>(val.sign_)));
151     }
152 
operator ()OHOS::NativePreferences::ValueVisitor153     void operator()(const std::monostate&)
154     {
155         typeTag = "unknown";
156         LOG_ERROR("Encountered unknown type in PreferencesValue");
157     }
158 };
159 
160 class XmlWriterWrapper {
161 public:
XmlWriterWrapper(xmlTextWriterPtr writer)162     explicit XmlWriterWrapper(xmlTextWriterPtr writer) : writer_(writer) {}
~XmlWriterWrapper()163     ~XmlWriterWrapper()
164     {
165         if (writer_) {
166             xmlFreeTextWriter(writer_);
167         }
168     }
169 
get() const170     xmlTextWriterPtr get() const
171     {
172         return writer_;
173     }
174 private:
175     xmlTextWriterPtr writer_;
176 };
177 
178 class XmlBufferWrapper {
179 public:
XmlBufferWrapper(xmlBufferPtr buffer)180     explicit XmlBufferWrapper(xmlBufferPtr buffer) : buffer_(buffer) {}
~XmlBufferWrapper()181     ~XmlBufferWrapper()
182     {
183         if (buffer_) {
184             xmlBufferFree(buffer_);
185         }
186     }
187 
get() const188     xmlBufferPtr get() const
189     {
190         return buffer_;
191     }
192 private:
193     xmlBufferPtr buffer_;
194 };
195 
196 #define XML_CHECK(expr, msg)                                                                        \
197     do {                                                                                            \
198         if (!(expr)) {                                                                              \
199             const xmlError *xmlErr = xmlGetLastError();                                                 \
200             LOG_ERROR("%{public}s. Error: %{public}s", msg, xmlErr ? xmlErr->message : "Unknown");  \
201             return false;                                                                           \
202         }                                                                                           \
203     } while (0)
204 
205 template<typename T>
GetTypeName()206 std::string GetTypeName()
207 {
208     return "unknown";
209 }
210 
211 template<>
GetTypeName()212 std::string GetTypeName<int>()
213 {
214     return "int";
215 }
216 
217 template<>
GetTypeName()218 std::string GetTypeName<bool>()
219 {
220     return "bool";
221 }
222 
223 template<>
GetTypeName()224 std::string GetTypeName<int64_t>()
225 {
226     return "long";
227 }
228 
229 template<>
GetTypeName()230 std::string GetTypeName<uint64_t>()
231 {
232     return "uint64_t";
233 }
234 
235 template<>
GetTypeName()236 std::string GetTypeName<float>()
237 {
238     return "float";
239 }
240 
241 template<>
GetTypeName()242 std::string GetTypeName<double>()
243 {
244     return "double";
245 }
246 
247 template<>
GetTypeName()248 std::string GetTypeName<std::string>()
249 {
250     return "string";
251 }
252 
253 template<>
GetTypeName()254 std::string GetTypeName<std::vector<std::string>>()
255 {
256     return "stringArray";
257 }
258 
259 template<>
GetTypeName()260 std::string GetTypeName<std::vector<double>>()
261 {
262     return "doubleArray";
263 }
264 
265 template<>
GetTypeName()266 std::string GetTypeName<std::vector<bool>>()
267 {
268     return "boolArray";
269 }
270 
271 template<>
GetTypeName()272 std::string GetTypeName<std::vector<uint8_t>>()
273 {
274     return "uint8Array";
275 }
276 
277 template<>
GetTypeName()278 std::string GetTypeName<Object>()
279 {
280     return "object";
281 }
282 
283 template<>
GetTypeName()284 std::string GetTypeName<BigInt>()
285 {
286     return "BigInt";
287 }
288 
289 template<typename T>
Convert2PrefValue(const Element & element,T & value)290 static void Convert2PrefValue(const Element &element, T &value)
291 {
292     if constexpr (std::is_same<T, std::string>::value) {
293         value = element.value_;
294     } else if constexpr (std::is_same<T, bool>::value) {
295         value = (element.value_.compare("true") == 0) ? true : false;
296     } else if constexpr (std::is_same<T, std::monostate>::value) {
297         value = std::monostate();
298     } else {
299         std::stringstream ss;
300         ss << element.value_;
301         ss >> value;
302     }
303 }
304 
305 template<typename T>
Convert2PrefValue(const Element & element,std::vector<T> & values)306 static void Convert2PrefValue(const Element &element, std::vector<T> &values)
307 {
308     for (const auto &child : element.children_) {
309         T value;
310         Convert2PrefValue(child, value);
311         values.push_back(value);
312     }
313 }
314 
Convert2PrefValue(const Element & element,BigInt & value)315 static void Convert2PrefValue(const Element &element, BigInt &value)
316 {
317     for (const auto &child : element.children_) {
318         uint64_t val;
319         Convert2PrefValue(child, val);
320         value.words_.push_back(val);
321     }
322     value.sign_ = 0;
323     if (!value.words_.empty()) {
324         value.sign_ = static_cast<int>(value.words_[value.words_.size() - 1]);
325         value.words_.pop_back();
326     }
327 }
328 
329 template<typename T>
GetPrefValue(const Element & element,T & value)330 bool GetPrefValue(const Element &element, T &value)
331 {
332     LOG_WARN("unknown element type. the key is %{public}s", Anonymous::ToBeAnonymous(element.key_).c_str());
333     return false;
334 }
335 
Convert2PrefValue(const Element & element,std::vector<uint8_t> & value)336 static void Convert2PrefValue(const Element &element, std::vector<uint8_t> &value)
337 {
338     if (!Base64Helper::Decode(element.value_, value)) {
339         value.clear();
340     }
341 }
342 
Convert2PrefValue(const Element & element,Object & value)343 static void Convert2PrefValue(const Element &element, Object &value)
344 {
345     value.valueStr = element.value_;
346 }
347 
348 template<typename T, typename First, typename... Types>
GetPrefValue(const Element & element,T & value)349 bool GetPrefValue(const Element &element, T &value)
350 {
351     if (element.tag_ == GetTypeName<First>()) {
352         First val;
353         Convert2PrefValue(element, val);
354         value = val;
355         return true;
356     }
357     return GetPrefValue<T, Types...>(element, value);
358 }
359 
360 template<typename... Types>
Convert2PrefValue(const Element & element,std::variant<Types...> & value)361 bool Convert2PrefValue(const Element &element, std::variant<Types...> &value)
362 {
363     return GetPrefValue<decltype(value), Types...>(element, value);
364 }
365 
ReadXmlElement(const Element & element,std::unordered_map<std::string,PreferencesValue> & prefConMap)366 void ReadXmlElement(const Element &element, std::unordered_map<std::string, PreferencesValue> &prefConMap)
367 {
368     PreferencesValue value(static_cast<int64_t>(0));
369     if (Convert2PrefValue(element, value.value_)) {
370         prefConMap.insert({element.key_, value});
371     }
372 }
373 
IsFileExist(const std::string & inputPath)374 static bool IsFileExist(const std::string &inputPath)
375 {
376     if (inputPath.length() > PATH_MAX) {
377         return false;
378     }
379     struct stat buffer;
380     return (stat(inputPath.c_str(), &buffer) == 0);
381 }
382 
RemoveBackupFile(const std::string & fileName)383 static void RemoveBackupFile(const std::string &fileName)
384 {
385     std::string backupFileName = PreferencesUtils::MakeFilePath(fileName, PreferencesUtils::STR_BACKUP);
386     if (IsFileExist(backupFileName) && std::remove(backupFileName.c_str())) {
387         LOG_WARN("failed to delete backup file %{public}d.", errno);
388     }
389 }
390 
ReadFile(const std::string & fileName,int & errCode)391 static xmlDoc *ReadFile(const std::string &fileName, int &errCode)
392 {
393     xmlDoc *doc = xmlReadFile(fileName.c_str(), "UTF-8", XML_PARSE_NOBLANKS | XML_PARSE_HUGE);
394     errCode = errno;
395     return doc;
396 }
397 
ReportXmlFileCorrupted(const std::string & fileName,const std::string & bundleName,const std::string & operationMsg,int errCode)398 static void ReportXmlFileCorrupted(const std::string &fileName, const std::string &bundleName,
399     const std::string &operationMsg, int errCode)
400 {
401     ReportParam reportParam = { bundleName, NORMAL_DB, ExtractFileName(fileName),
402         E_ERROR, errCode, operationMsg };
403     PreferencesDfxManager::Report(reportParam, EVENT_NAME_DB_CORRUPTED);
404     ReportParam succreportParam = reportParam;
405     succreportParam.errCode = E_OK;
406     succreportParam.errnoCode = 0;
407     succreportParam.appendix = "restore success";
408     PreferencesDfxManager::Report(succreportParam, EVENT_NAME_DB_CORRUPTED);
409 }
410 
ReportNonCorruptError(const std::string & faultType,const std::string & fileName,const std::string & bundleName,int errCode)411 static bool ReportNonCorruptError(
412     const std::string &faultType, const std::string &fileName, const std::string &bundleName, int errCode)
413 {
414     if (errCode == REQUIRED_KEY_NOT_AVAILABLE || errCode == REQUIRED_KEY_REVOKED) {
415         ReportFaultParam reportParam = { faultType, bundleName, NORMAL_DB, ExtractFileName(fileName),
416             E_OPERAT_IS_LOCKED, faultType + " the screen is locked." };
417         PreferencesDfxManager::ReportAbnormalOperation(reportParam, ReportedFaultBitMap::USE_WHEN_SCREEN_LOCKED);
418         return true;
419     }
420     if (errCode == NO_SPACE_LEFT_ON_DEVICE || errCode == DISK_QUOTA_EXCEEDED || errCode == TOO_MANY_OPEN_FILES) {
421         ReportFaultParam param = { faultType, bundleName, NORMAL_DB, ExtractFileName(fileName),
422             E_ERROR, faultType + " " + std::strerror(errCode)};
423         PreferencesDfxManager::ReportFault(param);
424         return true;
425     }
426     return false;
427 }
428 
RenameFromBackupFile(const std::string & fileName,const std::string & bundleName,bool & isReportCorrupt,bool & isBakFileExist)429 static bool RenameFromBackupFile(
430     const std::string &fileName, const std::string &bundleName, bool &isReportCorrupt, bool &isBakFileExist)
431 {
432     std::string backupFileName = PreferencesUtils::MakeFilePath(fileName, PreferencesUtils::STR_BACKUP);
433     if (!IsFileExist(backupFileName)) {
434         isBakFileExist = false;
435         LOG_DEBUG("the backup file does not exist.");
436         return false;
437     }
438     isBakFileExist = true;
439     xmlResetLastError();
440     int errCode = 0;
441     auto bakDoc = std::shared_ptr<xmlDoc>(ReadFile(backupFileName, errCode),
442         [](xmlDoc *bakDoc) { xmlFreeDoc(bakDoc); });
443     if (bakDoc == nullptr) {
444         const xmlError *xmlErr = xmlGetLastError();
445         std::string errMessage = (xmlErr != nullptr) ? xmlErr->message : "null";
446         LOG_ERROR("%{public}s restore failed, errno:%{public}d, error:%{public}s.",
447             ExtractFileName(fileName).c_str(), errCode, errMessage.c_str());
448         std::remove(backupFileName.c_str());
449         if (ReportNonCorruptError("read bak failed", fileName, bundleName, errCode)) {
450             return false;
451         }
452         isReportCorrupt = true;
453         return false;
454     }
455     if (std::rename(backupFileName.c_str(), fileName.c_str())) {
456         LOG_ERROR("failed to restore backup errno %{public}d.", errno);
457         return false;
458     }
459     isReportCorrupt = false;
460     struct stat fileStats;
461     if (stat(fileName.c_str(), &fileStats) == -1) {
462         LOG_ERROR("failed to stat backup file.");
463     }
464     std::string appindex = "Restored from the backup. The file size is " + std::to_string(fileStats.st_size) + ".";
465     ReportFaultParam reportParam = { "read failed", bundleName, NORMAL_DB, ExtractFileName(fileName),
466         E_XML_RESTORED_FROM_BACKUP_FILE, appindex };
467     PreferencesDfxManager::ReportAbnormalOperation(reportParam, ReportedFaultBitMap::RESTORE_FROM_BAK);
468     LOG_INFO("%{public}s restored", ExtractFileName(fileName).c_str());
469     return true;
470 }
471 
RenameFile(const std::string & fileName,const std::string & fileType)472 static bool RenameFile(const std::string &fileName, const std::string &fileType)
473 {
474     std::string name = PreferencesUtils::MakeFilePath(fileName, fileType);
475     if (std::rename(fileName.c_str(), name.c_str())) {
476         LOG_ERROR("failed to rename to %{public}s, err:%{public}d.", fileType.c_str(), errno);
477         return false;
478     }
479     return true;
480 }
481 
RenameToBackupFile(const std::string & fileName)482 static bool RenameToBackupFile(const std::string &fileName)
483 {
484     return RenameFile(fileName, PreferencesUtils::STR_BACKUP);
485 }
486 
RenameToBrokenFile(const std::string & fileName)487 static bool RenameToBrokenFile(const std::string &fileName)
488 {
489     return RenameFile(fileName, PreferencesUtils::STR_BROKEN);
490 }
491 
XmlReadFile(const std::string & fileName,const std::string & bundleName)492 static xmlDoc *XmlReadFile(const std::string &fileName, const std::string &bundleName)
493 {
494     xmlDoc *doc = nullptr;
495     bool isReport = false;
496     bool isMultiProcessing = false;
497     PreferencesFileLock fileLock(fileName);
498     fileLock.ReadLock(isMultiProcessing);
499     int errCode = 0;
500     std::string errMessage;
501     if (IsFileExist(fileName)) {
502         LOG_INFO("file:%{public}s, m:%{public}d.", ExtractFileName(fileName).c_str(), isMultiProcessing);
503         doc = ReadFile(fileName, errCode);
504         if (doc != nullptr) {
505             return doc;
506         }
507         const xmlError *xmlErr = xmlGetLastError();
508         errMessage = (xmlErr != nullptr) ? xmlErr->message : "null";
509         LOG_ERROR("failed to read:%{public}s, errno:%{public}d, error:%{public}s.",
510             ExtractFileName(fileName).c_str(), errCode, errMessage.c_str());
511         if (ReportNonCorruptError("read failed", fileName, bundleName, errCode)) {
512             return nullptr;
513         }
514         if (!RenameToBrokenFile(fileName)) {
515             return doc;
516         }
517         isReport = true;
518     }
519 
520     bool isExist = true;
521     if (RenameFromBackupFile(fileName, bundleName, isReport, isExist)) {
522         int bakErrCode = 0;
523         doc = ReadFile(fileName, bakErrCode);
524         const xmlError *xmlErr = xmlGetLastError();
525         std::string message = (xmlErr != nullptr) ? xmlErr->message : "null";
526         errMessage.append(" bak: errno is " + std::to_string(bakErrCode) + ", errMessage is " + message);
527     }
528     if (isMultiProcessing) {
529         ReportFaultParam param = { "read failed", bundleName, NORMAL_DB, ExtractFileName(fileName),
530             E_OPERAT_IS_CROSS_PROESS, "Cross-process operations." };
531         PreferencesDfxManager::ReportFault(param);
532         return doc;
533     }
534     if (isReport) {
535         ReportFaultParam param = { "read failed", bundleName, NORMAL_DB, ExtractFileName(fileName),
536             E_ERROR, "read failed, " + errMessage};
537         isExist ? ReportXmlFileCorrupted(fileName, bundleName, errMessage, errCode) :
538             PreferencesDfxManager::ReportFault(param);
539     }
540     return doc;
541 }
542 
543 /* static */
ReadSettingXml(const std::string & fileName,const std::string & bundleName,std::unordered_map<std::string,PreferencesValue> & conMap)544 bool PreferencesXmlUtils::ReadSettingXml(const std::string &fileName, const std::string &bundleName,
545     std::unordered_map<std::string, PreferencesValue> &conMap)
546 {
547     if (fileName.size() == 0) {
548         LOG_ERROR("The length of the file name is 0.");
549         return false;
550     }
551     auto doc =
552         std::shared_ptr<xmlDoc>(XmlReadFile(fileName, bundleName), [](xmlDoc *doc) { xmlFreeDoc(doc); });
553     if (doc == nullptr) {
554         return false;
555     }
556 
557     xmlNode *root = xmlDocGetRootElement(doc.get());
558     if (!root || xmlStrcmp(root->name, reinterpret_cast<const xmlChar *>("preferences"))) {
559         LOG_ERROR("Failed to obtain the XML root element.");
560         return false;
561     }
562 
563     bool success = true;
564     const xmlNode *cur = nullptr;
565     for (cur = root->children; cur != nullptr; cur = cur->next) {
566         Element element;
567 
568         if (ParseNodeElement(cur, element)) {
569             ReadXmlElement(element, conMap);
570         } else {
571             success = false;
572             LOG_ERROR("The error occurred during getting xml child elements.");
573             break;
574         }
575     }
576     return success;
577 }
578 
579 /* static */
ParseNodeElement(const xmlNode * node,Element & element)580 bool ParseNodeElement(const xmlNode *node, Element &element)
581 {
582     if (!xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("string"))
583         || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("uint8Array"))
584         || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("object"))) {
585         return ParseStringNodeElement(node, element);
586     }
587 
588     if (!xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("int"))
589         || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("long"))
590         || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("bool"))
591         || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("float"))
592         || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("double"))
593         || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("uint64_t"))) {
594         return ParsePrimitiveNodeElement(node, element);
595     }
596 
597     if (!xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("boolArray"))
598         || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("stringArray"))
599         || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("doubleArray"))
600         || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("BigInt"))
601         || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("set"))) {
602         return ParseArrayNodeElement(node, element);
603     }
604 
605     std::string keyName;
606     xmlChar *key = xmlGetProp(node, (const xmlChar *)ATTR_KEY);
607     if (key != nullptr) {
608         keyName = std::string(reinterpret_cast<char *>(key));
609         xmlFree(key);
610     }
611     LOG_ERROR("An unsupported element type was encountered in parsing = %{public}s, keyName = %{public}s.", node->name,
612         Anonymous::ToBeAnonymous(keyName).c_str());
613     return false;
614 }
615 
616 /* static */
ParsePrimitiveNodeElement(const xmlNode * node,Element & element)617 bool ParsePrimitiveNodeElement(const xmlNode *node, Element &element)
618 {
619     xmlChar *key = xmlGetProp(node, reinterpret_cast<const xmlChar *>(ATTR_KEY));
620     xmlChar *value = xmlGetProp(node, reinterpret_cast<const xmlChar *>(ATTR_VALUE));
621 
622     bool success = false;
623     if (value != nullptr) {
624         element.tag_ = std::string(reinterpret_cast<const char *>(node->name));
625         if (key != nullptr) {
626             element.key_ = std::string(reinterpret_cast<char *>(key));
627         }
628         element.value_ = std::string(reinterpret_cast<char *>(value));
629         success = true;
630     } else {
631         LOG_ERROR("Failed to obtain a valid key or value when parsing %{public}s.", node->name);
632     }
633 
634     if (key != nullptr) {
635         xmlFree(key);
636     }
637     if (value != nullptr) {
638         xmlFree(value);
639     }
640     return success;
641 }
642 
643 /* static */
ParseStringNodeElement(const xmlNode * node,Element & element)644 bool ParseStringNodeElement(const xmlNode *node, Element &element)
645 {
646     xmlChar *key = xmlGetProp(node, (const xmlChar *)ATTR_KEY);
647     xmlChar *text = xmlNodeGetContent(node);
648 
649     bool success = false;
650     if (text != nullptr) {
651         element.tag_ = std::string(reinterpret_cast<const char *>(node->name));
652         if (key != nullptr) {
653             element.key_ = std::string(reinterpret_cast<char *>(key));
654         }
655         element.value_ = std::string(reinterpret_cast<char *>(text));
656         success = true;
657     } else {
658         LOG_ERROR("Failed to obtain a valid key or value when parsing string element.");
659     }
660 
661     if (key != nullptr) {
662         xmlFree(key);
663     }
664     if (text != nullptr) {
665         xmlFree(text);
666     }
667     return success;
668 }
669 
670 /* static */
ParseArrayNodeElement(const xmlNode * node,Element & element)671 bool ParseArrayNodeElement(const xmlNode *node, Element &element)
672 {
673     xmlChar *key = xmlGetProp(node, (const xmlChar *)ATTR_KEY);
674     const xmlNode *children = node->children;
675 
676     bool success = false;
677     if (key != nullptr) {
678         element.tag_ = std::string(reinterpret_cast<const char *>(node->name));
679         element.key_ = std::string(reinterpret_cast<char *>(key));
680 
681         const xmlNode *cur = nullptr;
682         bool finishTravelChild = true;
683         for (cur = children; cur != nullptr; cur = cur->next) {
684             Element child;
685             if (ParseNodeElement(cur, child)) {
686                 element.children_.push_back(child);
687             } else {
688                 finishTravelChild = false;
689                 LOG_ERROR("Failed to parse the Array element and could not be completed successfully.");
690                 break;
691             }
692         }
693         success = finishTravelChild;
694     } else {
695         LOG_ERROR("Failed to obtain a valid key or value when parsing a Array element.");
696     }
697 
698     if (key != nullptr) {
699         xmlFree(key);
700     }
701     return success;
702 }
703 
ReportSaveFileFault(const std::string fileName,const std::string & bundleName,bool & isReport,bool isMultiProcessing)704 static void ReportSaveFileFault(const std::string fileName, const std::string &bundleName,
705     bool &isReport, bool isMultiProcessing)
706 {
707     int errCode = errno;
708     bool isExist = false;
709     const xmlError *xmlErr = xmlGetLastError();
710     std::string errMessage = (xmlErr != nullptr) ? xmlErr->message : "null";
711     LOG_ERROR("Save:%{public}s, errno:%{public}d, error:%{public}s.",
712         ExtractFileName(fileName).c_str(), errCode, errMessage.c_str());
713     if (IsFileExist(fileName)) {
714         RenameToBrokenFile(fileName);
715         isReport = true;
716     }
717     RenameFromBackupFile(fileName, bundleName, isReport, isExist);
718     if (ReportNonCorruptError("write failed", fileName, bundleName, errCode)) {
719         return;
720     }
721     if (isMultiProcessing) {
722         ReportFaultParam param = { "write failed", bundleName, NORMAL_DB, ExtractFileName(fileName),
723             E_OPERAT_IS_CROSS_PROESS, "Cross-process operations." };
724         PreferencesDfxManager::ReportFault(param);
725         return;
726     }
727     if (isReport) {
728         ReportFaultParam param = { "write failed", bundleName, NORMAL_DB, ExtractFileName(fileName),
729             E_ERROR, "write failed, " + errMessage};
730         isExist ? ReportXmlFileCorrupted(fileName, bundleName, errMessage, errCode) :
731             PreferencesDfxManager::ReportFault(param);
732     }
733 }
734 
SaveXmlFile(const std::string & fileName,const std::string & bundleName,xmlBufferPtr buf)735 static bool SaveXmlFile(const std::string &fileName, const std::string &bundleName, xmlBufferPtr buf)
736 {
737     bool isReport = false;
738     bool isMultiProcessing = false;
739     PreferencesFileLock fileLock(fileName);
740     fileLock.WriteLock(isMultiProcessing);
741     LOG_INFO("file:%{public}s, m:%{public}d.", ExtractFileName(fileName).c_str(), isMultiProcessing);
742     if (IsFileExist(fileName) && !RenameToBackupFile(fileName)) {
743         return false;
744     }
745     int fd = Open(fileName.c_str());
746     if (fd == -1) {
747         LOG_ERROR("failed open:%{public}s", ExtractFileName(fileName).c_str());
748         ReportSaveFileFault(fileName, bundleName, isReport, isMultiProcessing);
749         return false;
750     }
751     if (Write(fd, buf->content, buf->use) < 0) {
752         LOG_ERROR("Failed write:%{public}s", ExtractFileName(fileName).c_str());
753         ReportSaveFileFault(fileName, bundleName, isReport, isMultiProcessing);
754         Close(fd);
755         return false;
756     }
757     if (!Fsync(fd)) {
758         LOG_WARN("Failed to write to the disk.");
759     }
760     Close(fd);
761     RemoveBackupFile(fileName);
762     return true;
763 }
764 
WriteXmlElement(xmlTextWriterPtr writer,const std::string & key,const PreferencesValue & value)765 static bool WriteXmlElement(xmlTextWriterPtr writer, const std::string &key, const PreferencesValue &value)
766 {
767     ValueVisitor visitor;
768     std::visit(visitor, value.value_);
769     const char* tag = visitor.typeTag.data();
770     const char* keyPtr = key.c_str();
771     if (visitor.typeTag == "string" || visitor.typeTag == "uint8Array" || visitor.typeTag == "object") {
772         XML_CHECK(xmlTextWriterStartElement(writer, BAD_CAST tag) >= 0, "Start element failed");
773         XML_CHECK(xmlTextWriterWriteAttribute(writer, BAD_CAST ATTR_KEY, BAD_CAST keyPtr) >= 0, "Write attr failed");
774         XML_CHECK(xmlTextWriterWriteString(writer, BAD_CAST visitor.valueStr.c_str()) >= 0, "Write value failed");
775         XML_CHECK(xmlTextWriterEndElement(writer) >= 0, "End element failed");
776     } else if (visitor.typeTag == "int" || visitor.typeTag == "long" ||visitor.typeTag == "float" ||
777         visitor.typeTag == "bool" || visitor.typeTag == "double") {
778         XML_CHECK(xmlTextWriterStartElement(writer, BAD_CAST tag) >= 0, "Start element failed");
779         XML_CHECK(xmlTextWriterWriteAttribute(writer, BAD_CAST ATTR_KEY, BAD_CAST keyPtr) >= 0, "Write attr failed");
780         XML_CHECK(xmlTextWriterWriteAttribute(writer, BAD_CAST ATTR_VALUE, BAD_CAST visitor.valueStr.c_str()) >= 0,
781             "Write attr failed");
782         XML_CHECK(xmlTextWriterEndElement(writer) >= 0, "End element failed");
783     } else if (visitor.typeTag == "doubleArray" || visitor.typeTag == "stringArray" ||
784         visitor.typeTag == "boolArray" || visitor.typeTag == "BigInt") {
785         XML_CHECK(xmlTextWriterStartElement(writer, BAD_CAST tag) >= 0, "Start element failed");
786         XML_CHECK(xmlTextWriterWriteAttribute(writer, BAD_CAST ATTR_KEY, BAD_CAST keyPtr) >= 0, "Write attr failed");
787         for (auto &child : visitor.arrayValues) {
788             if (visitor.childrenTag == "string") {
789                 XML_CHECK(xmlTextWriterStartElement(writer, BAD_CAST visitor.childrenTag.data()) >= 0,
790                     "Start element failed");
791                 XML_CHECK(xmlTextWriterWriteString(writer, BAD_CAST child.c_str()) >= 0, "Write value failed");
792                 XML_CHECK(xmlTextWriterEndElement(writer) >= 0, "End element failed");
793             } else {
794                 XML_CHECK(xmlTextWriterStartElement(writer, BAD_CAST visitor.childrenTag.data()) >= 0,
795                     "Start element failed");
796                 XML_CHECK(xmlTextWriterWriteAttribute(writer, BAD_CAST ATTR_VALUE, BAD_CAST child.c_str()) >= 0,
797                     "Write attr failed");
798                 XML_CHECK(xmlTextWriterEndElement(writer) >= 0, "End element failed");
799             }
800         }
801         XML_CHECK(xmlTextWriterEndElement(writer) >= 0, "End element failed");
802     }
803     return true;
804 }
805 
806 /* static */
WriteSettingXml(const std::string & fileName,const std::string & bundleName,const std::unordered_map<std::string,PreferencesValue> & writeToDiskMap)807 bool PreferencesXmlUtils::WriteSettingXml(const std::string &fileName, const std::string &bundleName,
808     const std::unordered_map<std::string, PreferencesValue> &writeToDiskMap)
809 {
810     if (fileName.empty()) {
811         LOG_ERROR("The length of the file name is 0.");
812         return false;
813     }
814 
815     XmlBufferWrapper bufferWrapper(xmlBufferCreateSize(writeToDiskMap.size() * PRE_ALLOCATE_BUFFER_SIZE));
816     if (!bufferWrapper.get()) {
817         LOG_ERROR("Failed to create XML buffer");
818         return false;
819     }
820     XmlWriterWrapper writerWrapper(xmlNewTextWriterMemory(bufferWrapper.get(), 0));
821     if (!writerWrapper.get()) {
822         LOG_ERROR("Failed to create XML writer");
823         return false;
824     }
825 
826     xmlTextWriterPtr writer = writerWrapper.get();
827     xmlTextWriterSetIndent(writer, 0);
828 
829     XML_CHECK(xmlTextWriterStartDocument(writer, nullptr, "UTF-8", nullptr) >= 0, "Start document failed");
830     XML_CHECK(xmlTextWriterStartElement(writer, BAD_CAST TAG_PREFERENCES) >= 0, "Start root element failed");
831     XML_CHECK(xmlTextWriterWriteAttribute(writer, BAD_CAST TAG_VERSION, BAD_CAST VERSION_VALUE) >= 0,
832             "Write version failed");
833 
834     for (const auto& [key, prefValue] : writeToDiskMap) {
835         if (!WriteXmlElement(writer, key, prefValue)) {
836             return false;
837         }
838     }
839 
840     XML_CHECK(xmlTextWriterEndElement(writer) >= 0, "End root failed");
841     XML_CHECK(xmlTextWriterEndDocument(writer) >= 0, "End document failed");
842 
843     return SaveXmlFile(fileName, bundleName, bufferWrapper.get());
844 }
845 } // End of namespace NativePreferences
846 } // End of namespace OHOS