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