1 /*
2 * Copyright (c) 2023-2025 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 "untar_file.h"
17
18 #include <utime.h>
19
20 #include "b_anony/b_anony.h"
21 #include "b_filesystem/b_dir.h"
22 #include "directory_ex.h"
23 #include "filemgmt_libhilog.h"
24 #include "securec.h"
25
26 namespace OHOS::FileManagement::Backup {
27 using namespace std;
28 const int32_t PATH_MAX_LEN = 4096;
29 const int32_t OCTAL = 8;
30 const int DEFAULT_ERR = -1;
31
IsEmptyBlock(const char * p)32 static bool IsEmptyBlock(const char *p)
33 {
34 if (p != nullptr) {
35 return (p[0] == '\0');
36 }
37 return true;
38 }
39
40 // 八进制字符串转十进制数字
ParseOctalStr(const string & octalStr,size_t destLen)41 static off_t ParseOctalStr(const string &octalStr, size_t destLen)
42 {
43 off_t ret = 0;
44 string::const_iterator it = octalStr.begin();
45
46 while (it != octalStr.end() && (*it < '0' || *it > '7') && destLen > 0) {
47 ++it;
48 --destLen;
49 }
50
51 while (it != octalStr.end() && *it >= '0' && *it <= '7' && destLen > 0) {
52 ret *= OCTAL;
53 ret += *it - '0';
54 ++it;
55 --destLen;
56 }
57
58 return ret;
59 }
60
ForceCreateDirectoryWithMode(const string & path,mode_t mode)61 static bool ForceCreateDirectoryWithMode(const string& path, mode_t mode)
62 {
63 string::size_type index = 0;
64 do {
65 index = path.find('/', index + 1);
66 string subPath = (index == string::npos) ? path : path.substr(0, index);
67 if (access(subPath.c_str(), F_OK) != 0) {
68 if (mkdir(subPath.c_str(), mode) != 0) {
69 return false;
70 }
71 }
72 } while (index != string::npos);
73 return access(path.c_str(), F_OK) == 0;
74 }
75
RTrimNull(std::string & s)76 static void RTrimNull(std::string &s)
77 {
78 auto iter = std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return ch != '\0'; });
79 s.erase(iter.base(), s.end());
80 }
81
ReadLongName(FileStatInfo & info)82 std::tuple<int, ErrFileInfo> UntarFile::ReadLongName(FileStatInfo &info)
83 {
84 size_t nameLen = static_cast<size_t>(tarFileSize_);
85 int ret = 0;
86 if (nameLen <= PATH_MAX_LEN) {
87 string tempName("");
88 tempName.resize(nameLen);
89 size_t read = fread(&(tempName[0]), sizeof(char), nameLen, tarFilePtr_);
90 if (read < nameLen) {
91 HILOGE("Failed to fread longName of %{private}s", info.fullPath.c_str());
92 ret = -1;
93 }
94 info.longName = tempName;
95 } else {
96 HILOGE("longName of %{private}s exceed PATH_MAX_LEN", info.fullPath.c_str());
97 ret = -1;
98 }
99 ErrFileInfo errFileInfo;
100 if (ret != 0) {
101 errFileInfo[info.fullPath].emplace_back(ret);
102 return {-1, errFileInfo};
103 }
104 if (fseeko(tarFilePtr_, pos_ + tarFileBlockCnt_ * BLOCK_SIZE, SEEK_SET) != 0) {
105 HILOGE("Failed to fseeko of %{private}s, err = %{public}d", info.fullPath.c_str(), errno);
106 errFileInfo[info.fullPath].emplace_back(errno);
107 return {-1, errFileInfo};
108 }
109 return {0, errFileInfo};
110 }
111
GetInstance()112 UntarFile &UntarFile::GetInstance()
113 {
114 static UntarFile instance;
115 return instance;
116 }
117
UnPacket(const std::string & tarFile,const std::string & rootPath)118 std::tuple<int, EndFileInfo, ErrFileInfo> UntarFile::UnPacket(
119 const std::string &tarFile, const std::string &rootPath)
120 {
121 tarFilePtr_ = fopen(tarFile.c_str(), "rb");
122 if (tarFilePtr_ == nullptr) {
123 HILOGE("Failed to open tar file %{public}s, err = %{public}d", tarFile.c_str(), errno);
124 return {errno, {}, {}};
125 }
126
127 auto [ret, fileInfos, errInfos] = ParseTarFile(rootPath);
128 if (ret != 0) {
129 HILOGE("Failed to parse tar file");
130 }
131
132 fclose(tarFilePtr_);
133 tarFilePtr_ = nullptr;
134
135 return {0, fileInfos, errInfos};
136 }
137
IncrementalUnPacket(const string & tarFile,const string & rootPath,const unordered_map<string,struct ReportFileInfo> & includes)138 std::tuple<int, EndFileInfo, ErrFileInfo> UntarFile::IncrementalUnPacket(
139 const string &tarFile, const string &rootPath, const unordered_map<string, struct ReportFileInfo> &includes)
140 {
141 includes_ = includes;
142 tarFilePtr_ = fopen(tarFile.c_str(), "rb");
143 if (tarFilePtr_ == nullptr) {
144 HILOGE("Failed to open tar file %{public}s, err = %{public}d", tarFile.c_str(), errno);
145 return {errno, {}, {}};
146 }
147
148 auto [ret, fileInfos, errFileInfos] = ParseIncrementalTarFile(rootPath);
149 if (ret != 0) {
150 HILOGE("Failed to parse tar file");
151 }
152
153 fclose(tarFilePtr_);
154 tarFilePtr_ = nullptr;
155
156 return {0, fileInfos, errFileInfos};
157 }
158
HandleTarBuffer(const string & buff,const string & name,FileStatInfo & info)159 off_t UntarFile::HandleTarBuffer(const string &buff, const string &name, FileStatInfo &info)
160 {
161 info.mode = static_cast<mode_t>(ParseOctalStr(&buff[0] + TMODE_BASE, TMODE_LEN));
162 info.uid = static_cast<uid_t>(ParseOctalStr(&buff[0] + TUID_BASE, TUID_LEN));
163 info.gid = static_cast<gid_t>(ParseOctalStr(&buff[0] + TGID_BASE, TGID_LEN));
164 info.mtime = ParseOctalStr(&buff[0] + TMTIME_BASE, MTIME_LEN);
165
166 tarFileSize_ = ParseOctalStr(&buff[0] + TSIZE_BASE, TSIZE_LEN);
167 tarFileBlockCnt_ = (tarFileSize_ + BLOCK_SIZE - 1) / BLOCK_SIZE;
168 pos_ = ftello(tarFilePtr_);
169
170 string realName = name;
171 if (!info.longName.empty()) {
172 realName = info.longName;
173 info.longName.clear();
174 }
175 if (realName.length() > 0 && realName[0] == '/') {
176 info.fullPath = realName.substr(1, realName.length() - 1);
177 return tarFileSize_;
178 }
179 info.fullPath = realName;
180 return tarFileSize_;
181 }
182
CheckAndFillTarSize()183 int UntarFile::CheckAndFillTarSize()
184 {
185 // tarFileSize
186 int ret = fseeko(tarFilePtr_, 0L, SEEK_END);
187 if (ret != 0) {
188 HILOGE("Failed to fseeko tarFileSize SEEK_SET, err = %{public}d", errno);
189 return ret;
190 }
191 tarFileSize_ = ftello(tarFilePtr_);
192 // reback file to begin
193 ret = fseeko(tarFilePtr_, 0L, SEEK_SET);
194 if (ret != 0) {
195 HILOGE("Failed to fseeko reback SEEK_SET, err = %{public}d", errno);
196 return ret;
197 }
198 return ret;
199 }
200
DealParseTarFileResult(const std::tuple<int,bool,ErrFileInfo> & result,const off_t fileSize,const std::string & fileName,EndFileInfo & fileInfos,ErrFileInfo & errInfos)201 int UntarFile::DealParseTarFileResult(const std::tuple<int, bool, ErrFileInfo> &result,
202 const off_t fileSize, const std::string &fileName, EndFileInfo &fileInfos, ErrFileInfo &errInfos)
203 {
204 auto [ret, isFilter, subErrInfos] = result;
205 if (ret != 0) {
206 HILOGE("Failed to parse incremental file by type flag");
207 return ret;
208 }
209 if (!isFilter) {
210 fileInfos[fileName] = fileSize;
211 }
212 if (!errInfos.empty()) {
213 errInfos.merge(subErrInfos);
214 }
215 return 0;
216 }
217
CheckIfTarBlockValid(char * buff,size_t buffLen,TarHeader * header,int & ret)218 bool UntarFile::CheckIfTarBlockValid(char *buff, size_t buffLen, TarHeader *header, int &ret)
219 {
220 // two empty continuous block indicate end of file
221 if (buff == nullptr || buffLen != BLOCK_SIZE || header == nullptr) {
222 return false;
223 }
224 if (IsEmptyBlock(buff) && header->typeFlag != GNUTYPE_LONGNAME) {
225 char tailBuff[BLOCK_SIZE] = {0};
226 size_t tailRead = fread(tailBuff, 1, BLOCK_SIZE, tarFilePtr_);
227 if (tailRead == BLOCK_SIZE && IsEmptyBlock(tailBuff)) {
228 HILOGE("Parsing tar file completed, tailBuff is empty.");
229 ret = 0;
230 return false;
231 }
232 }
233 // check header
234 if (!IsValidTarBlock(*header)) {
235 // when split unpack, ftell size is over than file really size [0,READ_BUFF_SIZE]
236 if (ftello(tarFilePtr_) > (tarFileSize_ + READ_BUFF_SIZE) || !IsEmptyBlock(buff)) {
237 HILOGE("Invalid tar file format");
238 ret = ERR_FORMAT;
239 }
240 HILOGE("invalid tar block header");
241 return false;
242 }
243 return true;
244 }
245
ParseTarFile(const string & rootPath)246 std::tuple<int, EndFileInfo, ErrFileInfo> UntarFile::ParseTarFile(const string &rootPath)
247 {
248 // re-parse tar header
249 rootPath_ = rootPath;
250 char buff[BLOCK_SIZE] = {0};
251 FileStatInfo info {};
252 int ret = 0;
253 if ((ret = CheckAndFillTarSize()) != 0) {
254 return {ret, {}, {}};
255 }
256 EndFileInfo fileInfos;
257 ErrFileInfo errInfos;
258 while (1) {
259 readCnt_ = fread(buff, 1, BLOCK_SIZE, tarFilePtr_);
260 if (readCnt_ < BLOCK_SIZE) {
261 HILOGE("Parsing tar file completed, read data count is less then block size.");
262 return {0, fileInfos, errInfos};
263 }
264 TarHeader *header = reinterpret_cast<TarHeader *>(buff);
265 bool isValid = CheckIfTarBlockValid(buff, sizeof(buff), header, ret);
266 if (!isValid) {
267 return {ret, fileInfos, errInfos};
268 }
269 off_t fileSize = HandleTarBuffer(string(buff, BLOCK_SIZE), header->name, info);
270 auto result = ParseFileByTypeFlag(header->typeFlag, info);
271 if ((ret = DealParseTarFileResult(result, fileSize, info.fullPath, fileInfos, errInfos)) != 0) {
272 return {ret, fileInfos, errInfos};
273 }
274 }
275
276 return {ret, fileInfos, errInfos};
277 }
278
DealIncreParseTarFileResult(const std::tuple<int,bool,ErrFileInfo> & result,const off_t fileSize,const std::string & fileName,EndFileInfo & fileInfos,ErrFileInfo & errInfos)279 int UntarFile::DealIncreParseTarFileResult(const std::tuple<int, bool, ErrFileInfo> &result,
280 const off_t fileSize, const std::string &fileName, EndFileInfo &fileInfos, ErrFileInfo &errInfos)
281 {
282 auto [ret, isFilter, subErrInfo] = result;
283 if (ret != 0) {
284 HILOGE("Failed to parse incremental file by type flag");
285 return ret;
286 }
287 if (!isFilter) {
288 fileInfos[fileName] = fileSize;
289 }
290 if (!subErrInfo.empty()) {
291 errInfos.merge(subErrInfo);
292 }
293 return 0;
294 }
295
ParseIncrementalTarFile(const string & rootPath)296 std::tuple<int, EndFileInfo, ErrFileInfo> UntarFile::ParseIncrementalTarFile(const string &rootPath)
297 {
298 // re-parse tar header
299 rootPath_ = rootPath;
300 char buff[BLOCK_SIZE] = {0};
301 FileStatInfo info {};
302 int ret = 0;
303 if ((ret = CheckAndFillTarSize()) != 0) {
304 return {ret, {}, {}};
305 }
306 EndFileInfo fileInfos;
307 ErrFileInfo errFileInfo;
308 do {
309 readCnt_ = fread(buff, 1, BLOCK_SIZE, tarFilePtr_);
310 if (readCnt_ < BLOCK_SIZE) {
311 HILOGE("Parsing tar file completed, read data count is less then block size.");
312 return {0, fileInfos, errFileInfo};
313 }
314 TarHeader *header = reinterpret_cast<TarHeader *>(buff);
315 bool isValid = CheckIfTarBlockValid(buff, sizeof(buff), header, ret);
316 if (!isValid) {
317 return {ret, fileInfos, errFileInfo};
318 }
319 off_t fileSize = HandleTarBuffer(string(buff, BLOCK_SIZE), header->name, info);
320 auto result = ParseIncrementalFileByTypeFlag(header->typeFlag, info);
321 ret = DealIncreParseTarFileResult(result, fileSize, info.fullPath, fileInfos, errFileInfo);
322 if (ret != 0) {
323 return {ret, fileInfos, errFileInfo};
324 }
325 } while (readCnt_ >= BLOCK_SIZE);
326
327 return {ret, fileInfos, errFileInfo};
328 }
329
MatchAregType(bool & isRightRes,FileStatInfo & info,ErrFileInfo & errFileInfo,bool & isFilter)330 void UntarFile::MatchAregType(bool &isRightRes, FileStatInfo &info, ErrFileInfo &errFileInfo, bool &isFilter)
331 {
332 info.fullPath = GenRealPath(rootPath_, info.fullPath);
333 if (!BDir::IsFilePathValid(info.fullPath)) {
334 HILOGE("Check file path : %{public}s err, path is forbidden", GetAnonyPath(info.fullPath).c_str());
335 isRightRes = false;
336 return;
337 }
338 errFileInfo = ParseRegularFile(info);
339 isFilter = false;
340 }
341
MatchDirType(bool & isRightRes,FileStatInfo & info,ErrFileInfo & errFileInfo,bool & isFilter)342 void UntarFile::MatchDirType(bool &isRightRes, FileStatInfo &info, ErrFileInfo &errFileInfo, bool &isFilter)
343 {
344 info.fullPath = GenRealPath(rootPath_, info.fullPath);
345 if (!BDir::IsFilePathValid(info.fullPath)) {
346 HILOGE("Check file path : %{public}s err, path is forbidden", GetAnonyPath(info.fullPath).c_str());
347 isRightRes = false;
348 return;
349 }
350 errFileInfo = CreateDir(info.fullPath, info.mode);
351 isFilter = false;
352 }
353
MatchGnuTypeLongName(bool & isRightRes,FileStatInfo & info,ErrFileInfo & errFileInfo,bool & isFilter)354 void UntarFile::MatchGnuTypeLongName(bool &isRightRes, FileStatInfo &info, ErrFileInfo &errFileInfo, bool &isFilter)
355 {
356 auto result = ReadLongName(info);
357 if (!BDir::IsFilePathValid(info.fullPath) || !BDir::IsFilePathValid(info.longName)) {
358 HILOGE("Check file path : %{public}s or long name : %{public}s err, path is forbidden",
359 GetAnonyPath(info.fullPath).c_str(), GetAnonyPath(info.longName).c_str());
360 isRightRes = false;
361 return;
362 }
363 errFileInfo = std::get<SECOND_PARAM>(result);
364 isFilter = true;
365 isRightRes = (std::get<FIRST_PARAM>(result) == ERR_OK) ? true : false;
366 }
367
MatchExtHeader(bool & isRightRes,FileStatInfo & info,bool & isFilter)368 void UntarFile::MatchExtHeader(bool &isRightRes, FileStatInfo &info, bool &isFilter)
369 {
370 auto [err, LongName] = ParsePaxBlock();
371 if (err != ERR_OK) {
372 isRightRes = false;
373 return;
374 }
375 CheckLongName(LongName, info);
376 isFilter = true;
377 }
378
MatchDefault(bool & isRightRes,FileStatInfo & info)379 void UntarFile::MatchDefault(bool &isRightRes, FileStatInfo &info)
380 {
381 if (fseeko(tarFilePtr_, tarFileBlockCnt_ * BLOCK_SIZE, SEEK_CUR) != 0) {
382 HILOGE("Failed to fseeko of %{private}s, err = %{public}d", info.fullPath.c_str(), errno);
383 isRightRes = false;
384 }
385 }
386
ParseFileByTypeFlag(char typeFlag,FileStatInfo & info)387 tuple<int, bool, ErrFileInfo> UntarFile::ParseFileByTypeFlag(char typeFlag, FileStatInfo &info)
388 {
389 HILOGD("untar file: %{public}s, rootPath: %{public}s", GetAnonyPath(info.fullPath).c_str(), rootPath_.c_str());
390 bool isFilter = true;
391 ErrFileInfo errFileInfo;
392 bool isRightRes = true;
393 switch (typeFlag) {
394 case REGTYPE:
395 case AREGTYPE:
396 MatchAregType(isRightRes, info, errFileInfo, isFilter);
397 break;
398 case SYMTYPE:
399 break;
400 case DIRTYPE:
401 MatchDirType(isRightRes, info, errFileInfo, isFilter);
402 break;
403 case GNUTYPE_LONGNAME:
404 MatchGnuTypeLongName(isRightRes, info, errFileInfo, isFilter);
405 break;
406 case EXTENSION_HEADER:
407 MatchExtHeader(isRightRes, info, isFilter);
408 break;
409 default:
410 MatchDefault(isRightRes, info);
411 }
412 if (!isRightRes) {
413 return {DEFAULT_ERR, true, {{info.fullPath, {DEFAULT_ERR}}}};
414 }
415 return {0, isFilter, errFileInfo};
416 }
417
DealFileTag(ErrFileInfo & errFileInfo,FileStatInfo & info,bool & isFilter,const std::string & tmpFullPath)418 bool UntarFile::DealFileTag(ErrFileInfo &errFileInfo,
419 FileStatInfo &info, bool &isFilter, const std::string &tmpFullPath)
420 {
421 if (!includes_.empty() && includes_.find(tmpFullPath) == includes_.end()) { // not in includes
422 if (fseeko(tarFilePtr_, pos_ + tarFileBlockCnt_ * BLOCK_SIZE, SEEK_SET) != 0) {
423 HILOGE("Failed to fseeko of %{private}s, err = %{public}d", info.fullPath.c_str(), errno);
424 errFileInfo[info.fullPath].emplace_back(DEFAULT_ERR);
425 return false;
426 }
427 isFilter = true;
428 return true;
429 }
430 info.fullPath = GenRealPath(rootPath_, info.fullPath);
431 if (!BDir::IsFilePathValid(info.fullPath)) {
432 HILOGE("Check file path : %{public}s err, path is forbidden", GetAnonyPath(info.fullPath).c_str());
433 errFileInfo[info.fullPath].emplace_back(DEFAULT_ERR);
434 return false;
435 }
436 errFileInfo = ParseRegularFile(info);
437 isFilter = false;
438 return true;
439 }
440
MatchIncrementalScenario(bool isFilter,ErrFileInfo & errFileInfo,string tmpFullPath,char typeFlag,FileStatInfo & info)441 std::tuple<int, bool, ErrFileInfo> UntarFile::MatchIncrementalScenario(bool isFilter, ErrFileInfo &errFileInfo,
442 string tmpFullPath, char typeFlag, FileStatInfo &info)
443 {
444 switch (typeFlag) {
445 case REGTYPE:
446 case AREGTYPE: {
447 if (!DealFileTag(errFileInfo, info, isFilter, tmpFullPath)) {
448 return {DEFAULT_ERR, true, errFileInfo};
449 }
450 break;
451 }
452 case SYMTYPE:
453 break;
454 case DIRTYPE:
455 info.fullPath = GenRealPath(rootPath_, info.fullPath);
456 if (!BDir::IsFilePathValid(info.fullPath)) {
457 HILOGE("Check file path : %{public}s err, path is forbidden", GetAnonyPath(info.fullPath).c_str());
458 return {DEFAULT_ERR, true, {{info.fullPath, {DEFAULT_ERR}}}};
459 }
460 errFileInfo = CreateDir(info.fullPath, info.mode);
461 isFilter = false;
462 break;
463 case GNUTYPE_LONGNAME: {
464 auto result = ReadLongName(info);
465 if (!BDir::IsFilePathValid(info.fullPath)) {
466 HILOGE("Check file path : %{public}s err, path is forbidden", GetAnonyPath(info.fullPath).c_str());
467 return {DEFAULT_ERR, true, {{info.fullPath, {DEFAULT_ERR}}}};
468 }
469 return {std::get<FIRST_PARAM>(result), isFilter, std::get<SECOND_PARAM>(result)};
470 break;
471 }
472 case EXTENSION_HEADER: { // pax x header
473 auto [err, LongName] = ParsePaxBlock();
474 if (err == ERR_OK) {
475 CheckLongName(LongName, info);
476 return {err, true, errFileInfo};
477 }
478 return {err, isFilter, {{info.fullPath, {DEFAULT_ERR}}}};
479 break;
480 }
481 default: {
482 if (fseeko(tarFilePtr_, tarFileBlockCnt_ * BLOCK_SIZE, SEEK_CUR) != 0) {
483 HILOGE("Failed to fseeko of %{private}s, err = %{public}d", info.fullPath.c_str(), errno);
484 return {DEFAULT_ERR, true, {{info.fullPath, {DEFAULT_ERR}}}};
485 }
486 break;
487 }
488 }
489 return {0, isFilter, errFileInfo};
490 }
491
ParseIncrementalFileByTypeFlag(char typeFlag,FileStatInfo & info)492 std::tuple<int, bool, ErrFileInfo> UntarFile::ParseIncrementalFileByTypeFlag(char typeFlag, FileStatInfo &info)
493 {
494 HILOGD("untar file: %{public}s, rootPath: %{public}s", GetAnonyPath(info.fullPath).c_str(), rootPath_.c_str());
495 string tmpFullPath = info.fullPath;
496 bool isFilter = true;
497 ErrFileInfo errFileInfo;
498 RTrimNull(tmpFullPath);
499 return MatchIncrementalScenario(isFilter, errFileInfo, tmpFullPath, typeFlag, info);
500 }
501
ParseRegularFile(FileStatInfo & info)502 ErrFileInfo UntarFile::ParseRegularFile(FileStatInfo &info)
503 {
504 ErrFileInfo errFileInfo;
505 FILE *destFile = CreateFile(info.fullPath);
506 if (destFile != nullptr) {
507 string destStr("");
508 destStr.resize(READ_BUFF_SIZE);
509 size_t remainSize = static_cast<size_t>(tarFileSize_);
510 size_t readBuffSize = READ_BUFF_SIZE;
511 while (remainSize > 0) {
512 if (remainSize < READ_BUFF_SIZE) {
513 readBuffSize = remainSize;
514 }
515 auto readSize = fread(&destStr[0], sizeof(char), readBuffSize, tarFilePtr_);
516 if (readSize != readBuffSize) {
517 readBuffSize = readSize;
518 }
519 size_t writeSize = 0;
520 do {
521 writeSize += fwrite(&destStr[writeSize], sizeof(char), readBuffSize - writeSize, destFile);
522 } while (writeSize < readBuffSize);
523 remainSize -= readBuffSize;
524 }
525 fclose(destFile);
526 if (chmod(info.fullPath.data(), info.mode) != 0) {
527 HILOGE("Failed to chmod of %{public}s, err = %{public}d", GetAnonyPath(info.fullPath).c_str(), errno);
528 errFileInfo[info.fullPath].emplace_back(errno);
529 }
530 struct utimbuf times;
531 struct stat attr;
532 if (stat(info.fullPath.c_str(), &attr) != 0) {
533 errFileInfo[info.fullPath].emplace_back(errno);
534 HILOGE("Failed to get stat of %{public}s, err = %{public}d", GetAnonyPath(info.fullPath).c_str(), errno);
535 times.actime = info.mtime;
536 } else {
537 times.actime = attr.st_atime;
538 }
539 times.modtime = info.mtime;
540 if (info.mtime != 0 && utime(info.fullPath.c_str(), ×) != 0) {
541 errFileInfo[info.fullPath].emplace_back(errno);
542 HILOGE("Failed to set mtime of %{public}s, err = %{public}d", GetAnonyPath(info.fullPath).c_str(), errno);
543 }
544 // anyway, go to correct
545 fseeko(tarFilePtr_, pos_ + tarFileBlockCnt_ * BLOCK_SIZE, SEEK_SET);
546 } else {
547 HILOGE("Failed to create file %{public}s, err = %{public}d", GetAnonyPath(info.fullPath).c_str(), errno);
548 errFileInfo[info.fullPath].emplace_back(errno);
549 fseeko(tarFilePtr_, tarFileBlockCnt_ * BLOCK_SIZE, SEEK_CUR);
550 }
551 return errFileInfo;
552 }
553
VerifyChecksum(TarHeader & header)554 bool UntarFile::VerifyChecksum(TarHeader &header)
555 {
556 vector<uint8_t> buffer {};
557 buffer.resize(sizeof(header));
558 buffer.assign(reinterpret_cast<uint8_t *>(&header), reinterpret_cast<uint8_t *>(&header) + sizeof(header));
559 int sum = 0;
560 for (uint32_t index = 0; index < BLOCK_SIZE; ++index) {
561 if (index < CHKSUM_BASE || index > CHKSUM_BASE + CHKSUM_LEN - 1) {
562 // Standard tar checksum adds unsigned bytes.
563 sum += (buffer[index] & 0xFF);
564 } else {
565 sum += BLANK_SPACE;
566 }
567 }
568 string strChksum;
569 strChksum.assign(buffer.begin(), buffer.end());
570 return (sum == ParseOctalStr(&strChksum[0] + CHKSUM_BASE, CHKSUM_LEN));
571 }
572
IsValidTarBlock(TarHeader & header)573 bool UntarFile::IsValidTarBlock(TarHeader &header)
574 {
575 // check magic && checksum
576 if (strncmp(header.magic, TMAGIC.c_str(), TMAGIC_LEN - 1) == 0 && VerifyChecksum(header)) {
577 return true;
578 }
579 HILOGE("Invalid tar block");
580 return false;
581 }
582
GenRealPath(const string & rootPath,const string & realName)583 string UntarFile::GenRealPath(const string &rootPath, const string &realName)
584 {
585 if (rootPath.empty() || realName.empty()) {
586 return "";
587 }
588 string realPath(rootPath);
589 size_t len = realPath.length();
590 if (realPath[len - 1] == '/') {
591 realPath = realPath.substr(0, len - 1);
592 }
593 realPath.append((realName[0] == '/') ? realName : ("/" + realName));
594 if (realPath[0] == '/') {
595 realPath = realPath.substr(1, realPath.length());
596 }
597 return realPath;
598 }
599
CreateDir(string & path,mode_t mode)600 ErrFileInfo UntarFile::CreateDir(string &path, mode_t mode)
601 {
602 ErrFileInfo errFileInfo;
603 if (path.empty()) {
604 return errFileInfo;
605 }
606 size_t len = path.length();
607 if (path[len - 1] == '/') {
608 path[len - 1] = '\0';
609 }
610 if (access(path.c_str(), F_OK) != 0) {
611 HILOGE("directory does not exist, path:%{public}s, err = %{public}d", path.c_str(), errno);
612 if (!ForceCreateDirectoryWithMode(path, mode)) {
613 HILOGE("Failed to force create directory %{public}s, err = %{public}d", path.c_str(), errno);
614 errFileInfo[path].emplace_back(errno);
615 }
616 }
617 return errFileInfo;
618 }
619
CreateFile(string & filePath)620 FILE *UntarFile::CreateFile(string &filePath)
621 {
622 FILE *f = nullptr;
623 char rpath[PATH_MAX] = {0};
624 if (realpath(filePath.c_str(), rpath)) {
625 f = fopen(filePath.c_str(), "wb+");
626 if (f == nullptr) {
627 HILOGE("Failed to create file %{public}s, err = %{public}d", GetAnonyPath(filePath).c_str(), errno);
628 }
629 return f;
630 }
631
632 uint32_t len = filePath.length();
633 HILOGD("Failed to open file %{public}d, %{public}s, err = %{public}d, Will create", len,
634 GetAnonyPath(filePath).c_str(), errno);
635 size_t pos = filePath.rfind('/');
636 if (pos == string::npos) {
637 return nullptr;
638 }
639
640 string path = filePath.substr(0, pos);
641 if (ForceCreateDirectory(path)) {
642 if (!realpath(path.c_str(), rpath)) {
643 HILOGE("Failed to access path %{public}s, err = %{public}d", GetAnonyPath(path).c_str(), errno);
644 return nullptr;
645 }
646 f = fopen(filePath.c_str(), "wb+");
647 if (f == nullptr) {
648 HILOGE("Failed to open file %{public}s, err = %{public}d", GetAnonyPath(filePath).c_str(), errno);
649 }
650 }
651
652 return f;
653 }
654
ParsePaxBlock()655 std::tuple<int, std::string> UntarFile::ParsePaxBlock()
656 {
657 int err = DEFAULT_ERR;
658 char block[BLOCK_SIZE] = {0};
659 auto readCnt = fread(block, 1, BLOCK_SIZE, tarFilePtr_);
660 if (readCnt < BLOCK_SIZE) {
661 HILOGE("Parsing tar file completed, read data count is less then block size.");
662 return {err, ""};
663 }
664 string content(block, BLOCK_SIZE);
665 string longName = "";
666 size_t pos = 0;
667 uint32_t recLen = 0;
668 uint32_t allLen = 0;
669 bool isLongName = false;
670 while (pos < BLOCK_SIZE) {
671 size_t lenEnd = content.find(' ', pos);
672 if (lenEnd == string::npos) {
673 err = ERR_OK;
674 break;
675 }
676 string pathLen = content.substr(pos, lenEnd - pos);
677 recLen = static_cast<uint32_t>(std::atoi(pathLen.c_str()));
678 allLen = recLen + OTHER_HEADER;
679 if (allLen > BLOCK_SIZE) {
680 isLongName = true;
681 break;
682 }
683 string kvPair = content.substr(lenEnd + 1, recLen - (lenEnd - pos + 1));
684 size_t eqPos = kvPair.find('=');
685 if (eqPos == string::npos) {
686 break;
687 }
688 string key = kvPair.substr(0, eqPos);
689 string value = kvPair.substr(eqPos + 1);
690 if (key == "path") {
691 longName = value;
692 err = ERR_OK;
693 }
694 pos += recLen;
695 }
696 if (isLongName) {
697 HILOGI("is long name");
698 return GetLongName(recLen, allLen);
699 }
700 return {err, longName};
701 }
702
CheckLongName(std::string longName,FileStatInfo & info)703 void UntarFile::CheckLongName(std::string longName, FileStatInfo &info)
704 {
705 if (!longName.empty() && longName.back() == '\n') {
706 longName.pop_back();
707 info.longName = longName;
708 }
709 }
710
GetLongName(uint32_t recLen,uint32_t allLen)711 std::tuple<int, std::string> UntarFile::GetLongName(uint32_t recLen, uint32_t allLen)
712 {
713 int err = DEFAULT_ERR;
714 off_t curPos = ftello(tarFilePtr_);
715 if (fseeko(tarFilePtr_, curPos - BLOCK_SIZE, SEEK_SET) != 0) {
716 HILOGE("fseeko failed");
717 return {err, ""};
718 }
719 double result = static_cast<double>(allLen) / BLOCK_SIZE;
720 auto ret = static_cast<uint32_t>(std::ceil(result));
721 auto curSize = ret * BLOCK_SIZE;
722 char *block = new char[curSize];
723 if (memset_s(block, curSize, 0, curSize) != EOK) {
724 delete[] block;
725 return {err, ""};
726 }
727 auto readCnt = fread(block, 1, curSize, tarFilePtr_);
728 if (readCnt < curSize) {
729 HILOGE("Parsing tar file completed, read data count is less then block size.");
730 delete[] block;
731 return {err, ""};
732 }
733 string content(block, curSize);
734 string longName = "";
735 size_t pos = 0;
736 while (pos < curSize) {
737 size_t lenEnd = content.find(' ', pos);
738 if (lenEnd == string::npos) {
739 err = ERR_OK;
740 break;
741 }
742 string pathLen = content.substr(pos, lenEnd - pos);
743 size_t recLen = static_cast<size_t>(std::atoi(pathLen.c_str()));
744 string KvPair = content.substr(lenEnd + 1, recLen - (lenEnd - pos + 1));
745 size_t eqPos = KvPair.find('=');
746 if (eqPos == string::npos) {
747 break;
748 }
749 string key = KvPair.substr(0, eqPos);
750 string value = KvPair.substr(eqPos + 1);
751 if (key == "path") {
752 longName = value;
753 err = ERR_OK;
754 }
755 pos += recLen;
756 }
757 delete[] block;
758 return {err, longName};
759 }
760 } // namespace OHOS::FileManagement::Backup