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
18 #include <sys/stat.h>
19
20 #include <cerrno>
21 #include <cstring>
22
23 #include "libxml/parser.h"
24 #include "log_print.h"
25 #include "preferences_dfx_adapter.h"
26 #include "preferences_file_lock.h"
27 #include "preferences_file_operation.h"
28 #include "preferences_impl.h"
29
30 namespace OHOS {
31 namespace NativePreferences {
32 constexpr int REQUIRED_KEY_NOT_AVAILABLE = 126;
33 constexpr int REQUIRED_KEY_REVOKED = 128;
34 static bool ParseNodeElement(const xmlNode *node, Element &element);
35 static bool ParsePrimitiveNodeElement(const xmlNode *node, Element &element);
36 static bool ParseStringNodeElement(const xmlNode *node, Element &element);
37 static bool ParseArrayNodeElement(const xmlNode *node, Element &element);
38 static xmlNode *CreateElementNode(Element &element);
39 static xmlNode *CreatePrimitiveNode(Element &element);
40 static xmlNode *CreateStringNode(Element &element);
41 static xmlNode *CreateArrayNode(Element &element);
42
IsFileExist(const std::string &inputPath)43 static bool IsFileExist(const std::string &inputPath)
44 {
45 if (inputPath.length() > PATH_MAX) {
46 return false;
47 }
48 struct stat buffer;
49 return (stat(inputPath.c_str(), &buffer) == 0);
50 }
51
RemoveBackupFile(const std::string &fileName)52 static void RemoveBackupFile(const std::string &fileName)
53 {
54 std::string backupFileName = MakeFilePath(fileName, STR_BACKUP);
55 if (IsFileExist(backupFileName) && std::remove(backupFileName.c_str())) {
56 LOG_WARN("failed to delete backup file %{public}d.", errno);
57 }
58 }
59
ReadFile(const std::string &fileName, int &errCode)60 static xmlDoc *ReadFile(const std::string &fileName, int &errCode)
61 {
62 xmlDoc *doc = xmlReadFile(fileName.c_str(), "UTF-8", XML_PARSE_NOBLANKS | XML_PARSE_HUGE);
63 errCode = errno;
64 return doc;
65 }
66
ReportXmlFileIsBroken(const std::string &fileName, const std::string &bundleName, const std::string &operationMsg, int errCode)67 static void ReportXmlFileIsBroken(const std::string &fileName, const std::string &bundleName,
68 const std::string &operationMsg, int errCode)
69 {
70 ReportParam reportParam = { bundleName, NORMAL_DB, ExtractFileName(fileName), E_ERROR, errCode, operationMsg };
71 PreferencesDfxManager::ReportDbFault(reportParam);
72 ReportParam succreportParam = reportParam;
73 succreportParam.errCode = E_OK;
74 succreportParam.errnoCode = 0;
75 succreportParam.appendix = "operation: restore success";
76 PreferencesDfxManager::ReportDbFault(succreportParam);
77 }
78
RenameFromBackupFile(const std::string &fileName, const std::string &bundleName, bool &isReportCorrupt)79 static bool RenameFromBackupFile(const std::string &fileName, const std::string &bundleName, bool &isReportCorrupt)
80 {
81 std::string backupFileName = MakeFilePath(fileName, STR_BACKUP);
82 if (!IsFileExist(backupFileName)) {
83 LOG_DEBUG("the backup file does not exist.");
84 return false;
85 }
86 xmlResetLastError();
87 int errCode = 0;
88 auto bakDoc = std::shared_ptr<xmlDoc>(ReadFile(backupFileName, errCode),
89 [](xmlDoc *bakDoc) { xmlFreeDoc(bakDoc); });
90 if (bakDoc == nullptr) {
91 xmlErrorPtr xmlErr = xmlGetLastError();
92 std::string errMessage = (xmlErr != nullptr) ? xmlErr->message : "null";
93 LOG_ERROR("restore XML file: %{public}s failed, errno is %{public}d, error is %{public}s.",
94 ExtractFileName(fileName).c_str(), errCode, errMessage.c_str());
95 std::remove(backupFileName.c_str());
96 isReportCorrupt = true;
97 return false;
98 }
99 if (std::rename(backupFileName.c_str(), fileName.c_str())) {
100 LOG_ERROR("failed to restore backup errno %{public}d.", errno);
101 return false;
102 }
103 isReportCorrupt = false;
104 LOG_INFO("restore XML file %{public}s successfully.", ExtractFileName(fileName).c_str());
105 return true;
106 }
107
RenameFile(const std::string &fileName, const std::string &fileType)108 static bool RenameFile(const std::string &fileName, const std::string &fileType)
109 {
110 std::string name = MakeFilePath(fileName, fileType);
111 if (std::rename(fileName.c_str(), name.c_str())) {
112 LOG_ERROR("failed to rename file to %{public}s file %{public}d.", fileType.c_str(), errno);
113 return false;
114 }
115 return true;
116 }
117
RenameToBackupFile(const std::string &fileName)118 static bool RenameToBackupFile(const std::string &fileName)
119 {
120 return RenameFile(fileName, STR_BACKUP);
121 }
122
RenameToBrokenFile(const std::string &fileName)123 static bool RenameToBrokenFile(const std::string &fileName)
124 {
125 return RenameFile(fileName, STR_BROKEN);
126 }
127
XmlReadFile(const std::string &fileName, const std::string &bundleName, const std::string &dataGroupId)128 static xmlDoc *XmlReadFile(const std::string &fileName, const std::string &bundleName, const std::string &dataGroupId)
129 {
130 xmlDoc *doc = nullptr;
131 bool isReport = false;
132 PreferencesFileLock fileLock(MakeFilePath(fileName, STR_LOCK), dataGroupId);
133 int errCode = 0;
134 if (IsFileExist(fileName)) {
135 doc = ReadFile(fileName, errCode);
136 if (doc != nullptr) {
137 return doc;
138 }
139 xmlErrorPtr xmlErr = xmlGetLastError();
140 std::string errMessage = (xmlErr != nullptr) ? xmlErr->message : "null";
141 LOG_ERROR("failed to read XML format file: %{public}s, errno is %{public}d, error is %{public}s.",
142 ExtractFileName(fileName).c_str(), errCode, errMessage.c_str());
143 if (errCode == REQUIRED_KEY_NOT_AVAILABLE || errCode == REQUIRED_KEY_REVOKED) {
144 return nullptr;
145 }
146 if (!RenameToBrokenFile(fileName)) {
147 return doc;
148 }
149 isReport = true;
150 }
151
152 if (RenameFromBackupFile(fileName, bundleName, isReport)) {
153 doc = ReadFile(fileName, errCode);
154 }
155 if (isReport) {
156 const std::string operationMsg = "operation: failed to read XML format file.";
157 ReportXmlFileIsBroken(fileName, bundleName, operationMsg, errCode);
158 }
159 return doc;
160 }
161
162 /* static */
ReadSettingXml(const std::string &fileName, const std::string &bundleName, const std::string &dataGroupId, std::vector<Element> &settings)163 bool PreferencesXmlUtils::ReadSettingXml(const std::string &fileName, const std::string &bundleName,
164 const std::string &dataGroupId, std::vector<Element> &settings)
165 {
166 LOG_RECORD_FILE_NAME("Read setting xml start.");
167 if (fileName.size() == 0) {
168 LOG_ERROR("The length of the file name is 0.");
169 return false;
170 }
171 auto doc =
172 std::shared_ptr<xmlDoc>(XmlReadFile(fileName, bundleName, dataGroupId), [](xmlDoc *doc) { xmlFreeDoc(doc); });
173 if (doc == nullptr) {
174 return false;
175 }
176
177 xmlNode *root = xmlDocGetRootElement(doc.get());
178 if (!root || xmlStrcmp(root->name, reinterpret_cast<const xmlChar *>("preferences"))) {
179 LOG_ERROR("Failed to obtain the XML root element.");
180 return false;
181 }
182
183 bool success = true;
184 const xmlNode *cur = nullptr;
185 for (cur = root->children; cur != nullptr; cur = cur->next) {
186 Element element;
187
188 if (ParseNodeElement(cur, element)) {
189 settings.push_back(element);
190 } else {
191 success = false;
192 LOG_ERROR("The error occurred during getting xml child elements.");
193 break;
194 }
195 }
196
197 /* free the document */
198 LOG_RECORD_FILE_NAME("Read setting xml end.");
199 return success;
200 }
201
202 /* static */
ParseNodeElement(const xmlNode *node, Element &element)203 bool ParseNodeElement(const xmlNode *node, Element &element)
204 {
205 if (!xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("int"))
206 || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("long"))
207 || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("bool"))
208 || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("float"))
209 || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("double"))
210 || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("uint64_t"))) {
211 return ParsePrimitiveNodeElement(node, element);
212 }
213
214 if (!xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("string"))
215 || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("uint8Array"))
216 || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("object"))) {
217 return ParseStringNodeElement(node, element);
218 }
219
220 if (!xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("boolArray"))
221 || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("stringArray"))
222 || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("doubleArray"))
223 || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("BigInt"))
224 || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("set"))) {
225 return ParseArrayNodeElement(node, element);
226 }
227
228 LOG_ERROR("An unsupported element type was encountered in parsing = %{public}s.", node->name);
229 return false;
230 }
231
232 /* static */
ParsePrimitiveNodeElement(const xmlNode *node, Element &element)233 bool ParsePrimitiveNodeElement(const xmlNode *node, Element &element)
234 {
235 xmlChar *key = xmlGetProp(node, reinterpret_cast<const xmlChar *>("key"));
236 xmlChar *value = xmlGetProp(node, reinterpret_cast<const xmlChar *>("value"));
237
238 bool success = false;
239 if (value != nullptr) {
240 element.tag_ = std::string(reinterpret_cast<const char *>(node->name));
241 if (key != nullptr) {
242 element.key_ = std::string(reinterpret_cast<char *>(key));
243 }
244 element.value_ = std::string(reinterpret_cast<char *>(value));
245 success = true;
246 } else {
247 LOG_ERROR("Failed to obtain a valid key or value when parsing %{public}s.", node->name);
248 }
249
250 if (key != nullptr) {
251 xmlFree(key);
252 }
253 if (value != nullptr) {
254 xmlFree(value);
255 }
256 return success;
257 }
258
259 /* static */
ParseStringNodeElement(const xmlNode *node, Element &element)260 bool ParseStringNodeElement(const xmlNode *node, Element &element)
261 {
262 xmlChar *key = xmlGetProp(node, (const xmlChar *)"key");
263 xmlChar *text = xmlNodeGetContent(node);
264
265 bool success = false;
266 if (text != nullptr) {
267 element.tag_ = std::string(reinterpret_cast<const char *>(node->name));
268 if (key != nullptr) {
269 element.key_ = std::string(reinterpret_cast<char *>(key));
270 }
271 element.value_ = std::string(reinterpret_cast<char *>(text));
272 success = true;
273 } else {
274 LOG_ERROR("Failed to obtain a valid key or value when parsing string element.");
275 }
276
277 if (key != nullptr) {
278 xmlFree(key);
279 }
280 if (text != nullptr) {
281 xmlFree(text);
282 }
283 return success;
284 }
285
286 /* static */
ParseArrayNodeElement(const xmlNode *node, Element &element)287 bool ParseArrayNodeElement(const xmlNode *node, Element &element)
288 {
289 xmlChar *key = xmlGetProp(node, (const xmlChar *)"key");
290 const xmlNode *children = node->children;
291
292 bool success = false;
293 if (key != nullptr) {
294 element.tag_ = std::string(reinterpret_cast<const char *>(node->name));
295 element.key_ = std::string(reinterpret_cast<char *>(key));
296
297 const xmlNode *cur = nullptr;
298 bool finishTravelChild = true;
299 for (cur = children; cur != nullptr; cur = cur->next) {
300 Element child;
301 if (ParseNodeElement(cur, child)) {
302 element.children_.push_back(child);
303 } else {
304 finishTravelChild = false;
305 LOG_ERROR("Failed to parse the Array element and could not be completed successfully.");
306 break;
307 }
308 }
309 success = finishTravelChild;
310 } else {
311 LOG_ERROR("Failed to obtain a valid key or value when parsing a Array element.");
312 }
313
314 if (key != nullptr) {
315 xmlFree(key);
316 }
317 return success;
318 }
319
SaveFormatFileEnc(const std::string &fileName, xmlDoc *doc)320 static std::pair<bool, int> SaveFormatFileEnc(const std::string &fileName, xmlDoc *doc)
321 {
322 return {xmlSaveFormatFileEnc(fileName.c_str(), doc, "UTF-8", 1) > 0, errno};
323 }
324
XmlSaveFormatFileEnc( const std::string &fileName, const std::string &bundleName, const std::string &dataGroupId, xmlDoc *doc)325 bool XmlSaveFormatFileEnc(
326 const std::string &fileName, const std::string &bundleName, const std::string &dataGroupId, xmlDoc *doc)
327 {
328 PreferencesFileLock fileLock(MakeFilePath(fileName, STR_LOCK), dataGroupId);
329 LOG_INFO("save xml file:%{public}s.", ExtractFileName(fileName).c_str());
330 if (IsFileExist(fileName) && !RenameToBackupFile(fileName)) {
331 return false;
332 }
333
334 bool isReport = false;
335 auto [ret, errCode] = SaveFormatFileEnc(fileName, doc);
336 if (!ret) {
337 xmlErrorPtr xmlErr = xmlGetLastError();
338 std::string errMessage = (xmlErr != nullptr) ? xmlErr->message : "null";
339 LOG_ERROR("failed to save XML format file: %{public}s, errno is %{public}d, error is %{public}s.",
340 ExtractFileName(fileName).c_str(), errCode, errMessage.c_str());
341 if (errCode == REQUIRED_KEY_NOT_AVAILABLE || errCode == REQUIRED_KEY_REVOKED) {
342 return false;
343 }
344 if (IsFileExist(fileName)) {
345 RenameToBrokenFile(fileName);
346 isReport = true;
347 }
348 RenameFromBackupFile(fileName, bundleName, isReport);
349 if (isReport) {
350 const std::string operationMsg = "operation: failed to save XML format file.";
351 ReportXmlFileIsBroken(fileName, bundleName, operationMsg, errCode);
352 }
353 return false;
354 }
355
356 RemoveBackupFile(fileName);
357 PreferencesXmlUtils::LimitXmlPermission(fileName);
358 // make sure the file is written to disk.
359 if (!Fsync(fileName)) {
360 LOG_WARN("failed to write the file to the disk.");
361 }
362 LOG_DEBUG("successfully saved the XML format file");
363 return true;
364 }
365
366 /* static */
WriteSettingXml(const std::string &fileName, const std::string &bundleName, const std::string &dataGroupId, const std::vector<Element> &settings)367 bool PreferencesXmlUtils::WriteSettingXml(const std::string &fileName, const std::string &bundleName,
368 const std::string &dataGroupId, const std::vector<Element> &settings)
369 {
370 LOG_RECORD_FILE_NAME("Write setting xml start.");
371 if (fileName.size() == 0) {
372 LOG_ERROR("The length of the file name is 0.");
373 return false;
374 }
375
376 // define doc and root Node
377 auto doc = std::shared_ptr<xmlDoc>(xmlNewDoc(BAD_CAST "1.0"), [](xmlDoc *doc) { xmlFreeDoc(doc); });
378 if (doc == nullptr) {
379 LOG_ERROR("Failed to initialize the xmlDoc.");
380 return false;
381 }
382 xmlNode *rootNode = xmlNewNode(NULL, BAD_CAST "preferences");
383 if (rootNode == nullptr) {
384 LOG_ERROR("The xmlDoc failed to initialize the root node.");
385 return false;
386 }
387 xmlNewProp(rootNode, BAD_CAST "version", BAD_CAST "1.0");
388
389 // set root node
390 xmlDocSetRootElement(doc.get(), rootNode);
391
392 // set children node
393 for (Element element : settings) {
394 xmlNode *node = CreateElementNode(element);
395 if (node == nullptr) {
396 LOG_ERROR("The xmlDoc failed to initialize the element node.");
397 return false;
398 }
399 if (xmlAddChild(rootNode, node) == nullptr) {
400 /* free node in case of error */
401 LOG_ERROR("The xmlDoc failed to add the child node.");
402 xmlFreeNode(node);
403 return false;
404 }
405 }
406
407 /* 1: formatting spaces are added. */
408 bool result = XmlSaveFormatFileEnc(fileName, bundleName, dataGroupId, doc.get());
409 LOG_RECORD_FILE_NAME("Write setting xml end.");
410 return result;
411 }
412
413 /* static */
CreateElementNode(Element &element)414 xmlNode *CreateElementNode(Element &element)
415 {
416 if ((element.tag_.compare("int") == 0) || (element.tag_.compare("long") == 0)
417 || (element.tag_.compare("float") == 0) || (element.tag_.compare("bool") == 0)
418 || (element.tag_.compare("double") == 0)) {
419 return CreatePrimitiveNode(element);
420 }
421
422 if (element.tag_.compare("string") == 0 || element.tag_.compare("uint8Array") == 0
423 || element.tag_.compare("object") == 0) {
424 return CreateStringNode(element);
425 }
426
427 if ((element.tag_.compare("doubleArray") == 0) || (element.tag_.compare("stringArray") == 0)
428 || (element.tag_.compare("boolArray") == 0) || (element.tag_.compare("BigInt") == 0)) {
429 return CreateArrayNode(element);
430 }
431
432 LOG_ERROR("An unsupported element type was encountered in parsing = %{public}s.", element.tag_.c_str());
433 return nullptr;
434 }
435
436 /* static */
CreatePrimitiveNode(Element &element)437 xmlNode *CreatePrimitiveNode(Element &element)
438 {
439 xmlNode *node = xmlNewNode(NULL, BAD_CAST element.tag_.c_str());
440 if (node == nullptr) {
441 LOG_ERROR("The xmlDoc failed to initialize the primitive element node.");
442 return nullptr;
443 }
444 if (!element.key_.empty()) {
445 const char *key = element.key_.c_str();
446 xmlNewProp(node, BAD_CAST "key", BAD_CAST key);
447 }
448
449 const char *value = element.value_.c_str();
450 xmlNewProp(node, BAD_CAST "value", BAD_CAST value);
451 return node;
452 }
453
CreateStringNode(Element &element)454 xmlNode *CreateStringNode(Element &element)
455 {
456 xmlNode *node = xmlNewNode(NULL, BAD_CAST element.tag_.c_str());
457 if (node == nullptr) {
458 LOG_ERROR("The xmlDoc failed to initialize the string element node.");
459 return nullptr;
460 }
461
462 if (!element.key_.empty()) {
463 const char *key = element.key_.c_str();
464 xmlNewProp(node, BAD_CAST "key", BAD_CAST key);
465 }
466
467 const char *value = element.value_.c_str();
468 xmlNodePtr text = xmlNewText(BAD_CAST value);
469 if (xmlAddChild(node, text) == nullptr) {
470 xmlFreeNode(text);
471 }
472 return node;
473 }
474
CreateArrayNode(Element &element)475 xmlNode *CreateArrayNode(Element &element)
476 {
477 xmlNode *node = xmlNewNode(NULL, BAD_CAST element.tag_.c_str());
478 if (node == nullptr) {
479 LOG_ERROR("The xmlDoc failed to initialize the array element node.");
480 return nullptr;
481 }
482
483 const char *key = element.key_.c_str();
484 xmlNewProp(node, BAD_CAST "key", BAD_CAST key);
485
486 if (element.children_.empty()) {
487 return node;
488 }
489 Element flag = element.children_[0];
490 if ((flag.tag_.compare("bool") == 0) || (flag.tag_.compare("double") == 0) ||
491 (flag.tag_.compare("uint64_t") == 0)) {
492 for (Element &child : element.children_) {
493 xmlNode *childNode = CreatePrimitiveNode(child);
494 if (childNode == nullptr) {
495 continue;
496 }
497 if (xmlAddChild(node, childNode) == nullptr) {
498 xmlFreeNode(childNode);
499 }
500 }
501 } else if (flag.tag_.compare("string") == 0) {
502 for (Element child : element.children_) {
503 xmlNode *childNode = CreateStringNode(child);
504 if (childNode == nullptr) {
505 continue;
506 }
507 if (xmlAddChild(node, childNode) == nullptr) {
508 xmlFreeNode(childNode);
509 }
510 }
511 }
512 return node;
513 }
514
LimitXmlPermission(const std::string &fileName)515 void PreferencesXmlUtils::LimitXmlPermission(const std::string &fileName)
516 {
517 /* clear execute permission of owner, clear execute permission of group, clear all permission of group. */
518 struct stat fileStat = { 0 };
519 if (stat(fileName.c_str(), &fileStat) != 0) {
520 LOG_ERROR("Failed to obtain stat of file, errno:%{public}d.", errno);
521 return;
522 }
523 if ((fileStat.st_mode & (S_IXUSR | S_IXGRP | S_IRWXO)) != 0) {
524 int result = chmod(fileName.c_str(), fileStat.st_mode & (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP));
525 if (result != 0) {
526 LOG_ERROR("Failed to chmod file, errno:%{public}d.", errno);
527 }
528 }
529 }
530 } // End of namespace NativePreferences
531 } // End of namespace OHOS