1 /*
2  * Copyright (c) 2023-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 "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].push_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].push_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 
ParseFileByTypeFlag(char typeFlag,FileStatInfo & info)330 tuple<int, bool, ErrFileInfo> UntarFile::ParseFileByTypeFlag(char typeFlag, FileStatInfo &info)
331 {
332     HILOGD("untar file: %{public}s, rootPath: %{public}s", GetAnonyPath(info.fullPath).c_str(), rootPath_.c_str());
333     bool isFilter = true;
334     ErrFileInfo errFileInfo;
335     switch (typeFlag) {
336         case REGTYPE:
337         case AREGTYPE:
338             info.fullPath = GenRealPath(rootPath_, info.fullPath);
339             if (BDir::CheckFilePathInvalid(info.fullPath)) {
340                 HILOGE("Check file path : %{public}s err, path is forbidden", GetAnonyPath(info.fullPath).c_str());
341                 return {DEFAULT_ERR, true, {{info.fullPath, {DEFAULT_ERR}}}};
342             }
343             errFileInfo = ParseRegularFile(info);
344             isFilter = false;
345             break;
346         case SYMTYPE:
347             break;
348         case DIRTYPE:
349             info.fullPath = GenRealPath(rootPath_, info.fullPath);
350             if (BDir::CheckFilePathInvalid(info.fullPath)) {
351                 HILOGE("Check file path : %{public}s err, path is forbidden", GetAnonyPath(info.fullPath).c_str());
352                 return {DEFAULT_ERR, true, {{info.fullPath, {DEFAULT_ERR}}}};
353             }
354             errFileInfo = CreateDir(info.fullPath, info.mode);
355             isFilter = false;
356             break;
357         case GNUTYPE_LONGNAME: {
358             auto result = ReadLongName(info);
359             if (BDir::CheckFilePathInvalid(info.fullPath) || BDir::CheckFilePathInvalid(info.longName)) {
360                 HILOGE("Check file path : %{public}s or long name : %{public}s err, path is forbidden",
361                     GetAnonyPath(info.fullPath).c_str(), GetAnonyPath(info.longName).c_str());
362                 return {DEFAULT_ERR, true, {{info.fullPath, {DEFAULT_ERR}}}};
363             }
364             errFileInfo = std::get<SECOND_PARAM>(result);
365             return {std::get<FIRST_PARAM>(result), isFilter, errFileInfo};
366             break;
367         }
368         default: {
369             if (fseeko(tarFilePtr_, tarFileBlockCnt_ * BLOCK_SIZE, SEEK_CUR) != 0) {
370                 HILOGE("Failed to fseeko of %{private}s, err = %{public}d", info.fullPath.c_str(), errno);
371                 return {-1, true, errFileInfo};
372             }
373             break;
374         }
375     }
376     return {0, isFilter, errFileInfo};
377 }
378 
DealFileTag(ErrFileInfo & errFileInfo,FileStatInfo & info,bool & isFilter,const std::string & tmpFullPath)379 bool UntarFile::DealFileTag(ErrFileInfo &errFileInfo,
380     FileStatInfo &info, bool &isFilter, const std::string &tmpFullPath)
381 {
382     if (!includes_.empty() && includes_.find(tmpFullPath) == includes_.end()) { // not in includes
383         if (fseeko(tarFilePtr_, pos_ + tarFileBlockCnt_ * BLOCK_SIZE, SEEK_SET) != 0) {
384             HILOGE("Failed to fseeko of %{private}s, err = %{public}d", info.fullPath.c_str(), errno);
385             errFileInfo[info.fullPath].push_back(DEFAULT_ERR);
386             return false;
387         }
388         return true;
389     }
390     info.fullPath = GenRealPath(rootPath_, info.fullPath);
391     if (BDir::CheckFilePathInvalid(info.fullPath)) {
392         HILOGE("Check file path : %{public}s err, path is forbidden", GetAnonyPath(info.fullPath).c_str());
393         errFileInfo[info.fullPath].push_back(DEFAULT_ERR);
394         return false;
395     }
396     errFileInfo = ParseRegularFile(info);
397     isFilter = false;
398     return true;
399 }
400 
401 
ParseIncrementalFileByTypeFlag(char typeFlag,FileStatInfo & info)402 std::tuple<int, bool, ErrFileInfo> UntarFile::ParseIncrementalFileByTypeFlag(char typeFlag, FileStatInfo &info)
403 {
404     HILOGD("untar file: %{public}s, rootPath: %{public}s", GetAnonyPath(info.fullPath).c_str(), rootPath_.c_str());
405     string tmpFullPath = info.fullPath;
406     bool isFilter = true;
407     ErrFileInfo errFileInfo;
408     RTrimNull(tmpFullPath);
409     switch (typeFlag) {
410         case REGTYPE:
411         case AREGTYPE: {
412             if (!DealFileTag(errFileInfo, info, isFilter, tmpFullPath)) {
413                 return {DEFAULT_ERR, true, errFileInfo};
414             }
415             break;
416         }
417         case SYMTYPE:
418             break;
419         case DIRTYPE:
420             info.fullPath = GenRealPath(rootPath_, info.fullPath);
421             if (BDir::CheckFilePathInvalid(info.fullPath)) {
422                 HILOGE("Check file path : %{public}s err, path is forbidden", GetAnonyPath(info.fullPath).c_str());
423                 return {DEFAULT_ERR, true, {{info.fullPath, {DEFAULT_ERR}}}};
424             }
425             errFileInfo = CreateDir(info.fullPath, info.mode);
426             isFilter = false;
427             break;
428         case GNUTYPE_LONGNAME: {
429             auto result = ReadLongName(info);
430             if (BDir::CheckFilePathInvalid(info.fullPath)) {
431                 HILOGE("Check file path : %{public}s err, path is forbidden", GetAnonyPath(info.fullPath).c_str());
432                 return {DEFAULT_ERR, true, {{info.fullPath, {DEFAULT_ERR}}}};
433             }
434             return {std::get<FIRST_PARAM>(result), isFilter, std::get<SECOND_PARAM>(result)};
435             break;
436         }
437         default: {
438             if (fseeko(tarFilePtr_, tarFileBlockCnt_ * BLOCK_SIZE, SEEK_CUR) != 0) {
439                 HILOGE("Failed to fseeko of %{private}s, err = %{public}d", info.fullPath.c_str(), errno);
440                 return {DEFAULT_ERR, true, {{info.fullPath, {DEFAULT_ERR}}}};
441             }
442             break;
443         }
444     }
445 
446     return {0, isFilter, errFileInfo};
447 }
448 
ParseRegularFile(FileStatInfo & info)449 ErrFileInfo UntarFile::ParseRegularFile(FileStatInfo &info)
450 {
451     ErrFileInfo errFileInfo;
452     FILE *destFile = CreateFile(info.fullPath);
453     if (destFile != nullptr) {
454         string destStr("");
455         destStr.resize(READ_BUFF_SIZE);
456         size_t remainSize = static_cast<size_t>(tarFileSize_);
457         size_t readBuffSize = READ_BUFF_SIZE;
458         while (remainSize > 0) {
459             if (remainSize < READ_BUFF_SIZE) {
460                 readBuffSize = remainSize;
461             }
462             fread(&destStr[0], sizeof(char), readBuffSize, tarFilePtr_);
463             fwrite(&destStr[0], sizeof(char), readBuffSize, destFile);
464             remainSize -= readBuffSize;
465         }
466         fclose(destFile);
467         if (chmod(info.fullPath.data(), info.mode) != 0) {
468             HILOGE("Failed to chmod of %{public}s, err = %{public}d", GetAnonyPath(info.fullPath).c_str(), errno);
469             errFileInfo[info.fullPath].push_back(errno);
470         }
471         struct utimbuf times;
472         struct stat attr;
473         if (stat(info.fullPath.c_str(), &attr) != 0) {
474             errFileInfo[info.fullPath].push_back(errno);
475             HILOGE("Failed to get stat of %{public}s, err = %{public}d", GetAnonyPath(info.fullPath).c_str(), errno);
476             times.actime = info.mtime;
477         } else {
478             times.actime = attr.st_atime;
479         }
480         times.modtime = info.mtime;
481         if (info.mtime != 0 && utime(info.fullPath.c_str(), &times) != 0) {
482             errFileInfo[info.fullPath].push_back(errno);
483             HILOGE("Failed to set mtime of %{public}s, err = %{public}d", GetAnonyPath(info.fullPath).c_str(), errno);
484         }
485         // anyway, go to correct
486         fseeko(tarFilePtr_, pos_ + tarFileBlockCnt_ * BLOCK_SIZE, SEEK_SET);
487     } else {
488         HILOGE("Failed to create file %{public}s, err = %{public}d", GetAnonyPath(info.fullPath).c_str(), errno);
489         errFileInfo[info.fullPath].push_back(errno);
490         fseeko(tarFilePtr_, tarFileBlockCnt_ * BLOCK_SIZE, SEEK_CUR);
491     }
492     return errFileInfo;
493 }
494 
VerifyChecksum(TarHeader & header)495 bool UntarFile::VerifyChecksum(TarHeader &header)
496 {
497     vector<uint8_t> buffer {};
498     buffer.resize(sizeof(header));
499     buffer.assign(reinterpret_cast<uint8_t *>(&header), reinterpret_cast<uint8_t *>(&header) + sizeof(header));
500     int sum = 0;
501     for (uint32_t index = 0; index < BLOCK_SIZE; ++index) {
502         if (index < CHKSUM_BASE || index > CHKSUM_BASE + CHKSUM_LEN - 1) {
503             // Standard tar checksum adds unsigned bytes.
504             sum += (buffer[index] & 0xFF);
505         } else {
506             sum += BLANK_SPACE;
507         }
508     }
509     string strChksum;
510     strChksum.assign(buffer.begin(), buffer.end());
511     return (sum == ParseOctalStr(&strChksum[0] + CHKSUM_BASE, CHKSUM_LEN));
512 }
513 
IsValidTarBlock(TarHeader & header)514 bool UntarFile::IsValidTarBlock(TarHeader &header)
515 {
516     // check magic && checksum
517     if (strncmp(header.magic, TMAGIC.c_str(), TMAGIC_LEN - 1) == 0 && VerifyChecksum(header)) {
518         return true;
519     }
520     HILOGE("Invalid tar block");
521     return false;
522 }
523 
GenRealPath(const string & rootPath,const string & realName)524 string UntarFile::GenRealPath(const string &rootPath, const string &realName)
525 {
526     if (rootPath.empty() || realName.empty()) {
527         return "";
528     }
529     string realPath(rootPath);
530     size_t len = realPath.length();
531     if (realPath[len - 1] == '/') {
532         realPath = realPath.substr(0, len - 1);
533     }
534     realPath.append((realName[0] == '/') ? realName : ("/" + realName));
535     if (realPath[0] == '/') {
536         realPath = realPath.substr(1, realPath.length());
537     }
538     return realPath;
539 }
540 
CreateDir(string & path,mode_t mode)541 ErrFileInfo UntarFile::CreateDir(string &path, mode_t mode)
542 {
543     ErrFileInfo errFileInfo;
544     if (path.empty()) {
545         return errFileInfo;
546     }
547     size_t len = path.length();
548     if (path[len - 1] == '/') {
549         path[len - 1] = '\0';
550     }
551     if (access(path.c_str(), F_OK) != 0) {
552         HILOGE("directory does not exist, path:%{public}s, err = %{public}d", path.c_str(), errno);
553         if (!ForceCreateDirectoryWithMode(path, mode)) {
554             HILOGE("Failed to force create directory %{public}s, err = %{public}d", path.c_str(), errno);
555             errFileInfo[path].push_back(errno);
556         }
557     }
558     return errFileInfo;
559 }
560 
CreateFile(string & filePath)561 FILE *UntarFile::CreateFile(string &filePath)
562 {
563     FILE *f = fopen(filePath.c_str(), "wb+");
564     if (f != nullptr) {
565         return f;
566     }
567 
568     uint32_t len = filePath.length();
569     HILOGE("Failed to open file %{public}d, %{public}s, err = %{public}d", len,
570         GetAnonyPath(filePath).c_str(), errno);
571     size_t pos = filePath.rfind('/');
572     if (pos == string::npos) {
573         return nullptr;
574     }
575 
576     string path = filePath.substr(0, pos);
577     if (ForceCreateDirectory(path)) {
578         f = fopen(filePath.c_str(), "wb+");
579         if (f == nullptr) {
580             HILOGE("Failed to open file %{public}s, err = %{public}d", GetAnonyPath(filePath).c_str(), errno);
581         }
582     }
583 
584     return f;
585 }
586 
587 } // namespace OHOS::FileManagement::Backup