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