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