1 /*
2  * Copyright (c) 2024 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 "b_json/b_report_entity.h"
17 
18 #include <map>
19 #include <sstream>
20 #include <string>
21 #include <unordered_map>
22 #include <vector>
23 
24 #include "b_error/b_error.h"
25 #include "filemgmt_libhilog.h"
26 #include "sandbox_helper.h"
27 #include "unique_fd.h"
28 
29 namespace OHOS::FileManagement::Backup {
30 using namespace std;
31 namespace {
32 const char ATTR_SEP = ';';
33 const char LINE_SEP = '\n';
34 const char LINE_WRAP = '\r';
35 const int64_t HASH_BUFFER_SIZE = 4096; // 每次读取的siz
36 const int INFO_ALIGN_NUM = 2;
37 const int BUFFER_LENGTH = 2;
38 const string INFO_DIR = "dir";
39 const string INFO_HASH = "hash";
40 const string INFO_IS_INCREMENTAL = "isIncremental";
41 const string INFO_MODE = "mode";
42 const string INFO_MTIME = "mtime";
43 const string INFO_PATH = "path";
44 const string INFO_SIZE = "size";
45 const string INFO_ENCODE_FLAG = "encodeFlag";
46 } // namespace
47 
SplitStringByChar(const string & str,const char & sep)48 static vector<string> SplitStringByChar(const string &str, const char &sep)
49 {
50     vector<string> splits;
51     string newStr = str;
52     if (str.empty() ||  str.size() < 1) {
53         return splits;
54     }
55     if (str.rfind(sep) == str.size() - 1) {
56         newStr += sep;
57     }
58     stringstream ss(newStr);
59     string res;
60     while (getline(ss, res, sep)) {
61         splits.push_back(res);
62     }
63 
64     return splits;
65 }
66 
ParseSplitsItem(const vector<string> & splits,const unordered_map<string,int> & keys,vector<string> & residue,string & path)67 static void ParseSplitsItem(const vector<string> &splits, const unordered_map<string, int> &keys,
68     vector<string> &residue, string &path)
69 {
70     size_t splitsLen = splits.size();
71     for (size_t i = 0; i < splitsLen; i++) {
72         if (i <= splitsLen - keys.size()) {
73             path += splits[i] + ";";
74         } else {
75             residue.emplace_back(splits[i]);
76         }
77     }
78 }
79 
ParseReportInfo(struct ReportFileInfo & fileStat,const vector<string> & splits,const unordered_map<string,int> & keys)80 static ErrCode ParseReportInfo(struct ReportFileInfo &fileStat,
81                                const vector<string> &splits,
82                                const unordered_map<string, int> &keys)
83 {
84     // 根据数据拼接结构体
85     // 处理path路径
86     string path;
87     vector<string> residue;
88     try {
89         // 识别path字段与其他字段
90         ParseSplitsItem(splits, keys, residue, path);
91         if (residue.size() != keys.size() - 1) {
92             HILOGE("Error residue size");
93             return EPERM;
94         }
95         if (keys.find(INFO_ENCODE_FLAG) != keys.end()) {
96             fileStat.encodeFlag = residue[keys.find(INFO_ENCODE_FLAG)->second] == "1" ? true : false;
97         }
98         path = (path.length() > 0 && path[0] == '/') ? path.substr(1, path.length() - 1) : path;
99         auto fileRawPath = (path.length() > 0) ? path.substr(0, path.length() - 1) : path;
100         fileStat.filePath = BReportEntity::DecodeReportItem(fileRawPath, fileStat.encodeFlag);
101         HILOGD("Briefings file %{public}s", fileStat.filePath.c_str());
102         if (keys.find(INFO_MODE) != keys.end()) {
103             fileStat.mode = residue[keys.find(INFO_MODE)->second];
104         }
105         if (keys.find(INFO_DIR) != keys.end()) {
106             fileStat.isDir = residue[keys.find(INFO_DIR)->second] == "1" ? true : false;
107         }
108         if (keys.find(INFO_SIZE) != keys.end()) {
109             stringstream sizeStr(residue[keys.find(INFO_SIZE)->second]);
110             off_t size = 0;
111             sizeStr >> size;
112             fileStat.size = size;
113         }
114         if (keys.find(INFO_MTIME) != keys.end()) {
115             stringstream mtimeStr(residue[keys.find(INFO_MTIME)->second]);
116             off_t mtime = 0;
117             mtimeStr >> mtime;
118             fileStat.mtime = mtime;
119         }
120         if (keys.find(INFO_HASH) != keys.end()) {
121             fileStat.hash = residue[keys.find(INFO_HASH)->second];
122         }
123         if (keys.find(INFO_IS_INCREMENTAL) != keys.end()) {
124             fileStat.isIncremental = residue[keys.find(INFO_IS_INCREMENTAL)->second] == "1" ? true : false;
125         }
126         return ERR_OK;
127     } catch (...) {
128         HILOGE("Failed to ParseReportInfo");
129         return EPERM;
130     }
131 }
132 
DealLine(unordered_map<string,int> & keys,int & num,const string & line,unordered_map<string,struct ReportFileInfo> & infos)133 static void DealLine(unordered_map<string, int> &keys,
134                      int &num,
135                      const string &line,
136                      unordered_map<string, struct ReportFileInfo> &infos)
137 {
138     if (line.empty()) {
139         return;
140     }
141 
142     string currentLine = line;
143     if (currentLine[currentLine.length() - 1] == LINE_WRAP) {
144         currentLine.pop_back();
145     }
146 
147     vector<string> splits = SplitStringByChar(currentLine, ATTR_SEP);
148     if (num < INFO_ALIGN_NUM) {
149         if (num == 1) {
150             for (int j = 0; j < (int)splits.size(); j++) {
151                 keys.emplace(splits[j], j - 1);
152             }
153         }
154         num++;
155     } else {
156         struct ReportFileInfo fileState;
157         auto code = ParseReportInfo(fileState, splits, keys);
158         if (code != ERR_OK) {
159             HILOGE("ParseReportInfo err:%{public}d, %{public}s", code, currentLine.c_str());
160         } else {
161             infos.try_emplace(fileState.filePath, fileState);
162         }
163     }
164 }
165 
StorageDealLine(unordered_map<string,int> & keys,int & num,const string & line)166 static struct ReportFileInfo StorageDealLine(unordered_map<string, int> &keys, int &num, const string &line)
167 {
168     if (line.empty()) {
169         return {};
170     }
171 
172     string currentLine = line;
173     if (currentLine[currentLine.length() - 1] == LINE_WRAP) {
174         currentLine.pop_back();
175     }
176 
177     vector<string> splits = SplitStringByChar(currentLine, ATTR_SEP);
178     if (num < INFO_ALIGN_NUM) {
179         if (num == 1) {
180             for (int j = 0; j < (int)splits.size(); j++) {
181                 keys.emplace(splits[j], j - 1);
182             }
183         }
184         num++;
185     } else {
186         struct ReportFileInfo fileState;
187         auto code = ParseReportInfo(fileState, splits, keys);
188         if (code != ERR_OK) {
189             HILOGE("ParseReportInfo err:%{public}d, %{public}s", code, currentLine.c_str());
190         } else {
191             return fileState;
192         }
193     }
194     return {};
195 }
196 
GetReportInfos() const197 unordered_map<string, struct ReportFileInfo> BReportEntity::GetReportInfos() const
198 {
199     unordered_map<string, struct ReportFileInfo> infos {};
200 
201     char buffer[HASH_BUFFER_SIZE];
202     ssize_t bytesRead;
203     string currentLine;
204     unordered_map<string, int> keys;
205 
206     int num = 0;
207     while ((bytesRead = read(srcFile_, buffer, sizeof(buffer))) > 0) {
208         for (ssize_t i = 0; i < bytesRead; i++) {
209             if (buffer[i] == LINE_SEP) {
210                 DealLine(keys, num, currentLine, infos);
211                 currentLine.clear();
212             } else {
213                 currentLine += buffer[i];
214             }
215         }
216     }
217 
218     // 处理文件中的最后一行
219     if (!currentLine.empty()) {
220         DealLine(keys, num, currentLine, infos);
221     }
222 
223     return infos;
224 }
225 
GetStorageReportInfos(struct ReportFileInfo & fileStat)226 bool BReportEntity::GetStorageReportInfos(struct ReportFileInfo &fileStat)
227 {
228     char buffer[BUFFER_LENGTH];
229     ssize_t bytesRead;
230     string currentLine;
231     static unordered_map<string, int> keys;
232     static int num = 0;
233 
234     if ((bytesRead = read(srcFile_, buffer, 1)) <= 0) {
235         keys = {};
236         num = 0;
237         return false;
238     }
239     do {
240         if (buffer[0] != LINE_SEP && buffer[0] != '\0') {
241             currentLine += buffer[0];
242         } else {
243             currentLine += LINE_SEP;
244             fileStat = StorageDealLine(keys, num, currentLine);
245             return true;
246         }
247     } while ((bytesRead = read(srcFile_, buffer, 1)) > 0);
248     currentLine += LINE_SEP;
249     fileStat = StorageDealLine(keys, num, currentLine);
250     return true;
251 }
252 
CheckAndUpdateIfReportLineEncoded(std::string & path)253 void BReportEntity::CheckAndUpdateIfReportLineEncoded(std::string &path)
254 {
255     if (path.empty()) {
256         return;
257     }
258 
259     unordered_map<string, struct ReportFileInfo> infos = GetReportInfos();
260     constexpr int BIG_FILE_REPORT_INFO_NUM = 1;
261     if (infos.size() == BIG_FILE_REPORT_INFO_NUM) {
262         auto info = infos.begin();
263         path = info->first;
264     } else {
265         HILOGE("Invalid item sizes in report, current is %{public}zu", infos.size());
266     }
267 }
268 
EncodeReportItem(const string & reportItem,bool enableEncode)269 string BReportEntity::EncodeReportItem(const string &reportItem, bool enableEncode)
270 {
271     string encodeItem;
272     if (enableEncode) {
273         encodeItem = AppFileService::SandboxHelper::Encode(reportItem);
274     } else {
275         encodeItem = reportItem;
276     }
277     return encodeItem;
278 }
279 
DecodeReportItem(const string & reportItem,bool enableEncode)280 string BReportEntity::DecodeReportItem(const string &reportItem, bool enableEncode)
281 {
282     string decodeItem;
283     if (enableEncode) {
284         decodeItem = AppFileService::SandboxHelper::Decode(reportItem);
285     } else {
286         decodeItem = reportItem;
287     }
288     return decodeItem;
289 }
290 } // namespace OHOS::FileManagement::Backup