diff --git a/frameworks/native/backup_ext/BUILD.gn b/frameworks/native/backup_ext/BUILD.gn index 2fe333fc36bcdd6d2f2638151198837f1681dd86..35f7fbf658a009b9a99374cda61ad95502cda3c8 100644 --- a/frameworks/native/backup_ext/BUILD.gn +++ b/frameworks/native/backup_ext/BUILD.gn @@ -37,6 +37,7 @@ ohos_shared_library("backup_extension_ability_native") { "src/sub_ext_extension.cpp", "src/tar_file.cpp", "src/untar_file.cpp", + "src/InstalldUnTarFile.cpp", ] defines = [ diff --git a/frameworks/native/backup_ext/include/InstalldTarUtils.h b/frameworks/native/backup_ext/include/InstalldTarUtils.h new file mode 100644 index 0000000000000000000000000000000000000000..f25221607397d3191fe0703535fccf2fdd60d652 --- /dev/null +++ b/frameworks/native/backup_ext/include/InstalldTarUtils.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/InstalldUnTarFile.h b/frameworks/native/backup_ext/include/InstalldUnTarFile.h new file mode 100644 index 0000000000000000000000000000000000000000..e51ed8b58c2643697fa4c41c1103ea5b339e5e0d --- /dev/null +++ b/frameworks/native/backup_ext/include/InstalldUnTarFile.h @@ -0,0 +1,64 @@ +/* + * 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 +#include +#include "InstalldTarUtils.h" +#include "TarUtil.h" + +namespace installd { +// 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: + int ParseTarFile(const char *rootPath = "", UnTarFile::EParseType type = eList); + bool IsEmptyBlock(const char *p); + 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(char *longName, char *longLink, char *fullPath); + 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/TarUtil.h b/frameworks/native/backup_ext/include/TarUtil.h new file mode 100644 index 0000000000000000000000000000000000000000..e253e47286d31b4dcca97989cb1c4e4af88f1b7b --- /dev/null +++ b/frameworks/native/backup_ext/include/TarUtil.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/include/ext_extension.h b/frameworks/native/backup_ext/include/ext_extension.h index ca5611f4a20b19cb3a3aa0c3c532bce132a89c60..9cfa8bd7a13b40daf28d9bc6203ed702fb62704c 100644 --- a/frameworks/native/backup_ext/include/ext_extension.h +++ b/frameworks/native/backup_ext/include/ext_extension.h @@ -341,6 +341,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); + private: std::shared_mutex lock_; std::shared_ptr extension_; diff --git a/frameworks/native/backup_ext/src/InstalldUnTarFile.cpp b/frameworks/native/backup_ext/src/InstalldUnTarFile.cpp new file mode 100644 index 0000000000000000000000000000000000000000..96759236ca097572f48e16b1bbb134555c2a3711 --- /dev/null +++ b/frameworks/native/backup_ext/src/InstalldUnTarFile.cpp @@ -0,0 +1,744 @@ +/* + * 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 "InstalldUnTarFile.h" + +#include +#include +#include +#include +#include +#include + +#include "securec.h" + +#include + +namespace installd { +const uid_t OCTAL = 8; + +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::ParseTarFile(const char *rootPath, EParseType type) +{ + if (FilePtr == nullptr) { + LOGE("read tar happened error!\n"); + return ERR_PARAM; + } + + if (rootPath == nullptr) { + LOGE("rootPath is nullptr!\n"); + return ERR_NOEXIST; + } + LOGI("ParseTarFile"); + + // re-parse tar header + char buff[BLOCK_SIZE] = {0}; + size_t readCnt = 0; + off_t pos = 0; + char *longName = nullptr; + char *longLink = nullptr; + char *fullPath = nullptr; + bool isSkip = false; + bool isSoftlink = false; + int ret = 0; + + // 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; + } + + fullPath = (char *)malloc(PATH_MAX_LEN * sizeof(char)); + if (fullPath == nullptr) { + return ERR_MALLOC; + } + + memset_s(fullPath, PATH_MAX_LEN * sizeof(char), 0, PATH_MAX_LEN * sizeof(char)); + + while (1) { + 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; + } + FreePointer(longName, longLink, fullPath); + return ret; + } + + // 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!"); + FreePointer(longName, longLink, fullPath); + return ret; + } + } + + // 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; + } + FreePointer(longName, longLink, fullPath); + return ret; + } + + // mode + mode_t mode = (mode_t) 448; + + // 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); + + // file size & content offset + off_t fileSize = ParseOctalStr(buff + TSIZE_BASE, TSIZE_LEN); + off_t fileBlockCnt = (fileSize + BLOCK_SIZE - 1) / BLOCK_SIZE; + pos = ftello(FilePtr); + + // longName & longLink + char *realName = tarHeader->name; + if (longName != nullptr) { + realName = longName; + } + + GenRealPath(rootPath, realName, fullPath); + char *realLink = tarHeader->linkname; + if (longLink != nullptr) { + realLink = longLink; + } + CreateDir(const_cast(rootPath), mode); + switch (tarHeader->typeflag) { + case SPLIT_START_TYPE: + case SPLIT_END_TYPE: + case SPLIT_CONTINUE_TYPE: + if (eCheckSplit == type) { + isSplit = true; + FreePointer(longName, longLink, fullPath); + return ret; + } + case REGTYPE: /* regular file */ + case AREGTYPE: { /* regular file */ + if (eList == type) { + file_names.push_back(std::string(realName)); + file_sizes.push_back(fileSize); + file_data_addrs.push_back(pos); + + fseeko(FilePtr, fileBlockCnt * BLOCK_SIZE, SEEK_CUR); + } else if (eUnpack == type) { + char *destBuff = (char *)malloc(READ_BUFF_SIZE * sizeof(char)); + if (destBuff != nullptr) { + FILE *destF = CreateFile(fullPath, mode, tarHeader->typeflag); + bool IsAbort = false; + bool IsInvalid = false; + if (destF != nullptr) { + off_t restSize = fileSize; + size_t readBuffSize = READ_BUFF_SIZE; + + memset_s(destBuff, READ_BUFF_SIZE * sizeof(char), 0, READ_BUFF_SIZE * sizeof(char)); + + while (restSize > 0) { + if (restSize < READ_BUFF_SIZE) { + readBuffSize = restSize; + } + if (readBuffSize != fread(destBuff, sizeof(char), readBuffSize, FilePtr)) { + LOGE("read file content shorter than expect!\n"); + IsInvalid = true; + break; + } + + if (readBuffSize != fwrite(destBuff, sizeof(char), readBuffSize, destF)) { + LOGE("write file content shorter than expect!\n"); + 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(fullPath); + isSkip = true; + } + if (IsAbort) { + FreePointer(longName, longLink, fullPath); + return ret; + } + + // anyway, go to correct pos + fseeko(FilePtr, pos + fileBlockCnt * BLOCK_SIZE, SEEK_SET); + } else { + LOGE("destF is null!"); + fseeko(FilePtr, fileBlockCnt * BLOCK_SIZE, SEEK_CUR); + } + } else { + LOGE("malloc memory fail!skip!"); + } + isSkip = false; + } + break; + } + case SYMTYPE: { + CreateSoftlink(realLink, fullPath); + isSoftlink = true; + isSkip = false; + break; + } + case DIRTYPE: { + CreateDir(fullPath, mode); + isSkip = false; + break; + } + case TarUtil::GNUTYPE_LONGNAME: { + if (longName != nullptr) { + free(longName); + longName = nullptr; + } + + size_t nameLen = (size_t)fileSize; + if (nameLen < PATH_MAX_LEN) { + longName = (char *)malloc((nameLen + 1) * sizeof(char)); + } + if (longName != nullptr) { + memset_s(longName, (nameLen + 1) * sizeof(char), 0, (nameLen + 1) * sizeof(char)); + if (nameLen != fread(longName, sizeof(char), nameLen, FilePtr)) { + free(longName); + longName = nullptr; + } + } + + // anyway, go to correct pos + isSkip = true; + fseeko(FilePtr, pos + fileBlockCnt * BLOCK_SIZE, SEEK_SET); + continue; + } + case GNUTYPE_LONGLINK: { + /* long link */ + if (longLink != nullptr) { + free(longLink); + longLink = nullptr; + } + + size_t nameLen = (size_t)fileSize; + if (nameLen < PATH_MAX_LEN) { + longLink = (char *)malloc((nameLen + 1) * sizeof(char)); + } + if (longLink != nullptr) { + memset_s(longLink, (nameLen + 1) * sizeof(char), 0, (nameLen + 1) * sizeof(char)); + if (nameLen != fread(longLink, sizeof(char), nameLen, FilePtr)) { + free(longLink); + longLink = nullptr; + } + } + + // anyway, go to correct pos + isSkip = true; + fseeko(FilePtr, pos + fileBlockCnt * BLOCK_SIZE, SEEK_SET); + continue; + } + default: { + // Ignoring, skip + isSkip = true; + fseeko(FilePtr, fileBlockCnt * BLOCK_SIZE, SEEK_CUR); + break; + } + } + + if (!isSkip) { + if (!isSoftlink) { + chmod(fullPath, mode); + chown(fullPath, newUid, newGid); + } else { + lchown(fullPath, newUid, newGid); + isSoftlink = false; + } + } + isSkip = false; + + if (longName != nullptr) { + free(longName); + longName = nullptr; + } + if (longLink != nullptr) { + free(longLink); + longLink = nullptr; + } + } + return ret; +} + +void UnTarFile::FreePointer(char *longName, char *longLink, char *fullPath) +{ + if (fullPath != nullptr) { + free(fullPath); + fullPath = nullptr; + } + if (longName != nullptr) { + free(longName); + longName = nullptr; + } + if (longLink != nullptr) { + free(longLink); + 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/frameworks/native/backup_ext/src/ext_extension.cpp b/frameworks/native/backup_ext/src/ext_extension.cpp index cead8c8870b3f577d93a201d38996a81212a09f2..f50db82de509707404552f2536f3b9f974a35a9d 100644 --- a/frameworks/native/backup_ext/src/ext_extension.cpp +++ b/frameworks/native/backup_ext/src/ext_extension.cpp @@ -58,6 +58,7 @@ #include "sandbox_helper.h" #include "service_client.h" #include "tar_file.h" +#include "InstalldUnTarFile.h" namespace OHOS::FileManagement::Backup { const string INDEX_FILE_BACKUP = string(BConstants::PATH_BUNDLE_BACKUP_HOME). @@ -1144,6 +1145,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__); @@ -1158,6 +1249,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"); @@ -1169,20 +1261,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 =