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