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