diff --git a/frameworks/native/backup_ext/BUILD.gn b/frameworks/native/backup_ext/BUILD.gn index 6a0f591c68e9c870df4beccf6f218c1c7f79baa2..a256453d8db83c95f57a1aa4f4e70e556fa371d1 100644 --- a/frameworks/native/backup_ext/BUILD.gn +++ b/frameworks/native/backup_ext/BUILD.gn @@ -33,6 +33,7 @@ ohos_shared_library("backup_extension_ability_native") { "src/ext_backup_js.cpp", "src/ext_backup_loader.cpp", "src/ext_extension.cpp", + "src/installd_un_tar_file.cpp", "src/sub_ext_extension.cpp", "src/tar_file.cpp", "src/untar_file.cpp", diff --git a/frameworks/native/backup_ext/include/ext_extension.h b/frameworks/native/backup_ext/include/ext_extension.h index 4abcac15bc02b963b39266b3e3266b3c08cb2a70..f298b40f9c67d388f8a41f05e9564e9fa2aef3fd 100644 --- a/frameworks/native/backup_ext/include/ext_extension.h +++ b/frameworks/native/backup_ext/include/ext_extension.h @@ -345,6 +345,11 @@ private: ErrCode CloudSpecialRestore(std::string tarName, std::string untarPath, off_t tarFileSize); void GetTarIncludes(const string &tarName, unordered_map &infos); void DeleteIndexAndRpFile(); + ErrCode RestoreTarListForSpecialCloneCloud(const std::vector &tarList); + bool CheckIsSplitTarList(const std::vector &tarList); + ErrCode RestoreUnSplitTarListForSpecialCloneCloud(const std::vector &tarList); + ErrCode RestoreSplitTarListForSpecialCloneCloud(const std::vector &tarList); + tuple GetIncreFileHandleForSpecialVersion(const string &fileName); void RmBigFileReportForSpecialCloneCloud(const std::string &srcFile); string GetReportFileName(const string &fileName); diff --git a/frameworks/native/backup_ext/include/installd_tar_utils.h b/frameworks/native/backup_ext/include/installd_tar_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..f25221607397d3191fe0703535fccf2fdd60d652 --- /dev/null +++ b/frameworks/native/backup_ext/include/installd_tar_utils.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PHONECLONE_INSTALLDTARUTILS_H +#define PHONECLONE_INSTALLDTARUTILS_H +#include +#include +#include "securec.h" +#include "log.h" + +namespace installd { +#define UID_GID_OFFSET 10000 +#define APP_ID_START 10000 +#define EOVERLAP_AND_RESET 182 +#define TMAGIC "ustar" /* ustar and a null */ +#define TMAGIC_LEN 6 + +#define TMODE_BASE 100 +#define TMODE_LEN 8 +#define TUID_BASE 108 +#define TUID_LEN 8 +#define TGID_BASE 116 +#define TGID_LEN 8 +#define TSIZE_BASE 124 +#define TSIZE_LEN 12 + +#define CHKSUM_BASE 148 + +#define BLOCK_SIZE 512 +#define BLANK_SPACE 0x20 + +#define PATH_MAX_LEN 2048 +#define READ_BUFF_SIZE (512 * 1024) +const int MB_TO_BYTE = (1024 * 1024); + +#define ERR_PARAM (-1) +#define ERR_NOEXIST (-2) +#define ERR_FORMAT (-3) +#define ERR_MALLOC (-4) +#define ERR_IO (-5) + +#define REGTYPE '0' /* regular file */ +#define AREGTYPE '\0' /* regular file */ +#define SYMTYPE '2' /* reserved */ +#define DIRTYPE '5' /* directory */ +#define SPLIT_START_TYPE '8' +#define SPLIT_CONTINUE_TYPE '9' +#define SPLIT_END_TYPE 'A' + +#define GNUTYPE_LONGLINK 'K' + +#define PERMISSION_MASK 07777 +#define MAX_FILESIZE 0777777777777LL +const int SIZE_OF_LONG_IN_32_SYSTEM = 4; + +#define OCTSTRING_LENGTH (sizeof(off_t) * 3 + 1) + +#define LONG_LINK_SYMBOL "././@LongLink" +#define VERSION "00" +#define SPACE ' ' +#define SLASH '/' + +#define DO_ERROR (-1) +#define DO_CONTINUE 0 +#define DO_IGNORETHIS 1 +#define DO_TRAVERSAL 2 +#define DO_EXIT 3 +#define SPLIT_SIZE (1024 * 1024 * 500) // 500M +#define FILTER_SIZE (1024 * 1024 * 100) // 100M + +// callback +class TarCallBack { +public: + virtual ~TarCallBack() {} + +public: + virtual bool IsAborted() = 0; + virtual void OnTaskProgress(size_t szProcessed) = 0; + virtual void OnPackagedUseBriefGenTar(long szProcessed) = 0; + virtual void OnPackagedOneSplitTar(const char *name) = 0; + virtual void WaitTaskLocked() = 0; +}; + +const int PATH_LENTH = 2048; +struct File_info { + char path[PATH_LENTH]; + int mode; + int type; + long size; + time_t modified_time; +}; +} // namespace installd +#endif // PHONECLONE_INSTALLDTARUTILS_H \ No newline at end of file diff --git a/frameworks/native/backup_ext/include/installd_un_tar_file.h b/frameworks/native/backup_ext/include/installd_un_tar_file.h new file mode 100644 index 0000000000000000000000000000000000000000..1d35bbb5a74814a0e8e51469c2691e8a638c3ff7 --- /dev/null +++ b/frameworks/native/backup_ext/include/installd_un_tar_file.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PHONECLONE_INSTALLDUNTARFILE_H +#define PHONECLONE_INSTALLDUNTARFILE_H +#include "installd_tar_utils.h" +#include "tar_util.h" +#include +#include + +namespace installd { + +struct ParseTarPath { + char *longName = nullptr; + char *longLink = nullptr; + char *fullPath = nullptr; + char *realName = nullptr; + char *realLink = nullptr; +}; + +struct TarFileInfo { + off_t fileSize; + off_t fileBlockCnt; + off_t pos; +}; + +// helper function. +off_t ParseOctalStr(const char *p, size_t n); + +class UnTarFile { +public: + UnTarFile(const char *TarPath); + virtual ~UnTarFile(void); + +public: + std::vector GetFileNames(); + int UnSplitPack(const char *path, uid_t owner = 0); + int UnPack(const char *path, uid_t owner = 0); + void Reset(); + bool CheckIsSplitTar(const std::string &tarFile, const std::string &rootpath); + int UnSplitTar(const std::string &tarFile, const std::string &rootpath); + +public: + typedef enum { eList = 0, eUnpack = 1, eCheckSplit = 2 } EParseType; + +private: + bool IsProcessTarEnd(char *buff, int &ret); + int ParseTarFile(const char *rootPath = "", EParseType type = eList); + bool IsEmptyBlock(const char *p); + int CheckFileAndInitPath(const char *rootPath, ParseTarPath *parseTarPath); + void SetFileChmodAndChown(char *buff, ParseTarPath *parseTarPath, bool &isSoftLink); + void HandleGnuLongLink(ParseTarPath *parseTarPath, bool &isSkip, TarFileInfo &tarFileInfo); + void HandleGnuLongName(ParseTarPath *parseTarPath, bool &isSkip, TarFileInfo &tarFileInfo); + void HandleRegularFile(char *buff, EParseType type, ParseTarPath *parseTarPath, bool &isSkip, + TarFileInfo &tarFileInfo); + bool FileReadAndWrite(char *destBuff, FILE *destF, size_t readBuffSize); + void HandleRegularEUnpackFile(char *buff, ParseTarPath *parseTarPath, bool &isSkip, TarFileInfo &tarFileInfo); + bool ProcessTarBlock(char *buff, EParseType type, ParseTarPath *parseTarPath, bool &isSkip, bool &isSoftLink); + bool IsValidTarBlock(const TarHeader *tarHeader); + bool VerifyChecksum(const TarHeader *tarHeader); + bool CheckSliceTar(const char *tarInfo, const char *dstPathName, std::vector &fileNameVector); + bool HandleCheckFile(const char *tarBaseName, std::vector &fileNameVector, int &num); + void FreePointer(ParseTarPath *parseTarPath); + void FreeLongTypePointer(ParseTarPath *parseTarPath); + bool CreateDirWithRecursive(const std::string &filePath, mode_t mode = (mode_t)448); + +private: + FILE *FilePtr; + off_t tarSize; + uid_t newOwner; + std::string m_srcPath; + bool isSplit = false; + std::vector file_names; + std::vector file_sizes; + std::vector file_data_addrs; +}; +} // namespace installd +#endif // PHONECLONE_INSTALLDUNTARFILE_H \ No newline at end of file diff --git a/frameworks/native/backup_ext/include/tar_util.h b/frameworks/native/backup_ext/include/tar_util.h new file mode 100644 index 0000000000000000000000000000000000000000..e253e47286d31b4dcca97989cb1c4e4af88f1b7b --- /dev/null +++ b/frameworks/native/backup_ext/include/tar_util.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TAR_UTIL_H +#define TAR_UTIL_H + +#include + +class TarUtil { +public: + static const int32_t MODE_MASK = 07777; + static const int32_t NAME_LEN = 100; + static const int32_t MODE_LEN = 8; + static const int32_t SIZE_LEN = 12; + static const int32_t CHKSUM_LEN = 8; + static const int32_t BLOCK_LEN = 512; + static const int32_t MAX_PATH_LEN = 4096; // Linux系统中的文件路径长度上限为4096个字符 + static const int32_t RENAME_START_CNT = 1; + static const int32_t CHKSUM_ASCII_VALUE = 256; + static const char GNUTYPE_LONGNAME = 'L'; +}; + +enum RESULT { + OK = 0, + ERROR = -1, + NULL_POINTER = -2, + LIST_EMPTY = -3, + PATH_EMPTY = -4, + OPEN_FILE_FAIL = -5, + FILE_NOT_EXIST = -6, + FILE_UNREADABLE = -7, + READ_INCOMPLETE = -8, + WRITE_INCOMPLETE = -9, + FILE_REMOVE_FAIL = -10, + MAKE_DIR_FAIL = -11, +}; + +typedef struct { /* byte offset */ + char name[100]; /* 0 */ + char mode[8]; /* 100 */ + char uid[8]; /* 108 */ + char gid[8]; /* 116 */ + char size[12]; /* 124 */ + char mtime[12]; /* 136 */ + char chksum[8]; /* 148 */ + char typeflag; /* 156 */ + char linkname[100]; /* 157 */ + char magic[6]; /* 257 */ + char version[2]; /* 263 */ + char uname[32]; /* 265 */ + char gname[32]; /* 297 */ + char devmajor[8]; /* 329 */ + char devminor[8]; /* 337 */ + char prefix[155]; /* 345 */ + char paddings[12]; /* 500 */ +} TarHeader; + +#endif // TAR_UTIL_H \ No newline at end of file diff --git a/frameworks/native/backup_ext/src/ext_extension.cpp b/frameworks/native/backup_ext/src/ext_extension.cpp index ba0764b4f7336d6c4d47ad0004c8d858798667ba..0158285b3b53abd76b10d9f75e7e1f7a9a06651a 100644 --- a/frameworks/native/backup_ext/src/ext_extension.cpp +++ b/frameworks/native/backup_ext/src/ext_extension.cpp @@ -54,6 +54,7 @@ #include "b_utils/b_time.h" #include "filemgmt_libhilog.h" #include "hitrace_meter.h" +#include "installd_un_tar_file.h" #include "iservice.h" #include "sandbox_helper.h" #include "service_client.h" @@ -1147,6 +1148,96 @@ ErrCode BackupExtExtension::RestoreTarForSpecialCloneCloud(const ExtManageInfo & return ERR_OK; } +ErrCode BackupExtExtension::RestoreTarListForSpecialCloneCloud(const std::vector &tarList) +{ + if (tarList.empty()) { + HILOGI("no tar files, bundle: %{public}s", bundleName_.c_str()); + return ERR_OK; + } + bool isSplit = CheckIsSplitTarList(tarList); + if (!isSplit) { + HILOGI("tar list unsplit, bundle: %{public}s", bundleName_.c_str()); + return RestoreUnSplitTarListForSpecialCloneCloud(tarList); + } + + HILOGI("tar list split, bundle: %{public}s", bundleName_.c_str()); + return RestoreSplitTarListForSpecialCloneCloud(tarList); +} + +bool BackupExtExtension::CheckIsSplitTarList(const std::vector &tarList) +{ + auto *unSplitTar = new installd::UnTarFile(nullptr); + bool isSplit = false; + for (const auto &item : tarList) { + // reset untar object + unSplitTar->Reset(); + + HILOGI("check if split tar, filename: %{public}s, path: %{public}s", GetAnonyPath(item.hashName).c_str(), + GetAnonyPath(item.fileName).c_str()); + // check if tar is split + isSplit = unSplitTar->CheckIsSplitTar(item.hashName, item.fileName); + if (isSplit) { + HILOGI("check is split tar, filename: %{public}s, path: %{public}s", GetAnonyPath(item.hashName).c_str(), + GetAnonyPath(item.fileName).c_str()); + break; + } + } + // destory untar object + delete unSplitTar; + return isSplit; +} + +ErrCode BackupExtExtension::RestoreUnSplitTarListForSpecialCloneCloud(const std::vector &tarList) +{ + for (const auto &item : tarList) { + // 待解压tar文件处理 + HILOGI("untar unsplit, filename: %{public}s, path: %{public}s", GetAnonyPath(item.hashName).c_str(), + GetAnonyPath(item.fileName).c_str()); + radarRestoreInfo_.tarFileNum++; + radarRestoreInfo_.tarFileSize += static_cast(item.sta.st_size); + int ret = RestoreTarForSpecialCloneCloud(item); + if (isDebug_ && ret != ERR_OK) { + errFileInfos_[item.hashName].emplace_back(ret); + endFileInfos_[item.hashName] = item.sta.st_size; + } + if (ret != ERR_OK) { + HILOGE("Failed to restore tar file %{public}s", item.hashName.c_str()); + return ERR_INVALID_VALUE; + } + } + return ERR_OK; +} + +ErrCode BackupExtExtension::RestoreSplitTarListForSpecialCloneCloud(const std::vector &tarList) +{ + auto *unSplitTar = new installd::UnTarFile(nullptr); + ErrCode errCode = ERR_OK; + for (const auto &item : tarList) { + radarRestoreInfo_.tarFileNum++; + radarRestoreInfo_.tarFileSize += static_cast(item.sta.st_size); + // reset untar object + unSplitTar->Reset(); + + // do untar with root path + int ret = unSplitTar->UnSplitTar(item.hashName, item.fileName); + if (isDebug_ && ret != ERR_OK) { + errFileInfos_[item.hashName].emplace_back(ret); + endFileInfos_[item.hashName] = item.sta.st_size; + } + if (ret != ERR_OK) { + HILOGE("Failed to restore tar file %{public}s", item.hashName.c_str()); + errCode = ERR_INVALID_VALUE; + break; + } + if (!RemoveFile(item.hashName)) { + HILOGE("Failed to delete the backup split tar %{public}s", item.hashName.c_str()); + } + } + // destory untar object + delete unSplitTar; + return errCode; +} + ErrCode BackupExtExtension::RestoreFilesForSpecialCloneCloud() { HITRACE_METER_NAME(HITRACE_TAG_FILEMANAGEMENT, __PRETTY_FUNCTION__); @@ -1161,6 +1252,7 @@ ErrCode BackupExtExtension::RestoreFilesForSpecialCloneCloud() auto info = cache.GetExtManageInfo(); HILOGI("Start do restore for SpecialCloneCloud."); auto startTime = std::chrono::system_clock::now(); + auto tarList = std::vector(); for (const auto &item : info) { if (item.hashName.empty()) { HILOGE("Hash name empty"); @@ -1172,20 +1264,16 @@ ErrCode BackupExtExtension::RestoreFilesForSpecialCloneCloud() radarRestoreInfo_.bigFileSize += static_cast(item.sta.st_size); RestoreBigFilesForSpecialCloneCloud(item); } else { - // 待解压tar文件处理 - radarRestoreInfo_.tarFileNum++; - radarRestoreInfo_.tarFileSize += static_cast(item.sta.st_size); - int ret = RestoreTarForSpecialCloneCloud(item); - if (isDebug_ && ret != ERR_OK) { - errFileInfos_[item.hashName].emplace_back(ret); - endFileInfos_[item.hashName] = item.sta.st_size; - } - if (ret != ERR_OK) { - HILOGE("Failed to restore tar file %{public}s", item.hashName.c_str()); - return ERR_INVALID_VALUE; - } + tarList.emplace_back(item); } } + + int ret = RestoreTarListForSpecialCloneCloud(tarList); + if (ret != ERR_OK) { + HILOGE("Failed to restore tar file %{public}s", bundleName_.c_str()); + return ERR_INVALID_VALUE; + } + DeleteIndexAndRpFile(); auto endTime = std::chrono::system_clock::now(); radarRestoreInfo_.totalFileSpendTime = diff --git a/frameworks/native/backup_ext/src/installd_un_tar_file.cpp b/frameworks/native/backup_ext/src/installd_un_tar_file.cpp new file mode 100644 index 0000000000000000000000000000000000000000..da86712a3606bfea7ddc69780ffcffd6e793131f --- /dev/null +++ b/frameworks/native/backup_ext/src/installd_un_tar_file.cpp @@ -0,0 +1,787 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "installd_un_tar_file.h" + +#include +#include +#include +#include +#include +#include + +#include "securec.h" + +#include + +namespace installd { +const uid_t OCTAL = 8; +const mode_t FILE_MODE = 448; + +uid_t FixUpOwnerDirFile(const uid_t uid, const gid_t gid, const uid_t owner, gid_t& newGid) +{ + uid_t newUid = owner; + if (0 == owner || uid < APP_ID_START) { + newUid = uid; + newGid = gid; + + return newUid; + } + + if (uid == gid) { + newGid = newUid; + } else if (0 == ((gid - uid) % UID_GID_OFFSET)) { + newGid = (gid - uid) + newUid; + } else { + newGid = gid; + } + + return newUid; +} + +off_t ParseOctalStr(const char* p, size_t n) +{ + off_t ret = 0; + std::string octalStr(p); + auto it = octalStr.begin(); + + while (it != octalStr.end() && (*it < '0' || *it > '7') && n > 0) { + ++it; + --n; + } + + while (it != octalStr.end() && *it >= '0' && *it <= '7' && n > 0) { + ret *= OCTAL; + ret += *it - '0'; + ++it; + --n; + } + + return ret; +} + +static int GenRealPath(const char *rootPath, const char *pathName, char* &realPath) +{ + if (rootPath == nullptr || pathName == nullptr || realPath == nullptr) { + return ERR_PARAM; + } + size_t allLen = strlen(rootPath); + if (rootPath[allLen - 1] != '/') { + allLen += 1; + } + allLen += strlen(pathName); + if (0 == allLen || allLen >= PATH_MAX_LEN) { + LOGE("ERR_PARAM"); + return ERR_PARAM; + } + + size_t curLen = strlen(rootPath); + if (strncpy_s(realPath, PATH_MAX_LEN, rootPath, curLen) != 0) { + LOGE("GenRealPath get realPath error"); + return ERR_PARAM; + } + + if (rootPath[curLen - 1] != '/') { + realPath[curLen] = '/'; + curLen += 1; + } + + if (strncpy_s(realPath + curLen, PATH_MAX_LEN - curLen, pathName, strlen(pathName)) != 0) { + LOGE("GenRealPath get realPath by curLen error"); + return ERR_PARAM; + } + realPath[allLen] = '\0'; + return 0; +} + +static int CreateDir(char *path, mode_t mode) +{ + if (path == nullptr) { + return ERR_PARAM; + } + + size_t len = strlen(path); + if (path[len - 1] == '/') { + path[len - 1] = '\0'; + } + + int ret = access(path, F_OK); + if (ret == -1) { + ret = mkdir(path, mode); + } + return ret; +} + +static FILE *CreateFile(char* path, mode_t mode, char fileType) +{ + if (path == nullptr) { + return nullptr; + } + + std::string appendStr = "wb+"; + if (fileType == SPLIT_END_TYPE || fileType == SPLIT_CONTINUE_TYPE) { + appendStr = "ab+"; + } + FILE *f = fopen(path, appendStr.c_str()); + if (f == nullptr) { + char *p = strrchr(path, '/'); + if (p != nullptr) { + *p = '\0'; + if (CreateDir(path, mode) == 0) { + *p = '/'; + f = fopen(path, "wb+"); + } + } + } + if (f == nullptr) { + return f; + } + if (fchmod(fileno(f), S_IRUSR | S_IWUSR) == -1) { + LOGE("fail to change file permission"); + return nullptr; + } + + return f; +} + +static int CreateSoftlink(const char* oldPath, const char* newPath) +{ + if (oldPath == nullptr || newPath == nullptr) { + return ERR_PARAM; + } + + unlink(newPath); + int ret = symlink(oldPath, newPath); + return ret; +} + +UnTarFile::UnTarFile(const char *tarPath): FilePtr(nullptr), tarSize(0), newOwner(0) +{ + file_names.clear(); + file_sizes.clear(); + file_data_addrs.clear(); + + if (tarPath != nullptr) { + LOGI("untarfile begin.."); + m_srcPath = tarPath; + FilePtr = fopen(tarPath, "rb"); + if (FilePtr == nullptr) { + LOGE("open file fail"); + } + } +} + +UnTarFile::~UnTarFile(void) +{ + if (FilePtr != nullptr) { + (void)fclose(FilePtr); + FilePtr = nullptr; + } + + file_names.clear(); + file_sizes.clear(); + file_data_addrs.clear(); +} + +void UnTarFile::Reset() +{ + if (FilePtr != nullptr) { + (void)fclose(FilePtr); + FilePtr = nullptr; + } + + isSplit = false; + file_names.clear(); + file_sizes.clear(); + file_data_addrs.clear(); +} + +int UnTarFile::UnSplitTar(const std::string &tarFile, const std::string &rootpath) +{ + FilePtr = fopen(tarFile.c_str(), "rb"); + if (FilePtr == nullptr) { + LOGE("UnTarFile::UnSplitPack, untar split failed!"); + } + isSplit = true; + std::string destPath(rootpath); + + CreateDirWithRecursive(destPath); + + int parseRet = ParseTarFile(destPath.c_str(), eUnpack); + if (parseRet != 0) { + LOGE("UnTarFile::UnSplitPack, untar split failed!"); + } else { + LOGI("UnTarFile::UnSplitPack, untar split suc!"); + } + (void)fclose(FilePtr); + FilePtr = nullptr; + return parseRet; +} + +bool UnTarFile::CheckIsSplitTar(const std::string &tarFile, const std::string &rootpath) +{ + FilePtr = fopen(tarFile.c_str(), "rb"); + if (FilePtr == nullptr) { + LOGE("UnTarFile::CheckIsSplitTar, open split failed!"); + } + std::string destPath(rootpath); + int parseRet = ParseTarFile(destPath.c_str(), eCheckSplit); + if (parseRet != 0) { + LOGE("UnTarFile::CheckIsSplitTar, check is split failed!"); + } else { + LOGI("UnTarFile::CheckIsSplitTar, check is split, %{public}d", isSplit); + } + (void)fclose(FilePtr); + FilePtr = nullptr; + if (isSplit) { + return true; + } + return false; +} + +bool UnTarFile::VerifyChecksum(const TarHeader *tarHeader) +{ + if (tarHeader == nullptr) { + return false; + } + + char *headerBuff = (char *)tarHeader; + + int u = 0; + for (int n = 0; n < BLOCK_SIZE; ++n) { + if (n < CHKSUM_BASE || n > CHKSUM_BASE + TarUtil::CHKSUM_LEN - 1) { + /* Standard tar checksum adds unsigned bytes. */ + u += (*(headerBuff + n) & 0xFF); + } else { + u += BLANK_SPACE; + } + } + + return (u == static_cast(ParseOctalStr(headerBuff + CHKSUM_BASE, TarUtil::CHKSUM_LEN))); +} + +bool UnTarFile::IsValidTarBlock(const TarHeader *tarHeader) +{ + if (tarHeader == nullptr) { + return false; + } + + // check magic && checksum + if (0 == strncmp(tarHeader->magic, TMAGIC, TMAGIC_LEN - 1) && VerifyChecksum(tarHeader)) { + return true; + } + + LOGD("Invalid tar format."); + return false; +} + +bool UnTarFile::IsEmptyBlock(const char *p) +{ + return ('\0' == p[0]); +} + +int UnTarFile::CheckFileAndInitPath(const char *rootPath, ParseTarPath *parseTarPath) +{ + if (FilePtr == nullptr) { + LOGE("read tar happened error!\n"); + return ERR_PARAM; + } + + if (rootPath == nullptr) { + LOGE("rootPath is nullptr!\n"); + return ERR_NOEXIST; + } + // tarSize + fseeko(FilePtr, 0L, SEEK_END); + tarSize = ftello(FilePtr); + // reback file to begin + fseeko(FilePtr, 0L, SEEK_SET); + if (tarSize % BLOCK_SIZE != 0) { + LOGE("tarfile size should be a multiple of 512 bytes"); + return ERR_FORMAT; + } + + parseTarPath->fullPath = (char *)malloc(PATH_MAX_LEN * sizeof(char)); + if (parseTarPath->fullPath == nullptr) { + return ERR_MALLOC; + } + memset_s(parseTarPath->fullPath, PATH_MAX_LEN * sizeof(char), 0, PATH_MAX_LEN * sizeof(char)); + return 0; +} + +bool UnTarFile::ProcessTarBlock(char *buff, EParseType type, ParseTarPath *parseTarPath, bool &isSkip, bool &isSoftLink) +{ + TarHeader *tarHeader = (TarHeader *)buff; + TarFileInfo tarFileInfo = {}; + tarFileInfo.fileSize = ParseOctalStr(buff + TSIZE_BASE, TSIZE_LEN); + tarFileInfo.fileBlockCnt = (tarFileInfo.fileSize + BLOCK_SIZE - 1) / BLOCK_SIZE; + tarFileInfo.pos = ftello(FilePtr); + switch (tarHeader->typeflag) { + case SPLIT_START_TYPE: + case SPLIT_END_TYPE: + case SPLIT_CONTINUE_TYPE: + if (eCheckSplit == type) { + isSplit = true; + return false; + } + case REGTYPE: + case AREGTYPE: + HandleRegularFile(buff, type, parseTarPath, isSkip, tarFileInfo); + break; + case SYMTYPE: + CreateSoftlink(parseTarPath->realLink, parseTarPath->fullPath); + isSoftLink = true; + isSkip = false; + break; + case DIRTYPE: + CreateDir(parseTarPath->fullPath, FILE_MODE); + isSkip = false; + break; + case TarUtil::GNUTYPE_LONGNAME: + HandleGnuLongName(parseTarPath, isSkip, tarFileInfo); + return true; + case GNUTYPE_LONGLINK: + HandleGnuLongLink(parseTarPath, isSkip, tarFileInfo); + return true; + default: + isSkip = true; + fseeko(FilePtr, tarFileInfo.fileBlockCnt * BLOCK_SIZE, SEEK_CUR); + break; + } + + if (!isSkip) { + SetFileChmodAndChown(buff, parseTarPath, isSoftLink); + } + isSkip = false; + FreeLongTypePointer(parseTarPath); + return true; +} + +void UnTarFile::SetFileChmodAndChown(char *buff, ParseTarPath *parseTarPath, bool &isSoftLink) +{ + // uid & gid + gid_t newGid = 0; + uid_t uid = (uid_t)ParseOctalStr(buff + TUID_BASE, TUID_LEN); + gid_t gid = (gid_t)ParseOctalStr(buff + TGID_BASE, TGID_LEN); + uid_t newUid = FixUpOwnerDirFile(uid, gid, newOwner, newGid); + if (!isSoftLink) { + chmod(parseTarPath->fullPath, FILE_MODE); + chown(parseTarPath->fullPath, newUid, newGid); + return; + } + lchown(parseTarPath->fullPath, newUid, newGid); + isSoftLink = false; +} + +void UnTarFile::HandleGnuLongLink(ParseTarPath *parseTarPath, bool &isSkip, TarFileInfo &tarFileInfo) +{ + /* long link */ + if (parseTarPath->longLink != nullptr) { + free(parseTarPath->longLink); + parseTarPath->longLink = nullptr; + } + size_t nameLen = (size_t)tarFileInfo.fileSize; + if (nameLen < PATH_MAX_LEN) { + parseTarPath->longLink = (char *)malloc((nameLen + 1) * sizeof(char)); + } + if (parseTarPath->longLink != nullptr) { + memset_s(parseTarPath->longLink, (nameLen + 1) * sizeof(char), 0, (nameLen + 1) * sizeof(char)); + if (nameLen != fread(parseTarPath->longLink, sizeof(char), nameLen, FilePtr)) { + free(parseTarPath->longLink); + parseTarPath->longLink = nullptr; + } + } + + // anyway, go to correct pos + isSkip = true; + fseeko(FilePtr, tarFileInfo.pos + tarFileInfo.fileBlockCnt * BLOCK_SIZE, SEEK_SET); +} + +void UnTarFile::HandleGnuLongName(ParseTarPath *parseTarPath, bool &isSkip, TarFileInfo &tarFileInfo) +{ + if (parseTarPath->longName != nullptr) { + free(parseTarPath->longName); + parseTarPath->longName = nullptr; + } + size_t nameLen = (size_t)tarFileInfo.fileSize; + if (nameLen < PATH_MAX_LEN) { + parseTarPath->longName = (char *)malloc((nameLen + 1) * sizeof(char)); + } + if (parseTarPath->longName != nullptr) { + memset_s(parseTarPath->longName, (nameLen + 1) * sizeof(char), 0, (nameLen + 1) * sizeof(char)); + if (nameLen != fread(parseTarPath->longName, sizeof(char), nameLen, FilePtr)) { + free(parseTarPath->longName); + parseTarPath->longName = nullptr; + } + } + + // anyway, go to correct pos + isSkip = true; + fseeko(FilePtr, tarFileInfo.pos + tarFileInfo.fileBlockCnt * BLOCK_SIZE, SEEK_SET); +} + +void UnTarFile::HandleRegularFile(char *buff, EParseType type, ParseTarPath *parseTarPath, bool &isSkip, + TarFileInfo &tarFileInfo) +{ + if (eList == type) { + file_names.push_back(std::string(parseTarPath->realName)); + file_sizes.push_back(tarFileInfo.fileSize); + file_data_addrs.push_back(tarFileInfo.pos); + + fseeko(FilePtr, tarFileInfo.fileBlockCnt * BLOCK_SIZE, SEEK_CUR); + return; + } + if (eUnpack == type) { + HandleRegularEUnpackFile(buff, parseTarPath, isSkip, tarFileInfo); + } +} + +bool UnTarFile::FileReadAndWrite(char *destBuff, FILE *destF, size_t readBuffSize) +{ + if (readBuffSize != fread(destBuff, sizeof(char), readBuffSize, FilePtr)) { + LOGE("read file content shorter than expect!\n"); + return false; + } + + if (readBuffSize != fwrite(destBuff, sizeof(char), readBuffSize, destF)) { + LOGE("write file content shorter than expect!\n"); + return false; + } + return true; +} + +void UnTarFile::HandleRegularEUnpackFile(char *buff, ParseTarPath *parseTarPath, bool &isSkip, TarFileInfo &tarFileInfo) +{ + TarHeader *tarHeader = (TarHeader *)buff; + char *destBuff = (char *)malloc(READ_BUFF_SIZE * sizeof(char)); + if (destBuff == nullptr) { + LOGE("malloc memory fail!skip!"); + isSkip = false; + return; + } + FILE *destF = CreateFile(parseTarPath->fullPath, FILE_MODE, tarHeader->typeflag); + if (destF == nullptr) { + LOGE("destF is null!"); + free(destBuff); + fseeko(FilePtr, tarFileInfo.fileBlockCnt * BLOCK_SIZE, SEEK_CUR); + isSkip = false; + return; + } + memset_s(destBuff, READ_BUFF_SIZE * sizeof(char), 0, READ_BUFF_SIZE * sizeof(char)); + bool isInvalid = false; + off_t readBuffSize = READ_BUFF_SIZE; + off_t restSize = tarFileInfo.fileSize; + while (restSize > 0) { + if (restSize < READ_BUFF_SIZE) { + readBuffSize = restSize; + } + if (!FileReadAndWrite(destBuff, destF, readBuffSize)) { + isInvalid = true; + break; + } + restSize -= readBuffSize; + } + + if (destBuff != nullptr) { + free(destBuff); + destBuff = nullptr; + } + if (destF != nullptr) { + fflush(destF); + (void)fclose(destF); + destF = nullptr; + } + if (isInvalid) { + unlink(parseTarPath->fullPath); + isSkip = true; + } else { + isSkip = false; + } + // anyway, go to correct pos + fseeko(FilePtr, tarFileInfo.pos + tarFileInfo.fileBlockCnt * BLOCK_SIZE, SEEK_SET); +} + +bool UnTarFile::IsProcessTarEnd(char *buff, int &ret) +{ + size_t readCnt = fread(buff, 1, BLOCK_SIZE, FilePtr); + if (readCnt < BLOCK_SIZE) { + LOGE("read short than 512 expected, got %{public}zu, tarSize", readCnt); + + // when split unpack, ftell size is over than file really size [0,READ_BUFF_SIZE] + if (!isSplit || readCnt != 0 || ftello(FilePtr) > (tarSize + READ_BUFF_SIZE)) { + ret = ERR_IO; + } + return true; + } + + // two empty continuous block indicate end of file + if (IsEmptyBlock(buff)) { + char tailBuff[BLOCK_SIZE] = {0}; + size_t tailRead = 0; + tailRead = fread(tailBuff, 1, BLOCK_SIZE, FilePtr); + if ((tailRead == BLOCK_SIZE) && IsEmptyBlock(tailBuff)) { + LOGI("untarfile is end.Success!"); + return true; + } + } + + // check header + TarHeader *tarHeader = (TarHeader *)buff; + if (!IsValidTarBlock(tarHeader)) { + LOGE("isSplit cur size %{public}jd, tarSize %{public}jd", ftello(FilePtr), tarSize); + + // when split unpack, ftell size is over than file really size [0,READ_BUFF_SIZE] + if (!isSplit || ftello(FilePtr) > (tarSize + READ_BUFF_SIZE) || !IsEmptyBlock(buff)) { + ret = ERR_FORMAT; + } + return true; + } + return false; +} + +int UnTarFile::ParseTarFile(const char *rootPath, EParseType type) +{ + ParseTarPath parseTarPath = {}; + int ret = CheckFileAndInitPath(rootPath, &parseTarPath); + if (ret != 0) { + return ret; + } + LOGI("ParseTarFile"); + + // re-parse tar header + char buff[BLOCK_SIZE] = {}; + bool isSkip = false; + bool isSoftLink = false; + const off_t blockCnt = tarSize / BLOCK_SIZE; + for (off_t i = 0; i < blockCnt; i++) { + if (IsProcessTarEnd(buff, ret)) { + FreePointer(&parseTarPath); + return ret; + } + + TarHeader *tarHeader = (TarHeader *)buff; + parseTarPath.realName = tarHeader->name; + if (parseTarPath.longName != nullptr) { + parseTarPath.realName = parseTarPath.longName; + } + + GenRealPath(rootPath, parseTarPath.realName, parseTarPath.fullPath); + parseTarPath.realLink = tarHeader->linkname; + if (parseTarPath.longLink != nullptr) { + parseTarPath.realLink = parseTarPath.longLink; + } + + CreateDir(const_cast(rootPath), FILE_MODE); + + if (!ProcessTarBlock(buff, type, &parseTarPath, isSkip, isSoftLink)) { + FreePointer(&parseTarPath); + return ret; + } + } + LOGI("blockCnt overflow"); + FreePointer(&parseTarPath); + return ret; +} + +void UnTarFile::FreePointer(ParseTarPath *parseTarPath) +{ + if (parseTarPath->fullPath != nullptr) { + free(parseTarPath->fullPath); + parseTarPath->fullPath = nullptr; + } + if (parseTarPath->longName != nullptr) { + free(parseTarPath->longName); + parseTarPath->longName = nullptr; + } + if (parseTarPath->longLink != nullptr) { + free(parseTarPath->longLink); + parseTarPath->longLink = nullptr; + } +} + +void UnTarFile::FreeLongTypePointer(ParseTarPath *parseTarPath) +{ + if (parseTarPath->longName != nullptr) { + free(parseTarPath->longName); + parseTarPath->longName = nullptr; + } + if (parseTarPath->longLink != nullptr) { + free(parseTarPath->longLink); + parseTarPath->longLink = nullptr; + } +} + +bool UnTarFile::CreateDirWithRecursive(const std::string &filePath, mode_t mode) +{ + if (filePath.empty()) { + LOGE("CreateDirWithRecursive filePath is empty"); + return false; + } + if (access(filePath.c_str(), F_OK) == 0) { + return true; + } + + LOGI("CreateDirWithRecursive filePath %{public}s is not exist, need create", filePath.c_str()); + std::string::size_type index = 0; + do { + index = filePath.find('/', index + 1); + std::string subPath = (index == std::string::npos) ? filePath : filePath.substr(0, index); + if (access(subPath.c_str(), F_OK) != 0) { + if (mkdir(subPath.c_str(), mode) != 0) { + return false; + } + } + } while (index != std::string::npos); + return access(filePath.c_str(), F_OK) == 0; +} + +std::vector UnTarFile::GetFileNames() +{ + if (file_names.empty()) { + ParseTarFile(); + } + + return file_names; +} + +int UnTarFile::UnPack(const char *path, uid_t owner) +{ + newOwner = owner; + int ret = ParseTarFile(path, eUnpack); + if (remove(m_srcPath.c_str()) != 0) { + LOGI("delete failed"); + } + return ret; +} + +int UnTarFile::UnSplitPack(const char *path, uid_t owner) +{ + LOGI("Start UnSplitPack"); + newOwner = owner; + isSplit = true; + int num = 0; + std::vector taskSrcPathName; + std::string pathDir(path); + pathDir = pathDir.substr(0, pathDir.find_last_of('/')); + + if (!HandleCheckFile(m_srcPath.c_str(), taskSrcPathName, num)) { + LOGE("UnTarFile::UnSplitPack, handleCheckFile failed!"); + return -1; + } + + int ret = 0; + if (FilePtr != nullptr) { + (void)fclose(FilePtr); + FilePtr = nullptr; + LOGE("FilePtr is not null!"); + } + for (int i = 0; i < num; i++) { + FilePtr = fopen(taskSrcPathName[i].c_str(), "rb"); + if (FilePtr != nullptr) { + int parseRet = ParseTarFile(pathDir.c_str(), eUnpack); + if (parseRet != 0) { + LOGE("UnTarFile::UnSplitPack, untar split failed!"); + (void)fclose(FilePtr); + FilePtr = nullptr; + return parseRet; + } else { + LOGI("UnTarFile::UnSplitPack, untar split suc!"); + (void)fclose(FilePtr); + FilePtr = nullptr; + } + } + if (remove(taskSrcPathName[i].c_str()) != 0) { + LOGI("delete failed"); + } + } + + LOGI("UnTarFile::UnSplitPack, untar split finish!"); + taskSrcPathName.clear(); + return ret; +} + +// check slice tar file according the tar info +bool UnTarFile::CheckSliceTar(const char *tarInfo, const char *dstPathName, std::vector &fileNameVector) +{ + if (tarInfo == nullptr) { + LOGE("error, tarInfo is NULL"); + return false; + } + std::string info(tarInfo); + size_t pos = info.find_last_of("|"); + std::string tarName(dstPathName); + tarName.append("/"); + tarName.append(info.substr(0, pos)); + + std::string subSize = info.substr(pos + 1); + if (subSize.empty()) { + LOGE("error subSize is empty"); + return false; + } + off_t size = strtol(subSize.c_str(), nullptr, 0); + + if (access(tarName.c_str(), F_OK)) { + LOGE("error, slice tar file don't exist, error:%{public}s", strerror(errno)); + return true; + } + + struct stat stSliceTar; + if (stat(tarName.c_str(), &stSliceTar) == -1) { + LOGE("error, failed to get stat of tar file, error: %{public}s", strerror(errno)); + return false; + } + + if (size != stSliceTar.st_size) { + LOGE("error, the size of the tar file is not equal to the size in the check file\n"); + return false; + } + fileNameVector.push_back(tarName); + return true; +} + +bool UnTarFile::HandleCheckFile(const char *tarBaseName, std::vector &fileNameVector, int &num) +{ + LOGI("HandleCheckFile"); + if (tarBaseName == nullptr) { + LOGE("tarBaseName is nullptr"); + return false; + } + + FILE *fd = fopen(tarBaseName, "rb"); + if (fd == nullptr) { + LOGE("open check file error, error:%{public}s", strerror(errno)); + return false; + } + + std::string taskSPath(tarBaseName); + std::string dirName = taskSPath.substr(0, taskSPath.find_last_of('/')); + + char tarInfo[PATH_MAX_LEN] = {0}; + bool ret = true; + while ((fgets(tarInfo, (sizeof(tarInfo) - 1), fd)) != nullptr) { + if (!CheckSliceTar(tarInfo, dirName.c_str(), fileNameVector)) { + LOGE("handleCheckFile: failed"); + ret = false; + break; + } + num++; + memset_s(tarInfo, PATH_MAX_LEN, 0, PATH_MAX_LEN); + } + LOGI("HandleCheckFile end"); + (void)fclose(fd); + fd = nullptr; + return ret; +} +} // namespace installd diff --git a/test/fuzztest/backupext_fuzzer/BUILD.gn b/test/fuzztest/backupext_fuzzer/BUILD.gn index 319f0ebd5e05fb2c551c15f6794f19145a6711e1..d298eeae0992a0ac0fdd9e1e18925229043ef4f6 100644 --- a/test/fuzztest/backupext_fuzzer/BUILD.gn +++ b/test/fuzztest/backupext_fuzzer/BUILD.gn @@ -42,6 +42,7 @@ ohos_fuzztest("BackupExtFuzzTest") { "${path_backup}/frameworks/native/backup_ext/src/ext_backup_js.cpp", "${path_backup}/frameworks/native/backup_ext/src/ext_backup_loader.cpp", "${path_backup}/frameworks/native/backup_ext/src/ext_extension.cpp", + "${path_backup}/frameworks/native/backup_ext/src/installd_un_tar_file.cpp", "${path_backup}/frameworks/native/backup_ext/src/sub_ext_extension.cpp", "${path_backup}/frameworks/native/backup_ext/src/tar_file.cpp", "${path_backup}/frameworks/native/backup_ext/src/untar_file.cpp", diff --git a/tests/unittests/backup_ext/BUILD.gn b/tests/unittests/backup_ext/BUILD.gn index 6c3468b7ef394ed59f2d79ecf98d63aa473f684f..139df97bb1c8dcebb72cc4f9797e5788c9ef20aa 100644 --- a/tests/unittests/backup_ext/BUILD.gn +++ b/tests/unittests/backup_ext/BUILD.gn @@ -135,6 +135,7 @@ ohos_unittest("tar_file_test") { "${path_backup}/frameworks/native/backup_ext/src/ext_backup_js.cpp", "${path_backup}/frameworks/native/backup_ext/src/ext_backup_loader.cpp", "${path_backup}/frameworks/native/backup_ext/src/ext_extension.cpp", + "${path_backup}/frameworks/native/backup_ext/src/installd_un_tar_file.cpp", "${path_backup}/frameworks/native/backup_ext/src/sub_ext_extension.cpp", "${path_backup}/frameworks/native/backup_ext/src/tar_file.cpp", "${path_backup}/frameworks/native/backup_ext/src/untar_file.cpp", @@ -207,6 +208,7 @@ ohos_unittest("untar_file_sup_test") { "${path_backup}/frameworks/native/backup_ext/src/ext_backup_js.cpp", "${path_backup}/frameworks/native/backup_ext/src/ext_backup_loader.cpp", "${path_backup}/frameworks/native/backup_ext/src/ext_extension.cpp", + "${path_backup}/frameworks/native/backup_ext/src/installd_un_tar_file.cpp", "${path_backup}/frameworks/native/backup_ext/src/sub_ext_extension.cpp", "${path_backup}/frameworks/native/backup_ext/src/tar_file.cpp", "${path_backup}/tests/mock/library_func_mock/library_func_mock.cpp", @@ -283,6 +285,7 @@ ohos_unittest("untar_file_test") { "${path_backup}/frameworks/native/backup_ext/src/ext_backup_js.cpp", "${path_backup}/frameworks/native/backup_ext/src/ext_backup_loader.cpp", "${path_backup}/frameworks/native/backup_ext/src/ext_extension.cpp", + "${path_backup}/frameworks/native/backup_ext/src/installd_un_tar_file.cpp", "${path_backup}/frameworks/native/backup_ext/src/sub_ext_extension.cpp", "${path_backup}/frameworks/native/backup_ext/src/tar_file.cpp", "${path_backup}/frameworks/native/backup_ext/src/untar_file.cpp", @@ -442,12 +445,48 @@ ohos_unittest("tar_file_sub_test") { use_exceptions = true } +ohos_unittest("installd_un_tar_file_test") { + module_out_path = path_module_out_tests + + sources = [ + "${path_backup}/frameworks/native/backup_ext/src/installd_un_tar_file.cpp", + "installd_un_tar_file_test.cpp", + ] + + include_dirs = [ + "${path_backup}/frameworks/native/backup_ext/include", + "${path_backup}/interfaces/common/include", + "${path_backup}/utils/include", + ] + + cflags = [ "--coverage" ] + ldflags = [ "--coverage" ] + cflags_cc = [ "--coverage" ] + + deps = [ + "${path_backup}/tests/utils:backup_test_utils", + "${path_backup}/utils:backup_utils", + ] + + external_deps = [ + "c_utils:utils", + "googletest:gmock_main", + "googletest:gtest_main", + "hilog:libhilog", + ] + + defines = [ "private=public" ] + + use_exceptions = true +} + group("backup_ext_test") { testonly = true if (!use_libfuzzer) { deps = [ ":ext_backup_js_test", ":ext_extension_test", + ":installd_un_tar_file_test", ":tar_file_sub_test", ":tar_file_test", ":untar_file_sup_test", diff --git a/tests/unittests/backup_ext/installd_un_tar_file_test.cpp b/tests/unittests/backup_ext/installd_un_tar_file_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fc768dd5cd3599448c0e4d5a2fc3d01809371077 --- /dev/null +++ b/tests/unittests/backup_ext/installd_un_tar_file_test.cpp @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "installd_un_tar_file.h" + +#include +#include +#include +#include + +#include "b_error/b_error.h" +#include "test_manager.h" + +#include + +namespace OHOS::FileManagement::Backup { +using namespace std; +using namespace testing; + +class InstalldUnTarFileTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(); + void SetUp() override {}; + void TearDown() override {}; +}; + +void InstalldUnTarFileTest::SetUpTestCase() +{ + GTEST_LOG_(INFO) << "InstalldUnTarFileTest::SetUpTestCase enter"; +} + +void InstalldUnTarFileTest::TearDownTestCase() +{ + GTEST_LOG_(INFO) << "InstalldUnTarFileTest::TearDownTestCase enter"; +} + +static string CreateUnSplitTar(const string &rootPath, const string &rootPathSubPath, int32_t innerFileCount) +{ + try { + string testDir = rootPath + rootPathSubPath; + if (mkdir(testDir.data(), S_IRWXU) && errno != EEXIST) { + GTEST_LOG_(INFO) << " invoked mkdir failure, errno :" << errno; + throw BError(errno); + } + for (int i = 0; i < innerFileCount; ++i) { + string file = testDir + "/" + to_string(i + 1) + ".txt"; + SaveStringToFile(file, "hello" + to_string(i)); + GTEST_LOG_(INFO) << "InstalldUnTarFileTest-an access: " << access(file.c_str(), F_OK); + } + + string tarFile = rootPath + "testUnSplit.tar"; + string cmd = "tar -cvf " + tarFile + " " + testDir; + if (system(cmd.c_str()) != 0) { + GTEST_LOG_(INFO) << " execute tar failure, errno :" << errno; + throw BError(errno); + } + return tarFile; + } catch (...) { + EXPECT_TRUE(false); + GTEST_LOG_(INFO) << "InstalldUnTarFileTest-an exception occurred by CreateUnSplitTar."; + } + return ""; +} + +static vector CreateSplitTar(const string& rootPath, const string& rootPathSubPath) +{ + try { + const string testDir = rootPath + rootPathSubPath; + vector needCreateDir = {testDir, testDir + "/dir1", testDir + "/dir1/dir2", testDir + "/dir3", + testDir + "/dir4"}; + for (const auto &dir : needCreateDir) { + if (mkdir(dir.data(), S_IRWXU) && errno != EEXIST) { + throw BError(errno); + } + } + constexpr int32_t fileCount = 5; + for (auto i = 0; i < needCreateDir.size(); ++i) { + string file = needCreateDir[i] + "/" + to_string(i) + ".txt"; + string data = ""; + for (auto j = 0; j < fileCount * (i + 1); ++j) { + data += "test data 123456789"; + } + SaveStringToFile(file, data); + } + + string tarDir = rootPath + "testTar"; + if (mkdir(tarDir.data(), S_IRWXU) && errno != EEXIST) { + throw BError(errno); + } + + string tarPath = tarDir + "/test.tar"; + string softlinkCmd = "cd " + testDir + "/dir4" + " && ln -s " + testDir + "/dir4/4.txt ./4_new.txt"; + string tarCmd = "tar -cvf " + tarPath + " " + testDir; + string splitTarCmd = "cd " + tarDir + " && split -b 4k -a 2 test.tar test.tar."; + vector cmds = {softlinkCmd, tarCmd, splitTarCmd}; + + for (const string& cmd : cmds) { + if (system(cmd.c_str()) != 0) { + throw BError(errno); + } + } + vector res; + for (const auto& entry : std::filesystem::directory_iterator(tarDir)) { + if (entry.path().filename() != "test.tar") { + res.push_back(entry.path().string()); + } + } + return res; + } catch (...) { + GTEST_LOG_(INFO) << "InstalldUnTarFileTest-an exception occurred by CreateSplitTar."; + } + return {}; +} + +/** + * @tc.number: Installd_Un_Tar_File_UnTarFile_0100 + * @tc.name: Installd_Un_Tar_File_UnTarFile_0100 + * @tc.desc: test File_UnTarFile gen + * @tc.size: MEDIUM + * @tc.type: FUNC + * @tc.level Level 1 + * @tc.require: IC15LE + */ +HWTEST_F(InstalldUnTarFileTest, Installd_Un_Tar_File_UnTarFile_0100, testing::ext::TestSize.Level1) +{ + GTEST_LOG_(INFO) << "InstalldUnTarFileTest-begin Installd_Un_Tar_File_UnTarFile_0100"; + try { + char *tarPath = nullptr; + installd::UnTarFile unTarFile(tarPath); + + const char *tarPath2 = string("/data/test/backup/test.tar").c_str(); + installd::UnTarFile unTarFile2(tarPath2); + + EXPECT_TRUE(true); + } catch (...) { + EXPECT_TRUE(false); + GTEST_LOG_(INFO) << "InstalldUnTarFileTest-an exception occurred by InstalldUnTarFile."; + } + GTEST_LOG_(INFO) << "InstalldUnTarFileTest-end Installd_Un_Tar_File_UnTarFile_0100"; +} + +/** + * @tc.number: Installd_Un_Tar_File_CheckIsSplitTar_0100 + * @tc.name: Installd_Un_Tar_File_CheckIsSplitTar_0100 + * @tc.desc: test File_CheckIsSplitTar method + * @tc.size: MEDIUM + * @tc.type: FUNC + * @tc.level Level 1 + * @tc.require: IC15LE + */ +HWTEST_F(InstalldUnTarFileTest, Installd_Un_Tar_File_CheckIsSplitTar_0100, testing::ext::TestSize.Level1) +{ + GTEST_LOG_(INFO) << "InstalldUnTarFileTest-begin Installd_Un_Tar_File_CheckIsSplitTar_0100"; + try { + const TestManager tm("Installd_Un_Tar_File_CheckIsSplitTar_0100"); + const string rootPath = tm.GetRootDirCurTest(); + constexpr int32_t innerFileCount = 10; + const string tarPath = CreateUnSplitTar(rootPath, "testTarPath", innerFileCount); + EXPECT_FALSE(tarPath.empty()); + installd::UnTarFile unTarFile(rootPath.c_str()); + const bool ret = unTarFile.CheckIsSplitTar(tarPath, rootPath); + EXPECT_FALSE(ret); + } catch (...) { + EXPECT_TRUE(false); + GTEST_LOG_(INFO) << "InstalldUnTarFileTest-an exception occurred by InstalldUnTarFile."; + } + GTEST_LOG_(INFO) << "InstalldUnTarFileTest-end Installd_Un_Tar_File_CheckIsSplitTar_0100"; +} + +/** + * @tc.number: Installd_Un_Tar_File_IsEmptyBlock_0100 + * @tc.name: Installd_Un_Tar_File_IsEmptyBlock_0100 + * @tc.desc: test File_IsEmptyBlock method + * @tc.size: MEDIUM + * @tc.type: FUNC + * @tc.level Level 1 + * @tc.require: IC15LE + */ +HWTEST_F(InstalldUnTarFileTest, Installd_Un_Tar_File_IsEmptyBlock_0100, testing::ext::TestSize.Level1) +{ + GTEST_LOG_(INFO) << "InstalldUnTarFileTest-begin Installd_Un_Tar_File_IsEmptyBlock_0100"; + try { + const char *p = "\0"; + installd::UnTarFile unTarFile(nullptr); + bool ret = unTarFile.IsEmptyBlock(p); + EXPECT_TRUE(ret); + + const char *p1 = "test"; + installd::UnTarFile unTarFile1(nullptr); + ret = unTarFile.IsEmptyBlock(p1); + EXPECT_FALSE(ret); + } catch (...) { + EXPECT_TRUE(false); + GTEST_LOG_(INFO) << "InstalldUnTarFileTest-an exception occurred by InstalldUnTarFile."; + } + GTEST_LOG_(INFO) << "InstalldUnTarFileTest-end Installd_Un_Tar_File_IsEmptyBlock_0100"; +} + +/** + * @tc.number: Installd_Un_Tar_File_CreateDirWithRecursive_0100 + * @tc.name: Installd_Un_Tar_File_CreateDirWithRecursive_0100 + * @tc.desc: test File_CreateDirWithRecursive method + * @tc.size: MEDIUM + * @tc.type: FUNC + * @tc.level Level 1 + * @tc.require: IC15LE + */ +HWTEST_F(InstalldUnTarFileTest, Installd_Un_Tar_File_CreateDirWithRecursive_0100, testing::ext::TestSize.Level1) +{ + GTEST_LOG_(INFO) << "InstalldUnTarFileTest-begin Installd_Un_Tar_File_CreateDirWithRecursive_0100"; + try { + installd::UnTarFile unTarFile(nullptr); + mode_t mode = 448; + bool ret = unTarFile.CreateDirWithRecursive("", mode); + EXPECT_FALSE(ret); + + TestManager tm("Installd_Un_Tar_File_CreateDirWithRecursive_0100"); + string root = tm.GetRootDirCurTest(); + ret = unTarFile.CreateDirWithRecursive(root, mode); + EXPECT_TRUE(ret); + + string destPath(root + "/testDir/test1/test2"); + ret = unTarFile.CreateDirWithRecursive(destPath, mode); + EXPECT_TRUE(ret); + } catch (...) { + EXPECT_TRUE(false); + GTEST_LOG_(INFO) << "InstalldUnTarFileTest-an exception occurred by InstalldUnTarFile."; + } + GTEST_LOG_(INFO) << "InstalldUnTarFileTest-end Installd_Un_Tar_File_CreateDirWithRecursive_0100"; +} + +/** + * @tc.number: Installd_Un_Tar_File_UnSplitTar_0100 + * @tc.name: Installd_Un_Tar_File_UnSplitTar_0100 + * @tc.desc: test File_UnSplitTar method + * @tc.size: MEDIUM + * @tc.type: FUNC + * @tc.level Level 1 + * @tc.require: IC15LE + */ +HWTEST_F(InstalldUnTarFileTest, Installd_Un_Tar_File_UnSplitTar_0100, testing::ext::TestSize.Level1) +{ + GTEST_LOG_(INFO) << "InstalldUnTarFileTest-begin Installd_Un_Tar_File_UnSplitTar_0100"; + try { + TestManager tm("Installd_Un_Tar_File_UnSplitTar_0100"); + string rootPath = tm.GetRootDirCurTest(); + vector splitTarList = CreateSplitTar(rootPath, "testTarFile"); + EXPECT_FALSE(splitTarList.empty()); + + installd::UnTarFile unTarFile(nullptr); + for (auto& tarName : splitTarList) { + GTEST_LOG_(INFO) << "InstalldUnTarFileTest-begin tarName: " << tarName; + auto ret = unTarFile.UnSplitTar(tarName, rootPath); + EXPECT_EQ(ret, 0); + } + } catch (...) { + EXPECT_TRUE(false); + GTEST_LOG_(INFO) << "InstalldUnTarFileTest-an exception occurred by InstalldUnTarFile."; + } + GTEST_LOG_(INFO) << "InstalldUnTarFileTest-end Installd_Un_Tar_File_UnSplitTar_0100"; +} + +/** + * @tc.number: Installd_Un_Tar_File_FreePointer_0100 + * @tc.name: Installd_Un_Tar_File_FreePointer_0100 + * @tc.desc: test File_FreePointer method + * @tc.size: MEDIUM + * @tc.type: FUNC + * @tc.level Level 1 + * @tc.require: IC15LE + */ +HWTEST_F(InstalldUnTarFileTest, Installd_Un_Tar_File_FreePointer_0100, testing::ext::TestSize.Level1) +{ + GTEST_LOG_(INFO) << "InstalldUnTarFileTest-begin Installd_Un_Tar_File_FreePointer_0100"; + try { + installd::ParseTarPath parseTarPath = {}; + parseTarPath.fullPath = (char *)malloc(sizeof(char)); + parseTarPath.longName = (char *)malloc(sizeof(char)); + parseTarPath.longLink = (char *)malloc(sizeof(char)); + installd::UnTarFile unTarFile(nullptr); + unTarFile.FreePointer(&parseTarPath); + EXPECT_TRUE(parseTarPath.fullPath == nullptr); + EXPECT_TRUE(parseTarPath.longName == nullptr); + EXPECT_TRUE(parseTarPath.longLink == nullptr); + } catch (...) { + EXPECT_TRUE(false); + GTEST_LOG_(INFO) << "InstalldUnTarFileTest-an exception occurred by InstalldUnTarFile."; + } + GTEST_LOG_(INFO) << "InstalldUnTarFileTest-end Installd_Un_Tar_File_FreePointer_0100"; +} + +/** + * @tc.number: Installd_Un_Tar_File_FreeLongTypePointer_0100 + * @tc.name: Installd_Un_Tar_File_FreeLongTypePointer_0100 + * @tc.desc: test File_FreeLongTypePointer method + * @tc.size: MEDIUM + * @tc.type: FUNC + * @tc.level Level 1 + * @tc.require: IC15LE + */ +HWTEST_F(InstalldUnTarFileTest, Installd_Un_Tar_File_FreeLongTypePointer_0100, testing::ext::TestSize.Level1) +{ + GTEST_LOG_(INFO) << "InstalldUnTarFileTest-begin Installd_Un_Tar_File_FreeLongTypePointer_0100"; + try { + installd::ParseTarPath parseTarPath = {}; + parseTarPath.longName = (char *)malloc(sizeof(char)); + parseTarPath.longLink = (char *)malloc(sizeof(char)); + installd::UnTarFile unTarFile(nullptr); + unTarFile.FreeLongTypePointer(&parseTarPath); + EXPECT_TRUE(parseTarPath.longName == nullptr); + EXPECT_TRUE(parseTarPath.longLink == nullptr); + } catch (...) { + EXPECT_TRUE(false); + GTEST_LOG_(INFO) << "InstalldUnTarFileTest-an exception occurred by InstalldUnTarFile."; + } + GTEST_LOG_(INFO) << "InstalldUnTarFileTest-end Installd_Un_Tar_File_FreeLongTypePointer_0100"; +} +} // namespace OHOS::FileManagement::Backup \ No newline at end of file